blob: 9907b2c8d696b9230bcbca9af8650188252707e1 [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"
24)
25
26type Config struct{ *configImpl }
27
28type configImpl struct {
29 // From the environment
30 arguments []string
31 goma bool
32 environ *Environment
33
34 // From the arguments
35 parallel int
36 keepGoing int
37 verbose bool
Dan Willemsen8a073a82017-02-04 17:30:44 -080038 dist bool
Dan Willemsen1e704462016-08-21 15:17:17 -070039
40 // From the product config
Dan Willemsen02781d52017-05-12 19:28:13 -070041 katiArgs []string
42 ninjaArgs []string
43 katiSuffix string
44 targetDevice string
Dan Willemsen1e704462016-08-21 15:17:17 -070045}
46
Dan Willemsenc2af0be2017-01-20 14:10:01 -080047const srcDirFileCheck = "build/soong/root.bp"
48
Dan Willemsen1e704462016-08-21 15:17:17 -070049func NewConfig(ctx Context, args ...string) Config {
50 ret := &configImpl{
51 environ: OsEnvironment(),
52 }
53
Dan Willemsen0c3919e2017-03-02 15:49:10 -080054 // Make sure OUT_DIR is set appropriately
Dan Willemsen02f3add2017-05-12 13:50:19 -070055 if outDir, ok := ret.environ.Get("OUT_DIR"); ok {
56 ret.environ.Set("OUT_DIR", filepath.Clean(outDir))
57 } else {
Dan Willemsen0c3919e2017-03-02 15:49:10 -080058 outDir := "out"
59 if baseDir, ok := ret.environ.Get("OUT_DIR_COMMON_BASE"); ok {
60 if wd, err := os.Getwd(); err != nil {
61 ctx.Fatalln("Failed to get working directory:", err)
62 } else {
63 outDir = filepath.Join(baseDir, filepath.Base(wd))
64 }
Dan Pasanenedb2e6c2017-08-23 08:32:09 -050065 } else {
66 outDir = filepath.Join(os.Getenv("TOP"), outDir)
Dan Willemsen0c3919e2017-03-02 15:49:10 -080067 }
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
255func (c *configImpl) KatiSuffix() string {
256 if c.katiSuffix != "" {
257 return c.katiSuffix
258 }
259 panic("SetKatiSuffix has not been called")
260}
261
Dan Willemsen8a073a82017-02-04 17:30:44 -0800262func (c *configImpl) Dist() bool {
263 return c.dist
264}
265
Dan Willemsen1e704462016-08-21 15:17:17 -0700266func (c *configImpl) IsVerbose() bool {
267 return c.verbose
268}
269
270func (c *configImpl) TargetProduct() string {
271 if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
272 return v
273 }
274 panic("TARGET_PRODUCT is not defined")
275}
276
Dan Willemsen02781d52017-05-12 19:28:13 -0700277func (c *configImpl) TargetDevice() string {
278 return c.targetDevice
279}
280
281func (c *configImpl) SetTargetDevice(device string) {
282 c.targetDevice = device
283}
284
285func (c *configImpl) TargetBuildVariant() string {
286 if v, ok := c.environ.Get("TARGET_BUILD_VARIANT"); ok {
287 return v
288 }
289 panic("TARGET_BUILD_VARIANT is not defined")
290}
291
Dan Willemsen1e704462016-08-21 15:17:17 -0700292func (c *configImpl) KatiArgs() []string {
293 return c.katiArgs
294}
295
296func (c *configImpl) Parallel() int {
297 return c.parallel
298}
299
300func (c *configImpl) UseGoma() bool {
301 if v, ok := c.environ.Get("USE_GOMA"); ok {
302 v = strings.TrimSpace(v)
303 if v != "" && v != "false" {
304 return true
305 }
306 }
307 return false
308}
309
310// RemoteParallel controls how many remote jobs (i.e., commands which contain
311// gomacc) are run in parallel. Note the paralleism of all other jobs is
312// still limited by Parallel()
313func (c *configImpl) RemoteParallel() int {
314 if v, ok := c.environ.Get("NINJA_REMOTE_NUM_JOBS"); ok {
315 if i, err := strconv.Atoi(v); err == nil {
316 return i
317 }
318 }
319 return 500
320}
321
322func (c *configImpl) SetKatiArgs(args []string) {
323 c.katiArgs = args
324}
325
326func (c *configImpl) SetNinjaArgs(args []string) {
327 c.ninjaArgs = args
328}
329
330func (c *configImpl) SetKatiSuffix(suffix string) {
331 c.katiSuffix = suffix
332}
333
334func (c *configImpl) KatiEnvFile() string {
335 return filepath.Join(c.OutDir(), "env"+c.KatiSuffix()+".sh")
336}
337
338func (c *configImpl) KatiNinjaFile() string {
339 return filepath.Join(c.OutDir(), "build"+c.KatiSuffix()+".ninja")
340}
341
342func (c *configImpl) SoongNinjaFile() string {
343 return filepath.Join(c.SoongOutDir(), "build.ninja")
344}
345
346func (c *configImpl) CombinedNinjaFile() string {
347 return filepath.Join(c.OutDir(), "combined"+c.KatiSuffix()+".ninja")
348}
349
350func (c *configImpl) SoongAndroidMk() string {
351 return filepath.Join(c.SoongOutDir(), "Android-"+c.TargetProduct()+".mk")
352}
353
354func (c *configImpl) SoongMakeVarsMk() string {
355 return filepath.Join(c.SoongOutDir(), "make_vars-"+c.TargetProduct()+".mk")
356}
357
Dan Willemsenf052f782017-05-18 15:29:04 -0700358func (c *configImpl) ProductOut() string {
359 if buildType, ok := c.environ.Get("TARGET_BUILD_TYPE"); ok && buildType == "debug" {
360 return filepath.Join(c.OutDir(), "debug", "target", "product", c.TargetDevice())
361 } else {
362 return filepath.Join(c.OutDir(), "target", "product", c.TargetDevice())
363 }
364}
365
Dan Willemsen02781d52017-05-12 19:28:13 -0700366func (c *configImpl) DevicePreviousProductConfig() string {
Dan Willemsenf052f782017-05-18 15:29:04 -0700367 return filepath.Join(c.ProductOut(), "previous_build_config.mk")
368}
369
370func (c *configImpl) hostOutRoot() string {
371 if buildType, ok := c.environ.Get("HOST_BUILD_TYPE"); ok && buildType == "debug" {
372 return filepath.Join(c.OutDir(), "debug", "host")
373 } else {
374 return filepath.Join(c.OutDir(), "host")
375 }
376}
377
378func (c *configImpl) HostOut() string {
379 return filepath.Join(c.hostOutRoot(), c.HostPrebuiltTag())
380}
381
382// This probably needs to be multi-valued, so not exporting it for now
383func (c *configImpl) hostCrossOut() string {
384 if runtime.GOOS == "linux" {
385 return filepath.Join(c.hostOutRoot(), "windows-x86")
386 } else {
387 return ""
388 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700389}
390
Dan Willemsen1e704462016-08-21 15:17:17 -0700391func (c *configImpl) HostPrebuiltTag() string {
392 if runtime.GOOS == "linux" {
393 return "linux-x86"
394 } else if runtime.GOOS == "darwin" {
395 return "darwin-x86"
396 } else {
397 panic("Unsupported OS")
398 }
399}
Dan Willemsenf173d592017-04-27 14:28:00 -0700400
Dan Willemsena3e6c522017-05-05 15:29:20 -0700401func (c *configImpl) HostAsan() bool {
Dan Willemsenf173d592017-04-27 14:28:00 -0700402 if v, ok := c.environ.Get("SANITIZE_HOST"); ok {
403 if sanitize := strings.Fields(v); inList("address", sanitize) {
Dan Willemsena3e6c522017-05-05 15:29:20 -0700404 return true
405 }
406 }
407 return false
408}
409
410func (c *configImpl) PrebuiltBuildTool(name string) string {
411 // (b/36182021) We're seeing rare ckati crashes, so always enable asan kati on the build servers.
412 if c.HostAsan() || (c.Dist() && name == "ckati") {
413 asan := filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "asan/bin", name)
414 if _, err := os.Stat(asan); err == nil {
415 return asan
Dan Willemsenf173d592017-04-27 14:28:00 -0700416 }
417 }
418 return filepath.Join("prebuilts/build-tools", c.HostPrebuiltTag(), "bin", name)
419}