2015-02-06 23:26:08 +00:00
|
|
|
// 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.
|
|
|
|
|
2015-07-08 10:58:53 -06:00
|
|
|
//go:generate go run gendex.go -o dex.go
|
|
|
|
|
2015-02-06 23:26:08 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2015-07-24 16:47:16 -04:00
|
|
|
"bufio"
|
2015-02-06 23:26:08 +00:00
|
|
|
"fmt"
|
|
|
|
"go/build"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2015-07-24 16:47:16 -04:00
|
|
|
"regexp"
|
2015-02-06 23:26:08 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var ctx = build.Default
|
2015-07-10 16:47:46 -06:00
|
|
|
var pkg *build.Package // TODO(crawshaw): remove global pkg variable
|
2015-02-12 08:23:25 -05:00
|
|
|
var tmpdir string
|
2015-02-06 23:26:08 +00:00
|
|
|
|
|
|
|
var cmdBuild = &command{
|
|
|
|
run: runBuild,
|
|
|
|
Name: "build",
|
2017-11-21 13:38:29 -05:00
|
|
|
Usage: "[-target android|ios] [-o output] [-bundleid bundleID] [build flags] [package]",
|
2015-03-26 14:21:54 -04:00
|
|
|
Short: "compile android APK and iOS app",
|
2015-02-06 23:26:08 +00:00
|
|
|
Long: `
|
|
|
|
Build compiles and encodes the app named by the import path.
|
|
|
|
|
|
|
|
The named package must define a main function.
|
|
|
|
|
2015-06-19 12:27:15 -04:00
|
|
|
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
|
2016-02-18 05:25:39 -05:00
|
|
|
manifest is generated. By default, this builds a fat APK for all supported
|
2016-03-15 12:20:10 -04:00
|
|
|
instruction sets (arm, 386, amd64, arm64). A subset of instruction sets can
|
|
|
|
be selected by specifying target type with the architecture name. E.g.
|
2016-02-18 05:25:39 -05:00
|
|
|
-target=android/arm,android/386.
|
2015-06-19 12:27:15 -04:00
|
|
|
|
|
|
|
For -target ios, gomobile must be run on an OS X machine with Xcode
|
2018-09-14 17:00:49 +02:00
|
|
|
installed.
|
2015-02-06 23:26:08 +00:00
|
|
|
|
|
|
|
If the package directory contains an assets subdirectory, its contents
|
2015-06-19 12:27:15 -04:00
|
|
|
are copied into the output.
|
2015-02-06 23:26:08 +00:00
|
|
|
|
2018-09-15 07:51:14 +00:00
|
|
|
Flag -iosversion sets the minimal version of the iOS SDK to compile against.
|
|
|
|
The default version is 6.1.
|
|
|
|
|
2019-01-07 13:23:47 +01:00
|
|
|
The -bundleid flag is required for -target ios and sets the bundle ID to use
|
|
|
|
with the app.
|
2017-11-10 14:20:07 -05:00
|
|
|
|
2015-02-11 14:51:15 -05:00
|
|
|
The -o flag specifies the output file name. If not specified, the
|
2015-06-19 12:27:15 -04:00
|
|
|
output file name depends on the package built.
|
2015-02-11 14:51:15 -05:00
|
|
|
|
|
|
|
The -v flag provides verbose output, including the list of packages built.
|
|
|
|
|
2015-07-27 16:33:08 -04:00
|
|
|
The build flags -a, -i, -n, -x, -gcflags, -ldflags, -tags, and -work are
|
|
|
|
shared with the build command. For documentation, see 'go help build'.
|
2015-02-06 23:26:08 +00:00
|
|
|
`,
|
|
|
|
}
|
|
|
|
|
2015-03-25 19:47:16 -04:00
|
|
|
func runBuild(cmd *command) (err error) {
|
2015-07-16 13:32:51 -04:00
|
|
|
cleanup, err := buildEnvInit()
|
2015-02-06 23:26:08 +00:00
|
|
|
if err != nil {
|
2015-07-10 16:47:46 -06:00
|
|
|
return err
|
2015-02-06 23:26:08 +00:00
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
defer cleanup()
|
|
|
|
|
2015-02-10 15:02:35 -05:00
|
|
|
args := cmd.flag.Args()
|
2015-02-06 23:26:08 +00:00
|
|
|
|
2015-12-11 18:36:28 -05:00
|
|
|
targetOS, targetArchs, err := parseBuildTarget(buildTarget)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf(`invalid -target=%q: %v`, buildTarget, err)
|
2015-07-24 07:04:40 -04:00
|
|
|
}
|
|
|
|
|
2018-04-03 16:24:44 +02:00
|
|
|
oldCtx := ctx
|
|
|
|
defer func() {
|
|
|
|
ctx = oldCtx
|
|
|
|
}()
|
2015-12-11 18:36:28 -05:00
|
|
|
ctx.GOARCH = targetArchs[0]
|
|
|
|
ctx.GOOS = targetOS
|
|
|
|
|
2018-06-18 22:54:52 +02:00
|
|
|
if ctx.GOOS == "darwin" {
|
|
|
|
ctx.BuildTags = append(ctx.BuildTags, "ios")
|
|
|
|
}
|
|
|
|
|
2015-02-10 15:02:35 -05:00
|
|
|
switch len(args) {
|
|
|
|
case 0:
|
2015-02-06 23:26:08 +00:00
|
|
|
pkg, err = ctx.ImportDir(cwd, build.ImportComment)
|
2015-02-10 15:02:35 -05:00
|
|
|
case 1:
|
|
|
|
pkg, err = ctx.Import(args[0], cwd, build.ImportComment)
|
2015-02-06 23:26:08 +00:00
|
|
|
default:
|
|
|
|
cmd.usage()
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-07-10 16:47:46 -06:00
|
|
|
if pkg.Name != "main" && buildO != "" {
|
|
|
|
return fmt.Errorf("cannot set -o when building non-main package")
|
2015-02-06 23:26:08 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 16:47:16 -04:00
|
|
|
var nmpkgs map[string]bool
|
2015-12-11 18:36:28 -05:00
|
|
|
switch targetOS {
|
2015-07-10 16:47:46 -06:00
|
|
|
case "android":
|
|
|
|
if pkg.Name != "main" {
|
2015-12-11 18:36:28 -05:00
|
|
|
for _, arch := range targetArchs {
|
2015-12-11 17:19:23 -05:00
|
|
|
env := androidEnv[arch]
|
|
|
|
if err := goBuild(pkg.ImportPath, env); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2015-02-12 08:23:25 -05:00
|
|
|
}
|
2015-12-11 18:36:28 -05:00
|
|
|
nmpkgs, err = goAndroidBuild(pkg, targetArchs)
|
2015-07-24 16:47:16 -04:00
|
|
|
if err != nil {
|
2015-02-12 08:23:25 -05:00
|
|
|
return err
|
|
|
|
}
|
2015-12-18 12:04:02 -05:00
|
|
|
case "darwin":
|
2017-01-27 18:29:22 +01:00
|
|
|
if !xcodeAvailable() {
|
|
|
|
return fmt.Errorf("-target=ios requires XCode")
|
2015-02-12 08:23:25 -05:00
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
if pkg.Name != "main" {
|
2018-03-28 13:37:41 +02:00
|
|
|
for _, arch := range targetArchs {
|
|
|
|
env := darwinEnv[arch]
|
|
|
|
if err := goBuild(pkg.ImportPath, env); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-05 20:58:01 -08:00
|
|
|
}
|
2018-03-28 13:37:41 +02:00
|
|
|
return nil
|
2015-02-06 23:26:08 +00:00
|
|
|
}
|
2019-01-07 13:23:47 +01:00
|
|
|
if buildBundleID == "" {
|
|
|
|
return fmt.Errorf("-target=ios requires -bundleid set")
|
|
|
|
}
|
2018-03-28 13:37:41 +02:00
|
|
|
nmpkgs, err = goIOSBuild(pkg, buildBundleID, targetArchs)
|
2015-07-24 16:47:16 -04:00
|
|
|
if err != nil {
|
2015-02-06 23:26:08 +00:00
|
|
|
return err
|
2015-07-10 16:47:46 -06:00
|
|
|
}
|
2015-02-06 23:26:08 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 16:47:16 -04:00
|
|
|
if !nmpkgs["golang.org/x/mobile/app"] {
|
|
|
|
return fmt.Errorf(`%s does not import "golang.org/x/mobile/app"`, pkg.ImportPath)
|
2015-02-12 08:23:25 -05:00
|
|
|
}
|
2015-05-14 14:33:56 -07:00
|
|
|
|
|
|
|
return nil
|
2015-07-24 16:47:16 -04:00
|
|
|
}
|
|
|
|
|
2018-03-22 21:23:17 +09:00
|
|
|
var nmRE = regexp.MustCompile(`[0-9a-f]{8} t (?:.*/vendor/)?(golang.org/x.*/[^.]*)`)
|
2015-07-24 16:47:16 -04:00
|
|
|
|
|
|
|
func extractPkgs(nm string, path string) (map[string]bool, error) {
|
|
|
|
if buildN {
|
|
|
|
return map[string]bool{"golang.org/x/mobile/app": true}, nil
|
|
|
|
}
|
|
|
|
r, w := io.Pipe()
|
|
|
|
cmd := exec.Command(nm, path)
|
|
|
|
cmd.Stdout = w
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
|
|
|
nmpkgs := make(map[string]bool)
|
|
|
|
errc := make(chan error, 1)
|
|
|
|
go func() {
|
|
|
|
s := bufio.NewScanner(r)
|
|
|
|
for s.Scan() {
|
|
|
|
if res := nmRE.FindStringSubmatch(s.Text()); res != nil {
|
|
|
|
nmpkgs[res[1]] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
errc <- s.Err()
|
|
|
|
}()
|
|
|
|
|
|
|
|
err := cmd.Run()
|
|
|
|
w.Close()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("%s %s: %v", nm, path, err)
|
|
|
|
}
|
|
|
|
if err := <-errc; err != nil {
|
|
|
|
return nil, fmt.Errorf("%s %s: %v", nm, path, err)
|
|
|
|
}
|
|
|
|
return nmpkgs, nil
|
2015-02-06 23:26:08 +00:00
|
|
|
}
|
|
|
|
|
2015-07-08 14:36:07 -06:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2015-02-16 13:47:49 -05:00
|
|
|
var xout io.Writer = os.Stderr
|
|
|
|
|
2015-02-12 08:23:25 -05:00
|
|
|
func printcmd(format string, args ...interface{}) {
|
|
|
|
cmd := fmt.Sprintf(format+"\n", args...)
|
|
|
|
if tmpdir != "" {
|
|
|
|
cmd = strings.Replace(cmd, tmpdir, "$WORK", -1)
|
|
|
|
}
|
2015-07-16 20:36:27 -04:00
|
|
|
if androidHome := os.Getenv("ANDROID_HOME"); androidHome != "" {
|
|
|
|
cmd = strings.Replace(cmd, androidHome, "$ANDROID_HOME", -1)
|
|
|
|
}
|
2015-05-03 10:50:35 -04:00
|
|
|
if gomobilepath != "" {
|
|
|
|
cmd = strings.Replace(cmd, gomobilepath, "$GOMOBILE", -1)
|
2015-02-12 08:23:25 -05:00
|
|
|
}
|
2015-02-26 16:28:36 -05:00
|
|
|
if goroot := goEnv("GOROOT"); goroot != "" {
|
|
|
|
cmd = strings.Replace(cmd, goroot, "$GOROOT", -1)
|
|
|
|
}
|
2015-06-19 14:07:40 -04:00
|
|
|
if gopath := goEnv("GOPATH"); gopath != "" {
|
|
|
|
cmd = strings.Replace(cmd, gopath, "$GOPATH", -1)
|
|
|
|
}
|
2015-02-16 13:47:49 -05:00
|
|
|
if env := os.Getenv("HOME"); env != "" {
|
|
|
|
cmd = strings.Replace(cmd, env, "$HOME", -1)
|
|
|
|
}
|
2015-04-20 17:11:20 -04:00
|
|
|
if env := os.Getenv("HOMEPATH"); env != "" {
|
|
|
|
cmd = strings.Replace(cmd, env, "$HOMEPATH", -1)
|
|
|
|
}
|
2015-02-16 13:47:49 -05:00
|
|
|
fmt.Fprint(xout, cmd)
|
2015-02-12 08:23:25 -05:00
|
|
|
}
|
|
|
|
|
2015-02-10 15:02:35 -05:00
|
|
|
// "Build flags", used by multiple commands.
|
|
|
|
var (
|
2018-09-15 07:51:14 +00:00
|
|
|
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
|
|
|
|
buildWork bool // -work
|
|
|
|
buildBundleID string // -bundleid
|
|
|
|
buildIOSVersion string // -iosversion
|
2015-02-10 15:02:35 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func addBuildFlags(cmd *command) {
|
2015-06-24 15:15:51 -07:00
|
|
|
cmd.flag.StringVar(&buildO, "o", "", "")
|
2015-07-16 18:10:19 -04:00
|
|
|
cmd.flag.StringVar(&buildGcflags, "gcflags", "", "")
|
|
|
|
cmd.flag.StringVar(&buildLdflags, "ldflags", "", "")
|
2015-06-24 15:15:51 -07:00
|
|
|
cmd.flag.StringVar(&buildTarget, "target", "android", "")
|
2019-01-07 13:23:47 +01:00
|
|
|
cmd.flag.StringVar(&buildBundleID, "bundleid", "", "")
|
2018-09-15 07:51:14 +00:00
|
|
|
cmd.flag.StringVar(&buildIOSVersion, "iosversion", "6.1", "")
|
2015-06-19 12:27:15 -04:00
|
|
|
|
2015-02-10 15:02:35 -05:00
|
|
|
cmd.flag.BoolVar(&buildA, "a", false, "")
|
2015-02-11 14:51:15 -05:00
|
|
|
cmd.flag.BoolVar(&buildI, "i", false, "")
|
2015-02-10 15:02:35 -05:00
|
|
|
cmd.flag.Var((*stringsFlag)(&ctx.BuildTags), "tags", "")
|
|
|
|
}
|
|
|
|
|
2015-07-27 16:33:08 -04:00
|
|
|
func addBuildFlagsNVXWork(cmd *command) {
|
2015-02-12 08:23:25 -05:00
|
|
|
cmd.flag.BoolVar(&buildN, "n", false, "")
|
2015-02-10 15:02:35 -05:00
|
|
|
cmd.flag.BoolVar(&buildV, "v", false, "")
|
2015-02-12 08:23:25 -05:00
|
|
|
cmd.flag.BoolVar(&buildX, "x", false, "")
|
2015-07-27 16:33:08 -04:00
|
|
|
cmd.flag.BoolVar(&buildWork, "work", false, "")
|
2015-02-10 15:02:35 -05:00
|
|
|
}
|
|
|
|
|
2015-07-10 16:47:46 -06:00
|
|
|
type binInfo struct {
|
|
|
|
hasPkgApp bool
|
|
|
|
hasPkgAL bool
|
2015-03-05 20:58:01 -08:00
|
|
|
}
|
|
|
|
|
2015-02-10 15:02:35 -05:00
|
|
|
func init() {
|
|
|
|
addBuildFlags(cmdBuild)
|
2015-07-27 16:33:08 -04:00
|
|
|
addBuildFlagsNVXWork(cmdBuild)
|
2015-02-11 14:51:15 -05:00
|
|
|
|
|
|
|
addBuildFlags(cmdInstall)
|
2015-07-27 16:33:08 -04:00
|
|
|
addBuildFlagsNVXWork(cmdInstall)
|
2015-02-11 14:51:15 -05:00
|
|
|
|
2015-07-27 16:33:08 -04:00
|
|
|
addBuildFlagsNVXWork(cmdInit)
|
2015-02-11 15:23:09 -05:00
|
|
|
|
|
|
|
addBuildFlags(cmdBind)
|
2015-07-27 16:33:08 -04:00
|
|
|
addBuildFlagsNVXWork(cmdBind)
|
2016-06-14 11:11:41 -04:00
|
|
|
|
|
|
|
addBuildFlagsNVXWork(cmdClean)
|
2015-02-10 15:02:35 -05:00
|
|
|
}
|
|
|
|
|
2015-07-10 16:47:46 -06:00
|
|
|
func goBuild(src string, env []string, args ...string) error {
|
2015-11-19 10:51:29 +05:30
|
|
|
return goCmd("build", []string{src}, env, args...)
|
2015-11-13 13:57:15 -05:00
|
|
|
}
|
|
|
|
|
2015-11-19 10:51:29 +05:30
|
|
|
func goInstall(srcs []string, env []string, args ...string) error {
|
|
|
|
return goCmd("install", srcs, env, args...)
|
2015-11-13 13:57:15 -05:00
|
|
|
}
|
|
|
|
|
2015-11-19 10:51:29 +05:30
|
|
|
func goCmd(subcmd string, srcs []string, env []string, args ...string) error {
|
2015-07-10 16:47:46 -06:00
|
|
|
cmd := exec.Command(
|
2015-07-16 13:32:51 -04:00
|
|
|
"go",
|
2015-11-13 13:57:15 -05:00
|
|
|
subcmd,
|
2015-07-10 16:47:46 -06:00
|
|
|
)
|
2017-01-08 18:10:11 +01:00
|
|
|
if len(ctx.BuildTags) > 0 {
|
|
|
|
cmd.Args = append(cmd.Args, "-tags", strings.Join(ctx.BuildTags, " "))
|
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
if buildV {
|
|
|
|
cmd.Args = append(cmd.Args, "-v")
|
|
|
|
}
|
2015-11-30 14:01:47 -05:00
|
|
|
if subcmd != "install" && buildI {
|
2015-07-10 16:47:46 -06:00
|
|
|
cmd.Args = append(cmd.Args, "-i")
|
|
|
|
}
|
|
|
|
if buildX {
|
|
|
|
cmd.Args = append(cmd.Args, "-x")
|
|
|
|
}
|
2015-07-16 18:10:19 -04:00
|
|
|
if buildGcflags != "" {
|
|
|
|
cmd.Args = append(cmd.Args, "-gcflags", buildGcflags)
|
|
|
|
}
|
|
|
|
if buildLdflags != "" {
|
|
|
|
cmd.Args = append(cmd.Args, "-ldflags", buildLdflags)
|
|
|
|
}
|
2015-07-27 16:33:08 -04:00
|
|
|
if buildWork {
|
|
|
|
cmd.Args = append(cmd.Args, "-work")
|
|
|
|
}
|
2015-07-10 16:47:46 -06:00
|
|
|
cmd.Args = append(cmd.Args, args...)
|
2015-11-19 10:51:29 +05:30
|
|
|
cmd.Args = append(cmd.Args, srcs...)
|
2015-07-16 13:32:51 -04:00
|
|
|
cmd.Env = append([]string{}, env...)
|
2015-07-19 21:29:35 -04:00
|
|
|
return runCmd(cmd)
|
2015-07-10 16:47:46 -06:00
|
|
|
}
|
2015-12-11 18:36:28 -05:00
|
|
|
|
|
|
|
func parseBuildTarget(buildTarget string) (os string, archs []string, _ error) {
|
|
|
|
if buildTarget == "" {
|
|
|
|
return "", nil, fmt.Errorf(`invalid target ""`)
|
|
|
|
}
|
|
|
|
|
|
|
|
all := false
|
|
|
|
archNames := []string{}
|
|
|
|
for i, p := range strings.Split(buildTarget, ",") {
|
|
|
|
osarch := strings.SplitN(p, "/", 2) // len(osarch) > 0
|
|
|
|
if osarch[0] != "android" && osarch[0] != "ios" {
|
|
|
|
return "", nil, fmt.Errorf(`unsupported os`)
|
|
|
|
}
|
|
|
|
|
|
|
|
if i == 0 {
|
|
|
|
os = osarch[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
if os != osarch[0] {
|
|
|
|
return "", nil, fmt.Errorf(`cannot target different OSes`)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(osarch) == 1 {
|
|
|
|
all = true
|
|
|
|
} else {
|
|
|
|
archNames = append(archNames, osarch[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify all archs are supported one while deduping.
|
|
|
|
isSupported := func(arch string) bool {
|
2018-03-28 13:37:41 +02:00
|
|
|
for _, a := range allArchs {
|
2015-12-11 18:36:28 -05:00
|
|
|
if a == arch {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
seen := map[string]bool{}
|
|
|
|
for _, arch := range archNames {
|
|
|
|
if _, ok := seen[arch]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !isSupported(arch) {
|
|
|
|
return "", nil, fmt.Errorf(`unsupported arch: %q`, arch)
|
|
|
|
}
|
|
|
|
|
|
|
|
seen[arch] = true
|
|
|
|
archs = append(archs, arch)
|
|
|
|
}
|
|
|
|
|
2015-12-18 12:04:02 -05:00
|
|
|
targetOS := os
|
|
|
|
if os == "ios" {
|
|
|
|
targetOS = "darwin"
|
|
|
|
}
|
2015-12-11 18:36:28 -05:00
|
|
|
if all {
|
2018-03-28 13:37:41 +02:00
|
|
|
return targetOS, allArchs, nil
|
2015-12-11 18:36:28 -05:00
|
|
|
}
|
2015-12-18 12:04:02 -05:00
|
|
|
return targetOS, archs, nil
|
2015-12-11 18:36:28 -05:00
|
|
|
}
|