Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quellcode-Bibliothek context.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 blueprint

import (
 "bufio"
 "bytes"
 "cmp"
 "context"
 "encoding/json"
 "errors"
 "fmt"
 "hash/fnv"
 "io"
 "io/ioutil"
 "iter"
 "maps"
 "math"
 "os"
 "path/filepath"
 "reflect"
 "runtime"
 "runtime/pprof"
 "slices"
 "sort"
 "strconv"
 "strings"
 "sync"
 "sync/atomic"
 "text/scanner"
 "text/template"
 "unsafe"

 "github.com/google/blueprint/gobtools"
 "github.com/google/blueprint/metrics"
 "github.com/google/blueprint/parser"
 "github.com/google/blueprint/pathtools"
 "github.com/google/blueprint/pool"
 "github.com/google/blueprint/proptools"
 "github.com/google/blueprint/syncmap"
 "github.com/google/blueprint/uniquelist"
)

//go:generate go run ./gobtools/codegen

var ErrBuildActionsNotReady = errors.New("build actions are not ready")

const maxErrors = 10
const MockModuleListFile = "bplist"

const OutFilePermissions = 0666

const BuildActionsCacheFile = "build_actions.gob"
const OrderOnlyStringsCacheFile = "order_only_strings.gob"

// sandboxConfig is an interface for config objects that can report if the
// build is action sandboxed.
type sandboxConfig interface {
 IsActionSandboxedBuild() bool
 ActionSandboxMetrics() *SandboxMetrics
}

type SandboxedAction struct {
 Name      string
 Sandboxed bool
}

// SandboxMetrics tracks the total number of rules and action sandboxing disabled
// (i.e. opted-out) rules in action sandboxed builds
type SandboxMetrics struct {
 deduper       sync.Map
 totalRules    int64
 disabledRules int64
 actions       []SandboxedAction
}

func (s *SandboxMetrics) updateSandboxMetrics(name string, source string, isSandboxDisabled bool) {
 if source != "" {
  if _, ok := s.deduper.LoadOrStore(source, true); ok {
   return
  }
  name = source
 }
 s.totalRules += 1
 if isSandboxDisabled {
  s.disabledRules += 1
 }
 s.actions = append(s.actions, SandboxedAction{
  Name:      name,
  Sandboxed: !isSandboxDisabled,
 })
}

func (s *SandboxMetrics) TotalRules() int64 {
 return s.totalRules
}

func (s *SandboxMetrics) DisabledRules() int64 {
 return s.disabledRules
}

func (s *SandboxMetrics) Actions() []SandboxedAction {
 return s.actions
}

// A Context contains all the state needed to parse a set of Blueprints files
// and generate a Ninja file.  The process of generating a Ninja file proceeds
// through a series of four phases.  Each phase corresponds with a some methods
// on the Context object
//
//       Phase                            Methods
//    ------------      -------------------------------------------
// 1. Registration         RegisterModuleType, RegisterSingletonType
//
// 2. Parse                    ParseBlueprintsFiles, Parse
//
// 3. Generate            ResolveDependencies, PrepareBuildActions
//
// 4. Write                           WriteBuildFile
//
// The registration phase prepares the context to process Blueprints files
// containing various types of modules.  The parse phase reads in one or more
// Blueprints files and validates their contents against the module types that
// have been registered.  The generate phase then analyzes the parsed Blueprints
// contents to create an internal representation for the build actions that must
// be performed.  This phase also performs validation of the module dependencies
// and property values defined in the parsed Blueprints files.  Finally, the
// write phase generates the Ninja manifest text based on the generated build
// actions.
type Context struct {
 context.Context

 // Used for metrics-related event logging.
 EventHandler *metrics.EventHandler

 BeforePrepareBuildActionsHook func() error

 moduleFactories     map[string]ModuleFactory
 nameInterface       NameInterface
 moduleGroups        []*moduleGroup
 singletonInfo       []*singletonInfo
 mutatorInfo         []*mutatorInfo
 variantMutatorNames []string

 completedTransitionMutators int
 transitionMutators          []*transitionMutatorImpl
 transitionMutatorNames      []string

 needsUpdateDependencies uint32 // positive if a mutator modified the dependencies

 dependenciesReady bool // set to true on a successful ResolveDependencies
 buildActionsReady bool // set to true on a successful PrepareBuildActions

 // set by SetIgnoreUnknownModuleTypes
 ignoreUnknownModuleTypes bool

 // set by SetAllowMissingDependencies
 allowMissingDependencies bool

 // set during PrepareBuildActions
 nameTracker     *nameTracker
 liveGlobals     *liveTracker
 globalVariables map[Variable]*ninjaString
 globalPools     map[Pool]*poolDef
 globalRules     map[Rule]*ruleDef

 // set during PrepareBuildActions
 outDir             *ninjaString // The builddir special Ninja variable
 requiredNinjaMajor int          // For the ninja_required_version variable
 requiredNinjaMinor int          // For the ninja_required_version variable
 requiredNinjaMicro int          // For the ninja_required_version variable

 subninjas []string

 // set lazily by sortedModuleGroups
 cachedSortedModuleGroups []*moduleGroup
 // cache deps modified to determine whether cachedSortedModuleGroups needs to be recalculated
 cachedDepsModified bool

 globs syncmap.SyncMap[globKey, globsMapEntry]

 restoredGlobsFromCache map[globKey]pathtools.GlobResult
 restoredGlobMetrics    pathtools.RestoredGlobsMetrics

 srcDir           string
 incrementalDBDir string
 fs               pathtools.FileSystem
 moduleListFile   string

 // Mutators indexed by the ID of the provider associated with them.  Not all mutators will
 // have providers, and not all providers will have a mutator, or if they do the mutator may
 // not be registered in this Context.
 providerMutators []*mutatorInfo

 // True for the index of any mutators that have already run over all modules
 finishedMutators []bool

 // If true, RunBlueprint will skip cloning modules at the end of RunBlueprint.
 // Cloning modules intentionally invalidates some Module values after
 // mutators run (to ensure that mutators don't set such Module values in a way
 // which ruins the integrity of the graph). However, keeping Module values
 // changed by mutators may be a desirable outcome (such as for tooling or tests).
 SkipCloneModulesAfterMutators bool

 // String values that can be used to gate build graph traversal
 includeTags *IncludeTags

 sourceRootDirs *SourceRootDirs

 // True if an incremental analysis can be attempted, i.e., there is no Soong
 // code changes, no environmental variable changes and no product config
 // variable changes.
 incrementalAnalysis bool

 // True if the flag --incremental-build-actions is set, in which case Soong
 // will try to do a incremental build. Mainly two tasks will involve here:
 // caching the providers of all the participating modules, and restoring the
 // providers and skip the build action generations if there is a cache hit.
 // Enabling this flag will only guarantee the former task to be performed, the
 // latter will depend on the flag above.
 incrementalEnabled bool

 incrementalProviderTest bool

 keyValueStoreCache      *KeyValueStoreCache
 buildActionsToCacheLock sync.Mutex
 orderOnlyStringsCache   OrderOnlyStringsCache
 orderOnlyStrings        syncmap.SyncMap[uniquelist.UniqueList[string], *orderOnlyStringsInfo]
 incrementalDebugFile    string
 EncContext              gobtools.EncContext
 providerValueHashes     []proptools.Hash

 moduleDebugDataChannel chan []byte

 captureBuildParams bool

 // index of the last mutator which uses `CreateModule`.
 mutatorIndexAfterLastCreateModule int

 // If splitAllVariants is true, all variants will be created upfront rather than on-demand.
 splitAllVariants bool

 // index of the first mutator that supports partial analysis.
 mutatorIndexPartialAnalysis int
 partialAnalysisTargets      []string
}

type orderOnlyStringsInfo struct {
 dedup       bool
 incremental bool
 dedupName   string
}

// A container for String keys. The keys can be used to gate build graph traversal
type SourceRootDirs struct {
 dirs []string
}

func (dirs *SourceRootDirs) Add(names ...string) {
 dirs.dirs = append(dirs.dirs, names...)
}

func (dirs *SourceRootDirs) SourceRootDirAllowed(path string) (bool, string) {
 sort.Slice(dirs.dirs, func(i, j int) bool {
  return len(dirs.dirs[i]) < len(dirs.dirs[j])
 })
 last := len(dirs.dirs)
 for i := range dirs.dirs {
  // iterate from longest paths (most specific)
  prefix := dirs.dirs[last-i-1]
  disallowedPrefix := false
  if len(prefix) >= 1 && prefix[0] == '-' {
   prefix = prefix[1:]
   disallowedPrefix = true
  }
  if strings.HasPrefix(path, prefix) {
   if disallowedPrefix {
    return false, prefix
   } else {
    return true, prefix
   }
  }
 }
 return true, ""
}

func (c *Context) AddSourceRootDirs(dirs ...string) {
 c.sourceRootDirs.Add(dirs...)
}

// A container for String keys. The keys can be used to gate build graph traversal
type IncludeTags map[string]bool

func (tags *IncludeTags) Add(names ...string) {
 for _, name := range names {
  (*tags)[name] = true
 }
}

func (tags *IncludeTags) Contains(tag string) bool {
 _, exists := (*tags)[tag]
 return exists
}

func (c *Context) AddIncludeTags(names ...string) {
 c.includeTags.Add(names...)
}

func (c *Context) ContainsIncludeTag(name string) bool {
 return c.includeTags.Contains(name)
}

// iterateAllVariants returns an iter.Seq that iterates over every variant of every module.
func (c *Context) iterateAllVariants() iter.Seq[*moduleInfo] {
 return func(yield func(*moduleInfo) bool) {
  for _, group := range c.moduleGroups {
   for _, module := range group.modules {
    if !yield(module) {
     return
    }
   }
  }
 }
}

// An Error describes a problem that was encountered that is related to a
// particular location in a Blueprints file.
type BlueprintError struct {
 Err error            // the error that occurred
 Pos scanner.Position // the relevant Blueprints file location
}

// A ModuleError describes a problem that was encountered that is related to a
// particular module in a Blueprints file
type ModuleError struct {
 BlueprintError
 module *moduleInfo
}

// A PropertyError describes a problem that was encountered that is related to a
// particular property in a Blueprints file
type PropertyError struct {
 ModuleError
 property string
}

func (e *BlueprintError) Error() string {
 return fmt.Sprintf("%s: %s", e.Pos, e.Err)
}

func (e *ModuleError) Error() string {
 return fmt.Sprintf("%s: %s: %s", e.Pos, e.module, e.Err)
}

func (e *PropertyError) Error() string {
 return fmt.Sprintf("%s: %s: %s: %s", e.Pos, e.module, e.property, e.Err)
}

type localBuildActions struct {
 variables []*localVariable
 rules     []*localRule
 buildDefs []*buildDef
}

type moduleList []*moduleInfo

func (l moduleList) firstModule() *moduleInfo {
 if len(l) > 0 {
  return l[0]
 }
 panic(fmt.Errorf("no first module!"))
}

func (l moduleList) lastModule() *moduleInfo {
 if len(l) > 0 {
  return l[len(l)-1]
 }
 panic(fmt.Errorf("no last module!"))
}

type moduleGroup struct {
 name string

 modules moduleList

 namespace Namespace

 // A partial copy of moduleInfo at the end of defaults mutator.
 // common across all variants.
 coreModuleInfo moduleInfo

 // used for creating on demand variants.
 variantOnDemandLock sync.Mutex

 // Additional variations supported by this module group that were not
 // created by Split.
 supportedVariantsOnDemand map[string]TransitionInfos

 // Map of requested variation map to on-demand variants.
 // Set to empty at the end of mutator.
 cachedVariantsOnDemand map[string]*moduleInfo

 // Indicates the module group is not yet in the build graph.
 passive bool
}

func (group *moduleGroup) moduleByVariantName(name string) *moduleInfo {
 for _, module := range group.modules {
  if module.variant.name == name {
   return module
  }
 }
 return nil
}

// registerSupportedVariants registers the supported variants, but does not create them
func (group *moduleGroup) registerSupportedVariants(mutatorName string, infos TransitionInfos) {
 if len(infos) == 0 {
  return
 }
 group.variantOnDemandLock.Lock()
 defer group.variantOnDemandLock.Unlock()
 if group.supportedVariantsOnDemand == nil {
  group.supportedVariantsOnDemand = map[string]TransitionInfos{}
 }
 if _, exists := group.supportedVariantsOnDemand[mutatorName]; exists {
  return
 }
 group.supportedVariantsOnDemand[mutatorName] = append(group.supportedVariantsOnDemand[mutatorName], infos...)
}

// searchOnDemandVariant returns true if an on-demand variant creation should be attempted.
// It uses the following heuristics.
// 1. SplitOnDemand is non-nil for any mutator that precedes the Add.*Dependency request.
// 2. No transition has occurred yet
//
// Feasibility of on-demand variant request will be determined subsequently by using `applyTransitions`
// across a sliding window on the on-demand variant.
func (group *moduleGroup) searchOnDemandVariant(onDemandVariants variationMap, far bool, atMutatorIndex int, transitionMutators []*transitionMutatorImpl) bool {
 for _, mutator := range transitionMutators {
  if atMutatorIndex >= mutator.mutatorIndex {
   if _, exists := group.supportedVariantsOnDemand[mutator.name]; exists {
    return true
   }
  }
 }

 if len(transitionMutators) > 0 && atMutatorIndex < transitionMutators[0].mutatorIndex {
  return true
 }
 return false
}

// loadOrCreateVariantOnDemand returns an on-demand variant (if supported) or nil.
// It reruns the completed transitions, applying Outgoing/Incoming transition to resolve the requested variant to a final variant.
//
// To reduce duplicate reruns, it stores the results in a map.
func (c *Context) loadOrCreateVariantOnDemand(config any, module *moduleInfo, depTag DependencyTag, possibleDeps *moduleGroup, variant variationMap,
 requestedVariations []Variation, far bool, moduleOutgoingTransitionInfos []TransitionInfo) (*moduleInfo, []error) {
 createVariantOnDemand := func() (*moduleInfo, []error) {
  var variantOnDemand *moduleInfo
  var errs []error
  onDemandVariantForIncomingTransition := c.createVariantOnDemand(possibleDeps, variant)
  _, _, errs = c.applyTransitions(config, module, depTag, possibleDeps, variant, requestedVariations, far, onDemandVariantForIncomingTransition)
  if len(errs) > 0 || onDemandVariantForIncomingTransition.createdOnDemandIncompatible {
   variantOnDemand = nil
  } else {
   variantOnDemand = c.createVariantOnDemand(possibleDeps, onDemandVariantForIncomingTransition.requestedOnDemandVariant.clone())
  }

  return variantOnDemand, errs

 }
 if module.group == possibleDeps {
  // inter-variant dep can cause a deadlock, so always compute.
  return createVariantOnDemand()
 }

 var variantSb strings.Builder
 for _, mutator := range slices.Sorted(maps.Keys(variant.variations)) {
  variantSb.WriteString("-")
  variantSb.WriteString(variant.variations[mutator])
 }
 for _, mutator := range slices.Sorted(maps.Keys(module.requestedOnDemandVariant.variations)) {
  variantSb.WriteString("-")
  variantSb.WriteString(variant.variations[mutator])
 }
 for _, oti := range moduleOutgoingTransitionInfos {
  if oti != nil && oti.Variation() != "" {
   variantSb.WriteString("-")
   variantSb.WriteString(oti.Variation())
  }
 }

 possibleDeps.variantOnDemandLock.Lock()
 defer possibleDeps.variantOnDemandLock.Unlock()
 if possibleDeps.cachedVariantsOnDemand == nil {
  possibleDeps.cachedVariantsOnDemand = make(map[string]*moduleInfo)
 }
 // Match found
 if cached, exists := possibleDeps.cachedVariantsOnDemand[variantSb.String()]; exists {
  return cached, nil
 }
 // No match found
 variantOnDemand, errs := createVariantOnDemand()
 possibleDeps.cachedVariantsOnDemand[variantSb.String()] = variantOnDemand
 return variantOnDemand, errs
}

// createVariantOnDemand uses group.coreModuleInfo.factory() to create an "empty" variant on demand.
// `runMutator` will be responsible for populating this variant using cloneLogicModule
// and creating the dependency edges to this on demand variant.
func (c *Context) createVariantOnDemand(group *moduleGroup, onDemandVariants variationMap) *moduleInfo {

 newlogicmodule, newproperties := group.coreModuleInfo.factory()
 newmodule := moduleInfo{
  logicModule:              newlogicmodule,
  properties:               newproperties,
  factory:                  group.coreModuleInfo.factory,
  group:                    group,
  createdOnDemand:          true,
  requestedOnDemandVariant: onDemandVariants,
  relBlueprintsFile:        group.coreModuleInfo.relBlueprintsFile,
  pos:                      group.coreModuleInfo.pos,
 }
 newmodule.createdOnDemandReplaceWith = &newmodule
 newlogicmodule.setInfo(&newmodule)

 return &newmodule
}

// Initialize the properties of the on demand variant and
// re-run the completed mutators on this newly created module.
func (c *Context) rerunMutatorsOnVariantOnDemand(newmodule *moduleInfo, fromMutatorIndex, tillMutatorIndex int, p pauseFunc, config interface{}) {
 pause := func(dep *moduleInfo) {
  if dep.createdOnDemand {
   // Transitive variant on demand.
   // Set `requestedOnDemandVariant` on this transitive dep.
   // This will be used in the main coordinator goroutine to create the correct transition for this variant.
   for _, transitionMutator := range c.transitionMutators[:c.completedTransitionMutators] {
    if transitionMutator.mutatorIndex >=
     dep.finishedMutator+3 { // TODO (b/448182248): Revisit partially transitioned on-demand variants.
     // Copy requestedOnDemandVariant from rdep to dep on-demand modules.
     // This will be done only for the transition mutators that have not yet
     // been completed in rerunMutator.
     // variation map for earlier transition mutators will be resolved by `findVariant`.
     //
     // TODO (spandandas): Does this handle transition mutators between
     // newmodule.finishedMutator and c.completedTransitionMutators?
     dep.requestedOnDemandVariant.set(transitionMutator.name, newmodule.requestedOnDemandVariant.get(transitionMutator.name))
    }
   }
  }
  if p != nil {
   p(dep)
  }
 }

 // initialize the properties from coreModuleInfo.
 if fromMutatorIndex == c.mutatorIndexAfterLastCreateModule {
  newlogicmodule, newproperties := c.cloneLogicModule(&newmodule.group.coreModuleInfo)
  newlogicmodule.setInfo(newmodule)
  newmodule.logicModule = newlogicmodule
  newmodule.properties = newproperties
 }

 for index, mi := range c.mutatorInfo {
  if index < fromMutatorIndex {
   continue
  }
  newmodule.startedMutator = index
  mctx := &mutatorContext{
   baseModuleContext: baseModuleContext{
    context: c,
    config:  config,
    module:  newmodule,
   },
   mutator:   mi,
   pauseFunc: pause,
  }
  if mi.transitionPropagateMutator != nil {
   mi.transitionPropagateMutator(mctx)
  } else {
   mctx.mutator = mi
   mi.bottomUpMutator(mctx)
  }
  newmodule.finishedMutator = index
  if len(mctx.reverseDeps) > 0 || len(mctx.replace) > 0 || len(mctx.rename) > 0 {
   panic("TODO (b/448182009): Add support for AddReverseDependency, ReplaceDependency, Rename")
  }

  // The module has been mutated till the current mutator.
  // Do not mutate further.
  if index == tillMutatorIndex {
   break
  }

  // Err
  if newmodule.createdOnDemandIncompatible {
   break
  }
 }
}

type moduleInfo struct {
 // set during Parse
 typeName          string
 factory           ModuleFactory
 relBlueprintsFile string
 pos               scanner.Position
 propertyPos       map[string]scanner.Position
 createdBy         *moduleInfo

 variant variant

 logicModule Module
 group       *moduleGroup
 properties  []interface{}

 // set during ResolveDependencies
 missingDeps            []string
 newDirectDeps          []*moduleInfo
 newOnDemandReverseDeps []reverseDep

 // set during updateDependencies
 reverseDeps []*moduleInfo
 forwardDeps []*moduleInfo
 directDeps  []depInfo

 // used by parallelVisit
 waitingCount atomic.Int32

 // set during each runMutator
 splitModules           moduleList
 obsoletedByNewVariants bool

 // Used by TransitionMutator implementations

 // incomingTransitionInfos stores the map from variation to TransitionInfo object for transitions that were
 // requested by reverse dependencies.  It is updated by reverse dependencies and protected by
 // incomingTransitionInfosLock.  It is invalid after the TransitionMutator top down mutator has run on
 // this module.
 incomingTransitionInfos     map[string]TransitionInfo
 incomingTransitionInfosLock sync.Mutex
 // splitTransitionInfos and splitTransitionVariations stores the list of TransitionInfo objects, and their
 // corresponding variations, returned by Split or requested by reverse dependencies.  They are valid after the
 // TransitionMutator top down mutator has run on this module, and invalid after the bottom up mutator has run.
 splitTransitionInfos      []TransitionInfo
 splitTransitionVariations []string
 currentTransitionMutator  string

 // transitionInfos stores the final TransitionInfo for this module indexed by transitionMutatorImpl.index
 transitionInfos []TransitionInfo

 // outgoingTransitionCache stores the final variation for each dependency, indexed by the source variation
 // index in splitTransitionInfos and then by the index of the dependency in directDeps
 outgoingTransitionCache [][]string

 // set during PrepareBuildActions
 actionDefs  localBuildActions
 buildParams *[]BuildParams

 startedMutator  int
 finishedMutator int

 startedGenerateBuildActions  bool
 finishedGenerateBuildActions bool

 // freeAfterGenerateBuildActions is set if the module called ModuleContext.FreeModuleAfterGenerateBuildActions,
 // allowing the Module to be freed after GenerateBuildActions complete, and requiring all future accesses
 // to go through ModuleProxy instead of the Module.
 freeAfterGenerateBuildActions bool
 // cachedName stores the result of Module.Name() after the end of GenerateBuildActions for use in ModuleProxy.Name()
 cachedName string
 // cachedString stores the result of Module.String() after the end of GenerateBuildActions for use in ModuleProxy.String().
 cachedString string
 // cachedUniqueName stores the result of UniqueName after the end of GenerateBuildActions for use when sorting
 // modules.
 cachedUniqueName string

 moduleIncrementalInfo

 createdOnDemand            bool
 createdOnDemandReplaceWith *moduleInfo
 requestedOnDemandVariant   variationMap
 // Properties used to determine whether a requested on-demand variant can be created.
 createdOnDemandIncompatible    bool
 createdOnDemandSupportedSplits []TransitionInfo
 // Index used for sorting primary and on-demand variants. Special-cased to android's image mutator for now.
 sortIndex int
}

type providerInfo struct {
 providers                  []interface{}
 providerInitialValueHashes []proptools.Hash
}

// @auto-generate: gob
type globResultCache struct {
 Pattern  string
 Excludes []string
 Result   proptools.Hash
}

func (g *globResultCache) equal(other globResultCache) bool {
 return g.Result == other.Result && g.Pattern == other.Pattern && slices.Equal(g.Excludes, other.Excludes)
}

type moduleIncrementalInfo struct {
 commonIncrementalInfo
 buildActionInputHash proptools.Hash
 orderOnlyStrings     []string
 incrementalDebugInfo []byte

 // providersHash is the hash of the providers set by this module
 providersHash proptools.Hash
 // transitiveProvidersHash is the hash of the providers set by
 // this module and all transitive dependencies of this module.
 transitiveProvidersHash proptools.Hash
}

type commonIncrementalInfo struct {
 incrementalRestored bool
 // hasUnrestoredProvider is true when the module has been restored from the cache and the provider
 // (indexed in the same order as providerRegistry) exists in the cache but has not yet been
 // restored.
 hasUnrestoredProvider []bool
 providerRestoreLock   sync.Mutex
 buildActionCacheKey   *DataCacheKey
 globCache             []globResultCache
 providerInfo
}

type variant struct {
 name       string
 variations variationMap
}

type depInfo struct {
 module *moduleInfo
 tag    DependencyTag
}

func (module *moduleInfo) Name() string {
 // If this is called from a LoadHook (which is run before the module has been registered)
 // then group will not be set and so the name is retrieved from logicModule.Name().
 // Usually, using that method is not safe as it does not track renames (group.name does).
 // However, when called from LoadHook it is safe as there is no way to rename a module
 // until after the LoadHook has run and the module has been registered.
 if module.group != nil {
  return module.group.name
 } else {
  return module.logicModule.Name()
 }
}

func (module *moduleInfo) String() string {
 s := fmt.Sprintf("module %q", module.Name())
 if module.variant.name != "" {
  s += fmt.Sprintf(" variant %q", module.variant.name)
 }
 if module.createdBy != nil {
  s += fmt.Sprintf(" (created by %s)", module.createdBy)
 }

 return s
}

func (module *moduleInfo) namespace() Namespace {
 return module.group.namespace
}

func (module *moduleInfo) moduleCacheKey() string {
 variant := module.variant.name
 if variant == "" {
  variant = "none"
 }
 return calculateFileNameHash(fmt.Sprintf("%s-%s-%s-%s",
  filepath.Dir(module.relBlueprintsFile), module.cachedUniqueName, variant, module.typeName))
}

// @auto-generate: gob
type stringHash struct {
 string
}

