2
0
mirror of synced 2025-02-23 14:58:12 +00:00
mobile/cmd/gomobile/build.go
David Crawshaw ecc4253790 cmd/gomobile: set *build.Context GOOS
This lets `gomobile build` work on a package that contains files all
protected as '// +build android'.

Change-Id: I22915aecda8674597cfe18e1f75d30e6bfc4aab7
Reviewed-on: https://go-review.googlesource.com/12640
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2015-07-24 19:52:34 +00:00

243 lines
5.9 KiB
Go

// 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.
//go:generate go run gendex.go -o dex.go
package main
import (
"fmt"
"go/build"
"io"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
)
var ctx = build.Default
var pkg *build.Package // TODO(crawshaw): remove global pkg variable
var tmpdir string
var cmdBuild = &command{
run: runBuild,
Name: "build",
Usage: "[-target android|ios] [-o output] [build flags] [package]",
Short: "compile android APK and iOS app",
Long: `
Build compiles and encodes the app named by the import path.
The named package must define a main function.
The -target flag takes a target system name, either android (the
default) or ios.
For -target android, if an AndroidManifest.xml is defined in the
package directory, it is added to the APK output. Otherwise, a default
manifest is generated.
For -target ios, gomobile must be run on an OS X machine with Xcode
installed. Support is not complete.
If the package directory contains an assets subdirectory, its contents
are copied into the output.
The -o flag specifies the output file name. If not specified, the
output file name depends on the package built.
The -v flag provides verbose output, including the list of packages built.
The build flags -a, -i, -n, -x, -gcflags, -ldflags, and -tags are shared
with the build command. For documentation, see 'go help build'.
`,
}
func runBuild(cmd *command) (err error) {
cleanup, err := buildEnvInit()
if err != nil {
return err
}
defer cleanup()
args := cmd.flag.Args()
ctx.GOARCH = "arm"
switch buildTarget {
case "android":
ctx.GOOS = "android"
case "ios":
ctx.GOOS = "darwin"
default:
return fmt.Errorf(`unknown -target, %q.`, buildTarget)
}
switch len(args) {
case 0:
pkg, err = ctx.ImportDir(cwd, build.ImportComment)
case 1:
pkg, err = ctx.Import(args[0], cwd, build.ImportComment)
default:
cmd.usage()
os.Exit(1)
}
if err != nil {
return err
}
if pkg.Name != "main" && buildO != "" {
return fmt.Errorf("cannot set -o when building non-main package")
}
switch buildTarget {
case "android":
if pkg.Name != "main" {
return goBuild(pkg.ImportPath, androidArmEnv)
}
if err := goAndroidBuild(pkg); err != nil {
return err
}
case "ios":
if runtime.GOOS != "darwin" {
return fmt.Errorf("-target=ios requires darwin host")
}
if pkg.Name != "main" {
if err := goBuild(pkg.ImportPath, darwinArmEnv); err != nil {
return err
}
return goBuild(pkg.ImportPath, darwinArm64Env)
}
if err := goIOSBuild(pkg); err != nil {
return err
}
}
// TODO(crawshaw): This is an incomplete package scan.
// A complete package scan would be too expensive. Instead,
// fake it. After the binary is built, scan its symbols
// with nm and look for the app and al packages.
if err := importsApp(pkg); err != nil {
return err
}
return nil
}
func importsApp(pkg *build.Package) error {
// Building a program, make sure it is appropriate for mobile.
for _, path := range pkg.Imports {
if path == "golang.org/x/mobile/app" {
return nil
}
}
return fmt.Errorf(`%s does not import "golang.org/x/mobile/app"`, pkg.ImportPath)
}
var xout io.Writer = os.Stderr
func printcmd(format string, args ...interface{}) {
cmd := fmt.Sprintf(format+"\n", args...)
if tmpdir != "" {
cmd = strings.Replace(cmd, tmpdir, "$WORK", -1)
}
if androidHome := os.Getenv("ANDROID_HOME"); androidHome != "" {
cmd = strings.Replace(cmd, androidHome, "$ANDROID_HOME", -1)
}
if gomobilepath != "" {
cmd = strings.Replace(cmd, gomobilepath, "$GOMOBILE", -1)
}
if goroot := goEnv("GOROOT"); goroot != "" {
cmd = strings.Replace(cmd, goroot, "$GOROOT", -1)
}
if gopath := goEnv("GOPATH"); gopath != "" {
cmd = strings.Replace(cmd, gopath, "$GOPATH", -1)
}
if env := os.Getenv("HOME"); env != "" {
cmd = strings.Replace(cmd, env, "$HOME", -1)
}
if env := os.Getenv("HOMEPATH"); env != "" {
cmd = strings.Replace(cmd, env, "$HOMEPATH", -1)
}
fmt.Fprint(xout, cmd)
}
// "Build flags", used by multiple commands.
var (
buildA bool // -a
buildI bool // -i
buildN bool // -n
buildV bool // -v
buildX bool // -x
buildO string // -o
buildGcflags string // -gcflags
buildLdflags string // -ldflags
buildTarget string // -target
)
func addBuildFlags(cmd *command) {
cmd.flag.StringVar(&buildO, "o", "", "")
cmd.flag.StringVar(&buildGcflags, "gcflags", "", "")
cmd.flag.StringVar(&buildLdflags, "ldflags", "", "")
cmd.flag.StringVar(&buildTarget, "target", "android", "")
cmd.flag.BoolVar(&buildA, "a", false, "")
cmd.flag.BoolVar(&buildI, "i", false, "")
cmd.flag.Var((*stringsFlag)(&ctx.BuildTags), "tags", "")
}
func addBuildFlagsNVX(cmd *command) {
cmd.flag.BoolVar(&buildN, "n", false, "")
cmd.flag.BoolVar(&buildV, "v", false, "")
cmd.flag.BoolVar(&buildX, "x", false, "")
}
type binInfo struct {
hasPkgApp bool
hasPkgAL bool
}
func init() {
addBuildFlags(cmdBuild)
addBuildFlagsNVX(cmdBuild)
addBuildFlags(cmdInstall)
addBuildFlagsNVX(cmdInstall)
addBuildFlagsNVX(cmdInit)
addBuildFlags(cmdBind)
addBuildFlagsNVX(cmdBind)
}
func goBuild(src string, env []string, args ...string) error {
// The -p flag is to speed up darwin/arm builds.
// Remove when golang.org/issue/10477 is resolved.
cmd := exec.Command(
"go",
"build",
fmt.Sprintf("-p=%d", runtime.NumCPU()),
"-pkgdir="+pkgdir(env),
"-tags="+strconv.Quote(strings.Join(ctx.BuildTags, ",")),
)
if buildV {
cmd.Args = append(cmd.Args, "-v")
}
if buildI {
cmd.Args = append(cmd.Args, "-i")
}
if buildX {
cmd.Args = append(cmd.Args, "-x")
}
if buildGcflags != "" {
cmd.Args = append(cmd.Args, "-gcflags", buildGcflags)
}
if buildLdflags != "" {
cmd.Args = append(cmd.Args, "-ldflags", buildLdflags)
}
cmd.Args = append(cmd.Args, args...)
cmd.Args = append(cmd.Args, src)
cmd.Env = append([]string{}, env...)
return runCmd(cmd)
}