blob: e9196a9f6fd009adddf0dae4fe74383aed8d6aa4 [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"))
Dan Willemsene0879fc2017-08-04 15:06:27 -070031 if !config.SkipMake() {
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 {
47 err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0777)
48 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) {
Dan Willemsene0879fc2017-08-04 15:06:27 -070070 // If we're in SkipMake mode, skip creating this file if it already exists
71 if config.SkipMake() {
72 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
88const (
89 BuildNone = iota
90 BuildProductConfig = 1 << iota
91 BuildSoong = 1 << iota
92 BuildKati = 1 << iota
93 BuildNinja = 1 << iota
Rupert Shuttleworth680387b2020-10-25 12:31:27 +000094 BuildBazel = 1 << iota
Colin Cross37193492017-11-16 17:55:00 -080095 RunBuildTests = 1 << iota
Dan Willemsen1e704462016-08-21 15:17:17 -070096 BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
Rupert Shuttleworth680387b2020-10-25 12:31:27 +000097 BuildAllWithBazel = BuildProductConfig | BuildSoong | BuildKati | BuildBazel
Dan Willemsen1e704462016-08-21 15:17:17 -070098)
99
Anton Hanssonecf0f102018-09-19 22:14:17 +0100100func checkProblematicFiles(ctx Context) {
101 files := []string{"Android.mk", "CleanSpec.mk"}
102 for _, file := range files {
103 if _, err := os.Stat(file); !os.IsNotExist(err) {
104 absolute := absPath(ctx, file)
105 ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file)
106 ctx.Fatalf(" rm %s\n", absolute)
107 }
108 }
109}
110
Dan Willemsendb8457c2017-05-12 16:38:17 -0700111func checkCaseSensitivity(ctx Context, config Config) {
112 outDir := config.OutDir()
113 lowerCase := filepath.Join(outDir, "casecheck.txt")
114 upperCase := filepath.Join(outDir, "CaseCheck.txt")
115 lowerData := "a"
116 upperData := "B"
117
118 err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777)
119 if err != nil {
120 ctx.Fatalln("Failed to check case sensitivity:", err)
121 }
122
123 err = ioutil.WriteFile(upperCase, []byte(upperData), 0777)
124 if err != nil {
125 ctx.Fatalln("Failed to check case sensitivity:", err)
126 }
127
128 res, err := ioutil.ReadFile(lowerCase)
129 if err != nil {
130 ctx.Fatalln("Failed to check case sensitivity:", err)
131 }
132
133 if string(res) != lowerData {
134 ctx.Println("************************************************************")
135 ctx.Println("You are building on a case-insensitive filesystem.")
136 ctx.Println("Please move your source tree to a case-sensitive filesystem.")
137 ctx.Println("************************************************************")
138 ctx.Fatalln("Case-insensitive filesystems not supported")
139 }
140}
141
Dan Willemsenf052f782017-05-18 15:29:04 -0700142func help(ctx Context, config Config, what int) {
Jeff Gastondf4a0812017-05-30 20:11:20 -0700143 cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
Dan Willemsenb2e6c2e2017-07-13 17:24:44 -0700144 cmd.Sandbox = dumpvarsSandbox
Dan Willemsenb82471a2018-05-17 16:37:09 -0700145 cmd.RunAndPrintOrFatal()
Dan Willemsen02781d52017-05-12 19:28:13 -0700146}
147
Dan Willemsen1e704462016-08-21 15:17:17 -0700148// Build the tree. The 'what' argument can be used to chose which components of
149// the build to run.
150func Build(ctx Context, config Config, what int) {
151 ctx.Verboseln("Starting build with args:", config.Arguments())
152 ctx.Verboseln("Environment:", config.Environment().Environ())
Dan Willemsen570a2922020-05-26 23:02:29 -0700153
154 if totalRAM := config.TotalRAM(); totalRAM != 0 {
155 ram := float32(totalRAM) / (1024 * 1024 * 1024)
156 ctx.Verbosef("Total RAM: %.3vGB", ram)
157
158 if ram <= 16 {
159 ctx.Println("************************************************************")
160 ctx.Printf("You are building on a machine with %.3vGB of RAM\n", ram)
161 ctx.Println("")
162 ctx.Println("The minimum required amount of free memory is around 16GB,")
163 ctx.Println("and even with that, some configurations may not work.")
164 ctx.Println("")
165 ctx.Println("If you run into segfaults or other errors, try reducing your")
166 ctx.Println("-j value.")
167 ctx.Println("************************************************************")
168 } else if ram <= float32(config.Parallel()) {
169 ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
170 ctx.Println("If you run into segfaults or other errors, try a lower -j value")
171 }
172 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700173
Colin Cross74cda722020-01-16 15:25:50 -0800174 ctx.BeginTrace(metrics.Total, "total")
175 defer ctx.EndTrace()
176
Dan Willemsene0879fc2017-08-04 15:06:27 -0700177 if config.SkipMake() {
178 ctx.Verboseln("Skipping Make/Kati as requested")
179 what = what & (BuildSoong | BuildNinja)
180 }
181
Dan Willemsen1e704462016-08-21 15:17:17 -0700182 if inList("help", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700183 help(ctx, config, what)
Dan Willemsen1e704462016-08-21 15:17:17 -0700184 return
Dan Willemsen0b73b4b2017-05-12 19:28:13 -0700185 } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700186 clean(ctx, config, what)
Dan Willemsen0b73b4b2017-05-12 19:28:13 -0700187 return
Dan Willemsen1e704462016-08-21 15:17:17 -0700188 }
189
Jeff Gaston3615fe82017-05-24 13:14:34 -0700190 // Make sure that no other Soong process is running with the same output directory
191 buildLock := BecomeSingletonOrFail(ctx, config)
192 defer buildLock.Unlock()
193
Anton Hanssonecf0f102018-09-19 22:14:17 +0100194 checkProblematicFiles(ctx)
195
Dan Willemsen1e704462016-08-21 15:17:17 -0700196 SetupOutDir(ctx, config)
197
Dan Willemsendb8457c2017-05-12 16:38:17 -0700198 checkCaseSensitivity(ctx, config)
199
Jeff Gastonefc1b412017-03-29 17:29:06 -0700200 ensureEmptyDirectoriesExist(ctx, config.TempDir())
201
Dan Willemsen18490112018-05-25 16:30:04 -0700202 SetupPath(ctx, config)
203
Yoshisato Yanagisawa2cb0e5d2019-01-10 10:14:16 +0900204 if config.StartGoma() {
205 // Ensure start Goma compiler_proxy
206 startGoma(ctx, config)
207 }
208
Ramy Medhatbbf25672019-07-17 12:30:04 +0000209 if config.StartRBE() {
210 // Ensure RBE proxy is started
211 startRBE(ctx, config)
212 }
213
Dan Willemsen1e704462016-08-21 15:17:17 -0700214 if what&BuildProductConfig != 0 {
215 // Run make for product config
216 runMakeProductConfig(ctx, config)
217 }
218
Colin Cross806fd942019-05-03 13:35:58 -0700219 if inList("installclean", config.Arguments()) ||
220 inList("install-clean", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700221 installClean(ctx, config, what)
222 ctx.Println("Deleted images and staging directories.")
223 return
Colin Cross806fd942019-05-03 13:35:58 -0700224 } else if inList("dataclean", config.Arguments()) ||
225 inList("data-clean", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700226 dataClean(ctx, config, what)
227 ctx.Println("Deleted data files.")
228 return
229 }
230
Dan Willemsen1e704462016-08-21 15:17:17 -0700231 if what&BuildSoong != 0 {
232 // Run Soong
Dan Willemsen1e704462016-08-21 15:17:17 -0700233 runSoong(ctx, config)
234 }
235
Dan Willemsen1e704462016-08-21 15:17:17 -0700236 if what&BuildKati != 0 {
237 // Run ckati
Dan Willemsen29971232018-09-26 14:58:30 -0700238 genKatiSuffix(ctx, config)
239 runKatiCleanSpec(ctx, config)
240 runKatiBuild(ctx, config)
Dan Willemsenfb1271a2018-09-26 15:00:42 -0700241 runKatiPackage(ctx, config)
Dan Willemsene0879fc2017-08-04 15:06:27 -0700242
243 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
244 } else {
245 // Load last Kati Suffix if it exists
246 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
247 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
248 config.SetKatiSuffix(string(katiSuffix))
249 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700250 }
251
Colin Cross37193492017-11-16 17:55:00 -0800252 // Write combined ninja file
253 createCombinedBuildNinjaFile(ctx, config)
254
Colin Cross8ba7d472020-06-25 11:27:52 -0700255 distGzipFile(ctx, config, config.CombinedNinjaFile())
256
Colin Cross37193492017-11-16 17:55:00 -0800257 if what&RunBuildTests != 0 {
258 testForDanglingRules(ctx, config)
259 }
260
Dan Willemsen1e704462016-08-21 15:17:17 -0700261 if what&BuildNinja != 0 {
Dan Willemsene0879fc2017-08-04 15:06:27 -0700262 if !config.SkipMake() {
263 installCleanIfNecessary(ctx, config)
264 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700265
Dan Willemsen1e704462016-08-21 15:17:17 -0700266 // Run ninja
267 runNinja(ctx, config)
268 }
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000269
270 if what&BuildBazel != 0 {
271 runBazel(ctx, config)
272 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700273}
Colin Cross8ba7d472020-06-25 11:27:52 -0700274
275// distGzipFile writes a compressed copy of src to the distDir if dist is enabled. Failures
276// are printed but non-fatal.
277func distGzipFile(ctx Context, config Config, src string, subDirs ...string) {
278 if !config.Dist() {
279 return
280 }
281
282 subDir := filepath.Join(subDirs...)
283 destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
284
285 err := os.MkdirAll(destDir, 0777)
286 if err != nil {
287 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
288
289 }
290
291 err = gzipFileToDir(src, destDir)
292 if err != nil {
293 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
294 }
295}
296
297// distFile writes a copy of src to the distDir if dist is enabled. Failures are printed but
298// non-fatal.
299func distFile(ctx Context, config Config, src string, subDirs ...string) {
300 if !config.Dist() {
301 return
302 }
303
304 subDir := filepath.Join(subDirs...)
305 destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
306
307 err := os.MkdirAll(destDir, 0777)
308 if err != nil {
309 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
310
311 }
312
313 _, err = copyFile(src, filepath.Join(destDir, filepath.Base(src)))
314 if err != nil {
315 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
316 }
317}