Quelle bootstrap.go
Sprache: unbekannt
|
|
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2. 0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2. 0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bootstrap
import (
"encoding/json"
"fmt"
"path/filepath"
"runtime"
"strings"
"github.com/google/blueprint"
"github.com/google/blueprint/depset"
"github.com/google/blueprint/pathtools"
"github.com/google/blueprint/proptools"
)
//go:generate go run ../../blueprint/gobtools/codegen
var (
pctx = blueprint.NewPackageContext("github.com/google/blueprint/bootstrap")
goTestMainCmd = pctx.StaticVariable("goTestMainCmd", filepath.Join("$ToolDir", "gote stmain"))
goTestRunnerCmd = pctx.StaticVariable("goTestRunnerCmd", filepath.Join("$ToolDir", "gotestrunner"))
pluginGenSrcCmd = pctx.StaticVariable("pluginGenSrcCmd", filepath.Join("$ToolDir", "loadplugins"))
gobGenCmd = pctx.StaticVariable("gobGenCmd", filepath.Join("$ToolDir", "gob_gen"))
goToolchainTarget = "go_toolchain"
_ = pctx.VariableFunc("toybox", func(ctx blueprint.VariableFuncContext, config interface{}) (string, error) {
prebuiltOs := func() string {
switch runtime.GOOS {
case "linux":
switch runtime.GOARCH {
case "amd64":
return "linux-x86"
case "arm64":
return "linux-arm64"
default:
panic(fmt.Errorf("Unknown GOARCH %s", runtime.GOARCH))
}
case "darwin":
return "darwin-x86"
default:
panic(fmt.Errorf("Unknown GOOS %s", runtime.GOOS))
}
}()
return filepath.Join("prebuilts/build-tools", prebuiltOs, "bin/toybox"), nil
})
parallelCompile = pctx.StaticVariable("parallelCompile", func() string {
numCpu := runtime.NumCPU()
// This will cause us to recompile all go programs if the
// number of cpus changes. We don't get a lot of benefit from
// higher values, so cap this to make it cheaper to move trees
// between machines.
if numCpu > 8 {
numCpu = 8
}
return fmt.Sprintf("-c %d", numCpu)
}())
// We must explicitly use the $toybox path for utilities like cmp, rm, and mv in
// bootstrap rules. Unlike downstream Soong rules (which are wrapped by sbox or
// RuleBuilder with injected $PATHs), bootstrap rules execute raw commands.
// Explicit paths guarantee execution hermeticity on remote bots (RBE) that
// lack basic POSIX tools in their base image.
compile = pctx.StaticRule("compile",
blueprint.RuleParams{
Command: "GOROOT='$goRoot' $compileCmd $parallelCompile -o $out.tmp " +
"$debugFlags -p $pkgPath -complete $incFlags $embedFlags -pack $in && " +
"if $toybox cmp --quiet $out.tmp $out; then $toybox rm $out.tmp; else $toybox mv -f $out.tmp $out; fi",
CommandDeps: []string{"$compileCmd", "${toybox}"},
Description: "compile $out",
Restat: true,
},
"pkgPath", "incFlags", "embedFlags")
// See comment on compile rule for why $toybox is explicitly prefixed.
link = pctx.StaticRule("link",
blueprint.RuleParams{
Command: "GOROOT='$goRoot' $linkCmd -o $out.tmp $libDirFlags $in && " +
"if $toybox cmp --quiet $out.tmp $out; then $toybox rm $out.tmp; else $toybox mv -f $out.tmp $out; fi",
CommandDeps: []string{"$linkCmd", "${toybox}"},
Description: "link $out",
Restat: true,
},
"libDirFlags")
goTestMain = pctx.StaticRule("gotestmain",
blueprint.RuleParams{
Command: "$goTestMainCmd -o $out -pkg $pkg $in",
CommandDeps: []string{"$goTestMainCmd"},
Description: "gotestmain $out",
},
"pkg")
pluginGenSrc = pctx.StaticRule("pluginGenSrc",
blueprint.RuleParams{
Command: "$pluginGenSrcCmd -o $out -p $pkg $plugins",
CommandDeps: []string{"$pluginGenSrcCmd"},
Description: "create $out",
},
"pkg", "plugins")
verifySerializers = pctx.StaticRule("generateSerializers",
blueprint.RuleParams{
Command: "${toybox} rm -f $out && $gobGenCmd -verify $in && ${toybox} touch $out",
CommandDeps: []string{"$gobGenCmd", "${toybox}"},
Description: "generate serializers $out",
})
test = pctx.StaticRule("test",
blueprint.RuleParams{
Command: "$goTestRunnerCmd -p $pkgSrcDir -f $out -- $in -test.short",
CommandDeps: []string{"$goTestRunnerCmd"},
Description: "test $pkg",
},
"pkg", "pkgSrcDir")
cp = pctx.StaticRule("cp",
blueprint.RuleParams{
Command: "${toybox} cp -P $in $out",
CommandDeps: []string{`${toybox}`},
Description: "cp $out",
},
"generator")
touch = pctx.StaticRule("touch",
blueprint.RuleParams{
Command: "${toybox} touch $out",
CommandDeps: []string{`${toybox}`},
Description: "touch $out",
},
"depfile", "generator")
cat = pctx.StaticRule("Cat",
blueprint.RuleParams{
Command: "${toybox} rm -f $out && ${toybox} cat $in > $out",
CommandDeps: []string{`${toybox}`},
Description: "concatenate files to $out",
})
writeFile = pctx.StaticRule("writeFile",
blueprint.RuleParams{
Command: `${toybox} rm -f $out && ${toybox} echo -e -n $content > $out`,
CommandDeps: []string{`${toybox}`},
Description: "writing file $out",
},
"content")
generateBuildNinja = pctx.StaticRule("build.ninja",
blueprint.RuleParams{
// TODO: it's kinda ugly that some parameters are computed from
// environment variables and some from Ninja parameters, but it's probably
// better to not to touch that while Blueprint and Soong are separate
// NOTE: The spaces at EOL are important because otherwise Ninja would
// omit all spaces between the different options.
Command: `cd "$$(dirname "$builder")" && ` +
`BUILDER="$$PWD/$$(basename "$builder")" && ` +
`cd / && ` +
`env -i $env "$$BUILDER" ` +
` --top "$$TOP" ` +
` --soong_out "$soongOutDir" ` +
` --out "$outDir" ` +
` $extra`,
CommandDeps: []string{"$builder"},
Description: "$builder $out",
Deps: blueprint.DepsGCC,
Depfile: "$out.d",
Restat: true,
SandboxDisabled: true,
},
"builder", "env", "extra", "pool")
_ = pctx.VariableFunc("ToolDir", func(ctx blueprint.VariableFuncContext, config interface{}) (string, error) {
return config.(BootstrapConfig).HostToolDir(), nil
})
)
var (
// echoEscaper escapes a string such that passing it to "echo -e" will produce the input value.
echoEscaper = strings.NewReplacer(
`\`, `\\`, // First escape existing backslashes so they aren't interpreted by `echo -e`.
"\n", `\n`, // Then replace newlines with \n
)
)
// shardString takes a string and returns a slice of strings where the length of each one is
// at most shardSize.
func shardString(s string, shardSize int) []string {
if len(s) == 0 {
return nil
}
ret := make([]string, 0, (len(s)+shardSize-1)/shardSize)
for len(s) > shardSize {
ret = append(ret, s[0:shardSize])
s = s[shardSize:]
}
if len(s) > 0 {
ret = append(ret, s)
}
return ret
}
// writeFileRule creates a ninja rule to write contents to a file. The contents will be
// escaped so that the file contains exactly the contents passed to the function.
func writeFileRule(ctx blueprint.ModuleContext, outputFile string, content string) {
// This is MAX_ARG_STRLEN subtracted with some safety to account for shell escapes
const SHARD_SIZE = 131072 - 10000
buildWriteFileRule := func(outputFile string, content string) {
content = echoEscaper.Replace(content)
content = proptools.NinjaEscape(proptools.ShellEscapeIncludingSpaces(content))
if content == "" {
content = "''"
}
ctx.Build(pctx, blueprint.BuildParams{
Rule: writeFile,
Outputs: []string{outputFile},
Description: "write " + outputFile,
Args: map[string]string{
"content": content,
},
})
}
if len(content) > SHARD_SIZE {
var chunks []string
for i, c := range shardString(content, SHARD_SIZE) {
tempPath := fmt.Sprintf("%s.%d", outputFile, i)
buildWriteFileRule(tempPath, c)
chunks = append(chunks, tempPath)
}
ctx.Build(pctx, blueprint.BuildParams{
Rule: cat,
Inputs: chunks,
Outputs: []string{outputFile},
Description: "Merging to " + outputFile,
})
return
}
buildWriteFileRule(outputFile, content)
}
type pluginDependencyTag struct {
blueprint.BaseDependencyTag
}
func (pluginDependencyTag) ExcludeFromVisibilityEnforcement() {}
type bootstrapDependencies interface {
bootstrapDeps(ctx blueprint.BottomUpMutatorContext)
}
var PluginDepTag = pluginDependencyTag{}
func BootstrapDeps(ctx blueprint.BottomUpMutatorContext) {
if pkg, ok := ctx.Module().(bootstrapDependencies); ok {
pkg.bootstrapDeps(ctx)
}
}
// @auto-generate: gob
type PackageInfo struct {
PkgPath string
PkgRoot string
PackageTarget string
TestTargets []string
TransitivePkgRoot depset.DepSet[string]
TransitivePackageTarget depset.DepSet[string]
}
var PackageProvider = blueprint.NewProvider[*PackageInfo]()
// @auto-generate: gob
type BinaryInfo struct {
IntermediatePath string
InstallPath string
TestTargets []string
}
var BinaryProvider = blueprint.NewProvider[*BinaryInfo]()
// @auto-generate: gob
type DocsPackageInfo struct {
PkgPath string
Srcs []string
}
var DocsPackageProvider = blueprint.NewMutatorProvider[*DocsPackageInfo]("bootstrap_deps")
// A GoPackage is a module for building Go packages.
type GoPackage struct {
blueprint.ModuleBase
blueprint.SimpleName
properties struct {
Deps []string
PkgPath string
Srcs []string
TestSrcs []string
TestData []string
PluginFor []string
EmbedSrcs []string
// The visibility property is unused in blueprint, but exists so that soong
// can add one and not have the bp files fail to parse during the bootstrap build.
Visibility []string
Darwin struct {
Srcs []string
TestSrcs []string
}
Linux struct {
Srcs []string
TestSrcs []string
}
}
}
func newGoPackageModuleFactory() func() (blueprint.Module, []interface{}) {
return func() (blueprint.Module, []interface{}) {
module := &GoPackage{}
return module, []interface{}{&module.properties, &module.SimpleName.Properties}
}
}
// Properties returns the list of property structs to be used for registering a wrapped module type.
func (g *GoPackage) Properties() []interface{} {
return []interface{}{&g.properties}
}
func (g *GoPackage) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
return g.properties.Deps
}
func (g *GoPackage) bootstrapDeps(ctx blueprint.BottomUpMutatorContext) {
for _, plugin := range g.properties.PluginFor {
ctx.AddReverseDependency(ctx.Module(), PluginDepTag, plugin)
}
blueprint.SetProvider(ctx, DocsPackageProvider, &DocsPackageInfo{
PkgPath: g.properties.PkgPath,
Srcs: g.properties.Srcs,
})
}
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
}
pkgRoot := packageRoot(ctx)
archiveFile := filepath.Join(pkgRoot,
filepath.FromSlash(g.properties.PkgPath)+".a")
ctx.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
if ctx.OtherModuleDependencyTag(module) == PluginDepTag {
hasPlugins = true
}
})
if hasPlugins {
pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
genSrcs = append(genSrcs, pluginSrc)
}
if hasPlugins && !buildGoPluginLoader(ctx, g.properties.PkgPath, pluginSrc) {
return
}
var srcs, testSrcs []string
if runtime.GOOS == "darwin" {
srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...)
testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...)
} else if runtime.GOOS == "linux" {
srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...)
testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...)
}
testArchiveFile := filepath.Join(testRoot(ctx),
filepath.FromSlash(g.properties.PkgPath)+".a")
testResultFile := buildGoTest(ctx, testRoot(ctx), testArchiveFile,
g.properties.PkgPath, srcs, genSrcs, testSrcs, g.properties.EmbedSrcs)
// Don't build for test-only packages
if len(srcs) == 0 && len(genSrcs) == 0 {
ctx.Build(pctx, blueprint.BuildParams{
Rule: touch,
Outputs: []string{archiveFile},
})
return
}
incFlags, deps, _, _, transitivePkgRoot, transitivePackageTarget := collectGoDeps(ctx)
buildGoPackage(ctx, pkgRoot, g.properties.PkgPath, archiveFile,
srcs, genSrcs, g.properties.EmbedSrcs, incFlags, deps)
blueprint.SetProvider(ctx, PackageProvider, &PackageInfo{
PkgPath: g.properties.PkgPath,
PkgRoot: pkgRoot,
PackageTarget: archiveFile,
TestTargets: testResultFile,
TransitivePkgRoot: transitivePkgRoot,
TransitivePackageTarget: transitivePackageTarget,
})
}
func collectGoDeps(ctx blueprint.ModuleContext) (incFlags, incFlagsDeps, linkFlags, linkFlagsDeps []string,
transitivePkgRoot, transitivePackageTarget depset.DepSet[string]) {
transitivePkgRootBuilder := depset.NewBuilder[string](depset.POSTORDER)
transitivePackageTargetBuilder := depset.NewBuilder[string](depset.POSTORDER)
ctx.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
if info, ok := blueprint.OtherModuleProvider(ctx, module, PackageProvider); ok {
incDir := info.PkgRoot
target := info.PackageTarget
incFlags = append(incFlags, "-I "+incDir)
incFlagsDeps = append(incFlagsDeps, target)
transitivePkgRootBuilder.Direct(info.PkgRoot).Transitive(info.TransitivePkgRoot)
transitivePackageTargetBuilder.Direct(info.PackageTarget).Transitive(info.TransitivePackageTarget)
}
})
transitivePkgRoot = transitivePkgRootBuilder.Build()
transitivePackageTarget = transitivePackageTargetBuilder.Build()
linkFlagsDeps = transitivePackageTarget.ToList()
for _, libDir := range transitivePkgRoot.ToList() {
linkFlags = append(linkFlags, "-L "+libDir)
}
return
}
func buildVerifySerializers(ctx blueprint.ModuleContext, outputFile string, srcs []string, archiveFile string) {
var srcPaths []string
for _, src := range srcs {
srcPaths = append(srcPaths, filepath.Join(moduleSrcDir(ctx), src))
}
ctx.Build(pctx, blueprint.BuildParams{
Rule: verifySerializers,
Inputs: srcPaths,
Implicits: []string{archiveFile},
Outputs: []string{outputFile},
})
}
// A GoBinary is a module for building executable binaries from Go sources.
type GoBinary struct {
blueprint.ModuleBase
blueprint.SimpleName
properties struct {
Deps []string
Srcs []string
TestSrcs []string
TestData []string
EmbedSrcs []string
PrimaryBuilder bool
Default bool
// The visibility property is unused in blueprint, but exists so that soong
// can add one and not have the bp files fail to parse during the bootstrap build.
Visibility []string
Darwin struct {
Srcs []string
TestSrcs []string
}
Linux struct {
Srcs []string
TestSrcs []string
}
}
installPath string
// skipInstall can be set to true by a module type that wraps GoBinary to skip the install rule,
// allowing the wrapping module type to create the install rule itself.
skipInstall bool
// outputFile is set to the path to the intermediate output file.
outputFile string
}
func newGoBinaryModuleFactory() func() (blueprint.Module, []interface{}) {
return func() (blueprint.Module, []interface{}) {
module := &GoBinary{}
return module, []interface{}{&module.properties, &module.SimpleName.Properties}
}
}
func (g *GoBinary) DynamicDependencies(ctx blueprint.DynamicDependerModuleContext) []string {
return g.properties.Deps
}
func (g *GoBinary) bootstrapDeps(ctx blueprint.BottomUpMutatorContext) {
if g.properties.PrimaryBuilder {
blueprint.SetProvider(ctx, PrimaryBuilderProvider, PrimaryBuilderInfo{})
}
}
// IntermediateFile returns the path to the final linked intermedate file.
func (g *GoBinary) IntermediateFile() string {
return g.outputFile
}
// SetSkipInstall is called by module types that wrap GoBinary to skip the install rule,
// allowing the wrapping module type to create the install rule itself.
func (g *GoBinary) SetSkipInstall() {
g.skipInstall = true
}
// Properties returns the list of property structs to be used for registering a wrapped module type.
func (g *GoBinary) Properties() []interface{} {
return []interface{}{&g.properties}
}
func (g *GoBinary) GenerateBuildActions(ctx blueprint.ModuleContext) {
var (
name = ctx.ModuleName()
objDir = packageRoot(ctx)
archiveFile = filepath.Join(objDir, name+".a")
testArchiveFile = filepath.Join(testRoot(ctx), name+".a")
aoutFile = filepath.Join(objDir, name)
hasPlugins = false
pluginSrc = ""
genSrcs = []string{}
)
if !g.skipInstall {
g.installPath = filepath.Join(ctx.Config().(BootstrapConfig).HostToolDir(), name)
}
ctx.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
if ctx.OtherModuleDependencyTag(module) == PluginDepTag {
hasPlugins = true
}
})
if hasPlugins {
pluginSrc = filepath.Join(moduleGenSrcDir(ctx), "plugin.go")
genSrcs = append(genSrcs, pluginSrc)
}
if hasPlugins && !buildGoPluginLoader(ctx, "main", pluginSrc) {
return
}
var srcs, testSrcs []string
if runtime.GOOS == "darwin" {
srcs = append(g.properties.Srcs, g.properties.Darwin.Srcs...)
testSrcs = append(g.properties.TestSrcs, g.properties.Darwin.TestSrcs...)
} else if runtime.GOOS == "linux" {
srcs = append(g.properties.Srcs, g.properties.Linux.Srcs...)
testSrcs = append(g.properties.TestSrcs, g.properties.Linux.TestSrcs...)
}
testResultFile := buildGoTest(ctx, testRoot(ctx), testArchiveFile,
name, srcs, genSrcs, testSrcs, g.properties.EmbedSrcs)
incFlags, deps, linkFlags, linkFlagsDeps, _, _ := collectGoDeps(ctx)
buildGoPackage(ctx, objDir, "main", archiveFile, srcs, genSrcs, g.properties.EmbedSrcs, incFlags, deps)
linkArgs := map[string]string{}
if len(linkFlags) > 0 {
linkArgs["libDirFlags"] = strings.Join(linkFlags, " ")
}
ctx.Build(pctx, blueprint.BuildParams{
Rule: link,
Outputs: []string{aoutFile},
Inputs: []string{archiveFile},
Implicits: append(linkFlagsDeps, goToolchainPhony()),
Args: linkArgs,
})
g.outputFile = aoutFile
var validations []string
if ctx.Config().(BootstrapConfig).RunGoTests() {
validations = testResultFile
}
if !g.skipInstall {
ctx.Build(pctx, blueprint.BuildParams{
Rule: cp,
Outputs: []string{g.installPath},
Inputs: []string{aoutFile},
Validations: validations,
Default: g.properties.Default,
})
}
blueprint.SetProvider(ctx, BinaryProvider, &BinaryInfo{
IntermediatePath: g.outputFile,
InstallPath: g.installPath,
TestTargets: testResultFile,
})
}
func buildGoPluginLoader(ctx blueprint.ModuleContext, pkgPath, pluginSrc string) bool {
ret := true
var pluginPaths []string
ctx.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
if ctx.OtherModuleDependencyTag(module) == PluginDepTag {
if info, ok := blueprint.OtherModuleProvider(ctx, module, PackageProvider); ok {
pluginPaths = append(pluginPaths, info.PkgPath)
}
}
})
ctx.Build(pctx, blueprint.BuildParams{
Rule: pluginGenSrc,
Outputs: []string{pluginSrc},
Args: map[string]string{
"pkg": pkgPath,
"plugins": strings.Join(pluginPaths, " "),
},
})
return ret
}
func generateEmbedcfgFile(ctx blueprint.ModuleContext, files []string, srcDir string, embedcfgFile string) {
embedcfg := struct {
Patterns map[string][]string
Files map[string]string
}{
make(map[string][]string, len(files)),
make(map[string]string, len(files)),
}
for _, file := range files {
embedcfg.Patterns[file] = []string{file}
embedcfg.Files[file] = filepath.Join(srcDir, file)
}
embedcfgData, err := json.Marshal(&embedcfg)
if err != nil {
ctx.ModuleErrorf("Failed to marshal embedcfg data: %s", err.Error())
}
writeFileRule(ctx, embedcfgFile, string(embedcfgData))
}
func buildGoPackage(ctx blueprint.ModuleContext, pkgRoot string,
pkgPath string, archiveFile string, srcs []string, genSrcs []string, embedSrcs []string,
incFlags []string, deps []string) {
srcDir := moduleSrcDir(ctx)
srcFiles := pathtools.PrefixPaths(srcs, srcDir)
srcFiles = append(srcFiles, genSrcs...)
compileArgs := map[string]string{
"pkgPath": pkgPath,
}
if len(incFlags) > 0 {
compileArgs["incFlags"] = strings.Join(incFlags, " ")
}
if len(embedSrcs) > 0 {
embedcfgFile := archiveFile + ".embedcfg"
generateEmbedcfgFile(ctx, embedSrcs, srcDir, embedcfgFile)
compileArgs["embedFlags"] = "-embedcfg " + embedcfgFile
deps = append(deps, embedcfgFile)
deps = append(deps, pathtools.PrefixPaths(embedSrcs, srcDir)...)
}
var validations []string
if ctx.Config().(BootstrapConfig).IsBootstrap() {
verifySerializers := archiveFile + ".verify_serializers"
buildVerifySerializers(ctx, verifySerializers, srcs, archiveFile)
validations = append(validations, verifySerializers)
}
ctx.Build(pctx, blueprint.BuildParams{
Rule: compile,
Outputs: []string{archiveFile},
Inputs: srcFiles,
Implicits: append(deps, goToolchainPhony()),
Args: compileArgs,
Validations: validations,
})
}
func buildGoTest(ctx blueprint.ModuleContext, testRoot, testPkgArchive,
pkgPath string, srcs, genSrcs, testSrcs []string, embedSrcs []string) []string {
var testDeps []string
ctx.VisitDirectDepsProxy(func(module blueprint.ModuleProxy) {
if info, ok := blueprint.OtherModuleProvider(ctx, module, PackageProvider); ok {
testDeps = append(testDeps, info.TestTargets...)
}
})
if len(testSrcs) == 0 {
if len(testDeps) > 0 {
testDependencies := filepath.Join(testRoot, "test.dependencies")
ctx.Build(pctx, blueprint.BuildParams{
Rule: touch,
Outputs: []string{testDependencies},
Validations: testDeps,
})
return []string{testDependencies}
}
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")
incFlags, deps, linkFlags, linkFlagsDeps, _, _ := collectGoDeps(ctx)
buildGoPackage(ctx, testRoot, pkgPath, testPkgArchive,
append(srcs, testSrcs...), genSrcs, embedSrcs, incFlags, deps)
ctx.Build(pctx, blueprint.BuildParams{
Rule: goTestMain,
Outputs: []string{mainFile},
Inputs: testFiles,
Args: map[string]string{
"pkg": pkgPath,
},
})
linkFlagsDeps = append([]string{testPkgArchive}, linkFlagsDeps...)
linkFlags = append([]string{"-L " + testRoot}, linkFlags...)
ctx.Build(pctx, blueprint.BuildParams{
Rule: compile,
Outputs: []string{testArchive},
Inputs: []string{mainFile},
Implicits: []string{testPkgArchive, goToolchainPhony()},
Args: map[string]string{
"pkgPath": "main",
"incFlags": "-I " + testRoot,
},
})
ctx.Build(pctx, blueprint.BuildParams{
Rule: link,
Outputs: []string{testFile},
Inputs: []string{testArchive},
Implicits: append(linkFlagsDeps, goToolchainPhony()),
Args: map[string]string{
"libDirFlags": strings.Join(linkFlags, " "),
},
})
ctx.Build(pctx, blueprint.BuildParams{
Rule: test,
Outputs: []string{testPassed},
Inputs: []string{testFile},
Validations: testDeps,
Args: map[string]string{
"pkg": pkgPath,
"pkgSrcDir": filepath.Dir(testFiles[0]),
},
})
return []string{testPassed}
}
var PrimaryBuilderProvider = blueprint.NewMutatorProvider[PrimaryBuilderInfo]("bootstrap_deps")
// @auto-generate: gob
type PrimaryBuilderInfo struct{}
type singleton struct {
}
func newSingletonFactory() func() blueprint.Singleton {
return func() blueprint.Singleton {
return &singleton{}
}
}
func (s *singleton) GenerateBuildActions(ctx blueprint.SingletonContext) {
// Create the go_toolchain phony rule in a singleton to ensure deterministic
// Ninja file generation regardless of parallel module evaluation order.
emitGoToolchainPhony(ctx)
// Find the module that's marked as the "primary builder", which means it's
// creating the binary that we'll use to generate the non-bootstrap
// build.ninja file.
var primaryBuilders []string
// blueprintTools contains blueprint go binaries that will be built in StageMain
var blueprintTools []string
// blueprintTools contains the test outputs of go tests that can be run in StageMain
var blueprintTests []string
// blueprintGoPackages contains all blueprint go packages that can be built in StageMain
var blueprintGoPackages []string
ctx.VisitAllModuleProxies(func(module blueprint.ModuleProxy) {
if ctx.IsPrimaryModule(module) {
if binaryInfo, ok := blueprint.SingletonModuleProvider(ctx, module, BinaryProvider); ok {
if binaryInfo.InstallPath != "" {
blueprintTools = append(blueprintTools, binaryInfo.InstallPath)
}
blueprintTests = append(blueprintTests, binaryInfo.TestTargets...)
if _, ok := blueprint.SingletonModuleProvider(ctx, module, PrimaryBuilderProvider); ok {
primaryBuilders = append(primaryBuilders, binaryInfo.InstallPath)
}
}
if packageInfo, ok := blueprint.SingletonModuleProvider(ctx, module, PackageProvider); ok {
blueprintGoPackages = append(blueprintGoPackages, packageInfo.PackageTarget)
blueprintTests = append(blueprintTests, packageInfo.TestTargets...)
}
}
})
var primaryBuilderCmdlinePrefix []string
var primaryBuilderFile string
if len(primaryBuilders) == 0 {
ctx.Errorf("no primary builder module present")
return
} else if len(primaryBuilders) > 1 {
ctx.Errorf("multiple primary builder modules present: %q", primaryBuilders)
return
} else {
primaryBuilderFile = primaryBuilders[0]
}
ctx.SetOutDir(pctx, "${outDir}")
for _, subninja := range ctx.Config().(BootstrapConfig).Subninjas() {
ctx.AddSubninja(subninja)
}
for _, i := range ctx.Config().(BootstrapConfig).PrimaryBuilderInvocations() {
flags := make([]string, 0)
flags = append(flags, primaryBuilderCmdlinePrefix...)
flags = append(flags, i.Args...)
pool := ""
if i.Console {
pool = "console"
}
envAssignments := ""
for k, v := range i.Env {
// NB: This is rife with quoting issues but we don't care because we trust
// soong_ui to not abuse this facility too much
envAssignments += k + "=" + v + " "
}
// Build the main build.ninja
ctx.Build(pctx, blueprint.BuildParams{
Rule: generateBuildNinja,
Outputs: i.Outputs,
Inputs: i.Inputs,
Implicits: i.Implicits,
OrderOnly: i.OrderOnlyInputs,
Args: map[string]string{
"builder": primaryBuilderFile,
"env": envAssignments,
"extra": strings.Join(flags, " "),
"pool": pool,
},
Description: i.Description,
})
}
// Add a phony target for building various tools that are part of blueprint
if len(blueprintTools) > 0 {
ctx.Build(pctx, blueprint.BuildParams{
Rule: blueprint.Phony,
Outputs: []string{"blueprint_tools"},
Inputs: blueprintTools,
})
}
// Add a phony target for running various tests that are part of blueprint
ctx.Build(pctx, blueprint.BuildParams{
Rule: blueprint.Phony,
Outputs: []string{"blueprint_tests"},
Inputs: blueprintTests,
})
// Add a phony target for running go tests
ctx.Build(pctx, blueprint.BuildParams{
Rule: blueprint.Phony,
Outputs: []string{"blueprint_go_packages"},
Inputs: blueprintGoPackages,
})
}
func (s *singleton) IncrementalSupported() bool {
return false
}
// 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 {
toolDir := ctx.Config().(BootstrapConfig).HostToolDir()
return filepath.Join(toolDir, "go", ctx.ModuleName(), ctx.ModuleSubDir(), "pkg")
}
// 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 {
toolDir := ctx.Config().(BootstrapConfig).HostToolDir()
return filepath.Join(toolDir, "go", ctx.ModuleName(), ctx.ModuleSubDir(), "test")
}
// moduleSrcDir returns the path of the directory that all source file paths are
// specified relative to.
func moduleSrcDir(ctx blueprint.ModuleContext) string {
return ctx.ModuleDir()
}
// moduleGenSrcDir returns the module-specific generated sources path.
func moduleGenSrcDir(ctx blueprint.ModuleContext) string {
toolDir := ctx.Config().(BootstrapConfig).HostToolDir()
return filepath.Join(toolDir, "go", ctx.ModuleName(), ctx.ModuleSubDir(), "gen")
}
func goToolchainPhony() string {
return goToolchainTarget
}
func emitGoToolchainPhony(ctx blueprint.SingletonContext) {
goroot := goRoot()
osArch := runtime.GOOS + "_" + runtime.GOARCH
// Exclude Go race detector precompiled objects (e.g. linux_amd64_race)
// which are not needed for standard bootstrap builds. We also exclude
// pkg/tool here since specific rules add the exact binary as an implicit dep.
//
// The expected structure of GOROOT/pkg is:
// include/ <-- Headers
// linux_amd64/ <-- Standard library .a files
// linux_amd64_race/ (Excluded)
// tool/ (Excluded)
pathsToGlob := []string{
"pkg/include/**/*",
"pkg/" + osArch + "/**/*",
}
var inputs []string
for _, p := range pathsToGlob {
files, err := ctx.GlobWithDeps(filepath.Join(goroot, p), nil)
if err != nil {
panic(fmt.Errorf("Failed to glob GOROOT %q subpath %q: %s", goroot, p, err))
}
for _, f := range files {
if !strings.HasSuffix(f, "/") {
inputs = append(inputs, f)
}
}
}
ctx.Build(pctx, blueprint.BuildParams{
Rule: blueprint.Phony,
Outputs: []string{goToolchainTarget},
Inputs: inputs,
})
}
[Dauer der Verarbeitung: 0.25 Sekunden, vorverarbeitet 2026-06-28]
|
2026-07-09
|