• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 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 bootstrap
16
17import (
18	"fmt"
19	"go/build"
20	"path/filepath"
21	"runtime"
22	"strings"
23
24	"github.com/google/blueprint"
25	"github.com/google/blueprint/pathtools"
26)
27
28var (
29	pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap")
30
31	goTestMainCmd   = pctx.StaticVariable("goTestMainCmd", filepath.Join("$ToolDir", "gotestmain"))
32	goTestRunnerCmd = pctx.StaticVariable("goTestRunnerCmd", filepath.Join("$ToolDir", "gotestrunner"))
33	pluginGenSrcCmd = pctx.StaticVariable("pluginGenSrcCmd", filepath.Join("$ToolDir", "loadplugins"))
34
35	parallelCompile = pctx.StaticVariable("parallelCompile", func() string {
36		// Parallel compilation is only supported on >= go1.9
37		for _, r := range build.Default.ReleaseTags {
38			if r == "go1.9" {
39				numCpu := runtime.NumCPU()
40				// This will cause us to recompile all go programs if the
41				// number of cpus changes. We don't get a lot of benefit from
42				// higher values, so cap this to make it cheaper to move trees
43				// between machines.
44				if numCpu > 8 {
45					numCpu = 8
46				}
47				return fmt.Sprintf("-c %d", numCpu)
48			}
49		}
50		return ""
51	}())
52
53	compile = pctx.StaticRule("compile",
54		blueprint.RuleParams{
55			Command: "GOROOT='$goRoot' $compileCmd $parallelCompile -o $out.tmp " +
56				"$debugFlags -p $pkgPath -complete $incFlags -pack $in && " +
57				"if cmp --quiet $out.tmp $out; then rm $out.tmp; else mv -f $out.tmp $out; fi",
58			CommandDeps: []string{"$compileCmd"},
59			Description: "compile $out",
60			Restat:      true,
61		},
62		"pkgPath", "incFlags")
63
64	link = pctx.StaticRule("link",
65		blueprint.RuleParams{
66			Command: "GOROOT='$goRoot' $linkCmd -o $out.tmp $libDirFlags $in && " +
67				"if cmp --quiet $out.tmp $out; then rm $out.tmp; else mv -f $out.tmp $out; fi",
68			CommandDeps: []string{"$linkCmd"},
69			Description: "link $out",
70			Restat:      true,
71		},
72		"libDirFlags")
73
74	goTestMain = pctx.StaticRule("gotestmain",
75		blueprint.RuleParams{
76			Command:     "$goTestMainCmd -o $out -pkg $pkg $in",
77			CommandDeps: []string{"$goTestMainCmd"},
78			Description: "gotestmain $out",
79		},
80		"pkg")
81
82	pluginGenSrc = pctx.StaticRule("pluginGenSrc",
83		blueprint.RuleParams{
84			Command:     "$pluginGenSrcCmd -o $out -p $pkg $plugins",
85			CommandDeps: []string{"$pluginGenSrcCmd"},
86			Description: "create $out",
87		},
88		"pkg", "plugins")
89
90	test = pctx.StaticRule("test",
91		blueprint.RuleParams{
92			Command:     "$goTestRunnerCmd -p $pkgSrcDir -f $out -- $in -test.short",
93			CommandDeps: []string{"$goTestRunnerCmd"},
94			Description: "test $pkg",
95		},
96		"pkg", "pkgSrcDir")
97
98	cp = pctx.StaticRule("cp",
99		blueprint.RuleParams{
100			Command:     "cp $in $out",
101			Description: "cp $out",
102		},
103		"generator")
104
105	bootstrap = pctx.StaticRule("bootstrap",
106		blueprint.RuleParams{
107			Command:     "BUILDDIR=$soongOutDir $bootstrapCmd -i $in",
108			CommandDeps: []string{"$bootstrapCmd"},
109			Description: "bootstrap $in",
110			Generator:   true,
111		})
112
113	touch = pctx.StaticRule("touch",
114		blueprint.RuleParams{
115			Command:     "touch $out",
116			Description: "touch $out",
117		},
118		"depfile", "generator")
119
120	generateBuildNinja = pctx.StaticRule("build.ninja",
121		blueprint.RuleParams{
122			// TODO: it's kinda ugly that some parameters are computed from
123			// environment variables and some from Ninja parameters, but it's probably
124			// better to not to touch that while Blueprint and Soong are separate
125			// NOTE: The spaces at EOL are important because otherwise Ninja would
126			// omit all spaces between the different options.
127			Command: `cd "$$(dirname "$builder")" && ` +
128				`BUILDER="$$PWD/$$(basename "$builder")" && ` +
129				`cd / && ` +
130				`env -i $env "$$BUILDER" ` +
131				`    --top "$$TOP" ` +
132				`    --soong_out "$soongOutDir" ` +
133				`    --out "$outDir" ` +
134				`    $extra`,
135			CommandDeps: []string{"$builder"},
136			Description: "$builder $out",
137			Deps:        blueprint.DepsGCC,
138			Depfile:     "$out.d",
139			Restat:      true,
140		},
141		"builder", "env", "extra", "pool")
142
143	// Work around a Ninja issue.  See https://github.com/martine/ninja/pull/634
144	phony = pctx.StaticRule("phony",
145		blueprint.RuleParams{
146			Command:     "# phony $out",
147			Description: "phony $out",
148			Generator:   true,
149		},
150		"depfile")
151
152	_ = pctx.VariableFunc("ToolDir", func(config interface{}) (string, error) {
153		return config.(BootstrapConfig).HostToolDir(), nil
154	})
155)
156
157type GoBinaryTool interface {
158	InstallPath() string
159
160	// So that other packages can't implement this interface
161	isGoBinary()
162}
163
164func pluginDeps(ctx blueprint.BottomUpMutatorContext) {
165	if pkg, ok := ctx.Module().(*goPackage); ok {
166		if ctx.PrimaryModule() == ctx.Module() {
167			for _, plugin := range pkg.properties.PluginFor {
168				ctx.AddReverseDependency(ctx.Module(), nil, plugin)
169			}
170		}
171	}
172}
173
174type goPackageProducer interface {
175	GoPkgRoot() string
176	GoPackageTarget() string
177	GoTestTargets() []string
178}
179
180func isGoPackageProducer(module blueprint.Module) bool {
181	_, ok := module.(goPackageProducer)
182	return ok
183}
184
185type goPluginProvider interface {
186	GoPkgPath() string
187	IsPluginFor(string) bool
188}
189
190func isGoPluginFor(name string) func(blueprint.Module) bool {
191	return func(module blueprint.Module) bool {
192		if plugin, ok := module.(goPluginProvider); ok {
193			return plugin.IsPluginFor(name)
194		}
195		return false
196	}
197}
198
199func IsBootstrapModule(module blueprint.Module) bool {
200	_, isPackage := module.(*goPackage)
201	_, isBinary := module.(*goBinary)
202	return isPackage || isBinary
203}
204
205func isBootstrapBinaryModule(module blueprint.Module) bool {
206	_, isBinary := module.(*goBinary)
207	return isBinary
208}
209
210// A goPackage is a module for building Go packages.
211type goPackage struct {
212	blueprint.SimpleName
213	properties struct {
214		Deps      []string
215		PkgPath   string
216		Srcs      []string
217		TestSrcs  []string
218		PluginFor []string
219
220		Darwin struct {
221			Srcs     []string
222			TestSrcs []string
223		}
224		Linux struct {
225			Srcs     []string
226			TestSrcs []string
227		}
228	}
229
230	// The root dir in which the package .a file is located.  The full .a file
231	// path will be "packageRoot/PkgPath.a"
232	pkgRoot string
233
234	// The path of the .a file that is to be built.
235	archiveFile string
236
237	// The path of the test result file.
238	testResultFile []string
239}
240
241var _ goPackageProducer = (*goPackage)(nil)
242
243func newGoPackageModuleFactory() func() (blueprint.Module, []interface{}) {
244	return func() (blueprint.Module, []interface{}) {
245		module := &goPackage{}
246		return module, []interface{}{&module.properties, &module.SimpleName.Properties}
247	}
248}
249
250func (g *goPackage) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
251	if ctx.Module() != ctx.PrimaryModule() {
252		return nil
253	}
254	return g.properties.Deps
255}
256
257func (g *goPackage) GoPkgPath() string {
258	return g.properties.PkgPath
259}
260
261func (g *goPackage) GoPkgRoot() string {
262	return g.pkgRoot
263}
264
265func (g *goPackage) GoPackageTarget() string {
266	return g.archiveFile
267}
268
269func (g *goPackage) GoTestTargets() []string {
270	return g.testResultFile
271}
272
273func (g *goPackage) IsPluginFor(name string) bool {
274	for _, plugin := range g.properties.PluginFor {
275		if plugin == name {
276			return true
277		}
278	}
279	return false
280}
281
282func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
283	// Allow the primary builder to create multiple variants.  Any variants after the first
284	// will copy outputs from the first.
285	if ctx.Module() != ctx.PrimaryModule() {
286		primary := ctx.PrimaryModule().(*goPackage)
287		g.pkgRoot = primary.pkgRoot
288		g.archiveFile = primary.archiveFile
289		g.testResultFile = primary.testResultFile
290		return
291	}
292
293	var (
294		name       = ctx.ModuleName()
295		hasPlugins = false
296		pluginSrc  = ""
297		genSrcs    = []string{}
298	)
299
300	if g.properties.PkgPath == "" {
301		ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name)
302		return
303	}
304
305	g.pkgRoot = packageRoot(ctx)
306	g.archiveFile = filepath.Join(g.pkgRoot,
307		filepath.FromSlash(g.properties.PkgPath)+".a")
308
309	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
310		func(module blueprint.Module) { hasPlugins = true })
311	if hasPlugins {
312		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
313		genSrcs = append(genSrcs, pluginSrc)
314	}
315
316	if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc) {
317		return
318	}
319
320	var srcs, testSrcs []string
321	if runtime.GOOS == "darwin" {
322		srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...)
323		testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...)
324	} else if runtime.GOOS == "linux" {
325		srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...)
326		testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...)
327	}
328
329	if ctx.Config().(BootstrapConfig).RunGoTests() {
330		testArchiveFile := filepath.Join(testRoot(ctx),
331			filepath.FromSlash(g.properties.PkgPath)+".a")
332		g.testResultFile = buildGoTest(ctx, testRoot(ctx), testArchiveFile,
333			g.properties.PkgPath, srcs, genSrcs, testSrcs)
334	}
335
336	// Don't build for test-only packages
337	if len(srcs) == 0 && len(genSrcs) == 0 {
338		ctx.Build(pctx, blueprint.BuildParams{
339			Rule:     touch,
340			Outputs:  []string{g.archiveFile},
341			Optional: true,
342		})
343		return
344	}
345
346	buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile,
347		srcs, genSrcs)
348}
349
350// A goBinary is a module for building executable binaries from Go sources.
351type goBinary struct {
352	blueprint.SimpleName
353	properties struct {
354		Deps           []string
355		Srcs           []string
356		TestSrcs       []string
357		PrimaryBuilder bool
358		Default        bool
359
360		Darwin struct {
361			Srcs     []string
362			TestSrcs []string
363		}
364		Linux struct {
365			Srcs     []string
366			TestSrcs []string
367		}
368	}
369
370	installPath string
371}
372
373var _ GoBinaryTool = (*goBinary)(nil)
374
375func newGoBinaryModuleFactory() func() (blueprint.Module, []interface{}) {
376	return func() (blueprint.Module, []interface{}) {
377		module := &goBinary{}
378		return module, []interface{}{&module.properties, &module.SimpleName.Properties}
379	}
380}
381
382func (g *goBinary) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
383	if ctx.Module() != ctx.PrimaryModule() {
384		return nil
385	}
386	return g.properties.Deps
387}
388
389func (g *goBinary) isGoBinary() {}
390func (g *goBinary) InstallPath() string {
391	return g.installPath
392}
393
394func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
395	// Allow the primary builder to create multiple variants.  Any variants after the first
396	// will copy outputs from the first.
397	if ctx.Module() != ctx.PrimaryModule() {
398		primary := ctx.PrimaryModule().(*goBinary)
399		g.installPath = primary.installPath
400		return
401	}
402
403	var (
404		name            = ctx.ModuleName()
405		objDir          = moduleObjDir(ctx)
406		archiveFile     = filepath.Join(objDir, name+".a")
407		testArchiveFile = filepath.Join(testRoot(ctx), name+".a")
408		aoutFile        = filepath.Join(objDir, "a.out")
409		hasPlugins      = false
410		pluginSrc       = ""
411		genSrcs         = []string{}
412	)
413
414	g.installPath = filepath.Join(ctx.Config().(BootstrapConfig).HostToolDir(), name)
415	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
416		func(module blueprint.Module) { hasPlugins = true })
417	if hasPlugins {
418		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
419		genSrcs = append(genSrcs, pluginSrc)
420	}
421
422	var testDeps []string
423
424	if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc) {
425		return
426	}
427
428	var srcs, testSrcs []string
429	if runtime.GOOS == "darwin" {
430		srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...)
431		testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...)
432	} else if runtime.GOOS == "linux" {
433		srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...)
434		testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...)
435	}
436
437	if ctx.Config().(BootstrapConfig).RunGoTests() {
438		testDeps = buildGoTest(ctx, testRoot(ctx), testArchiveFile,
439			name, srcs, genSrcs, testSrcs)
440	}
441
442	buildGoPackage(ctx, objDir, "main", archiveFile, srcs, genSrcs)
443
444	var linkDeps []string
445	var libDirFlags []string
446	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
447		func(module blueprint.Module) {
448			dep := module.(goPackageProducer)
449			linkDeps = append(linkDeps, dep.GoPackageTarget())
450			libDir := dep.GoPkgRoot()
451			libDirFlags = append(libDirFlags, "-L "+libDir)
452			testDeps = append(testDeps, dep.GoTestTargets()...)
453		})
454
455	linkArgs := map[string]string{}
456	if len(libDirFlags) > 0 {
457		linkArgs["libDirFlags"] = strings.Join(libDirFlags, " ")
458	}
459
460	ctx.Build(pctx, blueprint.BuildParams{
461		Rule:      link,
462		Outputs:   []string{aoutFile},
463		Inputs:    []string{archiveFile},
464		Implicits: linkDeps,
465		Args:      linkArgs,
466		Optional:  true,
467	})
468
469	ctx.Build(pctx, blueprint.BuildParams{
470		Rule:        cp,
471		Outputs:     []string{g.installPath},
472		Inputs:      []string{aoutFile},
473		Validations: testDeps,
474		Optional:    !g.properties.Default,
475	})
476}
477
478func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string) bool {
479	ret := true
480	name := ctx.ModuleName()
481
482	var pluginPaths []string
483	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
484		func(module blueprint.Module) {
485			plugin := module.(goPluginProvider)
486			pluginPaths = append(pluginPaths, plugin.GoPkgPath())
487		})
488
489	ctx.Build(pctx, blueprint.BuildParams{
490		Rule:    pluginGenSrc,
491		Outputs: []string{pluginSrc},
492		Args: map[string]string{
493			"pkg":     pkgPath,
494			"plugins": strings.Join(pluginPaths, " "),
495		},
496		Optional: true,
497	})
498
499	return ret
500}
501
502func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string,
503	pkgPath string, archiveFile string, srcs []string, genSrcs []string) {
504
505	srcDir := moduleSrcDir(ctx)
506	srcFiles := pathtools.PrefixPaths(srcs, srcDir)
507	srcFiles = append(srcFiles, genSrcs...)
508
509	var incFlags []string
510	var deps []string
511	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
512		func(module blueprint.Module) {
513			dep := module.(goPackageProducer)
514			incDir := dep.GoPkgRoot()
515			target := dep.GoPackageTarget()
516			incFlags = append(incFlags, "-I "+incDir)
517			deps = append(deps, target)
518		})
519
520	compileArgs := map[string]string{
521		"pkgPath": pkgPath,
522	}
523
524	if len(incFlags) > 0 {
525		compileArgs["incFlags"] = strings.Join(incFlags, " ")
526	}
527
528	ctx.Build(pctx, blueprint.BuildParams{
529		Rule:      compile,
530		Outputs:   []string{archiveFile},
531		Inputs:    srcFiles,
532		Implicits: deps,
533		Args:      compileArgs,
534		Optional:  true,
535	})
536}
537
538func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive,
539	pkgPath string, srcs, genSrcs, testSrcs []string) []string {
540
541	if len(testSrcs) == 0 {
542		return nil
543	}
544
545	srcDir := moduleSrcDir(ctx)
546	testFiles := pathtools.PrefixPaths(testSrcs, srcDir)
547
548	mainFile := filepath.Join(testRoot, "test.go")
549	testArchive := filepath.Join(testRoot, "test.a")
550	testFile := filepath.Join(testRoot, "test")
551	testPassed := filepath.Join(testRoot, "test.passed")
552
553	buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive,
554		append(srcs, testSrcs...), genSrcs)
555
556	ctx.Build(pctx, blueprint.BuildParams{
557		Rule:    goTestMain,
558		Outputs: []string{mainFile},
559		Inputs:  testFiles,
560		Args: map[string]string{
561			"pkg": pkgPath,
562		},
563		Optional: true,
564	})
565
566	linkDeps := []string{testPkgArchive}
567	libDirFlags := []string{"-L " + testRoot}
568	testDeps := []string{}
569	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
570		func(module blueprint.Module) {
571			dep := module.(goPackageProducer)
572			linkDeps = append(linkDeps, dep.GoPackageTarget())
573			libDir := dep.GoPkgRoot()
574			libDirFlags = append(libDirFlags, "-L "+libDir)
575			testDeps = append(testDeps, dep.GoTestTargets()...)
576		})
577
578	ctx.Build(pctx, blueprint.BuildParams{
579		Rule:      compile,
580		Outputs:   []string{testArchive},
581		Inputs:    []string{mainFile},
582		Implicits: []string{testPkgArchive},
583		Args: map[string]string{
584			"pkgPath":  "main",
585			"incFlags": "-I " + testRoot,
586		},
587		Optional: true,
588	})
589
590	ctx.Build(pctx, blueprint.BuildParams{
591		Rule:      link,
592		Outputs:   []string{testFile},
593		Inputs:    []string{testArchive},
594		Implicits: linkDeps,
595		Args: map[string]string{
596			"libDirFlags": strings.Join(libDirFlags, " "),
597		},
598		Optional: true,
599	})
600
601	ctx.Build(pctx, blueprint.BuildParams{
602		Rule:        test,
603		Outputs:     []string{testPassed},
604		Inputs:      []string{testFile},
605		Validations: testDeps,
606		Args: map[string]string{
607			"pkg":       pkgPath,
608			"pkgSrcDir": filepath.Dir(testFiles[0]),
609		},
610		Optional: true,
611	})
612
613	return []string{testPassed}
614}
615
616type singleton struct {
617}
618
619func newSingletonFactory() func() blueprint.Singleton {
620	return func() blueprint.Singleton {
621		return &singleton{}
622	}
623}
624
625func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
626	// Find the module that's marked as the "primary builder", which means it's
627	// creating the binary that we'll use to generate the non-bootstrap
628	// build.ninja file.
629	var primaryBuilders []*goBinary
630	// blueprintTools contains blueprint go binaries that will be built in StageMain
631	var blueprintTools []string
632	// blueprintGoPackages contains all blueprint go packages that can be built in StageMain
633	var blueprintGoPackages []string
634	ctx.VisitAllModulesIf(IsBootstrapModule,
635		func(module blueprint.Module) {
636			if ctx.PrimaryModule(module) == module {
637				if binaryModule, ok := module.(*goBinary); ok {
638					blueprintTools = append(blueprintTools, binaryModule.InstallPath())
639					if binaryModule.properties.PrimaryBuilder {
640						primaryBuilders = append(primaryBuilders, binaryModule)
641					}
642				}
643
644				if packageModule, ok := module.(*goPackage); ok {
645					blueprintGoPackages = append(blueprintGoPackages,
646						packageModule.GoPackageTarget())
647					blueprintGoPackages = append(blueprintGoPackages,
648						packageModule.GoTestTargets()...)
649				}
650			}
651		})
652
653	var primaryBuilderCmdlinePrefix []string
654	var primaryBuilderName string
655
656	if len(primaryBuilders) == 0 {
657		ctx.Errorf("no primary builder module present")
658		return
659	} else if len(primaryBuilders) > 1 {
660		ctx.Errorf("multiple primary builder modules present:")
661		for _, primaryBuilder := range primaryBuilders {
662			ctx.ModuleErrorf(primaryBuilder, "<-- module %s",
663				ctx.ModuleName(primaryBuilder))
664		}
665		return
666	} else {
667		primaryBuilderName = ctx.ModuleName(primaryBuilders[0])
668	}
669
670	primaryBuilderFile := filepath.Join("$ToolDir", primaryBuilderName)
671	ctx.SetOutDir(pctx, "${outDir}")
672
673	for _, subninja := range ctx.Config().(BootstrapConfig).Subninjas() {
674		ctx.AddSubninja(subninja)
675	}
676
677	for _, i := range ctx.Config().(BootstrapConfig).PrimaryBuilderInvocations() {
678		flags := make([]string, 0)
679		flags = append(flags, primaryBuilderCmdlinePrefix...)
680		flags = append(flags, i.Args...)
681
682		pool := ""
683		if i.Console {
684			pool = "console"
685		}
686
687		envAssignments := ""
688		for k, v := range i.Env {
689			// NB: This is rife with quoting issues but we don't care because we trust
690			// soong_ui to not abuse this facility too much
691			envAssignments += k + "=" + v + " "
692		}
693
694		// Build the main build.ninja
695		ctx.Build(pctx, blueprint.BuildParams{
696			Rule:    generateBuildNinja,
697			Outputs: i.Outputs,
698			Inputs:  i.Inputs,
699			Args: map[string]string{
700				"builder": primaryBuilderFile,
701				"env":     envAssignments,
702				"extra":   strings.Join(flags, " "),
703				"pool":    pool,
704			},
705			// soong_ui explicitly requests what it wants to be build. This is
706			// because the same Ninja file contains instructions to run
707			// soong_build, run bp2build and to generate the JSON module graph.
708			Optional:    true,
709			Description: i.Description,
710		})
711	}
712
713	// Add a phony target for building various tools that are part of blueprint
714	ctx.Build(pctx, blueprint.BuildParams{
715		Rule:    blueprint.Phony,
716		Outputs: []string{"blueprint_tools"},
717		Inputs:  blueprintTools,
718	})
719
720	// Add a phony target for running go tests
721	ctx.Build(pctx, blueprint.BuildParams{
722		Rule:     blueprint.Phony,
723		Outputs:  []string{"blueprint_go_packages"},
724		Inputs:   blueprintGoPackages,
725		Optional: true,
726	})
727}
728
729// packageRoot returns the module-specific package root directory path.  This
730// directory is where the final package .a files are output and where dependant
731// modules search for this package via -I arguments.
732func packageRoot(ctx blueprint.ModuleContext) string {
733	toolDir := ctx.Config().(BootstrapConfig).HostToolDir()
734	return filepath.Join(toolDir, "go", ctx.ModuleName(), "pkg")
735}
736
737// testRoot returns the module-specific package root directory path used for
738// building tests. The .a files generated here will include everything from
739// packageRoot, plus the test-only code.
740func testRoot(ctx blueprint.ModuleContext) string {
741	toolDir := ctx.Config().(BootstrapConfig).HostToolDir()
742	return filepath.Join(toolDir, "go", ctx.ModuleName(), "test")
743}
744
745// moduleSrcDir returns the path of the directory that all source file paths are
746// specified relative to.
747func moduleSrcDir(ctx blueprint.ModuleContext) string {
748	return ctx.ModuleDir()
749}
750
751// moduleObjDir returns the module-specific object directory path.
752func moduleObjDir(ctx blueprint.ModuleContext) string {
753	toolDir := ctx.Config().(BootstrapConfig).HostToolDir()
754	return filepath.Join(toolDir, "go", ctx.ModuleName(), "obj")
755}
756
757// moduleGenSrcDir returns the module-specific generated sources path.
758func moduleGenSrcDir(ctx blueprint.ModuleContext) string {
759	toolDir := ctx.Config().(BootstrapConfig).HostToolDir()
760	return filepath.Join(toolDir, "go", ctx.ModuleName(), "gen")
761}
762