1// 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 ( 18 "io/ioutil" 19 "os" 20 "path/filepath" 21 "text/template" 22 23 "android/soong/ui/metrics" 24) 25 26// SetupOutDir ensures the out directory exists, and has the proper files to 27// prevent kati 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")) 31 if !config.SkipKati() { 32 // 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")) 40 } 41 // 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")) 44 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir")) 45 46 if buildDateTimeFile, ok := config.environ.Get("BUILD_DATETIME_FILE"); ok { 47 err := ioutil.WriteFile(buildDateTimeFile, []byte(config.buildDateTime), 0666) // a+rw 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 } 54} 55 56var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(` 57builddir = {{.OutDir}} 58{{if .UseRemoteBuild }}pool local_pool 59 depth = {{.Parallel}} 60{{end -}} 61pool highmem_pool 62 depth = {{.HighmemParallel}} 63{{if and (not .SkipKatiNinja) .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}} 64subninja {{.KatiPackageNinjaFile}} 65{{end -}} 66subninja {{.SoongNinjaFile}} 67`)) 68 69func createCombinedBuildNinjaFile(ctx Context, config Config) { 70 // If we're in SkipKati mode but want to run kati ninja, skip creating this file if it already exists 71 if config.SkipKati() && !config.SkipKatiNinja() { 72 if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) { 73 return 74 } 75 } 76 77 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 88// These are bitmasks which can be used to check whether various flags are set e.g. whether to use Bazel. 89const ( 90 _ = iota 91 // Whether to run the kati config step. 92 RunProductConfig = 1 << iota 93 // Whether to run soong to generate a ninja file. 94 RunSoong = 1 << iota 95 // Whether to run kati to generate a ninja file. 96 RunKati = 1 << iota 97 // Whether to include the kati-generated ninja file in the combined ninja. 98 RunKatiNinja = 1 << iota 99 // Whether to run ninja on the combined ninja. 100 RunNinja = 1 << iota 101 // Whether to run bazel on the combined ninja. 102 RunBazel = 1 << iota 103 RunBuildTests = 1 << iota 104 RunAll = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunNinja 105 RunAllWithBazel = RunProductConfig | RunSoong | RunKati | RunKatiNinja | RunBazel 106) 107 108// checkProblematicFiles fails the build if existing Android.mk or CleanSpec.mk files are found at the root of the tree. 109func checkProblematicFiles(ctx Context) { 110 files := []string{"Android.mk", "CleanSpec.mk"} 111 for _, file := range files { 112 if _, err := os.Stat(file); !os.IsNotExist(err) { 113 absolute := absPath(ctx, file) 114 ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file) 115 ctx.Fatalf(" rm %s\n", absolute) 116 } 117 } 118} 119 120// checkCaseSensitivity issues a warning if a case-insensitive file system is being used. 121func checkCaseSensitivity(ctx Context, config Config) { 122 outDir := config.OutDir() 123 lowerCase := filepath.Join(outDir, "casecheck.txt") 124 upperCase := filepath.Join(outDir, "CaseCheck.txt") 125 lowerData := "a" 126 upperData := "B" 127 128 if err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0666); err != nil { // a+rw 129 ctx.Fatalln("Failed to check case sensitivity:", err) 130 } 131 132 if err := ioutil.WriteFile(upperCase, []byte(upperData), 0666); err != nil { // a+rw 133 ctx.Fatalln("Failed to check case sensitivity:", err) 134 } 135 136 res, err := ioutil.ReadFile(lowerCase) 137 if err != nil { 138 ctx.Fatalln("Failed to check case sensitivity:", err) 139 } 140 141 if string(res) != lowerData { 142 ctx.Println("************************************************************") 143 ctx.Println("You are building on a case-insensitive filesystem.") 144 ctx.Println("Please move your source tree to a case-sensitive filesystem.") 145 ctx.Println("************************************************************") 146 ctx.Fatalln("Case-insensitive filesystems not supported") 147 } 148} 149 150// help prints a help/usage message, via the build/make/help.sh script. 151func help(ctx Context, config Config) { 152 cmd := Command(ctx, config, "help.sh", "build/make/help.sh") 153 cmd.Sandbox = dumpvarsSandbox 154 cmd.RunAndPrintOrFatal() 155} 156 157// checkRAM warns if there probably isn't enough RAM to complete a build. 158func checkRAM(ctx Context, config Config) { 159 if totalRAM := config.TotalRAM(); totalRAM != 0 { 160 ram := float32(totalRAM) / (1024 * 1024 * 1024) 161 ctx.Verbosef("Total RAM: %.3vGB", ram) 162 163 if ram <= 16 { 164 ctx.Println("************************************************************") 165 ctx.Printf("You are building on a machine with %.3vGB of RAM\n", ram) 166 ctx.Println("") 167 ctx.Println("The minimum required amount of free memory is around 16GB,") 168 ctx.Println("and even with that, some configurations may not work.") 169 ctx.Println("") 170 ctx.Println("If you run into segfaults or other errors, try reducing your") 171 ctx.Println("-j value.") 172 ctx.Println("************************************************************") 173 } else if ram <= float32(config.Parallel()) { 174 // Want at least 1GB of RAM per job. 175 ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram) 176 ctx.Println("If you run into segfaults or other errors, try a lower -j value") 177 } 178 } 179} 180 181// Build the tree. The 'what' argument can be used to chose which components of 182// the build to run, via checking various bitmasks. 183func Build(ctx Context, config Config) { 184 ctx.Verboseln("Starting build with args:", config.Arguments()) 185 ctx.Verboseln("Environment:", config.Environment().Environ()) 186 187 ctx.BeginTrace(metrics.Total, "total") 188 defer ctx.EndTrace() 189 190 if inList("help", config.Arguments()) { 191 help(ctx, config) 192 return 193 } 194 195 // Make sure that no other Soong process is running with the same output directory 196 buildLock := BecomeSingletonOrFail(ctx, config) 197 defer buildLock.Unlock() 198 199 if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) { 200 clean(ctx, config) 201 return 202 } 203 204 // checkProblematicFiles aborts the build if Android.mk or CleanSpec.mk are found at the root of the tree. 205 checkProblematicFiles(ctx) 206 207 checkRAM(ctx, config) 208 209 SetupOutDir(ctx, config) 210 211 // checkCaseSensitivity issues a warning if a case-insensitive file system is being used. 212 checkCaseSensitivity(ctx, config) 213 214 ensureEmptyDirectoriesExist(ctx, config.TempDir()) 215 216 SetupPath(ctx, config) 217 218 what := RunAll 219 if config.UseBazel() { 220 what = RunAllWithBazel 221 } 222 if config.Checkbuild() { 223 what |= RunBuildTests 224 } 225 if config.SkipConfig() { 226 ctx.Verboseln("Skipping Config as requested") 227 what = what &^ RunProductConfig 228 } 229 if config.SkipKati() { 230 ctx.Verboseln("Skipping Kati as requested") 231 what = what &^ RunKati 232 } 233 if config.SkipKatiNinja() { 234 ctx.Verboseln("Skipping use of Kati ninja as requested") 235 what = what &^ RunKatiNinja 236 } 237 if config.SkipNinja() { 238 ctx.Verboseln("Skipping Ninja as requested") 239 what = what &^ RunNinja 240 } 241 242 if config.StartGoma() { 243 startGoma(ctx, config) 244 } 245 246 if config.StartRBE() { 247 startRBE(ctx, config) 248 } 249 250 if what&RunProductConfig != 0 { 251 runMakeProductConfig(ctx, config) 252 } 253 254 // Everything below here depends on product config. 255 256 if inList("installclean", config.Arguments()) || 257 inList("install-clean", config.Arguments()) { 258 installClean(ctx, config) 259 ctx.Println("Deleted images and staging directories.") 260 return 261 } 262 263 if inList("dataclean", config.Arguments()) || 264 inList("data-clean", config.Arguments()) { 265 dataClean(ctx, config) 266 ctx.Println("Deleted data files.") 267 return 268 } 269 270 if what&RunSoong != 0 { 271 runSoong(ctx, config) 272 273 if config.bazelBuildMode() == generateBuildFiles { 274 // Return early, if we're using Soong as solely the generator of BUILD files. 275 return 276 } 277 } 278 279 if what&RunKati != 0 { 280 genKatiSuffix(ctx, config) 281 runKatiCleanSpec(ctx, config) 282 runKatiBuild(ctx, config) 283 runKatiPackage(ctx, config) 284 285 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0666) // a+rw 286 } else if what&RunKatiNinja != 0 { 287 // Load last Kati Suffix if it exists 288 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil { 289 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix)) 290 config.SetKatiSuffix(string(katiSuffix)) 291 } 292 } 293 294 // Write combined ninja file 295 createCombinedBuildNinjaFile(ctx, config) 296 297 distGzipFile(ctx, config, config.CombinedNinjaFile()) 298 299 if what&RunBuildTests != 0 { 300 testForDanglingRules(ctx, config) 301 } 302 303 if what&RunNinja != 0 { 304 if what&RunKati != 0 { 305 installCleanIfNecessary(ctx, config) 306 } 307 308 runNinjaForBuild(ctx, config) 309 } 310 311 // Currently, using Bazel requires Kati and Soong to run first, so check whether to run Bazel last. 312 if what&RunBazel != 0 { 313 runBazel(ctx, config) 314 } 315} 316 317// distGzipFile writes a compressed copy of src to the distDir if dist is enabled. Failures 318// are printed but non-fatal. 319func distGzipFile(ctx Context, config Config, src string, subDirs ...string) { 320 if !config.Dist() { 321 return 322 } 323 324 subDir := filepath.Join(subDirs...) 325 destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir) 326 327 if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx 328 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error()) 329 } 330 331 if err := gzipFileToDir(src, destDir); err != nil { 332 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error()) 333 } 334} 335 336// distFile writes a copy of src to the distDir if dist is enabled. Failures are printed but 337// non-fatal. 338func distFile(ctx Context, config Config, src string, subDirs ...string) { 339 if !config.Dist() { 340 return 341 } 342 343 subDir := filepath.Join(subDirs...) 344 destDir := filepath.Join(config.RealDistDir(), "soong_ui", subDir) 345 346 if err := os.MkdirAll(destDir, 0777); err != nil { // a+rwx 347 ctx.Printf("failed to mkdir %s: %s", destDir, err.Error()) 348 } 349 350 if _, err := copyFile(src, filepath.Join(destDir, filepath.Base(src))); err != nil { 351 ctx.Printf("failed to dist %s: %s", filepath.Base(src), err.Error()) 352 } 353} 354