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 "io/ioutil" 19 "log" 20 "os" 21 "path/filepath" 22 "runtime" 23 "strconv" 24 "strings" 25 "time" 26 27 "android/soong/shared" 28) 29 30type Config struct{ *configImpl } 31 32type configImpl struct { 33 // From the environment 34 arguments []string 35 goma bool 36 environ *Environment 37 distDir string 38 39 // From the arguments 40 parallel int 41 keepGoing int 42 verbose bool 43 checkbuild bool 44 dist bool 45 skipMake bool 46 47 // From the product config 48 katiArgs []string 49 ninjaArgs []string 50 katiSuffix string 51 targetDevice string 52 targetDeviceDir string 53 54 pdkBuild bool 55 56 brokenDupRules bool 57 brokenPhonyTargets bool 58 brokenUsesNetwork bool 59 60 pathReplaced bool 61} 62 63const srcDirFileCheck = "build/soong/root.bp" 64 65func NewConfig(ctx Context, args ...string) Config { 66 ret := &configImpl{ 67 environ: OsEnvironment(), 68 } 69 70 // Sane default matching ninja 71 ret.parallel = runtime.NumCPU() + 2 72 ret.keepGoing = 1 73 74 ret.parseArgs(ctx, args) 75 76 // Make sure OUT_DIR is set appropriately 77 if outDir, ok := ret.environ.Get("OUT_DIR"); ok { 78 ret.environ.Set("OUT_DIR", filepath.Clean(outDir)) 79 } else { 80 outDir := "out" 81 if baseDir, ok := ret.environ.Get("OUT_DIR_COMMON_BASE"); ok { 82 if wd, err := os.Getwd(); err != nil { 83 ctx.Fatalln("Failed to get working directory:", err) 84 } else { 85 outDir = filepath.Join(baseDir, filepath.Base(wd)) 86 } 87 } 88 ret.environ.Set("OUT_DIR", outDir) 89 } 90 91 if distDir, ok := ret.environ.Get("DIST_DIR"); ok { 92 ret.distDir = filepath.Clean(distDir) 93 } else { 94 ret.distDir = filepath.Join(ret.OutDir(), "dist") 95 } 96 97 ret.environ.Unset( 98 // We're already using it 99 "USE_SOONG_UI", 100 101 // We should never use GOROOT/GOPATH from the shell environment 102 "GOROOT", 103 "GOPATH", 104 105 // These should only come from Soong, not the environment. 106 "CLANG", 107 "CLANG_CXX", 108 "CCC_CC", 109 "CCC_CXX", 110 111 // Used by the goma compiler wrapper, but should only be set by 112 // gomacc 113 "GOMACC_PATH", 114 115 // We handle this above 116 "OUT_DIR_COMMON_BASE", 117 118 // This is handled above too, and set for individual commands later 119 "DIST_DIR", 120 121 // Variables that have caused problems in the past 122 "CDPATH", 123 "DISPLAY", 124 "GREP_OPTIONS", 125 "NDK_ROOT", 126 "POSIXLY_CORRECT", 127 128 // Drop make flags 129 "MAKEFLAGS", 130 "MAKELEVEL", 131 "MFLAGS", 132 133 // Set in envsetup.sh, reset in makefiles 134 "ANDROID_JAVA_TOOLCHAIN", 135 136 // Set by envsetup.sh, but shouldn't be used inside the build because envsetup.sh is optional 137 "ANDROID_BUILD_TOP", 138 "ANDROID_HOST_OUT", 139 "ANDROID_PRODUCT_OUT", 140 "ANDROID_HOST_OUT_TESTCASES", 141 "ANDROID_TARGET_OUT_TESTCASES", 142 "ANDROID_TOOLCHAIN", 143 "ANDROID_TOOLCHAIN_2ND_ARCH", 144 "ANDROID_DEV_SCRIPTS", 145 "ANDROID_EMULATOR_PREBUILTS", 146 "ANDROID_PRE_BUILD_PATHS", 147 148 // Only set in multiproduct_kati after config generation 149 "EMPTY_NINJA_FILE", 150 ) 151 152 // Tell python not to spam the source tree with .pyc files. 153 ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1") 154 155 ret.environ.Set("TMPDIR", absPath(ctx, ret.TempDir())) 156 157 // Precondition: the current directory is the top of the source tree 158 if _, err := os.Stat(srcDirFileCheck); err != nil { 159 if os.IsNotExist(err) { 160 log.Fatalf("Current working directory must be the source tree. %q not found", srcDirFileCheck) 161 } 162 log.Fatalln("Error verifying tree state:", err) 163 } 164 165 if srcDir := absPath(ctx, "."); strings.ContainsRune(srcDir, ' ') { 166 log.Println("You are building in a directory whose absolute path contains a space character:") 167 log.Println() 168 log.Printf("%q\n", srcDir) 169 log.Println() 170 log.Fatalln("Directory names containing spaces are not supported") 171 } 172 173 if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') { 174 log.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:") 175 log.Println() 176 log.Printf("%q\n", outDir) 177 log.Println() 178 log.Fatalln("Directory names containing spaces are not supported") 179 } 180 181 if distDir := ret.DistDir(); strings.ContainsRune(distDir, ' ') { 182 log.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:") 183 log.Println() 184 log.Printf("%q\n", distDir) 185 log.Println() 186 log.Fatalln("Directory names containing spaces are not supported") 187 } 188 189 // Configure Java-related variables, including adding it to $PATH 190 java8Home := filepath.Join("prebuilts/jdk/jdk8", ret.HostPrebuiltTag()) 191 java9Home := filepath.Join("prebuilts/jdk/jdk9", ret.HostPrebuiltTag()) 192 javaHome := func() string { 193 if override, ok := ret.environ.Get("OVERRIDE_ANDROID_JAVA_HOME"); ok { 194 return override 195 } 196 return java9Home 197 }() 198 absJavaHome := absPath(ctx, javaHome) 199 200 ret.configureLocale(ctx) 201 202 newPath := []string{filepath.Join(absJavaHome, "bin")} 203 if path, ok := ret.environ.Get("PATH"); ok && path != "" { 204 newPath = append(newPath, path) 205 } 206 ret.environ.Unset("OVERRIDE_ANDROID_JAVA_HOME") 207 ret.environ.Set("JAVA_HOME", absJavaHome) 208 ret.environ.Set("ANDROID_JAVA_HOME", javaHome) 209 ret.environ.Set("ANDROID_JAVA8_HOME", java8Home) 210 ret.environ.Set("ANDROID_JAVA9_HOME", java9Home) 211 ret.environ.Set("PATH", strings.Join(newPath, string(filepath.ListSeparator))) 212 213 outDir := ret.OutDir() 214 buildDateTimeFile := filepath.Join(outDir, "build_date.txt") 215 var content string 216 if buildDateTime, ok := ret.environ.Get("BUILD_DATETIME"); ok && buildDateTime != "" { 217 content = buildDateTime 218 } else { 219 content = strconv.FormatInt(time.Now().Unix(), 10) 220 } 221 if ctx.Metrics != nil { 222 ctx.Metrics.SetBuildDateTime(content) 223 } 224 err := ioutil.WriteFile(buildDateTimeFile, []byte(content), 0777) 225 if err != nil { 226 ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err) 227 } 228 ret.environ.Set("BUILD_DATETIME_FILE", buildDateTimeFile) 229 230 return Config{ret} 231} 232 233func (c *configImpl) parseArgs(ctx Context, args []string) { 234 for i := 0; i < len(args); i++ { 235 arg := strings.TrimSpace(args[i]) 236 if arg == "--make-mode" { 237 } else if arg == "showcommands" { 238 c.verbose = true 239 } else if arg == "--skip-make" { 240 c.skipMake = true 241 } else if len(arg) > 0 && arg[0] == '-' { 242 parseArgNum := func(def int) int { 243 if len(arg) > 2 { 244 p, err := strconv.ParseUint(arg[2:], 10, 31) 245 if err != nil { 246 ctx.Fatalf("Failed to parse %q: %v", arg, err) 247 } 248 return int(p) 249 } else if i+1 < len(args) { 250 p, err := strconv.ParseUint(args[i+1], 10, 31) 251 if err == nil { 252 i++ 253 return int(p) 254 } 255 } 256 return def 257 } 258 259 if len(arg) > 1 && arg[1] == 'j' { 260 c.parallel = parseArgNum(c.parallel) 261 } else if len(arg) > 1 && arg[1] == 'k' { 262 c.keepGoing = parseArgNum(0) 263 } else { 264 ctx.Fatalln("Unknown option:", arg) 265 } 266 } else if k, v, ok := decodeKeyValue(arg); ok && len(k) > 0 { 267 c.environ.Set(k, v) 268 } else if arg == "dist" { 269 c.dist = true 270 } else { 271 if arg == "checkbuild" { 272 c.checkbuild = true 273 } 274 c.arguments = append(c.arguments, arg) 275 } 276 } 277} 278 279func (c *configImpl) configureLocale(ctx Context) { 280 cmd := Command(ctx, Config{c}, "locale", "locale", "-a") 281 output, err := cmd.Output() 282 283 var locales []string 284 if err == nil { 285 locales = strings.Split(string(output), "\n") 286 } else { 287 // If we're unable to list the locales, let's assume en_US.UTF-8 288 locales = []string{"en_US.UTF-8"} 289 ctx.Verbosef("Failed to list locales (%q), falling back to %q", err, locales) 290 } 291 292 // gettext uses LANGUAGE, which is passed directly through 293 294 // For LANG and LC_*, only preserve the evaluated version of 295 // LC_MESSAGES 296 user_lang := "" 297 if lc_all, ok := c.environ.Get("LC_ALL"); ok { 298 user_lang = lc_all 299 } else if lc_messages, ok := c.environ.Get("LC_MESSAGES"); ok { 300 user_lang = lc_messages 301 } else if lang, ok := c.environ.Get("LANG"); ok { 302 user_lang = lang 303 } 304 305 c.environ.UnsetWithPrefix("LC_") 306 307 if user_lang != "" { 308 c.environ.Set("LC_MESSAGES", user_lang) 309 } 310 311 // The for LANG, use C.UTF-8 if it exists (Debian currently, proposed 312 // for others) 313 if inList("C.UTF-8", locales) { 314 c.environ.Set("LANG", "C.UTF-8") 315 } else if inList("C.utf8", locales) { 316 // These normalize to the same thing 317 c.environ.Set("LANG", "C.UTF-8") 318 } else if inList("en_US.UTF-8", locales) { 319 c.environ.Set("LANG", "en_US.UTF-8") 320 } else if inList("en_US.utf8", locales) { 321 // These normalize to the same thing 322 c.environ.Set("LANG", "en_US.UTF-8") 323 } else { 324 ctx.Fatalln("System doesn't support either C.UTF-8 or en_US.UTF-8") 325 } 326} 327 328// Lunch configures the environment for a specific product similarly to the 329// `lunch` bash function. 330func (c *configImpl) Lunch(ctx Context, product, variant string) { 331 if variant != "eng" && variant != "userdebug" && variant != "user" { 332 ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant) 333 } 334 335 c.environ.Set("TARGET_PRODUCT", product) 336 c.environ.Set("TARGET_BUILD_VARIANT", variant) 337 c.environ.Set("TARGET_BUILD_TYPE", "release") 338 c.environ.Unset("TARGET_BUILD_APPS") 339} 340 341// Tapas configures the environment to build one or more unbundled apps, 342// similarly to the `tapas` bash function. 343func (c *configImpl) Tapas(ctx Context, apps []string, arch, variant string) { 344 if len(apps) == 0 { 345 apps = []string{"all"} 346 } 347 if variant == "" { 348 variant = "eng" 349 } 350 351 if variant != "eng" && variant != "userdebug" && variant != "user" { 352 ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant) 353 } 354 355 var product string 356 switch arch { 357 case "arm", "": 358 product = "aosp_arm" 359 case "arm64": 360 product = "aosm_arm64" 361 case "mips": 362 product = "aosp_mips" 363 case "mips64": 364 product = "aosp_mips64" 365 case "x86": 366 product = "aosp_x86" 367 case "x86_64": 368 product = "aosp_x86_64" 369 default: 370 ctx.Fatalf("Invalid architecture: %q", arch) 371 } 372 373 c.environ.Set("TARGET_PRODUCT", product) 374 c.environ.Set("TARGET_BUILD_VARIANT", variant) 375 c.environ.Set("TARGET_BUILD_TYPE", "release") 376 c.environ.Set("TARGET_BUILD_APPS", strings.Join(apps, " ")) 377} 378 379func (c *configImpl) Environment() *Environment { 380 return c.environ 381} 382 383func (c *configImpl) Arguments() []string { 384 return c.arguments 385} 386 387func (c *configImpl) OutDir() string { 388 if outDir, ok := c.environ.Get("OUT_DIR"); ok { 389 return filepath.Clean(outDir) 390 } 391 return "out" 392} 393 394func (c *configImpl) DistDir() string { 395 return c.distDir 396} 397 398func (c *configImpl) NinjaArgs() []string { 399 if c.skipMake { 400 return c.arguments 401 } 402 return c.ninjaArgs 403} 404 405func (c *configImpl) SoongOutDir() string { 406 return filepath.Join(c.OutDir(), "soong") 407} 408 409func (c *configImpl) TempDir() string { 410 return shared.TempDirForOutDir(c.SoongOutDir()) 411} 412 413func (c *configImpl) FileListDir() string { 414 return filepath.Join(c.OutDir(), ".module_paths") 415} 416 417func (c *configImpl) KatiSuffix() string { 418 if c.katiSuffix != "" { 419 return c.katiSuffix 420 } 421 panic("SetKatiSuffix has not been called") 422} 423 424// Checkbuild returns true if "checkbuild" was one of the build goals, which means that the 425// user is interested in additional checks at the expense of build time. 426func (c *configImpl) Checkbuild() bool { 427 return c.checkbuild 428} 429 430func (c *configImpl) Dist() bool { 431 return c.dist 432} 433 434func (c *configImpl) IsVerbose() bool { 435 return c.verbose 436} 437 438func (c *configImpl) SkipMake() bool { 439 return c.skipMake 440} 441 442func (c *configImpl) TargetProduct() string { 443 if v, ok := c.environ.Get("TARGET_PRODUCT"); ok { 444 return v 445 } 446 panic("TARGET_PRODUCT is not defined") 447} 448 449func (c *configImpl) TargetDevice() string { 450 return c.targetDevice 451} 452 453func (c *configImpl) SetTargetDevice(device string) { 454 c.targetDevice = device 455} 456 457func (c *configImpl) TargetBuildVariant() string { 458 if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok { 459 return v 460 } 461 panic("TARGET_BUILD_VARIANT is not defined") 462} 463 464func (c *configImpl) KatiArgs() []string { 465 return c.katiArgs 466} 467 468func (c *configImpl) Parallel() int { 469 return c.parallel 470} 471 472func (c *configImpl) UseGoma() bool { 473 if v, ok := c.environ.Get("USE_GOMA"); ok { 474 v = strings.TrimSpace(v) 475 if v != "" && v != "false" { 476 return true 477 } 478 } 479 return false 480} 481 482func (c *configImpl) StartGoma() bool { 483 if !c.UseGoma() { 484 return false 485 } 486 487 if v, ok := c.environ.Get("NOSTART_GOMA"); ok { 488 v = strings.TrimSpace(v) 489 if v != "" && v != "false" { 490 return false 491 } 492 } 493 return true 494} 495 496// RemoteParallel controls how many remote jobs (i.e., commands which contain 497// gomacc) are run in parallel. Note the parallelism of all other jobs is 498// still limited by Parallel() 499func (c *configImpl) RemoteParallel() int { 500 if v, ok := c.environ.Get("NINJA_REMOTE_NUM_JOBS"); ok { 501 if i, err := strconv.Atoi(v); err == nil { 502 return i 503 } 504 } 505 return 500 506} 507 508func (c *configImpl) SetKatiArgs(args []string) { 509 c.katiArgs = args 510} 511 512func (c *configImpl) SetNinjaArgs(args []string) { 513 c.ninjaArgs = args 514} 515 516func (c *configImpl) SetKatiSuffix(suffix string) { 517 c.katiSuffix = suffix 518} 519 520func (c *configImpl) LastKatiSuffixFile() string { 521 return filepath.Join(c.OutDir(), "last_kati_suffix") 522} 523 524func (c *configImpl) HasKatiSuffix() bool { 525 return c.katiSuffix != "" 526} 527 528func (c *configImpl) KatiEnvFile() string { 529 return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh") 530} 531 532func (c *configImpl) KatiBuildNinjaFile() string { 533 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiBuildSuffix+".ninja") 534} 535 536func (c *configImpl) KatiPackageNinjaFile() string { 537 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+katiPackageSuffix+".ninja") 538} 539 540func (c *configImpl) SoongNinjaFile() string { 541 return filepath.Join(c.SoongOutDir(), "build.ninja") 542} 543 544func (c *configImpl) CombinedNinjaFile() string { 545 if c.katiSuffix == "" { 546 return filepath.Join(c.OutDir(), "combined.ninja") 547 } 548 return filepath.Join(c.OutDir(), "combined"+c.KatiSuffix()+".ninja") 549} 550 551func (c *configImpl) SoongAndroidMk() string { 552 return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+".mk") 553} 554 555func (c *configImpl) SoongMakeVarsMk() string { 556 return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk") 557} 558 559func (c *configImpl) ProductOut() string { 560 return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice()) 561} 562 563func (c *configImpl) DevicePreviousProductConfig() string { 564 return filepath.Join(c.ProductOut(), "previous_build_config.mk") 565} 566 567func (c *configImpl) KatiPackageMkDir() string { 568 return filepath.Join(c.ProductOut(), "obj", "CONFIG", "kati_packaging") 569} 570 571func (c *configImpl) hostOutRoot() string { 572 return filepath.Join(c.OutDir(), "host") 573} 574 575func (c *configImpl) HostOut() string { 576 return filepath.Join(c.hostOutRoot(), c.HostPrebuiltTag()) 577} 578 579// This probably needs to be multi-valued, so not exporting it for now 580func (c *configImpl) hostCrossOut() string { 581 if runtime.GOOS == "linux" { 582 return filepath.Join(c.hostOutRoot(), "windows-x86") 583 } else { 584 return "" 585 } 586} 587 588func (c *configImpl) HostPrebuiltTag() string { 589 if runtime.GOOS == "linux" { 590 return "linux-x86" 591 } else if runtime.GOOS == "darwin" { 592 return "darwin-x86" 593 } else { 594 panic("Unsupported OS") 595 } 596} 597 598func (c *configImpl) PrebuiltBuildTool(name string) string { 599 if v, ok := c.environ.Get("SANITIZE_HOST"); ok { 600 if sanitize := strings.Fields(v); inList("address", sanitize) { 601 asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name) 602 if _, err := os.Stat(asan); err == nil { 603 return asan 604 } 605 } 606 } 607 return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name) 608} 609 610func (c *configImpl) SetBuildBrokenDupRules(val bool) { 611 c.brokenDupRules = val 612} 613 614func (c *configImpl) BuildBrokenDupRules() bool { 615 return c.brokenDupRules 616} 617 618func (c *configImpl) SetBuildBrokenPhonyTargets(val bool) { 619 c.brokenPhonyTargets = val 620} 621 622func (c *configImpl) BuildBrokenPhonyTargets() bool { 623 return c.brokenPhonyTargets 624} 625 626func (c *configImpl) SetBuildBrokenUsesNetwork(val bool) { 627 c.brokenUsesNetwork = val 628} 629 630func (c *configImpl) BuildBrokenUsesNetwork() bool { 631 return c.brokenUsesNetwork 632} 633 634func (c *configImpl) SetTargetDeviceDir(dir string) { 635 c.targetDeviceDir = dir 636} 637 638func (c *configImpl) TargetDeviceDir() string { 639 return c.targetDeviceDir 640} 641 642func (c *configImpl) SetPdkBuild(pdk bool) { 643 c.pdkBuild = pdk 644} 645 646func (c *configImpl) IsPdkBuild() bool { 647 return c.pdkBuild 648} 649