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