blob: 1cf2023533f92a335fec0b3a8ce0bf1d1a852b5b [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
26// Ensures the out directory exists, and has the proper files to prevent kati
27// from recursing into it.
28func 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() {
32 ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.in_make"))
33 }
Dan Willemsen1e704462016-08-21 15:17:17 -070034 // The ninja_build file is used by our buildbots to understand that the output
35 // can be parsed as ninja output.
36 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build"))
Jeff Gastonb64fc1c2017-08-04 12:30:12 -070037 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir"))
Colin Cross28f527c2019-11-26 16:19:04 -080038
39 if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok {
40 err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0777)
41 if err != nil {
42 ctx.Fatalln("Failed to write BUILD_DATETIME to file:", err)
43 }
44 } else {
45 ctx.Fatalln("Missing BUILD_DATETIME_FILE")
46 }
Dan Willemsen1e704462016-08-21 15:17:17 -070047}
48
49var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(`
50builddir = {{.OutDir}}
Colin Cross8b8bec32019-11-15 13:18:43 -080051{{if .UseRemoteBuild }}pool local_pool
Dan Willemsen29971232018-09-26 14:58:30 -070052 depth = {{.Parallel}}
Colin Cross8b8bec32019-11-15 13:18:43 -080053{{end -}}
54pool highmem_pool
55 depth = {{.HighmemParallel}}
Dan Willemsenfb1271a2018-09-26 15:00:42 -070056{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}}
57subninja {{.KatiPackageNinjaFile}}
Dan Willemsene0879fc2017-08-04 15:06:27 -070058{{end -}}
Dan Willemsenfb1271a2018-09-26 15:00:42 -070059subninja {{.SoongNinjaFile}}
Dan Willemsen1e704462016-08-21 15:17:17 -070060`))
61
62func createCombinedBuildNinjaFile(ctx Context, config Config) {
Dan Willemsene0879fc2017-08-04 15:06:27 -070063 // If we're in SkipMake mode, skip creating this file if it already exists
64 if config.SkipMake() {
65 if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) {
66 return
67 }
68 }
69
Dan Willemsen1e704462016-08-21 15:17:17 -070070 file, err := os.Create(config.CombinedNinjaFile())
71 if err != nil {
72 ctx.Fatalln("Failed to create combined ninja file:", err)
73 }
74 defer file.Close()
75
76 if err := combinedBuildNinjaTemplate.Execute(file, config); err != nil {
77 ctx.Fatalln("Failed to write combined ninja file:", err)
78 }
79}
80
81const (
82 BuildNone = iota
83 BuildProductConfig = 1 << iota
84 BuildSoong = 1 << iota
85 BuildKati = 1 << iota
86 BuildNinja = 1 << iota
Rupert Shuttleworth680387b2020-10-25 12:31:27 +000087 BuildBazel = 1 << iota
Colin Cross37193492017-11-16 17:55:00 -080088 RunBuildTests = 1 << iota
Dan Willemsen1e704462016-08-21 15:17:17 -070089 BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
Rupert Shuttleworth680387b2020-10-25 12:31:27 +000090 BuildAllWithBazel = BuildProductConfig | BuildSoong | BuildKati | BuildBazel
Dan Willemsen1e704462016-08-21 15:17:17 -070091)
92
Anton Hanssonecf0f102018-09-19 22:14:17 +010093func checkProblematicFiles(ctx Context) {
94 files := []string{"Android.mk", "CleanSpec.mk"}
95 for _, file := range files {
96 if _, err := os.Stat(file); !os.IsNotExist(err) {
97 absolute := absPath(ctx, file)
98 ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file)
99 ctx.Fatalf(" rm %s\n", absolute)
100 }
101 }
102}
103
Dan Willemsendb8457c2017-05-12 16:38:17 -0700104func checkCaseSensitivity(ctx Context, config Config) {
105 outDir := config.OutDir()
106 lowerCase := filepath.Join(outDir, "casecheck.txt")
107 upperCase := filepath.Join(outDir, "CaseCheck.txt")
108 lowerData := "a"
109 upperData := "B"
110
111 err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777)
112 if err != nil {
113 ctx.Fatalln("Failed to check case sensitivity:", err)
114 }
115
116 err = ioutil.WriteFile(upperCase, []byte(upperData), 0777)
117 if err != nil {
118 ctx.Fatalln("Failed to check case sensitivity:", err)
119 }
120
121 res, err := ioutil.ReadFile(lowerCase)
122 if err != nil {
123 ctx.Fatalln("Failed to check case sensitivity:", err)
124 }
125
126 if string(res) != lowerData {
127 ctx.Println("************************************************************")
128 ctx.Println("You are building on a case-insensitive filesystem.")
129 ctx.Println("Please move your source tree to a case-sensitive filesystem.")
130 ctx.Println("************************************************************")
131 ctx.Fatalln("Case-insensitive filesystems not supported")
132 }
133}
134
Dan Willemsenf052f782017-05-18 15:29:04 -0700135func help(ctx Context, config Config, what int) {
Jeff Gastondf4a0812017-05-30 20:11:20 -0700136 cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
Dan Willemsenb2e6c2e2017-07-13 17:24:44 -0700137 cmd.Sandbox = dumpvarsSandbox
Dan Willemsenb82471a2018-05-17 16:37:09 -0700138 cmd.RunAndPrintOrFatal()
Dan Willemsen02781d52017-05-12 19:28:13 -0700139}
140
Dan Willemsen1e704462016-08-21 15:17:17 -0700141// Build the tree. The 'what' argument can be used to chose which components of
142// the build to run.
143func Build(ctx Context, config Config, what int) {
144 ctx.Verboseln("Starting build with args:", config.Arguments())
145 ctx.Verboseln("Environment:", config.Environment().Environ())
Dan Willemsen570a2922020-05-26 23:02:29 -0700146
147 if totalRAM := config.TotalRAM(); totalRAM != 0 {
148 ram := float32(totalRAM) / (1024 * 1024 * 1024)
149 ctx.Verbosef("Total RAM: %.3vGB", ram)
150
151 if ram <= 16 {
152 ctx.Println("************************************************************")
153 ctx.Printf("You are building on a machine with %.3vGB of RAM\n", ram)
154 ctx.Println("")
155 ctx.Println("The minimum required amount of free memory is around 16GB,")
156 ctx.Println("and even with that, some configurations may not work.")
157 ctx.Println("")
158 ctx.Println("If you run into segfaults or other errors, try reducing your")
159 ctx.Println("-j value.")
160 ctx.Println("************************************************************")
161 } else if ram <= float32(config.Parallel()) {
162 ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
163 ctx.Println("If you run into segfaults or other errors, try a lower -j value")
164 }
165 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700166
Colin Cross74cda722020-01-16 15:25:50 -0800167 ctx.BeginTrace(metrics.Total, "total")
168 defer ctx.EndTrace()
169
Dan Willemsene0879fc2017-08-04 15:06:27 -0700170 if config.SkipMake() {
171 ctx.Verboseln("Skipping Make/Kati as requested")
172 what = what & (BuildSoong | BuildNinja)
173 }
174
Dan Willemsen1e704462016-08-21 15:17:17 -0700175 if inList("help", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700176 help(ctx, config, what)
Dan Willemsen1e704462016-08-21 15:17:17 -0700177 return
Dan Willemsen0b73b4b2017-05-12 19:28:13 -0700178 } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700179 clean(ctx, config, what)
Dan Willemsen0b73b4b2017-05-12 19:28:13 -0700180 return
Dan Willemsen1e704462016-08-21 15:17:17 -0700181 }
182
Jeff Gaston3615fe82017-05-24 13:14:34 -0700183 // Make sure that no other Soong process is running with the same output directory
184 buildLock := BecomeSingletonOrFail(ctx, config)
185 defer buildLock.Unlock()
186
Anton Hanssonecf0f102018-09-19 22:14:17 +0100187 checkProblematicFiles(ctx)
188
Dan Willemsen1e704462016-08-21 15:17:17 -0700189 SetupOutDir(ctx, config)
190
Dan Willemsendb8457c2017-05-12 16:38:17 -0700191 checkCaseSensitivity(ctx, config)
192
Jeff Gastonefc1b412017-03-29 17:29:06 -0700193 ensureEmptyDirectoriesExist(ctx, config.TempDir())
194
Dan Willemsen18490112018-05-25 16:30:04 -0700195 SetupPath(ctx, config)
196
Yoshisato Yanagisawa2cb0e5d2019-01-10 10:14:16 +0900197 if config.StartGoma() {
198 // Ensure start Goma compiler_proxy
199 startGoma(ctx, config)
200 }
201
Ramy Medhatbbf25672019-07-17 12:30:04 +0000202 if config.StartRBE() {
203 // Ensure RBE proxy is started
204 startRBE(ctx, config)
205 }
206
Dan Willemsen1e704462016-08-21 15:17:17 -0700207 if what&BuildProductConfig != 0 {
208 // Run make for product config
209 runMakeProductConfig(ctx, config)
210 }
211
Colin Cross806fd942019-05-03 13:35:58 -0700212 if inList("installclean", config.Arguments()) ||
213 inList("install-clean", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700214 installClean(ctx, config, what)
215 ctx.Println("Deleted images and staging directories.")
216 return
Colin Cross806fd942019-05-03 13:35:58 -0700217 } else if inList("dataclean", config.Arguments()) ||
218 inList("data-clean", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700219 dataClean(ctx, config, what)
220 ctx.Println("Deleted data files.")
221 return
222 }
223
Dan Willemsen1e704462016-08-21 15:17:17 -0700224 if what&BuildSoong != 0 {
225 // Run Soong
Dan Willemsen1e704462016-08-21 15:17:17 -0700226 runSoong(ctx, config)
227 }
228
Dan Willemsen1e704462016-08-21 15:17:17 -0700229 if what&BuildKati != 0 {
230 // Run ckati
Dan Willemsen29971232018-09-26 14:58:30 -0700231 genKatiSuffix(ctx, config)
232 runKatiCleanSpec(ctx, config)
233 runKatiBuild(ctx, config)
Dan Willemsenfb1271a2018-09-26 15:00:42 -0700234 runKatiPackage(ctx, config)
Dan Willemsene0879fc2017-08-04 15:06:27 -0700235
236 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
237 } else {
238 // Load last Kati Suffix if it exists
239 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
240 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
241 config.SetKatiSuffix(string(katiSuffix))
242 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700243 }
244
Colin Cross37193492017-11-16 17:55:00 -0800245 // Write combined ninja file
246 createCombinedBuildNinjaFile(ctx, config)
247
Colin Cross8ba7d472020-06-25 11:27:52 -0700248 distGzipFile(ctx, config, config.CombinedNinjaFile())
249
Colin Cross37193492017-11-16 17:55:00 -0800250 if what&RunBuildTests != 0 {
251 testForDanglingRules(ctx, config)
252 }
253
Dan Willemsen1e704462016-08-21 15:17:17 -0700254 if what&BuildNinja != 0 {
Dan Willemsene0879fc2017-08-04 15:06:27 -0700255 if !config.SkipMake() {
256 installCleanIfNecessary(ctx, config)
257 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700258
Dan Willemsen1e704462016-08-21 15:17:17 -0700259 // Run ninja
260 runNinja(ctx, config)
261 }
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000262
263 if what&BuildBazel != 0 {
264 runBazel(ctx, config)
265 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700266}
Colin Cross8ba7d472020-06-25 11:27:52 -0700267
268// distGzipFile writes a compressed copy of src to the distDir if dist is enabled. Failures
269// are printed but non-fatal.
270func distGzipFile(ctx Context, config Config, src string, subDirs ...string) {
271 if !config.Dist() {
272 return
273 }
274
275 subDir := filepath.Join(subDirs...)
276 destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
277
278 err := os.MkdirAll(destDir, 0777)
279 if err != nil {
280 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
281
282 }
283
284 err = gzipFileToDir(src, destDir)
285 if err != nil {
286 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
287 }
288}
289
290// distFile writes a copy of src to the distDir if dist is enabled. Failures are printed but
291// non-fatal.
292func distFile(ctx Context, config Config, src string, subDirs ...string) {
293 if !config.Dist() {
294 return
295 }
296
297 subDir := filepath.Join(subDirs...)
298 destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
299
300 err := os.MkdirAll(destDir, 0777)
301 if err != nil {
302 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
303
304 }
305
306 _, err = copyFile(src, filepath.Join(destDir, filepath.Base(src)))
307 if err != nil {
308 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
309 }
310}