github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/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 ndkccpath string // $GOPATH/pkg/gomobile/android-{{.NDK}} 20 21 androidArmEnv []string 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 version, err := goVersion() 52 if err != nil { 53 return nil, err 54 } 55 if gomobilepath == "" { 56 return nil, errors.New("toolchain not installed, run `gomobile init`") 57 } 58 59 cleanupFn := func() { 60 if buildWork { 61 fmt.Printf("WORK=%s\n", tmpdir) 62 return 63 } 64 removeAll(tmpdir) 65 } 66 if buildN { 67 tmpdir = "$WORK" 68 cleanupFn = func() {} 69 } else { 70 verpath := filepath.Join(gomobilepath, "version") 71 installedVersion, err := ioutil.ReadFile(verpath) 72 if err != nil { 73 return nil, errors.New("toolchain partially installed, run `gomobile init`") 74 } 75 if !bytes.Equal(installedVersion, version) { 76 return nil, errors.New("toolchain out of date, run `gomobile init`") 77 } 78 79 tmpdir, err = ioutil.TempDir("", "gomobile-work-") 80 if err != nil { 81 return nil, err 82 } 83 } 84 if buildX { 85 fmt.Fprintln(xout, "WORK="+tmpdir) 86 } 87 88 return cleanupFn, nil 89 } 90 91 func envInit() (err error) { 92 // TODO(crawshaw): cwd only used by ctx.Import, which can take "." 93 cwd, err = os.Getwd() 94 if err != nil { 95 return err 96 } 97 98 // Setup the cross-compiler environments. 99 100 // TODO(crawshaw): Remove ndkccpath global. 101 ndkccpath = filepath.Join(gomobilepath, "android-"+ndkVersion) 102 ndkccbin := filepath.Join(ndkccpath, "arm", "bin") 103 104 exe := "" 105 if goos == "windows" { 106 exe = ".exe" 107 } 108 androidArmEnv = []string{ 109 "GOOS=android", 110 "GOARCH=arm", 111 "GOARM=7", 112 "CC=" + filepath.Join(ndkccbin, "arm-linux-androideabi-gcc"+exe), 113 "CXX=" + filepath.Join(ndkccbin, "arm-linux-androideabi-g++"+exe), 114 "CGO_ENABLED=1", 115 } 116 androidArmNM = filepath.Join(ndkccbin, "arm-linux-androideabi-nm"+exe) 117 118 if runtime.GOOS != "darwin" { 119 return nil 120 } 121 122 clang, cflags, err := envClang("iphoneos") 123 if err != nil { 124 return err 125 } 126 darwinArmEnv = []string{ 127 "GOOS=darwin", 128 "GOARCH=arm", 129 "GOARM=7", 130 "CC=" + clang, 131 "CXX=" + clang, 132 "CGO_CFLAGS=" + cflags + " -arch " + archClang("arm"), 133 "CGO_LDFLAGS=" + cflags + " -arch " + archClang("arm"), 134 "CGO_ENABLED=1", 135 } 136 darwinArmNM = "nm" 137 darwinArm64Env = []string{ 138 "GOOS=darwin", 139 "GOARCH=arm64", 140 "CC=" + clang, 141 "CXX=" + clang, 142 "CGO_CFLAGS=" + cflags + " -arch " + archClang("arm64"), 143 "CGO_LDFLAGS=" + cflags + " -arch " + archClang("arm64"), 144 "CGO_ENABLED=1", 145 } 146 147 clang, cflags, err = envClang("iphonesimulator") 148 if err != nil { 149 return err 150 } 151 darwin386Env = []string{ 152 "GOOS=darwin", 153 "GOARCH=386", 154 "CC=" + clang, 155 "CXX=" + clang, 156 "CGO_CFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch " + archClang("386"), 157 "CGO_LDFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch " + archClang("386"), 158 "CGO_ENABLED=1", 159 } 160 darwinAmd64Env = []string{ 161 "GOOS=darwin", 162 "GOARCH=amd64", 163 "CC=" + clang, 164 "CXX=" + clang, 165 "CGO_CFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch x86_64", 166 "CGO_LDFLAGS=" + cflags + " -mios-simulator-version-min=6.1 -arch x86_64", 167 "CGO_ENABLED=1", 168 } 169 170 return nil 171 } 172 173 func envClang(sdkName string) (clang, cflags string, err error) { 174 if buildN { 175 return "clang-" + sdkName, "-isysroot=" + sdkName, nil 176 } 177 cmd := exec.Command("xcrun", "--sdk", sdkName, "--find", "clang") 178 out, err := cmd.Output() 179 if err != nil { 180 return "", "", fmt.Errorf("xcrun --find: %v\n%s", err, out) 181 } 182 clang = strings.TrimSpace(string(out)) 183 184 cmd = exec.Command("xcrun", "--sdk", sdkName, "--show-sdk-path") 185 out, err = cmd.Output() 186 if err != nil { 187 return "", "", fmt.Errorf("xcrun --show-sdk-path: %v\n%s", err, out) 188 } 189 sdk := strings.TrimSpace(string(out)) 190 191 return clang, "-isysroot " + sdk, nil 192 } 193 194 func archClang(goarch string) string { 195 switch goarch { 196 case "arm": 197 return "armv7" 198 case "arm64": 199 return "arm64" 200 case "386": 201 return "i386" 202 case "amd64": 203 return "x86_64" 204 default: 205 panic(fmt.Sprintf("unknown GOARCH: %q", goarch)) 206 } 207 } 208 209 // environ merges os.Environ and the given "key=value" pairs. 210 // If a key is in both os.Environ and kv, kv takes precedence. 211 func environ(kv []string) []string { 212 cur := os.Environ() 213 new := make([]string, 0, len(cur)+len(kv)) 214 215 envs := make(map[string]string, len(cur)) 216 for _, ev := range cur { 217 elem := strings.SplitN(ev, "=", 2) 218 if len(elem) != 2 || elem[0] == "" { 219 // pass the env var of unusual form untouched. 220 // e.g. Windows may have env var names starting with "=". 221 new = append(new, ev) 222 continue 223 } 224 if goos == "windows" { 225 elem[0] = strings.ToUpper(elem[0]) 226 } 227 envs[elem[0]] = elem[1] 228 } 229 for _, ev := range kv { 230 elem := strings.SplitN(ev, "=", 2) 231 if len(elem) != 2 || elem[0] == "" { 232 panic(fmt.Sprintf("malformed env var %q from input", ev)) 233 } 234 if goos == "windows" { 235 elem[0] = strings.ToUpper(elem[0]) 236 } 237 envs[elem[0]] = elem[1] 238 } 239 for k, v := range envs { 240 new = append(new, k+"="+v) 241 } 242 return new 243 } 244 245 func getenv(env []string, key string) string { 246 prefix := key + "=" 247 for _, kv := range env { 248 if strings.HasPrefix(kv, prefix) { 249 return kv[len(prefix):] 250 } 251 } 252 return "" 253 } 254 255 func pkgdir(env []string) string { 256 return gomobilepath + "/pkg_" + getenv(env, "GOOS") + "_" + getenv(env, "GOARCH") 257 }