func calculateFileNameHash(name string) string {
 hash, err := proptools.CalculateHash(stringHash{name})
 if err != nil {
  panic(newPanicErrorf(err, "failed to calculate hash for file name: %s", name))
 }
 return hash.FormatUint(16)
}

func (c *Context) setModuleTransitionInfo(module *moduleInfo, t *transitionMutatorImpl, info TransitionInfo) {
 if len(module.transitionInfos) == 0 {
  module.transitionInfos = make([]TransitionInfo, len(c.transitionMutators))
 }
 module.transitionInfos[t.index] = info
}

// A Variation is a way that a variant of a module differs from other variants of the same module.
// For example, two variants of the same module might have Variation{"arch","arm"} and
// Variation{"arch","arm64"}
// @auto-generate: gob
type Variation struct {
 // Mutator is the axis on which this variation applies, i.e. "arch" or "link"
 Mutator string
 // Variation is the name of the variation on the axis, i.e. "arm" or "arm64" for arch, or
 // "shared" or "static" for link.
 Variation string
}

// A variationMap stores a map of Mutator to Variation to specify a variant of a module.
type variationMap struct {
 variations map[string]string
}

func (vm variationMap) clone() variationMap {
 return variationMap{
  variations: maps.Clone(vm.variations),
 }
}

func (vm variationMap) cloneMatching(mutators []string) variationMap {
 newVariations := make(map[string]string)
 for _, mutator := range mutators {
  if variation, ok := vm.variations[mutator]; ok {
   newVariations[mutator] = variation
  }
 }
 return variationMap{
  variations: newVariations,
 }
}

// Compare this variationMap to another one.  Returns true if the every entry in this map
// exists and has the same value in the other map.
func (vm variationMap) subsetOf(other variationMap) bool {
 for k, v1 := range vm.variations {
  if v2, ok := other.variations[k]; !ok || v1 != v2 {
   return false
  }
 }
 return true
}

func (vm variationMap) equal(other variationMap) bool {
 return maps.Equal(vm.variations, other.variations)
}

func (vm *variationMap) set(mutator, variation string) {
 if variation == "" {
  if vm.variations != nil {
   delete(vm.variations, mutator)
  }
 } else {
  if vm.variations == nil {
   vm.variations = make(map[string]string)
  }
  vm.variations[mutator] = variation
 }
}

func (vm variationMap) get(mutator string) string {
 return vm.variations[mutator]
}

func (vm variationMap) delete(mutator string) {
 delete(vm.variations, mutator)
}

func (vm variationMap) empty() bool {
 return len(vm.variations) == 0
}

// differenceKeysCount returns the count of keys that exist in this variationMap that don't exist in the argument.  It
// ignores the values.
func (vm variationMap) differenceKeysCount(other variationMap) int {
 divergence := 0
 for mutator, _ := range vm.variations {
  if _, exists := other.variations[mutator]; !exists {
   divergence += 1
  }
 }
 return divergence
}

type singletonInfo struct {
 // set during RegisterSingletonType
 factory   SingletonFactory
 singleton Singleton
 name      string
 parallel  bool

 // set during PrepareBuildActions
 actionDefs                   localBuildActions
 subninjas                    []string
 startedGenerateBuildActions  bool
 finishedGenerateBuildActions bool
 commonIncrementalInfo
 // Whether this singleton supports incremental build.
 incrementalSupported bool
 // The provider hashes of all the singletons that this singleton might depend on.
 // These values are calculated before calling the GenerateBuildAction of the current
 // singleton, and combined with the hashes of all the module providers that this
 // singleton might depend on, we can decide if the input of the GenerateBuildAction
 // has any change, and skip the execution of it if there is no change.
 providerValueHashes []proptools.Hash
}

type mutatorInfo struct {
 // set during RegisterMutator
 transitionPropagateMutator func(BaseModuleContext)
 bottomUpMutator            BottomUpMutator
 name                       string
 index                      int
 transitionMutator          *transitionMutatorImpl

 usesRename              bool
 usesReverseDependencies bool
 usesReplaceDependencies bool
 usesCreateModule        bool
 mutatesDependencies     bool
 mutatesGlobalState      bool
 prePartial              bool
}

func newContext() *Context {
 eventHandler := metrics.EventHandler{}
 return &Context{
  Context:               context.Background(),
  EventHandler:          &eventHandler,
  moduleFactories:       make(map[string]ModuleFactory),
  nameInterface:         NewSimpleNameInterface(),
  fs:                    pathtools.OsFs,
  includeTags:           &IncludeTags{},
  sourceRootDirs:        &SourceRootDirs{},
  outDir:                nil,
  requiredNinjaMajor:    1,
  requiredNinjaMinor:    7,
  requiredNinjaMicro:    0,
  orderOnlyStringsCache: make(OrderOnlyStringsCache),
  orderOnlyStrings:      syncmap.SyncMap[uniquelist.UniqueList[string], *orderOnlyStringsInfo]{},
 }
}

// NewContext creates a new Context object.  The created context initially has
// no module or singleton factories registered, so the RegisterModuleFactory and
// RegisterSingletonFactory methods must be called before it can do anything
// useful.
func NewContext() *Context {
 ctx := newContext()

 ctx.RegisterBottomUpMutator("blueprint_deps", blueprintDepsMutator)

 return ctx
}

// A ModuleFactory function creates a new Module object.  See the
// Context.RegisterModuleType method for details about how a registered
// ModuleFactory is used by a Context.
type ModuleFactory func() (m Module, propertyStructs []interface{})

// RegisterModuleType associates a module type name (which can appear in a
// Blueprints file) with a Module factory function.  When the given module type
// name is encountered in a Blueprints file during parsing, the Module factory
// is invoked to instantiate a new Module object to handle the build action
// generation for the module.  If a Mutator splits a module into multiple variants,
// the factory is invoked again to create a new Module for each variant.
//
// The module type names given here must be unique for the context.  The factory
// function should be a named function so that its package and name can be
// included in the generated Ninja file for debugging purposes.
//
// The factory function returns two values.  The first is the newly created
// Module object.  The second is a slice of pointers to that Module object's
// properties structs.  Each properties struct is examined when parsing a module
// definition of this type in a Blueprints file.  Exported fields of the
// properties structs are automatically set to the property values specified in
// the Blueprints file.  The properties struct field names determine the name of
// the Blueprints file properties that are used - the Blueprints property name
// matches that of the properties struct field name with the first letter
// converted to lower-case.
//
// The fields of the properties struct must be either []string, a string, or
// bool. The Context will panic if a Module gets instantiated with a properties
// struct containing a field that is not one these supported types.
//
// Any properties that appear in the Blueprints files that are not built-in
// module properties (such as "name" and "deps") and do not have a corresponding
// field in the returned module properties struct result in an error during the
// Context's parse phase.
//
// As an example, the follow code:
//
// type myModule struct {
//     properties struct {
//         Foo string
//         Bar []string
//     }
// }
//
// func NewMyModule() (blueprint.Module, []interface{}) {
//     module := new(myModule)
//     properties := &module.properties
//     return module, []interface{}{properties}
// }
//
// func main() {
//     ctx := blueprint.NewContext()
//     ctx.RegisterModuleType("my_module", NewMyModule)
//     // ...
// }
//
// would support parsing a module defined in a Blueprints file as follows:
//
// my_module {
//     name: "myName",
//     foo:  "my foo string",
//     bar:  ["my", "bar", "strings"],
// }
//
// The factory function may be called from multiple goroutines.  Any accesses
// to global variables must be synchronized.
func (c *Context) RegisterModuleType(name string, factory ModuleFactory) {
 if _, present := c.moduleFactories[name]; present {
  panic(fmt.Errorf("module type %q is already registered", name))
 }
 c.moduleFactories[name] = factory
}

// A SingletonFactory function creates a new Singleton object.  See the
// Context.RegisterSingletonType method for details about how a registered
// SingletonFactory is used by a Context.
type SingletonFactory func() Singleton

// RegisterSingletonType registers a singleton type that will be invoked to
// generate build actions.  Each registered singleton type is instantiated
// and invoked exactly once as part of the generate phase.
//
// Those singletons registered with parallel=true are run in parallel, after
// which the other registered singletons are run in registration order.
//
// The singleton type names given here must be unique for the context.  The
// factory function should be a named function so that its package and name can
// be included in the generated Ninja file for debugging purposes.
func (c *Context) RegisterSingletonType(name string, factory SingletonFactory, parallel bool) {
 for _, s := range c.singletonInfo {
  if s.name == name {
   panic(fmt.Errorf("singleton %q is already registered", name))
  }
 }

 c.singletonInfo = append(c.singletonInfo, &singletonInfo{
  factory:   factory,
  singleton: factory(),
  name:      name,
  parallel:  parallel,
 })
}

func (c *Context) SetNameInterface(i NameInterface) {
 c.nameInterface = i
}

func (c *Context) SetIncrementalAnalysis(incremental bool) {
 c.incrementalAnalysis = incremental
}

func (c *Context) GetIncrementalAnalysis() bool {
 return c.incrementalAnalysis
}

func (c *Context) SetIncrementalEnabled(incremental bool) {
 c.incrementalEnabled = incremental
}

func (c *Context) SetIncrementalProviderTest(test bool) {
 c.incrementalProviderTest = test
}

func (c *Context) GetIncrementalEnabled() bool {
 return c.incrementalEnabled
}

func (c *Context) SetIncrementalDebugFile(file string) {
 c.incrementalDebugFile = file
}

func (c *Context) SetPartialAnalysisTargets(targets string) {
 rawSlice := strings.Split(strings.TrimSpace(targets), ",")
 for _, item := range rawSlice {
  cleanItem := strings.TrimSpace(item)
  if cleanItem != "" {
   c.partialAnalysisTargets = append(c.partialAnalysisTargets, cleanItem)
  }
 }
}

func (c *Context) GetPartialAnalysisTargets() []string {
 return c.partialAnalysisTargets
}

func (c *Context) GetSplitAllVariants() bool {
 return c.splitAllVariants
}

func (c *Context) SetSplitAllVariants(s bool) {
 c.splitAllVariants = s
}

func (c *Context) CacheAllBuildActions(soongOutDir string) (err error) {
 if err := cacheEncData(c, soongOutDir, OrderOnlyStringsCacheFile, &c.orderOnlyStringsCache); err != nil {
  return err
 }
 defer func() {
  err = errors.Join(err, c.keyValueStoreCache.close())
 }()
 err = c.EncContext.EncodeReferences()
 return err
}

func cacheEncData(ctx *Context, soongOutDir string, fileName string, data gobtools.CustomEnc) error {
 buf := new(bytes.Buffer)
 if err := data.Encode(ctx.EncContext, buf); err != nil {
  return err
 }
 return writeToCache(ctx, soongOutDir, fileName, buf)
}

func writeToCache(ctx *Context, soongOutDir string, fileName string, buf *bytes.Buffer) error {
 file, err := pathtools.OpenWithTruncateOnClose(ctx.fs, filepath.Join(ctx.SrcDir(), soongOutDir, fileName))
 if err != nil {
  return err
 }
 defer file.Close()

 _, err = file.Write(buf.Bytes())
 return err
}

func (c *Context) RestoreAllBuildActions(soongOutDir string) error {
 return restoreEncData(c, soongOutDir, OrderOnlyStringsCacheFile, &c.orderOnlyStringsCache)
}

func restoreEncData(ctx *Context, soongOutDir string, fileName string, data gobtools.CustomDec) error {
 if stream, err := restoreFromCache(ctx, soongOutDir, fileName); err == nil && stream != nil {
  return data.Decode(ctx.EncContext, bytes.NewReader(stream))
 } else {
  return err
 }
}

func restoreFromCache(ctx *Context, soongOutDir string, fileName string) ([]byte, error) {
 file := filepath.Join(ctx.SrcDir(), soongOutDir, fileName)
 if _, err := os.Stat(file); os.IsNotExist(err) {
  return nil, nil
 }

 return os.ReadFile(file)
}

func (c *Context) SetSrcDir(path string) {
 c.srcDir = path
 c.fs = pathtools.NewOsFs(path)
}

func (c *Context) SrcDir() string {
 return c.srcDir
}

func (c *Context) SetIncrementalDBDir(path string) {
 c.incrementalDBDir = path
}

func (c *Context) IncrementalDBDir() string {
 return c.incrementalDBDir
}

func singletonPkgPath(singleton Singleton) string {
 typ := reflect.TypeOf(singleton)
 for typ.Kind() == reflect.Ptr {
  typ = typ.Elem()
 }
 return typ.PkgPath()
}

func singletonTypeName(singleton Singleton) string {
 typ := reflect.TypeOf(singleton)
 for typ.Kind() == reflect.Ptr {
  typ = typ.Elem()
 }
 return typ.PkgPath() + "." + typ.Name()
}

// registerTransitionPropagateMutator registers a mutator that will be invoked to propagate transition mutator
// configuration info top-down between Modules.
func (c *Context) registerTransitionPropagateMutator(name string, mutator func(mctx BaseModuleContext)) MutatorHandle {
 for _, m := range c.mutatorInfo {
  if m.name == name && m.transitionPropagateMutator != nil {
   panic(fmt.Errorf("mutator %q is already registered", name))
  }
 }

 info := &mutatorInfo{
  transitionPropagateMutator: mutator,

  name:  name,
  index: len(c.mutatorInfo),
 }

 c.mutatorInfo = append(c.mutatorInfo, info)

 return info
}

// RegisterBottomUpMutator registers a mutator that will be invoked to split Modules into variants.
// Each registered mutator is invoked in registration order once per Module, and will not be invoked on a
// module until the invocations on all of the modules dependencies have returned.
//
// The mutator type names given here must be unique to all bottom up or early
// mutators in the Context.
func (c *Context) RegisterBottomUpMutator(name string, mutator BottomUpMutator) MutatorHandle {
 for _, m := range c.variantMutatorNames {
  if m == name {
   panic(fmt.Errorf("mutator %q is already registered", name))
  }
 }

 info := &mutatorInfo{
  bottomUpMutator: mutator,
  name:            name,
  index:           len(c.mutatorInfo),
 }
 c.mutatorInfo = append(c.mutatorInfo, info)

 c.variantMutatorNames = append(c.variantMutatorNames, name)

 return info
}

// RegisterFirstBottomUpMutator registers a mutator that will be invoked to split Modules into variants.
// The registered mutator is placed at the front of the list.
//
// The mutator type names given here must be unique to all bottom up mutators in the Context.
func (c *Context) RegisterFirstBottomUpMutator(name string, mutator BottomUpMutator) MutatorHandle {
 for _, m := range c.variantMutatorNames {
  if m == name {
   panic(fmt.Errorf("mutator %q is already registered", name))
  }
 }

 info := &mutatorInfo{
  bottomUpMutator: mutator,
  name:            name,
  index:           0,
 }
 c.mutatorInfo = append([]*mutatorInfo{info}, c.mutatorInfo...)
 c.variantMutatorNames = append([]string{name}, c.variantMutatorNames...)
 for i := range c.mutatorInfo {
  c.mutatorInfo[i].index = i
 }

 return info
}

// HasMutatorFinished returns true if the given mutator has finished running.
// It will panic if given an invalid mutator name.
func (c *Context) HasMutatorFinished(mutatorName string) bool {
 for _, mutator := range c.mutatorInfo {
  if mutator.name == mutatorName {
   return len(c.finishedMutators) > mutator.index && c.finishedMutators[mutator.index]
  }
 }
 panic(fmt.Sprintf("unknown mutator %q", mutatorName))
}

type MutatorHandle interface {
 // UsesRename marks the mutator as using the BottomUpMutatorContext.Rename method, which prevents
 // coalescing adjacent mutators into a single mutator pass.
 UsesRename() MutatorHandle

 // UsesReverseDependencies marks the mutator as using the BottomUpMutatorContext.AddReverseDependency
 // method, which prevents coalescing adjacent mutators into a single mutator pass.
 UsesReverseDependencies() MutatorHandle

 // UsesReplaceDependencies marks the mutator as using the BottomUpMutatorContext.ReplaceDependencies
 // method, which prevents coalescing adjacent mutators into a single mutator pass.
 UsesReplaceDependencies() MutatorHandle

 // UsesCreateModule marks the mutator as using the BottomUpMutatorContext.CreateModule method,
 // which prevents coalescing adjacent mutators into a single mutator pass.
 UsesCreateModule() MutatorHandle

 // MutatesDependencies marks the mutator as modifying properties in dependencies, which prevents
 // coalescing adjacent mutators into a single mutator pass.
 MutatesDependencies() MutatorHandle

 // MutatesGlobalState marks the mutator as modifying global state, which prevents coalescing
 // adjacent mutators into a single mutator pass.
 MutatesGlobalState() MutatorHandle

 PrePartial() MutatorHandle

 setTransitionMutator(impl *transitionMutatorImpl) MutatorHandle
}

func (mutator *mutatorInfo) UsesRename() MutatorHandle {
 mutator.usesRename = true
 return mutator
}

func (mutator *mutatorInfo) UsesReverseDependencies() MutatorHandle {
 mutator.usesReverseDependencies = true
 return mutator
}

func (mutator *mutatorInfo) UsesReplaceDependencies() MutatorHandle {
 mutator.usesReplaceDependencies = true
 return mutator
}

func (mutator *mutatorInfo) UsesCreateModule() MutatorHandle {
 mutator.usesCreateModule = true
 return mutator
}

func (mutator *mutatorInfo) MutatesDependencies() MutatorHandle {
 mutator.mutatesDependencies = true
 return mutator
}

func (mutator *mutatorInfo) MutatesGlobalState() MutatorHandle {
 mutator.mutatesGlobalState = true
 return mutator
}

func (mutator *mutatorInfo) PrePartial() MutatorHandle {
 mutator.prePartial = true
 return mutator
}

func (mutator *mutatorInfo) setTransitionMutator(impl *transitionMutatorImpl) MutatorHandle {
 mutator.transitionMutator = impl
 return mutator
}

// SetIgnoreUnknownModuleTypes sets the behavior of the context in the case
// where it encounters an unknown module type while parsing Blueprints files. By
// default, the context will report unknown module types as an error.  If this
// method is called with ignoreUnknownModuleTypes set to true then the context
// will silently ignore unknown module types.
//
// This method should generally not be used.  It exists to facilitate the
// bootstrapping process.
func (c *Context) SetIgnoreUnknownModuleTypes(ignoreUnknownModuleTypes bool) {
 c.ignoreUnknownModuleTypes = ignoreUnknownModuleTypes
}

// SetAllowMissingDependencies changes the behavior of Blueprint to ignore
// unresolved dependencies.  If the module's GenerateBuildActions calls
// ModuleContext.GetMissingDependencies Blueprint will not emit any errors
// for missing dependencies.
func (c *Context) SetAllowMissingDependencies(allowMissingDependencies bool) {
 c.allowMissingDependencies = allowMissingDependencies
}

func (c *Context) SetModuleListFile(listFile string) {
 c.moduleListFile = listFile
}

func (c *Context) ListModulePaths(baseDir string) (paths []string, err error) {
 reader, err := c.fs.Open(c.moduleListFile)
 if err != nil {
  return nil, err
 }
 defer reader.Close()
 bytes, err := ioutil.ReadAll(reader)
 if err != nil {
  return nil, err
 }
 text := string(bytes)

 text = strings.Trim(text, "\n")
 lines := strings.Split(text, "\n")
 for i := range lines {
  lines[i] = filepath.Join(baseDir, lines[i])
 }

 return lines, nil
}

// a fileParseContext tells the status of parsing a particular file
type fileParseContext struct {
 // name of file
 fileName string

 // scope to use when resolving variables
 Scope *parser.Scope

 // pointer to the one in the parent directory
 parent *fileParseContext

 // is closed once FileHandler has completed for this file
 doneVisiting chan struct{}
}

// ParseBlueprintsFiles parses a set of Blueprints files starting with the file
// at rootFile.  When it encounters a Blueprints file with a set of subdirs
// listed it recursively parses any Blueprints files found in those
// subdirectories.
//
// If no errors are encountered while parsing the files, the list of paths on
// which the future output will depend is returned.  This list will include both
// Blueprints file paths as well as directory paths for cases where wildcard
// subdirs are found.
func (c *Context) ParseBlueprintsFiles(rootFile string,
 config interface{}) (deps []string, errs []error) {

 baseDir := filepath.Dir(rootFile)
 pathsToParse, err := c.ListModulePaths(baseDir)
 if err != nil {
  return nil, []error{err}
 }
 return c.ParseFileList(baseDir, pathsToParse, config)
}

type shouldVisitFileInfo struct {
 shouldVisitFile bool
 skippedModules  []string
 reasonForSkip   string
 errs            []error
}

// Returns a boolean for whether this file should be analyzed
// Evaluates to true if the file either
// 1. does not contain a blueprint_package_includes
// 2. contains a blueprint_package_includes and all requested tags are set
// This should be processed before adding any modules to the build graph
func shouldVisitFile(c *Context, file *parser.File) shouldVisitFileInfo {
 skippedModules := []string{}
 for _, def := range file.Defs {
  switch def := def.(type) {
  case *parser.Module:
   skippedModules = append(skippedModules, def.Name())
  }
 }

 shouldVisit, invalidatingPrefix := c.sourceRootDirs.SourceRootDirAllowed(file.Name)
 if !shouldVisit {
  return shouldVisitFileInfo{
   shouldVisitFile: shouldVisit,
   skippedModules:  skippedModules,
   reasonForSkip: fmt.Sprintf(
    "%q is a descendant of %q, and that path prefix was not included in PRODUCT_SOURCE_ROOT_DIRS",
    file.Name,
    invalidatingPrefix,
   ),
  }
 }
 return shouldVisitFileInfo{shouldVisitFile: true}
}

func (c *Context) ParseFileList(rootDir string, filePaths []string,
 config interface{}) (deps []string, errs []error) {

 if len(filePaths) < 1 {
  return nil, []error{fmt.Errorf("no paths provided to parse")}
 }

 c.dependenciesReady = false

 type newModuleInfo struct {
  *moduleInfo
  deps  []string
  added chan<- struct{}
 }

 type newSkipInfo struct {
  shouldVisitFileInfo
  file string
 }

 moduleCh := make(chan newModuleInfo)
 errsCh := make(chan []error)
 doneCh := make(chan struct{})
 skipCh := make(chan newSkipInfo)
 var numErrs uint32
 var numGoroutines int32

 // handler must be reentrant
 handleOneFile := func(file *parser.File) {
  if atomic.LoadUint32(&numErrs) > maxErrors {
   return
  }

  addedCh := make(chan struct{})

  var scopedModuleFactories map[string]ModuleFactory

  var addModule func(module *moduleInfo) []error
  addModule = func(module *moduleInfo) []error {
   // Run any load hooks immediately before it is sent to the moduleCh and is
   // registered by name. This allows load hooks to set and/or modify any aspect
   // of the module (including names) using information that is not available when
   // the module factory is called.
   newModules, newDeps, errs := runAndRemoveLoadHooks(c, config, module, &scopedModuleFactories)
   if len(errs) > 0 {
    return errs
   }

   moduleCh <- newModuleInfo{module, newDeps, addedCh}
   <-addedCh
   for _, n := range newModules {
    errs = addModule(n)
    if len(errs) > 0 {
     return errs
    }
   }
   return nil
  }
  shouldVisitInfo := shouldVisitFile(c, file)
  errs := shouldVisitInfo.errs
  if len(errs) > 0 {
   atomic.AddUint32(&numErrs, uint32(len(errs)))
   errsCh <- errs
  }
  if !shouldVisitInfo.shouldVisitFile {
   skipCh <- newSkipInfo{
    file:                file.Name,
    shouldVisitFileInfo: shouldVisitInfo,
   }
   // TODO: Write a file that lists the skipped bp files
   return
  }

  for _, def := range file.Defs {
   switch def := def.(type) {
   case *parser.Module:
    module, errs := processModuleDef(def, file.Name, c.moduleFactories, scopedModuleFactories, c.ignoreUnknownModuleTypes)
    if len(errs) == 0 && module != nil {
     errs = addModule(module)
    }

    if len(errs) > 0 {
     atomic.AddUint32(&numErrs, uint32(len(errs)))
     errsCh <- errs
    }

   case *parser.Assignment:
    // Already handled via Scope object
   default:
    panic("unknown definition type")
   }

  }
 }

 atomic.AddInt32(&numGoroutines, 1)
 go func() {
  var errs []error
  deps, errs = c.WalkBlueprintsFiles(rootDir, filePaths, handleOneFile)
  if len(errs) > 0 {
   errsCh <- errs
  }
  doneCh <- struct{}{}
 }()

 var hookDeps []string
loop:
 for {
  select {
  case newErrs := <-errsCh:
   errs = append(errs, newErrs...)
  case module := <-moduleCh:
   newErrs := c.addModule(module.moduleInfo)
   hookDeps = append(hookDeps, module.deps...)
   if module.added != nil {
    module.added <- struct{}{}
   }
   if len(newErrs) > 0 {
    errs = append(errs, newErrs...)
   }
  case <-doneCh:
   n := atomic.AddInt32(&numGoroutines, -1)
   if n == 0 {
    break loop
   }
  case skipped := <-skipCh:
   nctx := newNamespaceContextFromFilename(skipped.file)
   for _, name := range skipped.skippedModules {
    c.nameInterface.NewSkippedModule(nctx, name, SkippedModuleInfo{
     filename: skipped.file,
     reason:   skipped.reasonForSkip,
    })
   }
  }
 }

 sort.Strings(hookDeps)

 deps = append(deps, hookDeps...)
 return deps, errs
}

