github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/cmd/gomobile/build.go (about)

     1  // Copyright 2015 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:generate go run gendex.go -o dex.go
     6  
     7  package main
     8  
     9  import (
    10  	"bufio"
    11  	"fmt"
    12  	"go/build"
    13  	"io"
    14  	"os"
    15  	"os/exec"
    16  	"regexp"
    17  	"runtime"
    18  	"strconv"
    19  	"strings"
    20  )
    21  
    22  var ctx = build.Default
    23  var pkg *build.Package // TODO(crawshaw): remove global pkg variable
    24  var tmpdir string
    25  
    26  var cmdBuild = &command{
    27  	run:   runBuild,
    28  	Name:  "build",
    29  	Usage: "[-target android|ios] [-o output] [build flags] [package]",
    30  	Short: "compile android APK and iOS app",
    31  	Long: `
    32  Build compiles and encodes the app named by the import path.
    33  
    34  The named package must define a main function.
    35  
    36  The -target flag takes a target system name, either android (the
    37  default) or ios.
    38  
    39  For -target android, if an AndroidManifest.xml is defined in the
    40  package directory, it is added to the APK output. Otherwise, a default
    41  manifest is generated.
    42  
    43  For -target ios, gomobile must be run on an OS X machine with Xcode
    44  installed. Support is not complete.
    45  
    46  If the package directory contains an assets subdirectory, its contents
    47  are copied into the output.
    48  
    49  The -o flag specifies the output file name. If not specified, the
    50  output file name depends on the package built.
    51  
    52  The -v flag provides verbose output, including the list of packages built.
    53  
    54  The build flags -a, -i, -n, -x, -gcflags, -ldflags, -tags, and -work are
    55  shared with the build command. For documentation, see 'go help build'.
    56  `,
    57  }
    58  
    59  func runBuild(cmd *command) (err error) {
    60  	cleanup, err := buildEnvInit()
    61  	if err != nil {
    62  		return err
    63  	}
    64  	defer cleanup()
    65  
    66  	args := cmd.flag.Args()
    67  
    68  	ctx.GOARCH = "arm"
    69  	switch buildTarget {
    70  	case "android":
    71  		ctx.GOOS = "android"
    72  	case "ios":
    73  		ctx.GOOS = "darwin"
    74  	default:
    75  		return fmt.Errorf(`unknown -target, %q.`, buildTarget)
    76  	}
    77  
    78  	switch len(args) {
    79  	case 0:
    80  		pkg, err = ctx.ImportDir(cwd, build.ImportComment)
    81  	case 1:
    82  		pkg, err = ctx.Import(args[0], cwd, build.ImportComment)
    83  	default:
    84  		cmd.usage()
    85  		os.Exit(1)
    86  	}
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	if pkg.Name != "main" && buildO != "" {
    92  		return fmt.Errorf("cannot set -o when building non-main package")
    93  	}
    94  
    95  	var nmpkgs map[string]bool
    96  	switch buildTarget {
    97  	case "android":
    98  		if pkg.Name != "main" {
    99  			return goBuild(pkg.ImportPath, androidArmEnv)
   100  		}
   101  		nmpkgs, err = goAndroidBuild(pkg)
   102  		if err != nil {
   103  			return err
   104  		}
   105  	case "ios":
   106  		if runtime.GOOS != "darwin" {
   107  			return fmt.Errorf("-target=ios requires darwin host")
   108  		}
   109  		if pkg.Name != "main" {
   110  			if err := goBuild(pkg.ImportPath, darwinArmEnv); err != nil {
   111  				return err
   112  			}
   113  			return goBuild(pkg.ImportPath, darwinArm64Env)
   114  		}
   115  		nmpkgs, err = goIOSBuild(pkg)
   116  		if err != nil {
   117  			return err
   118  		}
   119  	}
   120  
   121  	if !nmpkgs["github.com/c-darwin/mobile/app"] {
   122  		return fmt.Errorf(`%s does not import "github.com/c-darwin/mobile/app"`, pkg.ImportPath)
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  var nmRE = regexp.MustCompile(`[0-9a-f]{8} t (github.com/democratic-coin/dcoin-go/vendor/src/golang.org/x.*/[^.]*)`)
   129  
   130  func extractPkgs(nm string, path string) (map[string]bool, error) {
   131  	if buildN {
   132  		return map[string]bool{"github.com/c-darwin/mobile/app": true}, nil
   133  	}
   134  	r, w := io.Pipe()
   135  	cmd := exec.Command(nm, path)
   136  	cmd.Stdout = w
   137  	cmd.Stderr = os.Stderr
   138  
   139  	nmpkgs := make(map[string]bool)
   140  	errc := make(chan error, 1)
   141  	go func() {
   142  		s := bufio.NewScanner(r)
   143  		for s.Scan() {
   144  			if res := nmRE.FindStringSubmatch(s.Text()); res != nil {
   145  				nmpkgs[res[1]] = true
   146  			}
   147  		}
   148  		errc <- s.Err()
   149  	}()
   150  
   151  	err := cmd.Run()
   152  	w.Close()
   153  	if err != nil {
   154  		return nil, fmt.Errorf("%s %s: %v", nm, path, err)
   155  	}
   156  	if err := <-errc; err != nil {
   157  		return nil, fmt.Errorf("%s %s: %v", nm, path, err)
   158  	}
   159  	return nmpkgs, nil
   160  }
   161  
   162  func importsApp(pkg *build.Package) error {
   163  	// Building a program, make sure it is appropriate for mobile.
   164  	for _, path := range pkg.Imports {
   165  		if path == "github.com/c-darwin/mobile/app" {
   166  			return nil
   167  		}
   168  	}
   169  	return fmt.Errorf(`%s does not import "github.com/c-darwin/mobile/app"`, pkg.ImportPath)
   170  }
   171  
   172  var xout io.Writer = os.Stderr
   173  
   174  func printcmd(format string, args ...interface{}) {
   175  	cmd := fmt.Sprintf(format+"\n", args...)
   176  	if tmpdir != "" {
   177  		cmd = strings.Replace(cmd, tmpdir, "$WORK", -1)
   178  	}
   179  	if androidHome := os.Getenv("ANDROID_HOME"); androidHome != "" {
   180  		cmd = strings.Replace(cmd, androidHome, "$ANDROID_HOME", -1)
   181  	}
   182  	if gomobilepath != "" {
   183  		cmd = strings.Replace(cmd, gomobilepath, "$GOMOBILE", -1)
   184  	}
   185  	if goroot := goEnv("GOROOT"); goroot != "" {
   186  		cmd = strings.Replace(cmd, goroot, "$GOROOT", -1)
   187  	}
   188  	if gopath := goEnv("GOPATH"); gopath != "" {
   189  		cmd = strings.Replace(cmd, gopath, "$GOPATH", -1)
   190  	}
   191  	if env := os.Getenv("HOME"); env != "" {
   192  		cmd = strings.Replace(cmd, env, "$HOME", -1)
   193  	}
   194  	if env := os.Getenv("HOMEPATH"); env != "" {
   195  		cmd = strings.Replace(cmd, env, "$HOMEPATH", -1)
   196  	}
   197  	fmt.Fprint(xout, cmd)
   198  }
   199  
   200  // "Build flags", used by multiple commands.
   201  var (
   202  	buildA       bool   // -a
   203  	buildI       bool   // -i
   204  	buildN       bool   // -n
   205  	buildV       bool   // -v
   206  	buildX       bool   // -x
   207  	buildO       string // -o
   208  	buildGcflags string // -gcflags
   209  	buildLdflags string // -ldflags
   210  	buildTarget  string // -target
   211  	productName  string // -name
   212  	buildWork    bool   // -work
   213  )
   214  
   215  func addBuildFlags(cmd *command) {
   216  	cmd.flag.StringVar(&buildO, "o", "", "")
   217  	cmd.flag.StringVar(&buildGcflags, "gcflags", "", "")
   218  	cmd.flag.StringVar(&buildLdflags, "ldflags", "", "")
   219  	cmd.flag.StringVar(&buildTarget, "target", "android", "")
   220  	cmd.flag.StringVar(&productName, "name", "ProductName", "")
   221  
   222  	cmd.flag.BoolVar(&buildA, "a", false, "")
   223  	cmd.flag.BoolVar(&buildI, "i", false, "")
   224  	cmd.flag.Var((*stringsFlag)(&ctx.BuildTags), "tags", "")
   225  }
   226  
   227  func addBuildFlagsNVXWork(cmd *command) {
   228  	cmd.flag.BoolVar(&buildN, "n", false, "")
   229  	cmd.flag.BoolVar(&buildV, "v", false, "")
   230  	cmd.flag.BoolVar(&buildX, "x", false, "")
   231  	cmd.flag.BoolVar(&buildWork, "work", false, "")
   232  }
   233  
   234  type binInfo struct {
   235  	hasPkgApp bool
   236  	hasPkgAL  bool
   237  }
   238  
   239  func init() {
   240  	addBuildFlags(cmdBuild)
   241  	addBuildFlagsNVXWork(cmdBuild)
   242  
   243  	addBuildFlags(cmdInstall)
   244  	addBuildFlagsNVXWork(cmdInstall)
   245  
   246  	addBuildFlagsNVXWork(cmdInit)
   247  
   248  	addBuildFlags(cmdBind)
   249  	addBuildFlagsNVXWork(cmdBind)
   250  }
   251  
   252  func goBuild(src string, env []string, args ...string) error {
   253  	// The -p flag is to speed up darwin/arm builds.
   254  	// Remove when golang.org/issue/10477 is resolved.
   255  	cmd := exec.Command(
   256  		"go",
   257  		"build",
   258  		fmt.Sprintf("-p=%d", runtime.NumCPU()),
   259  		"-pkgdir="+pkgdir(env),
   260  		"-tags="+strconv.Quote(strings.Join(ctx.BuildTags, ",")),
   261  	)
   262  	if buildV {
   263  		cmd.Args = append(cmd.Args, "-v")
   264  	}
   265  	if buildI {
   266  		cmd.Args = append(cmd.Args, "-i")
   267  	}
   268  	if buildX {
   269  		cmd.Args = append(cmd.Args, "-x")
   270  	}
   271  	if buildGcflags != "" {
   272  		cmd.Args = append(cmd.Args, "-gcflags", buildGcflags)
   273  	}
   274  	if buildLdflags != "" {
   275  		cmd.Args = append(cmd.Args, "-ldflags", buildLdflags)
   276  	}
   277  	if buildWork {
   278  		cmd.Args = append(cmd.Args, "-work")
   279  	}
   280  	cmd.Args = append(cmd.Args, args...)
   281  	cmd.Args = append(cmd.Args, src)
   282  	cmd.Env = append([]string{}, env...)
   283  	return runCmd(cmd)
   284  }