func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string, pkgPath string, archiveFile string, srcs []string, genSrcs []string, orderDeps []string) { srcDir := moduleSrcDir(ctx) srcFiles := pathtools.PrefixPaths(srcs, srcDir) srcFiles = append(srcFiles, genSrcs...) var incFlags []string deps := []string{"$compileCmd"} ctx.VisitDepsDepthFirstIf(isGoPackageProducer, func(module blueprint.Module) { dep := module.(goPackageProducer) incDir := dep.GoPkgRoot() target := dep.GoPackageTarget() incFlags = append(incFlags, "-I "+incDir) deps = append(deps, target) }) compileArgs := map[string]string{ "pkgPath": pkgPath, } if len(incFlags) > 0 { compileArgs["incFlags"] = strings.Join(incFlags, " ") } ctx.Build(pctx, blueprint.BuildParams{ Rule: compile, Outputs: []string{archiveFile}, Inputs: srcFiles, OrderOnly: orderDeps, Implicits: deps, Args: compileArgs, }) }
func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string, stage Stage) bool { ret := true name := ctx.ModuleName() var pluginPaths []string ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), func(module blueprint.Module) { plugin := module.(goPluginProvider) pluginPaths = append(pluginPaths, plugin.GoPkgPath()) if stage == StageBootstrap { ctx.OtherModuleErrorf(module, "plugin %q may not be included in core module %q", ctx.OtherModuleName(module), name) ret = false } }) ctx.Build(pctx, blueprint.BuildParams{ Rule: pluginGenSrc, Outputs: []string{pluginSrc}, Implicits: []string{"$pluginGenSrcCmd"}, Args: map[string]string{ "pkg": pkgPath, "plugins": strings.Join(pluginPaths, " "), }, }) return ret }
func phonyGoTarget(ctx blueprint.ModuleContext, target string, srcs []string, gensrcs []string, intermediates []string) { var depTargets []string ctx.VisitDepsDepthFirstIf(isGoPackageProducer, func(module blueprint.Module) { dep := module.(goPackageProducer) target := dep.GoPackageTarget() depTargets = append(depTargets, target) }) moduleDir := ctx.ModuleDir() srcs = pathtools.PrefixPaths(srcs, filepath.Join("$srcDir", moduleDir)) srcs = append(srcs, gensrcs...) ctx.Build(pctx, blueprint.BuildParams{ Rule: phony, Outputs: []string{target}, Inputs: srcs, Implicits: depTargets, }) // If one of the source files gets deleted or renamed that will prevent the // re-bootstrapping happening because it depends on the missing source file. // To get around this we add a build statement using the built-in phony rule // for each source file, which will cause Ninja to treat it as dirty if its // missing. for _, src := range srcs { ctx.Build(pctx, blueprint.BuildParams{ Rule: blueprint.Phony, Outputs: []string{src}, }) } // If there is no rule to build the intermediate files of a bootstrap go package // the cleanup phase of the primary builder will delete the intermediate files, // forcing an unnecessary rebuild. Add phony rules for all of them. for _, intermediate := range intermediates { ctx.Build(pctx, blueprint.BuildParams{ Rule: blueprint.Phony, Outputs: []string{intermediate}, }) } }
func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) { name := ctx.ModuleName() if g.properties.PkgPath == "" { ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name) return } g.pkgRoot = packageRoot(ctx) g.archiveFile = filepath.Join(g.pkgRoot, filepath.FromSlash(g.properties.PkgPath)+".a") if len(g.properties.TestSrcs) > 0 && g.config.runGoTests { g.testArchiveFile = filepath.Join(testRoot(ctx), filepath.FromSlash(g.properties.PkgPath)+".a") } // We only actually want to build the builder modules if we're running as // minibp (i.e. we're generating a bootstrap Ninja file). This is to break // the circular dependence that occurs when the builder requires a new Ninja // file to be built, but building a new ninja file requires the builder to // be built. if g.config.generatingBootstrapper { var deps []string if g.config.runGoTests { deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile, g.properties.PkgPath, g.properties.Srcs, g.properties.TestSrcs) } buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile, g.properties.Srcs, deps) } else { if len(g.properties.TestSrcs) > 0 && g.config.runGoTests { phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil) } phonyGoTarget(ctx, g.archiveFile, g.properties.Srcs, nil) } }
func (g *goPackage) GenerateBuildActions(ctx blueprint.ModuleContext) { var ( name = ctx.ModuleName() hasPlugins = false pluginSrc = "" genSrcs = []string{} ) if g.properties.PkgPath == "" { ctx.ModuleErrorf("module %s did not specify a valid pkgPath", name) return } g.pkgRoot = packageRoot(ctx) g.archiveFile = filepath.Join(g.pkgRoot, filepath.FromSlash(g.properties.PkgPath)+".a") if len(g.properties.TestSrcs) > 0 && g.config.runGoTests { g.testArchiveFile = filepath.Join(testRoot(ctx), filepath.FromSlash(g.properties.PkgPath)+".a") } ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), func(module blueprint.Module) { hasPlugins = true }) if hasPlugins { pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go") genSrcs = append(genSrcs, pluginSrc) } // We only actually want to build the builder modules if we're running as // minibp (i.e. we're generating a bootstrap Ninja file). This is to break // the circular dependence that occurs when the builder requires a new Ninja // file to be built, but building a new ninja file requires the builder to // be built. if g.config.stage == g.BuildStage() { var deps []string if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc, g.config.stage) { return } if g.config.runGoTests { deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile, g.properties.PkgPath, g.properties.Srcs, genSrcs, g.properties.TestSrcs) } buildGoPackage(ctx, g.pkgRoot, g.properties.PkgPath, g.archiveFile, g.properties.Srcs, genSrcs, deps) } else if g.config.stage != StageBootstrap { if len(g.properties.TestSrcs) > 0 && g.config.runGoTests { phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil, nil) } phonyGoTarget(ctx, g.archiveFile, g.properties.Srcs, genSrcs, nil) } }
// moduleGenSrcDir returns the module-specific generated sources path. func moduleGenSrcDir(ctx blueprint.ModuleContext) string { return filepath.Join(bootstrapDir, ctx.ModuleName(), "gen") }
// moduleObjDir returns the module-specific object directory path. func moduleObjDir(ctx blueprint.ModuleContext) string { return filepath.Join(bootstrapDir, ctx.ModuleName(), "obj") }
// moduleSrcDir returns the path of the directory that all source file paths are // specified relative to. func moduleSrcDir(ctx blueprint.ModuleContext) string { return filepath.Join("$srcDir", ctx.ModuleDir()) }
// testRoot returns the module-specific package root directory path used for // building tests. The .a files generated here will include everything from // packageRoot, plus the test-only code. func testRoot(ctx blueprint.ModuleContext) string { return filepath.Join(bootstrapDir, ctx.ModuleName(), "test") }
// packageRoot returns the module-specific package root directory path. This // directory is where the final package .a files are output and where dependant // modules search for this package via -I arguments. func packageRoot(ctx blueprint.ModuleContext) string { return filepath.Join(bootstrapDir, ctx.ModuleName(), "pkg") }
func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive, pkgPath string, srcs, genSrcs, testSrcs []string) []string { if len(testSrcs) == 0 { return nil } srcDir := moduleSrcDir(ctx) testFiles := pathtools.PrefixPaths(testSrcs, srcDir) mainFile := filepath.Join(testRoot, "test.go") testArchive := filepath.Join(testRoot, "test.a") testFile := filepath.Join(testRoot, "test") testPassed := filepath.Join(testRoot, "test.passed") buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive, append(srcs, testSrcs...), genSrcs, nil) ctx.Build(pctx, blueprint.BuildParams{ Rule: goTestMain, Outputs: []string{mainFile}, Inputs: testFiles, Implicits: []string{"$goTestMainCmd"}, Args: map[string]string{ "pkg": pkgPath, }, }) libDirFlags := []string{"-L " + testRoot} ctx.VisitDepsDepthFirstIf(isGoPackageProducer, func(module blueprint.Module) { dep := module.(goPackageProducer) libDir := dep.GoPkgRoot() libDirFlags = append(libDirFlags, "-L "+libDir) }) ctx.Build(pctx, blueprint.BuildParams{ Rule: compile, Outputs: []string{testArchive}, Inputs: []string{mainFile}, Implicits: []string{"$compileCmd", testPkgArchive}, Args: map[string]string{ "pkgPath": "main", "incFlags": "-I " + testRoot, }, }) ctx.Build(pctx, blueprint.BuildParams{ Rule: link, Outputs: []string{testFile}, Inputs: []string{testArchive}, Implicits: []string{"$linkCmd"}, Args: map[string]string{ "libDirFlags": strings.Join(libDirFlags, " "), }, }) ctx.Build(pctx, blueprint.BuildParams{ Rule: test, Outputs: []string{testPassed}, Inputs: []string{testFile}, Args: map[string]string{ "pkg": pkgPath, "pkgSrcDir": filepath.Dir(testFiles[0]), }, }) return []string{testPassed} }
func (g *goBinary) GenerateBuildActions(ctx blueprint.ModuleContext) { var ( name = ctx.ModuleName() objDir = moduleObjDir(ctx) archiveFile = filepath.Join(objDir, name+".a") aoutFile = filepath.Join(objDir, "a.out") binaryFile = filepath.Join("$BinDir", name) hasPlugins = false pluginSrc = "" genSrcs = []string{} ) if len(g.properties.TestSrcs) > 0 && g.config.runGoTests { g.testArchiveFile = filepath.Join(testRoot(ctx), name+".a") } ctx.VisitDepsDepthFirstIf(isGoPluginFor(name), func(module blueprint.Module) { hasPlugins = true }) if hasPlugins { pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go") genSrcs = append(genSrcs, pluginSrc) } // We only actually want to build the builder modules if we're running as // minibp (i.e. we're generating a bootstrap Ninja file). This is to break // the circular dependence that occurs when the builder requires a new Ninja // file to be built, but building a new ninja file requires the builder to // be built. if g.config.stage == g.BuildStage() { var deps []string if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc, g.config.stage) { return } if g.config.runGoTests { deps = buildGoTest(ctx, testRoot(ctx), g.testArchiveFile, name, g.properties.Srcs, genSrcs, g.properties.TestSrcs) } buildGoPackage(ctx, objDir, name, archiveFile, g.properties.Srcs, genSrcs, deps) var libDirFlags []string ctx.VisitDepsDepthFirstIf(isGoPackageProducer, func(module blueprint.Module) { dep := module.(goPackageProducer) libDir := dep.GoPkgRoot() libDirFlags = append(libDirFlags, "-L "+libDir) }) linkArgs := map[string]string{} if len(libDirFlags) > 0 { linkArgs["libDirFlags"] = strings.Join(libDirFlags, " ") } ctx.Build(pctx, blueprint.BuildParams{ Rule: link, Outputs: []string{aoutFile}, Inputs: []string{archiveFile}, Implicits: []string{"$linkCmd"}, Args: linkArgs, }) ctx.Build(pctx, blueprint.BuildParams{ Rule: cp, Outputs: []string{binaryFile}, Inputs: []string{aoutFile}, }) } else if g.config.stage != StageBootstrap { if len(g.properties.TestSrcs) > 0 && g.config.runGoTests { phonyGoTarget(ctx, g.testArchiveFile, g.properties.TestSrcs, nil, nil) } intermediates := []string{aoutFile, archiveFile} phonyGoTarget(ctx, binaryFile, g.properties.Srcs, genSrcs, intermediates) } }