type FileHandler func(*parser.File)

// WalkBlueprintsFiles walks a set of Blueprints files starting with the given filepaths,
// calling the given file handler on each
//
// When WalkBlueprintsFiles encounters a Blueprints file with a set of subdirs listed,
// it recursively parses any Blueprints files found in those subdirectories.
//
// If any of the file paths is an ancestor directory of any other of file path, the ancestor
// will be parsed and visited first.
//
// the file handler will be called from a goroutine, so it must be reentrant.
//
// If no errors are encountered while parsing the files, the list of paths on
// which the future output will depend is returned.  This list will include both
// Blueprints file paths as well as directory paths for cases where wildcard
// subdirs are found.
//
// visitor will be called asynchronously, and will only be called once visitor for each
// ancestor directory has completed.
//
// WalkBlueprintsFiles will not return until all calls to visitor have returned.
func (c *Context) WalkBlueprintsFiles(rootDir string, filePaths []string,
 visitor FileHandler) (deps []string, errs []error) {

 // make a mapping from ancestors to their descendants to facilitate parsing ancestors first
 descendantsMap, err := findBlueprintDescendants(filePaths)
 if err != nil {
  panic(err.Error())
 }
 blueprintsSet := make(map[string]bool)

 // Channels to receive data back from openAndParse goroutines
 blueprintsCh := make(chan fileParseContext)
 errsCh := make(chan []error)
 depsCh := make(chan string)

 // Channel to notify main loop that a openAndParse goroutine has finished
 doneParsingCh := make(chan fileParseContext)

 // Number of outstanding goroutines to wait for
 activeCount := 0
 var pending []fileParseContext
 tooManyErrors := false

 // Limit concurrent calls to parseBlueprintFiles to 200
 // Darwin has a default limit of 256 open files
 maxActiveCount := 200

 // count the number of pending calls to visitor()
 visitorWaitGroup := sync.WaitGroup{}

 startParseBlueprintsFile := func(blueprint fileParseContext) {
  if blueprintsSet[blueprint.fileName] {
   return
  }
  blueprintsSet[blueprint.fileName] = true
  activeCount++
  deps = append(deps, blueprint.fileName)
  visitorWaitGroup.Add(1)
  go func() {
   file, blueprints, deps, errs := c.openAndParse(blueprint.fileName, blueprint.Scope, rootDir,
    &blueprint)
   if len(errs) > 0 {
    errsCh <- errs
   }
   for _, blueprint := range blueprints {
    blueprintsCh <- blueprint
   }
   for _, dep := range deps {
    depsCh <- dep
   }
   doneParsingCh <- blueprint

   if blueprint.parent != nil && blueprint.parent.doneVisiting != nil {
    // wait for visitor() of parent to complete
    <-blueprint.parent.doneVisiting
   }

   if len(errs) == 0 {
    // process this file
    visitor(file)
   }
   if blueprint.doneVisiting != nil {
    close(blueprint.doneVisiting)
   }
   visitorWaitGroup.Done()
  }()
 }

 foundParseableBlueprint := func(blueprint fileParseContext) {
  if activeCount >= maxActiveCount {
   pending = append(pending, blueprint)
  } else {
   startParseBlueprintsFile(blueprint)
  }
 }

 startParseDescendants := func(blueprint fileParseContext) {
  descendants, hasDescendants := descendantsMap[blueprint.fileName]
  if hasDescendants {
   for _, descendant := range descendants {
    foundParseableBlueprint(fileParseContext{descendant, parser.NewScope(blueprint.Scope), &blueprint, make(chan struct{})})
   }
  }
 }

 // begin parsing any files that have no ancestors
 startParseDescendants(fileParseContext{"", parser.NewScope(nil), nil, nil})

loop:
 for {
  if len(errs) > maxErrors {
   tooManyErrors = true
  }

  select {
  case newErrs := <-errsCh:
   errs = append(errs, newErrs...)
  case dep := <-depsCh:
   deps = append(deps, dep)
  case blueprint := <-blueprintsCh:
   if tooManyErrors {
    continue
   }
   foundParseableBlueprint(blueprint)
  case blueprint := <-doneParsingCh:
   activeCount--
   if !tooManyErrors {
    startParseDescendants(blueprint)
   }
   if activeCount < maxActiveCount && len(pending) > 0 {
    // start to process the next one from the queue
    next := pending[len(pending)-1]
    pending = pending[:len(pending)-1]
    startParseBlueprintsFile(next)
   }
   if activeCount == 0 {
    break loop
   }
  }
 }

 sort.Strings(deps)

 // wait for every visitor() to complete
 visitorWaitGroup.Wait()

 return
}

// MockFileSystem causes the Context to replace all reads with accesses to the provided map of
// filenames to contents stored as a byte slice.
func (c *Context) MockFileSystem(files map[string][]byte) {
 // look for a module list file
 _, ok := files[MockModuleListFile]
 if !ok {
  // no module list file specified; find every file named Blueprints
  pathsToParse := []string{}
  for candidate := range files {
   if filepath.Base(candidate) == "Android.bp" {
    pathsToParse = append(pathsToParse, candidate)
   }
  }
  if len(pathsToParse) < 1 {
   panic(fmt.Sprintf("No Blueprints files found in mock filesystem: %v\n", files))
  }
  // put the list of Blueprints files into a list file
  files[MockModuleListFile] = []byte(strings.Join(pathsToParse, "\n"))
 }
 c.SetModuleListFile(MockModuleListFile)

 // mock the filesystem
 c.fs = pathtools.MockFs(files)
}

func (c *Context) SetFs(fs pathtools.FileSystem) {
 c.fs = fs
}

// openAndParse opens and parses a single Blueprints file, and returns the results
func (c *Context) openAndParse(filename string, scope *parser.Scope, rootDir string,
 parent *fileParseContext) (file *parser.File,
 subBlueprints []fileParseContext, deps []string, errs []error) {

 f, err := c.fs.Open(filename)
 if err != nil {
  // couldn't open the file; see if we can provide a clearer error than "could not open file"
  stats, statErr := c.fs.Lstat(filename)
  if statErr == nil {
   isSymlink := stats.Mode()&os.ModeSymlink != 0
   if isSymlink {
    err = fmt.Errorf("could not open symlink %v : %v", filename, err)
    target, readlinkErr := os.Readlink(filename)
    if readlinkErr == nil {
     _, targetStatsErr := c.fs.Lstat(target)
     if targetStatsErr != nil {
      err = fmt.Errorf("could not open symlink %v; its target (%v) cannot be opened", filename, target)
     }
    }
   } else {
    err = fmt.Errorf("%v exists but could not be opened: %v", filename, err)
   }
  }
  return nil, nil, nil, []error{err}
 }

 func() {
  defer func() {
   err = f.Close()
   if err != nil {
    errs = append(errs, err)
   }
  }()
  file, subBlueprints, errs = c.parseOne(rootDir, filename, f, scope, parent)
 }()

 if len(errs) > 0 {
  return nil, nil, nil, errs
 }

 for _, b := range subBlueprints {
  deps = append(deps, b.fileName)
 }

 return file, subBlueprints, deps, nil
}

// parseOne parses a single Blueprints file from the given reader, creating Module
// objects for each of the module definitions encountered.  If the Blueprints
// file contains an assignment to the "subdirs" variable, then the
// subdirectories listed are searched for Blueprints files returned in the
// subBlueprints return value.  If the Blueprints file contains an assignment
// to the "build" variable, then the file listed are returned in the
// subBlueprints return value.
//
// rootDir specifies the path to the root directory of the source tree, while
// filename specifies the path to the Blueprints file.  These paths are used for
// error reporting and for determining the module's directory.
func (c *Context) parseOne(rootDir, filename string, reader io.Reader,
 scope *parser.Scope, parent *fileParseContext) (file *parser.File, subBlueprints []fileParseContext, errs []error) {

 relBlueprintsFile, err := filepath.Rel(rootDir, filename)
 if err != nil {
  return nil, nil, []error{err}
 }

 scope.DontInherit("subdirs")
 scope.DontInherit("optional_subdirs")
 scope.DontInherit("build")
 file, errs = parser.ParseAndEval(filename, reader, scope)
 if len(errs) > 0 {
  for i, err := range errs {
   if parseErr, ok := err.(*parser.ParseError); ok {
    err = &BlueprintError{
     Err: parseErr.Err,
     Pos: parseErr.Pos,
    }
    errs[i] = err
   }
  }

  // If there were any parse errors don't bother trying to interpret the
  // result.
  return nil, nil, errs
 }
 file.Name = relBlueprintsFile

 build, buildPos, err := getLocalStringListFromScope(scope, "build")
 if err != nil {
  errs = append(errs, err)
 }
 for _, buildEntry := range build {
  if strings.Contains(buildEntry, "/") {
   errs = append(errs, &BlueprintError{
    Err: fmt.Errorf("illegal value %v. The '/' character is not permitted", buildEntry),
    Pos: buildPos,
   })
  }
 }

 if err != nil {
  errs = append(errs, err)
 }

 var blueprints []string

 newBlueprints, newErrs := c.findBuildBlueprints(filepath.Dir(filename), build, buildPos)
 blueprints = append(blueprints, newBlueprints...)
 errs = append(errs, newErrs...)

 subBlueprintsAndScope := make([]fileParseContext, len(blueprints))
 for i, b := range blueprints {
  subBlueprintsAndScope[i] = fileParseContext{b, parser.NewScope(scope), parent, make(chan struct{})}
 }
 return file, subBlueprintsAndScope, errs
}

func (c *Context) findBuildBlueprints(dir string, build []string,
 buildPos scanner.Position) ([]string, []error) {

 var blueprints []string
 var errs []error

 for _, file := range build {
  pattern := filepath.Join(dir, file)
  var matches []string
  var err error

  matches, err = c.glob(pattern, nil)

  if err != nil {
   errs = append(errs, &BlueprintError{
    Err: fmt.Errorf("%q: %s", pattern, err.Error()),
    Pos: buildPos,
   })
   continue
  }

  if len(matches) == 0 {
   errs = append(errs, &BlueprintError{
    Err: fmt.Errorf("%q: not found", pattern),
    Pos: buildPos,
   })
  }

  for _, foundBlueprints := range matches {
   if strings.HasSuffix(foundBlueprints, "/") {
    errs = append(errs, &BlueprintError{
     Err: fmt.Errorf("%q: is a directory", foundBlueprints),
     Pos: buildPos,
    })
   }
   blueprints = append(blueprints, foundBlueprints)
  }
 }

 return blueprints, errs
}

func (c *Context) findSubdirBlueprints(dir string, subdirs []string, subdirsPos scanner.Position,
 subBlueprintsName string, optional bool) ([]string, []error) {

 var blueprints []string
 var errs []error

 for _, subdir := range subdirs {
  pattern := filepath.Join(dir, subdir, subBlueprintsName)
  var matches []string
  var err error

  matches, err = c.glob(pattern, nil)

  if err != nil {
   errs = append(errs, &BlueprintError{
    Err: fmt.Errorf("%q: %s", pattern, err.Error()),
    Pos: subdirsPos,
   })
   continue
  }

  if len(matches) == 0 && !optional {
   errs = append(errs, &BlueprintError{
    Err: fmt.Errorf("%q: not found", pattern),
    Pos: subdirsPos,
   })
  }

  for _, subBlueprints := range matches {
   if strings.HasSuffix(subBlueprints, "/") {
    errs = append(errs, &BlueprintError{
     Err: fmt.Errorf("%q: is a directory", subBlueprints),
     Pos: subdirsPos,
    })
   }
   blueprints = append(blueprints, subBlueprints)
  }
 }

 return blueprints, errs
}

func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) {
 if assignment := scope.GetLocal(v); assignment == nil {
  return nil, scanner.Position{}, nil
 } else {
  switch value := assignment.Value.(type) {
  case *parser.List:
   ret := make([]string, 0, len(value.Values))

   for _, listValue := range value.Values {
    s, ok := listValue.(*parser.String)
    if !ok {
     // The parser should not produce this.
     panic("non-string value found in list")
    }

    ret = append(ret, s.Value)
   }

   return ret, assignment.EqualsPos, nil
  case *parser.Bool, *parser.String:
   return nil, scanner.Position{}, &BlueprintError{
    Err: fmt.Errorf("%q must be a list of strings", v),
    Pos: assignment.EqualsPos,
   }
  default:
   panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type()))
  }
 }
}

// Clones a build logic module by calling the factory method for its module type, and then cloning
// property values.  Any values stored in the module object that are not stored in properties
// structs will be lost.
func (c *Context) cloneLogicModule(origModule *moduleInfo) (Module, []interface{}) {
 newLogicModule, newProperties := origModule.factory()

 if len(newProperties) != len(origModule.properties) {
  panic("mismatched properties array length in " + origModule.Name())
 }

 for i := range newProperties {
  dst := reflect.ValueOf(newProperties[i])
  src := reflect.ValueOf(origModule.properties[i])

  proptools.CopyProperties(dst, src)
 }

 return newLogicModule, newProperties
}

func newVariant(module *moduleInfo, mutatorName string, variationName string) variant {

 newVariantName := module.variant.name
 if variationName != "" {
  if newVariantName == "" {
   newVariantName = variationName
  } else {
   newVariantName += "_" + variationName
  }
 }

 newVariations := module.variant.variations.clone()
 newVariations.set(mutatorName, variationName)

 return variant{newVariantName, newVariations}
}

func (c *Context) createVariations(origModule *moduleInfo, mutator *mutatorInfo,
 depChooser depChooser, variationNames []string) (moduleList, []error) {

 if mutator.transitionMutator == nil {
  panic(fmt.Errorf("method createVariations called from mutator that was not a TransitionMutator"))
 }

 if len(variationNames) == 0 {
  panic(fmt.Errorf("mutator %q passed zero-length variation list for module %q",
   mutator.name, origModule.Name()))
 }

 var newModules moduleList

 var errs []error

 for i, variationName := range variationNames {
  var newLogicModule Module
  var newProperties []interface{}

  if i == 0 {
   // Reuse the existing module for the first new variant
   // This both saves creating a new module, and causes the insertion in c.moduleInfo below
   // with logicModule as the key to replace the original entry in c.moduleInfo
   newLogicModule, newProperties = origModule.logicModule, origModule.properties
  } else {
   newLogicModule, newProperties = c.cloneLogicModule(origModule)
  }

  m := *origModule
  newModule := &m
  newLogicModule.setInfo(newModule)
  newModule.directDeps = slices.Clone(origModule.directDeps)
  newModule.reverseDeps = nil
  newModule.forwardDeps = nil
  newModule.logicModule = newLogicModule
  newModule.variant = newVariant(origModule, mutator.name, variationName)
  newModule.properties = newProperties
  newModule.providers = slices.Clone(origModule.providers)
  newModule.providerInitialValueHashes = slices.Clone(origModule.providerInitialValueHashes)
  newModule.transitionInfos = slices.Clone(origModule.transitionInfos)

  newModules = append(newModules, newModule)

  newErrs := c.convertDepsToVariation(newModule, i, depChooser)
  if len(newErrs) > 0 {
   errs = append(errs, newErrs...)
  }
 }

 // Mark original variant as invalid.  Modules that depend on this module will still
 // depend on origModule, but we'll fix it when the mutator is called on them.
 origModule.obsoletedByNewVariants = true
 origModule.splitModules = newModules

 atomic.AddUint32(&c.needsUpdateDependencies, 1)

 return newModules, errs
}

type depChooser func(source *moduleInfo, variationIndex, depIndex int, dep depInfo) (*moduleInfo, string)

func chooseDep(candidates moduleList, mutatorName, variationName string, defaultVariationName *string) (*moduleInfo, string) {
 for _, m := range candidates {
  if m.variant.variations.get(mutatorName) == variationName {
   return m, ""
  }
 }

 if defaultVariationName != nil {
  // give it a second chance; match with defaultVariationName
  for _, m := range candidates {
   if m.variant.variations.get(mutatorName) == *defaultVariationName {
    return m, ""
   }
  }
 }

 return nil, variationName
}

func chooseDepByIndexes(mutatorName string, variations [][]string) depChooser {
 return func(source *moduleInfo, variationIndex, depIndex int, dep depInfo) (*moduleInfo, string) {
  desiredVariation := variations[variationIndex][depIndex]
  return chooseDep(dep.module.splitModules, mutatorName, desiredVariation, nil)
 }
}

func (c *Context) convertDepsToVariation(module *moduleInfo, variationIndex int, depChooser depChooser) (errs []error) {
 for i, dep := range module.directDeps {
  if dep.module.obsoletedByNewVariants {
   newDep, missingVariation := depChooser(module, variationIndex, i, dep)
   if newDep == nil {
    errs = append(errs, &BlueprintError{
     Err: fmt.Errorf("failed to find variation %q for module %q needed by %q",
      missingVariation, dep.module.Name(), module.Name()),
     Pos: module.pos,
    })
    continue
   }
   module.directDeps[i].module = newDep
  }
 }

 return errs
}

func (c *Context) prettyPrintVariant(variations variationMap) string {
 var names []string
 for _, m := range c.variantMutatorNames {
  if v := variations.get(m); v != "" {
   names = append(names, m+":"+v)
  }
 }
 if len(names) == 0 {
  return "<empty variant>"
 }

 return strings.Join(names, ",")
}

func (c *Context) prettyPrintGroupVariants(group *moduleGroup) string {
 var variants []string
 for _, module := range group.modules {
  variants = append(variants, c.prettyPrintVariant(module.variant.variations))
 }
 return strings.Join(variants, "\n  ")
}

func newModule(factory ModuleFactory) *moduleInfo {
 logicModule, properties := factory()

 moduleInfo := &moduleInfo{
  logicModule:     logicModule,
  factory:         factory,
  properties:      properties,
  startedMutator:  -1,
  finishedMutator: -1,
 }
 logicModule.setInfo(moduleInfo)
 return moduleInfo
}

func processModuleDef(moduleDef *parser.Module,
 relBlueprintsFile string, moduleFactories, scopedModuleFactories map[string]ModuleFactory, ignoreUnknownModuleTypes bool) (*moduleInfo, []error) {

 factory, ok := moduleFactories[moduleDef.Type]
 if !ok && scopedModuleFactories != nil {
  factory, ok = scopedModuleFactories[moduleDef.Type]
 }
 if !ok {
  if ignoreUnknownModuleTypes {
   return nil, nil
  }

  return nil, []error{
   &BlueprintError{
    Err: fmt.Errorf("unrecognized module type %q", moduleDef.Type),
    Pos: moduleDef.TypePos,
   },
  }
 }

 module := newModule(factory)
 module.typeName = moduleDef.Type

 module.relBlueprintsFile = relBlueprintsFile

 propertyMap, errs := proptools.UnpackProperties(moduleDef.Properties, module.properties...)
 if len(errs) > 0 {
  for i, err := range errs {
   if unpackErr, ok := err.(*proptools.UnpackError); ok {
    err = &BlueprintError{
     Err: unpackErr.Err,
     Pos: unpackErr.Pos,
    }
    errs[i] = err
   }
  }
  return nil, errs
 }

 module.pos = moduleDef.TypePos
 module.propertyPos = make(map[string]scanner.Position)
 for name, propertyDef := range propertyMap {
  module.propertyPos[name] = propertyDef.ColonPos
 }

 return module, nil
}

func (c *Context) addModule(module *moduleInfo) []error {
 name := module.logicModule.Name()
 if name == "" {
  return []error{
   &BlueprintError{
    Err: fmt.Errorf("property 'name' is missing from a module"),
    Pos: module.pos,
   },
  }
 }

 group := &moduleGroup{
  name:    name,
  modules: moduleList{module},
 }
 module.group = group
 namespace, errs := c.nameInterface.NewModule(
  newNamespaceContext(module),
  ModuleGroup{moduleGroup: group},
  module.logicModule)
 if len(errs) > 0 {
  for i := range errs {
   errs[i] = &BlueprintError{Err: errs[i], Pos: module.pos}
  }
  return errs
 }
 group.namespace = namespace

 c.moduleGroups = append(c.moduleGroups, group)

 return nil
}

// ResolveDependencies checks that the dependencies specified by all of the
// modules defined in the parsed Blueprints files are valid.  This means that
// the modules depended upon are defined and that no circular dependencies
// exist.
func (c *Context) ResolveDependencies(config interface{}) (deps []string, errs []error) {
 c.BeginEvent("resolve_deps")
 defer c.EndEvent("resolve_deps")
 return c.resolveDependencies(c.Context, config)
}

// coalesceMutators takes the list of mutators and returns a list of lists of mutators,
// where sublist is a compatible group of mutators that can be run with relaxed
// intra-mutator ordering.
func coalesceMutators(mutators []*mutatorInfo) [][]*mutatorInfo {
 var coalescedMutators [][]*mutatorInfo
 var last *mutatorInfo

 // Returns true if the mutator can be coalesced with other mutators that
 // also return true.
 coalescable := func(m *mutatorInfo) bool {
  return m.bottomUpMutator != nil &&
   m.transitionMutator == nil &&
   !m.usesCreateModule &&
   !m.usesReplaceDependencies &&
   !m.usesReverseDependencies &&
   !m.usesRename &&
   !m.mutatesGlobalState &&
   !m.mutatesDependencies &&
   // No mix of pre partial marker mutator with others.
   !m.prePartial
 }

 for _, mutator := range mutators {
  if last != nil && coalescable(last) && coalescable(mutator) {
   lastGroup := &coalescedMutators[len(coalescedMutators)-1]
   *lastGroup = append(*lastGroup, mutator)
  } else {
   coalescedMutators = append(coalescedMutators, []*mutatorInfo{mutator})
   last = mutator
  }
 }

 return coalescedMutators
}

func (c *Context) resolveDependencies(ctx context.Context, config interface{}) (deps []string, errs []error) {
 pprof.Do(ctx, pprof.Labels("blueprint", "ResolveDependencies"), func(ctx context.Context) {
  c.initProviders()

  errs = c.updateDependencies()
  if len(errs) > 0 {
   return
  }

  mutatorGroups := coalesceMutators(c.mutatorInfo)

  deps, errs = c.runMutators(ctx, config, mutatorGroups)
  if len(errs) > 0 {
   return
  }

  c.dependenciesReady = true
 })

 if len(errs) > 0 {
  return nil, errs
 }

 return deps, nil
}

// Default dependencies handling.  If the module implements the (deprecated)
// DynamicDependerModule interface then this set consists of the union of those
// module names returned by its DynamicDependencies method and those added by calling
// AddDependencies or AddVariationDependencies on DynamicDependencyModuleContext.
func blueprintDepsMutator(ctx BottomUpMutatorContext) {
 if dynamicDepender, ok := ctx.Module().(DynamicDependerModule); ok {
  func() {
   defer func() {
    if r := recover(); r != nil {
     ctx.error(newPanicErrorf(r, "DynamicDependencies for %s", ctx.moduleInfo()))
    }
   }()
   dynamicDeps := dynamicDepender.DynamicDependencies(ctx)

   if ctx.Failed() {
    return
   }

   ctx.AddDependency(ctx.Module(), nil, dynamicDeps...)
  }()
 }
}

