github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/cmd/gomobile/env.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "runtime" 12 "strings" 13 ) 14 15 // General mobile build environment. Initialized by envInit. 16 var ( 17 cwd string 18 gomobilepath string // $GOPATH/pkg/gomobile 19 20 androidEnv map[string][]string // android arch -> []string 21 22 darwinArmEnv []string 23 darwinArm64Env []string 24 darwin386Env []string 25 darwinAmd64Env []string 26 27 androidArmNM string 28 darwinArmNM string 29 ) 30 31 func buildEnvInit() (cleanup func(), err error) { 32 // Find gomobilepath. 33 gopath := goEnv("GOPATH") 34 for _, p := range filepath.SplitList(gopath) { 35 gomobilepath = filepath.Join(p, "pkg", "gomobile") 36 if _, err := os.Stat(gomobilepath); buildN || err == nil { 37 break 38 } 39 } 40 41 if err := envInit(); err != nil { 42 return nil, err 43 } 44 45 if buildX { 46 fmt.Fprintln(xout, "GOMOBILE="+gomobilepath) 47 } 48 49 // Check the toolchain is in a good state. 50 // Pick a temporary directory for assembling an apk/app. 51 if gomobilepath == "" { 52 return nil, errors.New("toolchain not installed, run `gomobile init`") 53 } 54 cleanupFn := func() { 55 if buildWork { 56 fmt.Printf("WORK=%s\n", tmpdir) 57 return 58 } 59 removeAll(tmpdir) 60 } 61 if buildN { 62 tmpdir = "$WORK" 63 cleanupFn = func() {} 64 } else { 65 verpath := filepath.Join(gomobilepath, "version") 66 installedVersion, err := ioutil.ReadFile(verpath) 67 if err != nil { 68 return nil, errors.New("toolchain partially installed, run `gomobile init`") 69 } 70 if !bytes.Equal(installedVersion, goVersionOut) { 71 return nil, errors.New("toolchain out of date, run `gomobile init`") 72 } 73 74 tmpdir, err = ioutil.TempDir("", "gomobile-work-") 75 if err != nil { 76 return nil, err 77 } 78 } 79 if buildX { 80 fmt.Fprintln(xout, "WORK="+tmpdir) 81 } 82 83 return cleanupFn, nil 84 } 85 86 func envInit() (err error) { 87 // TODO(crawshaw): cwd only used by ctx.Import, which can take "." 88 cwd, err = os.Getwd() 89 if err != nil { 90 return err 91 } 92 93 // Setup the cross-compiler environments. 94 95 androidEnv = make(map[string][]string) 96 for arch, toolchain := range ndk { 97 if goVersion < toolchain.minGoVer { 98 continue 99 } 100 101 androidEnv[arch] = []string{ 102 "GOOS=android", 103 "GOARCH=" + arch, 104 "CC=" + toolchain.Path("gcc"), 105 "CXX=" + toolchain.Path("g++"), 106 "CGO_ENABLED=1", 107 } 108 if arch == "arm" { 109 androidEnv[arch] = append(androidEnv[arch], "GOARM=7") 110 } 111 } 112 113 if runtime.GOOS != "darwin" { 114 return nil 115 } 116 117 clang, cflags, err := envClang("iphoneos") 118 if err != nil { 119 return err 120 } 121 darwinArmEnv = []string{ 122 "GOOS=darwin", 123 "GOARCH=arm", 124 "GOARM=7", 125 "CC=" + clang, 126 "CXX=" + clang, 127 "CGO_CFLAGS=" + cflags + " -arch " + archClang("arm"), 128 "CGO_LDFLAGS=" + cflags + " -arch " + archClang("arm"), 129 "CGO_ENABLED=1", 130 } 131 darwinArmNM = "nm" 132 darwinArm64Env = []string{ 133 "GOOS=darwin", 134 "GOARCH=arm64", 135 "CC=" + clang, 136 "CXX=" + clang, 137 "CGO_CFLAGS=" + cflags + " -arch " + archClang("arm64"), 138 "CGO_LDFLAGS=" + cflags + " -arch " + archClang("arm64"), 139 "CGO_ENABLED=1", 140 } 141 142 clang, cflags, err = envClang("iphonesimulator") 143 if err != nil { 144 return err 145 } 146 darwin386Env = []string{ 147 "GOOS=darwin", 148 "GOARCH=386", 149 "CC=" + clang, 150 "CXX=" + clang, 151 "CGO_CFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch " + archClang("386"), 152 "CGO_LDFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch " + archClang("386"), 153 "CGO_ENABLED=1", 154 } 155 darwinAmd64Env = []string{ 156 "GOOS=darwin", 157 "GOARCH=amd64", 158 "CC=" + clang, 159 "CXX=" + clang, 160 "CGO_CFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch x86_64", 161 "CGO_LDFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch x86_64", 162 "CGO_ENABLED=1", 163 } 164 165 return nil 166 } 167 168 func envClang(sdkName string) (clang, cflags string, err error) { 169 if buildN { 170 return "clang-" + sdkName, "-isysroot=" + sdkName, nil 171 } 172 cmd := exec.Command("xcrun", "--sdk", sdkName, "--find", "clang") 173 out, err := cmd.CombinedOutput() 174 if err != nil { 175 return "", "", fmt.Errorf("xcrun --find: %v\n%s", err, out) 176 } 177 clang = strings.TrimSpace(string(out)) 178 179 cmd = exec.Command("xcrun", "--sdk", sdkName, "--show-sdk-path") 180 out, err = cmd.CombinedOutput() 181 if err != nil { 182 return "", "", fmt.Errorf("xcrun --show-sdk-path: %v\n%s", err, out) 183 } 184 sdk := strings.TrimSpace(string(out)) 185 186 return clang, "-isysroot " + sdk, nil 187 } 188 189 func archClang(goarch string) string { 190 switch goarch { 191 case "arm": 192 return "armv7" 193 case "arm64": 194 return "arm64" 195 case "386": 196 return "i386" 197 case "amd64": 198 return "x86_64" 199 default: 200 panic(fmt.Sprintf("unknown GOARCH: %q", goarch)) 201 } 202 } 203 204 // environ merges os.Environ and the given "key=value" pairs. 205 // If a key is in both os.Environ and kv, kv takes precedence. 206 func environ(kv []string) []string { 207 cur := os.Environ() 208 new := make([]string, 0, len(cur)+len(kv)) 209 210 envs := make(map[string]string, len(cur)) 211 for _, ev := range cur { 212 elem := strings.SplitN(ev, "=", 2) 213 if len(elem) != 2 || elem[0] == "" { 214 // pass the env var of unusual form untouched. 215 // e.g. Windows may have env var names starting with "=". 216 new = append(new, ev) 217 continue 218 } 219 if goos == "windows" { 220 elem[0] = strings.ToUpper(elem[0]) 221 } 222 envs[elem[0]] = elem[1] 223 } 224 for _, ev := range kv { 225 elem := strings.SplitN(ev, "=", 2) 226 if len(elem) != 2 || elem[0] == "" { 227 panic(fmt.Sprintf("malformed env var %q from input", ev)) 228 } 229 if goos == "windows" { 230 elem[0] = strings.ToUpper(elem[0]) 231 } 232 envs[elem[0]] = elem[1] 233 } 234 for k, v := range envs { 235 new = append(new, k+"="+v) 236 } 237 return new 238 } 239 240 func getenv(env []string, key string) string { 241 prefix := key + "=" 242 for _, kv := range env { 243 if strings.HasPrefix(kv, prefix) { 244 return kv[len(prefix):] 245 } 246 } 247 return "" 248 } 249 250 func pkgdir(env []string) string { 251 return gomobilepath + "/pkg_" + getenv(env, "GOOS") + "_" + getenv(env, "GOARCH") 252 } 253 254 type ndkToolchain struct { 255 arch string 256 abi string 257 platform string 258 gcc string 259 toolPrefix string 260 minGoVer goToolVersion 261 } 262 263 func (tc *ndkToolchain) Path(toolName string) string { 264 if goos == "windows" { 265 toolName += ".exe" 266 } 267 return filepath.Join(ndk.Root(), tc.arch, "bin", tc.toolPrefix+"-"+toolName) 268 } 269 270 type ndkConfig map[string]ndkToolchain // map: GOOS->androidConfig. 271 272 func (nc ndkConfig) Root() string { 273 return filepath.Join(gomobilepath, "android-"+ndkVersion) 274 } 275 276 func (nc ndkConfig) Toolchain(arch string) ndkToolchain { 277 tc, ok := nc[arch] 278 if !ok || tc.minGoVer > goVersion { 279 panic(`unsupported architecture: ` + arch) 280 } 281 return tc 282 } 283 284 // TODO: share this with release.go 285 var ndk = ndkConfig{ 286 "arm": { 287 arch: "arm", 288 abi: "armeabi-v7a", 289 platform: "android-15", 290 gcc: "arm-linux-androideabi-4.8", 291 toolPrefix: "arm-linux-androideabi", 292 minGoVer: go1_5, 293 }, 294 /* 295 "386": { 296 arch: "x86", 297 abi: "x86", 298 platform: "android-15", 299 gcc: "x86-4.8", 300 toolPrefix: "i686-linux-android", 301 minGoVer: go1_6, 302 303 }, 304 "amd64": { 305 arch: "x86_64", 306 abi: "x86_64", 307 platform: "android-21", 308 gcc: "x86_64-4.9", 309 toolPrefix: "x86_64-linux-android", 310 minGoVer: go1_6, 311 }, 312 */ 313 }