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 "bytes" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "strings" 23 24 "android/soong/ui/metrics" 25 "android/soong/ui/status" 26) 27 28// DumpMakeVars can be used to extract the values of Make variables after the 29// product configurations are loaded. This is roughly equivalent to the 30// `get_build_var` bash function. 31// 32// goals can be used to set MAKECMDGOALS, which emulates passing arguments to 33// Make without actually building them. So all the variables based on 34// MAKECMDGOALS can be read. 35// 36// vars is the list of variables to read. The values will be put in the 37// returned map. 38// 39// variables controlled by soong_ui directly are now returned without needing 40// to call into make, to retain compatibility. 41func DumpMakeVars(ctx Context, config Config, goals, vars []string) (map[string]string, error) { 42 soongUiVars := map[string]func() string{ 43 "OUT_DIR": func() string { return config.OutDir() }, 44 "DIST_DIR": func() string { return config.DistDir() }, 45 "TMPDIR": func() string { return absPath(ctx, config.TempDir()) }, 46 } 47 48 makeVars := make([]string, 0, len(vars)) 49 for _, v := range vars { 50 if _, ok := soongUiVars[v]; !ok { 51 makeVars = append(makeVars, v) 52 } 53 } 54 55 var ret map[string]string 56 if len(makeVars) > 0 { 57 // It's not safe to use the same TMPDIR as the build, as that can be removed. 58 tmpDir, err := ioutil.TempDir("", "dumpvars") 59 if err != nil { 60 return nil, err 61 } 62 defer os.RemoveAll(tmpDir) 63 64 SetupLitePath(ctx, config, tmpDir) 65 66 ret, err = dumpMakeVars(ctx, config, goals, makeVars, false, tmpDir) 67 if err != nil { 68 return ret, err 69 } 70 } else { 71 ret = make(map[string]string) 72 } 73 74 for _, v := range vars { 75 if f, ok := soongUiVars[v]; ok { 76 ret[v] = f() 77 } 78 } 79 80 return ret, nil 81} 82 83func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool, tmpDir string) (map[string]string, error) { 84 ctx.BeginTrace(metrics.RunKati, "dumpvars") 85 defer ctx.EndTrace() 86 87 tool := ctx.Status.StartTool() 88 if write_soong_vars { 89 // only print this when write_soong_vars is true so that it's not printed when using 90 // the get_build_var command. 91 tool.Status("Running product configuration...") 92 } 93 defer tool.Finish() 94 95 cmd := Command(ctx, config, "dumpvars", 96 config.KatiBin(), 97 "-f", "build/make/core/config.mk", 98 "--color_warnings", 99 "--kati_stats", 100 "dump-many-vars", 101 "MAKECMDGOALS="+strings.Join(goals, " ")) 102 cmd.Environment.Set("CALLED_FROM_SETUP", "true") 103 if write_soong_vars { 104 cmd.Environment.Set("WRITE_SOONG_VARIABLES", "true") 105 } 106 cmd.Environment.Set("DUMP_MANY_VARS", strings.Join(vars, " ")) 107 if tmpDir != "" { 108 cmd.Environment.Set("TMPDIR", tmpDir) 109 } 110 cmd.Sandbox = dumpvarsSandbox 111 output := bytes.Buffer{} 112 cmd.Stdout = &output 113 pipe, err := cmd.StderrPipe() 114 if err != nil { 115 ctx.Fatalln("Error getting output pipe for ckati:", err) 116 } 117 cmd.StartOrFatal() 118 // TODO: error out when Stderr contains any content 119 status.KatiReader(tool, pipe) 120 cmd.WaitOrFatal() 121 122 ret := make(map[string]string, len(vars)) 123 for _, line := range strings.Split(output.String(), "\n") { 124 if len(line) == 0 { 125 continue 126 } 127 128 if key, value, ok := decodeKeyValue(line); ok { 129 if value, ok = singleUnquote(value); ok { 130 ret[key] = value 131 ctx.Verboseln(key, value) 132 } else { 133 return nil, fmt.Errorf("Failed to parse make line: %q", line) 134 } 135 } else { 136 return nil, fmt.Errorf("Failed to parse make line: %q", line) 137 } 138 } 139 if ctx.Metrics != nil { 140 // Also include TARGET_RELEASE in the metrics. Do this first 141 // so that it gets overwritten if dumpvars ever spits it out. 142 if release, found := os.LookupEnv("TARGET_RELEASE"); found { 143 ctx.Metrics.SetMetadataMetrics( 144 map[string]string{"TARGET_RELEASE": release}) 145 } 146 ctx.Metrics.SetMetadataMetrics(ret) 147 } 148 149 return ret, nil 150} 151 152// Variables to print out in the top banner 153var BannerVars = []string{ 154 "PLATFORM_VERSION_CODENAME", 155 "PLATFORM_VERSION", 156 "PRODUCT_SOURCE_ROOT_DIRS", 157 "TARGET_PRODUCT", 158 "TARGET_BUILD_VARIANT", 159 "TARGET_BUILD_APPS", 160 "TARGET_BUILD_UNBUNDLED", 161 "TARGET_ARCH", 162 "TARGET_ARCH_VARIANT", 163 "TARGET_CPU_VARIANT", 164 "TARGET_2ND_ARCH", 165 "TARGET_2ND_ARCH_VARIANT", 166 "TARGET_2ND_CPU_VARIANT", 167 "HOST_OS", 168 "HOST_OS_EXTRA", 169 "HOST_CROSS_OS", 170 "BUILD_ID", 171 "OUT_DIR", 172 "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE", 173} 174 175func Banner(config Config, make_vars map[string]string) string { 176 b := &bytes.Buffer{} 177 178 fmt.Fprintln(b, "============================================") 179 for _, name := range BannerVars { 180 if make_vars[name] != "" { 181 fmt.Fprintf(b, "%s=%s\n", name, make_vars[name]) 182 } 183 } 184 if config.skipKatiControlledByFlags { 185 fmt.Fprintf(b, "SOONG_ONLY=%t\n", config.soongOnlyRequested) 186 } else { // default for this product 187 fmt.Fprintf(b, "SOONG_ONLY=%t\n", make_vars["PRODUCT_SOONG_ONLY"] == "true") 188 } 189 190 fmt.Fprint(b, "============================================") 191 192 return b.String() 193} 194 195func runMakeProductConfig(ctx Context, config Config) { 196 // Variables to export into the environment of Kati/Ninja 197 exportEnvVars := []string{ 198 // So that we can use the correct TARGET_PRODUCT if it's been 199 // modified by a buildspec.mk 200 "TARGET_PRODUCT", 201 "TARGET_BUILD_VARIANT", 202 "TARGET_BUILD_APPS", 203 "TARGET_BUILD_UNBUNDLED", 204 205 // Additional release config maps 206 "PRODUCT_RELEASE_CONFIG_MAPS", 207 208 // compiler wrappers set up by make 209 "CC_WRAPPER", 210 "CXX_WRAPPER", 211 "RBE_WRAPPER", 212 "JAVAC_WRAPPER", 213 "R8_WRAPPER", 214 "D8_WRAPPER", 215 216 // ccache settings 217 "CCACHE_COMPILERCHECK", 218 "CCACHE_SLOPPINESS", 219 "CCACHE_BASEDIR", 220 "CCACHE_CPP2", 221 222 // LLVM compiler wrapper options 223 "TOOLCHAIN_RUSAGE_OUTPUT", 224 } 225 226 allVars := append(append([]string{ 227 // Used to execute Kati and Ninja 228 "NINJA_GOALS", 229 "KATI_GOALS", 230 231 // To find target/product/<DEVICE> 232 "TARGET_DEVICE", 233 234 // So that later Kati runs can find BoardConfig.mk faster 235 "TARGET_DEVICE_DIR", 236 237 // Whether --werror_overriding_commands will work 238 "BUILD_BROKEN_DUP_RULES", 239 240 // Whether to enable the network during the build 241 "BUILD_BROKEN_USES_NETWORK", 242 243 // Extra environment variables to be exported to ninja 244 "BUILD_BROKEN_NINJA_USES_ENV_VARS", 245 246 // Used to restrict write access to source tree 247 "BUILD_BROKEN_SRC_DIR_IS_WRITABLE", 248 "BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST", 249 250 // Whether missing outputs should be treated as warnings 251 // instead of errors. 252 // `true` will relegate missing outputs to warnings. 253 "BUILD_BROKEN_MISSING_OUTPUTS", 254 255 "PRODUCT_SOONG_ONLY", 256 257 // Not used, but useful to be in the soong.log 258 "TARGET_BUILD_TYPE", 259 "HOST_ARCH", 260 "HOST_2ND_ARCH", 261 "HOST_CROSS_ARCH", 262 "HOST_CROSS_2ND_ARCH", 263 "HOST_BUILD_TYPE", 264 "PRODUCT_SOONG_NAMESPACES", 265 266 "DEFAULT_WARNING_BUILD_MODULE_TYPES", 267 "DEFAULT_ERROR_BUILD_MODULE_TYPES", 268 "BUILD_BROKEN_PREBUILT_ELF_FILES", 269 "BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW", 270 "BUILD_BROKEN_USES_BUILD_COPY_HEADERS", 271 "BUILD_BROKEN_USES_BUILD_EXECUTABLE", 272 "BUILD_BROKEN_USES_BUILD_FUZZ_TEST", 273 "BUILD_BROKEN_USES_BUILD_HEADER_LIBRARY", 274 "BUILD_BROKEN_USES_BUILD_HOST_EXECUTABLE", 275 "BUILD_BROKEN_USES_BUILD_HOST_JAVA_LIBRARY", 276 "BUILD_BROKEN_USES_BUILD_HOST_PREBUILT", 277 "BUILD_BROKEN_USES_BUILD_HOST_SHARED_LIBRARY", 278 "BUILD_BROKEN_USES_BUILD_HOST_STATIC_LIBRARY", 279 "BUILD_BROKEN_USES_BUILD_JAVA_LIBRARY", 280 "BUILD_BROKEN_USES_BUILD_MULTI_PREBUILT", 281 "BUILD_BROKEN_USES_BUILD_NATIVE_TEST", 282 "BUILD_BROKEN_USES_BUILD_NOTICE_FILE", 283 "BUILD_BROKEN_USES_BUILD_PACKAGE", 284 "BUILD_BROKEN_USES_BUILD_PHONY_PACKAGE", 285 "BUILD_BROKEN_USES_BUILD_PREBUILT", 286 "BUILD_BROKEN_USES_BUILD_RRO_PACKAGE", 287 "BUILD_BROKEN_USES_BUILD_SHARED_LIBRARY", 288 "BUILD_BROKEN_USES_BUILD_STATIC_JAVA_LIBRARY", 289 "BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY", 290 "RELEASE_BUILD_EXECUTION_METRICS", 291 }, exportEnvVars...), BannerVars...) 292 293 makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "") 294 if err != nil { 295 ctx.Fatalln("Error dumping make vars:", err) 296 } 297 298 // Populate the environment 299 env := config.Environment() 300 for _, name := range exportEnvVars { 301 if makeVars[name] == "" { 302 env.Unset(name) 303 } else { 304 env.Set(name, makeVars[name]) 305 } 306 } 307 308 config.SetKatiArgs(strings.Fields(makeVars["KATI_GOALS"])) 309 config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"])) 310 config.SetTargetDevice(makeVars["TARGET_DEVICE"]) 311 config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"]) 312 config.sandboxConfig.SetSrcDirIsRO(makeVars["BUILD_BROKEN_SRC_DIR_IS_WRITABLE"] == "false") 313 config.sandboxConfig.SetSrcDirRWAllowlist(strings.Fields(makeVars["BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST"])) 314 315 config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true") 316 config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true") 317 config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"])) 318 config.SetSourceRootDirs(strings.Fields(makeVars["PRODUCT_SOURCE_ROOT_DIRS"])) 319 config.SetBuildBrokenMissingOutputs(makeVars["BUILD_BROKEN_MISSING_OUTPUTS"] == "true") 320 321 if !config.skipKatiControlledByFlags { 322 if makeVars["PRODUCT_SOONG_ONLY"] == "true" { 323 config.soongOnlyRequested = true 324 config.skipKati = true 325 config.skipKatiNinja = true 326 } 327 } 328 329 // Print the banner like make did 330 if !env.IsEnvTrue("ANDROID_QUIET_BUILD") { 331 fmt.Fprintln(ctx.Writer, Banner(config, makeVars)) 332 } 333} 334