• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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