2
0
mirror of synced 2025-02-23 14:58:12 +00:00
mobile/cmd/gomobile/init.go

201 lines
5.1 KiB
Go
Raw Normal View History

// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
// TODO(crawshaw): build darwin/arm cross compiler on darwin/{386,amd64}
// TODO(crawshaw): android/{386,arm64}
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
const ndkVersion = "ndk-r10d"
var cmdInit = &command{
run: runInit,
Name: "init",
Short: "install android compiler toolchain",
Long: `
Init downloads and installs the Android C++ compiler toolchain.
The toolchain is installed in $GOPATH/pkg/gomobile.
`,
}
func runInit(cmd *command) error {
if err := checkGoVersion(); err != nil {
return err
}
// Provide an early error message if Go was installed system-wide.
goroot := goEnv("GOROOT")
sentinel := filepath.Join(goroot, "gomobile-sentinel")
if err := ioutil.WriteFile(sentinel, []byte("write test"), 0664); err != nil {
if os.IsPermission(err) {
return fmt.Errorf("GOROOT %q is not writable. Run:\n\tsudo gomobile init", goroot)
}
return fmt.Errorf("GOROOT not writable: %v", err)
}
os.Remove(sentinel)
arch := runtime.GOARCH
if arch == "amd64" {
arch = "x86_64"
}
gopaths := filepath.SplitList(goEnv("GOPATH"))
if len(gopaths) == 0 {
return fmt.Errorf("GOPATH is not set")
}
dst := filepath.Join(gopaths[0], filepath.FromSlash("pkg/gomobile/android-"+ndkVersion+"/arm"))
if err := os.RemoveAll(dst); err != nil && !os.IsExist(err) {
return err
}
if err := os.MkdirAll(filepath.Join(dst, "sysroot", "usr"), 0755); err != nil {
return err
}
tmpdir, err := ioutil.TempDir(dst, "gomobile-init-")
if err != nil {
return err
}
defer os.RemoveAll(tmpdir)
ndkName := "android-" + ndkVersion + "-" + runtime.GOOS + "-" + arch + "."
if runtime.GOOS == "windows" {
ndkName += "exe"
} else {
ndkName += "bin"
}
f, err := os.OpenFile(filepath.Join(tmpdir, ndkName), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0755)
if err != nil {
return err
}
url := "http://dl.google.com/android/ndk/" + ndkName
if buildV {
fmt.Fprintf(os.Stderr, "fetching %s\n", url)
}
// TODO(crawshaw): The arm compiler toolchain compresses to 33 MB, less than a tenth of the NDK. Provide an alternative binary download.
resp, err := http.Get(url)
if err != nil {
return err
}
_, err = io.Copy(f, resp.Body)
err2 := resp.Body.Close()
err3 := f.Close()
if err != nil {
return err
}
if err2 != nil {
return err2
}
if err3 != nil {
return err3
}
inflate := exec.Command(filepath.Join(tmpdir, ndkName))
inflate.Dir = tmpdir
out, err := inflate.CombinedOutput()
if err != nil {
if buildV {
os.Stderr.Write(out)
}
return err
}
srcSysroot := filepath.Join(tmpdir, "android-ndk-r10d", "platforms", "android-15", "arch-arm", "usr")
dstSysroot := filepath.Join(dst, "sysroot", "usr")
if err := move(dstSysroot, srcSysroot, "include", "lib"); err != nil {
return err
}
ndkpath := filepath.Join(tmpdir, "android-ndk-r10d", "toolchains", "arm-linux-androideabi-4.8", "prebuilt", runtime.GOOS+"-"+arch)
if err := move(dst, ndkpath, "bin", "lib", "libexec"); err != nil {
return err
}
linkpath := filepath.Join(dst, "arm-linux-androideabi", "bin")
if err := os.MkdirAll(linkpath, 0755); err != nil {
return err
}
for _, name := range []string{"ld", "ld.gold", "as", "gcc", "g++"} {
if err := os.Symlink(filepath.Join(dst, "bin", "arm-linux-androideabi-"+name), filepath.Join(linkpath, name)); err != nil {
return err
}
}
// TODO(crawshaw): make.bat on windows
ccpath := filepath.Join(dst, "bin")
make := exec.Command(filepath.Join(goroot, "src", "make.bash"), "--no-clean")
make.Dir = filepath.Join(goroot, "src")
make.Env = []string{
`PATH=` + os.Getenv("PATH"),
`TMPDIR=` + tmpdir,
`HOME=` + os.Getenv("HOME"), // for default the go1.4 bootstrap
`GOOS=android`,
`GOARCH=arm`,
`GOARM=7`,
`CGO_ENABLED=1`,
`CC_FOR_TARGET=` + filepath.Join(ccpath, "arm-linux-androideabi-gcc"),
`CXX_FOR_TARGET=` + filepath.Join(ccpath, "arm-linux-androideabi-g++"),
`GOBIN=` + tmpdir, // avoid overwriting current Go tool
}
if v := goEnv("GOROOT_BOOTSTRAP"); v != "" {
make.Env = append(make.Env, `GOROOT_BOOTSTRAP=`+v)
}
if buildV {
fmt.Fprintf(os.Stderr, "building android/arm cross compiler\n")
make.Stdout = os.Stdout
make.Stderr = os.Stderr
}
if err := make.Run(); err != nil {
return err
}
return nil
}
func move(dst, src string, names ...string) error {
for _, name := range names {
if err := os.Rename(filepath.Join(src, name), filepath.Join(dst, name)); err != nil {
return err
}
}
return nil
}
func checkGoVersion() error {
if err := exec.Command("which", "go").Run(); err != nil {
return fmt.Errorf(`no Go tool on $PATH`)
}
_, err := exec.Command("go", "version").Output()
if err != nil {
return fmt.Errorf("bad Go tool: %v", err)
}
return nil
}
func goEnv(name string) string {
if val := os.Getenv(name); val != "" {
return val
}
val, err := exec.Command("go", "env", name).Output()
if err != nil {
panic(err) // the Go tool was tested to work earlier
}
return strings.TrimSpace(string(val))
}