// applyTransitions takes a variationMap being used to add a dependency on a module in a moduleGroup
// and applies the OutgoingTransition and IncomingTransition methods of each completed TransitionMutator to
// modify the requested variation.  It finds a variant that existed before the TransitionMutator ran that is
// a subset of the requested variant to use as the module context for IncomingTransition.
//
// for onDemand: true, a copy of the dependency will be created on demand and used as the context
// for IncomingTransition.
func (c *Context) applyTransitions(config any, module *moduleInfo, depTag DependencyTag, group *moduleGroup, variant variationMap,
 requestedVariations []Variation, far bool, onDemandMatchingVariant *moduleInfo) (variationMap, []TransitionInfo, []error) {
 // Initialize some variables that will be used to manage IncomingTransition context of on demand variants.
 onDemandFromMutatorIndex := c.mutatorIndexAfterLastCreateModule

 var moduleOutgoingTransitionInfos []TransitionInfo

 for _, transitionMutator := range c.transitionMutators[:c.completedTransitionMutators] {
  explicitlyRequested := slices.ContainsFunc(requestedVariations, func(variation Variation) bool {
   return variation.Mutator == transitionMutator.name
  })

  var outgoingTransitionInfo TransitionInfo
  if explicitlyRequested {
   sourceVariation := variant.get(transitionMutator.name)
   outgoingTransitionInfo = transitionMutator.mutator.TransitionInfoFromVariation(sourceVariation)
  } else {
   // Apply the outgoing transition if it was not explicitly requested.
   var srcTransitionInfo TransitionInfo
   if (!far || transitionMutator.neverFar) && len(module.transitionInfos) > transitionMutator.index {
    srcTransitionInfo = module.transitionInfos[transitionMutator.index]
   }
   ctx := outgoingTransitionContextPool.Get()
   *ctx = outgoingTransitionContextImpl{
    transitionContextImpl{context: c, source: module, dep: nil,
     depTag: depTag, postMutator: true, config: config},
   }
   outgoingTransitionInfo = transitionMutator.mutator.OutgoingTransition(ctx, srcTransitionInfo)
   errs := ctx.errs
   outgoingTransitionContextPool.Put(ctx)
   ctx = nil
   if len(errs) > 0 {
    return variationMap{}, moduleOutgoingTransitionInfos, errs
   }
   moduleOutgoingTransitionInfos = append(moduleOutgoingTransitionInfos, outgoingTransitionInfo)
  }

  earlierVariantCreatingMutators := c.transitionMutatorNames[:transitionMutator.index]
  filteredVariant := variant.cloneMatching(earlierVariantCreatingMutators)

  // Find an appropriate module to use as the context for the IncomingTransition.
  var matchingInputVariant *moduleInfo
  for _, module := range group.modules {
   filteredInputVariant := module.variant.variations.cloneMatching(earlierVariantCreatingMutators)
   if filteredInputVariant.equal(filteredVariant) {
    matchingInputVariant = module
    break
   }
  }

  if onDemandMatchingVariant != nil {
   // Mutate the onDemand variant through a moving window,
   // from the previous transition mutator to the current transition mutator.
   // This variant can then be used as the context for the IncomingTransition.
   matchingInputVariant = onDemandMatchingVariant
   if outgoingTransitionInfo != nil {
    onDemandMatchingVariant.requestedOnDemandVariant.variations[transitionMutator.name] = outgoingTransitionInfo.Variation()
   }
   // Rerun till propagate mutator handle of the Transition mutator.
   // transitionMutator.mutatorIndex corresponds to mutate.
   // transitionMutator.mutatorIndex-1 corresponds to bottomUp handle of the transition mutator
   // transitionMutator.mutatorIndex-2 (inclusive) corresponds to propagate mutator handle of the transition mutator
   c.rerunMutatorsOnVariantOnDemand(onDemandMatchingVariant, onDemandFromMutatorIndex, transitionMutator.mutatorIndex-2, nil, config)
   onDemandFromMutatorIndex = transitionMutator.mutatorIndex - 1
  }

  if matchingInputVariant != nil {
   // Apply the incoming transition.
   ctx := incomingTransitionContextPool.Get()
   *ctx = incomingTransitionContextImpl{
    transitionContextImpl{context: c, source: nil, dep: matchingInputVariant,
     depTag: depTag, postMutator: true, config: config},
   }

   finalTransitionInfo := transitionMutator.mutator.IncomingTransition(ctx, outgoingTransitionInfo)
   errs := ctx.errs
   incomingTransitionContextPool.Put(ctx)
   ctx = nil
   if len(errs) > 0 {
    return variationMap{}, moduleOutgoingTransitionInfos, errs
   }
   variation := ""
   if finalTransitionInfo != nil {
    variation = finalTransitionInfo.Variation()
   }
   variant.set(transitionMutator.name, variation)
   // For on-demand variants, check if requested variation can be supported by `SplitOnDemand`.
   if onDemandMatchingVariant != nil && variation == "" {
    onDemandMatchingVariant.requestedOnDemandVariant.delete(transitionMutator.name)
   } else if onDemandMatchingVariant != nil {
    // Rerun till Mutate of the Transition mutator
    onDemandMatchingVariant.requestedOnDemandVariant.variations[transitionMutator.name] = variation
    c.rerunMutatorsOnVariantOnDemand(onDemandMatchingVariant, onDemandFromMutatorIndex, transitionMutator.mutatorIndex, nil, config)
    onDemandFromMutatorIndex = transitionMutator.mutatorIndex + 1
    found := false
    for _, split := range onDemandMatchingVariant.createdOnDemandSupportedSplits {
     if split.Variation() == variation {
      found = true
     }
    }
    if !found {
     onDemandMatchingVariant.createdOnDemandIncompatible = true
     return variant, moduleOutgoingTransitionInfos, nil
    }
    onDemandMatchingVariant.createdOnDemandSupportedSplits = nil // reset for next transition mutator check.
   }
  }

  if (matchingInputVariant == nil && !explicitlyRequested) || variant.get(transitionMutator.name) == "" {
   // The transition mutator didn't apply anything to the target variant, remove the variation unless it
   // was explicitly requested when adding the dependency.
   variant.delete(transitionMutator.name)
  }
 }

 return variant, moduleOutgoingTransitionInfos, nil
}

func (c *Context) findVariant(config any, module *moduleInfo, depTag DependencyTag,
 possibleDeps *moduleGroup, requestedVariations []Variation, far bool, reverse bool, mutatorIndex int) (*moduleInfo, variationMap, []error) {

 // We can't just append variant.Variant to module.dependencyVariant.variantName and
 // compare the strings because the result won't be in mutator registration order.
 // Create a new map instead, and then deep compare the maps.
 var newVariant variationMap
 if !far {
  newVariant = module.variant.variations.clone()
 } else {
  for _, transitionMutator := range c.transitionMutators {
   if transitionMutator.neverFar {
    newVariant.set(transitionMutator.name, module.variant.variations.get(transitionMutator.name))
   }
  }
 }
 for _, v := range requestedVariations {
  newVariant.set(v.Mutator, v.Variation)
 }

 newVariantBeforeTransitions := newVariant.clone()
 var moduleOutgoingTransitionInfos []TransitionInfo

 if !reverse {
  var errs []error
  newVariant, moduleOutgoingTransitionInfos, errs = c.applyTransitions(config, module, depTag, possibleDeps, newVariant, requestedVariations, far, nil)
  if len(errs) > 0 {
   return nil, variationMap{}, errs
  }
 }

 // check returns a bool for whether the requested newVariant matches the given variant from possibleDeps, and a
 // divergence score.  A score of 0 is best match, and a positive integer is a worse match.
 // For a non-far search, the score is always 0 as the match must always be exact.  For a far search,
 // the score is the number of variants that are present in the given variant but not newVariant.
 check := func(variant variationMap) (bool, int) {
  if far {
   if newVariant.subsetOf(variant) {
    return true, variant.differenceKeysCount(newVariant)
   }
  } else {
   if variant.equal(newVariant) {
    return true, 0
   }
  }
  return false, math.MaxInt
 }

 var foundDep *moduleInfo
 bestDivergence := math.MaxInt
 for _, m := range possibleDeps.modules {
  if match, divergence := check(m.variant.variations); match && divergence < bestDivergence {
   foundDep = m
   bestDivergence = divergence
   if !far {
    // non-far dependencies use equality, so only the first match needs to be checked.
    break
   }
  }
 }

 if foundDep == nil &&
  !c.allowMissingDependencies && // TODO (b/448182009): Skip checking for possible on-demand variant if allowMissingDependencies is set.
  possibleDeps.searchOnDemandVariant(newVariantBeforeTransitions, far, mutatorIndex, c.transitionMutators) {
  if variantOnDemand, errs := c.loadOrCreateVariantOnDemand(config, module, depTag, possibleDeps, newVariantBeforeTransitions, requestedVariations, far, moduleOutgoingTransitionInfos); variantOnDemand != nil {
   foundDep = c.createVariantOnDemand(possibleDeps, variantOnDemand.requestedOnDemandVariant.clone())
   foundDep.finishedMutator = mutatorIndex
   newVariant = variantOnDemand.requestedOnDemandVariant.clone()
  } else {
   return nil, newVariant, errs
  }
 }

 return foundDep, newVariant, nil
}

func (c *Context) addVariationDependency(module *moduleInfo, mutator *mutatorInfo, config any, variations []Variation,
 tag DependencyTag, depName string, far bool) (*moduleInfo, []error) {
 if _, ok := tag.(BaseDependencyTag); ok {
  panic("BaseDependencyTag is not allowed to be used directly!")
 }

 possibleDeps := c.moduleGroupFromName(depName, module.namespace())
 if possibleDeps == nil {
  return nil, c.discoveredMissingDependencies(module, depName, variationMap{})
 }

 foundDep, newVariant, errs := c.findVariant(config, module, tag, possibleDeps, variations, far, false, mutator.index)
 if errs != nil {
  return nil, errs
 }

 if foundDep == nil {
  if c.allowMissingDependencies {
   // Allow missing variants.
   return nil, c.discoveredMissingDependencies(module, depName, newVariant)
  }
  return nil, []error{&BlueprintError{
   Err: fmt.Errorf("dependency %q of %q missing variant:\n  %s\navailable variants:\n  %s",
    depName, module.Name(),
    c.prettyPrintVariant(newVariant),
    c.prettyPrintGroupVariants(possibleDeps)),
   Pos: module.pos,
  }}
 }

 if module == foundDep {
  return nil, []error{&BlueprintError{
   Err: fmt.Errorf("%q depends on itself", depName),
   Pos: module.pos,
  }}
 }
 if foundDep.createdOnDemand {
  // `runMutator` will create the dependency edges, deduping on demand variants if necessary.
  // Also, defer the beforeInModuleList check to the end of runMutator.
  return foundDep, nil
 }
 // AddVariationDependency allows adding a dependency on itself, but only if
 // that module is earlier in the module list than this one, since we always
 // run GenerateBuildActions in order for the variants of a module
 if foundDep.group == module.group && beforeInModuleList(module, foundDep, module.group.modules) {
  return nil, []error{&BlueprintError{
   Err: fmt.Errorf("%q depends on later version of itself", depName),
   Pos: module.pos,
  }}
 }

 // The mutator will pause until the newly added dependency has finished running the current mutator,
 // so it is safe to add the new dependency directly to directDeps and forwardDeps where it will be visible
 // to future calls to VisitDirectDeps.  Set newDirectDeps so that at the end of the mutator the reverseDeps
 // of the dependencies can be updated to point to this module without running a full c.updateDependencies()
 module.directDeps = append(module.directDeps, depInfo{foundDep, tag})
 module.forwardDeps = append(module.forwardDeps, foundDep)
 module.newDirectDeps = append(module.newDirectDeps, foundDep)
 return foundDep, nil
}

// findBlueprintDescendants returns a map linking parent Blueprint files to child Blueprints files
// For example, if paths = []string{"a/b/c/Android.bp", "a/Android.bp"},
// then descendants = {"":[]string{"a/Android.bp"}, "a/Android.bp":[]string{"a/b/c/Android.bp"}}
func findBlueprintDescendants(paths []string) (descendants map[string][]string, err error) {
 // make mapping from dir path to file path
 filesByDir := make(map[string]string, len(paths))
 for _, path := range paths {
  dir := filepath.Dir(path)
  _, alreadyFound := filesByDir[dir]
  if alreadyFound {
   return nil, fmt.Errorf("Found two Blueprint files in directory %v : %v and %v", dir, filesByDir[dir], path)
  }
  filesByDir[dir] = path
 }

 findAncestor := func(childFile string) (ancestor string) {
  prevAncestorDir := filepath.Dir(childFile)
  for {
   ancestorDir := filepath.Dir(prevAncestorDir)
   if ancestorDir == prevAncestorDir {
    // reached the root dir without any matches; assign this as a descendant of ""
    return ""
   }

   ancestorFile, ancestorExists := filesByDir[ancestorDir]
   if ancestorExists {
    return ancestorFile
   }
   prevAncestorDir = ancestorDir
  }
 }
 // generate the descendants map
 descendants = make(map[string][]string, len(filesByDir))
 for _, childFile := range filesByDir {
  ancestorFile := findAncestor(childFile)
  descendants[ancestorFile] = append(descendants[ancestorFile], childFile)
 }
 return descendants, nil
}

type visitOrderer interface {
 // returns the number of modules that this module needs to wait for
 waitCount(module *moduleInfo) int
 // returns the list of modules that are waiting for this module
 propagate(module *moduleInfo) []*moduleInfo
}

type unorderedVisitorImpl struct{}

func (unorderedVisitorImpl) waitCount(module *moduleInfo) int {
 return 0
}

func (unorderedVisitorImpl) propagate(module *moduleInfo) []*moduleInfo {
 return nil
}

type bottomUpVisitorImpl struct{}

func (bottomUpVisitorImpl) waitCount(module *moduleInfo) int {
 return len(module.forwardDeps)
}

func (bottomUpVisitorImpl) propagate(module *moduleInfo) []*moduleInfo {
 return module.reverseDeps
}

type topDownVisitorImpl struct{}

func (topDownVisitorImpl) waitCount(module *moduleInfo) int {
 return len(module.reverseDeps)
}

func (topDownVisitorImpl) propagate(module *moduleInfo) []*moduleInfo {
 return module.forwardDeps
}

func (topDownVisitorImpl) visit(modules []*moduleInfo, visit func(*moduleInfo, chan<- pauseSpec) bool) {
 for i := 0; i < len(modules); i++ {
  module := modules[len(modules)-1-i]
  if visit(module, nil) {
   return
  }
 }
}

var (
 bottomUpVisitor bottomUpVisitorImpl
 topDownVisitor  topDownVisitorImpl
)

// unpause is a channel that will be closed when the paused module should resume.
type unpause chan struct{}

// pauseFunc is a function a visitor function can call to pause execution until the visitor
// function on the given module is completed.
type pauseFunc func(until *moduleInfo)

// parallelVisitWorker is an individual worker that will call visitor functions on behalf of
// a call to parallelVisit.  It's run method loops until the input queueCh is closed, receiving
// batches of modules on which to call visit method, and posting responses to the responseCh
// channel.
type parallelVisitWorker struct {
 // queue is the slice of modules that are currently being processed.
 queue []*moduleInfo
 // currentQueueIndex is the index into the queue of the module currently being visited.
 currentQueueIndex int

 // done is the slice of modules that have had the visit method called on them has returned.
 done []*moduleInfo
 // remaining is the slice of modules that have not had the visit method called on them.
 remaining []*moduleInfo
 // current is the module that is currently running the visit method.
 current *moduleInfo
 // unblocked is the slice of modules that are now runnable after visit returned on a module
 // in this batch.
 // TODO: can these modules just be handled in this worker?  Use some heuristic to decide
 //  whether to handle them or send them back to the orchestrator?
 unblocked []*moduleInfo

 // visit is the method that is called on each module.
 visit func(module *moduleInfo, pause pauseFunc) bool

 // order is the interface that describes which modules will be ready next once the current module
 // has had visit called on it.
 order visitOrderer

 // queueCh is the input channel that provides batches of modules to call visit on.  It is closed
 // when there is no more work to do.
 queueCh <-chan []*moduleInfo
 // responseCh is the output channel where a parallelVisitWorkerResponse is sent after processing
 // each batch.
 responseCh chan<- parallelVisitWorkerResponse
}

// pauseSpec describes a pause that a module needs to occur until another module has been visited,
// at which point the unpause channel will be closed.
type pauseSpec struct {
 paused  *moduleInfo
 until   *moduleInfo
 unpause unpause
}

// parallelVisitWorkerResponse is sent after a parallelVisitWorker processes a batch of modules.
type parallelVisitWorkerResponse struct {
 // done is the slice of modules that have had the visit method called on.
 done []*moduleInfo

 // returned is the slice of modules that the worker did not call the visit method on,
 // either because the visit method returned an error, or because the visit method called
 // the pause function, which may be waiting (directly or transitively) on a module that
 // is later in the batch.
 returned []*moduleInfo

 // unblocked is the slice of modules that became ready (i.e. waitingCount is now zero)
 // after the modules that the worker ran finished.
 unblocked []*moduleInfo

 // error is set when a visit method returned an error, signalling that parallelVisit should
 // abort.
 error bool

 // pause is set when the visit method called pause, signalling that the worker is waiting
 // indefinitely on the unpause channel, which should be closed when the requested module
 // has completed.
 pause pauseSpec
}

// run is the main loop method of a parallelVisitWorker.  It receives batches of modules to
// call the visit method on from queueCh, and then calls runQueue on them.
func (worker *parallelVisitWorker) run() {
 for queued := range worker.queueCh {
  if len(worker.queue) > 0 {
   panic(fmt.Errorf("already have queued work"))
  }
  errored := worker.runQueue(queued)
  if errored {
   return
  }
 }
}

// runQueue processes a single batch of modules.
func (worker *parallelVisitWorker) runQueue(queue []*moduleInfo) bool {
 worker.queue = queue
 worker.done = nil
 worker.unblocked = nil
 // Use a loop on worker.currentQueueIndex so that the pause method can update it when it sends
 // the done and remaining modules back to the orchestrator.
 for worker.currentQueueIndex = 0; worker.currentQueueIndex < len(worker.queue); worker.currentQueueIndex++ {
  worker.current = worker.queue[worker.currentQueueIndex]
  worker.remaining = worker.queue[worker.currentQueueIndex+1:]
  ret := worker.visit(worker.current, worker.pause)
  worker.done = worker.queue[:worker.currentQueueIndex+1]
  if ret {
   // An error occurred.  Send the completed and uncompleted modules back to the orchestrator with
   // the error flag set.
   worker.responseCh <- parallelVisitWorkerResponse{
    done:      worker.done,
    error:     true,
    returned:  worker.remaining,
    unblocked: worker.unblocked,
   }
   return true
  }

  // Decrement waitingCount on the next modules in the tree based
  // on propagation order, and add them to the queue if they are
  // ready to start.
  for _, module := range worker.order.propagate(worker.current) {
   if module.waitingCount.Add(-1) == 0 {
    worker.unblocked = append(worker.unblocked, module)
   }
  }
 }

 // The batch is complete.  Send the completed modules back to the orchestrator.
 worker.responseCh <- parallelVisitWorkerResponse{
  done:      worker.done,
  unblocked: worker.unblocked,
 }
 worker.queue = nil
 return false
}

// pause is called by visitors (via the function pointer passed into the visit function) in order to
// signal that the visitor needs to wait until the visitor has completed on the target module.
func (worker *parallelVisitWorker) pause(until *moduleInfo) {
 // If the target module is already done there is no need to pause.  This is safe because waitingCount
 // will never change once it has reached -1.
 if until.waitingCount.Load() == -1 {
  return
 }
 unpause := make(chan struct{})
 // This visitor needs to pause.  Send the completed and uncompleted modules back to the orchestrator
 // with a pauseSpec that describes how and when to unpause this module.  The uncompleted modules are
 // returned to the orchestrator in case the pause is waiting (directly or transitively) for one of
 // the uncompleted modules.
 worker.responseCh <- parallelVisitWorkerResponse{
  done:      worker.done,
  returned:  worker.remaining,
  unblocked: worker.unblocked,
  pause: pauseSpec{
   paused:  worker.current,
   until:   until,
   unpause: unpause,
  },
 }
 // Reset the queue to contain only the current module, as everything else has already been returned
 // to the orchestrator.
 worker.done = nil
 worker.queue = []*moduleInfo{worker.current}
 worker.currentQueueIndex = 0
 worker.remaining = nil
 worker.unblocked = nil

 // Wait for the orchestrator to close the unpause channel to signal that the requested module has
 // finished.
 <-unpause
}

// parallelVisitLimit is the maximum number of visitors that can be simultaneously active in parallelVisit.
var parallelVisitLimit = runtime.NumCPU() * 2

// parallelVisitBatchSize is the number of visitors that will be batched together and passed to a single
// parallelVisitWorker in order to reduce channel communication overhead.  Setting this value higher
// amortizes the synchronization and coordination costs across more modules, but setting it too high
// risks having a single worker left processing modules after all the other workers have finished if it
// has too many long visitors in a single batch.
const parallelVisitBatchSize = 100

// parallelVisit calls visit on each module, guaranteeing that visit is not called on a module until
// visit on all of its dependencies (as determined by the visitOrderer) has finished.  A visit function
// can call the pause function to wait for another dependency to be visited before continuing.
// If a visit function returns true to cancel while another visitor is paused, the paused visitor will
// never be resumed and its goroutine will stay paused forever.
// The limit argument sets the maximum number of workers that can be running visit functions simultaneously.
// The total number of workers can be higher than limit if some of them are paused, but the paused workers
// won't be unpaused until the number of active workers drops below the limit.
func parallelVisit(moduleIter iter.Seq[*moduleInfo], order visitOrderer, limit int,
 visit func(module *moduleInfo, pause pauseFunc) bool) []error {

 // queueCh sends batches of modules to process from the orchestrator to the workers.
 queueCh := make(chan []*moduleInfo, limit)
 // responseCh receives responses from the workers when a batch has finished processing.
 responseCh := make(chan parallelVisitWorkerResponse, limit)

 // Closing queueCh when parallelVisit finishes signals the workers to exit.
 defer close(queueCh)

 activeWorkers := 0 // Number of workers running, not counting paused visitors.
 activeModules := 0 // Number of modules that have been sent to workers.
 visited := 0       // Number of modules whose visitors have finished.
 pausedWorkers := 0 // Number of workers that are waiting on an unpause channel
 workers := 0       // Total number of spawned workers.

 hung := make(map[*moduleInfo]bool)

 cancel := false // will be set when any worker returns an error.

 var queue []*moduleInfo           // The list of modules that are ready to be sent to workers.
 var returnedQueue [][]*moduleInfo // The list of modules that were sent to workers and then returned and need to be resent.
 var unpauseQueue []pauseSpec      // Visitors that are ready to unpause but backlogged due to limit.

 queuedModules := 0 // The number of modules that are queued in queue, returnedQueue or unpauseQueue.

 // pauseMap holds the map from modules that are being waited on to the list of pauseSpecs that are waiting on them.
 pauseMap := make(map[*moduleInfo][]pauseSpec)

 // newWorker spawns a new worker goroutine.
 newWorker := func() {
  worker := ¶llelVisitWorker{
   visit:      visit,
   order:      order,
   queueCh:    queueCh,
   responseCh: responseCh,
  }
  workers++
  go worker.run()
 }

 toVisit := 0

 // Initialize waitingCount on each module with the number of modules that need to complete before it can run.
 // Add any modules whose waitingCount is 0 to the initial queue of ready modules.
 for module := range moduleIter {
  hung[module] = true
  toVisit++
  waitingCount := order.waitCount(module)
  module.waitingCount.Store(int32(waitingCount))
  if waitingCount == 0 {
   queue = append(queue, module)
   queuedModules++
  }
 }
 queue = slices.Grow(queue, toVisit-len(queue))

 // queueWork is called to send work to any available workers, including spawning new workers if there is work
 // to do and the number of active workers is below the limit.
 queueWork := func() {
  for queuedModules > 0 && activeWorkers < limit {
   // First priority: unpause any paused workers that are ready, as the visitor functions may already
   // be holding resources.
   if len(unpauseQueue) > 0 {
    unpause := unpauseQueue[0]
    unpauseQueue = unpauseQueue[1:]
    pausedWorkers--
    queuedModules--
    activeModules++
    close(unpause.unpause)
   } else {
    // If there are worker slots available and no idle workers, spawn a new worker.
    if activeWorkers < limit && activeWorkers+pausedWorkers == workers {
     newWorker()
    }
    var batch []*moduleInfo
    // Second priority: re-send any returned work back to a worker.
    if len(returnedQueue) > 0 {
     batch = returnedQueue[0]
     returnedQueue = returnedQueue[1:]
    } else {
     // Send a batch of work from the queue.  Limit the size of the batch to the size of the queue
     // divided by the number of available workers to avoid sending a big batch of work to a single
     // worker when other workers are available and to parallelVisitBatchSize.
     availableWorkersSlots := limit - activeWorkers
     queueSizePerAvailableWorker := (len(queue) + availableWorkersSlots - 1) / availableWorkersSlots
     batchSize := min(parallelVisitBatchSize, queueSizePerAvailableWorker)
     batch = queue[:batchSize]
     queue = queue[batchSize:]
    }
    activeModules += len(batch)
    queuedModules -= len(batch)
    if len(batch) == 0 {
     panic("zero length batch")
    }
    queueCh <- batch
   }
   activeWorkers++
  }
 }

 // Call queueWork before starting the loop so that activeModules is nonzero.
 queueWork()

 // The main orchestrator loop, which runs until there are no workers doing work.
 for activeModules > 0 {
  // Wait for a response from a worker.
  response := <-responseCh
  activeWorkers--

  if response.error {
   // Once cancel is set no more work will be sent to workers.
   cancel = true
  }

  // Process finished modules.
  visited += len(response.done)
  activeModules -= len(response.done)
  for _, doneModule := range response.done {
   delete(hung, doneModule)
   // Mark this module as done.  Nothing else should be updating waitingCount, so a single attempt
   // at CompareAndSwap should always succeed.  This is the only location that will ever update
   // waitingCount from 0 to -1, and once it is -1 it will never be changed for the rest of this
   // call to parallelVisit.
   if !doneModule.waitingCount.CompareAndSwap(0, -1) {
    panic(fmt.Errorf("failed to atomically mark module %s as done", doneModule))
   }
   // Add any modules that were paused on this module to the unpause queue.
   if unpauses, ok := pauseMap[doneModule]; ok {
    delete(pauseMap, doneModule)
    queuedModules += len(unpauses)
    unpauseQueue = append(unpauseQueue, unpauses...)
   }
  }

  if len(response.unblocked) > 0 {
   // Add any modules that were made ready to the queue.
   queuedModules += len(response.unblocked)
   queue = append(queue, response.unblocked...)
  }

  // Re-queue any returned modules.
  if len(response.returned) > 0 {
   queuedModules += len(response.returned)
   activeModules -= len(response.returned)
   returnedQueue = append(returnedQueue, response.returned)
  }

  // Handle a requested pause.
  if response.pause.paused != nil {
   // This goroutine is the only one that can set waitingCount to -1, so reading it here does not
   // race with updating pauseMap if the value is not yet -1.
   if response.pause.until.waitingCount.Load() == -1 {
    // Module being paused for is already finished, resume immediately.
    // activeWorkers was decremented above when the response was received,
    // re-increment it as it is going to resume.
    activeWorkers++
    close(response.pause.unpause)
   } else {
    // Register for unpausing.
    pauseMap[response.pause.until] = append(pauseMap[response.pause.until], response.pause)
    pausedWorkers++
    activeModules--
   }

   // If a dependency is created on demand, add it to the queue for processing.
   // The main coordinator goroutine will be responsible for deduping on demand variant
   // requested from multiple rdeps.
   if response.pause.until.createdOnDemand {
    queue = append(queue, response.pause.until)
    toVisit++
    hung[response.pause.until] = true
    queuedModules++
   }

  }

  // Each time a response has been handled check if there is work that can now be queued.
  if !cancel {
   queueWork()
  }
 }

 // The orchestrator loop has finished because there are no modules being processed.  In the normal case all
 // the modules should have been visited.  If an error occurred there may be queued or paused modules.
 // If a deadlock occurred and all remaining modules are not ready or paused then there is newly added
 // cyclic dependency.
 if !cancel {
  // Invariant checks: no queued, returned or unpaused modules.  These weren't waiting on anything except
  // the parallelism limit so they should have run.
  if len(queue) > 0 {
   panic(fmt.Errorf("parallelVisit finished with %d queued visitors", len(queue)))
  }
  if len(returnedQueue) > 0 {
   panic(fmt.Errorf("parallelVisit finished with %d returned queued visitors", len(returnedQueue)))
  }
  if len(unpauseQueue) > 0 {
   panic(fmt.Errorf("parallelVisit finished with %d queued unpaused visitors", len(unpauseQueue)))
  }

  if visited != toVisit || len(pauseMap) > 0 {
   // Probably a deadlock due to a dependency cycle. Start from each module in the order
   // of the input modules list and perform a depth-first search for any module that is
   // in the walk path twice.  Note this traverses from modules to the modules that would
   // have been unblocked when that module finished, i.e. the reverse of the visitOrderer.
   // This search takes into account both the pre-existing dependencies and any newly
   // added dependencies that are still in the pauseMap.

   // In order to reduce duplicated work, once a module has been checked and determined
   // not to be part of a cycle add it and everything that depends on it to the checked
   // map.
   checked := make(map[*moduleInfo]bool, toVisit) // modules that were already checked
   checking := make(map[*moduleInfo]bool)         // modules actively being checked

   var errs []error
   var check func(group *moduleInfo) []*moduleInfo

   check = func(module *moduleInfo) []*moduleInfo {
    if checking[module] {
     // This is a cycle.
     return []*moduleInfo{module}
    }
    if checked[module] {
     return nil
    }

    checked[module] = true
    checking[module] = true
    defer delete(checking, module)

    // Create an iterator that yields the existing dependencies of this module (via order.propagate),
    // followed by any newly added dependencies stored in pauseMap.
    dependencyIter := func(yield func(*moduleInfo) bool) {
     for _, dep := range order.propagate(module) {
      if !yield(dep) {
       return
      }
     }
     for _, pauseSpec := range pauseMap[module] {
      if !yield(pauseSpec.paused) {
       return
      }
     }
    }

    var cycle []*moduleInfo
    for dep := range dependencyIter {
     cycle = check(dep)
     if cycle != nil {
      break
     }
    }

    if cycle != nil {
     if cycle[0] == module {
      // We are the "start" of the cycle, so we're responsible
      // for generating the errors.
      slices.Reverse(cycle)
      errs = append(errs, cycleError(cycle)...)

      // We can continue processing this module's children to
      // find more cycles.  Since all the modules that were
      // part of the found cycle were marked as visited we
      // won't run into that cycle again.
     } else {
      // We're not the "start" of the cycle, so we just append
      // our module to the list and return it.
      return append(cycle, module)
     }
    }

    return nil
   }

   for module := range moduleIter {
    check(module)
   }

   if len(errs) > 0 {
    return errs
   }
  }

  // Invariant check: if there was no dependency cycle and no cancellation every module
  // should have been visited, so there is nothing left to be paused on.
  if len(pauseMap) > 0 {
   panic(fmt.Errorf("parallelVisit finished with %d paused visitors", len(pauseMap)))
  }

  // Invariant check: if there was no dependency cycle and no cancellation every module
  // should have been visited.
  if visited != toVisit {
   panic(fmt.Errorf("parallelVisit ran %d visitors, expected %d. Unvisited modules map: %v. Try rebuilding with SOONG_SPLIT_ALL_VARIANTS=true. Please file a go/soong-bug if build is successful with the environment variable.", visited, toVisit, hung))
  }
 }

 return nil
}

