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