Home | History | Annotate | Download | only in build
      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