func cycleError(cycle []*moduleInfo) (errs []error) {
 // The cycle list is in reverse order because all the 'check' calls append
 // their own module to the list.
 errs = append(errs, &BlueprintError{
  Err: fmt.Errorf("encountered dependency cycle:"),
  Pos: cycle[len(cycle)-1].pos,
 })

 // Iterate backwards through the cycle list.
 curModule := cycle[0]
 for i := len(cycle) - 1; i >= 0; i-- {
  nextModule := cycle[i]
  errs = append(errs, &BlueprintError{
   Err: fmt.Errorf("    %s depends on %s",
    curModule, nextModule),
   Pos: curModule.pos,
  })
  curModule = nextModule
 }

 return errs
}

// updateDependencies recursively walks the module dependency graph and updates
// additional fields based on the dependencies.  It builds a sorted list of modules
// such that dependencies of a module always appear first, and populates reverse
// dependency links and counts of total dependencies.  It also reports errors when
// it encounters dependency cycles.  This should be called after resolveDependencies,
// as well as after any mutator pass has called addDependency
func (c *Context) updateDependencies() (errs []error) {
 c.cachedDepsModified = true

 for module := range c.iterateAllVariants() {
  // Reset the forward and reverse deps without reducing their capacity to avoid reallocation.
  module.reverseDeps = module.reverseDeps[:0]
  module.forwardDeps = module.forwardDeps[:0]
 }

 for module := range c.iterateAllVariants() {
  // Add an implicit dependency ordering on all earlier modules in the same module group
  selfIndex := slices.Index(module.group.modules, module)
  module.forwardDeps = slices.Grow(module.forwardDeps, selfIndex+len(module.directDeps))
  module.forwardDeps = append(module.forwardDeps, module.group.modules[:selfIndex]...)

  for _, dep := range module.directDeps {
   module.forwardDeps = append(module.forwardDeps, dep.module)
  }

  for _, dep := range module.forwardDeps {
   dep.reverseDeps = append(dep.reverseDeps, module)
  }
 }

 return
}

// Gets a list of strings from the given list of ninjaStrings by invoking ninjaString.Value on each.
func getNinjaStrings(nStrs []*ninjaString, nameTracker *nameTracker) []string {
 var strs []string
 for _, nstr := range nStrs {
  strs = append(strs, nstr.Value(nameTracker))
 }
 return strs
}

type WeightedOutputsModuleInfo struct {
 Type      string
 DepsCount int
 SrcsCount int
 Outputs   []string
}

func (c *Context) GetWeightedOutputsFromPredicate(predicate func(*WeightedOutputsModuleInfo) (bool, int)) map[string]int {
 outputToWeight := make(map[string]int)
 for m := range c.iterateAllVariants() {
  info := WeightedOutputsModuleInfo{
   Type:      m.typeName,
   DepsCount: len(m.directDeps),
   SrcsCount: 0,
  }
  for _, bDef := range m.actionDefs.buildDefs {
   info.SrcsCount += len(bDef.InputStrings) + len(bDef.Inputs) + len(bDef.ImplicitStrings) + len(bDef.Implicits)
   info.Outputs = append(info.Outputs, bDef.OutputStrings...)
   info.Outputs = append(info.Outputs, bDef.ImplicitOutputStrings...)
   info.Outputs = append(info.Outputs, getNinjaStrings(bDef.Outputs, c.nameTracker)...)
   info.Outputs = append(info.Outputs, getNinjaStrings(bDef.ImplicitOutputs, c.nameTracker)...)
  }
  if ok, weight := predicate(&info); ok {
   for _, o := range info.Outputs {
    if val, ok := outputToWeight[o]; ok {
     if val > weight {
      continue
     }
    }
    outputToWeight[o] = weight
   }
  }
 }
 return outputToWeight
}

// PrepareBuildActions generates an internal representation of all the build
// actions that need to be performed.  This process involves invoking the
// GenerateBuildActions method on each of the Module objects created during the
// parse phase and then on each of the registered Singleton objects.
//
// If the ResolveDependencies method has not already been called it is called
// automatically by this method.
//
// The config argument is made available to all of the Module and Singleton
// objects via the Config method on the ModuleContext and SingletonContext
// objects passed to GenerateBuildActions.  It is also passed to the functions
// specified via PoolFunc, RuleFunc, and VariableFunc so that they can compute
// config-specific values.
//
// The returned deps is a list of the ninja files dependencies that were added
// by the modules and singletons via the ModuleContext.AddNinjaFileDeps(),
// SingletonContext.AddNinjaFileDeps(), and PackageContext.AddNinjaFileDeps()
// methods.

func (c *Context) PrepareBuildActions(config interface{}) (deps []string, errs []error) {
 c.BeginEvent("prepare_build_actions")
 defer c.EndEvent("prepare_build_actions")
 pprof.Do(c.Context, pprof.Labels("blueprint", "PrepareBuildActions"), func(ctx context.Context) {
  c.buildActionsReady = false

  c.liveGlobals = newLiveTracker(c, config)
  // Add all the global rules/variable/pools here because when we restore from
  // cache we don't have the build defs available to build the globals.
  // TODO(b/356414070): Revisit this logic once we have a clearer picture about
  // how the incremental build pieces fit together.
  if c.GetIncrementalEnabled() {
   if c.keyValueStoreCache == nil {
    c.keyValueStoreCache = &KeyValueStoreCache{}
    dbPath := filepath.Join(c.SrcDir(), c.IncrementalDBDir())
    // Remove gob files and all the cached data from the key-value store for a full build.
    if !c.GetIncrementalAnalysis() {
     err := errors.Join(c.keyValueStoreCache.reset(c, dbPath), c.fs.Remove(filepath.Join(dbPath, OrderOnlyStringsCacheFile)))
     if err != nil {
      panic(fmt.Errorf("error resetting incremental db: %w", err))
     }
    }
    if err := c.keyValueStoreCache.open(dbPath); err != nil {
     panic(fmt.Errorf("error opening incremental db: %w", err))
    }
    c.EncContext = gobtools.NewEncContext(c.keyValueStoreCache.referencesDb)
   }

   for _, p := range packageContexts {
    for _, v := range p.scope.variables {
     err := c.liveGlobals.addVariable(v)
     if err != nil {
      errs = []error{err}
      return
     }
    }
    for _, v := range p.scope.rules {
     _, err := c.liveGlobals.addRule(v)
     if err != nil {
      errs = []error{err}
      return
     }
    }
    for _, v := range p.scope.pools {
     err := c.liveGlobals.addPool(v)
     if err != nil {
      errs = []error{err}
      return
     }
    }
   }
  }

  if !c.dependenciesReady {
   var extraDeps []string
   extraDeps, errs = c.resolveDependencies(ctx, config)
   if len(errs) > 0 {
    return
   }
   deps = append(deps, extraDeps...)
  }

  var depsModules []string
  depsModules, errs = c.generateModuleBuildActions(config, c.liveGlobals)
  if len(errs) > 0 {
   return
  }

  pprof.Do(c.Context, pprof.Labels("blueprint", "GC"), func(ctx context.Context) {
   runtime.GC()
  })

  var depsSingletons []string
  depsSingletons, errs = c.generateSingletonBuildActions(config, c.singletonInfo, c.liveGlobals)
  if len(errs) > 0 {
   return
  }

  deps = append(deps, depsModules...)
  deps = append(deps, depsSingletons...)

  if c.outDir != nil {
   err := c.liveGlobals.addNinjaStringDeps(c.outDir)
   if err != nil {
    errs = []error{err}
    return
   }
  }

  pkgNames, depsPackages := c.makeUniquePackageNames(c.liveGlobals)

  deps = append(deps, depsPackages...)

  nameTracker := c.memoizeFullNames(c.liveGlobals, pkgNames)

  // This will panic if it finds a problem since it's a programming error.
  c.checkForVariableReferenceCycles(c.liveGlobals.variables, nameTracker)

  c.nameTracker = nameTracker
  c.globalVariables = c.liveGlobals.variables
  c.globalPools = c.liveGlobals.pools
  c.globalRules = c.liveGlobals.rules

  c.buildActionsReady = true
 })

 if len(errs) > 0 {
  return nil, errs
 }

 return deps, nil
}

func (c *Context) runMutators(ctx context.Context, config interface{}, mutatorGroups [][]*mutatorInfo) (deps []string, errs []error) {
 c.finishedMutators = make([]bool, len(c.mutatorInfo))

 pprof.Do(ctx, pprof.Labels("blueprint", "runMutators"), func(ctx context.Context) {
  mutatorIndexAfterLastCreateModule := -1
  mutatorIndexPartialAnalysis := -1
  for i := len(mutatorGroups) - 1; i >= 0; i-- {
   if mutatorIndexAfterLastCreateModule == -1 && mutatorGroups[i][0].usesCreateModule {
    mutatorIndexAfterLastCreateModule = mutatorGroups[i][0].index
   }
   if mutatorIndexPartialAnalysis == -1 && mutatorGroups[i][0].prePartial {
    mutatorIndexPartialAnalysis = mutatorGroups[i][0].index
   }
  }
  c.mutatorIndexAfterLastCreateModule = mutatorIndexAfterLastCreateModule + 1
  c.mutatorIndexPartialAnalysis = mutatorIndexPartialAnalysis + 1

  for _, mutatorGroup := range mutatorGroups {
   if mutatorGroup[0].index == c.mutatorIndexPartialAnalysis && len(c.partialAnalysisTargets) > 0 {
    targetMap := make(map[string]bool, len(c.partialAnalysisTargets))
    for _, t := range c.partialAnalysisTargets {
     targetMap[t] = false
    }

    foundCount := 0
    for _, m := range c.moduleGroups {
     if alreadyFound, isTarget := targetMap[m.name]; isTarget {
      m.passive = false
      if !alreadyFound {
       targetMap[m.name] = true
       foundCount++
      }
     } else {
      m.passive = true
     }
    }

    if foundCount < len(targetMap) {
     notFound := make([]string, 0, len(targetMap)-foundCount)
     for target, found := range targetMap {
      if !found {
       notFound = append(notFound, target)
      }
     }
     panic(fmt.Sprintf("the requested targets are not found: %v", notFound))
    }
   }
   name := mutatorGroup[0].name
   if len(mutatorGroup) > 1 {
    name += "_plus_" + strconv.Itoa(len(mutatorGroup)-1)
   }
   pprof.Do(ctx, pprof.Labels("mutator", name), func(context.Context) {
    c.BeginEvent(name)
    defer c.EndEvent(name)
    var newDeps []string
    if mutatorGroup[0].transitionPropagateMutator != nil {
     newDeps, errs = c.runMutator(config, mutatorGroup, topDownMutator)
    } else if mutatorGroup[0].bottomUpMutator != nil {
     newDeps, errs = c.runMutator(config, mutatorGroup, bottomUpMutator)
    } else {
     panic("no mutator set on " + mutatorGroup[0].name)
    }
    if len(errs) > 0 {
     return
    }
    deps = append(deps, newDeps...)
   })
   if len(errs) > 0 {
    return
   }
  }
 })

 if len(errs) > 0 {
  return nil, errs
 }

 return deps, nil
}

type mutatorDirection interface {
 run(mutator []*mutatorInfo, ctx *mutatorContext)
 orderer() visitOrderer
 fmt.Stringer
}

type bottomUpMutatorImpl struct{}

func (bottomUpMutatorImpl) run(mutatorGroup []*mutatorInfo, ctx *mutatorContext) {
 for _, mutator := range mutatorGroup {
  ctx.mutator = mutator
  ctx.module.startedMutator = mutator.index
  mutator.bottomUpMutator(ctx)
  ctx.module.finishedMutator = mutator.index
 }
}

func (bottomUpMutatorImpl) orderer() visitOrderer {
 return bottomUpVisitor
}

func (bottomUpMutatorImpl) String() string {
 return "bottom up mutator"
}

type topDownMutatorImpl struct{}

func (topDownMutatorImpl) run(mutatorGroup []*mutatorInfo, ctx *mutatorContext) {
 if len(mutatorGroup) > 1 {
  panic(fmt.Errorf("top down mutator group %s must only have 1 mutator, found %d", mutatorGroup[0].name, len(mutatorGroup)))
 }
 mutatorGroup[0].transitionPropagateMutator(ctx)
}

func (topDownMutatorImpl) orderer() visitOrderer {
 return topDownVisitor
}

func (topDownMutatorImpl) String() string {
 return "top down mutator"
}

var (
 topDownMutator  topDownMutatorImpl
 bottomUpMutator bottomUpMutatorImpl
)

type reverseDep struct {
 module *moduleInfo
 dep    depInfo
}

var mutatorContextPool = pool.New[mutatorContext]()

func (c *Context) runMutator(config interface{}, mutatorGroup []*mutatorInfo,
 direction mutatorDirection) (deps []string, errs []error) {

 type globalStateChange struct {
  reverse             []reverseDep
  rename              []rename
  replace             []replace
  newModules          []*moduleInfo
  onDemandModules     []*moduleInfo
  onDemandReverseDeps []reverseDep
  deps                []string
 }

 type createdOnDemandStateChange struct {
  module              *moduleInfo
  moduleReplaceWithCh chan *moduleInfo
 }

 reverseDeps := make(map[*moduleInfo][]depInfo)
 onDemandReverseDeps := make(map[*moduleInfo]map[*moduleInfo]bool)
 var rename []rename
 var replace []replace
 var newModules []*moduleInfo
 var onDemandModules []*moduleInfo

 errsCh := make(chan []error)
 globalStateCh := make(chan globalStateChange)
 createdOnDemandStateCh := make(chan createdOnDemandStateChange)
 done := make(chan bool)

 c.needsUpdateDependencies = 0

 visit := func(module *moduleInfo, pause pauseFunc) bool {
  if module.splitModules != nil {
   panic("split module found in sorted module list")
  }

  if module.createdOnDemand {
   // Send a request to the main coordinator goroutine to check
   // if the previous mutators need to be run on this on demand variant.
   // This allows the coordinator goroutine to dedupe processing if necessary.
   moduleReplaceWithCh := make(chan *moduleInfo)
   createdOnDemandStateCh <- createdOnDemandStateChange{
    module,
    moduleReplaceWithCh,
   }
   moduleReplaceWith := <-moduleReplaceWithCh
   if module == moduleReplaceWith {
    c.rerunMutatorsOnVariantOnDemand(module, c.mutatorIndexAfterLastCreateModule, mutatorGroup[len(mutatorGroup)-1].index, pause, config) // Mutate till the current mutator.
    globalStateCh <- globalStateChange{
     onDemandModules:     []*moduleInfo{module},
     onDemandReverseDeps: module.newOnDemandReverseDeps,
    }
   }
   module.createdOnDemandReplaceWith = moduleReplaceWith
   return false
  }

  mctx := mutatorContextPool.Get()
  *mctx = mutatorContext{
   baseModuleContext: baseModuleContext{
    context: c,
    config:  config,
    module:  module,
   },
   mutator:   mutatorGroup[0],
   pauseFunc: pause,
  }

  module.startedMutator = mutatorGroup[0].index

  func() {
   defer func() {
    if r := recover(); r != nil {
     in := fmt.Sprintf("%s %q for %s", direction, mutatorGroup[0].name, module)
     if err, ok := r.(panicError); ok {
      err.addIn(in)
      mctx.error(err)
     } else {
      mctx.error(newPanicErrorf(r, in))
     }
    }
   }()
   direction.run(mutatorGroup, mctx)
  }()

  module.finishedMutator = mutatorGroup[len(mutatorGroup)-1].index

  hasErrors := false
  if len(mctx.errs) > 0 {
   errsCh <- mctx.errs
   hasErrors = true
  } else {
   if len(mctx.reverseDeps) > 0 || len(mctx.replace) > 0 || len(mctx.rename) > 0 || len(mctx.newModules) > 0 || len(mctx.ninjaFileDeps) > 0 || len(mctx.module.newOnDemandReverseDeps) > 0 {
    globalStateCh <- globalStateChange{
     reverse:             mctx.reverseDeps,
     replace:             mctx.replace,
     rename:              mctx.rename,
     newModules:          mctx.newModules,
     deps:                mctx.ninjaFileDeps,
     onDemandReverseDeps: module.newOnDemandReverseDeps,
    }
   }
  }
  if module.startedMutator == c.mutatorIndexAfterLastCreateModule {
   // Store core module info after the final CreateModule mutator.
   // This ensures that storeCoreModuleInfo is called on modules
   // created using CreateModule.
   mctx.storeCoreModuleInfo()
  }

  mutatorContextPool.Put(mctx)
  mctx = nil

  return hasErrors
 }

 type onDemandModuleUniqueKey struct {
  group   *moduleGroup
  variant string
 }
 variantToOnDemandModule := map[onDemandModuleUniqueKey]*moduleInfo{}

 // Process errs and reverseDeps in a single goroutine
 go func() {
  for {
   select {
   case newErrs := <-errsCh:
    errs = append(errs, newErrs...)
   case globalStateChange := <-globalStateCh:
    for _, r := range globalStateChange.reverse {
     reverseDeps[r.module] = append(reverseDeps[r.module], r.dep)
    }
    for _, r := range globalStateChange.onDemandReverseDeps {
     if _, exists := onDemandReverseDeps[r.module]; !exists {
      onDemandReverseDeps[r.module] = make(map[*moduleInfo]bool)
     }
     onDemandReverseDeps[r.module][r.dep.module] = true
    }
    replace = append(replace, globalStateChange.replace...)
    rename = append(rename, globalStateChange.rename...)
    newModules = append(newModules, globalStateChange.newModules...)
    onDemandModules = append(onDemandModules, globalStateChange.onDemandModules...)
    deps = append(deps, globalStateChange.deps...)
   case createdOnDemandStateChange := <-createdOnDemandStateCh:
    var variantSb strings.Builder
    mod := createdOnDemandStateChange.module
    for _, mutator := range slices.Sorted(maps.Keys(mod.requestedOnDemandVariant.variations)) {
     variantSb.WriteString("-")
     variantSb.WriteString(mod.requestedOnDemandVariant.variations[mutator])
    }
    uniqueDepKey := onDemandModuleUniqueKey{
     mod.group,
     variantSb.String(),
    }
    if existing, exists := variantToOnDemandModule[uniqueDepKey]; exists {
     createdOnDemandStateChange.moduleReplaceWithCh <- existing
    } else {
     createdOnDemandStateChange.moduleReplaceWithCh <- createdOnDemandStateChange.module
     variantToOnDemandModule[uniqueDepKey] = createdOnDemandStateChange.module
    }
   case <-done:
    return
   }
  }
 }()

 visitErrs := parallelVisit(c.iterateAllVariants(), direction.orderer(), parallelVisitLimit, visit)

 if len(visitErrs) > 0 {
  return nil, visitErrs
 }

 for _, mutator := range mutatorGroup {
  c.finishedMutators[mutator.index] = true
 }

 done <- true

 if len(errs) > 0 {
  return nil, errs
 }

 transitionMutator := mutatorGroup[0].transitionMutator

 if transitionMutator != nil {
  for _, group := range c.moduleGroups {
   for i := 0; i < len(group.modules); i++ {
    module := group.modules[i]

    // Update module group to contain newly split variants
    if module.splitModules != nil {
     group.modules, i = spliceModules(group.modules, i, module.splitModules)
    }

    // Fix up any remaining dependencies on modules that were split into variants
    // by replacing them with the first variant
    for j, dep := range module.directDeps {
     if dep.module.obsoletedByNewVariants {
      module.directDeps[j].module = dep.module.splitModules.firstModule()
     }
    }

    if module.createdBy != nil && module.createdBy.obsoletedByNewVariants {
     module.createdBy = module.createdBy.splitModules.firstModule()
    }
   }
  }

  c.completedTransitionMutators = transitionMutator.index + 1
 } else {
  for _, group := range c.moduleGroups {
   for _, module := range group.modules {
    // Add any new forward dependencies to the reverse dependencies of the dependency to avoid
    // having to call a full c.updateDependencies().
    for _, m := range module.newDirectDeps {
     m.reverseDeps = append(m.reverseDeps, module)
    }
    module.newDirectDeps = nil
   }
  }
 }

 // Add in any new reverse dependencies that were added by the mutator
 for module, deps := range reverseDeps {
  sort.Sort(depSorter{deps, c.nameInterface})
  module.directDeps = append(module.directDeps, deps...)
  for _, dep := range deps {
   module.forwardDeps = append(module.forwardDeps, dep.module)
   dep.module.reverseDeps = append(dep.module.reverseDeps, module)
  }
 }

 // Set forward/reverseDeps of on-demand variants.
 // Sort the deps to ensure deterministic orderding.
 for module, depsAsKeys := range onDemandReverseDeps {
  deps := slices.Collect(maps.Keys(depsAsKeys))
  slices.SortFunc(deps, func(a, b *moduleInfo) int {
   return cmp.Compare(a.variant.name, b.variant.name)
  })
  for _, dep := range deps {
   module.forwardDeps = append(module.forwardDeps, dep)
   dep.reverseDeps = append(dep.reverseDeps, module)
  }
  module.newOnDemandReverseDeps = nil
 }

 for _, module := range newModules {
  errs = c.addModule(module)
  if len(errs) > 0 {
   return nil, errs
  }
 }

 errs = c.handleRenames(rename)
 if len(errs) > 0 {
  return nil, errs
 }

 errs = c.handleReplacements(replace)
 if len(errs) > 0 {
  return nil, errs
 }

 if c.needsUpdateDependencies > 0 {
  errs = c.updateDependencies()
  if len(errs) > 0 {
   return nil, errs
  }
 }

 // Add the on-demand variant into its module group.
 // Since there can be inter-variant deps, it needs to be inserted before its inter-variant rdep.
 insertIntoModuleGroup := func(module *moduleInfo) {
  isInterVariantDep := false
  // Check reverseDeps to see if inter-variant rdep exists.
  for _, rdep := range module.reverseDeps {
   if rdep.group == module.group {
    isInterVariantDep = true
    break
   }
  }
  insertIndex := len(module.group.modules)
  if isInterVariantDep {
   insertIndex = slices.IndexFunc(module.group.modules, func(groupModule *moduleInfo) bool {
    return slices.ContainsFunc(groupModule.directDeps, func(directDep depInfo) bool {
     return directDep.module == module
    })
   })
   if insertIndex == -1 {
    insertIndex = len(module.group.modules)
   }
  }
  module.group.modules = slices.Insert(module.group.modules, insertIndex, module)
 }

 moduleGroupsWithOnDemandModules := make(map[*moduleGroup]bool)

 for _, module := range onDemandModules {
  moduleGroupsWithOnDemandModules[module.group] = true
  insertIntoModuleGroup(module)
  for _, fd := range module.newDirectDeps {
   fd.reverseDeps = append(fd.reverseDeps, module)
   // AddVariationDependency allows adding a dependency on itself, but only if
   // that module is earlier in the module list than this one, since we always
   // run GenerateBuildActions in order for the variants of a module
   if fd.group == module.group && beforeInModuleList(module, fd, module.group.modules) {
    return nil, []error{&BlueprintError{
     Err: fmt.Errorf("%q depends on later version of itself", module.Name()),
     Pos: module.pos,
    }}
   }
  }

  // The module has been created and mutated.
  // For future mutators, this variant is the same as normal variants.
  // Set this flag to false so that we do not try to call `rerunMutatorsOnVariantOnDemand` again.
  module.createdOnDemand = false
  module.createdOnDemandReplaceWith = nil
  module.newDirectDeps = nil
  module.newOnDemandReverseDeps = nil
  module.createdOnDemandSupportedSplits = nil
  module.group.cachedVariantsOnDemand = nil
 }

 for g := range moduleGroupsWithOnDemandModules {
  slices.SortStableFunc(g.modules, func(a, b *moduleInfo) int {
   return cmp.Compare(a.sortIndex, b.sortIndex)
  })
 }

 return deps, errs
}

