blob: a451cfe5f71e7294c5b89adbbc640d9eb032cc96 [file] [log] [blame]
Dan Willemsen1e704462016-08-21 15:17:17 -07001// 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 (
Dan Willemsenc2af0be2017-01-20 14:10:01 -080018 "log"
19 "os"
Dan Willemsen1e704462016-08-21 15:17:17 -070020 "path/filepath"
21 "runtime"
22 "strconv"
23 "strings"
Jeff Gastonefc1b412017-03-29 17:29:06 -070024
25 "android/soong/shared"
Dan Willemsen1e704462016-08-21 15:17:17 -070026)
27
28type Config struct{ *configImpl }
29
30type configImpl struct {
31 // From the environment
32 arguments []string
33 goma bool
34 environ *Environment
35
36 // From the arguments
37 parallel int
38 keepGoing int
39 verbose bool
Dan Willemsen8a073a82017-02-04 17:30:44 -080040 dist bool
Dan Willemsen1e704462016-08-21 15:17:17 -070041
42 // From the product config
Dan Willemsen02781d52017-05-12 19:28:13 -070043 katiArgs []string
44 ninjaArgs []string
45 katiSuffix string
46 targetDevice string
Dan Willemsen1e704462016-08-21 15:17:17 -070047}
48
Dan Willemsenc2af0be2017-01-20 14:10:01 -080049const srcDirFileCheck = "build/soong/root.bp"
50
Dan Willemsen1e704462016-08-21 15:17:17 -070051func NewConfig(ctx Context, args ...string) Config {
52 ret := &configImpl{
53 environ: OsEnvironment(),
54 }
55
Dan Willemsen9b587492017-07-10 22:13:00 -070056 // Sane default matching ninja
57 ret.parallel = runtime.NumCPU() + 2
58 ret.keepGoing = 1
59
60 ret.parseArgs(ctx, args)
61
Dan Willemsen0c3919e2017-03-02 15:49:10 -080062 // Make sure OUT_DIR is set appropriately
Dan Willemsen02f3add2017-05-12 13:50:19 -070063 if outDir, ok := ret.environ.Get("OUT_DIR"); ok {
64 ret.environ.Set("OUT_DIR", filepath.Clean(outDir))
65 } else {
Dan Willemsen0c3919e2017-03-02 15:49:10 -080066 outDir := "out"
67 if baseDir, ok := ret.environ.Get("OUT_DIR_COMMON_BASE"); ok {
68 if wd, err := os.Getwd(); err != nil {
69 ctx.Fatalln("Failed to get working directory:", err)
70 } else {
71 outDir = filepath.Join(baseDir, filepath.Base(wd))
72 }
73 }
74 ret.environ.Set("OUT_DIR", outDir)
75 }
76
Dan Willemsen1e704462016-08-21 15:17:17 -070077 ret.environ.Unset(
78 // We're already using it
79 "USE_SOONG_UI",
80
81 // We should never use GOROOT/GOPATH from the shell environment
82 "GOROOT",
83 "GOPATH",
84
85 // These should only come from Soong, not the environment.
86 "CLANG",
87 "CLANG_CXX",
88 "CCC_CC",
89 "CCC_CXX",
90
91 // Used by the goma compiler wrapper, but should only be set by
92 // gomacc
93 "GOMACC_PATH",
Dan Willemsen0c3919e2017-03-02 15:49:10 -080094
95 // We handle this above
96 "OUT_DIR_COMMON_BASE",
Dan Willemsen68a09852017-04-18 13:56:57 -070097
98 // Variables that have caused problems in the past
99 "DISPLAY",
100 "GREP_OPTIONS",
Dan Willemsen1e704462016-08-21 15:17:17 -0700101 )
102
103 // Tell python not to spam the source tree with .pyc files.
104 ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
105
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800106 // Precondition: the current directory is the top of the source tree
107 if _, err := os.Stat(srcDirFileCheck); err != nil {
108 if os.IsNotExist(err) {
109 log.Fatalf("Current working directory must be the source tree. %q not found", srcDirFileCheck)
110 }
111 log.Fatalln("Error verifying tree state:", err)
112 }
113
Dan Willemsendb8457c2017-05-12 16:38:17 -0700114 if srcDir, err := filepath.Abs("."); err == nil {
115 if strings.ContainsRune(srcDir, ' ') {
116 log.Println("You are building in a directory whose absolute path contains a space character:")
117 log.Println()
118 log.Printf("%q\n", srcDir)
119 log.Println()
120 log.Fatalln("Directory names containing spaces are not supported")
121 }
122 }
123
124 if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') {
125 log.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:")
126 log.Println()
127 log.Printf("%q\n", outDir)
128 log.Println()
129 log.Fatalln("Directory names containing spaces are not supported")
130 }
131
132 if distDir := ret.DistDir(); strings.ContainsRune(distDir, ' ') {
133 log.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:")
134 log.Println()
135 log.Printf("%q\n", distDir)
136 log.Println()
137 log.Fatalln("Directory names containing spaces are not supported")
138 }
139
Dan Willemsen9b587492017-07-10 22:13:00 -0700140 return Config{ret}
141}
142
143func (c *configImpl) parseArgs(ctx Context, args []string) {
144 for i := 0; i < len(args); i++ {
145 arg := strings.TrimSpace(args[i])
Dan Willemsen1e704462016-08-21 15:17:17 -0700146 if arg == "--make-mode" {
147 continue
148 } else if arg == "showcommands" {
Dan Willemsen9b587492017-07-10 22:13:00 -0700149 c.verbose = true
Dan Willemsen1e704462016-08-21 15:17:17 -0700150 continue
Dan Willemsen8a073a82017-02-04 17:30:44 -0800151 } else if arg == "dist" {
Dan Willemsen9b587492017-07-10 22:13:00 -0700152 c.dist = true
Dan Willemsen1e704462016-08-21 15:17:17 -0700153 }
154 if arg[0] == '-' {
Dan Willemsen9b587492017-07-10 22:13:00 -0700155 parseArgNum := func(def int) int {
156 if len(arg) > 2 {
157 p, err := strconv.ParseUint(arg[2:], 10, 31)
158 if err != nil {
159 ctx.Fatalf("Failed to parse %q: %v", arg, err)
160 }
161 return int(p)
162 } else if i+1 < len(args) {
163 p, err := strconv.ParseUint(args[i+1], 10, 31)
164 if err == nil {
165 i++
166 return int(p)
167 }
168 }
169 return def
170 }
171
Dan Willemsen1e704462016-08-21 15:17:17 -0700172 if arg[1] == 'j' {
Dan Willemsen9b587492017-07-10 22:13:00 -0700173 c.parallel = parseArgNum(c.parallel)
Dan Willemsen1e704462016-08-21 15:17:17 -0700174 } else if arg[1] == 'k' {
Dan Willemsen9b587492017-07-10 22:13:00 -0700175 c.keepGoing = parseArgNum(0)
Dan Willemsen1e704462016-08-21 15:17:17 -0700176 } else {
177 ctx.Fatalln("Unknown option:", arg)
178 }
Dan Willemsen091525e2017-07-11 14:17:50 -0700179 } else if k, v, ok := decodeKeyValue(arg); ok && len(k) > 0 {
180 c.environ.Set(k, v)
Dan Willemsen1e704462016-08-21 15:17:17 -0700181 } else {
Dan Willemsen9b587492017-07-10 22:13:00 -0700182 c.arguments = append(c.arguments, arg)
Dan Willemsen1e704462016-08-21 15:17:17 -0700183 }
184 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700185}
186
187// Lunch configures the environment for a specific product similarly to the
188// `lunch` bash function.
189func (c *configImpl) Lunch(ctx Context, product, variant string) {
190 if variant != "eng" && variant != "userdebug" && variant != "user" {
191 ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
192 }
193
194 c.environ.Set("TARGET_PRODUCT", product)
195 c.environ.Set("TARGET_BUILD_VARIANT", variant)
196 c.environ.Set("TARGET_BUILD_TYPE", "release")
197 c.environ.Unset("TARGET_BUILD_APPS")
198}
199
200// Tapas configures the environment to build one or more unbundled apps,
201// similarly to the `tapas` bash function.
202func (c *configImpl) Tapas(ctx Context, apps []string, arch, variant string) {
203 if len(apps) == 0 {
204 apps = []string{"all"}
205 }
206 if variant == "" {
207 variant = "eng"
208 }
209
210 if variant != "eng" && variant != "userdebug" && variant != "user" {
211 ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
212 }
213
214 var product string
215 switch arch {
216 case "armv5":
217 product = "generic_armv5"
218 case "arm", "":
219 product = "aosp_arm"
220 case "arm64":
221 product = "aosm_arm64"
222 case "mips":
223 product = "aosp_mips"
224 case "mips64":
225 product = "aosp_mips64"
226 case "x86":
227 product = "aosp_x86"
228 case "x86_64":
229 product = "aosp_x86_64"
230 default:
231 ctx.Fatalf("Invalid architecture: %q", arch)
232 }
233
234 c.environ.Set("TARGET_PRODUCT", product)
235 c.environ.Set("TARGET_BUILD_VARIANT", variant)
236 c.environ.Set("TARGET_BUILD_TYPE", "release")
237 c.environ.Set("TARGET_BUILD_APPS", strings.Join(apps, " "))
238}
239
240func (c *configImpl) Environment() *Environment {
241 return c.environ
242}
243
244func (c *configImpl) Arguments() []string {
245 return c.arguments
246}
247
248func (c *configImpl) OutDir() string {
249 if outDir, ok := c.environ.Get("OUT_DIR"); ok {
250 return outDir
251 }
252 return "out"
253}
254
Dan Willemsen8a073a82017-02-04 17:30:44 -0800255func (c *configImpl) DistDir() string {
256 if distDir, ok := c.environ.Get("DIST_DIR"); ok {
257 return distDir
258 }
259 return filepath.Join(c.OutDir(), "dist")
260}
261
Dan Willemsen1e704462016-08-21 15:17:17 -0700262func (c *configImpl) NinjaArgs() []string {
263 return c.ninjaArgs
264}
265
266func (c *configImpl) SoongOutDir() string {
267 return filepath.Join(c.OutDir(), "soong")
268}
269
Jeff Gastonefc1b412017-03-29 17:29:06 -0700270func (c *configImpl) TempDir() string {
271 return shared.TempDirForOutDir(c.SoongOutDir())
272}
273
Dan Willemsen1e704462016-08-21 15:17:17 -0700274func (c *configImpl) KatiSuffix() string {
275 if c.katiSuffix != "" {
276 return c.katiSuffix
277 }
278 panic("SetKatiSuffix has not been called")
279}
280
Dan Willemsen8a073a82017-02-04 17:30:44 -0800281func (c *configImpl) Dist() bool {
282 return c.dist
283}
284
Dan Willemsen1e704462016-08-21 15:17:17 -0700285func (c *configImpl) IsVerbose() bool {
286 return c.verbose
287}
288
289func (c *configImpl) TargetProduct() string {
290 if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
291 return v
292 }
293 panic("TARGET_PRODUCT is not defined")
294}
295
Dan Willemsen02781d52017-05-12 19:28:13 -0700296func (c *configImpl) TargetDevice() string {
297 return c.targetDevice
298}
299
300func (c *configImpl) SetTargetDevice(device string) {
301 c.targetDevice = device
302}
303
304func (c *configImpl) TargetBuildVariant() string {
305 if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok {
306 return v
307 }
308 panic("TARGET_BUILD_VARIANT is not defined")
309}
310
Dan Willemsen1e704462016-08-21 15:17:17 -0700311func (c *configImpl) KatiArgs() []string {
312 return c.katiArgs
313}
314
315func (c *configImpl) Parallel() int {
316 return c.parallel
317}
318
319func (c *configImpl) UseGoma() bool {
320 if v, ok := c.environ.Get("USE_GOMA"); ok {
321 v = strings.TrimSpace(v)
322 if v != "" && v != "false" {
323 return true
324 }
325 }
326 return false
327}
328
329// RemoteParallel controls how many remote jobs (i.e., commands which contain
Jeff Gastonefc1b412017-03-29 17:29:06 -0700330// gomacc) are run in parallel. Note the parallelism of all other jobs is
Dan Willemsen1e704462016-08-21 15:17:17 -0700331// still limited by Parallel()
332func (c *configImpl) RemoteParallel() int {
333 if v, ok := c.environ.Get("NINJA_REMOTE_NUM_JOBS"); ok {
334 if i, err := strconv.Atoi(v); err == nil {
335 return i
336 }
337 }
338 return 500
339}
340
341func (c *configImpl) SetKatiArgs(args []string) {
342 c.katiArgs = args
343}
344
345func (c *configImpl) SetNinjaArgs(args []string) {
346 c.ninjaArgs = args
347}
348
349func (c *configImpl) SetKatiSuffix(suffix string) {
350 c.katiSuffix = suffix
351}
352
353func (c *configImpl) KatiEnvFile() string {
354 return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh")
355}
356
357func (c *configImpl) KatiNinjaFile() string {
358 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+".ninja")
359}
360
361func (c *configImpl) SoongNinjaFile() string {
362 return filepath.Join(c.SoongOutDir(), "build.ninja")
363}
364
365func (c *configImpl) CombinedNinjaFile() string {
366 return filepath.Join(c.OutDir(), "combined"+c.KatiSuffix()+".ninja")
367}
368
369func (c *configImpl) SoongAndroidMk() string {
370 return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+".mk")
371}
372
373func (c *configImpl) SoongMakeVarsMk() string {
374 return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk")
375}
376
Dan Willemsenf052f782017-05-18 15:29:04 -0700377func (c *configImpl) ProductOut() string {
378 if buildType, ok := c.environ.Get("TARGET_BUILD_TYPE"); ok && buildType == "debug" {
379 return filepath.Join(c.OutDir(), "debug", "target", "product", c.TargetDevice())
380 } else {
381 return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice())
382 }
383}
384
Dan Willemsen02781d52017-05-12 19:28:13 -0700385func (c *configImpl) DevicePreviousProductConfig() string {
Dan Willemsenf052f782017-05-18 15:29:04 -0700386 return filepath.Join(c.ProductOut(), "previous_build_config.mk")
387}
388
389func (c *configImpl) hostOutRoot() string {
390 if buildType, ok := c.environ.Get("HOST_BUILD_TYPE"); ok && buildType == "debug" {
391 return filepath.Join(c.OutDir(), "debug", "host")
392 } else {
393 return filepath.Join(c.OutDir(), "host")
394 }
395}
396
397func (c *configImpl) HostOut() string {
398 return filepath.Join(c.hostOutRoot(), c.HostPrebuiltTag())
399}
400
401// This probably needs to be multi-valued, so not exporting it for now
402func (c *configImpl) hostCrossOut() string {
403 if runtime.GOOS == "linux" {
404 return filepath.Join(c.hostOutRoot(), "windows-x86")
405 } else {
406 return ""
407 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700408}
409
Dan Willemsen1e704462016-08-21 15:17:17 -0700410func (c *configImpl) HostPrebuiltTag() string {
411 if runtime.GOOS == "linux" {
412 return "linux-x86"
413 } else if runtime.GOOS == "darwin" {
414 return "darwin-x86"
415 } else {
416 panic("Unsupported OS")
417 }
418}
Dan Willemsenf173d592017-04-27 14:28:00 -0700419
Dan Willemsena3e6c522017-05-05 15:29:20 -0700420func (c *configImpl) HostAsan() bool {
Dan Willemsenf173d592017-04-27 14:28:00 -0700421 if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
422 if sanitize := strings.Fields(v); inList("address", sanitize) {
Dan Willemsena3e6c522017-05-05 15:29:20 -0700423 return true
424 }
425 }
426 return false
427}
428
429func (c *configImpl) PrebuiltBuildTool(name string) string {
430 // (b/36182021) We're seeing rare ckati crashes, so always enable asan kati on the build servers.
431 if c.HostAsan() || (c.Dist() && name == "ckati") {
432 asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
433 if _, err := os.Stat(asan); err == nil {
434 return asan
Dan Willemsenf173d592017-04-27 14:28:00 -0700435 }
436 }
437 return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name)
438}