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.PrebuiltBuildTool("ckati"), 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 ctx.Metrics.SetMetadataMetrics(ret) 141 } 142 143 return ret, nil 144} 145 146// Variables to print out in the top banner 147var BannerVars = []string{ 148 "PLATFORM_VERSION_CODENAME", 149 "PLATFORM_VERSION", 150 "PRODUCT_INCLUDE_TAGS", 151 "PRODUCT_SOURCE_ROOT_DIRS", 152 "TARGET_PRODUCT", 153 "TARGET_BUILD_VARIANT", 154 "TARGET_BUILD_APPS", 155 "TARGET_BUILD_UNBUNDLED", 156 "TARGET_ARCH", 157 "TARGET_ARCH_VARIANT", 158 "TARGET_CPU_VARIANT", 159 "TARGET_2ND_ARCH", 160 "TARGET_2ND_ARCH_VARIANT", 161 "TARGET_2ND_CPU_VARIANT", 162 "HOST_OS", 163 "HOST_OS_EXTRA", 164 "HOST_CROSS_OS", 165 "BUILD_ID", 166 "OUT_DIR", 167 "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE", 168} 169 170func Banner(make_vars map[string]string) string { 171 b := &bytes.Buffer{} 172 173 fmt.Fprintln(b, "============================================") 174 for _, name := range BannerVars { 175 if make_vars[name] != "" { 176 fmt.Fprintf(b, "%s=%s\n", name, make_vars[name]) 177 } 178 } 179 fmt.Fprint(b, "============================================") 180 181 return b.String() 182} 183 184func runMakeProductConfig(ctx Context, config Config) { 185 // Variables to export into the environment of Kati/Ninja 186 exportEnvVars := []string{ 187 // So that we can use the correct TARGET_PRODUCT if it's been 188 // modified by a buildspec.mk 189 "TARGET_PRODUCT", 190 "TARGET_BUILD_VARIANT", 191 "TARGET_BUILD_APPS", 192 "TARGET_BUILD_UNBUNDLED", 193 194 // compiler wrappers set up by make 195 "CC_WRAPPER", 196 "CXX_WRAPPER", 197 "RBE_WRAPPER", 198 "JAVAC_WRAPPER", 199 "R8_WRAPPER", 200 "D8_WRAPPER", 201 202 // ccache settings 203 "CCACHE_COMPILERCHECK", 204 "CCACHE_SLOPPINESS", 205 "CCACHE_BASEDIR", 206 "CCACHE_CPP2", 207 208 // LLVM compiler wrapper options 209 "TOOLCHAIN_RUSAGE_OUTPUT", 210 } 211 212 allVars := append(append([]string{ 213 // Used to execute Kati and Ninja 214 "NINJA_GOALS", 215 "KATI_GOALS", 216 217 // To find target/product/<DEVICE> 218 "TARGET_DEVICE", 219 220 // So that later Kati runs can find BoardConfig.mk faster 221 "TARGET_DEVICE_DIR", 222 223 // Whether --werror_overriding_commands will work 224 "BUILD_BROKEN_DUP_RULES", 225 226 // Whether to enable the network during the build 227 "BUILD_BROKEN_USES_NETWORK", 228 229 // Extra environment variables to be exported to ninja 230 "BUILD_BROKEN_NINJA_USES_ENV_VARS", 231 232 // Used to restrict write access to source tree 233 "BUILD_BROKEN_SRC_DIR_IS_WRITABLE", 234 "BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST", 235 236 // Not used, but useful to be in the soong.log 237 "BOARD_VNDK_VERSION", 238 "TARGET_BUILD_TYPE", 239 "HOST_ARCH", 240 "HOST_2ND_ARCH", 241 "HOST_CROSS_ARCH", 242 "HOST_CROSS_2ND_ARCH", 243 "HOST_BUILD_TYPE", 244 "PRODUCT_SOONG_NAMESPACES", 245 246 "DEFAULT_WARNING_BUILD_MODULE_TYPES", 247 "DEFAULT_ERROR_BUILD_MODULE_TYPES", 248 "BUILD_BROKEN_PREBUILT_ELF_FILES", 249 "BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW", 250 "BUILD_BROKEN_USES_BUILD_COPY_HEADERS", 251 "BUILD_BROKEN_USES_BUILD_EXECUTABLE", 252 "BUILD_BROKEN_USES_BUILD_FUZZ_TEST", 253 "BUILD_BROKEN_USES_BUILD_HEADER_LIBRARY", 254 "BUILD_BROKEN_USES_BUILD_HOST_EXECUTABLE", 255 "BUILD_BROKEN_USES_BUILD_HOST_JAVA_LIBRARY", 256 "BUILD_BROKEN_USES_BUILD_HOST_PREBUILT", 257 "BUILD_BROKEN_USES_BUILD_HOST_SHARED_LIBRARY", 258 "BUILD_BROKEN_USES_BUILD_HOST_STATIC_LIBRARY", 259 "BUILD_BROKEN_USES_BUILD_JAVA_LIBRARY", 260 "BUILD_BROKEN_USES_BUILD_MULTI_PREBUILT", 261 "BUILD_BROKEN_USES_BUILD_NATIVE_TEST", 262 "BUILD_BROKEN_USES_BUILD_NOTICE_FILE", 263 "BUILD_BROKEN_USES_BUILD_PACKAGE", 264 "BUILD_BROKEN_USES_BUILD_PHONY_PACKAGE", 265 "BUILD_BROKEN_USES_BUILD_PREBUILT", 266 "BUILD_BROKEN_USES_BUILD_RRO_PACKAGE", 267 "BUILD_BROKEN_USES_BUILD_SHARED_LIBRARY", 268 "BUILD_BROKEN_USES_BUILD_STATIC_JAVA_LIBRARY", 269 "BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY", 270 }, exportEnvVars...), BannerVars...) 271 272 makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "") 273 if err != nil { 274 ctx.Fatalln("Error dumping make vars:", err) 275 } 276 277 env := config.Environment() 278 // Print the banner like make does 279 if !env.IsEnvTrue("ANDROID_QUIET_BUILD") { 280 fmt.Fprintln(ctx.Writer, Banner(makeVars)) 281 } 282 283 // Populate the environment 284 for _, name := range exportEnvVars { 285 if makeVars[name] == "" { 286 env.Unset(name) 287 } else { 288 env.Set(name, makeVars[name]) 289 } 290 } 291 292 config.SetKatiArgs(strings.Fields(makeVars["KATI_GOALS"])) 293 config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"])) 294 config.SetTargetDevice(makeVars["TARGET_DEVICE"]) 295 config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"]) 296 config.sandboxConfig.SetSrcDirIsRO(makeVars["BUILD_BROKEN_SRC_DIR_IS_WRITABLE"] == "false") 297 config.sandboxConfig.SetSrcDirRWAllowlist(strings.Fields(makeVars["BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST"])) 298 299 config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true") 300 config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true") 301 config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"])) 302 config.SetIncludeTags(strings.Fields(makeVars["PRODUCT_INCLUDE_TAGS"])) 303 config.SetSourceRootDirs(strings.Fields(makeVars["PRODUCT_SOURCE_ROOT_DIRS"])) 304} 305