blob: 3692f4fb45bad22f6da92fcacaa70895e2f075f5 [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 (
90 BuildNone = iota
91 BuildProductConfig = 1 << iota
92 BuildSoong = 1 << iota
93 BuildKati = 1 << iota
94 BuildNinja = 1 << iota
Rupert Shuttleworth680387b2020-10-25 12:31:27 +000095 BuildBazel = 1 << iota
Colin Cross37193492017-11-16 17:55:00 -080096 RunBuildTests = 1 << iota
Dan Willemsen1e704462016-08-21 15:17:17 -070097 BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
Rupert Shuttleworth680387b2020-10-25 12:31:27 +000098 BuildAllWithBazel = BuildProductConfig | BuildSoong | BuildKati | BuildBazel
Dan Willemsen1e704462016-08-21 15:17:17 -070099)
100
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000101// 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 +0100102func checkProblematicFiles(ctx Context) {
103 files := []string{"Android.mk", "CleanSpec.mk"}
104 for _, file := range files {
105 if _, err := os.Stat(file); !os.IsNotExist(err) {
106 absolute := absPath(ctx, file)
107 ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file)
108 ctx.Fatalf(" rm %s\n", absolute)
109 }
110 }
111}
112
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000113// checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
Dan Willemsendb8457c2017-05-12 16:38:17 -0700114func checkCaseSensitivity(ctx Context, config Config) {
115 outDir := config.OutDir()
116 lowerCase := filepath.Join(outDir, "casecheck.txt")
117 upperCase := filepath.Join(outDir, "CaseCheck.txt")
118 lowerData := "a"
119 upperData := "B"
120
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000121 if err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0666); err != nil { // a+rw
Dan Willemsendb8457c2017-05-12 16:38:17 -0700122 ctx.Fatalln("Failed to check case sensitivity:", err)
123 }
124
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000125 if err := ioutil.WriteFile(upperCase, []byte(upperData), 0666); err != nil { // a+rw
Dan Willemsendb8457c2017-05-12 16:38:17 -0700126 ctx.Fatalln("Failed to check case sensitivity:", err)
127 }
128
129 res, err := ioutil.ReadFile(lowerCase)
130 if err != nil {
131 ctx.Fatalln("Failed to check case sensitivity:", err)
132 }
133
134 if string(res) != lowerData {
135 ctx.Println("************************************************************")
136 ctx.Println("You are building on a case-insensitive filesystem.")
137 ctx.Println("Please move your source tree to a case-sensitive filesystem.")
138 ctx.Println("************************************************************")
139 ctx.Fatalln("Case-insensitive filesystems not supported")
140 }
141}
142
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000143// help prints a help/usage message, via the build/make/help.sh script.
144func help(ctx Context, config Config) {
Jeff Gastondf4a0812017-05-30 20:11:20 -0700145 cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
Dan Willemsenb2e6c2e2017-07-13 17:24:44 -0700146 cmd.Sandbox = dumpvarsSandbox
Dan Willemsenb82471a2018-05-17 16:37:09 -0700147 cmd.RunAndPrintOrFatal()
Dan Willemsen02781d52017-05-12 19:28:13 -0700148}
149
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000150// checkRAM warns if there probably isn't enough RAM to complete a build.
151func checkRAM(ctx Context, config Config) {
Dan Willemsen570a2922020-05-26 23:02:29 -0700152 if totalRAM := config.TotalRAM(); totalRAM != 0 {
153 ram := float32(totalRAM) / (1024 * 1024 * 1024)
154 ctx.Verbosef("Total RAM: %.3vGB", ram)
155
156 if ram <= 16 {
157 ctx.Println("************************************************************")
158 ctx.Printf("You are building on a machine with %.3vGB of RAM\n", ram)
159 ctx.Println("")
160 ctx.Println("The minimum required amount of free memory is around 16GB,")
161 ctx.Println("and even with that, some configurations may not work.")
162 ctx.Println("")
163 ctx.Println("If you run into segfaults or other errors, try reducing your")
164 ctx.Println("-j value.")
165 ctx.Println("************************************************************")
166 } else if ram <= float32(config.Parallel()) {
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000167 // Want at least 1GB of RAM per job.
Dan Willemsen570a2922020-05-26 23:02:29 -0700168 ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
169 ctx.Println("If you run into segfaults or other errors, try a lower -j value")
170 }
171 }
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000172}
173
174// Build the tree. The 'what' argument can be used to chose which components of
175// the build to run, via checking various bitmasks.
176func Build(ctx Context, config Config, what int) {
177 ctx.Verboseln("Starting build with args:", config.Arguments())
178 ctx.Verboseln("Environment:", config.Environment().Environ())
Dan Willemsen1e704462016-08-21 15:17:17 -0700179
Colin Cross74cda722020-01-16 15:25:50 -0800180 ctx.BeginTrace(metrics.Total, "total")
181 defer ctx.EndTrace()
182
Dan Willemsen1e704462016-08-21 15:17:17 -0700183 if inList("help", config.Arguments()) {
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000184 help(ctx, config)
Dan Willemsen0b73b4b2017-05-12 19:28:13 -0700185 return
Dan Willemsen1e704462016-08-21 15:17:17 -0700186 }
187
Jeff Gaston3615fe82017-05-24 13:14:34 -0700188 // Make sure that no other Soong process is running with the same output directory
189 buildLock := BecomeSingletonOrFail(ctx, config)
190 defer buildLock.Unlock()
191
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000192 if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
193 clean(ctx, config)
194 return
195 }
196
197 // 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 +0100198 checkProblematicFiles(ctx)
199
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000200 checkRAM(ctx, config)
201
Dan Willemsen1e704462016-08-21 15:17:17 -0700202 SetupOutDir(ctx, config)
203
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000204 // checkCaseSensitivity issues a warning if a case-insensitive file system is being used.
Dan Willemsendb8457c2017-05-12 16:38:17 -0700205 checkCaseSensitivity(ctx, config)
206
Jeff Gastonefc1b412017-03-29 17:29:06 -0700207 ensureEmptyDirectoriesExist(ctx, config.TempDir())
208
Dan Willemsen18490112018-05-25 16:30:04 -0700209 SetupPath(ctx, config)
210
Anton Hansson5e5c48b2020-11-27 12:35:20 +0000211 if config.SkipConfig() {
212 ctx.Verboseln("Skipping Config as requested")
213 what = what &^ BuildProductConfig
214 }
215
216 if config.SkipKati() {
217 ctx.Verboseln("Skipping Kati as requested")
218 what = what &^ BuildKati
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000219 }
220
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100221 if config.SkipNinja() {
222 ctx.Verboseln("Skipping Ninja as requested")
223 what = what &^ BuildNinja
224 }
225
Yoshisato Yanagisawa2cb0e5d2019-01-10 10:14:16 +0900226 if config.StartGoma() {
227 // Ensure start Goma compiler_proxy
228 startGoma(ctx, config)
229 }
230
Ramy Medhatbbf25672019-07-17 12:30:04 +0000231 if config.StartRBE() {
232 // Ensure RBE proxy is started
233 startRBE(ctx, config)
234 }
235
Dan Willemsen1e704462016-08-21 15:17:17 -0700236 if what&BuildProductConfig != 0 {
237 // Run make for product config
238 runMakeProductConfig(ctx, config)
239 }
240
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000241 // Everything below here depends on product config.
242
Colin Cross806fd942019-05-03 13:35:58 -0700243 if inList("installclean", config.Arguments()) ||
244 inList("install-clean", config.Arguments()) {
Rupert Shuttleworth1f304e62020-11-24 14:13:41 +0000245 installClean(ctx, config)
Dan Willemsenf052f782017-05-18 15:29:04 -0700246 ctx.Println("Deleted images and staging directories.")
247 return
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000248 }
249
250 if inList("dataclean", config.Arguments()) ||
Colin Cross806fd942019-05-03 13:35:58 -0700251 inList("data-clean", config.Arguments()) {
Rupert Shuttleworth1f304e62020-11-24 14:13:41 +0000252 dataClean(ctx, config)
Dan Willemsenf052f782017-05-18 15:29:04 -0700253 ctx.Println("Deleted data files.")
254 return
255 }
256
Dan Willemsen1e704462016-08-21 15:17:17 -0700257 if what&BuildSoong != 0 {
258 // Run Soong
Dan Willemsen1e704462016-08-21 15:17:17 -0700259 runSoong(ctx, config)
Jingwen Cheneb76c432021-01-28 08:22:12 -0500260
261 if config.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") {
262 // Return early, if we're using Soong as the bp2build converter.
263 return
264 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700265 }
266
Dan Willemsen1e704462016-08-21 15:17:17 -0700267 if what&BuildKati != 0 {
268 // Run ckati
Dan Willemsen29971232018-09-26 14:58:30 -0700269 genKatiSuffix(ctx, config)
270 runKatiCleanSpec(ctx, config)
271 runKatiBuild(ctx, config)
Dan Willemsenfb1271a2018-09-26 15:00:42 -0700272 runKatiPackage(ctx, config)
Dan Willemsene0879fc2017-08-04 15:06:27 -0700273
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000274 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
Dan Willemsene0879fc2017-08-04 15:06:27 -0700275 } else {
276 // Load last Kati Suffix if it exists
277 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
278 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
279 config.SetKatiSuffix(string(katiSuffix))
280 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700281 }
282
Colin Cross37193492017-11-16 17:55:00 -0800283 // Write combined ninja file
284 createCombinedBuildNinjaFile(ctx, config)
285
Colin Cross8ba7d472020-06-25 11:27:52 -0700286 distGzipFile(ctx, config, config.CombinedNinjaFile())
287
Colin Cross37193492017-11-16 17:55:00 -0800288 if what&RunBuildTests != 0 {
289 testForDanglingRules(ctx, config)
290 }
291
Dan Willemsen1e704462016-08-21 15:17:17 -0700292 if what&BuildNinja != 0 {
Anton Hansson5e5c48b2020-11-27 12:35:20 +0000293 if what&BuildKati != 0 {
Dan Willemsene0879fc2017-08-04 15:06:27 -0700294 installCleanIfNecessary(ctx, config)
295 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700296
Dan Willemsen1e704462016-08-21 15:17:17 -0700297 // Run ninja
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100298 runNinjaForBuild(ctx, config)
Dan Willemsen1e704462016-08-21 15:17:17 -0700299 }
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000300
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000301 // Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000302 if what&BuildBazel != 0 {
303 runBazel(ctx, config)
304 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700305}
Colin Cross8ba7d472020-06-25 11:27:52 -0700306
307// distGzipFile writes a compressed copy of src to the distDir if dist is enabled. Failures
308// are printed but non-fatal.
309func distGzipFile(ctx Context, config Config, src string, subDirs ...string) {
310 if !config.Dist() {
311 return
312 }
313
314 subDir := filepath.Join(subDirs...)
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000315 destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
Colin Cross8ba7d472020-06-25 11:27:52 -0700316
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000317 if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
Colin Cross8ba7d472020-06-25 11:27:52 -0700318 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
Colin Cross8ba7d472020-06-25 11:27:52 -0700319 }
320
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000321 if err := gzipFileToDir(src, destDir); err != nil {
Colin Cross8ba7d472020-06-25 11:27:52 -0700322 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
323 }
324}
325
326// distFile writes a copy of src to the distDir if dist is enabled. Failures are printed but
327// non-fatal.
328func distFile(ctx Context, config Config, src string, subDirs ...string) {
329 if !config.Dist() {
330 return
331 }
332
333 subDir := filepath.Join(subDirs...)
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000334 destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
Colin Cross8ba7d472020-06-25 11:27:52 -0700335
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000336 if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
Colin Cross8ba7d472020-06-25 11:27:52 -0700337 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
Colin Cross8ba7d472020-06-25 11:27:52 -0700338 }
339
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000340 if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil {
Colin Cross8ba7d472020-06-25 11:27:52 -0700341 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
342 }
343}