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