Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/blueprint/bootstrap/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 29 kB image not shown  

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", "gotestmain"))
 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.22 Sekunden, vorverarbeitet 2026-06-28]