• 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	"fmt"
19	"os"
20	"path/filepath"
21	"strconv"
22	"strings"
23
24	"android/soong/bazel"
25	"android/soong/ui/metrics"
26	"android/soong/ui/status"
27
28	"android/soong/shared"
29
30	"github.com/google/blueprint"
31	"github.com/google/blueprint/bootstrap"
32	"github.com/google/blueprint/microfactory"
33)
34
35const (
36	availableEnvFile = "soong.environment.available"
37	usedEnvFile      = "soong.environment.used"
38
39	soongBuildTag        = "build"
40	bp2buildFilesTag     = "bp2build_files"
41	bp2buildWorkspaceTag = "bp2build_workspace"
42	jsonModuleGraphTag   = "modulegraph"
43	queryviewTag         = "queryview"
44	apiBp2buildTag       = "api_bp2build"
45	soongDocsTag         = "soong_docs"
46
47	// bootstrapEpoch is used to determine if an incremental build is incompatible with the current
48	// version of bootstrap and needs cleaning before continuing the build.  Increment this for
49	// incompatible changes, for example when moving the location of the bpglob binary that is
50	// executed during bootstrap before the primary builder has had a chance to update the path.
51	bootstrapEpoch = 1
52)
53
54func writeEnvironmentFile(_ Context, envFile string, envDeps map[string]string) error {
55	data, err := shared.EnvFileContents(envDeps)
56	if err != nil {
57		return err
58	}
59
60	return os.WriteFile(envFile, data, 0644)
61}
62
63// This uses Android.bp files and various tools to generate <builddir>/build.ninja.
64//
65// However, the execution of <builddir>/build.ninja happens later in
66// build/soong/ui/build/build.go#Build()
67//
68// We want to rely on as few prebuilts as possible, so we need to bootstrap
69// Soong. The process is as follows:
70//
71// 1. We use "Microfactory", a simple tool to compile Go code, to build
72//    first itself, then soong_ui from soong_ui.bash. This binary contains
73//    parts of soong_build that are needed to build itself.
74// 2. This simplified version of soong_build then reads the Blueprint files
75//    that describe itself and emits .bootstrap/build.ninja that describes
76//    how to build its full version and use that to produce the final Ninja
77//    file Soong emits.
78// 3. soong_ui executes .bootstrap/build.ninja
79//
80// (After this, Kati is executed to parse the Makefiles, but that's not part of
81// bootstrapping Soong)
82
83// A tiny struct used to tell Blueprint that it's in bootstrap mode. It would
84// probably be nicer to use a flag in bootstrap.Args instead.
85type BlueprintConfig struct {
86	toolDir                   string
87	soongOutDir               string
88	outDir                    string
89	runGoTests                bool
90	debugCompilation          bool
91	subninjas                 []string
92	primaryBuilderInvocations []bootstrap.PrimaryBuilderInvocation
93}
94
95func (c BlueprintConfig) HostToolDir() string {
96	return c.toolDir
97}
98
99func (c BlueprintConfig) SoongOutDir() string {
100	return c.soongOutDir
101}
102
103func (c BlueprintConfig) OutDir() string {
104	return c.outDir
105}
106
107func (c BlueprintConfig) RunGoTests() bool {
108	return c.runGoTests
109}
110
111func (c BlueprintConfig) DebugCompilation() bool {
112	return c.debugCompilation
113}
114
115func (c BlueprintConfig) Subninjas() []string {
116	return c.subninjas
117}
118
119func (c BlueprintConfig) PrimaryBuilderInvocations() []bootstrap.PrimaryBuilderInvocation {
120	return c.primaryBuilderInvocations
121}
122
123func environmentArgs(config Config, tag string) []string {
124	return []string{
125		"--available_env", shared.JoinPath(config.SoongOutDir(), availableEnvFile),
126		"--used_env", config.UsedEnvFile(tag),
127	}
128}
129
130func writeEmptyFile(ctx Context, path string) {
131	err := os.MkdirAll(filepath.Dir(path), 0777)
132	if err != nil {
133		ctx.Fatalf("Failed to create parent directories of empty file '%s': %s", path, err)
134	}
135
136	if exists, err := fileExists(path); err != nil {
137		ctx.Fatalf("Failed to check if file '%s' exists: %s", path, err)
138	} else if !exists {
139		err = os.WriteFile(path, nil, 0666)
140		if err != nil {
141			ctx.Fatalf("Failed to create empty file '%s': %s", path, err)
142		}
143	}
144}
145
146func fileExists(path string) (bool, error) {
147	if _, err := os.Stat(path); os.IsNotExist(err) {
148		return false, nil
149	} else if err != nil {
150		return false, err
151	}
152	return true, nil
153}
154
155type PrimaryBuilderFactory struct {
156	name         string
157	description  string
158	config       Config
159	output       string
160	specificArgs []string
161	debugPort    string
162}
163
164func (pb PrimaryBuilderFactory) primaryBuilderInvocation() bootstrap.PrimaryBuilderInvocation {
165	commonArgs := make([]string, 0, 0)
166
167	if !pb.config.skipSoongTests {
168		commonArgs = append(commonArgs, "-t")
169	}
170
171	if pb.config.multitreeBuild {
172		commonArgs = append(commonArgs, "--multitree-build")
173	}
174	if pb.config.buildFromTextStub {
175		commonArgs = append(commonArgs, "--build-from-text-stub")
176	}
177
178	commonArgs = append(commonArgs, "-l", filepath.Join(pb.config.FileListDir(), "Android.bp.list"))
179	invocationEnv := make(map[string]string)
180	if pb.debugPort != "" {
181		//debug mode
182		commonArgs = append(commonArgs, "--delve_listen", pb.debugPort,
183			"--delve_path", shared.ResolveDelveBinary())
184		// GODEBUG=asyncpreemptoff=1 disables the preemption of goroutines. This
185		// is useful because the preemption happens by sending SIGURG to the OS
186		// thread hosting the goroutine in question and each signal results in
187		// work that needs to be done by Delve; it uses ptrace to debug the Go
188		// process and the tracer process must deal with every signal (it is not
189		// possible to selectively ignore SIGURG). This makes debugging slower,
190		// sometimes by an order of magnitude depending on luck.
191		// The original reason for adding async preemption to Go is here:
192		// https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md
193		invocationEnv["GODEBUG"] = "asyncpreemptoff=1"
194	}
195
196	var allArgs []string
197	allArgs = append(allArgs, pb.specificArgs...)
198	allArgs = append(allArgs,
199		"--globListDir", pb.name,
200		"--globFile", pb.config.NamedGlobFile(pb.name))
201
202	allArgs = append(allArgs, commonArgs...)
203	allArgs = append(allArgs, environmentArgs(pb.config, pb.name)...)
204	if profileCpu := os.Getenv("SOONG_PROFILE_CPU"); profileCpu != "" {
205		allArgs = append(allArgs, "--cpuprofile", profileCpu+"."+pb.name)
206	}
207	if profileMem := os.Getenv("SOONG_PROFILE_MEM"); profileMem != "" {
208		allArgs = append(allArgs, "--memprofile", profileMem+"."+pb.name)
209	}
210	allArgs = append(allArgs, "Android.bp")
211
212	return bootstrap.PrimaryBuilderInvocation{
213		Inputs:      []string{"Android.bp"},
214		Outputs:     []string{pb.output},
215		Args:        allArgs,
216		Description: pb.description,
217		// NB: Changing the value of this environment variable will not result in a
218		// rebuild. The bootstrap Ninja file will change, but apparently Ninja does
219		// not consider changing the pool specified in a statement a change that's
220		// worth rebuilding for.
221		Console: os.Getenv("SOONG_UNBUFFERED_OUTPUT") == "1",
222		Env:     invocationEnv,
223	}
224}
225
226// bootstrapEpochCleanup deletes files used by bootstrap during incremental builds across
227// incompatible changes.  Incompatible changes are marked by incrementing the bootstrapEpoch
228// constant.  A tree is considered out of date for the current epoch of the
229// .soong.bootstrap.epoch.<epoch> file doesn't exist.
230func bootstrapEpochCleanup(ctx Context, config Config) {
231	epochFile := fmt.Sprintf(".soong.bootstrap.epoch.%d", bootstrapEpoch)
232	epochPath := filepath.Join(config.SoongOutDir(), epochFile)
233	if exists, err := fileExists(epochPath); err != nil {
234		ctx.Fatalf("failed to check if bootstrap epoch file %q exists: %q", epochPath, err)
235	} else if !exists {
236		// The tree is out of date for the current epoch, delete files used by bootstrap
237		// and force the primary builder to rerun.
238		os.Remove(filepath.Join(config.SoongOutDir(), "build.ninja"))
239		for _, globFile := range bootstrapGlobFileList(config) {
240			os.Remove(globFile)
241		}
242
243		// Mark the tree as up to date with the current epoch by writing the epoch marker file.
244		writeEmptyFile(ctx, epochPath)
245	}
246}
247
248func bootstrapGlobFileList(config Config) []string {
249	return []string{
250		config.NamedGlobFile(soongBuildTag),
251		config.NamedGlobFile(bp2buildFilesTag),
252		config.NamedGlobFile(jsonModuleGraphTag),
253		config.NamedGlobFile(queryviewTag),
254		config.NamedGlobFile(apiBp2buildTag),
255		config.NamedGlobFile(soongDocsTag),
256	}
257}
258
259func bootstrapBlueprint(ctx Context, config Config) {
260	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
261	defer ctx.EndTrace()
262
263	// Clean up some files for incremental builds across incompatible changes.
264	bootstrapEpochCleanup(ctx, config)
265
266	mainSoongBuildExtraArgs := []string{"-o", config.SoongNinjaFile()}
267	if config.EmptyNinjaFile() {
268		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
269	}
270	if config.bazelProdMode {
271		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode")
272	}
273	if config.bazelDevMode {
274		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-dev")
275	}
276	if config.bazelStagingMode {
277		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-mode-staging")
278	}
279	if config.IsPersistentBazelEnabled() {
280		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--use-bazel-proxy")
281	}
282	if len(config.bazelForceEnabledModules) > 0 {
283		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--bazel-force-enabled-modules="+config.bazelForceEnabledModules)
284	}
285	if config.MultitreeBuild() {
286		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--multitree-build")
287	}
288	if config.buildFromTextStub {
289		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--build-from-text-stub")
290	}
291
292	queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
293	// The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)
294	// The final workspace will be generated in out/soong/api_bp2build
295	apiBp2buildDir := filepath.Join(config.SoongOutDir(), ".api_bp2build")
296
297	pbfs := []PrimaryBuilderFactory{
298		{
299			name:         soongBuildTag,
300			description:  fmt.Sprintf("analyzing Android.bp files and generating ninja file at %s", config.SoongNinjaFile()),
301			config:       config,
302			output:       config.SoongNinjaFile(),
303			specificArgs: mainSoongBuildExtraArgs,
304		},
305		{
306			name:         bp2buildFilesTag,
307			description:  fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
308			config:       config,
309			output:       config.Bp2BuildFilesMarkerFile(),
310			specificArgs: []string{"--bp2build_marker", config.Bp2BuildFilesMarkerFile()},
311		},
312		{
313			name:         bp2buildWorkspaceTag,
314			description:  "Creating Bazel symlink forest",
315			config:       config,
316			output:       config.Bp2BuildWorkspaceMarkerFile(),
317			specificArgs: []string{"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile()},
318		},
319		{
320			name:        jsonModuleGraphTag,
321			description: fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()),
322			config:      config,
323			output:      config.ModuleGraphFile(),
324			specificArgs: []string{
325				"--module_graph_file", config.ModuleGraphFile(),
326				"--module_actions_file", config.ModuleActionsFile(),
327			},
328		},
329		{
330			name:         queryviewTag,
331			description:  fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
332			config:       config,
333			output:       config.QueryviewMarkerFile(),
334			specificArgs: []string{"--bazel_queryview_dir", queryviewDir},
335		},
336		{
337			name:         apiBp2buildTag,
338			description:  fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
339			config:       config,
340			output:       config.ApiBp2buildMarkerFile(),
341			specificArgs: []string{"--bazel_api_bp2build_dir", apiBp2buildDir},
342		},
343		{
344			name:         soongDocsTag,
345			description:  fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
346			config:       config,
347			output:       config.SoongDocsHtml(),
348			specificArgs: []string{"--soong_docs", config.SoongDocsHtml()},
349		},
350	}
351
352	// Figure out which invocations will be run under the debugger:
353	//   * SOONG_DELVE if set specifies listening port
354	//   * SOONG_DELVE_STEPS if set specifies specific invocations to be debugged, otherwise all are
355	debuggedInvocations := make(map[string]bool)
356	delvePort := os.Getenv("SOONG_DELVE")
357	if delvePort != "" {
358		if steps := os.Getenv("SOONG_DELVE_STEPS"); steps != "" {
359			var validSteps []string
360			for _, pbf := range pbfs {
361				debuggedInvocations[pbf.name] = false
362				validSteps = append(validSteps, pbf.name)
363
364			}
365			for _, step := range strings.Split(steps, ",") {
366				if _, ok := debuggedInvocations[step]; ok {
367					debuggedInvocations[step] = true
368				} else {
369					ctx.Fatalf("SOONG_DELVE_STEPS contains unknown soong_build step %s\n"+
370						"Valid steps are %v", step, validSteps)
371				}
372			}
373		} else {
374			//  SOONG_DELVE_STEPS is not set, run all steps in the debugger
375			for _, pbf := range pbfs {
376				debuggedInvocations[pbf.name] = true
377			}
378		}
379	}
380
381	var invocations []bootstrap.PrimaryBuilderInvocation
382	for _, pbf := range pbfs {
383		if debuggedInvocations[pbf.name] {
384			pbf.debugPort = delvePort
385		}
386		pbi := pbf.primaryBuilderInvocation()
387		// Some invocations require adjustment:
388		switch pbf.name {
389		case soongBuildTag:
390			if config.BazelBuildEnabled() {
391				// Mixed builds call Bazel from soong_build and they therefore need the
392				// Bazel workspace to be available. Make that so by adding a dependency on
393				// the bp2build marker file to the action that invokes soong_build .
394				pbi.OrderOnlyInputs = append(pbi.OrderOnlyInputs, config.Bp2BuildWorkspaceMarkerFile())
395			}
396		case bp2buildWorkspaceTag:
397			pbi.Inputs = append(pbi.Inputs,
398				config.Bp2BuildFilesMarkerFile(),
399				filepath.Join(config.FileListDir(), "bazel.list"))
400		case bp2buildFilesTag:
401			pbi.Inputs = append(pbi.Inputs, filepath.Join(config.FileListDir(), "METADATA.list"))
402		}
403		invocations = append(invocations, pbi)
404	}
405
406	// The glob .ninja files are subninja'd. However, they are generated during
407	// the build itself so we write an empty file if the file does not exist yet
408	// so that the subninja doesn't fail on clean builds
409	for _, globFile := range bootstrapGlobFileList(config) {
410		writeEmptyFile(ctx, globFile)
411	}
412
413	blueprintArgs := bootstrap.Args{
414		ModuleListFile: filepath.Join(config.FileListDir(), "Android.bp.list"),
415		OutFile:        shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja"),
416		EmptyNinjaFile: false,
417	}
418
419	blueprintCtx := blueprint.NewContext()
420	blueprintCtx.AddIncludeTags(config.GetIncludeTags()...)
421	blueprintCtx.AddSourceRootDirs(config.GetSourceRootDirs()...)
422	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
423	blueprintConfig := BlueprintConfig{
424		soongOutDir: config.SoongOutDir(),
425		toolDir:     config.HostToolDir(),
426		outDir:      config.OutDir(),
427		runGoTests:  !config.skipSoongTests,
428		// If we want to debug soong_build, we need to compile it for debugging
429		debugCompilation:          delvePort != "",
430		subninjas:                 bootstrapGlobFileList(config),
431		primaryBuilderInvocations: invocations,
432	}
433
434	// since `bootstrap.ninja` is regenerated unconditionally, we ignore the deps, i.e. little
435	// reason to write a `bootstrap.ninja.d` file
436	_ = bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
437}
438
439func checkEnvironmentFile(currentEnv *Environment, envFile string) {
440	getenv := func(k string) string {
441		v, _ := currentEnv.Get(k)
442		return v
443	}
444
445	if stale, _ := shared.StaleEnvFile(envFile, getenv); stale {
446		os.Remove(envFile)
447	}
448}
449
450func runSoong(ctx Context, config Config) {
451	ctx.BeginTrace(metrics.RunSoong, "soong")
452	defer ctx.EndTrace()
453
454	// We have two environment files: .available is the one with every variable,
455	// .used with the ones that were actually used. The latter is used to
456	// determine whether Soong needs to be re-run since why re-run it if only
457	// unused variables were changed?
458	envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
459
460	// This is done unconditionally, but does not take a measurable amount of time
461	bootstrapBlueprint(ctx, config)
462
463	soongBuildEnv := config.Environment().Copy()
464	soongBuildEnv.Set("TOP", os.Getenv("TOP"))
465	// For Bazel mixed builds.
466	soongBuildEnv.Set("BAZEL_PATH", "./build/bazel/bin/bazel")
467	// Bazel's HOME var is set to an output subdirectory which doesn't exist. This
468	// prevents Bazel from file I/O in the actual user HOME directory.
469	soongBuildEnv.Set("BAZEL_HOME", absPath(ctx, filepath.Join(config.BazelOutDir(), "bazelhome")))
470	soongBuildEnv.Set("BAZEL_OUTPUT_BASE", config.bazelOutputBase())
471	soongBuildEnv.Set("BAZEL_WORKSPACE", absPath(ctx, "."))
472	soongBuildEnv.Set("BAZEL_METRICS_DIR", config.BazelMetricsDir())
473	soongBuildEnv.Set("LOG_DIR", config.LogsDir())
474	soongBuildEnv.Set("BAZEL_DEPS_FILE", absPath(ctx, filepath.Join(config.BazelOutDir(), "bazel.list")))
475
476	// For Soong bootstrapping tests
477	if os.Getenv("ALLOW_MISSING_DEPENDENCIES") == "true" {
478		soongBuildEnv.Set("ALLOW_MISSING_DEPENDENCIES", "true")
479	}
480
481	err := writeEnvironmentFile(ctx, envFile, soongBuildEnv.AsMap())
482	if err != nil {
483		ctx.Fatalf("failed to write environment file %s: %s", envFile, err)
484	}
485
486	func() {
487		ctx.BeginTrace(metrics.RunSoong, "environment check")
488		defer ctx.EndTrace()
489
490		checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongBuildTag))
491
492		if config.BazelBuildEnabled() || config.Bp2Build() {
493			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(bp2buildFilesTag))
494		}
495
496		if config.JsonModuleGraph() {
497			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(jsonModuleGraphTag))
498		}
499
500		if config.Queryview() {
501			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag))
502		}
503
504		if config.ApiBp2build() {
505			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(apiBp2buildTag))
506		}
507
508		if config.SoongDocs() {
509			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag))
510		}
511	}()
512
513	runMicrofactory(ctx, config, "bpglob", "github.com/google/blueprint/bootstrap/bpglob",
514		map[string]string{"github.com/google/blueprint": "build/blueprint"})
515
516	ninja := func(name, ninjaFile string, targets ...string) {
517		ctx.BeginTrace(metrics.RunSoong, name)
518		defer ctx.EndTrace()
519
520		if config.IsPersistentBazelEnabled() {
521			bazelProxy := bazel.NewProxyServer(ctx.Logger, config.OutDir(), filepath.Join(config.SoongOutDir(), "workspace"))
522			bazelProxy.Start()
523			defer bazelProxy.Close()
524		}
525
526		fifo := filepath.Join(config.OutDir(), ".ninja_fifo")
527		nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
528		defer nr.Close()
529
530		ninjaArgs := []string{
531			"-d", "keepdepfile",
532			"-d", "stats",
533			"-o", "usesphonyoutputs=yes",
534			"-o", "preremoveoutputs=yes",
535			"-w", "dupbuild=err",
536			"-w", "outputdir=err",
537			"-w", "missingoutfile=err",
538			"-j", strconv.Itoa(config.Parallel()),
539			"--frontend_file", fifo,
540			"-f", filepath.Join(config.SoongOutDir(), ninjaFile),
541		}
542
543		if extra, ok := config.Environment().Get("SOONG_UI_NINJA_ARGS"); ok {
544			ctx.Printf(`CAUTION: arguments in $SOONG_UI_NINJA_ARGS=%q, e.g. "-n", can make soong_build FAIL or INCORRECT`, extra)
545			ninjaArgs = append(ninjaArgs, strings.Fields(extra)...)
546		}
547
548		ninjaArgs = append(ninjaArgs, targets...)
549		cmd := Command(ctx, config, "soong "+name,
550			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
551
552		var ninjaEnv Environment
553
554		// This is currently how the command line to invoke soong_build finds the
555		// root of the source tree and the output root
556		ninjaEnv.Set("TOP", os.Getenv("TOP"))
557
558		cmd.Environment = &ninjaEnv
559		cmd.Sandbox = soongSandbox
560		cmd.RunAndStreamOrFatal()
561	}
562
563	targets := make([]string, 0, 0)
564
565	if config.JsonModuleGraph() {
566		targets = append(targets, config.ModuleGraphFile())
567	}
568
569	if config.Bp2Build() {
570		targets = append(targets, config.Bp2BuildWorkspaceMarkerFile())
571	}
572
573	if config.Queryview() {
574		targets = append(targets, config.QueryviewMarkerFile())
575	}
576
577	if config.ApiBp2build() {
578		targets = append(targets, config.ApiBp2buildMarkerFile())
579	}
580
581	if config.SoongDocs() {
582		targets = append(targets, config.SoongDocsHtml())
583	}
584
585	if config.SoongBuildInvocationNeeded() {
586		// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
587		targets = append(targets, config.SoongNinjaFile())
588	}
589
590	ninja("bootstrap", "bootstrap.ninja", targets...)
591
592	distGzipFile(ctx, config, config.SoongNinjaFile(), "soong")
593	distFile(ctx, config, config.SoongVarsFile(), "soong")
594
595	if !config.SkipKati() {
596		distGzipFile(ctx, config, config.SoongAndroidMk(), "soong")
597		distGzipFile(ctx, config, config.SoongMakeVarsMk(), "soong")
598	}
599
600	if config.JsonModuleGraph() {
601		distGzipFile(ctx, config, config.ModuleGraphFile(), "soong")
602	}
603}
604
605func runMicrofactory(ctx Context, config Config, name string, pkg string, mapping map[string]string) {
606	ctx.BeginTrace(metrics.RunSoong, name)
607	defer ctx.EndTrace()
608	cfg := microfactory.Config{TrimPath: absPath(ctx, ".")}
609	for pkgPrefix, pathPrefix := range mapping {
610		cfg.Map(pkgPrefix, pathPrefix)
611	}
612
613	exePath := filepath.Join(config.SoongOutDir(), name)
614	dir := filepath.Dir(exePath)
615	if err := os.MkdirAll(dir, 0777); err != nil {
616		ctx.Fatalf("cannot create %s: %s", dir, err)
617	}
618	if _, err := microfactory.Build(&cfg, exePath, pkg); err != nil {
619		ctx.Fatalf("failed to build %s: %s", name, err)
620	}
621}
622