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 "context" 19 "encoding/json" 20 "fmt" 21 "io/ioutil" 22 "os" 23 "os/exec" 24 "path/filepath" 25 "runtime" 26 "strconv" 27 "strings" 28 "time" 29 30 "android/soong/shared" 31 32 "google.golang.org/protobuf/proto" 33 34 smpb "android/soong/ui/metrics/metrics_proto" 35) 36 37const ( 38 envConfigDir = "vendor/google/tools/soong_config" 39 jsonSuffix = "json" 40 41 configFetcher = "vendor/google/tools/soong/expconfigfetcher" 42 envConfigFetchTimeout = 10 * time.Second 43) 44 45type Config struct{ *configImpl } 46 47type configImpl struct { 48 // Some targets that are implemented in soong_build 49 // (bp2build, json-module-graph) are not here and have their own bits below. 50 arguments []string 51 goma bool 52 environ *Environment 53 distDir string 54 buildDateTime string 55 56 // From the arguments 57 parallel int 58 keepGoing int 59 verbose bool 60 checkbuild bool 61 dist bool 62 jsonModuleGraph bool 63 bp2build bool 64 queryview bool 65 reportMkMetrics bool // Collect and report mk2bp migration progress metrics. 66 soongDocs bool 67 skipConfig bool 68 skipKati bool 69 skipKatiNinja bool 70 skipSoong bool 71 skipNinja bool 72 skipSoongTests bool 73 74 // From the product config 75 katiArgs []string 76 ninjaArgs []string 77 katiSuffix string 78 targetDevice string 79 targetDeviceDir string 80 sandboxConfig *SandboxConfig 81 82 // Autodetected 83 totalRAM uint64 84 85 brokenDupRules bool 86 brokenUsesNetwork bool 87 brokenNinjaEnvVars []string 88 89 pathReplaced bool 90 91 useBazel bool 92 93 // During Bazel execution, Bazel cannot write outside OUT_DIR. 94 // So if DIST_DIR is set to an external dir (outside of OUT_DIR), we need to rig it temporarily and then migrate files at the end of the build. 95 riggedDistDirForBazel string 96 97 // Set by multiproduct_kati 98 emptyNinjaFile bool 99 100 metricsUploader string 101 102 includeTags []string 103} 104 105const srcDirFileCheck = "build/soong/root.bp" 106 107var buildFiles = []string{"Android.mk", "Android.bp"} 108 109type BuildAction uint 110 111const ( 112 // Builds all of the modules and their dependencies of a specified directory, relative to the root 113 // directory of the source tree. 114 BUILD_MODULES_IN_A_DIRECTORY BuildAction = iota 115 116 // Builds all of the modules and their dependencies of a list of specified directories. All specified 117 // directories are relative to the root directory of the source tree. 118 BUILD_MODULES_IN_DIRECTORIES 119 120 // Build a list of specified modules. If none was specified, simply build the whole source tree. 121 BUILD_MODULES 122) 123 124type bazelBuildMode int 125 126// Bazel-related build modes. 127const ( 128 // Don't use bazel at all. 129 noBazel bazelBuildMode = iota 130 131 // Generate synthetic build files and incorporate these files into a build which 132 // partially uses Bazel. Build metadata may come from Android.bp or BUILD files. 133 mixedBuild 134) 135 136// checkTopDir validates that the current directory is at the root directory of the source tree. 137func checkTopDir(ctx Context) { 138 if _, err := os.Stat(srcDirFileCheck); err != nil { 139 if os.IsNotExist(err) { 140 ctx.Fatalf("Current working directory must be the source tree. %q not found.", srcDirFileCheck) 141 } 142 ctx.Fatalln("Error verifying tree state:", err) 143 } 144} 145 146// fetchEnvConfig optionally fetches environment config from an 147// experiments system to control Soong features dynamically. 148func fetchEnvConfig(ctx Context, config *configImpl, envConfigName string) error { 149 configName := envConfigName + "." + jsonSuffix 150 expConfigFetcher := &smpb.ExpConfigFetcher{} 151 defer func() { 152 ctx.Metrics.ExpConfigFetcher(expConfigFetcher) 153 }() 154 155 s, err := os.Stat(configFetcher) 156 if err != nil { 157 if os.IsNotExist(err) { 158 return nil 159 } 160 return err 161 } 162 if s.Mode()&0111 == 0 { 163 status := smpb.ExpConfigFetcher_ERROR 164 expConfigFetcher.Status = &status 165 return fmt.Errorf("configuration fetcher binary %v is not executable: %v", configFetcher, s.Mode()) 166 } 167 168 tCtx, cancel := context.WithTimeout(ctx, envConfigFetchTimeout) 169 defer cancel() 170 fetchStart := time.Now() 171 cmd := exec.CommandContext(tCtx, configFetcher, "-output_config_dir", config.OutDir(), 172 "-output_config_name", configName) 173 if err := cmd.Start(); err != nil { 174 status := smpb.ExpConfigFetcher_ERROR 175 expConfigFetcher.Status = &status 176 return err 177 } 178 179 if err := cmd.Wait(); err != nil { 180 status := smpb.ExpConfigFetcher_ERROR 181 expConfigFetcher.Status = &status 182 return err 183 } 184 fetchEnd := time.Now() 185 expConfigFetcher.Micros = proto.Uint64(uint64(fetchEnd.Sub(fetchStart).Microseconds())) 186 outConfigFilePath := filepath.Join(config.OutDir(), configName) 187 expConfigFetcher.Filename = proto.String(outConfigFilePath) 188 if _, err := os.Stat(outConfigFilePath); err == nil { 189 status := smpb.ExpConfigFetcher_CONFIG 190 expConfigFetcher.Status = &status 191 } else { 192 status := smpb.ExpConfigFetcher_NO_CONFIG 193 expConfigFetcher.Status = &status 194 } 195 return nil 196} 197 198func loadEnvConfig(ctx Context, config *configImpl) error { 199 bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG") 200 if bc == "" { 201 return nil 202 } 203 204 if err := fetchEnvConfig(ctx, config, bc); err != nil { 205 fmt.Fprintf(os.Stderr, "Failed to fetch config file: %v\n", err) 206 } 207 208 configDirs := []string{ 209 config.OutDir(), 210 os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR"), 211 envConfigDir, 212 } 213 for _, dir := range configDirs { 214 cfgFile := filepath.Join(os.Getenv("TOP"), dir, fmt.Sprintf("%s.%s", bc, jsonSuffix)) 215 envVarsJSON, err := ioutil.ReadFile(cfgFile) 216 if err != nil { 217 continue 218 } 219 ctx.Verbosef("Loading config file %v\n", cfgFile) 220 var envVars map[string]map[string]string 221 if err := json.Unmarshal(envVarsJSON, &envVars); err != nil { 222 fmt.Fprintf(os.Stderr, "Env vars config file %s did not parse correctly: %s", cfgFile, err.Error()) 223 continue 224 } 225 for k, v := range envVars["env"] { 226 if os.Getenv(k) != "" { 227 continue 228 } 229 config.environ.Set(k, v) 230 } 231 ctx.Verbosef("Finished loading config file %v\n", cfgFile) 232 break 233 } 234 235 return nil 236} 237 238func NewConfig(ctx Context, args ...string) Config { 239 ret := &configImpl{ 240 environ: OsEnvironment(), 241 sandboxConfig: &SandboxConfig{}, 242 } 243 244 // Default matching ninja 245 ret.parallel = runtime.NumCPU() + 2 246 ret.keepGoing = 1 247 248 ret.totalRAM = detectTotalRAM(ctx) 249 250 ret.parseArgs(ctx, args) 251 252 // Make sure OUT_DIR is set appropriately 253 if outDir, ok := ret.environ.Get("OUT_DIR"); ok { 254 ret.environ.Set("OUT_DIR", filepath.Clean(outDir)) 255 } else { 256 outDir := "out" 257 if baseDir, ok := ret.environ.Get("OUT_DIR_COMMON_BASE"); ok { 258 if wd, err := os.Getwd(); err != nil { 259 ctx.Fatalln("Failed to get working directory:", err) 260 } else { 261 outDir = filepath.Join(baseDir, filepath.Base(wd)) 262 } 263 } 264 ret.environ.Set("OUT_DIR", outDir) 265 } 266 267 // loadEnvConfig needs to know what the OUT_DIR is, so it should 268 // be called after we determine the appropriate out directory. 269 if err := loadEnvConfig(ctx, ret); err != nil { 270 ctx.Fatalln("Failed to parse env config files: %v", err) 271 } 272 273 if distDir, ok := ret.environ.Get("DIST_DIR"); ok { 274 ret.distDir = filepath.Clean(distDir) 275 } else { 276 ret.distDir = filepath.Join(ret.OutDir(), "dist") 277 } 278 279 if srcDirIsWritable, ok := ret.environ.Get("BUILD_BROKEN_SRC_DIR_IS_WRITABLE"); ok { 280 ret.sandboxConfig.SetSrcDirIsRO(srcDirIsWritable == "false") 281 } 282 283 ret.environ.Unset( 284 // We're already using it 285 "USE_SOONG_UI", 286 287 // We should never use GOROOT/GOPATH from the shell environment 288 "GOROOT", 289 "GOPATH", 290 291 // These should only come from Soong, not the environment. 292 "CLANG", 293 "CLANG_CXX", 294 "CCC_CC", 295 "CCC_CXX", 296 297 // Used by the goma compiler wrapper, but should only be set by 298 // gomacc 299 "GOMACC_PATH", 300 301 // We handle this above 302 "OUT_DIR_COMMON_BASE", 303 304 // This is handled above too, and set for individual commands later 305 "DIST_DIR", 306 307 // Variables that have caused problems in the past 308 "BASH_ENV", 309 "CDPATH", 310 "DISPLAY", 311 "GREP_OPTIONS", 312 "NDK_ROOT", 313 "POSIXLY_CORRECT", 314 315 // Drop make flags 316 "MAKEFLAGS", 317 "MAKELEVEL", 318 "MFLAGS", 319 320 // Set in envsetup.sh, reset in makefiles 321 "ANDROID_JAVA_TOOLCHAIN", 322 323 // Set by envsetup.sh, but shouldn't be used inside the build because envsetup.sh is optional 324 "ANDROID_BUILD_TOP", 325 "ANDROID_HOST_OUT", 326 "ANDROID_PRODUCT_OUT", 327 "ANDROID_HOST_OUT_TESTCASES", 328 "ANDROID_TARGET_OUT_TESTCASES", 329 "ANDROID_TOOLCHAIN", 330 "ANDROID_TOOLCHAIN_2ND_ARCH", 331 "ANDROID_DEV_SCRIPTS", 332 "ANDROID_EMULATOR_PREBUILTS", 333 "ANDROID_PRE_BUILD_PATHS", 334 ) 335 336 if ret.UseGoma() || ret.ForceUseGoma() { 337 ctx.Println("Goma for Android has been deprecated and replaced with RBE. See go/rbe_for_android for instructions on how to use RBE.") 338 ctx.Fatalln("USE_GOMA / FORCE_USE_GOMA flag is no longer supported.") 339 } 340 341 // Tell python not to spam the source tree with .pyc files. 342 ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1") 343 344 tmpDir := absPath(ctx, ret.TempDir()) 345 ret.environ.Set("TMPDIR", tmpDir) 346 347 // Always set ASAN_SYMBOLIZER_PATH so that ASAN-based tools can symbolize any crashes 348 symbolizerPath := filepath.Join("prebuilts/clang/host", ret.HostPrebuiltTag(), 349 "llvm-binutils-stable/llvm-symbolizer") 350 ret.environ.Set("ASAN_SYMBOLIZER_PATH", absPath(ctx, symbolizerPath)) 351 352 // Precondition: the current directory is the top of the source tree 353 checkTopDir(ctx) 354 355 srcDir := absPath(ctx, ".") 356 if strings.ContainsRune(srcDir, ' ') { 357 ctx.Println("You are building in a directory whose absolute path contains a space character:") 358 ctx.Println() 359 ctx.Printf("%q\n", srcDir) 360 ctx.Println() 361 ctx.Fatalln("Directory names containing spaces are not supported") 362 } 363 364 ret.metricsUploader = GetMetricsUploader(srcDir, ret.environ) 365 366 if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') { 367 ctx.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:") 368 ctx.Println() 369 ctx.Printf("%q\n", outDir) 370 ctx.Println() 371 ctx.Fatalln("Directory names containing spaces are not supported") 372 } 373 374 if distDir := ret.RealDistDir(); strings.ContainsRune(distDir, ' ') { 375 ctx.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:") 376 ctx.Println() 377 ctx.Printf("%q\n", distDir) 378 ctx.Println() 379 ctx.Fatalln("Directory names containing spaces are not supported") 380 } 381 382 // Configure Java-related variables, including adding it to $PATH 383 java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag()) 384 java9Home := filepath.Join("prebuilts/jdk/jdk9", ret.HostPrebuiltTag()) 385 java11Home := filepath.Join("prebuilts/jdk/jdk11", ret.HostPrebuiltTag()) 386 java17Home := filepath.Join("prebuilts/jdk/jdk17", ret.HostPrebuiltTag()) 387 javaHome := func() string { 388 if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok { 389 return override 390 } 391 if ret.environ.IsEnvTrue("EXPERIMENTAL_USE_OPENJDK17_TOOLCHAIN") { 392 return java17Home 393 } 394 if toolchain11, ok := ret.environ.Get("EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN"); ok && toolchain11 != "true" { 395 ctx.Fatalln("The environment variable EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN is no longer supported. An OpenJDK 11 toolchain is now the global default.") 396 } 397 return java11Home 398 }() 399 absJavaHome := absPath(ctx, javaHome) 400 401 ret.configureLocale(ctx) 402 403 newPath := []string{filepath.Join(absJavaHome, "bin")} 404 if path, ok := ret.environ.Get("PATH"); ok && path != "" { 405 newPath = append(newPath, path) 406 } 407 408 ret.environ.Unset("OVERRIDE_ANDROID_JAVA_HOME") 409 ret.environ.Set("JAVA_HOME", absJavaHome) 410 ret.environ.Set("ANDROID_JAVA_HOME", javaHome) 411 ret.environ.Set("ANDROID_JAVA8_HOME", java8Home) 412 ret.environ.Set("ANDROID_JAVA9_HOME", java9Home) 413 ret.environ.Set("ANDROID_JAVA11_HOME", java11Home) 414 ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator))) 415 416 outDir := ret.OutDir() 417 buildDateTimeFile := filepath.Join(outDir, "build_date.txt") 418 if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" { 419 ret.buildDateTime = buildDateTime 420 } else { 421 ret.buildDateTime = strconv.FormatInt(time.Now().Unix(), 10) 422 } 423 424 ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile) 425 426 if ret.UseRBE() { 427 for k, v := range getRBEVars(ctx, Config{ret}) { 428 ret.environ.Set(k, v) 429 } 430 } 431 432 bpd := ret.BazelMetricsDir() 433 if err := os.RemoveAll(bpd); err != nil { 434 ctx.Fatalf("Unable to remove bazel profile directory %q: %v", bpd, err) 435 } 436 437 ret.useBazel = ret.environ.IsEnvTrue("USE_BAZEL") 438 439 if ret.UseBazel() { 440 if err := os.MkdirAll(bpd, 0777); err != nil { 441 ctx.Fatalf("Failed to create bazel profile directory %q: %v", bpd, err) 442 } 443 } 444 445 if ret.UseBazel() { 446 ret.riggedDistDirForBazel = filepath.Join(ret.OutDir(), "dist") 447 } else { 448 // Not rigged 449 ret.riggedDistDirForBazel = ret.distDir 450 } 451 452 c := Config{ret} 453 storeConfigMetrics(ctx, c) 454 return c 455} 456 457// NewBuildActionConfig returns a build configuration based on the build action. The arguments are 458// processed based on the build action and extracts any arguments that belongs to the build action. 459func NewBuildActionConfig(action BuildAction, dir string, ctx Context, args ...string) Config { 460 return NewConfig(ctx, getConfigArgs(action, dir, ctx, args)...) 461} 462 463// storeConfigMetrics selects a set of configuration information and store in 464// the metrics system for further analysis. 465func storeConfigMetrics(ctx Context, config Config) { 466 if ctx.Metrics == nil { 467 return 468 } 469 470 ctx.Metrics.BuildConfig(buildConfig(config)) 471 472 s := &smpb.SystemResourceInfo{ 473 TotalPhysicalMemory: proto.Uint64(config.TotalRAM()), 474 AvailableCpus: proto.Int32(int32(runtime.NumCPU())), 475 } 476 ctx.Metrics.SystemResourceInfo(s) 477} 478 479func buildConfig(config Config) *smpb.BuildConfig { 480 c := &smpb.BuildConfig{ 481 ForceUseGoma: proto.Bool(config.ForceUseGoma()), 482 UseGoma: proto.Bool(config.UseGoma()), 483 UseRbe: proto.Bool(config.UseRBE()), 484 BazelAsNinja: proto.Bool(config.UseBazel()), 485 BazelMixedBuild: proto.Bool(config.bazelBuildMode() == mixedBuild), 486 } 487 c.Targets = append(c.Targets, config.arguments...) 488 489 return c 490} 491 492// getConfigArgs processes the command arguments based on the build action and creates a set of new 493// arguments to be accepted by Config. 494func getConfigArgs(action BuildAction, dir string, ctx Context, args []string) []string { 495 // The next block of code verifies that the current directory is the root directory of the source 496 // tree. It then finds the relative path of dir based on the root directory of the source tree 497 // and verify that dir is inside of the source tree. 498 checkTopDir(ctx) 499 topDir, err := os.Getwd() 500 if err != nil { 501 ctx.Fatalf("Error retrieving top directory: %v", err) 502 } 503 dir, err = filepath.EvalSymlinks(dir) 504 if err != nil { 505 ctx.Fatalf("Unable to evaluate symlink of %s: %v", dir, err) 506 } 507 dir, err = filepath.Abs(dir) 508 if err != nil { 509 ctx.Fatalf("Unable to find absolute path %s: %v", dir, err) 510 } 511 relDir, err := filepath.Rel(topDir, dir) 512 if err != nil { 513 ctx.Fatalf("Unable to find relative path %s of %s: %v", relDir, topDir, err) 514 } 515 // If there are ".." in the path, it's not in the source tree. 516 if strings.Contains(relDir, "..") { 517 ctx.Fatalf("Directory %s is not under the source tree %s", dir, topDir) 518 } 519 520 configArgs := args[:] 521 522 // If the arguments contains GET-INSTALL-PATH, change the target name prefix from MODULES-IN- to 523 // GET-INSTALL-PATH-IN- to extract the installation path instead of building the modules. 524 targetNamePrefix := "MODULES-IN-" 525 if inList("GET-INSTALL-PATH", configArgs) { 526 targetNamePrefix = "GET-INSTALL-PATH-IN-" 527 configArgs = removeFromList("GET-INSTALL-PATH", configArgs) 528 } 529 530 var targets []string 531 532 switch action { 533 case BUILD_MODULES: 534 // No additional processing is required when building a list of specific modules or all modules. 535 case BUILD_MODULES_IN_A_DIRECTORY: 536 // If dir is the root source tree, all the modules are built of the source tree are built so 537 // no need to find the build file. 538 if topDir == dir { 539 break 540 } 541 542 buildFile := findBuildFile(ctx, relDir) 543 if buildFile == "" { 544 ctx.Fatalf("Build file not found for %s directory", relDir) 545 } 546 targets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)} 547 case BUILD_MODULES_IN_DIRECTORIES: 548 newConfigArgs, dirs := splitArgs(configArgs) 549 configArgs = newConfigArgs 550 targets = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix) 551 } 552 553 // Tidy only override all other specified targets. 554 tidyOnly := os.Getenv("WITH_TIDY_ONLY") 555 if tidyOnly == "true" || tidyOnly == "1" { 556 configArgs = append(configArgs, "tidy_only") 557 } else { 558 configArgs = append(configArgs, targets...) 559 } 560 561 return configArgs 562} 563 564// convertToTarget replaces "/" to "-" in dir and pre-append the targetNamePrefix to the target name. 565func convertToTarget(dir string, targetNamePrefix string) string { 566 return targetNamePrefix + strings.ReplaceAll(dir, "/", "-") 567} 568 569// hasBuildFile returns true if dir contains an Android build file. 570func hasBuildFile(ctx Context, dir string) bool { 571 for _, buildFile := range buildFiles { 572 _, err := os.Stat(filepath.Join(dir, buildFile)) 573 if err == nil { 574 return true 575 } 576 if !os.IsNotExist(err) { 577 ctx.Fatalf("Error retrieving the build file stats: %v", err) 578 } 579 } 580 return false 581} 582 583// findBuildFile finds a build file (makefile or blueprint file) by looking if there is a build file 584// in the current and any sub directory of dir. If a build file is not found, traverse the path 585// up by one directory and repeat again until either a build file is found or reached to the root 586// source tree. The returned filename of build file is "Android.mk". If one was not found, a blank 587// string is returned. 588func findBuildFile(ctx Context, dir string) string { 589 // If the string is empty or ".", assume it is top directory of the source tree. 590 if dir == "" || dir == "." { 591 return "" 592 } 593 594 found := false 595 for buildDir := dir; buildDir != "."; buildDir = filepath.Dir(buildDir) { 596 err := filepath.Walk(buildDir, func(path string, info os.FileInfo, err error) error { 597 if err != nil { 598 return err 599 } 600 if found { 601 return filepath.SkipDir 602 } 603 if info.IsDir() { 604 return nil 605 } 606 for _, buildFile := range buildFiles { 607 if info.Name() == buildFile { 608 found = true 609 return filepath.SkipDir 610 } 611 } 612 return nil 613 }) 614 if err != nil { 615 ctx.Fatalf("Error finding Android build file: %v", err) 616 } 617 618 if found { 619 return filepath.Join(buildDir, "Android.mk") 620 } 621 } 622 623 return "" 624} 625 626// splitArgs iterates over the arguments list and splits into two lists: arguments and directories. 627func splitArgs(args []string) (newArgs []string, dirs []string) { 628 specialArgs := map[string]bool{ 629 "showcommands": true, 630 "snod": true, 631 "dist": true, 632 "checkbuild": true, 633 } 634 635 newArgs = []string{} 636 dirs = []string{} 637 638 for _, arg := range args { 639 // It's a dash argument if it starts with "-" or it's a key=value pair, it's not a directory. 640 if strings.IndexRune(arg, '-') == 0 || strings.IndexRune(arg, '=') != -1 { 641 newArgs = append(newArgs, arg) 642 continue 643 } 644 645 if _, ok := specialArgs[arg]; ok { 646 newArgs = append(newArgs, arg) 647 continue 648 } 649 650 dirs = append(dirs, arg) 651 } 652 653 return newArgs, dirs 654} 655 656// getTargetsFromDirs iterates over the dirs list and creates a list of targets to build. If a 657// directory from the dirs list does not exist, a fatal error is raised. relDir is related to the 658// source root tree where the build action command was invoked. Each directory is validated if the 659// build file can be found and follows the format "dir1:target1,target2,...". Target is optional. 660func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string) { 661 for _, dir := range dirs { 662 // The directory may have specified specific modules to build. ":" is the separator to separate 663 // the directory and the list of modules. 664 s := strings.Split(dir, ":") 665 l := len(s) 666 if l > 2 { // more than one ":" was specified. 667 ctx.Fatalf("%s not in proper directory:target1,target2,... format (\":\" was specified more than once)", dir) 668 } 669 670 dir = filepath.Join(relDir, s[0]) 671 if _, err := os.Stat(dir); err != nil { 672 ctx.Fatalf("couldn't find directory %s", dir) 673 } 674 675 // Verify that if there are any targets specified after ":". Each target is separated by ",". 676 var newTargets []string 677 if l == 2 && s[1] != "" { 678 newTargets = strings.Split(s[1], ",") 679 if inList("", newTargets) { 680 ctx.Fatalf("%s not in proper directory:target1,target2,... format", dir) 681 } 682 } 683 684 // If there are specified targets to build in dir, an android build file must exist for the one 685 // shot build. For the non-targets case, find the appropriate build file and build all the 686 // modules in dir (or the closest one in the dir path). 687 if len(newTargets) > 0 { 688 if !hasBuildFile(ctx, dir) { 689 ctx.Fatalf("Couldn't locate a build file from %s directory", dir) 690 } 691 } else { 692 buildFile := findBuildFile(ctx, dir) 693 if buildFile == "" { 694 ctx.Fatalf("Build file not found for %s directory", dir) 695 } 696 newTargets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)} 697 } 698 699 targets = append(targets, newTargets...) 700 } 701 702 return targets 703} 704 705func (c *configImpl) parseArgs(ctx Context, args []string) { 706 for i := 0; i < len(args); i++ { 707 arg := strings.TrimSpace(args[i]) 708 if arg == "showcommands" { 709 c.verbose = true 710 } else if arg == "--empty-ninja-file" { 711 c.emptyNinjaFile = true 712 } else if arg == "--skip-ninja" { 713 c.skipNinja = true 714 } else if arg == "--skip-make" { 715 // TODO(ccross): deprecate this, it has confusing behaviors. It doesn't run kati, 716 // but it does run a Kati ninja file if the .kati_enabled marker file was created 717 // by a previous build. 718 c.skipConfig = true 719 c.skipKati = true 720 } else if arg == "--skip-kati" { 721 // TODO: remove --skip-kati once module builds have been migrated to --song-only 722 c.skipKati = true 723 } else if arg == "--soong-only" { 724 c.skipKati = true 725 c.skipKatiNinja = true 726 } else if arg == "--config-only" { 727 c.skipKati = true 728 c.skipKatiNinja = true 729 c.skipSoong = true 730 } else if arg == "--skip-config" { 731 c.skipConfig = true 732 } else if arg == "--skip-soong-tests" { 733 c.skipSoongTests = true 734 } else if arg == "--mk-metrics" { 735 c.reportMkMetrics = true 736 } else if len(arg) > 0 && arg[0] == '-' { 737 parseArgNum := func(def int) int { 738 if len(arg) > 2 { 739 p, err := strconv.ParseUint(arg[2:], 10, 31) 740 if err != nil { 741 ctx.Fatalf("Failed to parse %q: %v", arg, err) 742 } 743 return int(p) 744 } else if i+1 < len(args) { 745 p, err := strconv.ParseUint(args[i+1], 10, 31) 746 if err == nil { 747 i++ 748 return int(p) 749 } 750 } 751 return def 752 } 753 754 if len(arg) > 1 && arg[1] == 'j' { 755 c.parallel = parseArgNum(c.parallel) 756 } else if len(arg) > 1 && arg[1] == 'k' { 757 c.keepGoing = parseArgNum(0) 758 } else { 759 ctx.Fatalln("Unknown option:", arg) 760 } 761 } else if k, v, ok := decodeKeyValue(arg); ok && len(k) > 0 { 762 if k == "OUT_DIR" { 763 ctx.Fatalln("OUT_DIR may only be set in the environment, not as a command line option.") 764 } 765 c.environ.Set(k, v) 766 } else if arg == "dist" { 767 c.dist = true 768 } else if arg == "json-module-graph" { 769 c.jsonModuleGraph = true 770 } else if arg == "bp2build" { 771 c.bp2build = true 772 } else if arg == "queryview" { 773 c.queryview = true 774 } else if arg == "soong_docs" { 775 c.soongDocs = true 776 } else { 777 if arg == "checkbuild" { 778 c.checkbuild = true 779 } 780 c.arguments = append(c.arguments, arg) 781 } 782 } 783} 784 785func (c *configImpl) configureLocale(ctx Context) { 786 cmd := Command(ctx, Config{c}, "locale", "locale", "-a") 787 output, err := cmd.Output() 788 789 var locales []string 790 if err == nil { 791 locales = strings.Split(string(output), "\n") 792 } else { 793 // If we're unable to list the locales, let's assume en_US.UTF-8 794 locales = []string{"en_US.UTF-8"} 795 ctx.Verbosef("Failed to list locales (%q), falling back to %q", err, locales) 796 } 797 798 // gettext uses LANGUAGE, which is passed directly through 799 800 // For LANG and LC_*, only preserve the evaluated version of 801 // LC_MESSAGES 802 userLang := "" 803 if lc_all, ok := c.environ.Get("LC_ALL"); ok { 804 userLang = lc_all 805 } else if lc_messages, ok := c.environ.Get("LC_MESSAGES"); ok { 806 userLang = lc_messages 807 } else if lang, ok := c.environ.Get("LANG"); ok { 808 userLang = lang 809 } 810 811 c.environ.UnsetWithPrefix("LC_") 812 813 if userLang != "" { 814 c.environ.Set("LC_MESSAGES", userLang) 815 } 816 817 // The for LANG, use C.UTF-8 if it exists (Debian currently, proposed 818 // for others) 819 if inList("C.UTF-8", locales) { 820 c.environ.Set("LANG", "C.UTF-8") 821 } else if inList("C.utf8", locales) { 822 // These normalize to the same thing 823 c.environ.Set("LANG", "C.UTF-8") 824 } else if inList("en_US.UTF-8", locales) { 825 c.environ.Set("LANG", "en_US.UTF-8") 826 } else if inList("en_US.utf8", locales) { 827 // These normalize to the same thing 828 c.environ.Set("LANG", "en_US.UTF-8") 829 } else { 830 ctx.Fatalln("System doesn't support either C.UTF-8 or en_US.UTF-8") 831 } 832} 833 834func (c *configImpl) Environment() *Environment { 835 return c.environ 836} 837 838func (c *configImpl) Arguments() []string { 839 return c.arguments 840} 841 842func (c *configImpl) SoongBuildInvocationNeeded() bool { 843 if len(c.Arguments()) > 0 { 844 // Explicit targets requested that are not special targets like b2pbuild 845 // or the JSON module graph 846 return true 847 } 848 849 if !c.JsonModuleGraph() && !c.Bp2Build() && !c.Queryview() && !c.SoongDocs() { 850 // Command line was empty, the default Ninja target is built 851 return true 852 } 853 854 // bp2build + dist may be used to dist bp2build logs but does not require SoongBuildInvocation 855 if c.Dist() && !c.Bp2Build() { 856 return true 857 } 858 859 // build.ninja doesn't need to be generated 860 return false 861} 862 863func (c *configImpl) OutDir() string { 864 if outDir, ok := c.environ.Get("OUT_DIR"); ok { 865 return outDir 866 } 867 return "out" 868} 869 870func (c *configImpl) DistDir() string { 871 if c.UseBazel() { 872 return c.riggedDistDirForBazel 873 } else { 874 return c.distDir 875 } 876} 877 878func (c *configImpl) RealDistDir() string { 879 return c.distDir 880} 881 882func (c *configImpl) NinjaArgs() []string { 883 if c.skipKati { 884 return c.arguments 885 } 886 return c.ninjaArgs 887} 888 889func (c *configImpl) BazelOutDir() string { 890 return filepath.Join(c.OutDir(), "bazel") 891} 892 893func (c *configImpl) SoongOutDir() string { 894 return filepath.Join(c.OutDir(), "soong") 895} 896 897func (c *configImpl) PrebuiltOS() string { 898 switch runtime.GOOS { 899 case "linux": 900 return "linux-x86" 901 case "darwin": 902 return "darwin-x86" 903 default: 904 panic("Unknown GOOS") 905 } 906} 907 908func (c *configImpl) HostToolDir() string { 909 if c.SkipKatiNinja() { 910 return filepath.Join(c.SoongOutDir(), "host", c.PrebuiltOS(), "bin") 911 } else { 912 return filepath.Join(c.OutDir(), "host", c.PrebuiltOS(), "bin") 913 } 914} 915 916func (c *configImpl) NamedGlobFile(name string) string { 917 return shared.JoinPath(c.SoongOutDir(), "globs-"+name+".ninja") 918} 919 920func (c *configImpl) UsedEnvFile(tag string) string { 921 return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+tag) 922} 923 924func (c *configImpl) Bp2BuildMarkerFile() string { 925 return shared.JoinPath(c.SoongOutDir(), "bp2build_workspace_marker") 926} 927 928func (c *configImpl) SoongDocsHtml() string { 929 return shared.JoinPath(c.SoongOutDir(), "docs/soong_build.html") 930} 931 932func (c *configImpl) QueryviewMarkerFile() string { 933 return shared.JoinPath(c.SoongOutDir(), "queryview.marker") 934} 935 936func (c *configImpl) ModuleGraphFile() string { 937 return shared.JoinPath(c.SoongOutDir(), "module-graph.json") 938} 939 940func (c *configImpl) ModuleActionsFile() string { 941 return shared.JoinPath(c.SoongOutDir(), "module-actions.json") 942} 943 944func (c *configImpl) TempDir() string { 945 return shared.TempDirForOutDir(c.SoongOutDir()) 946} 947 948func (c *configImpl) FileListDir() string { 949 return filepath.Join(c.OutDir(), ".module_paths") 950} 951 952func (c *configImpl) KatiSuffix() string { 953 if c.katiSuffix != "" { 954 return c.katiSuffix 955 } 956 panic("SetKatiSuffix has not been called") 957} 958 959// Checkbuild returns true if "checkbuild" was one of the build goals, which means that the 960// user is interested in additional checks at the expense of build time. 961func (c *configImpl) Checkbuild() bool { 962 return c.checkbuild 963} 964 965func (c *configImpl) Dist() bool { 966 return c.dist 967} 968 969func (c *configImpl) JsonModuleGraph() bool { 970 return c.jsonModuleGraph 971} 972 973func (c *configImpl) Bp2Build() bool { 974 return c.bp2build 975} 976 977func (c *configImpl) Queryview() bool { 978 return c.queryview 979} 980 981func (c *configImpl) SoongDocs() bool { 982 return c.soongDocs 983} 984 985func (c *configImpl) IsVerbose() bool { 986 return c.verbose 987} 988 989func (c *configImpl) SkipKati() bool { 990 return c.skipKati 991} 992 993func (c *configImpl) SkipKatiNinja() bool { 994 return c.skipKatiNinja 995} 996 997func (c *configImpl) SkipSoong() bool { 998 return c.skipSoong 999} 1000 1001func (c *configImpl) SkipNinja() bool { 1002 return c.skipNinja 1003} 1004 1005func (c *configImpl) SetSkipNinja(v bool) { 1006 c.skipNinja = v 1007} 1008 1009func (c *configImpl) SkipConfig() bool { 1010 return c.skipConfig 1011} 1012 1013func (c *configImpl) TargetProduct() string { 1014 if v, ok := c.environ.Get("TARGET_PRODUCT"); ok { 1015 return v 1016 } 1017 panic("TARGET_PRODUCT is not defined") 1018} 1019 1020func (c *configImpl) TargetDevice() string { 1021 return c.targetDevice 1022} 1023 1024func (c *configImpl) SetTargetDevice(device string) { 1025 c.targetDevice = device 1026} 1027 1028func (c *configImpl) TargetBuildVariant() string { 1029 if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok { 1030 return v 1031 } 1032 panic("TARGET_BUILD_VARIANT is not defined") 1033} 1034 1035func (c *configImpl) KatiArgs() []string { 1036 return c.katiArgs 1037} 1038 1039func (c *configImpl) Parallel() int { 1040 return c.parallel 1041} 1042 1043func (c *configImpl) GetIncludeTags() []string { 1044 return c.includeTags 1045} 1046 1047func (c *configImpl) SetIncludeTags(i []string) { 1048 c.includeTags = i 1049} 1050 1051func (c *configImpl) HighmemParallel() int { 1052 if i, ok := c.environ.GetInt("NINJA_HIGHMEM_NUM_JOBS"); ok { 1053 return i 1054 } 1055 1056 const minMemPerHighmemProcess = 8 * 1024 * 1024 * 1024 1057 parallel := c.Parallel() 1058 if c.UseRemoteBuild() { 1059 // Ninja doesn't support nested pools, and when remote builds are enabled the total ninja parallelism 1060 // is set very high (i.e. 500). Using a large value here would cause the total number of running jobs 1061 // to be the sum of the sizes of the local and highmem pools, which will cause extra CPU contention. 1062 // Return 1/16th of the size of the local pool, rounding up. 1063 return (parallel + 15) / 16 1064 } else if c.totalRAM == 0 { 1065 // Couldn't detect the total RAM, don't restrict highmem processes. 1066 return parallel 1067 } else if c.totalRAM <= 16*1024*1024*1024 { 1068 // Less than 16GB of ram, restrict to 1 highmem processes 1069 return 1 1070 } else if c.totalRAM <= 32*1024*1024*1024 { 1071 // Less than 32GB of ram, restrict to 2 highmem processes 1072 return 2 1073 } else if p := int(c.totalRAM / minMemPerHighmemProcess); p < parallel { 1074 // If less than 8GB total RAM per process, reduce the number of highmem processes 1075 return p 1076 } 1077 // No restriction on highmem processes 1078 return parallel 1079} 1080 1081func (c *configImpl) TotalRAM() uint64 { 1082 return c.totalRAM 1083} 1084 1085// ForceUseGoma determines whether we should override Goma deprecation 1086// and use Goma for the current build or not. 1087func (c *configImpl) ForceUseGoma() bool { 1088 if v, ok := c.environ.Get("FORCE_USE_GOMA"); ok { 1089 v = strings.TrimSpace(v) 1090 if v != "" && v != "false" { 1091 return true 1092 } 1093 } 1094 return false 1095} 1096 1097func (c *configImpl) UseGoma() bool { 1098 if v, ok := c.environ.Get("USE_GOMA"); ok { 1099 v = strings.TrimSpace(v) 1100 if v != "" && v != "false" { 1101 return true 1102 } 1103 } 1104 return false 1105} 1106 1107func (c *configImpl) StartGoma() bool { 1108 if !c.UseGoma() { 1109 return false 1110 } 1111 1112 if v, ok := c.environ.Get("NOSTART_GOMA"); ok { 1113 v = strings.TrimSpace(v) 1114 if v != "" && v != "false" { 1115 return false 1116 } 1117 } 1118 return true 1119} 1120 1121func (c *configImpl) UseRBE() bool { 1122 if v, ok := c.Environment().Get("USE_RBE"); ok { 1123 v = strings.TrimSpace(v) 1124 if v != "" && v != "false" { 1125 return true 1126 } 1127 } 1128 return false 1129} 1130 1131func (c *configImpl) UseBazel() bool { 1132 return c.useBazel 1133} 1134 1135func (c *configImpl) bazelBuildMode() bazelBuildMode { 1136 if c.Environment().IsEnvTrue("USE_BAZEL_ANALYSIS") { 1137 return mixedBuild 1138 } else { 1139 return noBazel 1140 } 1141} 1142 1143func (c *configImpl) StartRBE() bool { 1144 if !c.UseRBE() { 1145 return false 1146 } 1147 1148 if v, ok := c.environ.Get("NOSTART_RBE"); ok { 1149 v = strings.TrimSpace(v) 1150 if v != "" && v != "false" { 1151 return false 1152 } 1153 } 1154 return true 1155} 1156 1157func (c *configImpl) rbeLogDir() string { 1158 for _, f := range []string{"RBE_log_dir", "FLAG_log_dir"} { 1159 if v, ok := c.environ.Get(f); ok { 1160 return v 1161 } 1162 } 1163 if c.Dist() { 1164 return c.LogsDir() 1165 } 1166 return c.OutDir() 1167} 1168 1169func (c *configImpl) rbeStatsOutputDir() string { 1170 for _, f := range []string{"RBE_output_dir", "FLAG_output_dir"} { 1171 if v, ok := c.environ.Get(f); ok { 1172 return v 1173 } 1174 } 1175 return c.rbeLogDir() 1176} 1177 1178func (c *configImpl) rbeLogPath() string { 1179 for _, f := range []string{"RBE_log_path", "FLAG_log_path"} { 1180 if v, ok := c.environ.Get(f); ok { 1181 return v 1182 } 1183 } 1184 return fmt.Sprintf("text://%v/reproxy_log.txt", c.rbeLogDir()) 1185} 1186 1187func (c *configImpl) rbeExecRoot() string { 1188 for _, f := range []string{"RBE_exec_root", "FLAG_exec_root"} { 1189 if v, ok := c.environ.Get(f); ok { 1190 return v 1191 } 1192 } 1193 wd, err := os.Getwd() 1194 if err != nil { 1195 return "" 1196 } 1197 return wd 1198} 1199 1200func (c *configImpl) rbeDir() string { 1201 if v, ok := c.environ.Get("RBE_DIR"); ok { 1202 return v 1203 } 1204 return "prebuilts/remoteexecution-client/live/" 1205} 1206 1207func (c *configImpl) rbeReproxy() string { 1208 for _, f := range []string{"RBE_re_proxy", "FLAG_re_proxy"} { 1209 if v, ok := c.environ.Get(f); ok { 1210 return v 1211 } 1212 } 1213 return filepath.Join(c.rbeDir(), "reproxy") 1214} 1215 1216func (c *configImpl) rbeAuth() (string, string) { 1217 credFlags := []string{ 1218 "use_application_default_credentials", 1219 "use_gce_credentials", 1220 "credential_file", 1221 "use_google_prod_creds", 1222 } 1223 for _, cf := range credFlags { 1224 for _, f := range []string{"RBE_" + cf, "FLAG_" + cf} { 1225 if v, ok := c.environ.Get(f); ok { 1226 v = strings.TrimSpace(v) 1227 if v != "" && v != "false" && v != "0" { 1228 return "RBE_" + cf, v 1229 } 1230 } 1231 } 1232 } 1233 return "RBE_use_application_default_credentials", "true" 1234} 1235 1236func (c *configImpl) UseRemoteBuild() bool { 1237 return c.UseGoma() || c.UseRBE() 1238} 1239 1240// RemoteParallel controls how many remote jobs (i.e., commands which contain 1241// gomacc) are run in parallel. Note the parallelism of all other jobs is 1242// still limited by Parallel() 1243func (c *configImpl) RemoteParallel() int { 1244 if !c.UseRemoteBuild() { 1245 return 0 1246 } 1247 if i, ok := c.environ.GetInt("NINJA_REMOTE_NUM_JOBS"); ok { 1248 return i 1249 } 1250 return 500 1251} 1252 1253func (c *configImpl) SetKatiArgs(args []string) { 1254 c.katiArgs = args 1255} 1256 1257func (c *configImpl) SetNinjaArgs(args []string) { 1258 c.ninjaArgs = args 1259} 1260 1261func (c *configImpl) SetKatiSuffix(suffix string) { 1262 c.katiSuffix = suffix 1263} 1264 1265func (c *configImpl) LastKatiSuffixFile() string { 1266 return filepath.Join(c.OutDir(), "last_kati_suffix") 1267} 1268 1269func (c *configImpl) HasKatiSuffix() bool { 1270 return c.katiSuffix != "" 1271} 1272 1273func (c *configImpl) KatiEnvFile() string { 1274 return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh") 1275} 1276 1277func (c *configImpl) KatiBuildNinjaFile() string { 1278 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiBuildSuffix+".ninja") 1279} 1280 1281func (c *configImpl) KatiPackageNinjaFile() string { 1282 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja") 1283} 1284 1285func (c *configImpl) SoongNinjaFile() string { 1286 return filepath.Join(c.SoongOutDir(), "build.ninja") 1287} 1288 1289func (c *configImpl) CombinedNinjaFile() string { 1290 if c.katiSuffix == "" { 1291 return filepath.Join(c.OutDir(), "combined.ninja") 1292 } 1293 return filepath.Join(c.OutDir(), "combined"+c.KatiSuffix()+".ninja") 1294} 1295 1296func (c *configImpl) SoongAndroidMk() string { 1297 return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+".mk") 1298} 1299 1300func (c *configImpl) SoongMakeVarsMk() string { 1301 return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk") 1302} 1303 1304func (c *configImpl) ProductOut() string { 1305 return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice()) 1306} 1307 1308func (c *configImpl) DevicePreviousProductConfig() string { 1309 return filepath.Join(c.ProductOut(), "previous_build_config.mk") 1310} 1311 1312func (c *configImpl) KatiPackageMkDir() string { 1313 return filepath.Join(c.ProductOut(), "obj", "CONFIG", "kati_packaging") 1314} 1315 1316func (c *configImpl) hostOutRoot() string { 1317 return filepath.Join(c.OutDir(), "host") 1318} 1319 1320func (c *configImpl) HostOut() string { 1321 return filepath.Join(c.hostOutRoot(), c.HostPrebuiltTag()) 1322} 1323 1324// This probably needs to be multi-valued, so not exporting it for now 1325func (c *configImpl) hostCrossOut() string { 1326 if runtime.GOOS == "linux" { 1327 return filepath.Join(c.hostOutRoot(), "windows-x86") 1328 } else { 1329 return "" 1330 } 1331} 1332 1333func (c *configImpl) HostPrebuiltTag() string { 1334 if runtime.GOOS == "linux" { 1335 return "linux-x86" 1336 } else if runtime.GOOS == "darwin" { 1337 return "darwin-x86" 1338 } else { 1339 panic("Unsupported OS") 1340 } 1341} 1342 1343func (c *configImpl) PrebuiltBuildTool(name string) string { 1344 if v, ok := c.environ.Get("SANITIZE_HOST"); ok { 1345 if sanitize := strings.Fields(v); inList("address", sanitize) { 1346 asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name) 1347 if _, err := os.Stat(asan); err == nil { 1348 return asan 1349 } 1350 } 1351 } 1352 return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name) 1353} 1354 1355func (c *configImpl) SetBuildBrokenDupRules(val bool) { 1356 c.brokenDupRules = val 1357} 1358 1359func (c *configImpl) BuildBrokenDupRules() bool { 1360 return c.brokenDupRules 1361} 1362 1363func (c *configImpl) SetBuildBrokenUsesNetwork(val bool) { 1364 c.brokenUsesNetwork = val 1365} 1366 1367func (c *configImpl) BuildBrokenUsesNetwork() bool { 1368 return c.brokenUsesNetwork 1369} 1370 1371func (c *configImpl) SetBuildBrokenNinjaUsesEnvVars(val []string) { 1372 c.brokenNinjaEnvVars = val 1373} 1374 1375func (c *configImpl) BuildBrokenNinjaUsesEnvVars() []string { 1376 return c.brokenNinjaEnvVars 1377} 1378 1379func (c *configImpl) SetTargetDeviceDir(dir string) { 1380 c.targetDeviceDir = dir 1381} 1382 1383func (c *configImpl) TargetDeviceDir() string { 1384 return c.targetDeviceDir 1385} 1386 1387func (c *configImpl) BuildDateTime() string { 1388 return c.buildDateTime 1389} 1390 1391func (c *configImpl) MetricsUploaderApp() string { 1392 return c.metricsUploader 1393} 1394 1395// LogsDir returns the absolute path to the logs directory where build log and 1396// metrics files are located. By default, the logs directory is the out 1397// directory. If the argument dist is specified, the logs directory 1398// is <dist_dir>/logs. 1399func (c *configImpl) LogsDir() string { 1400 dir := c.OutDir() 1401 if c.Dist() { 1402 // Always write logs to the real dist dir, even if Bazel is using a rigged dist dir for other files 1403 dir = filepath.Join(c.RealDistDir(), "logs") 1404 } 1405 absDir, err := filepath.Abs(dir) 1406 if err != nil { 1407 fmt.Fprintf(os.Stderr, "\nError making log dir '%s' absolute: %s\n", dir, err.Error()) 1408 os.Exit(1) 1409 } 1410 return absDir 1411} 1412 1413// BazelMetricsDir returns the <logs dir>/bazel_metrics directory 1414// where the bazel profiles are located. 1415func (c *configImpl) BazelMetricsDir() string { 1416 return filepath.Join(c.LogsDir(), "bazel_metrics") 1417} 1418 1419// MkFileMetrics returns the file path for make-related metrics. 1420func (c *configImpl) MkMetrics() string { 1421 return filepath.Join(c.LogsDir(), "mk_metrics.pb") 1422} 1423 1424func (c *configImpl) SetEmptyNinjaFile(v bool) { 1425 c.emptyNinjaFile = v 1426} 1427 1428func (c *configImpl) EmptyNinjaFile() bool { 1429 return c.emptyNinjaFile 1430} 1431 1432func GetMetricsUploader(topDir string, env *Environment) string { 1433 if p, ok := env.Get("METRICS_UPLOADER"); ok { 1434 metricsUploader := filepath.Join(topDir, p) 1435 if _, err := os.Stat(metricsUploader); err == nil { 1436 return metricsUploader 1437 } 1438 } 1439 1440 return "" 1441} 1442