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