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  }