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	"strings"
21
22	"github.com/google/blueprint"
23	"github.com/google/blueprint/pathtools"
24)
25
26const bootstrapSubDir = ".bootstrap"
27const miniBootstrapSubDir = ".minibootstrap"
28
29var (
30	pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap")
31
32	goTestMainCmd   = pctx.StaticVariable("goTestMainCmd", filepath.Join(bootstrapDir, "bin", "gotestmain"))
33	goTestRunnerCmd = pctx.StaticVariable("goTestRunnerCmd", filepath.Join(bootstrapDir, "bin", "gotestrunner"))
34	chooseStageCmd  = pctx.StaticVariable("chooseStageCmd", filepath.Join(bootstrapDir, "bin", "choosestage"))
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	chooseStage = pctx.StaticRule("chooseStage",
94		blueprint.RuleParams{
95			Command:     "$chooseStageCmd --current $current --bootstrap $bootstrapManifest -o $out $in",
96			CommandDeps: []string{"$chooseStageCmd", "$bootstrapManifest"},
97			Description: "choosing next stage",
98		},
99		"current", "generator")
100
101	touch = pctx.StaticRule("touch",
102		blueprint.RuleParams{
103			Command:     "touch $out",
104			Description: "touch $out",
105		},
106		"depfile", "generator")
107
108	// Work around a Ninja issue.  See https://github.com/martine/ninja/pull/634
109	phony = pctx.StaticRule("phony",
110		blueprint.RuleParams{
111			Command:     "# phony $out",
112			Description: "phony $out",
113			Generator:   true,
114		},
115		"depfile")
116
117	binDir     = pctx.StaticVariable("BinDir", filepath.Join(bootstrapDir, "bin"))
118	minibpFile = filepath.Join("$BinDir", "minibp")
119
120	docsDir = filepath.Join(bootstrapDir, "docs")
121
122	bootstrapDir     = filepath.Join("$buildDir", bootstrapSubDir)
123	miniBootstrapDir = filepath.Join("$buildDir", miniBootstrapSubDir)
124)
125
126type bootstrapGoCore interface {
127	BuildStage() Stage
128	SetBuildStage(Stage)
129}
130
131func propagateStageBootstrap(mctx blueprint.TopDownMutatorContext) {
132	if mod, ok := mctx.Module().(bootstrapGoCore); !ok || mod.BuildStage() != StageBootstrap {
133		return
134	}
135
136	mctx.VisitDirectDeps(func(mod blueprint.Module) {
137		if m, ok := mod.(bootstrapGoCore); ok {
138			m.SetBuildStage(StageBootstrap)
139		}
140	})
141}
142
143func pluginDeps(ctx blueprint.BottomUpMutatorContext) {
144	if pkg, ok := ctx.Module().(*goPackage); ok {
145		for _, plugin := range pkg.properties.PluginFor {
146			ctx.AddReverseDependency(ctx.Module(), plugin)
147		}
148	}
149}
150
151type goPackageProducer interface {
152	GoPkgRoot() string
153	GoPackageTarget() string
154}
155
156func isGoPackageProducer(module blueprint.Module) bool {
157	_, ok := module.(goPackageProducer)
158	return ok
159}
160
161type goTestProducer interface {
162	GoTestTarget() string
163	BuildStage() Stage
164}
165
166func isGoTestProducer(module blueprint.Module) bool {
167	_, ok := module.(goTestProducer)
168	return ok
169}
170
171type goPluginProvider interface {
172	GoPkgPath() string
173	IsPluginFor(string) bool
174}
175
176func isGoPluginFor(name string) func(blueprint.Module) bool {
177	return func(module blueprint.Module) bool {
178		if plugin, ok := module.(goPluginProvider); ok {
179			return plugin.IsPluginFor(name)
180		}
181		return false
182	}
183}
184
185func isBootstrapModule(module blueprint.Module) bool {
186	_, isPackage := module.(*goPackage)
187	_, isBinary := module.(*goBinary)
188	return isPackage || isBinary
189}
190
191func isBootstrapBinaryModule(module blueprint.Module) bool {
192	_, isBinary := module.(*goBinary)
193	return isBinary
194}
195
196// A goPackage is a module for building Go packages.
197type goPackage struct {
198	properties struct {
199		PkgPath   string
200		Srcs      []string
201		TestSrcs  []string
202		PluginFor []string
203	}
204
205	// The root dir in which the package .a file is located.  The full .a file
206	// path will be "packageRoot/PkgPath.a"
207	pkgRoot string
208
209	// The path of the .a file that is to be built.
210	archiveFile string
211
212	// The path of the test .a file that is to be built.
213	testArchiveFile string
214
215	// The bootstrap Config
216	config *Config
217
218	// The stage in which this module should be built
219	buildStage Stage
220}
221
222var _ goPackageProducer = (*goPackage)(nil)
223
224func newGoPackageModuleFactory(config *Config) func() (blueprint.Module, []interface{}) {
225	return func() (blueprint.Module, []interface{}) {
226		module := &goPackage{
227			buildStage: StagePrimary,
228			config:     config,
229		}
230		return module, []interface{}{&module.properties}
231	}
232}
233
234func (g *goPackage) GoPkgPath() string {
235	return g.properties.PkgPath
236}
237
238func (g *goPackage) GoPkgRoot() string {
239	return g.pkgRoot
240}
241
242func (g *goPackage) GoPackageTarget() string {
243	return g.archiveFile
244}
245
246func (g *goPackage) GoTestTarget() string {
247	return g.testArchiveFile
248}
249
250func (g *goPackage) BuildStage() Stage {
251	return g.buildStage
252}
253
254func (g *goPackage) SetBuildStage(buildStage Stage) {
255	g.buildStage = buildStage
256}
257
258func (g *goPackage) IsPluginFor(name string) bool {
259	for _, plugin := range g.properties.PluginFor {
260		if plugin == name {
261			return true
262		}
263	}
264	return false
265}
266
267func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) {
268	var (
269		name       = ctx.ModuleName()
270		hasPlugins = false
271		pluginSrc  = ""
272		genSrcs    = []string{}
273	)
274
275	if g.properties.PkgPath == "" {
276		ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name)
277		return
278	}
279
280	g.pkgRoot = packageRoot(ctx)
281	g.archiveFile = filepath.Join(g.pkgRoot,
282		filepath.FromSlash(g.properties.PkgPath)+".a")
283	if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
284		g.testArchiveFile = filepath.Join(testRoot(ctx),
285			filepath.FromSlash(g.properties.PkgPath)+".a")
286	}
287
288	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
289		func(module blueprint.Module) { hasPlugins = true })
290	if hasPlugins {
291		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
292		genSrcs = append(genSrcs, pluginSrc)
293	}
294
295	// We only actually want to build the builder modules if we're running as
296	// minibp (i.e. we're generating a bootstrap Ninja file).  This is to break
297	// the circular dependence that occurs when the builder requires a new Ninja
298	// file to be built, but building a new ninja file requires the builder to
299	// be built.
300	if g.config.stage == g.BuildStage() {
301		var deps []string
302
303		if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc, g.config.stage) {
304			return
305		}
306
307		if g.config.runGoTests {
308			deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
309				g.properties.PkgPath, g.properties.Srcs, genSrcs,
310				g.properties.TestSrcs)
311		}
312
313		buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile,
314			g.properties.Srcs, genSrcs, deps)
315	} else if g.config.stage != StageBootstrap {
316		if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
317			phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil, nil)
318		}
319		phonyGoTarget(ctx, g.archiveFile, g.properties.Srcs, genSrcs, nil)
320	}
321}
322
323// A goBinary is a module for building executable binaries from Go sources.
324type goBinary struct {
325	properties struct {
326		Srcs           []string
327		TestSrcs       []string
328		PrimaryBuilder bool
329	}
330
331	// The path of the test .a file that is to be built.
332	testArchiveFile string
333
334	// The bootstrap Config
335	config *Config
336
337	// The stage in which this module should be built
338	buildStage Stage
339}
340
341func newGoBinaryModuleFactory(config *Config, buildStage Stage) func() (blueprint.Module, []interface{}) {
342	return func() (blueprint.Module, []interface{}) {
343		module := &goBinary{
344			config:     config,
345			buildStage: buildStage,
346		}
347		return module, []interface{}{&module.properties}
348	}
349}
350
351func (g *goBinary) GoTestTarget() string {
352	return g.testArchiveFile
353}
354
355func (g *goBinary) BuildStage() Stage {
356	return g.buildStage
357}
358
359func (g *goBinary) SetBuildStage(buildStage Stage) {
360	g.buildStage = buildStage
361}
362
363func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
364	var (
365		name        = ctx.ModuleName()
366		objDir      = moduleObjDir(ctx)
367		archiveFile = filepath.Join(objDir, name+".a")
368		aoutFile    = filepath.Join(objDir, "a.out")
369		binaryFile  = filepath.Join("$BinDir", name)
370		hasPlugins  = false
371		pluginSrc   = ""
372		genSrcs     = []string{}
373	)
374
375	if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
376		g.testArchiveFile = filepath.Join(testRoot(ctx), name+".a")
377	}
378
379	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
380		func(module blueprint.Module) { hasPlugins = true })
381	if hasPlugins {
382		pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
383		genSrcs = append(genSrcs, pluginSrc)
384	}
385
386	// We only actually want to build the builder modules if we're running as
387	// minibp (i.e. we're generating a bootstrap Ninja file).  This is to break
388	// the circular dependence that occurs when the builder requires a new Ninja
389	// file to be built, but building a new ninja file requires the builder to
390	// be built.
391	if g.config.stage == g.BuildStage() {
392		var deps []string
393
394		if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc, g.config.stage) {
395			return
396		}
397
398		if g.config.runGoTests {
399			deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile,
400				name, g.properties.Srcs, genSrcs, g.properties.TestSrcs)
401		}
402
403		buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs, genSrcs, deps)
404
405		var libDirFlags []string
406		ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
407			func(module blueprint.Module) {
408				dep := module.(goPackageProducer)
409				libDir := dep.GoPkgRoot()
410				libDirFlags = append(libDirFlags, "-L "+libDir)
411			})
412
413		linkArgs := map[string]string{}
414		if len(libDirFlags) > 0 {
415			linkArgs["libDirFlags"] = strings.Join(libDirFlags, " ")
416		}
417
418		ctx.Build(pctx, blueprint.BuildParams{
419			Rule:    link,
420			Outputs: []string{aoutFile},
421			Inputs:  []string{archiveFile},
422			Args:    linkArgs,
423		})
424
425		ctx.Build(pctx, blueprint.BuildParams{
426			Rule:    cp,
427			Outputs: []string{binaryFile},
428			Inputs:  []string{aoutFile},
429		})
430	} else if g.config.stage != StageBootstrap {
431		if len(g.properties.TestSrcs) > 0 && g.config.runGoTests {
432			phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil, nil)
433		}
434
435		intermediates := []string{aoutFile, archiveFile}
436		phonyGoTarget(ctx, binaryFile, g.properties.Srcs, genSrcs, intermediates)
437	}
438}
439
440func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string, stage Stage) bool {
441	ret := true
442	name := ctx.ModuleName()
443
444	var pluginPaths []string
445	ctx.VisitDepsDepthFirstIf(isGoPluginFor(name),
446		func(module blueprint.Module) {
447			plugin := module.(goPluginProvider)
448			pluginPaths = append(pluginPaths, plugin.GoPkgPath())
449			if stage == StageBootstrap {
450				ctx.OtherModuleErrorf(module, "plugin %q may not be included in core module %q",
451					ctx.OtherModuleName(module), name)
452				ret = false
453			}
454		})
455
456	ctx.Build(pctx, blueprint.BuildParams{
457		Rule:    pluginGenSrc,
458		Outputs: []string{pluginSrc},
459		Args: map[string]string{
460			"pkg":     pkgPath,
461			"plugins": strings.Join(pluginPaths, " "),
462		},
463	})
464
465	return ret
466}
467
468func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string,
469	pkgPath string, archiveFile string, srcs []string, genSrcs []string, orderDeps []string) {
470
471	srcDir := moduleSrcDir(ctx)
472	srcFiles := pathtools.PrefixPaths(srcs, srcDir)
473	srcFiles = append(srcFiles, genSrcs...)
474
475	var incFlags []string
476	var deps []string
477	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
478		func(module blueprint.Module) {
479			dep := module.(goPackageProducer)
480			incDir := dep.GoPkgRoot()
481			target := dep.GoPackageTarget()
482			incFlags = append(incFlags, "-I "+incDir)
483			deps = append(deps, target)
484		})
485
486	compileArgs := map[string]string{
487		"pkgPath": pkgPath,
488	}
489
490	if len(incFlags) > 0 {
491		compileArgs["incFlags"] = strings.Join(incFlags, " ")
492	}
493
494	ctx.Build(pctx, blueprint.BuildParams{
495		Rule:      compile,
496		Outputs:   []string{archiveFile},
497		Inputs:    srcFiles,
498		OrderOnly: orderDeps,
499		Implicits: deps,
500		Args:      compileArgs,
501	})
502}
503
504func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive,
505	pkgPath string, srcs, genSrcs, testSrcs []string) []string {
506
507	if len(testSrcs) == 0 {
508		return nil
509	}
510
511	srcDir := moduleSrcDir(ctx)
512	testFiles := pathtools.PrefixPaths(testSrcs, srcDir)
513
514	mainFile := filepath.Join(testRoot, "test.go")
515	testArchive := filepath.Join(testRoot, "test.a")
516	testFile := filepath.Join(testRoot, "test")
517	testPassed := filepath.Join(testRoot, "test.passed")
518
519	buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive,
520		append(srcs, testSrcs...), genSrcs, nil)
521
522	ctx.Build(pctx, blueprint.BuildParams{
523		Rule:    goTestMain,
524		Outputs: []string{mainFile},
525		Inputs:  testFiles,
526		Args: map[string]string{
527			"pkg": pkgPath,
528		},
529	})
530
531	libDirFlags := []string{"-L " + testRoot}
532	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
533		func(module blueprint.Module) {
534			dep := module.(goPackageProducer)
535			libDir := dep.GoPkgRoot()
536			libDirFlags = append(libDirFlags, "-L "+libDir)
537		})
538
539	ctx.Build(pctx, blueprint.BuildParams{
540		Rule:      compile,
541		Outputs:   []string{testArchive},
542		Inputs:    []string{mainFile},
543		Implicits: []string{testPkgArchive},
544		Args: map[string]string{
545			"pkgPath":  "main",
546			"incFlags": "-I " + testRoot,
547		},
548	})
549
550	ctx.Build(pctx, blueprint.BuildParams{
551		Rule:    link,
552		Outputs: []string{testFile},
553		Inputs:  []string{testArchive},
554		Args: map[string]string{
555			"libDirFlags": strings.Join(libDirFlags, " "),
556		},
557	})
558
559	ctx.Build(pctx, blueprint.BuildParams{
560		Rule:    test,
561		Outputs: []string{testPassed},
562		Inputs:  []string{testFile},
563		Args: map[string]string{
564			"pkg":       pkgPath,
565			"pkgSrcDir": filepath.Dir(testFiles[0]),
566		},
567	})
568
569	return []string{testPassed}
570}
571
572func phonyGoTarget(ctx blueprint.ModuleContext, target string, srcs []string,
573	gensrcs []string, intermediates []string) {
574
575	var depTargets []string
576	ctx.VisitDepsDepthFirstIf(isGoPackageProducer,
577		func(module blueprint.Module) {
578			dep := module.(goPackageProducer)
579			target := dep.GoPackageTarget()
580			depTargets = append(depTargets, target)
581		})
582
583	moduleDir := ctx.ModuleDir()
584	srcs = pathtools.PrefixPaths(srcs, filepath.Join("$srcDir", moduleDir))
585	srcs = append(srcs, gensrcs...)
586
587	ctx.Build(pctx, blueprint.BuildParams{
588		Rule:      phony,
589		Outputs:   []string{target},
590		Inputs:    srcs,
591		Implicits: depTargets,
592	})
593
594	// If one of the source files gets deleted or renamed that will prevent the
595	// re-bootstrapping happening because it depends on the missing source file.
596	// To get around this we add a build statement using the built-in phony rule
597	// for each source file, which will cause Ninja to treat it as dirty if its
598	// missing.
599	for _, src := range srcs {
600		ctx.Build(pctx, blueprint.BuildParams{
601			Rule:    blueprint.Phony,
602			Outputs: []string{src},
603		})
604	}
605
606	// If there is no rule to build the intermediate files of a bootstrap go package
607	// the cleanup phase of the primary builder will delete the intermediate files,
608	// forcing an unnecessary rebuild.  Add phony rules for all of them.
609	for _, intermediate := range intermediates {
610		ctx.Build(pctx, blueprint.BuildParams{
611			Rule:    blueprint.Phony,
612			Outputs: []string{intermediate},
613		})
614	}
615
616}
617
618type singleton struct {
619	// The bootstrap Config
620	config *Config
621}
622
623func newSingletonFactory(config *Config) func() blueprint.Singleton {
624	return func() blueprint.Singleton {
625		return &singleton{
626			config: config,
627		}
628	}
629}
630
631func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
632	// Find the module that's marked as the "primary builder", which means it's
633	// creating the binary that we'll use to generate the non-bootstrap
634	// build.ninja file.
635	var primaryBuilders []*goBinary
636	// rebootstrapDeps contains modules that will be built in StageBootstrap
637	var rebootstrapDeps []string
638	// primaryRebootstrapDeps contains modules that will be built in StagePrimary
639	var primaryRebootstrapDeps []string
640	ctx.VisitAllModulesIf(isBootstrapBinaryModule,
641		func(module blueprint.Module) {
642			binaryModule := module.(*goBinary)
643			binaryModuleName := ctx.ModuleName(binaryModule)
644			binaryModulePath := filepath.Join("$BinDir", binaryModuleName)
645
646			if binaryModule.BuildStage() == StageBootstrap {
647				rebootstrapDeps = append(rebootstrapDeps, binaryModulePath)
648			} else {
649				primaryRebootstrapDeps = append(primaryRebootstrapDeps, binaryModulePath)
650			}
651			if binaryModule.properties.PrimaryBuilder {
652				primaryBuilders = append(primaryBuilders, binaryModule)
653			}
654		})
655
656	var primaryBuilderName, primaryBuilderExtraFlags string
657	switch len(primaryBuilders) {
658	case 0:
659		// If there's no primary builder module then that means we'll use minibp
660		// as the primary builder.  We can trigger its primary builder mode with
661		// the -p flag.
662		primaryBuilderName = "minibp"
663		primaryBuilderExtraFlags = "-p"
664
665	case 1:
666		primaryBuilderName = ctx.ModuleName(primaryBuilders[0])
667
668	default:
669		ctx.Errorf("multiple primary builder modules present:")
670		for _, primaryBuilder := range primaryBuilders {
671			ctx.ModuleErrorf(primaryBuilder, "<-- module %s",
672				ctx.ModuleName(primaryBuilder))
673		}
674		return
675	}
676
677	primaryBuilderFile := filepath.Join("$BinDir", primaryBuilderName)
678
679	if s.config.runGoTests {
680		primaryBuilderExtraFlags += " -t"
681	}
682
683	// Get the filename of the top-level Blueprints file to pass to minibp.
684	topLevelBlueprints := filepath.Join("$srcDir",
685		filepath.Base(s.config.topLevelBlueprintsFile))
686
687	rebootstrapDeps = append(rebootstrapDeps, topLevelBlueprints)
688	primaryRebootstrapDeps = append(primaryRebootstrapDeps, topLevelBlueprints)
689
690	mainNinjaFile := filepath.Join(bootstrapDir, "main.ninja.in")
691	mainNinjaTimestampFile := mainNinjaFile + ".timestamp"
692	mainNinjaTimestampDepFile := mainNinjaTimestampFile + ".d"
693	primaryBuilderNinjaFile := filepath.Join(bootstrapDir, "primary.ninja.in")
694	primaryBuilderNinjaTimestampFile := primaryBuilderNinjaFile + ".timestamp"
695	primaryBuilderNinjaTimestampDepFile := primaryBuilderNinjaTimestampFile + ".d"
696	bootstrapNinjaFile := filepath.Join(bootstrapDir, "bootstrap.ninja.in")
697	docsFile := filepath.Join(docsDir, primaryBuilderName+".html")
698
699	primaryRebootstrapDeps = append(primaryRebootstrapDeps, docsFile)
700
701	// If the tests change, be sure to re-run them. These need to be
702	// dependencies for the ninja file so that it's updated after these
703	// run. Otherwise we'd never leave the bootstrap stage, since the
704	// timestamp file would be newer than the ninja file.
705	ctx.VisitAllModulesIf(isGoTestProducer,
706		func(module blueprint.Module) {
707			testModule := module.(goTestProducer)
708			target := testModule.GoTestTarget()
709			if target != "" {
710				if testModule.BuildStage() == StageBootstrap {
711					rebootstrapDeps = append(rebootstrapDeps, target)
712				} else {
713					primaryRebootstrapDeps = append(primaryRebootstrapDeps, target)
714				}
715			}
716		})
717
718	switch s.config.stage {
719	case StageBootstrap:
720		// We're generating a bootstrapper Ninja file, so we need to set things
721		// up to rebuild the build.ninja file using the primary builder.
722
723		// BuildDir must be different between the three stages, otherwise the
724		// cleanup process will remove files from the other builds.
725		ctx.SetNinjaBuildDir(pctx, miniBootstrapDir)
726
727		// Generate the Ninja file to build the primary builder. Save the
728		// timestamps and deps, so that we can come back to this stage if
729		// it needs to be regenerated.
730		primarybp := ctx.Rule(pctx, "primarybp",
731			blueprint.RuleParams{
732				Command: fmt.Sprintf("%s --build-primary $runTests -m $bootstrapManifest "+
733					"--timestamp $timestamp --timestampdep $timestampdep "+
734					"-b $buildDir -d $outfile.d -o $outfile $in", minibpFile),
735				Description: "minibp $outfile",
736				Depfile:     "$outfile.d",
737			},
738			"runTests", "timestamp", "timestampdep", "outfile")
739
740		args := map[string]string{
741			"outfile":      primaryBuilderNinjaFile,
742			"timestamp":    primaryBuilderNinjaTimestampFile,
743			"timestampdep": primaryBuilderNinjaTimestampDepFile,
744		}
745
746		if s.config.runGoTests {
747			args["runTests"] = "-t"
748		}
749
750		ctx.Build(pctx, blueprint.BuildParams{
751			Rule:      primarybp,
752			Outputs:   []string{primaryBuilderNinjaFile, primaryBuilderNinjaTimestampFile},
753			Inputs:    []string{topLevelBlueprints},
754			Implicits: rebootstrapDeps,
755			Args:      args,
756		})
757
758		// Rebuild the bootstrap Ninja file using the minibp that we just built.
759		// If this produces a difference, choosestage will retrigger this stage.
760		minibp := ctx.Rule(pctx, "minibp",
761			blueprint.RuleParams{
762				Command: fmt.Sprintf("%s $runTests -m $bootstrapManifest "+
763					"-b $buildDir -d $out.d -o $out $in", minibpFile),
764				// $bootstrapManifest is here so that when it is updated, we
765				// force a rebuild of bootstrap.ninja.in. chooseStage should
766				// have already copied the new version over, but kept the old
767				// timestamps to force this regeneration.
768				CommandDeps: []string{"$bootstrapManifest", minibpFile},
769				Description: "minibp $out",
770				Generator:   true,
771				Depfile:     "$out.d",
772			},
773			"runTests")
774
775		args = map[string]string{}
776
777		if s.config.runGoTests {
778			args["runTests"] = "-t"
779		}
780
781		ctx.Build(pctx, blueprint.BuildParams{
782			Rule:    minibp,
783			Outputs: []string{bootstrapNinjaFile},
784			Inputs:  []string{topLevelBlueprints},
785			Args:    args,
786		})
787
788		// When the current build.ninja file is a bootstrapper, we always want
789		// to have it replace itself with a non-bootstrapper build.ninja.  To
790		// accomplish that we depend on a file that should never exist and
791		// "build" it using Ninja's built-in phony rule.
792		notAFile := filepath.Join(bootstrapDir, "notAFile")
793		ctx.Build(pctx, blueprint.BuildParams{
794			Rule:    blueprint.Phony,
795			Outputs: []string{notAFile},
796		})
797
798		ctx.Build(pctx, blueprint.BuildParams{
799			Rule:      chooseStage,
800			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
801			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile},
802			Implicits: []string{notAFile},
803			Args: map[string]string{
804				"current": bootstrapNinjaFile,
805			},
806		})
807
808	case StagePrimary:
809		// We're generating a bootstrapper Ninja file, so we need to set things
810		// up to rebuild the build.ninja file using the primary builder.
811
812		// BuildDir must be different between the three stages, otherwise the
813		// cleanup process will remove files from the other builds.
814		ctx.SetNinjaBuildDir(pctx, bootstrapDir)
815
816		// We generate the depfile here that includes the dependencies for all
817		// the Blueprints files that contribute to generating the big build
818		// manifest (build.ninja file).  This depfile will be used by the non-
819		// bootstrap build manifest to determine whether it should touch the
820		// timestamp file to trigger a re-bootstrap.
821		bigbp := ctx.Rule(pctx, "bigbp",
822			blueprint.RuleParams{
823				Command: fmt.Sprintf("%s %s -m $bootstrapManifest "+
824					"--timestamp $timestamp --timestampdep $timestampdep "+
825					"-b $buildDir -d $outfile.d -o $outfile $in", primaryBuilderFile,
826					primaryBuilderExtraFlags),
827				Description: fmt.Sprintf("%s $outfile", primaryBuilderName),
828				Depfile:     "$outfile.d",
829			},
830			"timestamp", "timestampdep", "outfile")
831
832		ctx.Build(pctx, blueprint.BuildParams{
833			Rule:      bigbp,
834			Outputs:   []string{mainNinjaFile, mainNinjaTimestampFile},
835			Inputs:    []string{topLevelBlueprints},
836			Implicits: primaryRebootstrapDeps,
837			Args: map[string]string{
838				"timestamp":    mainNinjaTimestampFile,
839				"timestampdep": mainNinjaTimestampDepFile,
840				"outfile":      mainNinjaFile,
841			},
842		})
843
844		// Generate build system docs for the primary builder.  Generating docs reads the source
845		// files used to build the primary builder, but that dependency will be picked up through
846		// the dependency on the primary builder itself.  There are no dependencies on the
847		// Blueprints files, as any relevant changes to the Blueprints files would have caused
848		// a rebuild of the primary builder.
849		bigbpDocs := ctx.Rule(pctx, "bigbpDocs",
850			blueprint.RuleParams{
851				Command: fmt.Sprintf("%s %s -b $buildDir --docs $out %s", primaryBuilderFile,
852					primaryBuilderExtraFlags, topLevelBlueprints),
853				CommandDeps: []string{primaryBuilderFile},
854				Description: fmt.Sprintf("%s docs $out", primaryBuilderName),
855			})
856
857		ctx.Build(pctx, blueprint.BuildParams{
858			Rule:    bigbpDocs,
859			Outputs: []string{docsFile},
860		})
861
862		// Detect whether we need to rebuild the primary stage by going back to
863		// the bootstrapper. If this is newer than the primaryBuilderNinjaFile,
864		// then chooseStage will trigger a rebuild of primaryBuilderNinjaFile by
865		// returning to the bootstrap stage.
866		ctx.Build(pctx, blueprint.BuildParams{
867			Rule:      touch,
868			Outputs:   []string{primaryBuilderNinjaTimestampFile},
869			Implicits: rebootstrapDeps,
870			Args: map[string]string{
871				"depfile":   primaryBuilderNinjaTimestampDepFile,
872				"generator": "true",
873			},
874		})
875
876		// When the current build.ninja file is a bootstrapper, we always want
877		// to have it replace itself with a non-bootstrapper build.ninja.  To
878		// accomplish that we depend on a file that should never exist and
879		// "build" it using Ninja's built-in phony rule.
880		notAFile := filepath.Join(bootstrapDir, "notAFile")
881		ctx.Build(pctx, blueprint.BuildParams{
882			Rule:    blueprint.Phony,
883			Outputs: []string{notAFile},
884		})
885
886		ctx.Build(pctx, blueprint.BuildParams{
887			Rule:      chooseStage,
888			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
889			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile, mainNinjaFile},
890			Implicits: []string{notAFile, primaryBuilderNinjaTimestampFile},
891			Args: map[string]string{
892				"current": primaryBuilderNinjaFile,
893			},
894		})
895
896		// Create this phony rule so that upgrades don't delete these during
897		// cleanup
898		ctx.Build(pctx, blueprint.BuildParams{
899			Rule:    blueprint.Phony,
900			Outputs: []string{bootstrapNinjaFile},
901		})
902
903	case StageMain:
904		ctx.SetNinjaBuildDir(pctx, "${buildDir}")
905
906		// We're generating a non-bootstrapper Ninja file, so we need to set it
907		// up to re-bootstrap if necessary. We do this by making build.ninja.in
908		// depend on the various Ninja files, the source build.ninja.in, and
909		// on the timestamp files.
910		//
911		// The timestamp files themselves are set up with the same dependencies
912		// as their Ninja files, including their own depfile. If any of the
913		// dependencies need to be updated, we'll touch the timestamp file,
914		// which will tell choosestage to switch to the stage that rebuilds
915		// that Ninja file.
916		ctx.Build(pctx, blueprint.BuildParams{
917			Rule:      touch,
918			Outputs:   []string{primaryBuilderNinjaTimestampFile},
919			Implicits: rebootstrapDeps,
920			Args: map[string]string{
921				"depfile":   primaryBuilderNinjaTimestampDepFile,
922				"generator": "true",
923			},
924		})
925
926		ctx.Build(pctx, blueprint.BuildParams{
927			Rule:      touch,
928			Outputs:   []string{mainNinjaTimestampFile},
929			Implicits: primaryRebootstrapDeps,
930			Args: map[string]string{
931				"depfile":   mainNinjaTimestampDepFile,
932				"generator": "true",
933			},
934		})
935
936		ctx.Build(pctx, blueprint.BuildParams{
937			Rule:      chooseStage,
938			Outputs:   []string{filepath.Join(bootstrapDir, "build.ninja.in")},
939			Inputs:    []string{bootstrapNinjaFile, primaryBuilderNinjaFile, mainNinjaFile},
940			Implicits: []string{primaryBuilderNinjaTimestampFile, mainNinjaTimestampFile},
941			Args: map[string]string{
942				"current":   mainNinjaFile,
943				"generator": "true",
944			},
945		})
946
947		// Create this phony rule so that upgrades don't delete these during
948		// cleanup
949		ctx.Build(pctx, blueprint.BuildParams{
950			Rule:    blueprint.Phony,
951			Outputs: []string{mainNinjaFile, docsFile, "$bootstrapManifest"},
952		})
953
954		if primaryBuilderName == "minibp" {
955			// This is a standalone Blueprint build, so we copy the minibp
956			// binary to the "bin" directory to make it easier to find.
957			finalMinibp := filepath.Join("$buildDir", "bin", primaryBuilderName)
958			ctx.Build(pctx, blueprint.BuildParams{
959				Rule:    cp,
960				Inputs:  []string{primaryBuilderFile},
961				Outputs: []string{finalMinibp},
962			})
963		}
964	}
965
966	ctx.Build(pctx, blueprint.BuildParams{
967		Rule:    bootstrap,
968		Outputs: []string{"$buildDir/build.ninja"},
969		Inputs:  []string{filepath.Join(bootstrapDir, "build.ninja.in")},
970	})
971}
972
973// packageRoot returns the module-specific package root directory path.  This
974// directory is where the final package .a files are output and where dependant
975// modules search for this package via -I arguments.
976func packageRoot(ctx blueprint.ModuleContext) string {
977	return filepath.Join(bootstrapDir, ctx.ModuleName(), "pkg")
978}
979
980// testRoot returns the module-specific package root directory path used for
981// building tests. The .a files generated here will include everything from
982// packageRoot, plus the test-only code.
983func testRoot(ctx blueprint.ModuleContext) string {
984	return filepath.Join(bootstrapDir, ctx.ModuleName(), "test")
985}
986
987// moduleSrcDir returns the path of the directory that all source file paths are
988// specified relative to.
989func moduleSrcDir(ctx blueprint.ModuleContext) string {
990	return filepath.Join("$srcDir", ctx.ModuleDir())
991}
992
993// moduleObjDir returns the module-specific object directory path.
994func moduleObjDir(ctx blueprint.ModuleContext) string {
995	return filepath.Join(bootstrapDir, ctx.ModuleName(), "obj")
996}
997
998// moduleGenSrcDir returns the module-specific generated sources path.
999func moduleGenSrcDir(ctx blueprint.ModuleContext) string {
1000	return filepath.Join(bootstrapDir, ctx.ModuleName(), "gen")
1001}
1002