github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/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 targetOS, targetArchs, err := parseBuildTarget(buildTarget) 69 if err != nil { 70 return fmt.Errorf(`invalid -target=%q: %v`, buildTarget, err) 71 } 72 73 ctx.GOARCH = targetArchs[0] 74 ctx.GOOS = targetOS 75 76 switch len(args) { 77 case 0: 78 pkg, err = ctx.ImportDir(cwd, build.ImportComment) 79 case 1: 80 pkg, err = ctx.Import(args[0], cwd, build.ImportComment) 81 default: 82 cmd.usage() 83 os.Exit(1) 84 } 85 if err != nil { 86 return err 87 } 88 89 if pkg.Name != "main" && buildO != "" { 90 return fmt.Errorf("cannot set -o when building non-main package") 91 } 92 93 var nmpkgs map[string]bool 94 switch targetOS { 95 case "android": 96 if pkg.Name != "main" { 97 for _, arch := range targetArchs { 98 env := androidEnv[arch] 99 if err := goBuild(pkg.ImportPath, env); err != nil { 100 return err 101 } 102 } 103 return nil 104 } 105 nmpkgs, err = goAndroidBuild(pkg, targetArchs) 106 if err != nil { 107 return err 108 } 109 case "darwin": 110 // TODO: use targetArchs? 111 if runtime.GOOS != "darwin" { 112 return fmt.Errorf("-target=ios requires darwin host") 113 } 114 if pkg.Name != "main" { 115 if err := goBuild(pkg.ImportPath, darwinArmEnv); err != nil { 116 return err 117 } 118 return goBuild(pkg.ImportPath, darwinArm64Env) 119 } 120 nmpkgs, err = goIOSBuild(pkg) 121 if err != nil { 122 return err 123 } 124 } 125 126 if !nmpkgs["golang.org/x/mobile/app"] { 127 return fmt.Errorf(`%s does not import "golang.org/x/mobile/app"`, pkg.ImportPath) 128 } 129 130 return nil 131 } 132 133 var nmRE = regexp.MustCompile(`[0-9a-f]{8} t (golang.org/x.*/[^.]*)`) 134 135 func extractPkgs(nm string, path string) (map[string]bool, error) { 136 if buildN { 137 return map[string]bool{"golang.org/x/mobile/app": true}, nil 138 } 139 r, w := io.Pipe() 140 cmd := exec.Command(nm, path) 141 cmd.Stdout = w 142 cmd.Stderr = os.Stderr 143 144 nmpkgs := make(map[string]bool) 145 errc := make(chan error, 1) 146 go func() { 147 s := bufio.NewScanner(r) 148 for s.Scan() { 149 if res := nmRE.FindStringSubmatch(s.Text()); res != nil { 150 nmpkgs[res[1]] = true 151 } 152 } 153 errc <- s.Err() 154 }() 155 156 err := cmd.Run() 157 w.Close() 158 if err != nil { 159 return nil, fmt.Errorf("%s %s: %v", nm, path, err) 160 } 161 if err := <-errc; err != nil { 162 return nil, fmt.Errorf("%s %s: %v", nm, path, err) 163 } 164 return nmpkgs, nil 165 } 166 167 func importsApp(pkg *build.Package) error { 168 // Building a program, make sure it is appropriate for mobile. 169 for _, path := range pkg.Imports { 170 if path == "golang.org/x/mobile/app" { 171 return nil 172 } 173 } 174 return fmt.Errorf(`%s does not import "golang.org/x/mobile/app"`, pkg.ImportPath) 175 } 176 177 var xout io.Writer = os.Stderr 178 179 func printcmd(format string, args ...interface{}) { 180 cmd := fmt.Sprintf(format+"\n", args...) 181 if tmpdir != "" { 182 cmd = strings.Replace(cmd, tmpdir, "$WORK", -1) 183 } 184 if androidHome := os.Getenv("ANDROID_HOME"); androidHome != "" { 185 cmd = strings.Replace(cmd, androidHome, "$ANDROID_HOME", -1) 186 } 187 if gomobilepath != "" { 188 cmd = strings.Replace(cmd, gomobilepath, "$GOMOBILE", -1) 189 } 190 if goroot := goEnv("GOROOT"); goroot != "" { 191 cmd = strings.Replace(cmd, goroot, "$GOROOT", -1) 192 } 193 if gopath := goEnv("GOPATH"); gopath != "" { 194 cmd = strings.Replace(cmd, gopath, "$GOPATH", -1) 195 } 196 if env := os.Getenv("HOME"); env != "" { 197 cmd = strings.Replace(cmd, env, "$HOME", -1) 198 } 199 if env := os.Getenv("HOMEPATH"); env != "" { 200 cmd = strings.Replace(cmd, env, "$HOMEPATH", -1) 201 } 202 fmt.Fprint(xout, cmd) 203 } 204 205 // "Build flags", used by multiple commands. 206 var ( 207 buildA bool // -a 208 buildI bool // -i 209 buildN bool // -n 210 buildV bool // -v 211 buildX bool // -x 212 buildO string // -o 213 buildGcflags string // -gcflags 214 buildLdflags string // -ldflags 215 buildTarget string // -target 216 buildWork bool // -work 217 ) 218 219 func addBuildFlags(cmd *command) { 220 cmd.flag.StringVar(&buildO, "o", "", "") 221 cmd.flag.StringVar(&buildGcflags, "gcflags", "", "") 222 cmd.flag.StringVar(&buildLdflags, "ldflags", "", "") 223 cmd.flag.StringVar(&buildTarget, "target", "android", "") 224 225 cmd.flag.BoolVar(&buildA, "a", false, "") 226 cmd.flag.BoolVar(&buildI, "i", false, "") 227 cmd.flag.Var((*stringsFlag)(&ctx.BuildTags), "tags", "") 228 } 229 230 func addBuildFlagsNVXWork(cmd *command) { 231 cmd.flag.BoolVar(&buildN, "n", false, "") 232 cmd.flag.BoolVar(&buildV, "v", false, "") 233 cmd.flag.BoolVar(&buildX, "x", false, "") 234 cmd.flag.BoolVar(&buildWork, "work", false, "") 235 } 236 237 type binInfo struct { 238 hasPkgApp bool 239 hasPkgAL bool 240 } 241 242 func init() { 243 addBuildFlags(cmdBuild) 244 addBuildFlagsNVXWork(cmdBuild) 245 246 addBuildFlags(cmdInstall) 247 addBuildFlagsNVXWork(cmdInstall) 248 249 addBuildFlagsNVXWork(cmdInit) 250 251 addBuildFlags(cmdBind) 252 addBuildFlagsNVXWork(cmdBind) 253 } 254 255 func goBuild(src string, env []string, args ...string) error { 256 return goCmd("build", []string{src}, env, args...) 257 } 258 259 func goInstall(srcs []string, env []string, args ...string) error { 260 return goCmd("install", srcs, env, args...) 261 } 262 263 func goCmd(subcmd string, srcs []string, env []string, args ...string) error { 264 // The -p flag is to speed up darwin/arm builds. 265 // Remove when golang.org/issue/10477 is resolved. 266 cmd := exec.Command( 267 "go", 268 subcmd, 269 fmt.Sprintf("-p=%d", runtime.NumCPU()), 270 "-pkgdir="+pkgdir(env), 271 "-tags="+strconv.Quote(strings.Join(ctx.BuildTags, ",")), 272 ) 273 if buildV { 274 cmd.Args = append(cmd.Args, "-v") 275 } 276 if subcmd != "install" && buildI { 277 cmd.Args = append(cmd.Args, "-i") 278 } 279 if buildX { 280 cmd.Args = append(cmd.Args, "-x") 281 } 282 if buildGcflags != "" { 283 cmd.Args = append(cmd.Args, "-gcflags", buildGcflags) 284 } 285 if buildLdflags != "" { 286 cmd.Args = append(cmd.Args, "-ldflags", buildLdflags) 287 } 288 if buildWork { 289 cmd.Args = append(cmd.Args, "-work") 290 } 291 cmd.Args = append(cmd.Args, args...) 292 cmd.Args = append(cmd.Args, srcs...) 293 cmd.Env = append([]string{}, env...) 294 return runCmd(cmd) 295 } 296 297 func parseBuildTarget(buildTarget string) (os string, archs []string, _ error) { 298 if buildTarget == "" { 299 return "", nil, fmt.Errorf(`invalid target ""`) 300 } 301 302 all := false 303 archNames := []string{} 304 for i, p := range strings.Split(buildTarget, ",") { 305 osarch := strings.SplitN(p, "/", 2) // len(osarch) > 0 306 if osarch[0] != "android" && osarch[0] != "ios" { 307 return "", nil, fmt.Errorf(`unsupported os`) 308 } 309 310 if i == 0 { 311 os = osarch[0] 312 } 313 314 if os != osarch[0] { 315 return "", nil, fmt.Errorf(`cannot target different OSes`) 316 } 317 318 if len(osarch) == 1 { 319 all = true 320 } else { 321 archNames = append(archNames, osarch[1]) 322 } 323 } 324 325 // verify all archs are supported one while deduping. 326 var supported []string 327 switch os { 328 case "ios": 329 supported = []string{"arm", "arm64", "amd64"} 330 case "android": 331 for arch, tc := range ndk { 332 if tc.minGoVer <= goVersion { 333 supported = append(supported, arch) 334 } 335 } 336 } 337 338 isSupported := func(arch string) bool { 339 for _, a := range supported { 340 if a == arch { 341 return true 342 } 343 } 344 return false 345 } 346 347 seen := map[string]bool{} 348 for _, arch := range archNames { 349 if _, ok := seen[arch]; ok { 350 continue 351 } 352 if !isSupported(arch) { 353 return "", nil, fmt.Errorf(`unsupported arch: %q`, arch) 354 } 355 356 seen[arch] = true 357 archs = append(archs, arch) 358 } 359 360 targetOS := os 361 if os == "ios" { 362 targetOS = "darwin" 363 } 364 if all { 365 return targetOS, supported, nil 366 } 367 return targetOS, archs, nil 368 }