github.com/acrespo/mobile@v0.0.0-20190107162257-dc0771356504/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  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  	"text/template"
    15  )
    16  
    17  func goIOSBind(gobind string, pkgs []*build.Package, archs []string) error {
    18  	// Run gobind to generate the bindings
    19  	cmd := exec.Command(
    20  		gobind,
    21  		"-lang=go,objc",
    22  		"-outdir="+tmpdir,
    23  	)
    24  	cmd.Env = append(cmd.Env, "GOOS=darwin")
    25  	cmd.Env = append(cmd.Env, "CGO_ENABLED=1")
    26  	if len(ctx.BuildTags) > 0 {
    27  		cmd.Args = append(cmd.Args, "-tags="+strings.Join(ctx.BuildTags, ","))
    28  	}
    29  	if bindPrefix != "" {
    30  		cmd.Args = append(cmd.Args, "-prefix="+bindPrefix)
    31  	}
    32  	for _, p := range pkgs {
    33  		cmd.Args = append(cmd.Args, p.ImportPath)
    34  	}
    35  	if err := runCmd(cmd); err != nil {
    36  		return err
    37  	}
    38  
    39  	srcDir := filepath.Join(tmpdir, "src", "gobind")
    40  	gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, goEnv("GOPATH"))
    41  
    42  	name := pkgs[0].Name
    43  	title := strings.Title(name)
    44  
    45  	if buildO != "" && !strings.HasSuffix(buildO, ".framework") {
    46  		return fmt.Errorf("static framework name %q missing .framework suffix", buildO)
    47  	}
    48  	if buildO == "" {
    49  		buildO = title + ".framework"
    50  	}
    51  
    52  	fileBases := make([]string, len(pkgs)+1)
    53  	for i, pkg := range pkgs {
    54  		fileBases[i] = bindPrefix + strings.Title(pkg.Name)
    55  	}
    56  	fileBases[len(fileBases)-1] = "Universe"
    57  
    58  	cmd = exec.Command("xcrun", "lipo", "-create")
    59  
    60  	for _, arch := range archs {
    61  		env := darwinEnv[arch]
    62  		env = append(env, gopath)
    63  		path, err := goIOSBindArchive(name, env)
    64  		if err != nil {
    65  			return fmt.Errorf("darwin-%s: %v", arch, err)
    66  		}
    67  		cmd.Args = append(cmd.Args, "-arch", archClang(arch), path)
    68  	}
    69  
    70  	// Build static framework output directory.
    71  	if err := removeAll(buildO); err != nil {
    72  		return err
    73  	}
    74  	headers := buildO + "/Versions/A/Headers"
    75  	if err := mkdir(headers); err != nil {
    76  		return err
    77  	}
    78  	if err := symlink("A", buildO+"/Versions/Current"); err != nil {
    79  		return err
    80  	}
    81  	if err := symlink("Versions/Current/Headers", buildO+"/Headers"); err != nil {
    82  		return err
    83  	}
    84  	if err := symlink("Versions/Current/"+title, buildO+"/"+title); err != nil {
    85  		return err
    86  	}
    87  
    88  	cmd.Args = append(cmd.Args, "-o", buildO+"/Versions/A/"+title)
    89  	if err := runCmd(cmd); err != nil {
    90  		return err
    91  	}
    92  
    93  	// Copy header file next to output archive.
    94  	headerFiles := make([]string, len(fileBases))
    95  	if len(fileBases) == 1 {
    96  		headerFiles[0] = title + ".h"
    97  		err := copyFile(
    98  			headers+"/"+title+".h",
    99  			srcDir+"/"+bindPrefix+title+".objc.h",
   100  		)
   101  		if err != nil {
   102  			return err
   103  		}
   104  	} else {
   105  		for i, fileBase := range fileBases {
   106  			headerFiles[i] = fileBase + ".objc.h"
   107  			err := copyFile(
   108  				headers+"/"+fileBase+".objc.h",
   109  				srcDir+"/"+fileBase+".objc.h")
   110  			if err != nil {
   111  				return err
   112  			}
   113  		}
   114  		err := copyFile(
   115  			headers+"/ref.h",
   116  			srcDir+"/ref.h")
   117  		if err != nil {
   118  			return err
   119  		}
   120  		headerFiles = append(headerFiles, title+".h")
   121  		err = writeFile(headers+"/"+title+".h", func(w io.Writer) error {
   122  			return iosBindHeaderTmpl.Execute(w, map[string]interface{}{
   123  				"pkgs": pkgs, "title": title, "bases": fileBases,
   124  			})
   125  		})
   126  		if err != nil {
   127  			return err
   128  		}
   129  	}
   130  
   131  	resources := buildO + "/Versions/A/Resources"
   132  	if err := mkdir(resources); err != nil {
   133  		return err
   134  	}
   135  	if err := symlink("Versions/Current/Resources", buildO+"/Resources"); err != nil {
   136  		return err
   137  	}
   138  	err := writeFile(buildO+"/Resources/Info.plist", func(w io.Writer) error {
   139  		_, err := w.Write([]byte(iosBindInfoPlist))
   140  		return err
   141  	})
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	var mmVals = struct {
   147  		Module  string
   148  		Headers []string
   149  	}{
   150  		Module:  title,
   151  		Headers: headerFiles,
   152  	}
   153  	err = writeFile(buildO+"/Versions/A/Modules/module.modulemap", func(w io.Writer) error {
   154  		return iosModuleMapTmpl.Execute(w, mmVals)
   155  	})
   156  	if err != nil {
   157  		return err
   158  	}
   159  	return symlink("Versions/Current/Modules", buildO+"/Modules")
   160  }
   161  
   162  const iosBindInfoPlist = `<?xml version="1.0" encoding="UTF-8"?>
   163      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
   164      <plist version="1.0">
   165        <dict>
   166        </dict>
   167      </plist>
   168  `
   169  
   170  var iosModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework module "{{.Module}}" {
   171  	header "ref.h"
   172  {{range .Headers}}    header "{{.}}"
   173  {{end}}
   174      export *
   175  }`))
   176  
   177  func goIOSBindArchive(name string, env []string) (string, error) {
   178  	arch := getenv(env, "GOARCH")
   179  	archive := filepath.Join(tmpdir, name+"-"+arch+".a")
   180  	err := goBuild("gobind", env, "-buildmode=c-archive", "-o", archive)
   181  	if err != nil {
   182  		return "", err
   183  	}
   184  
   185  	return archive, nil
   186  }
   187  
   188  var iosBindHeaderTmpl = template.Must(template.New("ios.h").Parse(`
   189  // Objective-C API for talking to the following Go packages
   190  //
   191  {{range .pkgs}}//	{{.ImportPath}}
   192  {{end}}//
   193  // File is generated by gomobile bind. Do not edit.
   194  #ifndef __{{.title}}_FRAMEWORK_H__
   195  #define __{{.title}}_FRAMEWORK_H__
   196  
   197  {{range .bases}}#include "{{.}}.objc.h"
   198  {{end}}
   199  #endif
   200  `))