• 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
27const bootstrapSubDir = ".bootstrap"
28const miniBootstrapSubDir = ".minibootstrap"
29
30var (
31	pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap")
32
33	goTestMainCmd   = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain"))
34	goTestRunnerCmd = pctx.StaticVariable("goTestRunnerCmd", filepath.Join(bootstrapDir, "bin", "gotestrunner"))
35	pluginGenSrcCmd = pctx.StaticVariable("pluginGenSrcCmd", filepath.Join(bootstrapDir, "bin", "loadplugins"))
36
37	compile = pctx.StaticRule("compile",
38		blueprint.RuleParams{
39			Command: "GOROOT='$goRoot' $compileCmd -o $out -p $pkgPath -complete " +
40				"$incFlags -pack $in",
41			CommandDeps: []string{"$compileCmd"},
42			Description: "compile $out",
43		},
44		"pkgPath", "incFlags")
45
46	link = pctx.StaticRule("link",
47		blueprint.RuleParams{
48			Command:     "GOROOT='$goRoot' $linkCmd -o $out $libDirFlags $in",
49			CommandDeps: []string{"$linkCmd"},
50			Description: "link $out",
51		},
52		"libDirFlags")
53
54	goTestMain = pctx.StaticRule("gotestmain",
55		blueprint.RuleParams{
56			Command:     "$goTestMainCmd -o $out -pkg $pkg $in",
57			CommandDeps: []string{"$goTestMainCmd"},
58			Description: "gotestmain $out",
59		},
60		"pkg")
61
62	pluginGenSrc = pctx.StaticRule("pluginGenSrc",
63		blueprint.RuleParams{
64			Command:     "$pluginGenSrcCmd -o $out -p $pkg $plugins",
65			CommandDeps: []string{"$pluginGenSrcCmd"},
66			Description: "create $out",
67		},
68		"pkg", "plugins")
69
70	test = pctx.StaticRule("test",
71		blueprint.RuleParams{
72			Command:     "$goTestRunnerCmd -p $pkgSrcDir -f $out -- $in -test.short",
73			CommandDeps: []string{"$goTestRunnerCmd"},
74			Description: "test $pkg",
75		},
76		"pkg", "pkgSrcDir")
77
78	cp = pctx.StaticRule("cp",
79		blueprint.RuleParams{
80			Command:     "cp $in $out",
81			Description: "cp $out",
82		},
83		"generator")
84
85	bootstrap = pctx.StaticRule("bootstrap",
86		blueprint.RuleParams{
87			Command:     "BUILDDIR=$buildDir $bootstrapCmd -i $in",
88			CommandDeps: []string{"$bootstrapCmd"},
89			Description: "bootstrap $in",
90			Generator:   true,
91		})
92
93	touch = pctx.StaticRule("touch",
94		blueprint.RuleParams{
95			Command:     "touch $out",
96			Description: "touch $out",
97		},
98		"depfile", "generator")
99
100	generateBuildNinja = pctx.StaticRule("build.ninja",
101		blueprint.RuleParams{
102			Command:     "$builder $extra -b $buildDir -d $out.d -o $out $in",
103			CommandDeps: []string{"$builder"},
104			Description: "$builder $out",
105			Depfile:     "$out.d",
106			Restat:      true,
107		},
108		"builder", "extra", "generator")
109
110	// Work around a Ninja issue.  See https://github.com/martine/ninja/pull/634
111	phony = pctx.StaticRule("phony",
112		blueprint.RuleParams{
113			Command:     "# phony $out",
114			Description: "phony $out",
115			Generator:   true,
116		},
117		"depfile")
118
119	binDir     = pctx.StaticVariable("BinDir", filepath.Join(bootstrapDir, "bin"))
120	minibpFile = filepath.Join("$BinDir", "minibp")
121
122	docsDir = filepath.Join(bootstrapDir, "docs")
123	toolDir = pctx.VariableFunc("ToolDir", func(config interface{}) (string, error) {
124		if c, ok := config.(ConfigBlueprintToolLocation); ok {
125			return c.BlueprintToolLocation(), nil
126		}
127		return filepath.Join("$buildDir", "bin"), nil
128	})
129
130	bootstrapDir     = filepath.Join("$buildDir", bootstrapSubDir)
131	miniBootstrapDir = filepath.Join("$buildDir", miniBootstrapSubDir)
132)
133
134type bootstrapGoCore interface {
135	BuildStage() Stage
136	SetBuildStage(Stage)
137}
138
139func propagateStageBootstrap(mctx blueprint.TopDownMutatorContext) {
140	if mod, ok := mctx.Module().(bootstrapGoCore); ok {
141		stage := mod.BuildStage()
142
143		mctx.VisitDirectDeps(func(mod blueprint.Module) {
144			if m, ok := mod.(bootstrapGoCore); ok && m.BuildStage() > stage {
145				m.SetBuildStage(stage)
146			}
147		})
148	}
149}
150
151func pluginDeps(ctx blueprint.BottomUpMutatorContext) {
152	if pkg, ok := ctx.Module().(*goPackage); ok {
153		for _, plugin := range pkg.properties.PluginFor {
154			ctx.AddReverseDependency(ctx.Module(), nil, plugin)
155		}
156	}
157}
158
159type goPackageProducer interface {
160	GoPkgRoot() string
161	GoPackageTarget() string
162	GoTestTargets() []string
163}
164
165func isGoPackageProducer(module blueprint.Module) bool {
166	_, ok := module.(goPackageProducer)
167	return ok
168}
169
170type goPluginProvider interface {
171	GoPkgPath() string
172	IsPluginFor(string) bool
173}
174
175func isGoPluginFor(name string) func(blueprint.Module) bool {
176	return func(module blueprint.Module) bool {
177		if plugin, ok := module.(goPluginProvider); ok {
178			return plugin.IsPluginFor(name)
179		}
180		return false
181	}
182}
183
184func isBootstrapModule(module blueprint.Module) bool {
185	_, isPackage := module.(*goPackage)
186	_, isBinary := module.(*goBinary)
187	return isPackage || isBinary
188}
189
190func isBootstrapBinaryModule(module blueprint.Module) bool {
191	_, isBinary := module.(*goBinary)
192	return isBinary
193}
194
195// A goPackage is a module for building Go packages.
196type goPackage struct {
197	blueprint.SimpleName
198	properties struct {
199		Deps      []string
200		PkgPath   string
201		Srcs      []string
202		TestSrcs  []string
203		PluginFor []string
204
205		Darwin struct {
206			Srcs     []string
207			TestSrcs []string
208		}
209		Linux struct {
210			Srcs     []string
211			TestSrcs []string
212		}
213
214		// The stage in which this module should be built
215		BuildStage Stage `blueprint:"mutated"`
216	}
217
218	// The root dir in which the package .a file is located.  The full .a file
219	// path will be "packageRoot/PkgPath.a"
220	pkgRoot string
221
222	// The path of the .a file that is to be built.
223	archiveFile string
224
225	// The path of the test result file.
226	testResultFile []string
227
228	// The bootstrap Config
229	config *Config
230}
231
232var _ goPackageProducer = (*goPackage)(nil)
233
234func newGoPackageModuleFactory(config *Config) func() (blueprint.Module, []interface{}) {
235	return func() (blueprint.Module, []interface{}) {
236		module := &goPackage{
237			config: config,
238		}
239		module.properties.BuildStage = StageMain
240		return module, []interface{}{&module.properties, &module.SimpleName.Properties}
241	}
242}
243
244func (g *goPackage) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
245	return g.properties.Deps
246}
247
248func (g *goPackage) GoPkgPath() string {
249	return g.properties.PkgPath
250}
251
252func (g *goPackage) GoPkgRoot() string {
253	return g.pkgRoot
254}
255
256func (g *goPackage) GoPackageTarget() string {
257	return g.archiveFile
258}
259
260func (g *goPackage) GoTestTargets() []string {
261	return g.testResultFile
262}
263
264func (g *goPackage) BuildStage() Stage {
265	return g.properties.BuildStage
266}
267
268func (g *goPackage) SetBuildStage(buildStage Stage) {
269	g.properties.BuildStage = buildStage
270}
271
272func (g *goPackage) IsPluginFor(name string) bool {
273	for _, plugin := range g.properties.PluginFor {
274		if plugin == name {
275			return true
276		}
277	}
278	return false
279}
280
281func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
282	var (
283		name       = ctx.ModuleName()
284		hasPlugins = false
285		pluginSrc  = ""
286		genSrcs    = []string{}
287	)
288
289	if g.properties.PkgPath == "" {
290		ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name)
291		return
292	}
293
294	g.pkgRoot = packageRoot(ctx)
295	g.archiveFile = filepath.Join(g.pkgRoot,
296		filepath.FromSlash(g.properties.PkgPath)+".a")
297
298	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
299		func(module blueprint.Module) { hasPlugins = true })
300	if hasPlugins {
301		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
302		genSrcs = append(genSrcs, pluginSrc)
303	}
304
305	// We only actually want to build the builder modules if we're running as
306	// minibp (i.e. we're generating a bootstrap Ninja file).  This is to break
307	// the circular dependence that occurs when the builder requires a new Ninja
308	// file to be built, but building a new ninja file requires the builder to
309	// be built.
310	if g.config.stage == g.BuildStage() {
311		if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc, g.config.stage) {
312			return
313		}
314
315		var srcs, testSrcs []string
316		if runtime.GOOS == "darwin" {
317			srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...)
318			testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...)
319		} else if runtime.GOOS == "linux" {
320			srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...)
321			testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...)
322		}
323
324		if g.config.runGoTests {
325			testArchiveFile := filepath.Join(testRoot(ctx),
326				filepath.FromSlash(g.properties.PkgPath)+".a")
327			g.testResultFile = buildGoTest(ctx, testRoot(ctx), testArchiveFile,
328				g.properties.PkgPath, srcs, genSrcs,
329				testSrcs)
330		}
331
332		buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile,
333			srcs, genSrcs)
334	}
335}
336
337// A goBinary is a module for building executable binaries from Go sources.
338type goBinary struct {
339	blueprint.SimpleName
340	properties struct {
341		Deps           []string
342		Srcs           []string
343		TestSrcs       []string
344		PrimaryBuilder bool
345
346		Darwin struct {
347			Srcs     []string
348			TestSrcs []string
349		}
350		Linux struct {
351			Srcs     []string
352			TestSrcs []string
353		}
354
355		// The stage in which this module should be built
356		BuildStage Stage `blueprint:"mutated"`
357	}
358
359	// The bootstrap Config
360	config *Config
361}
362
363func newGoBinaryModuleFactory(config *Config, buildStage Stage) func() (blueprint.Module, []interface{}) {
364	return func() (blueprint.Module, []interface{}) {
365		module := &goBinary{
366			config: config,
367		}
368		module.properties.BuildStage = buildStage
369		return module, []interface{}{&module.properties, &module.SimpleName.Properties}
370	}
371}
372
373func (g *goBinary) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
374	return g.properties.Deps
375}
376
377func (g *goBinary) BuildStage() Stage {
378	return g.properties.BuildStage
379}
380
381func (g *goBinary) SetBuildStage(buildStage Stage) {
382	g.properties.BuildStage = buildStage
383}
384
385func (g *goBinary) InstallPath() string {
386	if g.BuildStage() == StageMain {
387		return "$ToolDir"
388	}
389	return "$BinDir"
390}
391
392func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
393	var (
394		name            = ctx.ModuleName()
395		objDir          = moduleObjDir(ctx)
396		archiveFile     = filepath.Join(objDir, name+".a")
397		testArchiveFile = filepath.Join(testRoot(ctx), name+".a")
398		aoutFile        = filepath.Join(objDir, "a.out")
399		binaryFile      = filepath.Join(g.InstallPath(), name)
400		hasPlugins      = false
401		pluginSrc       = ""
402		genSrcs         = []string{}
403	)
404
405	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
406		func(module blueprint.Module) { hasPlugins = true })
407	if hasPlugins {
408		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
409		genSrcs = append(genSrcs, pluginSrc)
410	}
411
412	if g.config.stage == g.BuildStage() {
413		var deps []string
414
415		if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc, g.config.stage) {
416			return
417		}
418
419		var srcs, testSrcs []string
420		if runtime.GOOS == "darwin" {
421			srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...)
422			testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...)
423		} else if runtime.GOOS == "linux" {
424			srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...)
425			testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...)
426		}
427
428		if g.config.runGoTests {
429			deps = buildGoTest(ctx, testRoot(ctx), testArchiveFile,
430				name, srcs, genSrcs, testSrcs)
431		}
432
433		buildGoPackage(ctx, objDir, name, archiveFile, srcs, genSrcs)
434
435		var libDirFlags []string
436		ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
437			func(module blueprint.Module) {
438				dep := module.(goPackageProducer)
439				libDir := dep.GoPkgRoot()
440				libDirFlags = append(libDirFlags, "-L "+libDir)
441				deps = append(deps, dep.GoTestTargets()...)
442			})
443
444		linkArgs := map[string]string{}
445		if len(libDirFlags) > 0 {
446			linkArgs["libDirFlags"] = strings.Join(libDirFlags, " ")
447		}
448
449		ctx.Build(pctx, blueprint.BuildParams{
450			Rule:    link,
451			Outputs: []string{aoutFile},
452			Inputs:  []string{archiveFile},
453			Args:    linkArgs,
454		})
455
456		ctx.Build(pctx, blueprint.BuildParams{
457			Rule:      cp,
458			Outputs:   []string{binaryFile},
459			Inputs:    []string{aoutFile},
460			OrderOnly: deps,
461		})
462	}
463}
464
465func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string, stage Stage) bool {
466	ret := true
467	name := ctx.ModuleName()
468
469	var pluginPaths []string
470	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
471		func(module blueprint.Module) {
472			plugin := module.(goPluginProvider)
473			pluginPaths = append(pluginPaths, plugin.GoPkgPath())
474			if stage == StageBootstrap {
475				ctx.OtherModuleErrorf(module, "plugin %q may not be included in core module %q",
476					ctx.OtherModuleName(module), name)
477				ret = false
478			}
479		})
480
481	ctx.Build(pctx, blueprint.BuildParams{
482		Rule:    pluginGenSrc,
483		Outputs: []string{pluginSrc},
484		Args: map[string]string{
485			"pkg":     pkgPath,
486			"plugins": strings.Join(pluginPaths, " "),
487		},
488	})
489
490	return ret
491}
492
493func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string,
494	pkgPath string, archiveFile string, srcs []string, genSrcs []string) {
495
496	srcDir := moduleSrcDir(ctx)
497	srcFiles := pathtools.PrefixPaths(srcs, srcDir)
498	srcFiles = append(srcFiles, genSrcs...)
499
500	var incFlags []string
501	var deps []string
502	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
503		func(module blueprint.Module) {
504			dep := module.(goPackageProducer)
505			incDir := dep.GoPkgRoot()
506			target := dep.GoPackageTarget()
507			incFlags = append(incFlags, "-I "+incDir)
508			deps = append(deps, target)
509		})
510
511	compileArgs := map[string]string{
512		"pkgPath": pkgPath,
513	}
514
515	if len(incFlags) > 0 {
516		compileArgs["incFlags"] = strings.Join(incFlags, " ")
517	}
518
519	ctx.Build(pctx, blueprint.BuildParams{
520		Rule:      compile,
521		Outputs:   []string{archiveFile},
522		Inputs:    srcFiles,
523		Implicits: deps,
524		Args:      compileArgs,
525	})
526}
527
528func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive,
529	pkgPath string, srcs, genSrcs, testSrcs []string) []string {
530
531	if len(testSrcs) == 0 {
532		return nil
533	}
534
535	srcDir := moduleSrcDir(ctx)
536	testFiles := pathtools.PrefixPaths(testSrcs, srcDir)
537
538	mainFile := filepath.Join(testRoot, "test.go")
539	testArchive := filepath.Join(testRoot, "test.a")
540	testFile := filepath.Join(testRoot, "test")
541	testPassed := filepath.Join(testRoot, "test.passed")
542
543	buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive,
544		append(srcs, testSrcs...), genSrcs)
545
546	ctx.Build(pctx, blueprint.BuildParams{
547		Rule:    goTestMain,
548		Outputs: []string{mainFile},
549		Inputs:  testFiles,
550		Args: map[string]string{
551			"pkg": pkgPath,
552		},
553	})
554
555	libDirFlags := []string{"-L " + testRoot}
556	testDeps := []string{}
557	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
558		func(module blueprint.Module) {
559			dep := module.(goPackageProducer)
560			libDir := dep.GoPkgRoot()
561			libDirFlags = append(libDirFlags, "-L "+libDir)
562			testDeps = append(testDeps, dep.GoTestTargets()...)
563		})
564
565	ctx.Build(pctx, blueprint.BuildParams{
566		Rule:      compile,
567		Outputs:   []string{testArchive},
568		Inputs:    []string{mainFile},
569		Implicits: []string{testPkgArchive},
570		Args: map[string]string{
571			"pkgPath":  "main",
572			"incFlags": "-I " + testRoot,
573		},
574	})
575
576	ctx.Build(pctx, blueprint.BuildParams{
577		Rule:    link,
578		Outputs: []string{testFile},
579		Inputs:  []string{testArchive},
580		Args: map[string]string{
581			"libDirFlags": strings.Join(libDirFlags, " "),
582		},
583	})
584
585	ctx.Build(pctx, blueprint.BuildParams{
586		Rule:      test,
587		Outputs:   []string{testPassed},
588		Inputs:    []string{testFile},
589		OrderOnly: testDeps,
590		Args: map[string]string{
591			"pkg":       pkgPath,
592			"pkgSrcDir": filepath.Dir(testFiles[0]),
593		},
594	})
595
596	return []string{testPassed}
597}
598
599type singleton struct {
600	// The bootstrap Config
601	config *Config
602}
603
604func newSingletonFactory(config *Config) func() blueprint.Singleton {
605	return func() blueprint.Singleton {
606		return &singleton{
607			config: config,
608		}
609	}
610}
611
612func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
613	// Find the module that's marked as the "primary builder", which means it's
614	// creating the binary that we'll use to generate the non-bootstrap
615	// build.ninja file.
616	var primaryBuilders []*goBinary
617	// blueprintTools contains blueprint go binaries that will be built in StageMain
618	var blueprintTools []string
619	ctx.VisitAllModulesIf(isBootstrapBinaryModule,
620		func(module blueprint.Module) {
621			binaryModule := module.(*goBinary)
622			binaryModuleName := ctx.ModuleName(binaryModule)
623			installPath := filepath.Join(binaryModule.InstallPath(), binaryModuleName)
624
625			if binaryModule.BuildStage() == StageMain {
626				blueprintTools = append(blueprintTools, installPath)
627			}
628			if binaryModule.properties.PrimaryBuilder {
629				primaryBuilders = append(primaryBuilders, binaryModule)
630			}
631		})
632
633	var extraTestFlags string
634	if s.config.runGoTests {
635		extraTestFlags = " -t"
636	}
637
638	var primaryBuilderName, primaryBuilderExtraFlags string
639	switch len(primaryBuilders) {
640	case 0:
641		// If there's no primary builder module then that means we'll use minibp
642		// as the primary builder.  We can trigger its primary builder mode with
643		// the -p flag.
644		primaryBuilderName = "minibp"
645		primaryBuilderExtraFlags = "-p" + extraTestFlags
646
647	case 1:
648		primaryBuilderName = ctx.ModuleName(primaryBuilders[0])
649		primaryBuilderExtraFlags = extraTestFlags
650
651	default:
652		ctx.Errorf("multiple primary builder modules present:")
653		for _, primaryBuilder := range primaryBuilders {
654			ctx.ModuleErrorf(primaryBuilder, "<-- module %s",
655				ctx.ModuleName(primaryBuilder))
656		}
657		return
658	}
659
660	primaryBuilderFile := filepath.Join("$BinDir", primaryBuilderName)
661
662	// Get the filename of the top-level Blueprints file to pass to minibp.
663	topLevelBlueprints := filepath.Join("$srcDir",
664		filepath.Base(s.config.topLevelBlueprintsFile))
665
666	mainNinjaFile := filepath.Join("$buildDir", "build.ninja")
667	primaryBuilderNinjaFile := filepath.Join(bootstrapDir, "build.ninja")
668	bootstrapNinjaFileTemplate := filepath.Join(miniBootstrapDir, "build.ninja.in")
669	bootstrapNinjaFile := filepath.Join(miniBootstrapDir, "build.ninja")
670	docsFile := filepath.Join(docsDir, primaryBuilderName+".html")
671
672	switch s.config.stage {
673	case StageBootstrap:
674		// We're generating a bootstrapper Ninja file, so we need to set things
675		// up to rebuild the build.ninja file using the primary builder.
676
677		// BuildDir must be different between the three stages, otherwise the
678		// cleanup process will remove files from the other builds.
679		ctx.SetNinjaBuildDir(pctx, miniBootstrapDir)
680
681		// Generate the Ninja file to build the primary builder.
682		ctx.Build(pctx, blueprint.BuildParams{
683			Rule:    generateBuildNinja,
684			Outputs: []string{primaryBuilderNinjaFile},
685			Inputs:  []string{topLevelBlueprints},
686			Args: map[string]string{
687				"builder": minibpFile,
688				"extra":   "--build-primary" + extraTestFlags,
689			},
690		})
691
692		// Rebuild the bootstrap Ninja file using the minibp that we just built.
693		ctx.Build(pctx, blueprint.BuildParams{
694			Rule:    generateBuildNinja,
695			Outputs: []string{bootstrapNinjaFileTemplate},
696			Inputs:  []string{topLevelBlueprints},
697			Args: map[string]string{
698				"builder": minibpFile,
699				"extra":   extraTestFlags,
700			},
701		})
702
703		ctx.Build(pctx, blueprint.BuildParams{
704			Rule:    bootstrap,
705			Outputs: []string{bootstrapNinjaFile},
706			Inputs:  []string{bootstrapNinjaFileTemplate},
707		})
708
709	case StagePrimary:
710		// We're generating a bootstrapper Ninja file, so we need to set things
711		// up to rebuild the build.ninja file using the primary builder.
712
713		// BuildDir must be different between the three stages, otherwise the
714		// cleanup process will remove files from the other builds.
715		ctx.SetNinjaBuildDir(pctx, bootstrapDir)
716
717		// Add a way to rebuild the primary build.ninja so that globs works
718		ctx.Build(pctx, blueprint.BuildParams{
719			Rule:    generateBuildNinja,
720			Outputs: []string{primaryBuilderNinjaFile},
721			Inputs:  []string{topLevelBlueprints},
722			Args: map[string]string{
723				"builder":   minibpFile,
724				"extra":     "--build-primary" + extraTestFlags,
725				"generator": "true",
726			},
727		})
728
729		// Build the main build.ninja
730		ctx.Build(pctx, blueprint.BuildParams{
731			Rule:    generateBuildNinja,
732			Outputs: []string{mainNinjaFile},
733			Inputs:  []string{topLevelBlueprints},
734			Args: map[string]string{
735				"builder": primaryBuilderFile,
736				"extra":   primaryBuilderExtraFlags,
737			},
738		})
739
740		// Generate build system docs for the primary builder.  Generating docs reads the source
741		// files used to build the primary builder, but that dependency will be picked up through
742		// the dependency on the primary builder itself.  There are no dependencies on the
743		// Blueprints files, as any relevant changes to the Blueprints files would have caused
744		// a rebuild of the primary builder.
745		bigbpDocs := ctx.Rule(pctx, "bigbpDocs",
746			blueprint.RuleParams{
747				Command: fmt.Sprintf("%s %s -b $buildDir --docs $out %s", primaryBuilderFile,
748					primaryBuilderExtraFlags, topLevelBlueprints),
749				CommandDeps: []string{primaryBuilderFile},
750				Description: fmt.Sprintf("%s docs $out", primaryBuilderName),
751			})
752
753		ctx.Build(pctx, blueprint.BuildParams{
754			Rule:    bigbpDocs,
755			Outputs: []string{docsFile},
756		})
757
758	case StageMain:
759		ctx.SetNinjaBuildDir(pctx, "${buildDir}")
760
761		// Add a way to rebuild the main build.ninja in case it creates rules that
762		// it will depend on itself. (In Android, globs with soong_glob)
763		ctx.Build(pctx, blueprint.BuildParams{
764			Rule:    generateBuildNinja,
765			Outputs: []string{mainNinjaFile},
766			Inputs:  []string{topLevelBlueprints},
767			Args: map[string]string{
768				"builder":   primaryBuilderFile,
769				"extra":     primaryBuilderExtraFlags,
770				"generator": "true",
771			},
772		})
773
774		if primaryBuilderName == "minibp" {
775			// This is a standalone Blueprint build, so we copy the minibp
776			// binary to the "bin" directory to make it easier to find.
777			finalMinibp := filepath.Join("$buildDir", "bin", primaryBuilderName)
778			ctx.Build(pctx, blueprint.BuildParams{
779				Rule:    cp,
780				Inputs:  []string{primaryBuilderFile},
781				Outputs: []string{finalMinibp},
782			})
783		}
784
785		ctx.Build(pctx, blueprint.BuildParams{
786			Rule:    blueprint.Phony,
787			Outputs: []string{"blueprint_tools"},
788			Inputs:  blueprintTools,
789		})
790	}
791}
792
793// packageRoot returns the module-specific package root directory path.  This
794// directory is where the final package .a files are output and where dependant
795// modules search for this package via -I arguments.
796func packageRoot(ctx blueprint.ModuleContext) string {
797	return filepath.Join(bootstrapDir, ctx.ModuleName(), "pkg")
798}
799
800// testRoot returns the module-specific package root directory path used for
801// building tests. The .a files generated here will include everything from
802// packageRoot, plus the test-only code.
803func testRoot(ctx blueprint.ModuleContext) string {
804	return filepath.Join(bootstrapDir, ctx.ModuleName(), "test")
805}
806
807// moduleSrcDir returns the path of the directory that all source file paths are
808// specified relative to.
809func moduleSrcDir(ctx blueprint.ModuleContext) string {
810	return filepath.Join("$srcDir", ctx.ModuleDir())
811}
812
813// moduleObjDir returns the module-specific object directory path.
814func moduleObjDir(ctx blueprint.ModuleContext) string {
815	return filepath.Join(bootstrapDir, ctx.ModuleName(), "obj")
816}
817
818// moduleGenSrcDir returns the module-specific generated sources path.
819func moduleGenSrcDir(ctx blueprint.ModuleContext) string {
820	return filepath.Join(bootstrapDir, ctx.ModuleName(), "gen")
821}
822