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 if oneShot, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok { 46 katiSuffix += "-" + spaceSlashReplacer.Replace(oneShot) 47 } 48 49 // If the suffix is too long, replace it with a md5 hash and write a 50 // file that contains the original suffix. 51 if len(katiSuffix) > 64 { 52 shortSuffix := "-" + fmt.Sprintf("%x", md5.Sum([]byte(katiSuffix))) 53 config.SetKatiSuffix(shortSuffix) 54 55 ctx.Verbosef("Kati ninja suffix too long: %q", katiSuffix) 56 ctx.Verbosef("Replacing with: %q", shortSuffix) 57 58 if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiBuildNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil { 59 ctx.Println("Error writing suffix file:", err) 60 } 61 } else { 62 config.SetKatiSuffix(katiSuffix) 63 } 64} 65 66func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) { 67 executable := config.PrebuiltBuildTool("ckati") 68 args = append([]string{ 69 "--ninja", 70 "--ninja_dir=" + config.OutDir(), 71 "--ninja_suffix=" + config.KatiSuffix() + extraSuffix, 72 "--no_ninja_prelude", 73 "--regen", 74 "--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"), 75 "--detect_android_echo", 76 "--color_warnings", 77 "--gen_all_targets", 78 "--use_find_emulator", 79 "--werror_find_emulator", 80 "--no_builtin_rules", 81 "--werror_suffix_rules", 82 "--warn_real_to_phony", 83 "--warn_phony_looks_real", 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 cmd := Command(ctx, config, "ckati", executable, args...) 93 cmd.Sandbox = katiSandbox 94 pipe, err := cmd.StdoutPipe() 95 if err != nil { 96 ctx.Fatalln("Error getting output pipe for ckati:", err) 97 } 98 cmd.Stderr = cmd.Stdout 99 100 envFunc(cmd.Environment) 101 102 if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok { 103 u, err := user.Current() 104 if err != nil { 105 ctx.Println("Failed to get current user") 106 } 107 cmd.Environment.Set("BUILD_USERNAME", u.Username) 108 } 109 110 if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok { 111 hostname, err := os.Hostname() 112 if err != nil { 113 ctx.Println("Failed to read hostname") 114 } 115 cmd.Environment.Set("BUILD_HOSTNAME", hostname) 116 } 117 118 cmd.StartOrFatal() 119 status.KatiReader(ctx.Status.StartTool(), pipe) 120 cmd.WaitOrFatal() 121} 122 123func runKatiBuild(ctx Context, config Config) { 124 ctx.BeginTrace(metrics.RunKati, "kati build") 125 defer ctx.EndTrace() 126 127 args := []string{ 128 "--writable", config.OutDir() + "/", 129 "-f", "build/make/core/main.mk", 130 } 131 132 // PDK builds still uses a few implicit rules 133 if !config.IsPdkBuild() { 134 args = append(args, "--werror_implicit_rules") 135 } 136 137 if !config.BuildBrokenDupRules() { 138 args = append(args, "--werror_overriding_commands") 139 } 140 141 if !config.BuildBrokenPhonyTargets() { 142 args = append(args, 143 "--werror_real_to_phony", 144 "--werror_phony_looks_real", 145 "--werror_writable") 146 } 147 148 args = append(args, config.KatiArgs()...) 149 150 args = append(args, 151 "SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(), 152 "SOONG_ANDROID_MK="+config.SoongAndroidMk(), 153 "TARGET_DEVICE_DIR="+config.TargetDeviceDir(), 154 "KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir()) 155 156 runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {}) 157} 158 159func runKatiPackage(ctx Context, config Config) { 160 ctx.BeginTrace(metrics.RunKati, "kati package") 161 defer ctx.EndTrace() 162 163 args := []string{ 164 "--writable", config.DistDir() + "/", 165 "--werror_writable", 166 "--werror_implicit_rules", 167 "--werror_overriding_commands", 168 "--werror_real_to_phony", 169 "--werror_phony_looks_real", 170 "-f", "build/make/packaging/main.mk", 171 "KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(), 172 } 173 174 runKati(ctx, config, katiPackageSuffix, args, func(env *Environment) { 175 env.Allow([]string{ 176 // Some generic basics 177 "LANG", 178 "LC_MESSAGES", 179 "PATH", 180 "PWD", 181 "TMPDIR", 182 183 // Tool configs 184 "JAVA_HOME", 185 "PYTHONDONTWRITEBYTECODE", 186 187 // Build configuration 188 "ANDROID_BUILD_SHELL", 189 "DIST_DIR", 190 "OUT_DIR", 191 }...) 192 193 if config.Dist() { 194 env.Set("DIST", "true") 195 env.Set("DIST_DIR", config.DistDir()) 196 } 197 }) 198} 199 200func runKatiCleanSpec(ctx Context, config Config) { 201 ctx.BeginTrace(metrics.RunKati, "kati cleanspec") 202 defer ctx.EndTrace() 203 204 runKati(ctx, config, katiCleanspecSuffix, []string{ 205 "--werror_writable", 206 "--werror_implicit_rules", 207 "--werror_overriding_commands", 208 "--werror_real_to_phony", 209 "--werror_phony_looks_real", 210 "-f", "build/make/core/cleanbuild.mk", 211 "SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(), 212 "TARGET_DEVICE_DIR=" + config.TargetDeviceDir(), 213 }, func(env *Environment) {}) 214} 215