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