blob: 16826f2462b01eeecc1c67dcc6cb251fefef0ff9 [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 Willemsen0c3919e2017-03-02 15:49:10 -080056 // Make sure OUT_DIR is set appropriately
Dan Willemsen02f3add2017-05-12 13:50:19 -070057 if outDir, ok := ret.environ.Get("OUT_DIR"); ok {
58 ret.environ.Set("OUT_DIR", filepath.Clean(outDir))
59 } else {
Dan Willemsen0c3919e2017-03-02 15:49:10 -080060 outDir := "out"
61 if baseDir, ok := ret.environ.Get("OUT_DIR_COMMON_BASE"); ok {
62 if wd, err := os.Getwd(); err != nil {
63 ctx.Fatalln("Failed to get working directory:", err)
64 } else {
65 outDir = filepath.Join(baseDir, filepath.Base(wd))
66 }
67 }
68 ret.environ.Set("OUT_DIR", outDir)
69 }
70
Dan Willemsen1e704462016-08-21 15:17:17 -070071 ret.environ.Unset(
72 // We're already using it
73 "USE_SOONG_UI",
74
75 // We should never use GOROOT/GOPATH from the shell environment
76 "GOROOT",
77 "GOPATH",
78
79 // These should only come from Soong, not the environment.
80 "CLANG",
81 "CLANG_CXX",
82 "CCC_CC",
83 "CCC_CXX",
84
85 // Used by the goma compiler wrapper, but should only be set by
86 // gomacc
87 "GOMACC_PATH",
Dan Willemsen0c3919e2017-03-02 15:49:10 -080088
89 // We handle this above
90 "OUT_DIR_COMMON_BASE",
Dan Willemsen68a09852017-04-18 13:56:57 -070091
92 // Variables that have caused problems in the past
93 "DISPLAY",
94 "GREP_OPTIONS",
Dan Willemsen1e704462016-08-21 15:17:17 -070095 )
96
97 // Tell python not to spam the source tree with .pyc files.
98 ret.environ.Set("PYTHONDONTWRITEBYTECODE", "1")
99
100 // Sane default matching ninja
101 ret.parallel = runtime.NumCPU() + 2
102 ret.keepGoing = 1
103
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800104 // Precondition: the current directory is the top of the source tree
105 if _, err := os.Stat(srcDirFileCheck); err != nil {
106 if os.IsNotExist(err) {
107 log.Fatalf("Current working directory must be the source tree. %q not found", srcDirFileCheck)
108 }
109 log.Fatalln("Error verifying tree state:", err)
110 }
111
Dan Willemsendb8457c2017-05-12 16:38:17 -0700112 if srcDir, err := filepath.Abs("."); err == nil {
113 if strings.ContainsRune(srcDir, ' ') {
114 log.Println("You are building in a directory whose absolute path contains a space character:")
115 log.Println()
116 log.Printf("%q\n", srcDir)
117 log.Println()
118 log.Fatalln("Directory names containing spaces are not supported")
119 }
120 }
121
122 if outDir := ret.OutDir(); strings.ContainsRune(outDir, ' ') {
123 log.Println("The absolute path of your output directory ($OUT_DIR) contains a space character:")
124 log.Println()
125 log.Printf("%q\n", outDir)
126 log.Println()
127 log.Fatalln("Directory names containing spaces are not supported")
128 }
129
130 if distDir := ret.DistDir(); strings.ContainsRune(distDir, ' ') {
131 log.Println("The absolute path of your dist directory ($DIST_DIR) contains a space character:")
132 log.Println()
133 log.Printf("%q\n", distDir)
134 log.Println()
135 log.Fatalln("Directory names containing spaces are not supported")
136 }
137
Dan Willemsen1e704462016-08-21 15:17:17 -0700138 for _, arg := range args {
139 arg = strings.TrimSpace(arg)
140 if arg == "--make-mode" {
141 continue
142 } else if arg == "showcommands" {
143 ret.verbose = true
144 continue
Dan Willemsen8a073a82017-02-04 17:30:44 -0800145 } else if arg == "dist" {
146 ret.dist = true
Dan Willemsen1e704462016-08-21 15:17:17 -0700147 }
148 if arg[0] == '-' {
149 var err error
150 if arg[1] == 'j' {
151 // TODO: handle space between j and number
152 // Unnecessary if used with makeparallel
153 ret.parallel, err = strconv.Atoi(arg[2:])
154 } else if arg[1] == 'k' {
155 // TODO: handle space between k and number
156 // Unnecessary if used with makeparallel
157 ret.keepGoing, err = strconv.Atoi(arg[2:])
158 } else {
159 ctx.Fatalln("Unknown option:", arg)
160 }
161 if err != nil {
162 ctx.Fatalln("Argument error:", err, arg)
163 }
164 } else {
165 ret.arguments = append(ret.arguments, arg)
166 }
167 }
168
169 return Config{ret}
170}
171
172// Lunch configures the environment for a specific product similarly to the
173// `lunch` bash function.
174func (c *configImpl) Lunch(ctx Context, product, variant string) {
175 if variant != "eng" && variant != "userdebug" && variant != "user" {
176 ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
177 }
178
179 c.environ.Set("TARGET_PRODUCT", product)
180 c.environ.Set("TARGET_BUILD_VARIANT", variant)
181 c.environ.Set("TARGET_BUILD_TYPE", "release")
182 c.environ.Unset("TARGET_BUILD_APPS")
183}
184
185// Tapas configures the environment to build one or more unbundled apps,
186// similarly to the `tapas` bash function.
187func (c *configImpl) Tapas(ctx Context, apps []string, arch, variant string) {
188 if len(apps) == 0 {
189 apps = []string{"all"}
190 }
191 if variant == "" {
192 variant = "eng"
193 }
194
195 if variant != "eng" && variant != "userdebug" && variant != "user" {
196 ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
197 }
198
199 var product string
200 switch arch {
201 case "armv5":
202 product = "generic_armv5"
203 case "arm", "":
204 product = "aosp_arm"
205 case "arm64":
206 product = "aosm_arm64"
207 case "mips":
208 product = "aosp_mips"
209 case "mips64":
210 product = "aosp_mips64"
211 case "x86":
212 product = "aosp_x86"
213 case "x86_64":
214 product = "aosp_x86_64"
215 default:
216 ctx.Fatalf("Invalid architecture: %q", arch)
217 }
218
219 c.environ.Set("TARGET_PRODUCT", product)
220 c.environ.Set("TARGET_BUILD_VARIANT", variant)
221 c.environ.Set("TARGET_BUILD_TYPE", "release")
222 c.environ.Set("TARGET_BUILD_APPS", strings.Join(apps, " "))
223}
224
225func (c *configImpl) Environment() *Environment {
226 return c.environ
227}
228
229func (c *configImpl) Arguments() []string {
230 return c.arguments
231}
232
233func (c *configImpl) OutDir() string {
234 if outDir, ok := c.environ.Get("OUT_DIR"); ok {
235 return outDir
236 }
237 return "out"
238}
239
Dan Willemsen8a073a82017-02-04 17:30:44 -0800240func (c *configImpl) DistDir() string {
241 if distDir, ok := c.environ.Get("DIST_DIR"); ok {
242 return distDir
243 }
244 return filepath.Join(c.OutDir(), "dist")
245}
246
Dan Willemsen1e704462016-08-21 15:17:17 -0700247func (c *configImpl) NinjaArgs() []string {
248 return c.ninjaArgs
249}
250
251func (c *configImpl) SoongOutDir() string {
252 return filepath.Join(c.OutDir(), "soong")
253}
254
Jeff Gastonefc1b412017-03-29 17:29:06 -0700255func (c *configImpl) TempDir() string {
256 return shared.TempDirForOutDir(c.SoongOutDir())
257}
258
Dan Willemsen1e704462016-08-21 15:17:17 -0700259func (c *configImpl) KatiSuffix() string {
260 if c.katiSuffix != "" {
261 return c.katiSuffix
262 }
263 panic("SetKatiSuffix has not been called")
264}
265
Dan Willemsen8a073a82017-02-04 17:30:44 -0800266func (c *configImpl) Dist() bool {
267 return c.dist
268}
269
Dan Willemsen1e704462016-08-21 15:17:17 -0700270func (c *configImpl) IsVerbose() bool {
271 return c.verbose
272}
273
274func (c *configImpl) TargetProduct() string {
275 if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
276 return v
277 }
278 panic("TARGET_PRODUCT is not defined")
279}
280
Dan Willemsen02781d52017-05-12 19:28:13 -0700281func (c *configImpl) TargetDevice() string {
282 return c.targetDevice
283}
284
285func (c *configImpl) SetTargetDevice(device string) {
286 c.targetDevice = device
287}
288
289func (c *configImpl) TargetBuildVariant() string {
290 if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok {
291 return v
292 }
293 panic("TARGET_BUILD_VARIANT is not defined")
294}
295
Dan Willemsen1e704462016-08-21 15:17:17 -0700296func (c *configImpl) KatiArgs() []string {
297 return c.katiArgs
298}
299
300func (c *configImpl) Parallel() int {
301 return c.parallel
302}
303
304func (c *configImpl) UseGoma() bool {
305 if v, ok := c.environ.Get("USE_GOMA"); ok {
306 v = strings.TrimSpace(v)
307 if v != "" && v != "false" {
308 return true
309 }
310 }
311 return false
312}
313
314// RemoteParallel controls how many remote jobs (i.e., commands which contain
Jeff Gastonefc1b412017-03-29 17:29:06 -0700315// gomacc) are run in parallel. Note the parallelism of all other jobs is
Dan Willemsen1e704462016-08-21 15:17:17 -0700316// still limited by Parallel()
317func (c *configImpl) RemoteParallel() int {
318 if v, ok := c.environ.Get("NINJA_REMOTE_NUM_JOBS"); ok {
319 if i, err := strconv.Atoi(v); err == nil {
320 return i
321 }
322 }
323 return 500
324}
325
326func (c *configImpl) SetKatiArgs(args []string) {
327 c.katiArgs = args
328}
329
330func (c *configImpl) SetNinjaArgs(args []string) {
331 c.ninjaArgs = args
332}
333
334func (c *configImpl) SetKatiSuffix(suffix string) {
335 c.katiSuffix = suffix
336}
337
338func (c *configImpl) KatiEnvFile() string {
339 return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh")
340}
341
342func (c *configImpl) KatiNinjaFile() string {
343 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+".ninja")
344}
345
346func (c *configImpl) SoongNinjaFile() string {
347 return filepath.Join(c.SoongOutDir(), "build.ninja")
348}
349
350func (c *configImpl) CombinedNinjaFile() string {
351 return filepath.Join(c.OutDir(), "combined"+c.KatiSuffix()+".ninja")
352}
353
354func (c *configImpl) SoongAndroidMk() string {
355 return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+".mk")
356}
357
358func (c *configImpl) SoongMakeVarsMk() string {
359 return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk")
360}
361
Dan Willemsenf052f782017-05-18 15:29:04 -0700362func (c *configImpl) ProductOut() string {
363 if buildType, ok := c.environ.Get("TARGET_BUILD_TYPE"); ok && buildType == "debug" {
364 return filepath.Join(c.OutDir(), "debug", "target", "product", c.TargetDevice())
365 } else {
366 return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice())
367 }
368}
369
Dan Willemsen02781d52017-05-12 19:28:13 -0700370func (c *configImpl) DevicePreviousProductConfig() string {
Dan Willemsenf052f782017-05-18 15:29:04 -0700371 return filepath.Join(c.ProductOut(), "previous_build_config.mk")
372}
373
374func (c *configImpl) hostOutRoot() string {
375 if buildType, ok := c.environ.Get("HOST_BUILD_TYPE"); ok && buildType == "debug" {
376 return filepath.Join(c.OutDir(), "debug", "host")
377 } else {
378 return filepath.Join(c.OutDir(), "host")
379 }
380}
381
382func (c *configImpl) HostOut() string {
383 return filepath.Join(c.hostOutRoot(), c.HostPrebuiltTag())
384}
385
386// This probably needs to be multi-valued, so not exporting it for now
387func (c *configImpl) hostCrossOut() string {
388 if runtime.GOOS == "linux" {
389 return filepath.Join(c.hostOutRoot(), "windows-x86")
390 } else {
391 return ""
392 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700393}
394
Dan Willemsen1e704462016-08-21 15:17:17 -0700395func (c *configImpl) HostPrebuiltTag() string {
396 if runtime.GOOS == "linux" {
397 return "linux-x86"
398 } else if runtime.GOOS == "darwin" {
399 return "darwin-x86"
400 } else {
401 panic("Unsupported OS")
402 }
403}
Dan Willemsenf173d592017-04-27 14:28:00 -0700404
Dan Willemsena3e6c522017-05-05 15:29:20 -0700405func (c *configImpl) HostAsan() bool {
Dan Willemsenf173d592017-04-27 14:28:00 -0700406 if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
407 if sanitize := strings.Fields(v); inList("address", sanitize) {
Dan Willemsena3e6c522017-05-05 15:29:20 -0700408 return true
409 }
410 }
411 return false
412}
413
414func (c *configImpl) PrebuiltBuildTool(name string) string {
415 // (b/36182021) We're seeing rare ckati crashes, so always enable asan kati on the build servers.
416 if c.HostAsan() || (c.Dist() && name == "ckati") {
417 asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
418 if _, err := os.Stat(asan); err == nil {
419 return asan
Dan Willemsenf173d592017-04-27 14:28:00 -0700420 }
421 }
422 return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name)
423}