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 24// Ensures the out directory exists, and has the proper files to prevent kati 25// from recursing into it. 26func SetupOutDir(ctx Context, config Config) { 27 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk")) 28 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk")) 29 if !config.SkipMake() { 30 ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.in_make")) 31 } 32 // The ninja_build file is used by our buildbots to understand that the output 33 // can be parsed as ninja output. 34 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build")) 35 ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), ".out-dir")) 36} 37 38var combinedBuildNinjaTemplate = template.Must(template.New("combined").Parse(` 39builddir = {{.OutDir}} 40pool local_pool 41 depth = {{.Parallel}} 42build _kati_always_build_: phony 43{{if .HasKatiSuffix}}subninja {{.KatiBuildNinjaFile}} 44subninja {{.KatiPackageNinjaFile}} 45{{end -}} 46subninja {{.SoongNinjaFile}} 47`)) 48 49func createCombinedBuildNinjaFile(ctx Context, config Config) { 50 // If we're in SkipMake mode, skip creating this file if it already exists 51 if config.SkipMake() { 52 if _, err := os.Stat(config.CombinedNinjaFile()); err == nil || !os.IsNotExist(err) { 53 return 54 } 55 } 56 57 file, err := os.Create(config.CombinedNinjaFile()) 58 if err != nil { 59 ctx.Fatalln("Failed to create combined ninja file:", err) 60 } 61 defer file.Close() 62 63 if err := combinedBuildNinjaTemplate.Execute(file, config); err != nil { 64 ctx.Fatalln("Failed to write combined ninja file:", err) 65 } 66} 67 68const ( 69 BuildNone = iota 70 BuildProductConfig = 1 << iota 71 BuildSoong = 1 << iota 72 BuildKati = 1 << iota 73 BuildNinja = 1 << iota 74 RunBuildTests = 1 << iota 75 BuildAll = BuildProductConfig | BuildSoong | BuildKati | BuildNinja 76) 77 78func checkProblematicFiles(ctx Context) { 79 files := []string{"Android.mk", "CleanSpec.mk"} 80 for _, file := range files { 81 if _, err := os.Stat(file); !os.IsNotExist(err) { 82 absolute := absPath(ctx, file) 83 ctx.Printf("Found %s in tree root. This file needs to be removed to build.\n", file) 84 ctx.Fatalf(" rm %s\n", absolute) 85 } 86 } 87} 88 89func checkCaseSensitivity(ctx Context, config Config) { 90 outDir := config.OutDir() 91 lowerCase := filepath.Join(outDir, "casecheck.txt") 92 upperCase := filepath.Join(outDir, "CaseCheck.txt") 93 lowerData := "a" 94 upperData := "B" 95 96 err := ioutil.WriteFile(lowerCase, []byte(lowerData), 0777) 97 if err != nil { 98 ctx.Fatalln("Failed to check case sensitivity:", err) 99 } 100 101 err = ioutil.WriteFile(upperCase, []byte(upperData), 0777) 102 if err != nil { 103 ctx.Fatalln("Failed to check case sensitivity:", err) 104 } 105 106 res, err := ioutil.ReadFile(lowerCase) 107 if err != nil { 108 ctx.Fatalln("Failed to check case sensitivity:", err) 109 } 110 111 if string(res) != lowerData { 112 ctx.Println("************************************************************") 113 ctx.Println("You are building on a case-insensitive filesystem.") 114 ctx.Println("Please move your source tree to a case-sensitive filesystem.") 115 ctx.Println("************************************************************") 116 ctx.Fatalln("Case-insensitive filesystems not supported") 117 } 118} 119 120func help(ctx Context, config Config, what int) { 121 cmd := Command(ctx, config, "help.sh", "build/make/help.sh") 122 cmd.Sandbox = dumpvarsSandbox 123 cmd.RunAndPrintOrFatal() 124} 125 126// Build the tree. The 'what' argument can be used to chose which components of 127// the build to run. 128func Build(ctx Context, config Config, what int) { 129 ctx.Verboseln("Starting build with args:", config.Arguments()) 130 ctx.Verboseln("Environment:", config.Environment().Environ()) 131 132 if config.SkipMake() { 133 ctx.Verboseln("Skipping Make/Kati as requested") 134 what = what & (BuildSoong | BuildNinja) 135 } 136 137 if inList("help", config.Arguments()) { 138 help(ctx, config, what) 139 return 140 } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) { 141 clean(ctx, config, what) 142 return 143 } 144 145 // Make sure that no other Soong process is running with the same output directory 146 buildLock := BecomeSingletonOrFail(ctx, config) 147 defer buildLock.Unlock() 148 149 checkProblematicFiles(ctx) 150 151 SetupOutDir(ctx, config) 152 153 checkCaseSensitivity(ctx, config) 154 155 ensureEmptyDirectoriesExist(ctx, config.TempDir()) 156 157 SetupPath(ctx, config) 158 159 if config.StartGoma() { 160 // Ensure start Goma compiler_proxy 161 startGoma(ctx, config) 162 } 163 164 if what&BuildProductConfig != 0 { 165 // Run make for product config 166 runMakeProductConfig(ctx, config) 167 } 168 169 if inList("installclean", config.Arguments()) { 170 installClean(ctx, config, what) 171 ctx.Println("Deleted images and staging directories.") 172 return 173 } else if inList("dataclean", config.Arguments()) { 174 dataClean(ctx, config, what) 175 ctx.Println("Deleted data files.") 176 return 177 } 178 179 if what&BuildSoong != 0 { 180 // Run Soong 181 runSoong(ctx, config) 182 } 183 184 if what&BuildKati != 0 { 185 // Run ckati 186 genKatiSuffix(ctx, config) 187 runKatiCleanSpec(ctx, config) 188 runKatiBuild(ctx, config) 189 runKatiPackage(ctx, config) 190 191 ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777) 192 } else { 193 // Load last Kati Suffix if it exists 194 if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil { 195 ctx.Verboseln("Loaded previous kati config:", string(katiSuffix)) 196 config.SetKatiSuffix(string(katiSuffix)) 197 } 198 } 199 200 // Write combined ninja file 201 createCombinedBuildNinjaFile(ctx, config) 202 203 if what&RunBuildTests != 0 { 204 testForDanglingRules(ctx, config) 205 } 206 207 if what&BuildNinja != 0 { 208 if !config.SkipMake() { 209 installCleanIfNecessary(ctx, config) 210 } 211 212 // Run ninja 213 runNinja(ctx, config) 214 } 215} 216