blob: 396f54cdaf94b3993069af18cd8d3046ad12f210 [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
Colin Cross37193492017-11-16 17:55:00 -080087 RunBuildTests = 1 << iota
Dan Willemsen1e704462016-08-21 15:17:17 -070088 BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja
89)
90
Anton Hanssonecf0f102018-09-19 22:14:17 +010091func checkProblematicFiles(ctx Context) {
92 files := []string{"Android.mk", "CleanSpec.mk"}
93 for _, file := range files {
94 if _, err := os.Stat(file); !os.IsNotExist(err) {
95 absolute := absPath(ctx, file)
96 ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file)
97 ctx.Fatalf(" rm %s\n", absolute)
98 }
99 }
100}
101
Dan Willemsendb8457c2017-05-12 16:38:17 -0700102func checkCaseSensitivity(ctx Context, config Config) {
103 outDir := config.OutDir()
104 lowerCase := filepath.Join(outDir, "casecheck.txt")
105 upperCase := filepath.Join(outDir, "CaseCheck.txt")
106 lowerData := "a"
107 upperData := "B"
108
109 err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777)
110 if err != nil {
111 ctx.Fatalln("Failed to check case sensitivity:", err)
112 }
113
114 err = ioutil.WriteFile(upperCase, []byte(upperData), 0777)
115 if err != nil {
116 ctx.Fatalln("Failed to check case sensitivity:", err)
117 }
118
119 res, err := ioutil.ReadFile(lowerCase)
120 if err != nil {
121 ctx.Fatalln("Failed to check case sensitivity:", err)
122 }
123
124 if string(res) != lowerData {
125 ctx.Println("************************************************************")
126 ctx.Println("You are building on a case-insensitive filesystem.")
127 ctx.Println("Please move your source tree to a case-sensitive filesystem.")
128 ctx.Println("************************************************************")
129 ctx.Fatalln("Case-insensitive filesystems not supported")
130 }
131}
132
Dan Willemsenf052f782017-05-18 15:29:04 -0700133func help(ctx Context, config Config, what int) {
Jeff Gastondf4a0812017-05-30 20:11:20 -0700134 cmd := Command(ctx, config, "help.sh", "build/make/help.sh")
Dan Willemsenb2e6c2e2017-07-13 17:24:44 -0700135 cmd.Sandbox = dumpvarsSandbox
Dan Willemsenb82471a2018-05-17 16:37:09 -0700136 cmd.RunAndPrintOrFatal()
Dan Willemsen02781d52017-05-12 19:28:13 -0700137}
138
Dan Willemsen1e704462016-08-21 15:17:17 -0700139// Build the tree. The 'what' argument can be used to chose which components of
140// the build to run.
141func Build(ctx Context, config Config, what int) {
142 ctx.Verboseln("Starting build with args:", config.Arguments())
143 ctx.Verboseln("Environment:", config.Environment().Environ())
Dan Willemsen570a2922020-05-26 23:02:29 -0700144
145 if totalRAM := config.TotalRAM(); totalRAM != 0 {
146 ram := float32(totalRAM) / (1024 * 1024 * 1024)
147 ctx.Verbosef("Total RAM: %.3vGB", ram)
148
149 if ram <= 16 {
150 ctx.Println("************************************************************")
151 ctx.Printf("You are building on a machine with %.3vGB of RAM\n", ram)
152 ctx.Println("")
153 ctx.Println("The minimum required amount of free memory is around 16GB,")
154 ctx.Println("and even with that, some configurations may not work.")
155 ctx.Println("")
156 ctx.Println("If you run into segfaults or other errors, try reducing your")
157 ctx.Println("-j value.")
158 ctx.Println("************************************************************")
159 } else if ram <= float32(config.Parallel()) {
160 ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
161 ctx.Println("If you run into segfaults or other errors, try a lower -j value")
162 }
163 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700164
Colin Cross74cda722020-01-16 15:25:50 -0800165 ctx.BeginTrace(metrics.Total, "total")
166 defer ctx.EndTrace()
167
Dan Willemsene0879fc2017-08-04 15:06:27 -0700168 if config.SkipMake() {
169 ctx.Verboseln("Skipping Make/Kati as requested")
170 what = what & (BuildSoong | BuildNinja)
171 }
172
Dan Willemsen1e704462016-08-21 15:17:17 -0700173 if inList("help", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700174 help(ctx, config, what)
Dan Willemsen1e704462016-08-21 15:17:17 -0700175 return
Dan Willemsen0b73b4b2017-05-12 19:28:13 -0700176 } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700177 clean(ctx, config, what)
Dan Willemsen0b73b4b2017-05-12 19:28:13 -0700178 return
Dan Willemsen1e704462016-08-21 15:17:17 -0700179 }
180
Jeff Gaston3615fe82017-05-24 13:14:34 -0700181 // Make sure that no other Soong process is running with the same output directory
182 buildLock := BecomeSingletonOrFail(ctx, config)
183 defer buildLock.Unlock()
184
Anton Hanssonecf0f102018-09-19 22:14:17 +0100185 checkProblematicFiles(ctx)
186
Dan Willemsen1e704462016-08-21 15:17:17 -0700187 SetupOutDir(ctx, config)
188
Dan Willemsendb8457c2017-05-12 16:38:17 -0700189 checkCaseSensitivity(ctx, config)
190
Jeff Gastonefc1b412017-03-29 17:29:06 -0700191 ensureEmptyDirectoriesExist(ctx, config.TempDir())
192
Dan Willemsen18490112018-05-25 16:30:04 -0700193 SetupPath(ctx, config)
194
Yoshisato Yanagisawa2cb0e5d2019-01-10 10:14:16 +0900195 if config.StartGoma() {
196 // Ensure start Goma compiler_proxy
197 startGoma(ctx, config)
198 }
199
Ramy Medhatbbf25672019-07-17 12:30:04 +0000200 if config.StartRBE() {
201 // Ensure RBE proxy is started
202 startRBE(ctx, config)
203 }
204
Dan Willemsen1e704462016-08-21 15:17:17 -0700205 if what&BuildProductConfig != 0 {
206 // Run make for product config
207 runMakeProductConfig(ctx, config)
208 }
209
Colin Cross806fd942019-05-03 13:35:58 -0700210 if inList("installclean", config.Arguments()) ||
211 inList("install-clean", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700212 installClean(ctx, config, what)
213 ctx.Println("Deleted images and staging directories.")
214 return
Colin Cross806fd942019-05-03 13:35:58 -0700215 } else if inList("dataclean", config.Arguments()) ||
216 inList("data-clean", config.Arguments()) {
Dan Willemsenf052f782017-05-18 15:29:04 -0700217 dataClean(ctx, config, what)
218 ctx.Println("Deleted data files.")
219 return
220 }
221
Dan Willemsen1e704462016-08-21 15:17:17 -0700222 if what&BuildSoong != 0 {
223 // Run Soong
Dan Willemsen1e704462016-08-21 15:17:17 -0700224 runSoong(ctx, config)
225 }
226
Dan Willemsen1e704462016-08-21 15:17:17 -0700227 if what&BuildKati != 0 {
228 // Run ckati
Dan Willemsen29971232018-09-26 14:58:30 -0700229 genKatiSuffix(ctx, config)
230 runKatiCleanSpec(ctx, config)
231 runKatiBuild(ctx, config)
Dan Willemsenfb1271a2018-09-26 15:00:42 -0700232 runKatiPackage(ctx, config)
Dan Willemsene0879fc2017-08-04 15:06:27 -0700233
234 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777)
235 } else {
236 // Load last Kati Suffix if it exists
237 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil {
238 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix))
239 config.SetKatiSuffix(string(katiSuffix))
240 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700241 }
242
Colin Cross37193492017-11-16 17:55:00 -0800243 // Write combined ninja file
244 createCombinedBuildNinjaFile(ctx, config)
245
Colin Cross8ba7d472020-06-25 11:27:52 -0700246 distGzipFile(ctx, config, config.CombinedNinjaFile())
247
Colin Cross37193492017-11-16 17:55:00 -0800248 if what&RunBuildTests != 0 {
249 testForDanglingRules(ctx, config)
250 }
251
Dan Willemsen1e704462016-08-21 15:17:17 -0700252 if what&BuildNinja != 0 {
Dan Willemsene0879fc2017-08-04 15:06:27 -0700253 if !config.SkipMake() {
254 installCleanIfNecessary(ctx, config)
255 }
Dan Willemsen02781d52017-05-12 19:28:13 -0700256
Dan Willemsen1e704462016-08-21 15:17:17 -0700257 // Run ninja
258 runNinja(ctx, config)
259 }
260}
Colin Cross8ba7d472020-06-25 11:27:52 -0700261
262// distGzipFile writes a compressed copy of src to the distDir if dist is enabled. Failures
263// are printed but non-fatal.
264func distGzipFile(ctx Context, config Config, src string, subDirs ...string) {
265 if !config.Dist() {
266 return
267 }
268
269 subDir := filepath.Join(subDirs...)
270 destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
271
272 err := os.MkdirAll(destDir, 0777)
273 if err != nil {
274 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
275
276 }
277
278 err = gzipFileToDir(src, destDir)
279 if err != nil {
280 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
281 }
282}
283
284// distFile writes a copy of src to the distDir if dist is enabled. Failures are printed but
285// non-fatal.
286func distFile(ctx Context, config Config, src string, subDirs ...string) {
287 if !config.Dist() {
288 return
289 }
290
291 subDir := filepath.Join(subDirs...)
292 destDir := filepath.Join(config.DistDir(), "soong_ui", subDir)
293
294 err := os.MkdirAll(destDir, 0777)
295 if err != nil {
296 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error())
297
298 }
299
300 _, err = copyFile(src, filepath.Join(destDir, filepath.Base(src)))
301 if err != nil {
302 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error())
303 }
304}