blob: da44b6568ac58f136cfb7661070ace6a529efa24 [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 Willemsendb8457c2017-05-12 16:38:17 -070018 "io/ioutil"
Dan Willemsen1e704462016-08-21 15:17:17 -070019 "os"
Dan Willemsen1e704462016-08-21 15:17:17 -070020 "path/filepath"
21 "text/template"
Colin Cross74cda722020-01-16 15:25:50 -080022
23 "android/soong/ui/metrics"
Dan Willemsen1e704462016-08-21 15:17:17 -070024)
25
Jingwen Chencda22c92020-11-23 00:22:30 -050026// SetupOutDir ensures the out directory exists, and has the proper files to
27// prevent kati from recursing into it.
Dan Willemsen1e704462016-08-21 15:17:17 -070028func SetupOutDir(ctx Context, config Config) {
29 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk"))
30 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
Anton Hansson5e5c48b2020-11-27 12:35:20 +000031 if !config.SkipKati() {
Jingwen Chencda22c92020-11-23 00:22:30 -050032 // Run soong_build with Kati for a hybrid build, e.g. running the
33 // AndroidMk singleton and postinstall commands. Communicate this to
34 // soong_build by writing an empty .soong.kati_enabled marker file in the
35 // soong_build output directory for the soong_build primary builder to
36 // know if the user wants to run Kati after.
37 //
38 // This does not preclude running Kati for *product configuration purposes*.
39 ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.kati_enabled"))
Dan Willemsene0879fc2017-08-04 15:06:27 -070040 }
Dan Willemsen1e704462016-08-21 15:17:17 -070041 // The ninja_build file is used by our buildbots to understand that the output
42 // can be parsed as ninja output.
43 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build"))
Jeff Gastonb64fc1c2017-08-04 12:30:12 -070044 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
Colin Cross28f527c2019-11-26 16:19:04 -080045
46 if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok {
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +000047 err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0666) // a+rw
Colin Cross28f527c2019-11-26 16:19:04 -080048 if err != nil {
49 ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
50 }
51 } else {
52 ctx.Fatalln("Missing BUILD_DATETIME_FILE")
53 }
Dan Willemsen1e704462016-08-21 15:17:17 -070054}
55
56var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
57builddir = {{.OutDir}}
Colin Cross8b8bec32019-11-15 13:18:43 -080058{{if .UseRemoteBuild }}pool local_pool
Dan Willemsen29971232018-09-26 14:58:30 -070059 depth = {{.Parallel}}
Colin Cross8b8bec32019-11-15 13:18:43 -080060{{end -}}
61pool highmem_pool
62 depth = {{.HighmemParallel}}
Dan Willemsenfb1271a2018-09-26 15:00:42 -070063{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
64subninja {{.KatiPackageNinjaFile}}
Dan Willemsene0879fc2017-08-04 15:06:27 -070065{{end -}}
Dan Willemsenfb1271a2018-09-26 15:00:42 -070066subninja {{.SoongNinjaFile}}
Dan Willemsen1e704462016-08-21 15:17:17 -070067`))
68
69func createCombinedBuildNinjaFile(ctx Context, config Config) {
Anton Hansson5e5c48b2020-11-27 12:35:20 +000070 // If we're in SkipKati mode, skip creating this file if it already exists
71 if config.SkipKati() {
Dan Willemsene0879fc2017-08-04 15:06:27 -070072 if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) {
73 return
74 }
75 }
76
Dan Willemsen1e704462016-08-21 15:17:17 -070077 file, err := os.Create(config.CombinedNinjaFile())
78 if err != nil {
79 ctx.Fatalln("Failed to create combined ninja file:", err)
80 }
81 defer file.Close()
82
83 if err := combinedBuildNinjaTemplate.Execute(file, config); err != nil {
84 ctx.Fatalln("Failed to write combined ninja file:", err)
85 }
86}
87
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +000088// These are bitmasks which can be used to check whether various flags are set e.g. whether to use Bazel.
Dan Willemsen1e704462016-08-21 15:17:17 -070089const (
Anton Hansson5a7861a2021-06-04 10:09:01 +010090 _ = iota
91 // Whether to run the kati config step.
92 RunProductConfig = 1 << iota
93 // Whether to run soong to generate a ninja file.
94 RunSoong = 1 << iota
95 // Whether to run kati to generate a ninja file.
96 RunKati = 1 << iota
97 // Whether to run ninja on the combined ninja.
98 RunNinja = 1 << iota
99 // Whether to run bazel on the combined ninja.
100 RunBazel = 1 << iota
101 RunBuildTests = 1 << iota
102 RunAll = RunProductConfig | RunSoong | RunKati | RunNinja
103 RunAllWithBazel = RunProductConfig | RunSoong | RunKati | RunBazel
Dan Willemsen1e704462016-08-21 15:17:17 -0700104)
105
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000106// checkProblematicFiles fails the build if existing Android.mk or CleanSpec.mk files are found at the root of the tree.
Anton Hanssonecf0f102018-09-19 22:14:17 +0100107func checkProblematicFiles(ctx Context) {
108 files := []string{"Android.mk", "CleanSpec.mk"}
109 for _, file := range files {
110 if _, err := os.Stat(file); !os.IsNotExist(err) {
111 absolute := absPath(ctx, file)
112 ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file)
113 ctx.Fatalf(" rm %s\n", absolute)
114 }
115 }
116}
117
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000118// checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
Dan Willemsendb8457c2017-05-12 16:38:17 -0700119func checkCaseSensitivity(ctx Context, config Config) {
120 outDir := config.OutDir()
121 lowerCase := filepath.Join(outDir, "casecheck.txt")
122 upperCase := filepath.Join(outDir, "CaseCheck.txt")
123 lowerData := "a"
124 upperData := "B"
125
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000126 if err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0666); err != nil { // a+rw
Dan Willemsendb8457c2017-05-12 16:38:17 -0700127 ctx.Fatalln("Failed to check case sensitivity:", err)
128 }
129
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000130 if err := ioutil.WriteFile(upperCase, []byte(upperData), 0666); err != nil { // a+rw
Dan Willemsendb8457c2017-05-12 16:38:17 -0700131 ctx.Fatalln("Failed to check case sensitivity:", err)
132 }
133
134 res, err := ioutil.ReadFile(lowerCase)
135 if err != nil {
136 ctx.Fatalln("Failed to check case sensitivity:", err)
137 }
138
139 if string(res) != lowerData {
140 ctx.Println("************************************************************")
141 ctx.Println("You are building on a case-insensitive filesystem.")
142 ctx.Println("Please move your source tree to a case-sensitive filesystem.")
143 ctx.Println("************************************************************")
144 ctx.Fatalln("Case-insensitive filesystems not supported")
145 }
146}
147
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000148// help prints a help/usage message, via the build/make/help.sh script.
149func help(ctx Context, config Config) {
Jeff Gastondf4a0812017-05-30 20:11:20 -0700150 cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
Dan Willemsenb2e6c2e2017-07-13 17:24:44 -0700151 cmd.Sandbox = dumpvarsSandbox
Dan Willemsenb82471a2018-05-17 16:37:09 -0700152 cmd.RunAndPrintOrFatal()
Dan Willemsen02781d52017-05-12 19:28:13 -0700153}
154
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000155// checkRAM warns if there probably isn't enough RAM to complete a build.
156func checkRAM(ctx Context, config Config) {
Dan Willemsen570a2922020-05-26 23:02:29 -0700157 if totalRAM := config.TotalRAM(); totalRAM != 0 {
158 ram := float32(totalRAM) / (1024 * 1024 * 1024)
159 ctx.Verbosef("Total RAM: %.3vGB", ram)
160
161 if ram <= 16 {
162 ctx.Println("************************************************************")
163 ctx.Printf("You are building on a machine with %.3vGB of RAM\n", ram)
164 ctx.Println("")
165 ctx.Println("The minimum required amount of free memory is around 16GB,")
166 ctx.Println("and even with that, some configurations may not work.")
167 ctx.Println("")
168 ctx.Println("If you run into segfaults or other errors, try reducing your")
169 ctx.Println("-j value.")
170 ctx.Println("************************************************************")
171 } else if ram <= float32(config.Parallel()) {
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000172 // Want at least 1GB of RAM per job.
Dan Willemsen570a2922020-05-26 23:02:29 -0700173 ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
174 ctx.Println("If you run into segfaults or other errors, try a lower -j value")
175 }
176 }
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000177}
178
179// Build the tree. The 'what' argument can be used to chose which components of
180// the build to run, via checking various bitmasks.
Anton Hansson5a7861a2021-06-04 10:09:01 +0100181func Build(ctx Context, config Config) {
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000182 ctx.Verboseln("Starting build with args:", config.Arguments())
183 ctx.Verboseln("Environment:", config.Environment().Environ())
Dan Willemsen1e704462016-08-21 15:17:17 -0700184
Colin Cross74cda722020-01-16 15:25:50 -0800185 ctx.BeginTrace(metrics.Total, "total")
186 defer ctx.EndTrace()
187
Dan Willemsen1e704462016-08-21 15:17:17 -0700188 if inList("help", config.Arguments()) {
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000189 help(ctx, config)
Dan Willemsen0b73b4b2017-05-12 19:28:13 -0700190 return
Dan Willemsen1e704462016-08-21 15:17:17 -0700191 }
192
Jeff Gaston3615fe82017-05-24 13:14:34 -0700193 // Make sure that no other Soong process is running with the same output directory
194 buildLock := BecomeSingletonOrFail(ctx, config)
195 defer buildLock.Unlock()
196
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000197 if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
198 clean(ctx, config)
199 return
200 }
201
202 // checkProblematicFiles aborts the build if Android.mk or CleanSpec.mk are found at the root of the tree.
Anton Hanssonecf0f102018-09-19 22:14:17 +0100203 checkProblematicFiles(ctx)
204
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000205 checkRAM(ctx, config)
206
Dan Willemsen1e704462016-08-21 15:17:17 -0700207 SetupOutDir(ctx, config)
208
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000209 // checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
Dan Willemsendb8457c2017-05-12 16:38:17 -0700210 checkCaseSensitivity(ctx, config)
211
Jeff Gastonefc1b412017-03-29 17:29:06 -0700212 ensureEmptyDirectoriesExist(ctx, config.TempDir())
213
Dan Willemsen18490112018-05-25 16:30:04 -0700214 SetupPath(ctx, config)
215
Anton Hansson5a7861a2021-06-04 10:09:01 +0100216 what := RunAll
217 if config.UseBazel() {
218 what = RunAllWithBazel
219 }
220 if config.Checkbuild() {
221 what |= RunBuildTests
222 }
Anton Hansson5e5c48b2020-11-27 12:35:20 +0000223 if config.SkipConfig() {
224 ctx.Verboseln("Skipping Config as requested")
Anton Hansson5a7861a2021-06-04 10:09:01 +0100225 what = what &^ RunProductConfig
Anton Hansson5e5c48b2020-11-27 12:35:20 +0000226 }
Anton Hansson5e5c48b2020-11-27 12:35:20 +0000227 if config.SkipKati() {
228 ctx.Verboseln("Skipping Kati as requested")
Anton Hansson5a7861a2021-06-04 10:09:01 +0100229 what = what &^ RunKati
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000230 }
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100231 if config.SkipNinja() {
232 ctx.Verboseln("Skipping Ninja as requested")
Anton Hansson5a7861a2021-06-04 10:09:01 +0100233 what = what &^ RunNinja
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100234 }
235
Yoshisato Yanagisawa2cb0e5d2019-01-10 10:14:16 +0900236 if config.StartGoma() {
Yoshisato Yanagisawa2cb0e5d2019-01-10 10:14:16 +0900237 startGoma(ctx, config)
238 }
239
Ramy Medhatbbf25672019-07-17 12:30:04 +0000240 if config.StartRBE() {
Ramy Medhatbbf25672019-07-17 12:30:04 +0000241 startRBE(ctx, config)
242 }
243
Anton Hansson5a7861a2021-06-04 10:09:01 +0100244 if what&RunProductConfig != 0 {
Dan Willemsen1e704462016-08-21 15:17:17 -0700245 runMakeProductConfig(ctx, config)
246 }
247
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000248 // Everything below here depends on product config.
249
Colin Cross806fd942019-05-03 13:35:58 -0700250 if inList("installclean", config.Arguments()) ||
251 inList("install-clean", config.Arguments()) {
Rupert Shuttleworth1f304e62020-11-24 14:13:41 +0000252 installClean(ctx, config)
Dan Willemsenf052f782017-05-18 15:29:04 -0700253 ctx.Println("Deleted images and staging directories.")
254 return
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000255 }
256
257 if inList("dataclean", config.Arguments()) ||
Colin Cross806fd942019-05-03 13:35:58 -0700258 inList("data-clean", config.Arguments()) {
Rupert Shuttleworth1f304e62020-11-24 14:13:41 +0000259 dataClean(ctx, config)
Dan Willemsenf052f782017-05-18 15:29:04 -0700260 ctx.Println("Deleted data files.")
261 return
262 }
263
Anton Hansson5a7861a2021-06-04 10:09:01 +0100264 if what&RunSoong != 0 {
Dan Willemsen1e704462016-08-21 15:17:17 -0700265 runSoong(ctx, config)
Jingwen Cheneb76c432021-01-28 08:22:12 -0500266
Chris Parsonsec1a3dc2021-04-20 15:32:07 -0400267 if config.bazelBuildMode() == generateBuildFiles {
268 // Return early, if we're using Soong as solely the generator of BUILD files.
Jingwen Cheneb76c432021-01-28 08:22:12 -0500269 return
270 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700271 }
272
Anton Hansson5a7861a2021-06-04 10:09:01 +0100273 if what&RunKati != 0 {
Dan Willemsen29971232018-09-26 14:58:30 -0700274 genKatiSuffix(ctx, config)
275 runKatiCleanSpec(ctx, config)
276 runKatiBuild(ctx, config)
Dan Willemsenfb1271a2018-09-26 15:00:42 -0700277 runKatiPackage(ctx, config)
Dan Willemsene0879fc2017-08-04 15:06:27 -0700278
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000279 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
Dan Willemsene0879fc2017-08-04 15:06:27 -0700280 } else {
281 // Load last Kati Suffix if it exists
282 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
283 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
284 config.SetKatiSuffix(string(katiSuffix))
285 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700286 }
287
Colin Cross37193492017-11-16 17:55:00 -0800288 // Write combined ninja file
289 createCombinedBuildNinjaFile(ctx, config)
290
Colin Cross8ba7d472020-06-25 11:27:52 -0700291 distGzipFile(ctx, config, config.CombinedNinjaFile())
292
Colin Cross37193492017-11-16 17:55:00 -0800293 if what&RunBuildTests != 0 {
294 testForDanglingRules(ctx, config)
295 }
296
Anton Hansson5a7861a2021-06-04 10:09:01 +0100297 if what&RunNinja != 0 {
298 if what&RunKati != 0 {
Dan Willemsene0879fc2017-08-04 15:06:27 -0700299 installCleanIfNecessary(ctx, config)
300 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700301
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100302 runNinjaForBuild(ctx, config)
Dan Willemsen1e704462016-08-21 15:17:17 -0700303 }
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000304
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000305 // Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
Anton Hansson5a7861a2021-06-04 10:09:01 +0100306 if what&RunBazel != 0 {
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000307 runBazel(ctx, config)
308 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700309}
Colin Cross8ba7d472020-06-25 11:27:52 -0700310
311// distGzipFile writes a compressed copy of src to the distDir if dist is enabled. Failures
312// are printed but non-fatal.
313func distGzipFile(ctx Context, config Config, src string, subDirs ...string) {
314 if !config.Dist() {
315 return
316 }
317
318 subDir := filepath.Join(subDirs...)
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000319 destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
Colin Cross8ba7d472020-06-25 11:27:52 -0700320
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000321 if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
Colin Cross8ba7d472020-06-25 11:27:52 -0700322 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
Colin Cross8ba7d472020-06-25 11:27:52 -0700323 }
324
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000325 if err := gzipFileToDir(src, destDir); err != nil {
Colin Cross8ba7d472020-06-25 11:27:52 -0700326 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
327 }
328}
329
330// distFile writes a copy of src to the distDir if dist is enabled. Failures are printed but
331// non-fatal.
332func distFile(ctx Context, config Config, src string, subDirs ...string) {
333 if !config.Dist() {
334 return
335 }
336
337 subDir := filepath.Join(subDirs...)
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000338 destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
Colin Cross8ba7d472020-06-25 11:27:52 -0700339
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000340 if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
Colin Cross8ba7d472020-06-25 11:27:52 -0700341 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
Colin Cross8ba7d472020-06-25 11:27:52 -0700342 }
343
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000344 if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil {
Colin Cross8ba7d472020-06-25 11:27:52 -0700345 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
346 }
347}