github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/cmd/gomobile/bind_iosapp.go (about)

     1  // Copyright 2015 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  	"go/build"
    10  	"io"
    11  	"io/ioutil"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"strings"
    15  	"text/template"
    16  )
    17  
    18  func goIOSBind(pkgs []*build.Package) error {
    19  	typesPkgs, err := loadExportData(pkgs, darwinArmEnv)
    20  	if err != nil {
    21  		return err
    22  	}
    23  
    24  	binder, err := newBinder(typesPkgs)
    25  	if err != nil {
    26  		return err
    27  	}
    28  	name := binder.pkgs[0].Name()
    29  	title := strings.Title(name)
    30  
    31  	if buildO != "" && !strings.HasSuffix(buildO, ".framework") {
    32  		return fmt.Errorf("static framework name %q missing .framework suffix", buildO)
    33  	}
    34  	if buildO == "" {
    35  		buildO = title + ".framework"
    36  	}
    37  
    38  	srcDir := filepath.Join(tmpdir, "src")
    39  	for _, pkg := range typesPkgs {
    40  		if err := binder.GenGo(pkg, srcDir); err != nil {
    41  			return err
    42  		}
    43  	}
    44  	mainFile := filepath.Join(tmpdir, "src/iosbin/main.go")
    45  	err = writeFile(mainFile, func(w io.Writer) error {
    46  		return iosBindTmpl.Execute(w, pkgs)
    47  	})
    48  	if err != nil {
    49  		return fmt.Errorf("failed to create the binding package for iOS: %v", err)
    50  	}
    51  
    52  	objcDir := filepath.Join(tmpdir, "objc")
    53  	fileBases := make([]string, len(typesPkgs))
    54  	for i, pkg := range typesPkgs {
    55  		if fileBases[i], err = binder.GenObjc(pkg, objcDir); err != nil {
    56  			return err
    57  		}
    58  	}
    59  
    60  	cmd := exec.Command("xcrun", "lipo", "-create")
    61  
    62  	for _, env := range [][]string{darwinArmEnv, darwinArm64Env, darwinAmd64Env} {
    63  		arch := archClang(getenv(env, "GOARCH"))
    64  		path, err := goIOSBindArchive(name, mainFile, env, fileBases)
    65  		if err != nil {
    66  			return fmt.Errorf("darwin-%s: %v", arch, err)
    67  		}
    68  		cmd.Args = append(cmd.Args, "-arch", arch, path)
    69  	}
    70  
    71  	// Build static framework output directory.
    72  	if err := removeAll(buildO); err != nil {
    73  		return err
    74  	}
    75  	headers := buildO + "/Versions/A/Headers"
    76  	if err := mkdir(headers); err != nil {
    77  		return err
    78  	}
    79  	if err := symlink("A", buildO+"/Versions/Current"); err != nil {
    80  		return err
    81  	}
    82  	if err := symlink("Versions/Current/Headers", buildO+"/Headers"); err != nil {
    83  		return err
    84  	}
    85  	if err := symlink("Versions/Current/"+title, buildO+"/"+title); err != nil {
    86  		return err
    87  	}
    88  
    89  	cmd.Args = append(cmd.Args, "-o", buildO+"/Versions/A/"+title)
    90  	if err := runCmd(cmd); err != nil {
    91  		return err
    92  	}
    93  
    94  	// Copy header file next to output archive.
    95  	headerFiles := make([]string, len(fileBases))
    96  	if len(fileBases) == 1 {
    97  		headerFiles[0] = title + ".h"
    98  		err = copyFile(
    99  			headers+"/"+title+".h",
   100  			tmpdir+"/objc/"+bindPrefix+title+".h",
   101  		)
   102  		if err != nil {
   103  			return err
   104  		}
   105  	} else {
   106  		for i, fileBase := range fileBases {
   107  			headerFiles[i] = fileBase + ".h"
   108  			err = copyFile(
   109  				headers+"/"+fileBase+".h",
   110  				tmpdir+"/objc/"+fileBase+".h")
   111  			if err != nil {
   112  				return err
   113  			}
   114  		}
   115  		headerFiles = append(headerFiles, title+".h")
   116  		err = writeFile(headers+"/"+title+".h", func(w io.Writer) error {
   117  			return iosBindHeaderTmpl.Execute(w, map[string]interface{}{
   118  				"pkgs": pkgs, "title": title, "bases": fileBases,
   119  			})
   120  		})
   121  		if err != nil {
   122  			return err
   123  		}
   124  	}
   125  
   126  	resources := buildO + "/Versions/A/Resources"
   127  	if err := mkdir(resources); err != nil {
   128  		return err
   129  	}
   130  	if err := symlink("Versions/Current/Resources", buildO+"/Resources"); err != nil {
   131  		return err
   132  	}
   133  	if err := ioutil.WriteFile(buildO+"/Resources/Info.plist", []byte(iosBindInfoPlist), 0666); err != nil {
   134  		return err
   135  	}
   136  
   137  	var mmVals = struct {
   138  		Module  string
   139  		Headers []string
   140  	}{
   141  		Module:  title,
   142  		Headers: headerFiles,
   143  	}
   144  	err = writeFile(buildO+"/Versions/A/Modules/module.modulemap", func(w io.Writer) error {
   145  		return iosModuleMapTmpl.Execute(w, mmVals)
   146  	})
   147  	if err != nil {
   148  		return err
   149  	}
   150  	return symlink("Versions/Current/Modules", buildO+"/Modules")
   151  }
   152  
   153  const iosBindInfoPlist = `<?xml version="1.0" encoding="UTF-8"?>
   154      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
   155      <plist version="1.0">
   156        <dict>
   157        </dict>
   158      </plist>
   159  `
   160  
   161  var iosModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework module "{{.Module}}" {
   162  {{range .Headers}}    header "{{.}}"
   163  {{end}}
   164      export *
   165  }`))
   166  
   167  func goIOSBindArchive(name, path string, env, fileBases []string) (string, error) {
   168  	arch := getenv(env, "GOARCH")
   169  	archive := filepath.Join(tmpdir, name+"-"+arch+".a")
   170  	err := goBuild(path, env, "-buildmode=c-archive", "-tags=ios", "-o", archive)
   171  	if err != nil {
   172  		return "", err
   173  	}
   174  
   175  	objs, mfiles := make([]string, len(fileBases)), make([]string, len(fileBases))
   176  	for i, b := range fileBases {
   177  		objs[i], mfiles[i] = b+".o", b+".m"
   178  	}
   179  
   180  	args := append([]string{
   181  		"-I", ".",
   182  		"-g", "-O2",
   183  		"-fobjc-arc", // enable ARC
   184  		"-c",
   185  	}, mfiles...)
   186  
   187  	cmd := exec.Command(getenv(env, "CC"), args...)
   188  	cmd.Args = append(cmd.Args, strings.Split(getenv(env, "CGO_CFLAGS"), " ")...)
   189  	cmd.Dir = filepath.Join(tmpdir, "objc")
   190  	cmd.Env = append([]string{}, env...)
   191  	if err := runCmd(cmd); err != nil {
   192  		return "", err
   193  	}
   194  
   195  	arArgs := append([]string{"-q", "-s", archive}, objs...)
   196  	cmd = exec.Command("ar", arArgs...)
   197  	cmd.Dir = filepath.Join(tmpdir, "objc")
   198  	if err := runCmd(cmd); err != nil {
   199  		return "", err
   200  	}
   201  	return archive, nil
   202  }
   203  
   204  var iosBindTmpl = template.Must(template.New("ios.go").Parse(`
   205  package main
   206  
   207  import (
   208  	_ "golang.org/x/mobile/bind/objc"
   209  {{range .}}	_ "../go_{{.Name}}"
   210  {{end}}
   211  )
   212  
   213  import "C"
   214  
   215  func main() {}
   216  `))
   217  
   218  var iosBindHeaderTmpl = template.Must(template.New("ios.h").Parse(`
   219  // Objective-C API for talking to the following Go packages
   220  //
   221  {{range .pkgs}}//	{{.ImportPath}}
   222  {{end}}//
   223  // File is generated by gomobile bind. Do not edit.
   224  #ifndef __{{.title}}_H__
   225  #define __{{.title}}_H__
   226  
   227  {{range .bases}}#include "{{.}}.h"
   228  {{end}}
   229  #endif
   230  `))