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