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