// Removes modules[i] from the list and inserts newModules... where it was located, returning
// the new slice and the index of the last inserted element
func spliceModules(modules moduleList, i int, newModules moduleList) (moduleList, int) {
 spliceSize := len(newModules)
 newLen := len(modules) + spliceSize - 1
 var dest moduleList
 if cap(modules) >= len(modules)-1+len(newModules) {
  // We can fit the splice in the existing capacity, do everything in place
  dest = modules[:newLen]
 } else {
  dest = make(moduleList, newLen)
  copy(dest, modules[:i])
 }

 // Move the end of the slice over by spliceSize-1
 copy(dest[i+spliceSize:], modules[i+1:])

 // Copy the new modules into the slice
 copy(dest[i:], newModules)

 return dest, i + spliceSize - 1
}

func (c *Context) generateModuleBuildActions(config interface{},
 liveGlobals *liveTracker) ([]string, []error) {

 c.BeginEvent("generateModuleBuildActions")
 defer c.EndEvent("generateModuleBuildActions")
 var deps []string
 var errs []error

 cancelCh := make(chan struct{})
 errsCh := make(chan []error)
 depsCh := make(chan []string)

 go func() {
  for {
   select {
   case <-cancelCh:
    close(cancelCh)
    return
   case newErrs := <-errsCh:
    errs = append(errs, newErrs...)
   case newDeps := <-depsCh:
    deps = append(deps, newDeps...)

   }
  }
 }()

 visitErrs := parallelVisit(c.iterateAllVariants(), bottomUpVisitor, parallelVisitLimit,
  func(module *moduleInfo, pause pauseFunc) bool {
   module.cachedUniqueName = c.nameInterface.UniqueName(newNamespaceContext(module), module.group.name)
   sanitizedName := toNinjaName(module.cachedUniqueName)
   sanitizedVariant := toNinjaName(module.variant.name)

   prefix := moduleNamespacePrefix(sanitizedName + "_" + sanitizedVariant)

   // The parent scope of the moduleContext's local scope gets overridden to be that of the
   // calling Go package on a per-call basis.  Since the initial parent scope doesn't matter we
   // just set it to nil.
   scope := newLocalScope(nil, prefix)

   mctx := &moduleContext{
    baseModuleContext: baseModuleContext{
     context: c,
     config:  config,
     module:  module,
    },
    scope:              scope,
    handledMissingDeps: module.missingDeps == nil,
   }

   // Use a deferred call for this, to avoid errors from trying to evaluate the select()
   // expressions in the configurable values that mctx.evaluator encounters too early.
   defer func() {
    if c.moduleDebugDataChannel != nil {
     c.moduleDebugDataChannel <- getModuleDebugJson(mctx.evaluator, module)
    }
    // The evaluator isn't needed anymore. Avoid possibly cyclic ref that may increase gc load.
    mctx.evaluator = nil
   }()

   module.startedGenerateBuildActions = true

   func() {
    defer func() {
     if r := recover(); r != nil {
      in := fmt.Sprintf("GenerateBuildActions for %s", module)
      if err, ok := r.(panicError); ok {
       err.addIn(in)
       mctx.error(err)
      } else {
       mctx.error(newPanicErrorf(r, in))
      }
     }
    }()
    if !module.restoreModuleBuildActions(c) || c.incrementalProviderTest {
     if !c.SkipCloneModulesAfterMutators {
      // Replaces every build logic module with a clone of itself.  Prevents introducing problems where
      // a mutator sets a non-property member variable on a module, which works until a later mutator
      // creates variants of that module.
      module.logicModule, module.properties = c.cloneLogicModule(module)
      module.logicModule.setInfo(module)
     }

     module.logicModule.GenerateBuildActions(mctx)
    }
    module.calculateProviderHash()
   }()

   module.finishedGenerateBuildActions = true

   if len(mctx.errs) > 0 {
    errsCh <- mctx.errs
    return true
   }

   if module.missingDeps != nil && !mctx.handledMissingDeps && !module.incrementalRestored {
    var errs []error
    for _, depName := range module.missingDeps {
     errs = append(errs, c.missingDependencyError(module, depName))
    }
    errsCh <- errs
    return true
   }

   depsCh <- mctx.ninjaFileDeps

   if module.freeAfterGenerateBuildActions {
    // This module is freed after GenerateBuildActions complete, requiring all future accesses
    // to go through ModuleProxy instead of the Module.
    // Cache Module.Name() and Module.String() for future use in ModuleProxy.Name() and ModuleProxy.String()
    module.cachedName = module.logicModule.Name()
    module.cachedString = module.logicModule.String()
    // When soong debug data is requested, don't remove these info, they will show up in soong-debug-info.json.
    if c.moduleDebugDataChannel == nil {
     // TODO: logicModule is needed to evaluate configurable properties, we should figure out an alternative.
     module.logicModule = nil
     module.properties = nil
     module.propertyPos = nil
    }
   }

   newErrs := c.processLocalBuildActions(&module.actionDefs,
    &mctx.actionDefs, liveGlobals)
   if len(newErrs) > 0 {
    errsCh <- newErrs
    return true
   }
   return false
  })

 cancelCh <- struct{}{}
 <-cancelCh

 errs = append(errs, visitErrs...)

 return deps, errs
}

func (c *Context) WriteIncrementalDebugInfo(filename string, modules []*moduleInfo) {
 f, err := os.Create(filename)
 if err != nil {
  // We expect this to be writable
  panic(fmt.Sprintf("couldn't create incremental module debug file %s: %s", filename, err))
 }
 defer f.Close()

 needComma := false
 f.WriteString("{\n\"modules\": [\n")

 for _, module := range modules {
  if module.incrementalDebugInfo == nil {
   continue
  }
  if needComma {
   f.WriteString(",\n")
  } else {
   needComma = true
  }

  f.Write(module.incrementalDebugInfo)
 }

 f.WriteString("\n]\n}")
}

func (c *Context) generateOneSingletonBuildActions(config interface{},
 info *singletonInfo, liveGlobals *liveTracker) ([]string, []error) {

 var deps []string
 var errs []error

 // The parent scope of the singletonContext's local scope gets overridden to be that of the
 // calling Go package on a per-call basis.  Since the initial parent scope doesn't matter we
 // just set it to nil.
 scope := newLocalScope(nil, singletonNamespacePrefix(info.name))

 sctx := &singletonContext{
  singleton:    info,
  context:      c,
  config:       config,
  scope:        scope,
  globals:      liveGlobals,
  depProviders: make(map[int]bool),
 }

 info.startedGenerateBuildActions = true

 func() {
  defer func() {
   if r := recover(); r != nil {
    in := fmt.Sprintf("GenerateBuildActions for singleton %s", info.name)
    if err, ok := r.(panicError); ok {
     err.addIn(in)
     sctx.error(err)
    } else {
     sctx.error(newPanicErrorf(r, in))
    }
   }
  }()

  c.restoreSingleton(info)
  if !info.incrementalRestored || c.incrementalProviderTest {
   info.singleton.GenerateBuildActions(sctx)
  }
  // A caching entry for singletons is only written if the singleton
  // depends on at least one provider. If zero providers are consumed,
  // the cache restore process would fail to locate
  // a cache entry, forcing the singleton to re-execute on every build.
  //
  // An edge case where a coding change removes a provider dependency is safely handled
  // because any detected code change automatically invalidates the entire global cache,
  // ensuring system consistency.
  if info.buildActionCacheKey != nil && !info.incrementalRestored {
   cache := make(map[int]proptools.Hash)
   for k, _ := range sctx.depProviders {
    // A singleton might depend on both module providers and singleton providers, and
    // the hashes of all the former are stored in context globally, and the hashes of
    // the latter are stored inside the current singletonInfo.
    if providerRegistry[k].mutator == singletonTag {
     cache[k] = info.providerValueHashes[k]
    } else {
     cache[k] = c.providerValueHashes[k]
    }
   }

   var providerHashes []ProviderHash
   for i, p := range info.providers {
    if p == nil {
     continue
    }
    err := c.keyValueStoreCache.writeProvider(c.EncContext, info.providerInitialValueHashes[i], CachedProvider{
     Id:    providerRegistry[i],
     Value: p,
    })
    if err != nil {
     panic(err)
    }
    providerHashes = append(providerHashes,
     ProviderHash{
      Id:   providerRegistry[i],
      Hash: info.providerInitialValueHashes[i],
     })
   }

   if err := c.keyValueStoreCache.writeSingletonBuildAction(c.EncContext, info.buildActionCacheKey,
    &SingletonActionCachedData{
     ProviderHashes:           providerHashes,
     DependencyProviderHashes: cache,
     GlobCache:                info.globCache,
    }); err != nil {
    panic(err)
   }
  }
 }()

 info.finishedGenerateBuildActions = true

 if len(sctx.errs) > 0 {
  errs = append(errs, sctx.errs...)
  return deps, errs
 }

 deps = append(deps, sctx.ninjaFileDeps...)
 info.subninjas = append(info.subninjas, sctx.subninjas...)

 newErrs := c.processLocalBuildActions(&info.actionDefs,
  &sctx.actionDefs, liveGlobals)
 errs = append(errs, newErrs...)
 return deps, errs
}

func (c *Context) restoreSingleton(info *singletonInfo) {
 info.incrementalSupported = c.GetIncrementalEnabled() && info.singleton.IncrementalSupported()
 if !info.incrementalSupported {
  return
 }

 info.buildActionCacheKey = &DataCacheKey{
  Id: info.name,
 }

 // We can pursue a incremental analysis because there is no soong coding change, no
 // product config and env variable changes.
 if !c.GetIncrementalAnalysis() {
  return
 }

 data, err := c.keyValueStoreCache.readSingletonBuildAction(c.EncContext, info.buildActionCacheKey)
 if err != nil {
  panic(err)
 }
 if data == nil {
  return
 }

 // This logic here assumes a singleton's behavior is a pure function of its providers.
 // Conditional access to certain providers must also be based on other provider
 // values, ensuring that any behavioral change is captured by the input providers hashes.
 for k, v := range data.DependencyProviderHashes {
  var hash proptools.Hash
  if providerRegistry[k].mutator == singletonTag {
   hash = info.providerValueHashes[k]
  } else {
   hash = c.providerValueHashes[k]
  }
  if hash != v {
   return
  }
 }

 for _, glob := range data.GlobCache {
  result, err := c.glob(glob.Pattern, glob.Excludes)
  if err != nil {
   panic(newPanicErrorf(err, "failed to glob for cached singleton: %s %s %v", info.name, glob.Pattern, glob.Excludes))
  }
  hash, err := proptools.CalculateHash(stringList(result))
  if err != nil {
   panic(newPanicErrorf(err, "failed to calculate hash for cached glob result: %s", info.name))
  }
  if hash != glob.Result {
   // Don't restore, a glob result has changed.
   return
  }
 }

 info.incrementalRestored = true
 info.providerInitialValueHashes = make([]proptools.Hash, len(providerRegistry))
 info.hasUnrestoredProvider = make([]bool, len(providerRegistry))
 for _, provider := range data.ProviderHashes {
  info.hasUnrestoredProvider[provider.Id.id] = true
  info.providerInitialValueHashes[provider.Id.id] = provider.Hash
 }
}

func (c *Context) generateParallelSingletonBuildActions(config interface{},
 singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) {

 c.BeginEvent("generateParallelSingletonBuildActions")
 defer c.EndEvent("generateParallelSingletonBuildActions")

 var deps []string
 var errs []error

 wg := sync.WaitGroup{}
 cancelCh := make(chan struct{})
 depsCh := make(chan []string)
 errsCh := make(chan []error)

 go func() {
  for {
   select {
   case <-cancelCh:
    close(cancelCh)
    return
   case dep := <-depsCh:
    deps = append(deps, dep...)
   case newErrs := <-errsCh:
    if len(errs) <= maxErrors {
     errs = append(errs, newErrs...)
    }
   }
  }
 }()

 for _, info := range singletons {
  if !info.parallel {
   // Skip any singletons registered with parallel=false.
   continue
  }
  wg.Add(1)
  go func(inf *singletonInfo) {
   defer wg.Done()
   newDeps, newErrs := c.generateOneSingletonBuildActions(config, inf, liveGlobals)
   depsCh <- newDeps
   errsCh <- newErrs
  }(info)
 }
 wg.Wait()

 cancelCh <- struct{}{}
 <-cancelCh

 return deps, errs
}

func (c *Context) calculateProvidersHashes() {
 c.providerValueHashes = make([]proptools.Hash, len(providerRegistry))
 var wg sync.WaitGroup
 for i := range len(providerRegistry) {
  wg.Add(1)
  go func() {
   defer wg.Done()

   var providerHashes []proptools.Hash
   // For singleton providers we collect provider hashes from singletons.
   if providerRegistry[i].mutator != singletonTag {
    c.visitAllModuleInfos(func(m *moduleInfo) {
     if m.providerInitialValueHashes != nil {
      providerHashes = append(providerHashes, m.providerInitialValueHashes[i])
     }
    })
   }
   var err error
   c.providerValueHashes[i], err = proptools.CalculateHash(hashList(providerHashes))
   if err != nil {
    panic(err)
   }
  }()
 }
 wg.Wait()
}

func (c *Context) generateSingletonBuildActions(config interface{},
 singletons []*singletonInfo, liveGlobals *liveTracker) ([]string, []error) {

 c.BeginEvent("generateSingletonBuildActions")
 defer c.EndEvent("generateSingletonBuildActions")

 var deps []string
 var errs []error

 // Run one singleton.  Use a variable to simplify manual validation testing.
 var runSingleton = func(info *singletonInfo) {
  c.BeginEvent("singleton:" + info.name)
  defer c.EndEvent("singleton:" + info.name)
  newDeps, newErrs := c.generateOneSingletonBuildActions(config, info, liveGlobals)
  deps = append(deps, newDeps...)
  errs = append(errs, newErrs...)
 }

 // Force a resort of the module groups before running singletons so that two singletons running in parallel
 // don't cause a data race when they trigger a resort in VisitAllModules.
 c.sortedModuleGroups()

 if c.GetIncrementalEnabled() {
  c.calculateProvidersHashes()
 }

 // First, take care of any singletons that want to run in parallel.
 deps, errs = c.generateParallelSingletonBuildActions(config, singletons, liveGlobals)

 for _, info := range singletons {
  if !info.parallel {
   if c.GetIncrementalEnabled() {
    // A singleton might depend on modules and other singletons that run before it.
    // We already captured the provider hashes of all the modules in calculateProvidersHashes,
    // now we calculate the hashes of all singletons that the current one might depend on.
    info.providerValueHashes = make([]proptools.Hash, len(providerRegistry))
    for i := range len(providerRegistry) {
     var providerHashes []proptools.Hash
     if providerRegistry[i].mutator == singletonTag {
      c.VisitAllSingletons(func(s SingletonProxy) {
       hash := proptools.ZeroHash
       if s.singleton.providerInitialValueHashes != nil {
        hash = s.singleton.providerInitialValueHashes[i]
       }
       providerHashes = append(providerHashes, hash)
      })
     }
     var err error
     info.providerValueHashes[i], err = proptools.CalculateHash(hashList(providerHashes))
     if err != nil {
      panic(err)
     }
    }
   }
   runSingleton(info)
   if len(errs) > maxErrors {
    break
   }
  }
 }

 return deps, errs
}

func (c *Context) processLocalBuildActions(out, in *localBuildActions,
 liveGlobals *liveTracker) []error {

 var errs []error

 // First we go through and add everything referenced by the module's
 // buildDefs to the live globals set.  This will end up adding the live
 // locals to the set as well, but we'll take them out after.
 for _, def := range in.buildDefs {
  err := liveGlobals.AddBuildDefDeps(def)
  if err != nil {
   errs = append(errs, err)
  }
 }

 if len(errs) > 0 {
  return errs
 }

 out.buildDefs = append(out.buildDefs, in.buildDefs...)

 // We use the now-incorrect set of live "globals" to determine which local
 // definitions are live.  As we go through copying those live locals to the
 // moduleGroup we remove them from the live globals set.
 for _, v := range in.variables {
  isLive := liveGlobals.RemoveVariableIfLive(v)
  if isLive {
   out.variables = append(out.variables, v)
  }
 }

 for _, r := range in.rules {
  isLive := liveGlobals.RemoveRuleIfLive(r)
  if isLive {
   out.rules = append(out.rules, r)
  }
 }

 return nil
}

func (c *Context) walkDeps(topModule *moduleInfo, allowDuplicates bool,
 visitDown func(depInfo, *moduleInfo) bool, visitUp func(depInfo, *moduleInfo)) {

 visited := make(map[*moduleInfo]bool)
 var visiting *moduleInfo

 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "WalkDeps(%s, %s, %s) for dependency %s",
    topModule, funcName(visitDown), funcName(visitUp), visiting))
  }
 }()

 var walk func(module *moduleInfo)
 walk = func(module *moduleInfo) {
  for _, dep := range module.directDeps {
   if allowDuplicates || !visited[dep.module] {
    visiting = dep.module
    recurse := true
    if visitDown != nil {
     recurse = visitDown(dep, module)
    }
    if recurse && !visited[dep.module] {
     walk(dep.module)
     visited[dep.module] = true
    }
    if visitUp != nil {
     visitUp(dep, module)
    }
   }
  }
 }

 walk(topModule)
}

type replace struct {
 from, to  *moduleInfo
 predicate ReplaceDependencyPredicate
}

type rename struct {
 group *moduleGroup
 name  string
}

type onDemandDep struct {
 from, to *moduleInfo
 tag      DependencyTag
}

// moduleVariantsThatDependOn takes the name of a module and a dependency and returns the all the variants of the
// module that depends on the dependency.
func (c *Context) moduleVariantsThatDependOn(name string, dep *moduleInfo) []*moduleInfo {
 group := c.moduleGroupFromName(name, dep.namespace())
 var variants []*moduleInfo

 if group == nil {
  return nil
 }

 for _, m := range group.modules {
  for _, moduleDep := range m.directDeps {
   if moduleDep.module == dep {
    variants = append(variants, m)
   }
  }
 }

 return variants
}

func (c *Context) handleRenames(renames []rename) []error {
 var errs []error
 for _, rename := range renames {
  group, name := rename.group, rename.name
  if name == group.name || len(group.modules) < 1 {
   continue
  }

  errs = append(errs, c.nameInterface.Rename(group.name, rename.name, group.namespace)...)
 }

 return errs
}

func (c *Context) handleReplacements(replacements []replace) []error {
 var errs []error
 changedDeps := false
 for _, replace := range replacements {
  for _, m := range replace.from.reverseDeps {
   for i, d := range m.directDeps {
    if d.module == replace.from {
     // If the replacement has a predicate then check it.
     if replace.predicate == nil || replace.predicate(m.logicModule, d.tag, d.module.logicModule) {
      m.directDeps[i].module = replace.to
      changedDeps = true
     }
    }
   }
  }

 }

 if changedDeps {
  c.needsUpdateDependencies++
 }
 return errs
}

func (c *Context) discoveredMissingDependencies(module *moduleInfo, depName string, depVariations variationMap) (errs []error) {
 if !depVariations.empty() {
  depName = depName + "{" + c.prettyPrintVariant(depVariations) + "}"
 }
 if c.allowMissingDependencies {
  module.missingDeps = append(module.missingDeps, depName)
  return nil
 }
 return []error{c.missingDependencyError(module, depName)}
}

func (c *Context) missingDependencyError(module *moduleInfo, depName string) (errs error) {
 guess := namesLike(depName, module.Name(), c.moduleGroups)
 err := c.nameInterface.MissingDependencyError(module.Name(), module.namespace(), depName, guess)
 return &BlueprintError{
  Err: err,
  Pos: module.pos,
 }
}

func (c *Context) moduleGroupFromName(name string, namespace Namespace) *moduleGroup {
 group, exists := c.nameInterface.ModuleFromName(name, namespace)
 if exists {
  return group.moduleGroup
 }
 return nil
}

func (c *Context) sortedModuleGroups() []*moduleGroup {
 moduleLess := func(a, b *moduleInfo) int {
  return cmp.Compare(a.variant.name, b.variant.name)
 }
 if c.cachedSortedModuleGroups == nil || c.cachedDepsModified {
  unwrap := func(wrappers []ModuleGroup) []*moduleGroup {
   result := make([]*moduleGroup, 0, len(wrappers))
   for _, group := range wrappers {
    // Order the module variants in-place
    slices.SortFunc(group.moduleGroup.modules, moduleLess)
    result = append(result, group.moduleGroup)
   }
   return result
  }

  c.cachedSortedModuleGroups = unwrap(c.nameInterface.AllModules())
  c.cachedDepsModified = false
 }

 return c.cachedSortedModuleGroups
}

func (c *Context) visitAllModuleVariants(module *moduleInfo,
 visit func(*moduleInfo)) {

 for _, module := range module.group.modules {
  visit(module)
 }
}

func (c *Context) visitAllModuleInfos(visit func(*moduleInfo)) {
 for _, moduleGroup := range c.sortedModuleGroups() {
  for _, module := range moduleGroup.modules {
   visit(module)
  }
 }
}

func (c *Context) requireNinjaVersion(major, minor, micro int) {
 if major != 1 {
  panic("ninja version with major version != 1 not supported")
 }
 if c.requiredNinjaMinor < minor {
  c.requiredNinjaMinor = minor
  c.requiredNinjaMicro = micro
 }
 if c.requiredNinjaMinor == minor && c.requiredNinjaMicro < micro {
  c.requiredNinjaMicro = micro
 }
}

func (c *Context) setOutDir(value *ninjaString) {
 if c.outDir == nil {
  c.outDir = value
 }
}

func (c *Context) makeUniquePackageNames(
 liveGlobals *liveTracker) (map[*packageContext]string, []string) {

 pkgs := make(map[string]*packageContext)
 pkgNames := make(map[*packageContext]string)
 longPkgNames := make(map[*packageContext]bool)

 processPackage := func(pctx *packageContext) {
  if pctx == nil {
   // This is a built-in rule and has no package.
   return
  }
  if _, ok := pkgNames[pctx]; ok {
   // We've already processed this package.
   return
  }

  otherPkg, present := pkgs[pctx.shortName]
  if present {
   // Short name collision.  Both this package and the one that's
   // already there need to use their full names.  We leave the short
   // name in pkgNames for now so future collisions still get caught.
   longPkgNames[pctx] = true
   longPkgNames[otherPkg] = true
  } else {
   // No collision so far.  Tentatively set the package's name to be
   // its short name.
   pkgNames[pctx] = pctx.shortName
   pkgs[pctx.shortName] = pctx
  }
 }

 // We try to give all packages their short name, but when we get collisions
 // we need to use the full unique package name.
 for v, _ := range liveGlobals.variables {
  processPackage(v.packageContext())
 }
 for p, _ := range liveGlobals.pools {
  processPackage(p.packageContext())
 }
 for r, _ := range liveGlobals.rules {
  processPackage(r.packageContext())
 }

 // Add the packages that had collisions using their full unique names.  This
 // will overwrite any short names that were added in the previous step.
 for pctx := range longPkgNames {
  pkgNames[pctx] = pctx.fullName
 }

 // Create deps list from calls to PackageContext.AddNinjaFileDeps
 deps := []string{}
 for _, pkg := range pkgs {
  deps = append(deps, pkg.ninjaFileDeps...)
 }

 return pkgNames, deps
}

