blob: 215a6c8cea1bb927707a4a292d01a69a4cc3fa47 [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
Yoshisato Yanagisawa2cb0e5d2019-01-10 10:14:16 +0900221 if config.StartGoma() {
222 // Ensure start Goma compiler_proxy
223 startGoma(ctx, config)
224 }
225
Ramy Medhatbbf25672019-07-17 12:30:04 +0000226 if config.StartRBE() {
227 // Ensure RBE proxy is started
228 startRBE(ctx, config)
229 }
230
Dan Willemsen1e704462016-08-21 15:17:17 -0700231 if what&BuildProductConfig != 0 {
232 // Run make for product config
233 runMakeProductConfig(ctx, config)
234 }
235
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000236 // Everything below here depends on product config.
237
Colin Cross806fd942019-05-03 13:35:58 -0700238 if inList("installclean", config.Arguments()) ||
239 inList("install-clean", config.Arguments()) {
Rupert Shuttleworth1f304e62020-11-24 14:13:41 +0000240 installClean(ctx, config)
Dan Willemsenf052f782017-05-18 15:29:04 -0700241 ctx.Println("Deleted images and staging directories.")
242 return
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000243 }
244
245 if inList("dataclean", config.Arguments()) ||
Colin Cross806fd942019-05-03 13:35:58 -0700246 inList("data-clean", config.Arguments()) {
Rupert Shuttleworth1f304e62020-11-24 14:13:41 +0000247 dataClean(ctx, config)
Dan Willemsenf052f782017-05-18 15:29:04 -0700248 ctx.Println("Deleted data files.")
249 return
250 }
251
Dan Willemsen1e704462016-08-21 15:17:17 -0700252 if what&BuildSoong != 0 {
253 // Run Soong
Dan Willemsen1e704462016-08-21 15:17:17 -0700254 runSoong(ctx, config)
Jingwen Cheneb76c432021-01-28 08:22:12 -0500255
256 if config.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") {
257 // Return early, if we're using Soong as the bp2build converter.
258 return
259 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700260 }
261
Dan Willemsen1e704462016-08-21 15:17:17 -0700262 if what&BuildKati != 0 {
263 // Run ckati
Dan Willemsen29971232018-09-26 14:58:30 -0700264 genKatiSuffix(ctx, config)
265 runKatiCleanSpec(ctx, config)
266 runKatiBuild(ctx, config)
Dan Willemsenfb1271a2018-09-26 15:00:42 -0700267 runKatiPackage(ctx, config)
Dan Willemsene0879fc2017-08-04 15:06:27 -0700268
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000269 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw
Dan Willemsene0879fc2017-08-04 15:06:27 -0700270 } else {
271 // Load last Kati Suffix if it exists
272 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
273 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
274 config.SetKatiSuffix(string(katiSuffix))
275 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700276 }
277
Colin Cross37193492017-11-16 17:55:00 -0800278 // Write combined ninja file
279 createCombinedBuildNinjaFile(ctx, config)
280
Colin Cross8ba7d472020-06-25 11:27:52 -0700281 distGzipFile(ctx, config, config.CombinedNinjaFile())
282
Colin Cross37193492017-11-16 17:55:00 -0800283 if what&RunBuildTests != 0 {
284 testForDanglingRules(ctx, config)
285 }
286
Dan Willemsen1e704462016-08-21 15:17:17 -0700287 if what&BuildNinja != 0 {
Anton Hansson5e5c48b2020-11-27 12:35:20 +0000288 if what&BuildKati != 0 {
Dan Willemsene0879fc2017-08-04 15:06:27 -0700289 installCleanIfNecessary(ctx, config)
290 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700291
Dan Willemsen1e704462016-08-21 15:17:17 -0700292 // Run ninja
293 runNinja(ctx, config)
294 }
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000295
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000296 // Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last.
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000297 if what&BuildBazel != 0 {
298 runBazel(ctx, config)
299 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700300}
Colin Cross8ba7d472020-06-25 11:27:52 -0700301
302// distGzipFile writes a compressed copy of src to the distDir if dist is enabled. Failures
303// are printed but non-fatal.
304func distGzipFile(ctx Context, config Config, src string, subDirs ...string) {
305 if !config.Dist() {
306 return
307 }
308
309 subDir := filepath.Join(subDirs...)
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000310 destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
Colin Cross8ba7d472020-06-25 11:27:52 -0700311
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000312 if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
Colin Cross8ba7d472020-06-25 11:27:52 -0700313 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
Colin Cross8ba7d472020-06-25 11:27:52 -0700314 }
315
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000316 if err := gzipFileToDir(src, destDir); err != nil {
Colin Cross8ba7d472020-06-25 11:27:52 -0700317 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
318 }
319}
320
321// distFile writes a copy of src to the distDir if dist is enabled. Failures are printed but
322// non-fatal.
323func distFile(ctx Context, config Config, src string, subDirs ...string) {
324 if !config.Dist() {
325 return
326 }
327
328 subDir := filepath.Join(subDirs...)
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000329 destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir)
Colin Cross8ba7d472020-06-25 11:27:52 -0700330
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000331 if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx
Colin Cross8ba7d472020-06-25 11:27:52 -0700332 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
Colin Cross8ba7d472020-06-25 11:27:52 -0700333 }
334
Rupert Shuttlewortheeb5caa2020-11-25 07:13:54 +0000335 if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil {
Colin Cross8ba7d472020-06-25 11:27:52 -0700336 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
337 }
338}