1// Copyright 2017 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package build 16 17import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 "runtime" 22 "strconv" 23 "strings" 24 "time" 25 26 "android/soong/shared" 27 28 "github.com/golang/protobuf/proto" 29 30 smpb "android/soong/ui/metrics/metrics_proto" 31) 32 33type Config struct{ *configImpl } 34 35type configImpl struct { 36 // From the environment 37 arguments []string 38 goma bool 39 environ *Environment 40 distDir string 41 buildDateTime string 42 43 // From the arguments 44 parallel int 45 keepGoing int 46 verbose bool 47 checkbuild bool 48 dist bool 49 skipMake bool 50 51 // From the product config 52 katiArgs []string 53 ninjaArgs []string 54 katiSuffix string 55 targetDevice string 56 targetDeviceDir string 57 58 // Autodetected 59 totalRAM uint64 60 61 pdkBuild bool 62 63 brokenDupRules bool 64 brokenUsesNetwork bool 65 brokenNinjaEnvVars []string 66 67 pathReplaced bool 68} 69 70const srcDirFileCheck = "build/soong/root.bp" 71 72var buildFiles = []string{"Android.mk", "Android.bp"} 73 74type BuildAction uint 75 76const ( 77 // Builds all of the modules and their dependencies of a specified directory, relative to the root 78 // directory of the source tree. 79 BUILD_MODULES_IN_A_DIRECTORY BuildAction = iota 80 81 // Builds all of the modules and their dependencies of a list of specified directories. All specified 82 // directories are relative to the root directory of the source tree. 83 BUILD_MODULES_IN_DIRECTORIES 84 85 // Build a list of specified modules. If none was specified, simply build the whole source tree. 86 BUILD_MODULES 87) 88 89// checkTopDir validates that the current directory is at the root directory of the source tree. 90func checkTopDir(ctx Context) { 91 if _, err := os.Stat(srcDirFileCheck); err != nil { 92 if os.IsNotExist(err) { 93 ctx.Fatalf("Current working directory must be the source tree. %q not found.", srcDirFileCheck) 94 } 95 ctx.Fatalln("Error verifying tree state:", err) 96 } 97} 98 99func NewConfig(ctx Context, args ...string) Config { 100 ret := &configImpl{ 101 environ: OsEnvironment(), 102 } 103 104 // Sane default matching ninja 105 ret.parallel = runtime.NumCPU() + 2 106 ret.keepGoing = 1 107 108 ret.totalRAM = detectTotalRAM(ctx) 109 110 ret.parseArgs(ctx, args) 111 112 // Make sure OUT_DIR is set appropriately 113 if outDir, ok := ret.environ.Get("OUT_DIR"); ok { 114 ret.environ.Set("OUT_DIR", filepath.Clean(outDir)) 115 } else { 116 outDir := "out" 117 if baseDir, ok := ret.environ.Get("OUT_DIR_COMMON_BASE"); ok { 118 if wd, err := os.Getwd(); err != nil { 119 ctx.Fatalln("Failed to get working directory:", err) 120 } else { 121 outDir = filepath.Join(baseDir, filepath.Base(wd)) 122 } 123 } 124 ret.environ.Set("OUT_DIR", outDir) 125 } 126 127 if distDir, ok := ret.environ.Get("DIST_DIR"); ok { 128 ret.distDir = filepath.Clean(distDir) 129 } else { 130 ret.distDir = filepath.Join(ret.OutDir(), "dist") 131 } 132 133 ret.environ.Unset( 134 // We're already using it 135 "USE_SOONG_UI", 136 137 // We should never use GOROOT/GOPATH from the shell environment 138 "GOROOT", 139 "GOPATH", 140 141 // These should only come from Soong, not the environment. 142 "CLANG", 143 "CLANG_CXX", 144 "CCC_CC", 145 "CCC_CXX", 146 147 // Used by the goma compiler wrapper, but should only be set by 148 // gomacc 149 "GOMACC_PATH", 150 151 // We handle this above 152 "OUT_DIR_COMMON_BASE", 153 154 // This is handled above too, and set for individual commands later 155 "DIST_DIR", 156 157 // Variables that have caused problems in the past 158 "BASH_ENV", 159 "CDPATH", 160 "DISPLAY", 161 "GREP_OPTIONS", 162 "NDK_ROOT", 163 "POSIXLY_CORRECT", 164 165 // Drop make flags 166 "MAKEFLAGS", 167 "MAKELEVEL", 168 "MFLAGS", 169 170 // Set in envsetup.sh, reset in makefiles 171 "ANDROID_JAVA_TOOLCHAIN", 172 173 // Set by envsetup.sh, but shouldn't be used inside the build because envsetup.sh is optional 174 "ANDROID_BUILD_TOP", 175 "ANDROID_HOST_OUT", 176 "ANDROID_PRODUCT_OUT", 177 "ANDROID_HOST_OUT_TESTCASES", 178 "ANDROID_TARGET_OUT_TESTCASES", 179 "ANDROID_TOOLCHAIN", 180 "ANDROID_TOOLCHAIN_2ND_ARCH", 181 "ANDROID_DEV_SCRIPTS", 182 "ANDROID_EMULATOR_PREBUILTS", 183 "ANDROID_PRE_BUILD_PATHS", 184 185 // Only set in multiproduct_kati after config generation 186 "EMPTY_NINJA_FILE", 187 ) 188 189 if ret.UseGoma() || ret.ForceUseGoma() { 190 ctx.Println("Goma for Android has been deprecated and replaced with RBE. See go/rbe_for_android for instructions on how to use RBE.") 191 ctx.Fatalln("USE_GOMA / FORCE_USE_GOMA flag is no longer supported.") 192 } 193 194 // Tell python not to spam the source tree with .pyc files. 195 ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1") 196 197 tmpDir := absPath(ctx, ret.TempDir()) 198 ret.environ.Set("TMPDIR", tmpDir) 199 200 // Always set ASAN_SYMBOLIZER_PATH so that ASAN-based tools can symbolize any crashes 201 symbolizerPath := filepath.Join("prebuilts/clang/host", ret.HostPrebuiltTag(), 202 "llvm-binutils-stable/llvm-symbolizer") 203 ret.environ.Set("ASAN_SYMBOLIZER_PATH", absPath(ctx, symbolizerPath)) 204 205 // Precondition: the current directory is the top of the source tree 206 checkTopDir(ctx) 207 208 if srcDir := absPath(ctx, "."); strings.ContainsRune(srcDir, ' ') { 209 ctx.Println("You are building in a directory whose absolute path contains a space character:") 210 ctx.Println() 211 ctx.Printf("%q\n", srcDir) 212 ctx.Println() 213 ctx.Fatalln("Directory names containing spaces are not supported") 214 } 215 216 if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') { 217 ctx.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:") 218 ctx.Println() 219 ctx.Printf("%q\n", outDir) 220 ctx.Println() 221 ctx.Fatalln("Directory names containing spaces are not supported") 222 } 223 224 if distDir := ret.DistDir(); strings.ContainsRune(distDir, ' ') { 225 ctx.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:") 226 ctx.Println() 227 ctx.Printf("%q\n", distDir) 228 ctx.Println() 229 ctx.Fatalln("Directory names containing spaces are not supported") 230 } 231 232 // Configure Java-related variables, including adding it to $PATH 233 java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag()) 234 java9Home := filepath.Join("prebuilts/jdk/jdk9", ret.HostPrebuiltTag()) 235 java11Home := filepath.Join("prebuilts/jdk/jdk11", ret.HostPrebuiltTag()) 236 javaHome := func() string { 237 if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok { 238 return override 239 } 240 if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" { 241 ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.") 242 } 243 return java11Home 244 }() 245 absJavaHome := absPath(ctx, javaHome) 246 247 ret.configureLocale(ctx) 248 249 newPath := []string{filepath.Join(absJavaHome, "bin")} 250 if path, ok := ret.environ.Get("PATH"); ok && path != "" { 251 newPath = append(newPath, path) 252 } 253 254 ret.environ.Unset("OVERRIDE_ANDROID_JAVA_HOME") 255 ret.environ.Set("JAVA_HOME", absJavaHome) 256 ret.environ.Set("ANDROID_JAVA_HOME", javaHome) 257 ret.environ.Set("ANDROID_JAVA8_HOME", java8Home) 258 ret.environ.Set("ANDROID_JAVA9_HOME", java9Home) 259 ret.environ.Set("ANDROID_JAVA11_HOME", java11Home) 260 ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator))) 261 262 outDir := ret.OutDir() 263 buildDateTimeFile := filepath.Join(outDir, "build_date.txt") 264 if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" { 265 ret.buildDateTime = buildDateTime 266 } else { 267 ret.buildDateTime = strconv.FormatInt(time.Now().Unix(), 10) 268 } 269 270 ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile) 271 272 if ret.UseRBE() { 273 for k, v := range getRBEVars(ctx, Config{ret}) { 274 ret.environ.Set(k, v) 275 } 276 } 277 278 c := Config{ret} 279 storeConfigMetrics(ctx, c) 280 return c 281} 282 283// NewBuildActionConfig returns a build configuration based on the build action. The arguments are 284// processed based on the build action and extracts any arguments that belongs to the build action. 285func NewBuildActionConfig(action BuildAction, dir string, ctx Context, args ...string) Config { 286 return NewConfig(ctx, getConfigArgs(action, dir, ctx, args)...) 287} 288 289// storeConfigMetrics selects a set of configuration information and store in 290// the metrics system for further analysis. 291func storeConfigMetrics(ctx Context, config Config) { 292 if ctx.Metrics == nil { 293 return 294 } 295 296 b := &smpb.BuildConfig{ 297 ForceUseGoma: proto.Bool(config.ForceUseGoma()), 298 UseGoma: proto.Bool(config.UseGoma()), 299 UseRbe: proto.Bool(config.UseRBE()), 300 } 301 ctx.Metrics.BuildConfig(b) 302} 303 304// getConfigArgs processes the command arguments based on the build action and creates a set of new 305// arguments to be accepted by Config. 306func getConfigArgs(action BuildAction, dir string, ctx Context, args []string) []string { 307 // The next block of code verifies that the current directory is the root directory of the source 308 // tree. It then finds the relative path of dir based on the root directory of the source tree 309 // and verify that dir is inside of the source tree. 310 checkTopDir(ctx) 311 topDir, err := os.Getwd() 312 if err != nil { 313 ctx.Fatalf("Error retrieving top directory: %v", err) 314 } 315 dir, err = filepath.EvalSymlinks(dir) 316 if err != nil { 317 ctx.Fatalf("Unable to evaluate symlink of %s: %v", dir, err) 318 } 319 dir, err = filepath.Abs(dir) 320 if err != nil { 321 ctx.Fatalf("Unable to find absolute path %s: %v", dir, err) 322 } 323 relDir, err := filepath.Rel(topDir, dir) 324 if err != nil { 325 ctx.Fatalf("Unable to find relative path %s of %s: %v", relDir, topDir, err) 326 } 327 // If there are ".." in the path, it's not in the source tree. 328 if strings.Contains(relDir, "..") { 329 ctx.Fatalf("Directory %s is not under the source tree %s", dir, topDir) 330 } 331 332 configArgs := args[:] 333 334 // If the arguments contains GET-INSTALL-PATH, change the target name prefix from MODULES-IN- to 335 // GET-INSTALL-PATH-IN- to extract the installation path instead of building the modules. 336 targetNamePrefix := "MODULES-IN-" 337 if inList("GET-INSTALL-PATH", configArgs) { 338 targetNamePrefix = "GET-INSTALL-PATH-IN-" 339 configArgs = removeFromList("GET-INSTALL-PATH", configArgs) 340 } 341 342 var targets []string 343 344 switch action { 345 case BUILD_MODULES: 346 // No additional processing is required when building a list of specific modules or all modules. 347 case BUILD_MODULES_IN_A_DIRECTORY: 348 // If dir is the root source tree, all the modules are built of the source tree are built so 349 // no need to find the build file. 350 if topDir == dir { 351 break 352 } 353 354 buildFile := findBuildFile(ctx, relDir) 355 if buildFile == "" { 356 ctx.Fatalf("Build file not found for %s directory", relDir) 357 } 358 targets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)} 359 case BUILD_MODULES_IN_DIRECTORIES: 360 newConfigArgs, dirs := splitArgs(configArgs) 361 configArgs = newConfigArgs 362 targets = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix) 363 } 364 365 // Tidy only override all other specified targets. 366 tidyOnly := os.Getenv("WITH_TIDY_ONLY") 367 if tidyOnly == "true" || tidyOnly == "1" { 368 configArgs = append(configArgs, "tidy_only") 369 } else { 370 configArgs = append(configArgs, targets...) 371 } 372 373 return configArgs 374} 375 376// convertToTarget replaces "/" to "-" in dir and pre-append the targetNamePrefix to the target name. 377func convertToTarget(dir string, targetNamePrefix string) string { 378 return targetNamePrefix + strings.ReplaceAll(dir, "/", "-") 379} 380 381// hasBuildFile returns true if dir contains an Android build file. 382func hasBuildFile(ctx Context, dir string) bool { 383 for _, buildFile := range buildFiles { 384 _, err := os.Stat(filepath.Join(dir, buildFile)) 385 if err == nil { 386 return true 387 } 388 if !os.IsNotExist(err) { 389 ctx.Fatalf("Error retrieving the build file stats: %v", err) 390 } 391 } 392 return false 393} 394 395// findBuildFile finds a build file (makefile or blueprint file) by looking if there is a build file 396// in the current and any sub directory of dir. If a build file is not found, traverse the path 397// up by one directory and repeat again until either a build file is found or reached to the root 398// source tree. The returned filename of build file is "Android.mk". If one was not found, a blank 399// string is returned. 400func findBuildFile(ctx Context, dir string) string { 401 // If the string is empty or ".", assume it is top directory of the source tree. 402 if dir == "" || dir == "." { 403 return "" 404 } 405 406 found := false 407 for buildDir := dir; buildDir != "."; buildDir = filepath.Dir(buildDir) { 408 err := filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error { 409 if err != nil { 410 return err 411 } 412 if found { 413 return filepath.SkipDir 414 } 415 if info.IsDir() { 416 return nil 417 } 418 for _, buildFile := range buildFiles { 419 if info.Name() == buildFile { 420 found = true 421 return filepath.SkipDir 422 } 423 } 424 return nil 425 }) 426 if err != nil { 427 ctx.Fatalf("Error finding Android build file: %v", err) 428 } 429 430 if found { 431 return filepath.Join(buildDir, "Android.mk") 432 } 433 } 434 435 return "" 436} 437 438// splitArgs iterates over the arguments list and splits into two lists: arguments and directories. 439func splitArgs(args []string) (newArgs []string, dirs []string) { 440 specialArgs := map[string]bool{ 441 "showcommands": true, 442 "snod": true, 443 "dist": true, 444 "checkbuild": true, 445 } 446 447 newArgs = []string{} 448 dirs = []string{} 449 450 for _, arg := range args { 451 // It's a dash argument if it starts with "-" or it's a key=value pair, it's not a directory. 452 if strings.IndexRune(arg, '-') == 0 || strings.IndexRune(arg, '=') != -1 { 453 newArgs = append(newArgs, arg) 454 continue 455 } 456 457 if _, ok := specialArgs[arg]; ok { 458 newArgs = append(newArgs, arg) 459 continue 460 } 461 462 dirs = append(dirs, arg) 463 } 464 465 return newArgs, dirs 466} 467 468// getTargetsFromDirs iterates over the dirs list and creates a list of targets to build. If a 469// directory from the dirs list does not exist, a fatal error is raised. relDir is related to the 470// source root tree where the build action command was invoked. Each directory is validated if the 471// build file can be found and follows the format "dir1:target1,target2,...". Target is optional. 472func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string) { 473 for _, dir := range dirs { 474 // The directory may have specified specific modules to build. ":" is the separator to separate 475 // the directory and the list of modules. 476 s := strings.Split(dir, ":") 477 l := len(s) 478 if l > 2 { // more than one ":" was specified. 479 ctx.Fatalf("%s not in proper directory:target1,target2,... format (\":\" was specified more than once)", dir) 480 } 481 482 dir = filepath.Join(relDir, s[0]) 483 if _, err := os.Stat(dir); err != nil { 484 ctx.Fatalf("couldn't find directory %s", dir) 485 } 486 487 // Verify that if there are any targets specified after ":". Each target is separated by ",". 488 var newTargets []string 489 if l == 2 && s[1] != "" { 490 newTargets = strings.Split(s[1], ",") 491 if inList("", newTargets) { 492 ctx.Fatalf("%s not in proper directory:target1,target2,... format", dir) 493 } 494 } 495 496 // If there are specified targets to build in dir, an android build file must exist for the one 497 // shot build. For the non-targets case, find the appropriate build file and build all the 498 // modules in dir (or the closest one in the dir path). 499 if len(newTargets) > 0 { 500 if !hasBuildFile(ctx, dir) { 501 ctx.Fatalf("Couldn't locate a build file from %s directory", dir) 502 } 503 } else { 504 buildFile := findBuildFile(ctx, dir) 505 if buildFile == "" { 506 ctx.Fatalf("Build file not found for %s directory", dir) 507 } 508 newTargets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)} 509 } 510 511 targets = append(targets, newTargets...) 512 } 513 514 return targets 515} 516 517func (c *configImpl) parseArgs(ctx Context, args []string) { 518 for i := 0; i < len(args); i++ { 519 arg := strings.TrimSpace(args[i]) 520 if arg == "--make-mode" { 521 } else if arg == "showcommands" { 522 c.verbose = true 523 } else if arg == "--skip-make" { 524 c.skipMake = true 525 } else if len(arg) > 0 && arg[0] == '-' { 526 parseArgNum := func(def int) int { 527 if len(arg) > 2 { 528 p, err := strconv.ParseUint(arg[2:], 10, 31) 529 if err != nil { 530 ctx.Fatalf("Failed to parse %q: %v", arg, err) 531 } 532 return int(p) 533 } else if i+1 < len(args) { 534 p, err := strconv.ParseUint(args[i+1], 10, 31) 535 if err == nil { 536 i++ 537 return int(p) 538 } 539 } 540 return def 541 } 542 543 if len(arg) > 1 && arg[1] == 'j' { 544 c.parallel = parseArgNum(c.parallel) 545 } else if len(arg) > 1 && arg[1] == 'k' { 546 c.keepGoing = parseArgNum(0) 547 } else { 548 ctx.Fatalln("Unknown option:", arg) 549 } 550 } else if k, v, ok := decodeKeyValue(arg); ok && len(k) > 0 { 551 c.environ.Set(k, v) 552 } else if arg == "dist" { 553 c.dist = true 554 } else { 555 if arg == "checkbuild" { 556 c.checkbuild = true 557 } 558 c.arguments = append(c.arguments, arg) 559 } 560 } 561} 562 563func (c *configImpl) configureLocale(ctx Context) { 564 cmd := Command(ctx, Config{c}, "locale", "locale", "-a") 565 output, err := cmd.Output() 566 567 var locales []string 568 if err == nil { 569 locales = strings.Split(string(output), "\n") 570 } else { 571 // If we're unable to list the locales, let's assume en_US.UTF-8 572 locales = []string{"en_US.UTF-8"} 573 ctx.Verbosef("Failed to list locales (%q), falling back to %q", err, locales) 574 } 575 576 // gettext uses LANGUAGE, which is passed directly through 577 578 // For LANG and LC_*, only preserve the evaluated version of 579 // LC_MESSAGES 580 user_lang := "" 581 if lc_all, ok := c.environ.Get("LC_ALL"); ok { 582 user_lang = lc_all 583 } else if lc_messages, ok := c.environ.Get("LC_MESSAGES"); ok { 584 user_lang = lc_messages 585 } else if lang, ok := c.environ.Get("LANG"); ok { 586 user_lang = lang 587 } 588 589 c.environ.UnsetWithPrefix("LC_") 590 591 if user_lang != "" { 592 c.environ.Set("LC_MESSAGES", user_lang) 593 } 594 595 // The for LANG, use C.UTF-8 if it exists (Debian currently, proposed 596 // for others) 597 if inList("C.UTF-8", locales) { 598 c.environ.Set("LANG", "C.UTF-8") 599 } else if inList("C.utf8", locales) { 600 // These normalize to the same thing 601 c.environ.Set("LANG", "C.UTF-8") 602 } else if inList("en_US.UTF-8", locales) { 603 c.environ.Set("LANG", "en_US.UTF-8") 604 } else if inList("en_US.utf8", locales) { 605 // These normalize to the same thing 606 c.environ.Set("LANG", "en_US.UTF-8") 607 } else { 608 ctx.Fatalln("System doesn't support either C.UTF-8 or en_US.UTF-8") 609 } 610} 611 612// Lunch configures the environment for a specific product similarly to the 613// `lunch` bash function. 614func (c *configImpl) Lunch(ctx Context, product, variant string) { 615 if variant != "eng" && variant != "userdebug" && variant != "user" { 616 ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant) 617 } 618 619 c.environ.Set("TARGET_PRODUCT", product) 620 c.environ.Set("TARGET_BUILD_VARIANT", variant) 621 c.environ.Set("TARGET_BUILD_TYPE", "release") 622 c.environ.Unset("TARGET_BUILD_APPS") 623} 624 625// Tapas configures the environment to build one or more unbundled apps, 626// similarly to the `tapas` bash function. 627func (c *configImpl) Tapas(ctx Context, apps []string, arch, variant string) { 628 if len(apps) == 0 { 629 apps = []string{"all"} 630 } 631 if variant == "" { 632 variant = "eng" 633 } 634 635 if variant != "eng" && variant != "userdebug" && variant != "user" { 636 ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant) 637 } 638 639 var product string 640 switch arch { 641 case "arm", "": 642 product = "aosp_arm" 643 case "arm64": 644 product = "aosm_arm64" 645 case "mips": 646 product = "aosp_mips" 647 case "mips64": 648 product = "aosp_mips64" 649 case "x86": 650 product = "aosp_x86" 651 case "x86_64": 652 product = "aosp_x86_64" 653 default: 654 ctx.Fatalf("Invalid architecture: %q", arch) 655 } 656 657 c.environ.Set("TARGET_PRODUCT", product) 658 c.environ.Set("TARGET_BUILD_VARIANT", variant) 659 c.environ.Set("TARGET_BUILD_TYPE", "release") 660 c.environ.Set("TARGET_BUILD_APPS", strings.Join(apps, " ")) 661} 662 663func (c *configImpl) Environment() *Environment { 664 return c.environ 665} 666 667func (c *configImpl) Arguments() []string { 668 return c.arguments 669} 670 671func (c *configImpl) OutDir() string { 672 if outDir, ok := c.environ.Get("OUT_DIR"); ok { 673 return outDir 674 } 675 return "out" 676} 677 678func (c *configImpl) DistDir() string { 679 return c.distDir 680} 681 682func (c *configImpl) NinjaArgs() []string { 683 if c.skipMake { 684 return c.arguments 685 } 686 return c.ninjaArgs 687} 688 689func (c *configImpl) SoongOutDir() string { 690 return filepath.Join(c.OutDir(), "soong") 691} 692 693func (c *configImpl) TempDir() string { 694 return shared.TempDirForOutDir(c.SoongOutDir()) 695} 696 697func (c *configImpl) FileListDir() string { 698 return filepath.Join(c.OutDir(), ".module_paths") 699} 700 701func (c *configImpl) KatiSuffix() string { 702 if c.katiSuffix != "" { 703 return c.katiSuffix 704 } 705 panic("SetKatiSuffix has not been called") 706} 707 708// Checkbuild returns true if "checkbuild" was one of the build goals, which means that the 709// user is interested in additional checks at the expense of build time. 710func (c *configImpl) Checkbuild() bool { 711 return c.checkbuild 712} 713 714func (c *configImpl) Dist() bool { 715 return c.dist 716} 717 718func (c *configImpl) IsVerbose() bool { 719 return c.verbose 720} 721 722func (c *configImpl) SkipMake() bool { 723 return c.skipMake 724} 725 726func (c *configImpl) TargetProduct() string { 727 if v, ok := c.environ.Get("TARGET_PRODUCT"); ok { 728 return v 729 } 730 panic("TARGET_PRODUCT is not defined") 731} 732 733func (c *configImpl) TargetDevice() string { 734 return c.targetDevice 735} 736 737func (c *configImpl) SetTargetDevice(device string) { 738 c.targetDevice = device 739} 740 741func (c *configImpl) TargetBuildVariant() string { 742 if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok { 743 return v 744 } 745 panic("TARGET_BUILD_VARIANT is not defined") 746} 747 748func (c *configImpl) KatiArgs() []string { 749 return c.katiArgs 750} 751 752func (c *configImpl) Parallel() int { 753 return c.parallel 754} 755 756func (c *configImpl) HighmemParallel() int { 757 if i, ok := c.environ.GetInt("NINJA_HIGHMEM_NUM_JOBS"); ok { 758 return i 759 } 760 761 const minMemPerHighmemProcess = 8 * 1024 * 1024 * 1024 762 parallel := c.Parallel() 763 if c.UseRemoteBuild() { 764 // Ninja doesn't support nested pools, and when remote builds are enabled the total ninja parallelism 765 // is set very high (i.e. 500). Using a large value here would cause the total number of running jobs 766 // to be the sum of the sizes of the local and highmem pools, which will cause extra CPU contention. 767 // Return 1/16th of the size of the local pool, rounding up. 768 return (parallel + 15) / 16 769 } else if c.totalRAM == 0 { 770 // Couldn't detect the total RAM, don't restrict highmem processes. 771 return parallel 772 } else if c.totalRAM <= 16*1024*1024*1024 { 773 // Less than 16GB of ram, restrict to 1 highmem processes 774 return 1 775 } else if c.totalRAM <= 32*1024*1024*1024 { 776 // Less than 32GB of ram, restrict to 2 highmem processes 777 return 2 778 } else if p := int(c.totalRAM / minMemPerHighmemProcess); p < parallel { 779 // If less than 8GB total RAM per process, reduce the number of highmem processes 780 return p 781 } 782 // No restriction on highmem processes 783 return parallel 784} 785 786func (c *configImpl) TotalRAM() uint64 { 787 return c.totalRAM 788} 789 790// ForceUseGoma determines whether we should override Goma deprecation 791// and use Goma for the current build or not. 792func (c *configImpl) ForceUseGoma() bool { 793 if v, ok := c.environ.Get("FORCE_USE_GOMA"); ok { 794 v = strings.TrimSpace(v) 795 if v != "" && v != "false" { 796 return true 797 } 798 } 799 return false 800} 801 802func (c *configImpl) UseGoma() bool { 803 if v, ok := c.environ.Get("USE_GOMA"); ok { 804 v = strings.TrimSpace(v) 805 if v != "" && v != "false" { 806 return true 807 } 808 } 809 return false 810} 811 812func (c *configImpl) StartGoma() bool { 813 if !c.UseGoma() { 814 return false 815 } 816 817 if v, ok := c.environ.Get("NOSTART_GOMA"); ok { 818 v = strings.TrimSpace(v) 819 if v != "" && v != "false" { 820 return false 821 } 822 } 823 return true 824} 825 826func (c *configImpl) UseRBE() bool { 827 if v, ok := c.environ.Get("USE_RBE"); ok { 828 v = strings.TrimSpace(v) 829 if v != "" && v != "false" { 830 return true 831 } 832 } 833 return false 834} 835 836func (c *configImpl) StartRBE() bool { 837 if !c.UseRBE() { 838 return false 839 } 840 841 if v, ok := c.environ.Get("NOSTART_RBE"); ok { 842 v = strings.TrimSpace(v) 843 if v != "" && v != "false" { 844 return false 845 } 846 } 847 return true 848} 849 850func (c *configImpl) logDir() string { 851 if c.Dist() { 852 return filepath.Join(c.DistDir(), "logs") 853 } 854 return c.OutDir() 855} 856 857func (c *configImpl) rbeStatsOutputDir() string { 858 for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} { 859 if v, ok := c.environ.Get(f); ok { 860 return v 861 } 862 } 863 return c.logDir() 864} 865 866func (c *configImpl) rbeLogPath() string { 867 for _, f := range []string{"RBE_log_path", "FLAG_log_path"} { 868 if v, ok := c.environ.Get(f); ok { 869 return v 870 } 871 } 872 return fmt.Sprintf("text://%v/reproxy_log.txt", c.logDir()) 873} 874 875func (c *configImpl) rbeExecRoot() string { 876 for _, f := range []string{"RBE_exec_root", "FLAG_exec_root"} { 877 if v, ok := c.environ.Get(f); ok { 878 return v 879 } 880 } 881 wd, err := os.Getwd() 882 if err != nil { 883 return "" 884 } 885 return wd 886} 887 888func (c *configImpl) rbeDir() string { 889 if v, ok := c.environ.Get("RBE_DIR"); ok { 890 return v 891 } 892 return "prebuilts/remoteexecution-client/live/" 893} 894 895func (c *configImpl) rbeReproxy() string { 896 for _, f := range []string{"RBE_re_proxy", "FLAG_re_proxy"} { 897 if v, ok := c.environ.Get(f); ok { 898 return v 899 } 900 } 901 return filepath.Join(c.rbeDir(), "reproxy") 902} 903 904func (c *configImpl) rbeAuth() (string, string) { 905 credFlags := []string{"use_application_default_credentials", "use_gce_credentials", "credential_file"} 906 for _, cf := range credFlags { 907 for _, f := range []string{"RBE_" + cf, "FLAG_" + cf} { 908 if v, ok := c.environ.Get(f); ok { 909 v = strings.TrimSpace(v) 910 if v != "" && v != "false" && v != "0" { 911 return "RBE_" + cf, v 912 } 913 } 914 } 915 } 916 return "RBE_use_application_default_credentials", "true" 917} 918 919func (c *configImpl) UseRemoteBuild() bool { 920 return c.UseGoma() || c.UseRBE() 921} 922 923// RemoteParallel controls how many remote jobs (i.e., commands which contain 924// gomacc) are run in parallel. Note the parallelism of all other jobs is 925// still limited by Parallel() 926func (c *configImpl) RemoteParallel() int { 927 if !c.UseRemoteBuild() { 928 return 0 929 } 930 if i, ok := c.environ.GetInt("NINJA_REMOTE_NUM_JOBS"); ok { 931 return i 932 } 933 return 500 934} 935 936func (c *configImpl) SetKatiArgs(args []string) { 937 c.katiArgs = args 938} 939 940func (c *configImpl) SetNinjaArgs(args []string) { 941 c.ninjaArgs = args 942} 943 944func (c *configImpl) SetKatiSuffix(suffix string) { 945 c.katiSuffix = suffix 946} 947 948func (c *configImpl) LastKatiSuffixFile() string { 949 return filepath.Join(c.OutDir(), "last_kati_suffix") 950} 951 952func (c *configImpl) HasKatiSuffix() bool { 953 return c.katiSuffix != "" 954} 955 956func (c *configImpl) KatiEnvFile() string { 957 return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh") 958} 959 960func (c *configImpl) KatiBuildNinjaFile() string { 961 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiBuildSuffix+".ninja") 962} 963 964func (c *configImpl) KatiPackageNinjaFile() string { 965 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja") 966} 967 968func (c *configImpl) SoongNinjaFile() string { 969 return filepath.Join(c.SoongOutDir(), "build.ninja") 970} 971 972func (c *configImpl) CombinedNinjaFile() string { 973 if c.katiSuffix == "" { 974 return filepath.Join(c.OutDir(), "combined.ninja") 975 } 976 return filepath.Join(c.OutDir(), "combined"+c.KatiSuffix()+".ninja") 977} 978 979func (c *configImpl) SoongAndroidMk() string { 980 return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+".mk") 981} 982 983func (c *configImpl) SoongMakeVarsMk() string { 984 return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk") 985} 986 987func (c *configImpl) ProductOut() string { 988 return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice()) 989} 990 991func (c *configImpl) DevicePreviousProductConfig() string { 992 return filepath.Join(c.ProductOut(), "previous_build_config.mk") 993} 994 995func (c *configImpl) KatiPackageMkDir() string { 996 return filepath.Join(c.ProductOut(), "obj", "CONFIG", "kati_packaging") 997} 998 999func (c *configImpl) hostOutRoot() string { 1000 return filepath.Join(c.OutDir(), "host") 1001} 1002 1003func (c *configImpl) HostOut() string { 1004 return filepath.Join(c.hostOutRoot(), c.HostPrebuiltTag()) 1005} 1006 1007// This probably needs to be multi-valued, so not exporting it for now 1008func (c *configImpl) hostCrossOut() string { 1009 if runtime.GOOS == "linux" { 1010 return filepath.Join(c.hostOutRoot(), "windows-x86") 1011 } else { 1012 return "" 1013 } 1014} 1015 1016func (c *configImpl) HostPrebuiltTag() string { 1017 if runtime.GOOS == "linux" { 1018 return "linux-x86" 1019 } else if runtime.GOOS == "darwin" { 1020 return "darwin-x86" 1021 } else { 1022 panic("Unsupported OS") 1023 } 1024} 1025 1026func (c *configImpl) PrebuiltBuildTool(name string) string { 1027 if v, ok := c.environ.Get("SANITIZE_HOST"); ok { 1028 if sanitize := strings.Fields(v); inList("address", sanitize) { 1029 asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name) 1030 if _, err := os.Stat(asan); err == nil { 1031 return asan 1032 } 1033 } 1034 } 1035 return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name) 1036} 1037 1038func (c *configImpl) SetBuildBrokenDupRules(val bool) { 1039 c.brokenDupRules = val 1040} 1041 1042func (c *configImpl) BuildBrokenDupRules() bool { 1043 return c.brokenDupRules 1044} 1045 1046func (c *configImpl) SetBuildBrokenUsesNetwork(val bool) { 1047 c.brokenUsesNetwork = val 1048} 1049 1050func (c *configImpl) BuildBrokenUsesNetwork() bool { 1051 return c.brokenUsesNetwork 1052} 1053 1054func (c *configImpl) SetBuildBrokenNinjaUsesEnvVars(val []string) { 1055 c.brokenNinjaEnvVars = val 1056} 1057 1058func (c *configImpl) BuildBrokenNinjaUsesEnvVars() []string { 1059 return c.brokenNinjaEnvVars 1060} 1061 1062func (c *configImpl) SetTargetDeviceDir(dir string) { 1063 c.targetDeviceDir = dir 1064} 1065 1066func (c *configImpl) TargetDeviceDir() string { 1067 return c.targetDeviceDir 1068} 1069 1070func (c *configImpl) SetPdkBuild(pdk bool) { 1071 c.pdkBuild = pdk 1072} 1073 1074func (c *configImpl) IsPdkBuild() bool { 1075 return c.pdkBuild 1076} 1077 1078func (c *configImpl) BuildDateTime() string { 1079 return c.buildDateTime 1080} 1081 1082func (c *configImpl) MetricsUploaderApp() string { 1083 if p, ok := c.environ.Get("ANDROID_ENABLE_METRICS_UPLOAD"); ok { 1084 return p 1085 } 1086 return "" 1087} 1088