// memoizeFullNames stores the full name of each live global variable, rule and pool since each is
// guaranteed to be used at least twice, once in the definition and once for each usage, and many
// are used much more than once.
func (c *Context) memoizeFullNames(liveGlobals *liveTracker, pkgNames map[*packageContext]string) *nameTracker {
 nameTracker := &nameTracker{
  pkgNames:  pkgNames,
  variables: make(map[Variable]string),
  rules:     make(map[Rule]string),
  pools:     make(map[Pool]string),
 }
 for v := range liveGlobals.variables {
  nameTracker.variables[v] = v.fullName(pkgNames)
 }
 for r := range liveGlobals.rules {
  nameTracker.rules[r] = r.fullName(pkgNames)
 }
 for p := range liveGlobals.pools {
  nameTracker.pools[p] = p.fullName(pkgNames)
 }
 return nameTracker
}

func (c *Context) checkForVariableReferenceCycles(
 variables map[Variable]*ninjaString, nameTracker *nameTracker) {

 visited := make(map[Variable]bool)  // variables that were already checked
 checking := make(map[Variable]bool) // variables actively being checked

 var check func(v Variable) []Variable

 check = func(v Variable) []Variable {
  visited[v] = true
  checking[v] = true
  defer delete(checking, v)

  value := variables[v]
  for _, dep := range value.Variables() {
   if checking[dep] {
    // This is a cycle.
    return []Variable{dep, v}
   }

   if !visited[dep] {
    cycle := check(dep)
    if cycle != nil {
     if cycle[0] == v {
      // We are the "start" of the cycle, so we're responsible
      // for generating the errors.  The cycle list is in
      // reverse order because all the 'check' calls append
      // their own module to the list.
      msgs := []string{"detected variable reference cycle:"}

      // Iterate backwards through the cycle list.
      curName := nameTracker.Variable(v)
      curValue := value.Value(nameTracker)
      for i := len(cycle) - 1; i >= 0; i-- {
       next := cycle[i]
       nextName := nameTracker.Variable(next)
       nextValue := variables[next].Value(nameTracker)

       msgs = append(msgs, fmt.Sprintf(
        "    %q depends on %q", curName, nextName))
       msgs = append(msgs, fmt.Sprintf(
        "    [%s = %s]", curName, curValue))

       curName = nextName
       curValue = nextValue
      }

      // Variable reference cycles are a programming error,
      // not the fault of the Blueprint file authors.
      panic(strings.Join(msgs, "\n"))
     } else {
      // We're not the "start" of the cycle, so we just append
      // our module to the list and return it.
      return append(cycle, v)
     }
    }
   }
  }

  return nil
 }

 for v := range variables {
  if !visited[v] {
   cycle := check(v)
   if cycle != nil {
    panic("inconceivable!")
   }
  }
 }
}

// ModuleTypePropertyStructs returns a mapping from module type name to a list of pointers to
// property structs returned by the factory for that module type.
func (c *Context) ModuleTypePropertyStructs() map[string][]interface{} {
 ret := make(map[string][]interface{}, len(c.moduleFactories))
 for moduleType, factory := range c.moduleFactories {
  _, ret[moduleType] = factory()
 }

 return ret
}

func (c *Context) ModuleTypeFactories() map[string]ModuleFactory {
 return maps.Clone(c.moduleFactories)
}

func (c *Context) ModuleName(logicModule ModuleOrProxy) string {
 return logicModule.info().Name()
}

func (c *Context) ModuleDir(logicModule ModuleOrProxy) string {
 return filepath.Dir(c.BlueprintFile(logicModule))
}

func (c *Context) ModuleSubDir(logicModule ModuleOrProxy) string {
 return logicModule.info().variant.name
}

func (c *Context) ModuleType(logicModule ModuleOrProxy) string {
 return logicModule.info().typeName
}

// ModuleProvider returns the value, if any, for the provider for a module.  If the value for the
// provider was not set it returns nil and false.  The return value should always be considered read-only.
// It panics if called before the appropriate mutator or GenerateBuildActions pass for the provider on the
// module.  The value returned may be a deep copy of the value originally passed to SetProvider.
func (c *Context) ModuleProvider(logicModule ModuleOrProxy, provider AnyProviderKey) (any, bool) {
 return c.provider(logicModule.info(), provider.provider())
}

func (c *Context) BlueprintFile(logicModule ModuleOrProxy) string {
 return logicModule.info().relBlueprintsFile
}

func (c *Context) moduleErrorf(module *moduleInfo, format string,
 args ...interface{}) error {
 if module == nil {
  // This can happen if ModuleErrorf is called from a load hook
  return &BlueprintError{
   Err: fmt.Errorf(format, args...),
  }
 }

 return &ModuleError{
  BlueprintError: BlueprintError{
   Err: fmt.Errorf(format, args...),
   Pos: module.pos,
  },
  module: module,
 }
}

func (c *Context) ModuleErrorf(logicModule ModuleOrProxy, format string,
 args ...interface{}) error {
 return c.moduleErrorf(logicModule.info(), format, args...)
}

func (c *Context) PropertyErrorf(logicModule ModuleOrProxy, property string, format string,
 args ...interface{}) error {

 module := logicModule.info()
 if module == nil {
  // This can happen if PropertyErrorf is called from a load hook
  return &BlueprintError{
   Err: fmt.Errorf(format, args...),
  }
 }

 pos := module.propertyPos[property]
 if !pos.IsValid() {
  pos = module.pos
 }

 return &PropertyError{
  ModuleError: ModuleError{
   BlueprintError: BlueprintError{
    Err: fmt.Errorf(format, args...),
    Pos: pos,
   },
   module: module,
  },
  property: property,
 }
}

func (c *Context) VisitAllModules(visit func(Module)) {
 var visitingModule *moduleInfo
 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitAllModules(%s) for %s",
    funcName(visit), visitingModule))
  }
 }()

 c.visitAllModuleInfos(func(module *moduleInfo) {
  visitingModule = module
  if module.logicModule == nil {
   panic(fmt.Errorf("VisitAllModules visited module %s that called FreeAfterGenerateBuildActions()", module))
  }
  visit(module.logicModule)
 })
}

func (c *Context) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
 var visitingModule *moduleInfo
 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitAllModulesIf(%s, %s) for %s",
    funcName(pred), funcName(visit), visitingModule))
  }
 }()

 c.visitAllModuleInfos(func(module *moduleInfo) {
  visitingModule = module
  if module.logicModule == nil {
   panic(fmt.Errorf("VisitAllModulesIf visited module %s that called FreeAfterGenerateBuildActions()", module))
  }
  if pred(module.logicModule) {
   visit(module.logicModule)
  }
 })
}

func (c *Context) VisitAllModulesProxies(visit func(ModuleProxy)) {
 var visitingModule *moduleInfo
 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitAllModules(%s) for %s",
    funcName(visit), visitingModule))
  }
 }()

 c.visitAllModuleInfos(func(module *moduleInfo) {
  visitingModule = module
  visit(ModuleProxy{module})
 })
}

func (c *Context) VisitAllModulesOrProxies(visit func(ModuleOrProxy)) {
 var visitingModule *moduleInfo
 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitAllModules(%s) for %s",
    funcName(visit), visitingModule))
  }
 }()

 c.visitAllModuleInfos(func(module *moduleInfo) {
  visitingModule = module
  if module.logicModule != nil {
   visit(module.logicModule)
  } else {
   visit(ModuleProxy{module})
  }
 })

}

func (c *Context) VisitDirectDeps(module Module, visit func(Module)) {
 c.VisitDirectDepsWithTags(module, func(m Module, _ DependencyTag) {
  visit(m)
 })
}

func (c *Context) VisitDirectDepsProxies(module ModuleOrProxy, visit func(ModuleProxy)) {
 topModule := module.info()

 var visiting *moduleInfo

 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitDirectDepsProxies(%s, %s) for dependency %s",
    topModule, funcName(visit), visiting))
  }
 }()

 for _, dep := range topModule.directDeps {
  visiting = dep.module
  visit(ModuleProxy{dep.module})
 }
}

func (c *Context) VisitDirectDepsProxiesWithTags(module ModuleOrProxy, visit func(ModuleProxy, DependencyTag)) {
 topModule := module.info()

 var visiting *moduleInfo

 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitDirectDepsProxies(%s, %s) for dependency %s",
    topModule, funcName(visit), visiting))
  }
 }()

 for _, dep := range topModule.directDeps {
  visiting = dep.module
  visit(ModuleProxy{dep.module}, dep.tag)
 }
}

func (c *Context) VisitDirectDepsWithTags(module Module, visit func(Module, DependencyTag)) {
 topModule := module.info()

 var visiting *moduleInfo

 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s",
    topModule, funcName(visit), visiting))
  }
 }()

 for _, dep := range topModule.directDeps {
  visiting = dep.module
  if dep.module.logicModule == nil {
   panic(fmt.Errorf("VisitDirectDepsWithTags visited module %s that called FreeAfterGenerateBuildActions()", dep.module))
  }
  visit(dep.module.logicModule, dep.tag)
 }
}

func (c *Context) VisitDirectDepsIf(module Module, pred func(Module) bool, visit func(Module)) {
 topModule := module.info()

 var visiting *moduleInfo

 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitDirectDepsIf(%s, %s, %s) for dependency %s",
    topModule, funcName(pred), funcName(visit), visiting))
  }
 }()

 for _, dep := range topModule.directDeps {
  visiting = dep.module
  if dep.module.logicModule == nil {
   panic(fmt.Errorf("VisitDirectDepsIf visited module %s that called FreeAfterGenerateBuildActions()", dep.module))
  }
  if pred(dep.module.logicModule) {
   visit(dep.module.logicModule)
  }
 }
}

func (c *Context) VisitDepsDepthFirst(module Module, visit func(Module)) {
 topModule := module.info()

 var visiting *moduleInfo

 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitDepsDepthFirst(%s, %s) for dependency %s",
    topModule, funcName(visit), visiting))
  }
 }()

 c.walkDeps(topModule, false, nil, func(dep depInfo, parent *moduleInfo) {
  visiting = dep.module
  if dep.module.logicModule == nil {
   panic(fmt.Errorf("VisitDepsDepthFirst visited module %s that called FreeAfterGenerateBuildActions()", dep.module))
  }
  visit(dep.module.logicModule)
 })
}

func (c *Context) VisitDepsDepthFirstIf(module Module, pred func(Module) bool, visit func(Module)) {
 topModule := module.info()

 var visiting *moduleInfo

 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitDepsDepthFirstIf(%s, %s, %s) for dependency %s",
    topModule, funcName(pred), funcName(visit), visiting))
  }
 }()

 c.walkDeps(topModule, false, nil, func(dep depInfo, parent *moduleInfo) {
  if dep.module.logicModule == nil {
   panic(fmt.Errorf("VisitDepsDepthFirstIf visited module %s that called FreeAfterGenerateBuildActions()", dep.module))
  }
  if pred(dep.module.logicModule) {
   visiting = dep.module
   visit(dep.module.logicModule)
  }
 })
}

func (c *Context) PrimaryModule(module Module) Module {
 return c.primaryModule(module.info()).logicModule
}

func (c *Context) primaryModule(moduleInfo *moduleInfo) *moduleInfo {
 return moduleInfo.group.modules.firstModule()
}

func (c *Context) IsPrimaryModule(module ModuleOrProxy) bool {
 return module.info().group.modules.firstModule() == module.info()
}

func (c *Context) IsFinalModule(module ModuleOrProxy) bool {
 return module.info().group.modules.lastModule() == module.info()
}

func (c *Context) VisitAllModuleVariants(module ModuleOrProxy, visit func(Module)) {
 var visitingModule *moduleInfo
 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitAllModuleVariants(%s) for %s",
    funcName(visit), visitingModule))
  }
 }()

 c.visitAllModuleVariants(module.info(), func(module *moduleInfo) {
  visitingModule = module
  if module.logicModule == nil {
   panic(fmt.Errorf("VisitAllModuleVariants visited module %s that called FreeAfterGenerateBuildActions()", module))
  }

  visit(module.logicModule)
 })
}

func (c *Context) VisitAllModuleVariantProxies(module ModuleProxy, visit func(ModuleProxy)) {
 var visitingModule *moduleInfo
 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitAllModuleVariantProxies(%s) for %s",
    funcName(visit), visitingModule))
  }
 }()

 c.visitAllModuleVariants(module.info(), func(module *moduleInfo) {
  visitingModule = module
  visit(ModuleProxy{module})
 })
}

func (c *Context) VisitAllSingletons(visit func(singleton SingletonProxy)) {
 var singleton *singletonInfo

 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitAllSingletons(%s) for %s",
    funcName(visit), singleton.name))
  }
 }()

 for _, singleton = range c.singletonInfo {
  // Only return the finished ones
  if singleton.finishedGenerateBuildActions {
   visit(SingletonProxy{singleton: singleton})
  }
 }
}

func (c *Context) ModuleToProxy(module ModuleOrProxy) ModuleProxy {
 return ModuleProxy{module.info()}
}

func (c *Context) CaptureBuildParams() {
 c.captureBuildParams = true
}

func (c *Context) BuildParamsForModule(module ModuleOrProxy) []BuildParams {
 if p := module.info().buildParams; p != nil {
  return *p
 }
 return nil
}

// Singletons returns a list of all registered Singletons.
func (c *Context) Singletons() []Singleton {
 var ret []Singleton
 for _, s := range c.singletonInfo {
  ret = append(ret, s.singleton)
 }
 return ret
}

// SingletonName returns the name that the given singleton was registered with.
func (c *Context) SingletonName(singleton Singleton) string {
 for _, s := range c.singletonInfo {
  if s.singleton == singleton {
   return s.name
  }
 }
 return ""
}

func (c *Context) singletonByName(name string) *singletonInfo {
 for _, s := range c.singletonInfo {
  if s.name == name {
   return s
  }
 }
 return nil
}

// Checks that the hashes of all the providers match the hashes from when they were first set.
// Does nothing on success, returns a list of errors otherwise. It's recommended to run this
// in a goroutine.
func (c *Context) VerifyProvidersWereUnchanged() []error {
 if !c.buildActionsReady {
  return []error{ErrBuildActionsNotReady}
 }

 errors := parallelVisitSimple(c.iterateAllVariants(), 1000, func(m *moduleInfo, _ int) []error {
  var errors []error
  for i, provider := range m.providers {
   if provider != nil {
    hash, err := proptools.CalculateHashReflection(provider)
    if err != nil {
     errors = append(errors, fmt.Errorf("provider %q on module %q was modified after being set, and no longer hashable afterwards: %s", providerRegistry[i].typ, m.Name(), err.Error()))
     continue
    }
    if m.providerInitialValueHashes[i] != hash {
     errors = append(errors, fmt.Errorf("provider %q on module %q was modified after being set", providerRegistry[i].typ, m.Name()))
    }
   } else if m.providerInitialValueHashes[i] != proptools.ZeroHash && !m.hasUnrestoredProvider[i] {
    // This should be unreachable, because in setProvider we check if the provider has already been set.
    errors = append(errors, fmt.Errorf("provider %q on module %q was unset somehow, this is an internal error", providerRegistry[i].typ, m.Name()))
   }
  }
  return errors
 })

 return errors
}

// WriteBuildFile writes the Ninja manifest text for the generated build
// actions to w.  If this is called before PrepareBuildActions successfully
// completes then ErrBuildActionsNotReady is returned.
func (c *Context) WriteBuildFile(w StringWriterWriter, shardNinja bool, ninjaFileName string) error {
 var err error
 pprof.Do(c.Context, pprof.Labels("blueprint", "WriteBuildFile"), func(ctx context.Context) {
  if !c.buildActionsReady {
   err = ErrBuildActionsNotReady
   return
  }

  nw := newNinjaWriter(w)

  if err = c.writeBuildFileHeader(nw); err != nil {
   return
  }

  if err = c.writeNinjaRequiredVersion(nw); err != nil {
   return
  }

  if err = c.writeSubninjas(nw, c.subninjas); err != nil {
   return
  }

  // TODO: Group the globals by package.

  if err = c.writeGlobalVariables(nw); err != nil {
   return
  }

  if err = c.writeGlobalPools(nw); err != nil {
   return
  }

  if err = c.writeBuildDir(nw); err != nil {
   return
  }

  if err = c.writeGlobalRules(nw); err != nil {
   return
  }

  if err = c.writeAllModuleActions(nw, shardNinja, ninjaFileName); err != nil {
   return
  }

  if err = c.writeAllSingletonActions(nw); err != nil {
   return
  }
 })

 return err
}

type pkgAssociation struct {
 PkgName string
 PkgPath string
}

type pkgAssociationSorter struct {
 pkgs []pkgAssociation
}

func (s *pkgAssociationSorter) Len() int {
 return len(s.pkgs)
}

func (s *pkgAssociationSorter) Less(i, j int) bool {
 iName := s.pkgs[i].PkgName
 jName := s.pkgs[j].PkgName
 return iName < jName
}

func (s *pkgAssociationSorter) Swap(i, j int) {
 s.pkgs[i], s.pkgs[j] = s.pkgs[j], s.pkgs[i]
}

func (c *Context) writeBuildFileHeader(nw *ninjaWriter) error {
 var pkgs []pkgAssociation
 maxNameLen := 0
 for pkg, name := range c.nameTracker.pkgNames {
  pkgs = append(pkgs, pkgAssociation{
   PkgName: name,
   PkgPath: pkg.pkgPath,
  })
  if len(name) > maxNameLen {
   maxNameLen = len(name)
  }
 }

 for i := range pkgs {
  pkgs[i].PkgName += strings.Repeat(" ", maxNameLen-len(pkgs[i].PkgName))
 }

 sort.Sort(&pkgAssociationSorter{pkgs})

 params := map[string]interface{}{
  "Pkgs": pkgs,
 }

 buf := bytes.NewBuffer(nil)
 err := fileHeaderTemplate.Execute(buf, params)
 if err != nil {
  return err
 }

 return nw.Comment(buf.String())
}

func (c *Context) writeNinjaRequiredVersion(nw *ninjaWriter) error {
 value := fmt.Sprintf("%d.%d.%d", c.requiredNinjaMajor, c.requiredNinjaMinor,
  c.requiredNinjaMicro)

 err := nw.Assign("ninja_required_version", value)
 if err != nil {
  return err
 }

 return nw.BlankLine()
}

func (c *Context) writeSubninjas(nw *ninjaWriter, subninjas []string) error {
 for _, subninja := range subninjas {
  err := nw.Subninja(subninja)
  if err != nil {
   return err
  }
 }
 return nw.BlankLine()
}

func (c *Context) writeBuildDir(nw *ninjaWriter) error {
 if c.outDir != nil {
  err := nw.Assign("builddir", c.outDir.Value(c.nameTracker))
  if err != nil {
   return err
  }

  err = nw.BlankLine()
  if err != nil {
   return err
  }
 }
 return nil
}

func (c *Context) writeGlobalVariables(nw *ninjaWriter) error {
 visited := make(map[Variable]bool)

 var walk func(v Variable) error
 walk = func(v Variable) error {
  visited[v] = true

  // First visit variables on which this variable depends.
  value := c.globalVariables[v]
  for _, dep := range value.Variables() {
   if !visited[dep] {
    err := walk(dep)
    if err != nil {
     return err
    }
   }
  }

  err := nw.Assign(c.nameTracker.Variable(v), value.Value(c.nameTracker))
  if err != nil {
   return err
  }

  err = nw.BlankLine()
  if err != nil {
   return err
  }

  return nil
 }

 globalVariables := make([]Variable, 0, len(c.globalVariables))
 for variable := range c.globalVariables {
  globalVariables = append(globalVariables, variable)
 }

 slices.SortFunc(globalVariables, func(a, b Variable) int {
  return cmp.Compare(c.nameTracker.Variable(a), c.nameTracker.Variable(b))
 })

 for _, v := range globalVariables {
  if !visited[v] {
   err := walk(v)
   if err != nil {
    return nil
   }
  }
 }

 return nil
}

func (c *Context) writeGlobalPools(nw *ninjaWriter) error {
 globalPools := make([]Pool, 0, len(c.globalPools))
 for pool := range c.globalPools {
  globalPools = append(globalPools, pool)
 }

 slices.SortFunc(globalPools, func(a, b Pool) int {
  return cmp.Compare(c.nameTracker.Pool(a), c.nameTracker.Pool(b))
 })

 for _, pool := range globalPools {
  name := c.nameTracker.Pool(pool)
  def := c.globalPools[pool]
  err := def.WriteTo(nw, name)
  if err != nil {
   return err
  }

  err = nw.BlankLine()
  if err != nil {
   return err
  }
 }

 return nil
}

func (c *Context) writeGlobalRules(nw *ninjaWriter) error {
 globalRules := make([]Rule, 0, len(c.globalRules))
 for rule := range c.globalRules {
  globalRules = append(globalRules, rule)
 }

 slices.SortFunc(globalRules, func(a, b Rule) int {
  return cmp.Compare(c.nameTracker.Rule(a), c.nameTracker.Rule(b))
 })

 for _, rule := range globalRules {
  name := c.nameTracker.Rule(rule)
  def := c.globalRules[rule]
  err := def.WriteTo(nw, name, c.nameTracker)
  if err != nil {
   return err
  }

  err = nw.BlankLine()
  if err != nil {
   return err
  }
 }

 return nil
}

type depSorter struct {
 deps          []depInfo
 nameInterface NameInterface
}

func (s depSorter) Len() int {
 return len(s.deps)
}

func (s depSorter) Less(i, j int) bool {
 return moduleLess(s.deps[i].module, s.deps[j].module, s.nameInterface)
}

func (s depSorter) Swap(i, j int) {
 s.deps[i], s.deps[j] = s.deps[j], s.deps[i]
}

func moduleLess(iMod, jMod *moduleInfo, nameInterface NameInterface) bool {
 iName := nameInterface.UniqueName(newNamespaceContext(iMod), iMod.group.name)
 jName := nameInterface.UniqueName(newNamespaceContext(jMod), jMod.group.name)
 if iName == jName {
  iVariantName := iMod.variant.name
  jVariantName := jMod.variant.name
  if iVariantName == jVariantName {
   panic(fmt.Sprintf("duplicate module name: %s %s: %#v and %#v\n",
    iName, iVariantName, iMod.variant.variations, jMod.variant.variations))
  } else {
   return iVariantName < jVariantName
  }
 } else {
  return iName < jName
 }
}

func GetNinjaShardFiles(ninjaFile string) []string {
 suffix := ".ninja"
 if !strings.HasSuffix(ninjaFile, suffix) {
  panic(fmt.Errorf("ninja file name in wrong format : %s", ninjaFile))
 }
 base := strings.TrimSuffix(ninjaFile, suffix)
 ninjaShardCnt := 10
 fileNames := make([]string, ninjaShardCnt)

 for i := 0; i < ninjaShardCnt; i++ {
  fileNames[i] = fmt.Sprintf("%s.%d%s", base, i, suffix)
 }
 return fileNames
}

func (c *Context) writeAllModuleActions(nw *ninjaWriter, shardNinja bool, ninjaFileName string) error {
 c.BeginEvent("modules")
 defer c.EndEvent("modules")

 var modules []*moduleInfo

 for module := range c.iterateAllVariants() {
  modules = append(modules, module)
 }

 // cachedNameModuleLess is similar to moduleLess, but uses the namespaced name cached in
 // moduleInfo.uniqueName instead of recomputing it each time.
 cachedNameModuleLess := func(a, b *moduleInfo) int {
  if a.cachedUniqueName == b.cachedUniqueName {
   if a.variant.name == b.variant.name {
    panic(fmt.Sprintf("duplicate module name: %s %s: %#v and %#v\n",
     a.cachedUniqueName, a.variant.name, a.variant.variations, a.variant.variations))
   } else {
    return cmp.Compare(a.variant.name, b.variant.name)
   }
  } else {
   return cmp.Compare(a.cachedUniqueName, b.cachedUniqueName)
  }
 }

 slices.SortFunc(modules, cachedNameModuleLess)

 phonys := c.deduplicateOrderOnlyDeps(modules)

 c.EventHandler.Do("sort_phony_builddefs", func() {
  // sorting for determinism, the phony output names are stable
  sort.Slice(phonys.buildDefs, func(i int, j int) bool {
   return phonys.buildDefs[i].OutputStrings[0] < phonys.buildDefs[j].OutputStrings[0]
  })
 })

 if err := c.writeLocalBuildActions(nw, phonys); err != nil {
  return err
 }

 if shardNinja {
  var wg sync.WaitGroup
  errorCh := make(chan error)
  files := GetNinjaShardFiles(ninjaFileName)
  shardedModules := proptools.ShardByCount(modules, len(files))
  for i, batchModules := range shardedModules {
   file := files[i]
   wg.Add(1)
   go func(file string, batchModules []*moduleInfo) {
    defer wg.Done()
    f, err := pathtools.OpenWithTruncateOnClose(c.fs, JoinPath(c.SrcDir(), file))
    if err != nil {
     errorCh <- fmt.Errorf("error opening Ninja file shard: %s", err)
     return
    }
    defer func() {
     err := f.Close()
     if err != nil {
      errorCh <- err
     }
    }()
    buf := bufio.NewWriterSize(f, 16*1024*1024)
    defer func() {
     err := buf.Flush()
     if err != nil {
      errorCh <- err
     }
    }()
    writer := newNinjaWriter(buf)
    err = c.writeIncrementalModules(batchModules, writer)
    if err != nil {
     errorCh <- err
    }
   }(file, batchModules)
   nw.Subninja(file)
  }

  if c.GetIncrementalEnabled() {
   wg.Add(1)
   go func() {
    defer wg.Done()
    parallelVisitSimple(slices.Values(modules), parallelVisitLimit,
     func(m *moduleInfo, _ int) []error {
      if !m.incrementalRestored {
       m.cacheModuleBuildActions(c.EncContext, c.keyValueStoreCache)
      }
      return nil
     })
   }()
  }

  go func() {
   wg.Wait()
   close(errorCh)
  }()

  if c.incrementalDebugFile != "" {
   c.WriteIncrementalDebugInfo(c.incrementalDebugFile, modules)
  }

  var errors []error
  for newErrors := range errorCh {
   errors = append(errors, newErrors)
  }
  if len(errors) > 0 {
   return proptools.MergeErrors(errors)
  }
  return nil
 } else {
  return c.writeModuleAction(modules, nw)
 }
}

