github.com/provpn/mobile@v0.0.0-20210315122651-28c475f89f6c/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 arch == "x86" { 71 arch = "amd64" 72 } 73 74 if err := writeGoMod("darwin", arch); err != nil { 75 return err 76 } 77 78 env := darwinEnv[arch] 79 // Add the generated packages to GOPATH for reverse bindings. 80 gopath := fmt.Sprintf("GOPATH=%s%c%s", tmpdir, filepath.ListSeparator, goEnv("GOPATH")) 81 env = append(env, gopath) 82 83 // Run `go mod tidy` to force to create go.sum. 84 // Without go.sum, `go build` fails as of Go 1.16. 85 if modulesUsed { 86 if err := goModTidyAt(filepath.Join(tmpdir, "src"), env); err != nil { 87 return err 88 } 89 } 90 91 path, err := goIOSBindArchive(name, env, filepath.Join(tmpdir, "src")) 92 if err != nil { 93 return fmt.Errorf("darwin-%s: %v", arch, err) 94 } 95 cmd.Args = append(cmd.Args, "-arch", archClang(arch), path) 96 } 97 98 // Build static framework output directory. 99 if err := removeAll(buildO); err != nil { 100 return err 101 } 102 headers := buildO + "/Versions/A/Headers" 103 if err := mkdir(headers); err != nil { 104 return err 105 } 106 if err := symlink("A", buildO+"/Versions/Current"); err != nil { 107 return err 108 } 109 if err := symlink("Versions/Current/Headers", buildO+"/Headers"); err != nil { 110 return err 111 } 112 if err := symlink("Versions/Current/"+title, buildO+"/"+title); err != nil { 113 return err 114 } 115 116 cmd.Args = append(cmd.Args, "-o", buildO+"/Versions/A/"+title) 117 if err := runCmd(cmd); err != nil { 118 return err 119 } 120 121 // Copy header file next to output archive. 122 headerFiles := make([]string, len(fileBases)) 123 if len(fileBases) == 1 { 124 headerFiles[0] = title + ".h" 125 err := copyFile( 126 headers+"/"+title+".h", 127 srcDir+"/"+bindPrefix+title+".objc.h", 128 ) 129 if err != nil { 130 return err 131 } 132 } else { 133 for i, fileBase := range fileBases { 134 headerFiles[i] = fileBase + ".objc.h" 135 err := copyFile( 136 headers+"/"+fileBase+".objc.h", 137 srcDir+"/"+fileBase+".objc.h") 138 if err != nil { 139 return err 140 } 141 } 142 err := copyFile( 143 headers+"/ref.h", 144 srcDir+"/ref.h") 145 if err != nil { 146 return err 147 } 148 headerFiles = append(headerFiles, title+".h") 149 err = writeFile(headers+"/"+title+".h", func(w io.Writer) error { 150 return iosBindHeaderTmpl.Execute(w, map[string]interface{}{ 151 "pkgs": pkgs, "title": title, "bases": fileBases, 152 }) 153 }) 154 if err != nil { 155 return err 156 } 157 } 158 159 resources := buildO + "/Versions/A/Resources" 160 if err := mkdir(resources); err != nil { 161 return err 162 } 163 if err := symlink("Versions/Current/Resources", buildO+"/Resources"); err != nil { 164 return err 165 } 166 if err := writeFile(buildO+"/Resources/Info.plist", func(w io.Writer) error { 167 _, err := w.Write([]byte(iosBindInfoPlist)) 168 return err 169 }); err != nil { 170 return err 171 } 172 173 var mmVals = struct { 174 Module string 175 Headers []string 176 }{ 177 Module: title, 178 Headers: headerFiles, 179 } 180 err = writeFile(buildO+"/Versions/A/Modules/module.modulemap", func(w io.Writer) error { 181 return iosModuleMapTmpl.Execute(w, mmVals) 182 }) 183 if err != nil { 184 return err 185 } 186 return symlink("Versions/Current/Modules", buildO+"/Modules") 187 } 188 189 const iosBindInfoPlist = `<?xml version="1.0" encoding="UTF-8"?> 190 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 191 <plist version="1.0"> 192 <dict> 193 </dict> 194 </plist> 195 ` 196 197 var iosModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework module "{{.Module}}" { 198 header "ref.h" 199 {{range .Headers}} header "{{.}}" 200 {{end}} 201 export * 202 }`)) 203 204 func goIOSBindArchive(name string, env []string, gosrc string) (string, error) { 205 arch := getenv(env, "GOARCH") 206 archive := filepath.Join(tmpdir, name+"-"+arch+".a") 207 err := goBuildAt(gosrc, "./gobind", env, "-buildmode=c-archive", "-o", archive) 208 if err != nil { 209 return "", err 210 } 211 return archive, nil 212 } 213 214 var iosBindHeaderTmpl = template.Must(template.New("ios.h").Parse(` 215 // Objective-C API for talking to the following Go packages 216 // 217 {{range .pkgs}}// {{.PkgPath}} 218 {{end}}// 219 // File is generated by gomobile bind. Do not edit. 220 #ifndef __{{.title}}_FRAMEWORK_H__ 221 #define __{{.title}}_FRAMEWORK_H__ 222 223 {{range .bases}}#include "{{.}}.objc.h" 224 {{end}} 225 #endif 226 `))