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 "crypto/md5" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "os/user" 23 "path/filepath" 24 "strings" 25 26 "android/soong/ui/metrics" 27 "android/soong/ui/status" 28) 29 30var spaceSlashReplacer = strings.NewReplacer("/", "_", " ", "_") 31 32const katiBuildSuffix = "" 33const katiCleanspecSuffix = "-cleanspec" 34const katiPackageSuffix = "-package" 35 36// genKatiSuffix creates a suffix for kati-generated files so that we can cache 37// them based on their inputs. So this should encode all common changes to Kati 38// inputs. Currently that includes the TARGET_PRODUCT, kati-processed command 39// line arguments, and the directories specified by mm/mmm. 40func genKatiSuffix(ctx Context, config Config) { 41 katiSuffix := "-" + config.TargetProduct() 42 if args := config.KatiArgs(); len(args) > 0 { 43 katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_")) 44 } 45 46 // If the suffix is too long, replace it with a md5 hash and write a 47 // file that contains the original suffix. 48 if len(katiSuffix) > 64 { 49 shortSuffix := "-" + fmt.Sprintf("%x", md5.Sum([]byte(katiSuffix))) 50 config.SetKatiSuffix(shortSuffix) 51 52 ctx.Verbosef("Kati ninja suffix too long: %q", katiSuffix) 53 ctx.Verbosef("Replacing with: %q", shortSuffix) 54 55 if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiBuildNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil { 56 ctx.Println("Error writing suffix file:", err) 57 } 58 } else { 59 config.SetKatiSuffix(katiSuffix) 60 } 61} 62 63func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) { 64 executable := config.PrebuiltBuildTool("ckati") 65 args = append([]string{ 66 "--ninja", 67 "--ninja_dir=" + config.OutDir(), 68 "--ninja_suffix=" + config.KatiSuffix() + extraSuffix, 69 "--no_ninja_prelude", 70 "--regen", 71 "--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"), 72 "--detect_android_echo", 73 "--color_warnings", 74 "--gen_all_targets", 75 "--use_find_emulator", 76 "--werror_find_emulator", 77 "--no_builtin_rules", 78 "--werror_suffix_rules", 79 "--warn_real_to_phony", 80 "--warn_phony_looks_real", 81 "--werror_real_to_phony", 82 "--werror_phony_looks_real", 83 "--werror_writable", 84 "--top_level_phony", 85 "--kati_stats", 86 }, args...) 87 88 if config.Environment().IsEnvTrue("EMPTY_NINJA_FILE") { 89 args = append(args, "--empty_ninja_file") 90 } 91 92 if config.UseRemoteBuild() { 93 args = append(args, "--default_pool=local_pool") 94 } 95 96 cmd := Command(ctx, config, "ckati", executable, args...) 97 cmd.Sandbox = katiSandbox 98 pipe, err := cmd.StdoutPipe() 99 if err != nil { 100 ctx.Fatalln("Error getting output pipe for ckati:", err) 101 } 102 cmd.Stderr = cmd.Stdout 103 104 envFunc(cmd.Environment) 105 106 if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok { 107 u, err := user.Current() 108 if err != nil { 109 ctx.Println("Failed to get current user") 110 } 111 cmd.Environment.Set("BUILD_USERNAME", u.Username) 112 } 113 114 if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok { 115 hostname, err := os.Hostname() 116 if err != nil { 117 ctx.Println("Failed to read hostname") 118 } 119 cmd.Environment.Set("BUILD_HOSTNAME", hostname) 120 } 121 122 cmd.StartOrFatal() 123 status.KatiReader(ctx.Status.StartTool(), pipe) 124 cmd.WaitOrFatal() 125} 126 127func runKatiBuild(ctx Context, config Config) { 128 ctx.BeginTrace(metrics.RunKati, "kati build") 129 defer ctx.EndTrace() 130 131 args := []string{ 132 "--writable", config.OutDir() + "/", 133 "-f", "build/make/core/main.mk", 134 } 135 136 // PDK builds still uses a few implicit rules 137 if !config.IsPdkBuild() { 138 args = append(args, "--werror_implicit_rules") 139 } 140 141 if !config.BuildBrokenDupRules() { 142 args = append(args, "--werror_overriding_commands") 143 } 144 145 args = append(args, config.KatiArgs()...) 146 147 args = append(args, 148 "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(), 149 "SOONG_ANDROID_MK="+config.SoongAndroidMk(), 150 "TARGET_DEVICE_DIR="+config.TargetDeviceDir(), 151 "KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir()) 152 153 runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {}) 154 155 cleanCopyHeaders(ctx, config) 156 cleanOldInstalledFiles(ctx, config) 157} 158 159func cleanCopyHeaders(ctx Context, config Config) { 160 ctx.BeginTrace("clean", "clean copy headers") 161 defer ctx.EndTrace() 162 163 data, err := ioutil.ReadFile(filepath.Join(config.ProductOut(), ".copied_headers_list")) 164 if err != nil { 165 if os.IsNotExist(err) { 166 return 167 } 168 ctx.Fatalf("Failed to read copied headers list: %v", err) 169 } 170 171 headers := strings.Fields(string(data)) 172 if len(headers) < 1 { 173 ctx.Fatal("Failed to parse copied headers list: %q", string(data)) 174 } 175 headerDir := headers[0] 176 headers = headers[1:] 177 178 filepath.Walk(headerDir, 179 func(path string, info os.FileInfo, err error) error { 180 if err != nil { 181 return nil 182 } 183 if info.IsDir() { 184 return nil 185 } 186 if !inList(path, headers) { 187 ctx.Printf("Removing obsolete header %q", path) 188 if err := os.Remove(path); err != nil { 189 ctx.Fatalf("Failed to remove obsolete header %q: %v", path, err) 190 } 191 } 192 return nil 193 }) 194} 195 196func cleanOldInstalledFiles(ctx Context, config Config) { 197 ctx.BeginTrace("clean", "clean old installed files") 198 defer ctx.EndTrace() 199 200 // We shouldn't be removing files from one side of the two-step asan builds 201 var suffix string 202 if v, ok := config.Environment().Get("SANITIZE_TARGET"); ok { 203 if sanitize := strings.Fields(v); inList("address", sanitize) { 204 suffix = "_asan" 205 } 206 } 207 208 cleanOldFiles(ctx, config.ProductOut(), ".installable_files"+suffix) 209 210 cleanOldFiles(ctx, config.HostOut(), ".installable_test_files") 211} 212 213func runKatiPackage(ctx Context, config Config) { 214 ctx.BeginTrace(metrics.RunKati, "kati package") 215 defer ctx.EndTrace() 216 217 args := []string{ 218 "--writable", config.DistDir() + "/", 219 "--werror_implicit_rules", 220 "--werror_overriding_commands", 221 "-f", "build/make/packaging/main.mk", 222 "KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(), 223 } 224 225 runKati(ctx, config, katiPackageSuffix, args, func(env *Environment) { 226 env.Allow([]string{ 227 // Some generic basics 228 "LANG", 229 "LC_MESSAGES", 230 "PATH", 231 "PWD", 232 "TMPDIR", 233 234 // Tool configs 235 "ASAN_SYMBOLIZER_PATH", 236 "JAVA_HOME", 237 "PYTHONDONTWRITEBYTECODE", 238 239 // Build configuration 240 "ANDROID_BUILD_SHELL", 241 "DIST_DIR", 242 "OUT_DIR", 243 }...) 244 245 if config.Dist() { 246 env.Set("DIST", "true") 247 env.Set("DIST_DIR", config.DistDir()) 248 } 249 }) 250} 251 252func runKatiCleanSpec(ctx Context, config Config) { 253 ctx.BeginTrace(metrics.RunKati, "kati cleanspec") 254 defer ctx.EndTrace() 255 256 runKati(ctx, config, katiCleanspecSuffix, []string{ 257 "--werror_implicit_rules", 258 "--werror_overriding_commands", 259 "-f", "build/make/core/cleanbuild.mk", 260 "SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(), 261 "TARGET_DEVICE_DIR=" + config.TargetDeviceDir(), 262 }, func(env *Environment) {}) 263} 264