// A simplified version of parallelVisit where multiple calls to it can be run at the same time.
func parallelVisitSimple(moduleIter iter.Seq[*moduleInfo], limit int, visit func(module *moduleInfo, idx int) []error) []error {
 type moduleToProcess struct {
  module *moduleInfo
  index  int
 }
 toProcess := make(chan moduleToProcess)
 errorCh := make(chan []error)
 var wg sync.WaitGroup
 go func() {
  idx := 0
  for m := range moduleIter {
   toProcess <- moduleToProcess{m, idx}
   idx++
  }
  close(toProcess)
 }()
 for i := 0; i < limit; i++ {
  wg.Add(1)
  go func() {
   var errors []error
   for m := range toProcess {
    errors = append(errors, visit(m.module, m.index)...)
   }
   if len(errors) > 0 {
    errorCh <- errors
   }
   wg.Done()
  }()
 }
 go func() {
  wg.Wait()
  close(errorCh)
 }()

 var errors []error
 for newErrors := range errorCh {
  errors = append(errors, newErrors...)
 }
 return errors
}
func (c *Context) writeIncrementalModules(modules []*moduleInfo, baseWriter *ninjaWriter) error {
 // Use a sync.Pool to reuse buffers and reduce memory allocations.
 bufferPool := sync.Pool{
  New: func() any {
   // Pre-allocate a reasonably sized buffer for each new writer.
   return bytes.NewBuffer(make([]byte, 0128*1024))
  },
 }
 headerBufPool := sync.Pool{New: func() any { return new(bytes.Buffer) }}

 writeSignals := make([]chan struct{}, len(modules))
 for i := range writeSignals {
  writeSignals[i] = make(chan struct{})
 }

 errs := parallelVisitSimple(slices.Values(modules), parallelVisitLimit, func(m *moduleInfo, idx int) []error {
  var moduleBytes []byte
  var err error

  if m.incrementalRestored && !c.incrementalProviderTest {
   // Read from the cache if the module is restored.
   moduleBytes, err = c.keyValueStoreCache.readNinjaStatements(m.buildActionCacheKey)
  } else {
   // Generate statements for dirty modules.
   inMemoryWriter := bufferPool.Get().(*bytes.Buffer)
   inMemoryWriter.Reset()
   defer bufferPool.Put(inMemoryWriter)

   headerBuf := headerBufPool.Get().(*bytes.Buffer)
   headerBuf.Reset()
   defer headerBufPool.Put(headerBuf)

   mWriter := newNinjaWriter(inMemoryWriter)
   err = c.writeOneModuleAction(m, mWriter, headerBuf)
   if err == nil {
    // Make a copy of the bytes, as the buffer will be reused.
    moduleBytes = slices.Clone(inMemoryWriter.Bytes())
    // Write the newly generated statements back to the cache for incremental module.
    if m.buildActionCacheKey != nil {
     err = c.keyValueStoreCache.writeNinjaStatements(m.buildActionCacheKey, moduleBytes)
    }
   }
  }

  // Wait for the signal from the previous thread before writing even when an
  // error happens to ensure no concurrently writing of the ninja file.
  if idx > 0 {
   <-writeSignals[idx-1]
  }

  if err == nil {
   baseWriter.writer.Write(moduleBytes)
  }

  // Signal the next thread that it's clear to write.
  close(writeSignals[idx])
  if err != nil {
   return []error{err}
  }
  return nil
 })

 if len(errs) > 0 {
  return errors.Join(errs...)
 }
 return nil
}

func (c *Context) writeModuleAction(modules []*moduleInfo, nw *ninjaWriter) error {
 buf := bytes.NewBuffer(nil)
 for _, module := range modules {
  if err := c.writeOneModuleAction(module, nw, buf); err != nil {
   return err
  }
 }
 return nil
}

func (c *Context) writeOneModuleAction(module *moduleInfo, nw *ninjaWriter, buf *bytes.Buffer) error {
 if len(module.actionDefs.variables)+len(module.actionDefs.rules)+len(module.actionDefs.buildDefs) == 0 {
  return nil
 }
 buf.Reset()

 // In order to make the bootstrap build manifest independent of the
 // build dir we need to output the Blueprints file locations in the
 // comments as paths relative to the source directory.
 relPos := module.pos
 relPos.Filename = module.relBlueprintsFile

 // Get the name and location of the factory function for the module.
 factoryFunc := runtime.FuncForPC(reflect.ValueOf(module.factory).Pointer())
 factoryName := factoryFunc.Name()

 infoMap := map[string]interface{}{
  "name":      module.Name(),
  "typeName":  module.typeName,
  "goFactory": factoryName,
  "pos":       relPos,
  "variant":   module.variant.name,
 }
 if err := moduleHeaderTemplate.Execute(buf, infoMap); err != nil {
  return err
 }

 if err := nw.Comment(buf.String()); err != nil {
  return err
 }

 if err := nw.BlankLine(); err != nil {
  return err
 }

 if err := c.writeLocalBuildActions(nw, &module.actionDefs); err != nil {
  return err
 }

 if err := nw.BlankLine(); err != nil {
  return err
 }

 return nil
}

func (c *Context) writeAllSingletonActions(nw *ninjaWriter) error {
 c.BeginEvent("singletons")
 defer c.EndEvent("singletons")

 buf := bytes.NewBuffer(nil)
 var ninjaBytes []byte
 var err error
 inMemoryWriter := bytes.NewBuffer(nil)

 for _, info := range c.singletonInfo {
  if info.incrementalRestored && !c.incrementalProviderTest {
   // Read from the cache if the singleton is restored.
   ninjaBytes, err = c.keyValueStoreCache.readNinjaStatements(info.buildActionCacheKey)
   if err != nil {
    return err
   }
  } else {
   if len(info.actionDefs.variables)+len(info.actionDefs.rules)+len(info.actionDefs.buildDefs)+len(info.subninjas) == 0 {
    continue
   }

   inMemoryWriter.Reset()
   sWriter := newNinjaWriter(inMemoryWriter)
   // Get the name of the factory function for the module.
   factory := info.factory
   factoryFunc := runtime.FuncForPC(reflect.ValueOf(factory).Pointer())
   factoryName := factoryFunc.Name()

   buf.Reset()
   infoMap := map[string]interface{}{
    "name":      info.name,
    "goFactory": factoryName,
   }
   err = singletonHeaderTemplate.Execute(buf, infoMap)
   if err != nil {
    return err
   }

   err = sWriter.Comment(buf.String())
   if err != nil {
    return err
   }

   err = sWriter.BlankLine()
   if err != nil {
    return err
   }

   err = c.writeLocalBuildActions(sWriter, &info.actionDefs)
   if err != nil {
    return err
   }

   err = c.writeSubninjas(sWriter, info.subninjas)

   err = sWriter.BlankLine()
   if err != nil {
    return err
   }
   ninjaBytes = inMemoryWriter.Bytes()

   if info.incrementalSupported {
    c.keyValueStoreCache.writeNinjaStatements(info.buildActionCacheKey, ninjaBytes)
   }
  }
  nw.writer.Write(ninjaBytes)
 }

 return nil
}

func (c *Context) GetEventHandler() *metrics.EventHandler {
 return c.EventHandler
}

func (c *Context) BeginEvent(name string) {
 c.EventHandler.Begin(name)
}

func (c *Context) EndEvent(name string) {
 c.EventHandler.End(name)
}

func (c *Context) SetBeforePrepareBuildActionsHook(hookFn func() error) {
 c.BeforePrepareBuildActionsHook = hookFn
}

func (c *Context) RestoredGlobMetrics() pathtools.RestoredGlobsMetrics {
 return c.restoredGlobMetrics
}

// keyForPhonyCandidate gives a unique identifier for a set of deps.
func keyForPhonyCandidate(stringDeps []string) uint64 {
 hasher := fnv.New64a()
 write := func(s string) {
  // The hasher doesn't retain or modify the input slice, so pass the string data directly to avoid
  // an extra allocation and copy.
  _, err := hasher.Write(unsafe.Slice(unsafe.StringData(s), len(s)))
  if err != nil {
   panic(fmt.Errorf("write failed: %w", err))
  }
 }
 for _, d := range stringDeps {
  write(d)
 }
 return hasher.Sum64()
}

// deduplicateOrderOnlyDeps searches for common sets of order-only dependencies across all
// buildDef instances in the provided moduleInfo instances. Each such
// common set forms a new buildDef representing a phony output that then becomes
// the sole order-only dependency of those buildDef instances
func (c *Context) deduplicateOrderOnlyDeps(modules []*moduleInfo) *localBuildActions {
 c.BeginEvent("deduplicate_order_only_deps")
 defer c.EndEvent("deduplicate_order_only_deps")

 var phonys []*buildDef
 if c.orderOnlyStringsCache == nil {
  c.orderOnlyStringsCache = make(OrderOnlyStringsCache)
 }
 c.orderOnlyStrings.Range(func(key uniquelist.UniqueList[string], info *orderOnlyStringsInfo) bool {
  if info.dedup {
   dedup := fmt.Sprintf("dedup-%x", keyForPhonyCandidate(key.ToSlice()))
   phony := &buildDef{
    Rule:          Phony,
    OutputStrings: []string{dedup},
    InputStrings:  key.ToSlice(),
   }
   info.dedupName = dedup
   phonys = append(phonys, phony)
   if info.incremental {
    c.orderOnlyStringsCache[phony.OutputStrings[0]] = phony.InputStrings
   }
  }
  return true
 })

 parallelVisit(slices.Values(modules), unorderedVisitorImpl{}, parallelVisitLimit,
  func(m *moduleInfo, pause pauseFunc) bool {
   for _, def := range m.actionDefs.buildDefs {
    if info, loaded := c.orderOnlyStrings.Load(def.OrderOnlyStrings); loaded {
     if info.dedup {
      def.OrderOnlyStrings = uniquelist.Make([]string{info.dedupName})
      m.orderOnlyStrings = append(m.orderOnlyStrings, info.dedupName)
     }
    }
   }
   return false
  })

 return &localBuildActions{buildDefs: phonys}
}

func (c *Context) writeLocalBuildActions(nw *ninjaWriter,
 defs *localBuildActions) error {

 // Write the local variable assignments.
 for _, v := range defs.variables {
  // A localVariable doesn't need the package names or config to
  // determine its name or value.
  name := v.fullName(nil)
  value, err := v.value(nil, nil)
  if err != nil {
   panic(err)
  }
  err = nw.Assign(name, value.Value(c.nameTracker))
  if err != nil {
   return err
  }
 }

 if len(defs.variables) > 0 {
  err := nw.BlankLine()
  if err != nil {
   return err
  }
 }

 // Write the local rules.
 for _, r := range defs.rules {
  // A localRule doesn't need the package names or config to determine
  // its name or definition.
  name := r.fullName(nil)
  def, err := r.def(nil)
  if err != nil {
   panic(err)
  }

  err = def.WriteTo(nw, name, c.nameTracker)
  if err != nil {
   return err
  }

  err = nw.BlankLine()
  if err != nil {
   return err
  }
 }

 // Write the build definitions.
 for _, buildDef := range defs.buildDefs {
  err := buildDef.WriteTo(nw, c.nameTracker)
  if err != nil {
   return err
  }

  if len(buildDef.Args) > 0 {
   err = nw.BlankLine()
   if err != nil {
    return err
   }
  }
 }

 return nil
}

func beforeInModuleList(a, b *moduleInfo, list moduleList) bool {
 found := false
 if a == b {
  return false
 }
 for _, l := range list {
  if l == a {
   found = true
  } else if l == b {
   return found
  }
 }

 missing := a
 if found {
  missing = b
 }
 panic(fmt.Errorf("element %v not found in list %v", missing, list))
}

type panicError struct {
 panic interface{}
 stack []byte
 in    string
}

func newPanicErrorf(panic interface{}, in string, a ...interface{}) error {
 buf := make([]byte, 4096)
 count := runtime.Stack(buf, false)
 return panicError{
  panic: panic,
  in:    fmt.Sprintf(in, a...),
  stack: buf[:count],
 }
}

func (p panicError) Error() string {
 return fmt.Sprintf("panic in %s\n%s\n%s\n", p.in, p.panic, p.stack)
}

func (p *panicError) addIn(in string) {
 p.in += " in " + in
}

func funcName(f interface{}) string {
 return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
}

// json representation of a dependency
type depJson struct {
 Name    string      `json:"name"`
 Variant string      `json:"variant"`
 TagType string      `json:"tag_type"`
 TagData interface{} `json:"tag_data"`
}

// json representation of a provider
type providerJson struct {
 Type   string      `json:"type"`
 Debug  string      `json:"debug"` // from GetDebugString on the provider data
 Fields interface{} `json:"fields"`
}

// interface for getting debug info from various data.
// TODO: Consider having this return a json object instead
type Debuggable interface {
 GetDebugString() string
}

// Convert a slice in a reflect.Value to a value suitable for outputting to json
func debugSlice(evaluator proptools.ConfigurableEvaluator, value reflect.Value) interface{} {
 size := value.Len()
 if size == 0 {
  return nil
 }
 result := make([]interface{}, size)
 for i := 0; i < size; i++ {
  result[i] = debugValue(evaluator, value.Index(i))
 }
 return result
}

// Convert a map in a reflect.Value to a value suitable for outputting to json
func debugMap(evaluator proptools.ConfigurableEvaluator, value reflect.Value) interface{} {
 if value.IsNil() {
  return nil
 }
 result := make(map[string]interface{})
 iter := value.MapRange()
 for iter.Next() {
  // In the (hopefully) rare case of a key collision (which will happen when multiple
  // go-typed keys have the same string representation, we'll just overwrite the last
  // value.
  result[debugKey(iter.Key())] = debugValue(evaluator, iter.Value())
 }
 return result
}

// Convert a value into a string, suitable for being a json map key.
func debugKey(value reflect.Value) string {
 return fmt.Sprintf("%v", value)
}

// Convert a single value (possibly a map or slice too) in a reflect.Value to a value suitable for outputting to json
func debugValue(evaluator proptools.ConfigurableEvaluator, value reflect.Value) interface{} {
 if proptools.IsConfigurable(value.Type()) {
  if evaluator == nil {
   return "<configurable value>"
  } else {
   if value.Kind() == reflect.Interface {
    value = value.Elem() // Get the underlying value in the interface.
   }
   if value.Kind() != reflect.Ptr {
    value = value.Addr() // The method needs a pointer receiver.
   }
   // value is now *proptools.Configurable[<something>]
   value = value.MethodByName("Get").Call([]reflect.Value{reflect.ValueOf(evaluator)})[0]
   // value is now an unaddressable proptools.ConfigurableOptional[<something>]
   ptrVal := reflect.New(value.Type())
   ptrVal.Elem().Set(value)
   // ptrVal is now *proptools.ConfigurableOptional[<something>]
   if ptrVal.MethodByName("IsEmpty").Call(nil)[0].Bool() {
    return nil
   }
   value = ptrVal.MethodByName("Get").Call(nil)[0]
  }
 }

 // Remember if we originally received a reflect.Interface.
 wasInterface := value.Kind() == reflect.Interface
 // Dereference pointers down to the real type
 for value.Kind() == reflect.Ptr || value.Kind() == reflect.Interface {
  // If it's nil, return nil
  if value.IsNil() {
   return nil
  }
  value = value.Elem()
 }

 switch kind := value.Kind(); kind {
 case reflect.Bool:
  return value.Bool()
 case reflect.String:
  return value.String()
 case reflect.Int:
  return value.Int()
 case reflect.Uint:
  return value.Uint()
 case reflect.Slice:
  return debugSlice(evaluator, value)
 case reflect.Struct:
  // At least some of the private struct fields cause stack overflow here.  Do not include them until
  // we track the recursion down.
  if !value.CanInterface() {
   return nil
  }
  // If we originally received an interface, and there is a String() method, call that.
  // TODO: figure out why Path doesn't work correctly otherwise (in aconfigPropagatingDeclarationsInfo)
  if s, ok := value.Interface().(interface{ String() string }); wasInterface && ok {
   return s.String()
  }
  return debugStruct(evaluator, value)
 case reflect.Map:
  return debugMap(evaluator, value)
 default:
  // TODO: add cases as we find them.
  return fmt.Sprintf("debugValue(Kind=%v, wasInterface=%v)", kind, wasInterface)
 }

 return nil
}

// Convert an object in a reflect.Value to a value suitable for outputting to json
func debugStruct(evaluator proptools.ConfigurableEvaluator, value reflect.Value) interface{} {
 result := make(map[string]interface{})
 debugStructAppend(evaluator, value, &result)
 if len(result) == 0 {
  return nil
 }
 return result
}

// Convert an object to a value suiable for outputting to json
func debugStructAppend(evaluator proptools.ConfigurableEvaluator, value reflect.Value, result *map[string]interface{}) {
 for value.Kind() == reflect.Ptr {
  if value.IsNil() {
   return
  }
  value = value.Elem()
 }
 if value.IsZero() {
  return
 }

 if value.Kind() != reflect.Struct {
  // TODO: could maybe support other types
  return
 }

 structType := value.Type()
 for i := 0; i < value.NumField(); i++ {
  v := debugValue(evaluator, value.Field(i))
  if v != nil {
   (*result)[structType.Field(i).Name] = v
  }
 }
}

func debugPropertyStruct(evaluator proptools.ConfigurableEvaluator, props interface{}, result *map[string]interface{}) {
 if props == nil {
  return
 }
 debugStructAppend(evaluator, reflect.ValueOf(props), result)
}

// Get the debug json for a single module. Returns thae data as
// flattened json text for easy concatenation by GenerateModuleDebugInfo.
func getModuleDebugJson(evaluator proptools.ConfigurableEvaluator, module *moduleInfo) []byte {
 info := struct {
  Name       string                 `json:"name"`
  SourceFile string                 `json:"source_file"`
  SourceLine int                    `json:"source_line"`
  Type       string                 `json:"type"`
  Variant    string                 `json:"variant"`
  Deps       []depJson              `json:"deps"`
  Providers  []providerJson         `json:"providers"`
  Debug      string                 `json:"debug"` // from GetDebugString on the module
  Properties map[string]interface{} `json:"properties"`
 }{
  Name:       module.Name(),
  SourceFile: module.pos.Filename,
  SourceLine: module.pos.Line,
  Type:       module.typeName,
  Variant:    module.variant.name,
  Deps: func() []depJson {
   result := make([]depJson, len(module.directDeps))
   for i, dep := range module.directDeps {
    result[i] = depJson{
     Name:    dep.module.Name(),
     Variant: dep.module.variant.name,
    }
    t := reflect.TypeOf(dep.tag)
    if t != nil {
     result[i].TagType = t.PkgPath() + "." + t.Name()
     result[i].TagData = debugStruct(nil, reflect.ValueOf(dep.tag))
    }
   }
   return result
  }(),
  Providers: func() []providerJson {
   result := make([]providerJson, 0, len(module.providers))
   for _, p := range module.providers {
    pj := providerJson{}
    include := false

    t := reflect.TypeOf(p)
    if t != nil {
     pj.Type = t.PkgPath() + "." + t.Name()
     include = true
    }

    if dbg, ok := p.(Debuggable); ok {
     pj.Debug = dbg.GetDebugString()
     if pj.Debug != "" {
      include = true
     }
    }

    if p != nil {
     pj.Fields = debugValue(nil, reflect.ValueOf(p))
     include = true
    }

    if include {
     result = append(result, pj)
    }
   }
   return result
  }(),
  Debug: func() string {
   if dbg, ok := module.logicModule.(Debuggable); ok {
    return dbg.GetDebugString()
   } else {
    return ""
   }
  }(),
  Properties: func() map[string]interface{} {
   result := make(map[string]interface{})
   for _, props := range module.properties {
    debugPropertyStruct(evaluator, props, &result)
   }
   return result
  }(),
 }
 buf, _ := json.Marshal(info)
 return buf
}

// InitializeModuleDebugInfoCollection sets up a channel and a receiver to write
// out/soong/soong-debug-info.json. Called if GENERATE_SOONG_DEBUG=true. Returns a function to be
// deferred until all modules have been processed.
func (this *Context) InitializeModuleDebugInfoCollection(filename string) func() {
 err := os.MkdirAll(filepath.Dir(filename), 0777)
 if err != nil {
  // We expect this to be writable
  panic(fmt.Sprintf("couldn't create directory for soong module debug file %s: %s", filepath.Dir(filename), err))
 }

 f, err := os.Create(filename)
 if err != nil {
  // We expect this to be writable
  panic(fmt.Sprintf("couldn't create soong module debug file %s: %s", filename, err))
 }

 this.moduleDebugDataChannel = make(chan []byte, 10)
 var wg sync.WaitGroup
 wg.Add(1)

 go func() {
  defer f.Close()
  defer wg.Done()

  needComma := false
  f.WriteString("{\n\"modules\": [\n")

  for moduleData := range this.moduleDebugDataChannel {
   if needComma {
    f.WriteString(",\n")
   } else {
    needComma = true
   }
   f.Write(moduleData)
  }

  f.WriteString("\n]\n}")
 }()

 return func() {
  close(this.moduleDebugDataChannel)
  wg.Wait()
 }
}

// getModule returns a module with the given name and variations, from the root namespace.
// It will return nil if the module doesn't exist.
func (c *Context) getModule(moduleName string, variant []Variation) *moduleInfo {
 possibleDeps := c.moduleGroupFromName(
  moduleName,
  // Search in the root namespace, but soong's nameInterface implementation will allow
  // using the //path:module syntax to specify other namespaces.
  c.nameInterface.GetNamespace(newNamespaceContextFromFilename(".")),
 )
 if possibleDeps == nil {
  return nil
 }

 var vm variationMap
 for _, variation := range variant {
  vm.set(variation.Mutator, variation.Variation)
 }
 for _, module := range possibleDeps.modules {
  if module.variant.variations.equal(vm) {
   return module
  }
 }
 return nil
}

// RecordSandboxMetrics visits all the rules and records their sandboxing state in the metrics.
func (c *Context) RecordSandboxMetrics(config any) {
 sbConfig := config.(sandboxConfig)
 metrics := sbConfig.ActionSandboxMetrics()

 if !sbConfig.IsActionSandboxedBuild() || metrics == nil {
  return
 }
 // We would get incorrect results if we recorded these metrics on an incremental analysis,
 // as we wouldn't process all rules
 if c.GetIncrementalAnalysis() {
  return
 }

 for rule, def := range c.globalRules {
  name := c.nameTracker.Rule(rule)
  metrics.updateSandboxMetrics(name, def.Source, def.SandboxDisabled)
 }

 for module := range c.iterateAllVariants() {
  for _, r := range module.actionDefs.rules {
   name := r.fullName(nil)
   metrics.updateSandboxMetrics(name, r.def_.Source, r.def_.SandboxDisabled)
  }
 }

 for _, info := range c.singletonInfo {
  for _, r := range info.actionDefs.rules {
   name := r.fullName(nil)
   metrics.updateSandboxMetrics(name, r.def_.Source, r.def_.SandboxDisabled)
  }
 }
}

var fileHeaderTemplate = template.Must(template.New("fileHeader").Parse(
 `******************************************************************************
***            This file is generated and should not be edited             ***
******************************************************************************
{{if .Pkgs}}
This file contains variables, rules, and pools with name prefixes indicating
they were generated by the following Go packages:
{{range .Pkgs}}
    {{.PkgName}} [from Go package {{.PkgPath}}]{{end}}{{end}}

`))

var moduleHeaderTemplate = template.Must(template.New("moduleHeader").Parse(
 `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
Module:  {{.name}}
Variant: {{.variant}}
Type:    {{.typeName}}
Factory: {{.goFactory}}
Defined: {{.pos}}
`))

var singletonHeaderTemplate = template.Must(template.New("singletonHeader").Parse(
 `# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
Singleton: {{.name}}
Factory:   {{.goFactory}}
`))

func JoinPath(base, path string) string {
 if filepath.IsAbs(path) {
  return path
 }
 return filepath.Join(base, path)
}

[0.154QuellennavigatorsProjekt 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik