• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 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 main
16
17import (
18	"flag"
19	"fmt"
20	"io/ioutil"
21	"os"
22	"path/filepath"
23	"strings"
24	"time"
25
26	"android/soong/android"
27	"android/soong/bp2build"
28	"android/soong/shared"
29	"android/soong/ui/metrics/bp2build_metrics_proto"
30
31	"github.com/google/blueprint/bootstrap"
32	"github.com/google/blueprint/deptools"
33	"github.com/google/blueprint/metrics"
34	androidProtobuf "google.golang.org/protobuf/android"
35)
36
37var (
38	topDir           string
39	outDir           string
40	soongOutDir      string
41	availableEnvFile string
42	usedEnvFile      string
43
44	runGoTests bool
45
46	globFile    string
47	globListDir string
48	delveListen string
49	delvePath   string
50
51	moduleGraphFile   string
52	moduleActionsFile string
53	docFile           string
54	bazelQueryViewDir string
55	bp2buildMarker    string
56
57	cmdlineArgs bootstrap.Args
58)
59
60func init() {
61	// Flags that make sense in every mode
62	flag.StringVar(&topDir, "top", "", "Top directory of the Android source tree")
63	flag.StringVar(&soongOutDir, "soong_out", "", "Soong output directory (usually $TOP/out/soong)")
64	flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
65	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
66	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
67	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
68	flag.StringVar(&outDir, "out", "", "the ninja builddir directory")
69	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
70
71	// Debug flags
72	flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
73	flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
74	flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
75	flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
76	flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
77	flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
78
79	// Flags representing various modes soong_build can run in
80	flag.StringVar(&moduleGraphFile, "module_graph_file", "", "JSON module graph file to output")
81	flag.StringVar(&moduleActionsFile, "module_actions_file", "", "JSON file to output inputs/outputs of actions of modules")
82	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
83	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
84	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
85	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
86	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
87
88	// Flags that probably shouldn't be flags of soong_build but we haven't found
89	// the time to remove them yet
90	flag.BoolVar(&runGoTests, "t", false, "build and run go tests during bootstrap")
91
92	// Disable deterministic randomization in the protobuf package, so incremental
93	// builds with unrelated Soong changes don't trigger large rebuilds (since we
94	// write out text protos in command lines, and command line changes trigger
95	// rebuilds).
96	androidProtobuf.DisableRand()
97}
98
99func newNameResolver(config android.Config) *android.NameResolver {
100	namespacePathsToExport := make(map[string]bool)
101
102	for _, namespaceName := range config.ExportedNamespaces() {
103		namespacePathsToExport[namespaceName] = true
104	}
105
106	namespacePathsToExport["."] = true // always export the root namespace
107
108	exportFilter := func(namespace *android.Namespace) bool {
109		return namespacePathsToExport[namespace.Path]
110	}
111
112	return android.NewNameResolver(exportFilter)
113}
114
115func newContext(configuration android.Config) *android.Context {
116	ctx := android.NewContext(configuration)
117	ctx.Register()
118	ctx.SetNameInterface(newNameResolver(configuration))
119	ctx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
120	ctx.AddIncludeTags(configuration.IncludeTags()...)
121	return ctx
122}
123
124func newConfig(availableEnv map[string]string) android.Config {
125	configuration, err := android.NewConfig(cmdlineArgs.ModuleListFile, runGoTests, outDir, soongOutDir, availableEnv)
126	if err != nil {
127		fmt.Fprintf(os.Stderr, "%s", err)
128		os.Exit(1)
129	}
130	return configuration
131}
132
133// Bazel-enabled mode. Soong runs in two passes.
134// First pass: Analyze the build tree, but only store all bazel commands
135// needed to correctly evaluate the tree in the second pass.
136// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
137// the incorrect results from the first pass, and file I/O is expensive.
138func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
139	firstCtx.EventHandler.Begin("mixed_build")
140	defer firstCtx.EventHandler.End("mixed_build")
141
142	firstCtx.EventHandler.Begin("prepare")
143	bootstrap.RunBlueprint(cmdlineArgs, bootstrap.StopBeforeWriteNinja, firstCtx.Context, configuration)
144	firstCtx.EventHandler.End("prepare")
145
146	firstCtx.EventHandler.Begin("bazel")
147	// Invoke bazel commands and save results for second pass.
148	if err := configuration.BazelContext.InvokeBazel(); err != nil {
149		fmt.Fprintf(os.Stderr, "%s", err)
150		os.Exit(1)
151	}
152	// Second pass: Full analysis, using the bazel command results. Output ninja file.
153	secondConfig, err := android.ConfigForAdditionalRun(configuration)
154	if err != nil {
155		fmt.Fprintf(os.Stderr, "%s", err)
156		os.Exit(1)
157	}
158	firstCtx.EventHandler.End("bazel")
159
160	secondCtx := newContext(secondConfig)
161	secondCtx.EventHandler = firstCtx.EventHandler
162	secondCtx.EventHandler.Begin("analyze")
163	ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, secondCtx.Context, secondConfig)
164	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
165	secondCtx.EventHandler.End("analyze")
166
167	globListFiles := writeBuildGlobsNinjaFile(secondCtx, configuration.SoongOutDir(), configuration)
168	ninjaDeps = append(ninjaDeps, globListFiles...)
169
170	writeDepFile(cmdlineArgs.OutFile, *secondCtx.EventHandler, ninjaDeps)
171}
172
173// Run the code-generation phase to convert BazelTargetModules to BUILD files.
174func runQueryView(queryviewDir, queryviewMarker string, configuration android.Config, ctx *android.Context) {
175	ctx.EventHandler.Begin("queryview")
176	defer ctx.EventHandler.End("queryview")
177	codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
178	absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir)
179	if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
180		fmt.Fprintf(os.Stderr, "%s", err)
181		os.Exit(1)
182	}
183
184	touch(shared.JoinPath(topDir, queryviewMarker))
185}
186
187func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler) {
188	metricsDir := configuration.Getenv("LOG_DIR")
189	if len(metricsDir) < 1 {
190		fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
191		os.Exit(1)
192	}
193	metricsFile := filepath.Join(metricsDir, "soong_build_metrics.pb")
194	err := android.WriteMetrics(configuration, eventHandler, metricsFile)
195	if err != nil {
196		fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
197		os.Exit(1)
198	}
199}
200
201func writeJsonModuleGraphAndActions(ctx *android.Context, graphPath string, actionsPath string) {
202	graphFile, graphErr := os.Create(shared.JoinPath(topDir, graphPath))
203	actionsFile, actionsErr := os.Create(shared.JoinPath(topDir, actionsPath))
204	if graphErr != nil || actionsErr != nil {
205		fmt.Fprintf(os.Stderr, "Graph err: %s, actions err: %s", graphErr, actionsErr)
206		os.Exit(1)
207	}
208
209	defer graphFile.Close()
210	defer actionsFile.Close()
211	ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
212}
213
214func writeBuildGlobsNinjaFile(ctx *android.Context, buildDir string, config interface{}) []string {
215	ctx.EventHandler.Begin("globs_ninja_file")
216	defer ctx.EventHandler.End("globs_ninja_file")
217
218	globDir := bootstrap.GlobDirectory(buildDir, globListDir)
219	bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
220		GlobLister: ctx.Globs,
221		GlobFile:   globFile,
222		GlobDir:    globDir,
223		SrcDir:     ctx.SrcDir(),
224	}, config)
225	return bootstrap.GlobFileListFiles(globDir)
226}
227
228func writeDepFile(outputFile string, eventHandler metrics.EventHandler, ninjaDeps []string) {
229	eventHandler.Begin("ninja_deps")
230	defer eventHandler.End("ninja_deps")
231	depFile := shared.JoinPath(topDir, outputFile+".d")
232	err := deptools.WriteDepFile(depFile, outputFile, ninjaDeps)
233	if err != nil {
234		fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", depFile, err)
235		os.Exit(1)
236	}
237}
238
239// doChosenActivity runs Soong for a specific activity, like bp2build, queryview
240// or the actual Soong build for the build.ninja file. Returns the top level
241// output file of the specific activity.
242func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
243	mixedModeBuild := configuration.BazelContext.BazelEnabled()
244	generateBazelWorkspace := bp2buildMarker != ""
245	generateQueryView := bazelQueryViewDir != ""
246	generateModuleGraphFile := moduleGraphFile != ""
247	generateDocFile := docFile != ""
248
249	if generateBazelWorkspace {
250		// Run the alternate pipeline of bp2build mutators and singleton to convert
251		// Blueprint to BUILD files before everything else.
252		runBp2Build(configuration, extraNinjaDeps)
253		return bp2buildMarker
254	}
255
256	blueprintArgs := cmdlineArgs
257
258	ctx := newContext(configuration)
259	if mixedModeBuild {
260		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
261	} else {
262		var stopBefore bootstrap.StopBefore
263		if generateModuleGraphFile {
264			stopBefore = bootstrap.StopBeforeWriteNinja
265		} else if generateQueryView {
266			stopBefore = bootstrap.StopBeforePrepareBuildActions
267		} else if generateDocFile {
268			stopBefore = bootstrap.StopBeforePrepareBuildActions
269		} else {
270			stopBefore = bootstrap.DoEverything
271		}
272
273		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, stopBefore, ctx.Context, configuration)
274		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
275
276		globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
277		ninjaDeps = append(ninjaDeps, globListFiles...)
278
279		// Convert the Soong module graph into Bazel BUILD files.
280		if generateQueryView {
281			queryviewMarkerFile := bazelQueryViewDir + ".marker"
282			runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx)
283			writeDepFile(queryviewMarkerFile, *ctx.EventHandler, ninjaDeps)
284			return queryviewMarkerFile
285		} else if generateModuleGraphFile {
286			writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
287			writeDepFile(moduleGraphFile, *ctx.EventHandler, ninjaDeps)
288			return moduleGraphFile
289		} else if generateDocFile {
290			// TODO: we could make writeDocs() return the list of documentation files
291			// written and add them to the .d file. Then soong_docs would be re-run
292			// whenever one is deleted.
293			if err := writeDocs(ctx, shared.JoinPath(topDir, docFile)); err != nil {
294				fmt.Fprintf(os.Stderr, "error building Soong documentation: %s\n", err)
295				os.Exit(1)
296			}
297			writeDepFile(docFile, *ctx.EventHandler, ninjaDeps)
298			return docFile
299		} else {
300			// The actual output (build.ninja) was written in the RunBlueprint() call
301			// above
302			writeDepFile(cmdlineArgs.OutFile, *ctx.EventHandler, ninjaDeps)
303		}
304	}
305
306	writeMetrics(configuration, *ctx.EventHandler)
307	return cmdlineArgs.OutFile
308}
309
310// soong_ui dumps the available environment variables to
311// soong.environment.available . Then soong_build itself is run with an empty
312// environment so that the only way environment variables can be accessed is
313// using Config, which tracks access to them.
314
315// At the end of the build, a file called soong.environment.used is written
316// containing the current value of all used environment variables. The next
317// time soong_ui is run, it checks whether any environment variables that was
318// used had changed and if so, it deletes soong.environment.used to cause a
319// rebuild.
320//
321// The dependency of build.ninja on soong.environment.used is declared in
322// build.ninja.d
323func parseAvailableEnv() map[string]string {
324	if availableEnvFile == "" {
325		fmt.Fprintf(os.Stderr, "--available_env not set\n")
326		os.Exit(1)
327	}
328
329	result, err := shared.EnvFromFile(shared.JoinPath(topDir, availableEnvFile))
330	if err != nil {
331		fmt.Fprintf(os.Stderr, "error reading available environment file '%s': %s\n", availableEnvFile, err)
332		os.Exit(1)
333	}
334
335	return result
336}
337
338func main() {
339	flag.Parse()
340
341	shared.ReexecWithDelveMaybe(delveListen, delvePath)
342	android.InitSandbox(topDir)
343
344	availableEnv := parseAvailableEnv()
345
346	configuration := newConfig(availableEnv)
347	extraNinjaDeps := []string{
348		configuration.ProductVariablesFileName,
349		usedEnvFile,
350	}
351
352	if configuration.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
353		configuration.SetAllowMissingDependencies()
354	}
355
356	if shared.IsDebugging() {
357		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
358		// enabled even if it completed successfully.
359		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
360	}
361
362	finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
363
364	writeUsedEnvironmentFile(configuration, finalOutputFile)
365}
366
367func writeUsedEnvironmentFile(configuration android.Config, finalOutputFile string) {
368	if usedEnvFile == "" {
369		return
370	}
371
372	path := shared.JoinPath(topDir, usedEnvFile)
373	data, err := shared.EnvFileContents(configuration.EnvDeps())
374	if err != nil {
375		fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err)
376		os.Exit(1)
377	}
378
379	err = ioutil.WriteFile(path, data, 0666)
380	if err != nil {
381		fmt.Fprintf(os.Stderr, "error writing used environment file '%s': %s\n", usedEnvFile, err)
382		os.Exit(1)
383	}
384
385	// Touch the output file so that it's not older than the file we just
386	// wrote. We can't write the environment file earlier because one an access
387	// new environment variables while writing it.
388	touch(shared.JoinPath(topDir, finalOutputFile))
389}
390
391func touch(path string) {
392	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
393	if err != nil {
394		fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err)
395		os.Exit(1)
396	}
397
398	err = f.Close()
399	if err != nil {
400		fmt.Fprintf(os.Stderr, "Error touching '%s': %s\n", path, err)
401		os.Exit(1)
402	}
403
404	currentTime := time.Now().Local()
405	err = os.Chtimes(path, currentTime, currentTime)
406	if err != nil {
407		fmt.Fprintf(os.Stderr, "error touching '%s': %s\n", path, err)
408		os.Exit(1)
409	}
410}
411
412// Find BUILD files in the srcDir which...
413//
414// - are not on the allow list (android/bazel.go#ShouldKeepExistingBuildFileForDir())
415//
416// - won't be overwritten by corresponding bp2build generated files
417//
418// And return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
419func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string, verbose bool) []string {
420	paths := make([]string, 0)
421
422	for _, srcDirBazelFileRelativePath := range srcDirBazelFiles {
423		srcDirBazelFileFullPath := shared.JoinPath(topDir, srcDirBazelFileRelativePath)
424		fileInfo, err := os.Stat(srcDirBazelFileFullPath)
425		if err != nil {
426			// Warn about error, but continue trying to check files
427			fmt.Fprintf(os.Stderr, "WARNING: Error accessing path '%s', err: %s\n", srcDirBazelFileFullPath, err)
428			continue
429		}
430		if fileInfo.IsDir() {
431			// Don't ignore entire directories
432			continue
433		}
434		if !(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
435			// Don't ignore this file - it is not a build file
436			continue
437		}
438		srcDirBazelFileDir := filepath.Dir(srcDirBazelFileRelativePath)
439		if android.ShouldKeepExistingBuildFileForDir(srcDirBazelFileDir) {
440			// Don't ignore this existing build file
441			continue
442		}
443		correspondingBp2BuildFile := shared.JoinPath(topDir, generatedRoot, srcDirBazelFileRelativePath)
444		if _, err := os.Stat(correspondingBp2BuildFile); err == nil {
445			// If bp2build generated an alternate BUILD file, don't exclude this workspace path
446			// BUILD file clash resolution happens later in the symlink forest creation
447			continue
448		}
449		if verbose {
450			fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
451		}
452		paths = append(paths, srcDirBazelFileRelativePath)
453	}
454
455	return paths
456}
457
458// Returns temporary symlink forest excludes necessary for bazel build //external/... (and bazel build //frameworks/...) to work
459func getTemporaryExcludes() []string {
460	excludes := make([]string, 0)
461
462	// FIXME: 'autotest_lib' is a symlink back to external/autotest, and this causes an infinite symlink expansion error for Bazel
463	excludes = append(excludes, "external/autotest/venv/autotest_lib")
464
465	// FIXME: The external/google-fruit/extras/bazel_root/third_party/fruit dir is poison
466	// It contains several symlinks back to real source dirs, and those source dirs contain BUILD files we want to ignore
467	excludes = append(excludes, "external/google-fruit/extras/bazel_root/third_party/fruit")
468
469	// FIXME: 'frameworks/compile/slang' has a filegroup error due to an escaping issue
470	excludes = append(excludes, "frameworks/compile/slang")
471
472	return excludes
473}
474
475// Read the bazel.list file that the Soong Finder already dumped earlier (hopefully)
476// It contains the locations of BUILD files, BUILD.bazel files, etc. in the source dir
477func getExistingBazelRelatedFiles(topDir string) ([]string, error) {
478	bazelFinderFile := filepath.Join(filepath.Dir(cmdlineArgs.ModuleListFile), "bazel.list")
479	if !filepath.IsAbs(bazelFinderFile) {
480		// Assume this was a relative path under topDir
481		bazelFinderFile = filepath.Join(topDir, bazelFinderFile)
482	}
483	data, err := ioutil.ReadFile(bazelFinderFile)
484	if err != nil {
485		return nil, err
486	}
487	files := strings.Split(strings.TrimSpace(string(data)), "\n")
488	return files, nil
489}
490
491// Run Soong in the bp2build mode. This creates a standalone context that registers
492// an alternate pipeline of mutators and singletons specifically for generating
493// Bazel BUILD files instead of Ninja files.
494func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
495	eventHandler := metrics.EventHandler{}
496	eventHandler.Begin("bp2build")
497
498	// Register an alternate set of singletons and mutators for bazel
499	// conversion for Bazel conversion.
500	bp2buildCtx := android.NewContext(configuration)
501
502	// Soong internals like LoadHooks behave differently when running as
503	// bp2build. This is the bit to differentiate between Soong-as-Soong and
504	// Soong-as-bp2build.
505	bp2buildCtx.SetRunningAsBp2build()
506
507	// Propagate "allow misssing dependencies" bit. This is normally set in
508	// newContext(), but we create bp2buildCtx without calling that method.
509	bp2buildCtx.SetAllowMissingDependencies(configuration.AllowMissingDependencies())
510	bp2buildCtx.SetNameInterface(newNameResolver(configuration))
511	bp2buildCtx.RegisterForBazelConversion()
512
513	// The bp2build process is a purely functional process that only depends on
514	// Android.bp files. It must not depend on the values of per-build product
515	// configurations or variables, since those will generate different BUILD
516	// files based on how the user has configured their tree.
517	bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
518	modulePaths, err := bp2buildCtx.ListModulePaths(".")
519	if err != nil {
520		panic(err)
521	}
522
523	extraNinjaDeps = append(extraNinjaDeps, modulePaths...)
524
525	// Run the loading and analysis pipeline to prepare the graph of regular
526	// Modules parsed from Android.bp files, and the BazelTargetModules mapped
527	// from the regular Modules.
528	blueprintArgs := cmdlineArgs
529	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
530	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
531
532	globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration)
533	ninjaDeps = append(ninjaDeps, globListFiles...)
534
535	// Run the code-generation phase to convert BazelTargetModules to BUILD files
536	// and print conversion metrics to the user.
537	codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
538	metrics := bp2build.Codegen(codegenContext)
539
540	generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
541	workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
542
543	excludes := []string{
544		"bazel-bin",
545		"bazel-genfiles",
546		"bazel-out",
547		"bazel-testlogs",
548		"bazel-" + filepath.Base(topDir),
549	}
550
551	if outDir[0] != '/' {
552		excludes = append(excludes, outDir)
553	}
554
555	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
556	if err != nil {
557		fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
558		os.Exit(1)
559	}
560
561	pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles, configuration.IsEnvTrue("BP2BUILD_VERBOSE"))
562	excludes = append(excludes, pathsToIgnoredBuildFiles...)
563
564	excludes = append(excludes, getTemporaryExcludes()...)
565
566	symlinkForestDeps := bp2build.PlantSymlinkForest(
567		topDir, workspaceRoot, generatedRoot, ".", excludes)
568
569	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
570	ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
571
572	writeDepFile(bp2buildMarker, eventHandler, ninjaDeps)
573
574	// Create an empty bp2build marker file.
575	touch(shared.JoinPath(topDir, bp2buildMarker))
576
577	eventHandler.End("bp2build")
578
579	// Only report metrics when in bp2build mode. The metrics aren't relevant
580	// for queryview, since that's a total repo-wide conversion and there's a
581	// 1:1 mapping for each module.
582	metrics.Print()
583	writeBp2BuildMetrics(&metrics, configuration, eventHandler)
584}
585
586// Write Bp2Build metrics into $LOG_DIR
587func writeBp2BuildMetrics(codegenMetrics *bp2build.CodegenMetrics,
588	configuration android.Config, eventHandler metrics.EventHandler) {
589	for _, event := range eventHandler.CompletedEvents() {
590		codegenMetrics.Events = append(codegenMetrics.Events,
591			&bp2build_metrics_proto.Event{
592				Name:      event.Id,
593				StartTime: uint64(event.Start.UnixNano()),
594				RealTime:  event.RuntimeNanoseconds(),
595			})
596	}
597	metricsDir := configuration.Getenv("LOG_DIR")
598	if len(metricsDir) < 1 {
599		fmt.Fprintf(os.Stderr, "\nMissing required env var for generating bp2build metrics: LOG_DIR\n")
600		os.Exit(1)
601	}
602	codegenMetrics.Write(metricsDir)
603}
604