| // Copyright 2017 Google Inc. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package build |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "strings" |
| |
| "android/soong/ui/metrics" |
| "android/soong/ui/status" |
| ) |
| |
| // DumpMakeVars can be used to extract the values of Make variables after the |
| // product configurations are loaded. This is roughly equivalent to the |
| // `get_build_var` bash function. |
| // |
| // goals can be used to set MAKECMDGOALS, which emulates passing arguments to |
| // Make without actually building them. So all the variables based on |
| // MAKECMDGOALS can be read. |
| // |
| // vars is the list of variables to read. The values will be put in the |
| // returned map. |
| // |
| // variables controlled by soong_ui directly are now returned without needing |
| // to call into make, to retain compatibility. |
| func DumpMakeVars(ctx Context, config Config, goals, vars []string) (map[string]string, error) { |
| soongUiVars := map[string]func() string{ |
| "OUT_DIR": func() string { return config.OutDir() }, |
| "DIST_DIR": func() string { return config.DistDir() }, |
| "TMPDIR": func() string { return absPath(ctx, config.TempDir()) }, |
| } |
| |
| makeVars := make([]string, 0, len(vars)) |
| for _, v := range vars { |
| if _, ok := soongUiVars[v]; !ok { |
| makeVars = append(makeVars, v) |
| } |
| } |
| |
| var ret map[string]string |
| if len(makeVars) > 0 { |
| // It's not safe to use the same TMPDIR as the build, as that can be removed. |
| tmpDir, err := ioutil.TempDir("", "dumpvars") |
| if err != nil { |
| return nil, err |
| } |
| defer os.RemoveAll(tmpDir) |
| |
| SetupLitePath(ctx, config, tmpDir) |
| |
| ret, err = dumpMakeVars(ctx, config, goals, makeVars, false, tmpDir) |
| if err != nil { |
| return ret, err |
| } |
| } else { |
| ret = make(map[string]string) |
| } |
| |
| for _, v := range vars { |
| if f, ok := soongUiVars[v]; ok { |
| ret[v] = f() |
| } |
| } |
| |
| return ret, nil |
| } |
| |
| func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool, tmpDir string) (map[string]string, error) { |
| ctx.BeginTrace(metrics.RunKati, "dumpvars") |
| defer ctx.EndTrace() |
| |
| tool := ctx.Status.StartTool() |
| if write_soong_vars { |
| // only print this when write_soong_vars is true so that it's not printed when using |
| // the get_build_var command. |
| tool.Status("Running product configuration...") |
| } |
| defer tool.Finish() |
| |
| cmd := Command(ctx, config, "dumpvars", |
| config.PrebuiltBuildTool("ckati"), |
| "-f", "build/make/core/config.mk", |
| "--color_warnings", |
| "--kati_stats", |
| "dump-many-vars", |
| "MAKECMDGOALS="+strings.Join(goals, " ")) |
| cmd.Environment.Set("CALLED_FROM_SETUP", "true") |
| if write_soong_vars { |
| cmd.Environment.Set("WRITE_SOONG_VARIABLES", "true") |
| } |
| cmd.Environment.Set("DUMP_MANY_VARS", strings.Join(vars, " ")) |
| if tmpDir != "" { |
| cmd.Environment.Set("TMPDIR", tmpDir) |
| } |
| cmd.Sandbox = dumpvarsSandbox |
| output := bytes.Buffer{} |
| cmd.Stdout = &output |
| pipe, err := cmd.StderrPipe() |
| if err != nil { |
| ctx.Fatalln("Error getting output pipe for ckati:", err) |
| } |
| cmd.StartOrFatal() |
| // TODO: error out when Stderr contains any content |
| status.KatiReader(tool, pipe) |
| cmd.WaitOrFatal() |
| |
| ret := make(map[string]string, len(vars)) |
| for _, line := range strings.Split(output.String(), "\n") { |
| if len(line) == 0 { |
| continue |
| } |
| |
| if key, value, ok := decodeKeyValue(line); ok { |
| if value, ok = singleUnquote(value); ok { |
| ret[key] = value |
| ctx.Verboseln(key, value) |
| } else { |
| return nil, fmt.Errorf("Failed to parse make line: %q", line) |
| } |
| } else { |
| return nil, fmt.Errorf("Failed to parse make line: %q", line) |
| } |
| } |
| if ctx.Metrics != nil { |
| ctx.Metrics.SetMetadataMetrics(ret) |
| } |
| |
| return ret, nil |
| } |
| |
| // Variables to print out in the top banner |
| var BannerVars = []string{ |
| "PLATFORM_VERSION_CODENAME", |
| "PLATFORM_VERSION", |
| "LEAF_VERSION", |
| "LEAF_FLAVOR", |
| "LEAF_BUILDTYPE", |
| "PRODUCT_INCLUDE_TAGS", |
| "PRODUCT_SOURCE_ROOT_DIRS", |
| "TARGET_PRODUCT", |
| "TARGET_BUILD_VARIANT", |
| "TARGET_BUILD_APPS", |
| "TARGET_BUILD_UNBUNDLED", |
| "TARGET_ARCH", |
| "TARGET_ARCH_VARIANT", |
| "TARGET_CPU_VARIANT", |
| "TARGET_2ND_ARCH", |
| "TARGET_2ND_ARCH_VARIANT", |
| "TARGET_2ND_CPU_VARIANT", |
| "HOST_OS", |
| "HOST_OS_EXTRA", |
| "HOST_CROSS_OS", |
| "BUILD_ID", |
| "OUT_DIR", |
| "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE", |
| } |
| |
| func Banner(make_vars map[string]string) string { |
| b := &bytes.Buffer{} |
| |
| fmt.Fprintln(b, "============================================") |
| for _, name := range BannerVars { |
| if make_vars[name] != "" { |
| fmt.Fprintf(b, "%s=%s\n", name, make_vars[name]) |
| } |
| } |
| fmt.Fprint(b, "============================================") |
| |
| return b.String() |
| } |
| |
| func runMakeProductConfig(ctx Context, config Config) { |
| // Variables to export into the environment of Kati/Ninja |
| exportEnvVars := []string{ |
| // So that we can use the correct TARGET_PRODUCT if it's been |
| // modified by a buildspec.mk |
| "TARGET_PRODUCT", |
| "TARGET_BUILD_VARIANT", |
| "TARGET_BUILD_APPS", |
| "TARGET_BUILD_UNBUNDLED", |
| |
| // Additional release config maps |
| "PRODUCT_RELEASE_CONFIG_MAPS", |
| |
| // compiler wrappers set up by make |
| "CC_WRAPPER", |
| "CXX_WRAPPER", |
| "RBE_WRAPPER", |
| "JAVAC_WRAPPER", |
| "R8_WRAPPER", |
| "D8_WRAPPER", |
| |
| // ccache settings |
| "CCACHE_COMPILERCHECK", |
| "CCACHE_SLOPPINESS", |
| "CCACHE_BASEDIR", |
| "CCACHE_CPP2", |
| |
| // LLVM compiler wrapper options |
| "TOOLCHAIN_RUSAGE_OUTPUT", |
| } |
| |
| allVars := append(append([]string{ |
| // Used to execute Kati and Ninja |
| "NINJA_GOALS", |
| "KATI_GOALS", |
| |
| // To find target/product/<DEVICE> |
| "TARGET_DEVICE", |
| |
| // So that later Kati runs can find BoardConfig.mk faster |
| "TARGET_DEVICE_DIR", |
| |
| // Whether --werror_overriding_commands will work |
| "BUILD_BROKEN_DUP_RULES", |
| |
| // Whether to enable the network during the build |
| "BUILD_BROKEN_USES_NETWORK", |
| |
| // Extra environment variables to be exported to ninja |
| "BUILD_BROKEN_NINJA_USES_ENV_VARS", |
| |
| // Used to restrict write access to source tree |
| "BUILD_BROKEN_SRC_DIR_IS_WRITABLE", |
| "BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST", |
| |
| // Not used, but useful to be in the soong.log |
| "BOARD_VNDK_VERSION", |
| "TARGET_BUILD_TYPE", |
| "HOST_ARCH", |
| "HOST_2ND_ARCH", |
| "HOST_CROSS_ARCH", |
| "HOST_CROSS_2ND_ARCH", |
| "HOST_BUILD_TYPE", |
| "PRODUCT_SOONG_NAMESPACES", |
| |
| "DEFAULT_WARNING_BUILD_MODULE_TYPES", |
| "DEFAULT_ERROR_BUILD_MODULE_TYPES", |
| "BUILD_BROKEN_PREBUILT_ELF_FILES", |
| "BUILD_BROKEN_TREBLE_SYSPROP_NEVERALLOW", |
| "BUILD_BROKEN_USES_BUILD_COPY_HEADERS", |
| "BUILD_BROKEN_USES_BUILD_EXECUTABLE", |
| "BUILD_BROKEN_USES_BUILD_FUZZ_TEST", |
| "BUILD_BROKEN_USES_BUILD_HEADER_LIBRARY", |
| "BUILD_BROKEN_USES_BUILD_HOST_EXECUTABLE", |
| "BUILD_BROKEN_USES_BUILD_HOST_JAVA_LIBRARY", |
| "BUILD_BROKEN_USES_BUILD_HOST_PREBUILT", |
| "BUILD_BROKEN_USES_BUILD_HOST_SHARED_LIBRARY", |
| "BUILD_BROKEN_USES_BUILD_HOST_STATIC_LIBRARY", |
| "BUILD_BROKEN_USES_BUILD_JAVA_LIBRARY", |
| "BUILD_BROKEN_USES_BUILD_MULTI_PREBUILT", |
| "BUILD_BROKEN_USES_BUILD_NATIVE_TEST", |
| "BUILD_BROKEN_USES_BUILD_NOTICE_FILE", |
| "BUILD_BROKEN_USES_BUILD_PACKAGE", |
| "BUILD_BROKEN_USES_BUILD_PHONY_PACKAGE", |
| "BUILD_BROKEN_USES_BUILD_PREBUILT", |
| "BUILD_BROKEN_USES_BUILD_RRO_PACKAGE", |
| "BUILD_BROKEN_USES_BUILD_SHARED_LIBRARY", |
| "BUILD_BROKEN_USES_BUILD_STATIC_JAVA_LIBRARY", |
| "BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY", |
| }, exportEnvVars...), BannerVars...) |
| |
| makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "") |
| if err != nil { |
| ctx.Fatalln("Error dumping make vars:", err) |
| } |
| |
| env := config.Environment() |
| // Print the banner like make does |
| if !env.IsEnvTrue("ANDROID_QUIET_BUILD") { |
| fmt.Fprintln(ctx.Writer, Banner(makeVars)) |
| } |
| |
| // Populate the environment |
| for _, name := range exportEnvVars { |
| if makeVars[name] == "" { |
| env.Unset(name) |
| } else { |
| env.Set(name, makeVars[name]) |
| } |
| } |
| |
| config.SetKatiArgs(strings.Fields(makeVars["KATI_GOALS"])) |
| config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"])) |
| config.SetTargetDevice(makeVars["TARGET_DEVICE"]) |
| config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"]) |
| config.sandboxConfig.SetSrcDirIsRO(makeVars["BUILD_BROKEN_SRC_DIR_IS_WRITABLE"] == "false") |
| config.sandboxConfig.SetSrcDirRWAllowlist(strings.Fields(makeVars["BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST"])) |
| |
| config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true") |
| config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true") |
| config.SetBuildBrokenNinjaUsesEnvVars(strings.Fields(makeVars["BUILD_BROKEN_NINJA_USES_ENV_VARS"])) |
| config.SetIncludeTags(strings.Fields(makeVars["PRODUCT_INCLUDE_TAGS"])) |
| config.SetSourceRootDirs(strings.Fields(makeVars["PRODUCT_SOURCE_ROOT_DIRS"])) |
| } |