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
46	// If the suffix is too long, replace it with a md5 hash and write a
47	// file that contains the original suffix.
48	if len(katiSuffix) > 64 {
49		shortSuffix := "-" + fmt.Sprintf("%x", md5.Sum([]byte(katiSuffix)))
50		config.SetKatiSuffix(shortSuffix)
51
52		ctx.Verbosef("Kati ninja suffix too long: %q", katiSuffix)
53		ctx.Verbosef("Replacing with: %q", shortSuffix)
54
55		if err := ioutil.WriteFile(strings.TrimSuffix(config.KatiBuildNinjaFile(), "ninja")+"suf", []byte(katiSuffix), 0777); err != nil {
56			ctx.Println("Error writing suffix file:", err)
57		}
58	} else {
59		config.SetKatiSuffix(katiSuffix)
60	}
61}
62
63func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) {
64	executable := config.PrebuiltBuildTool("ckati")
65	args = append([]string{
66		"--ninja",
67		"--ninja_dir=" + config.OutDir(),
68		"--ninja_suffix=" + config.KatiSuffix() + extraSuffix,
69		"--no_ninja_prelude",
70		"--use_ninja_phony_output",
71		"--regen",
72		"--ignore_optional_include=" + filepath.Join(config.OutDir(), "%.P"),
73		"--detect_android_echo",
74		"--color_warnings",
75		"--gen_all_targets",
76		"--use_find_emulator",
77		"--werror_find_emulator",
78		"--no_builtin_rules",
79		"--werror_suffix_rules",
80		"--warn_real_to_phony",
81		"--warn_phony_looks_real",
82		"--werror_real_to_phony",
83		"--werror_phony_looks_real",
84		"--werror_writable",
85		"--top_level_phony",
86		"--kati_stats",
87	}, args...)
88
89	if config.Environment().IsEnvTrue("EMPTY_NINJA_FILE") {
90		args = append(args, "--empty_ninja_file")
91	}
92
93	if config.UseRemoteBuild() {
94		args = append(args, "--default_pool=local_pool")
95	}
96
97	cmd := Command(ctx, config, "ckati", executable, args...)
98	cmd.Sandbox = katiSandbox
99	pipe, err := cmd.StdoutPipe()
100	if err != nil {
101		ctx.Fatalln("Error getting output pipe for ckati:", err)
102	}
103	cmd.Stderr = cmd.Stdout
104
105	envFunc(cmd.Environment)
106
107	if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
108		username := "unknown"
109		if u, err := user.Current(); err == nil {
110			username = u.Username
111		} else {
112			ctx.Println("Failed to get current user:", err)
113		}
114		cmd.Environment.Set("BUILD_USERNAME", username)
115	}
116
117	if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok {
118		hostname, err := os.Hostname()
119		if err != nil {
120			ctx.Println("Failed to read hostname:", err)
121			hostname = "unknown"
122		}
123		cmd.Environment.Set("BUILD_HOSTNAME", hostname)
124	}
125
126	cmd.StartOrFatal()
127	status.KatiReader(ctx.Status.StartTool(), pipe)
128	cmd.WaitOrFatal()
129}
130
131func runKatiBuild(ctx Context, config Config) {
132	ctx.BeginTrace(metrics.RunKati, "kati build")
133	defer ctx.EndTrace()
134
135	args := []string{
136		"--writable", config.OutDir() + "/",
137		"-f", "build/make/core/main.mk",
138	}
139
140	// PDK builds still uses a few implicit rules
141	if !config.IsPdkBuild() {
142		args = append(args, "--werror_implicit_rules")
143	}
144
145	if !config.BuildBrokenDupRules() {
146		args = append(args, "--werror_overriding_commands")
147	}
148
149	args = append(args, config.KatiArgs()...)
150
151	args = append(args,
152		"SOONG_MAKEVARS_MK="+config.SoongMakeVarsMk(),
153		"SOONG_ANDROID_MK="+config.SoongAndroidMk(),
154		"TARGET_DEVICE_DIR="+config.TargetDeviceDir(),
155		"KATI_PACKAGE_MK_DIR="+config.KatiPackageMkDir())
156
157	runKati(ctx, config, katiBuildSuffix, args, func(env *Environment) {})
158
159	distGzipFile(ctx, config, config.KatiBuildNinjaFile())
160
161	cleanCopyHeaders(ctx, config)
162	cleanOldInstalledFiles(ctx, config)
163}
164
165func cleanCopyHeaders(ctx Context, config Config) {
166	ctx.BeginTrace("clean", "clean copy headers")
167	defer ctx.EndTrace()
168
169	data, err := ioutil.ReadFile(filepath.Join(config.ProductOut(), ".copied_headers_list"))
170	if err != nil {
171		if os.IsNotExist(err) {
172			return
173		}
174		ctx.Fatalf("Failed to read copied headers list: %v", err)
175	}
176
177	headers := strings.Fields(string(data))
178	if len(headers) < 1 {
179		ctx.Fatal("Failed to parse copied headers list: %q", string(data))
180	}
181	headerDir := headers[0]
182	headers = headers[1:]
183
184	filepath.Walk(headerDir,
185		func(path string, info os.FileInfo, err error) error {
186			if err != nil {
187				return nil
188			}
189			if info.IsDir() {
190				return nil
191			}
192			if !inList(path, headers) {
193				ctx.Printf("Removing obsolete header %q", path)
194				if err := os.Remove(path); err != nil {
195					ctx.Fatalf("Failed to remove obsolete header %q: %v", path, err)
196				}
197			}
198			return nil
199		})
200}
201
202func cleanOldInstalledFiles(ctx Context, config Config) {
203	ctx.BeginTrace("clean", "clean old installed files")
204	defer ctx.EndTrace()
205
206	// We shouldn't be removing files from one side of the two-step asan builds
207	var suffix string
208	if v, ok := config.Environment().Get("SANITIZE_TARGET"); ok {
209		if sanitize := strings.Fields(v); inList("address", sanitize) {
210			suffix = "_asan"
211		}
212	}
213
214	cleanOldFiles(ctx, config.ProductOut(), ".installable_files"+suffix)
215
216	cleanOldFiles(ctx, config.HostOut(), ".installable_test_files")
217}
218
219func runKatiPackage(ctx Context, config Config) {
220	ctx.BeginTrace(metrics.RunKati, "kati package")
221	defer ctx.EndTrace()
222
223	args := []string{
224		"--writable", config.DistDir() + "/",
225		"--werror_implicit_rules",
226		"--werror_overriding_commands",
227		"-f", "build/make/packaging/main.mk",
228		"KATI_PACKAGE_MK_DIR=" + config.KatiPackageMkDir(),
229	}
230
231	runKati(ctx, config, katiPackageSuffix, args, func(env *Environment) {
232		env.Allow([]string{
233			// Some generic basics
234			"LANG",
235			"LC_MESSAGES",
236			"PATH",
237			"PWD",
238			"TMPDIR",
239
240			// Tool configs
241			"ASAN_SYMBOLIZER_PATH",
242			"JAVA_HOME",
243			"PYTHONDONTWRITEBYTECODE",
244
245			// Build configuration
246			"ANDROID_BUILD_SHELL",
247			"DIST_DIR",
248			"OUT_DIR",
249		}...)
250
251		if config.Dist() {
252			env.Set("DIST", "true")
253			env.Set("DIST_DIR", config.DistDir())
254		}
255	})
256
257	distGzipFile(ctx, config, config.KatiPackageNinjaFile())
258}
259
260func runKatiCleanSpec(ctx Context, config Config) {
261	ctx.BeginTrace(metrics.RunKati, "kati cleanspec")
262	defer ctx.EndTrace()
263
264	runKati(ctx, config, katiCleanspecSuffix, []string{
265		"--werror_implicit_rules",
266		"--werror_overriding_commands",
267		"-f", "build/make/core/cleanbuild.mk",
268		"SOONG_MAKEVARS_MK=" + config.SoongMakeVarsMk(),
269		"TARGET_DEVICE_DIR=" + config.TargetDeviceDir(),
270	}, func(env *Environment) {})
271}
272