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  }