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 `))