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