Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  module_ctx.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 (
 "cmp"
 "errors"
 "fmt"
 "path/filepath"
 "slices"
 "strings"
 "text/scanner"

 "github.com/google/blueprint/parser"
 "github.com/google/blueprint/pathtools"
 "github.com/google/blueprint/proptools"
)

// A Module handles generating all of the Ninja build actions needed to build a
// single module based on properties defined in a Blueprints file.  Module
// objects are initially created during the parse phase of a Context using one
// of the registered module types (and the associated ModuleFactory function).
// The Module's properties struct is automatically filled in with the property
// values specified in the Blueprints file (see Context.RegisterModuleType for more
// information on this).
//
// A Module can be split into multiple Modules by a Mutator.  All existing
// properties set on the module will be duplicated to the new Module, and then
// modified as necessary by the Mutator.
//
// The Module implementation can access the build configuration as well as any
// modules on which it depends (as defined by the "deps" property
// specified in the Blueprints file, dynamically added by implementing the
// (deprecated) DynamicDependerModule interface, or dynamically added by a
// BottomUpMutator) using the ModuleContext passed to GenerateBuildActions.
// This ModuleContext is also used to create Ninja build actions and to report
// errors to the user.
//
// In addition to implementing the GenerateBuildActions method, a Module should
// implement methods that provide dependant modules and singletons information
// they need to generate their build actions.  These methods will only be called
// after GenerateBuildActions is called because the Context calls
// GenerateBuildActions in dependency-order (and singletons are invoked after
// all the Modules).  The set of methods a Module supports will determine how
// dependant Modules interact with it.
//
// For example, consider a Module that is responsible for generating a library
// that other modules can link against.  The library Module might implement the
// following interface:
//
// type LibraryProducer interface {
//     LibraryFileName() string
// }
//
// func IsLibraryProducer(module blueprint.Module) {
//     _, ok := module.(LibraryProducer)
//     return ok
// }
//
// A binary-producing Module that depends on the library Module could then do:
//
// func (m *myBinaryModule) GenerateBuildActions(ctx blueprint.ModuleContext) {
//     ...
//     var libraryFiles []string
//     ctx.VisitDepsDepthFirstIf(IsLibraryProducer,
//         func(module blueprint.Module) {
//             libProducer := module.(LibraryProducer)
//             libraryFiles = append(libraryFiles, libProducer.LibraryFileName())
//         })
//     ...
// }
//
// to build the list of library file names that should be included in its link
// command.
//
// GenerateBuildActions may be called from multiple threads.  It is guaranteed to
// be called after it has finished being called on all dependencies and on all
// variants of that appear earlier in the ModuleContext.VisitAllModuleVariants list.
// Any accesses to global variables or to Module objects that are not dependencies
// or variants of the current Module must be synchronized by the implementation of
// GenerateBuildActions.
type Module interface {
 // Name returns a string used to uniquely identify each module.  The return
 // value must be unique across all modules.  It is only called once, during
 // initial blueprint parsing.  To change the name later a mutator must call
 // MutatorContext.Rename
 //
 // In most cases, Name should return the contents of a "name:" property from
 // the blueprint file.  An embeddable SimpleName object can be used for this
 // case.
 Name() string

 // GenerateBuildActions is called by the Context that created the Module
 // during its generate phase.  This call should generate all Ninja build
 // actions (rules, pools, and build statements) needed to build the module.
 GenerateBuildActions(ModuleContext)

 String() string

 addLoadHook(hook LoadHookWithPriority)
 getAndClearloadHooks() []LoadHookWithPriority

 info() *moduleInfo
 setInfo(*moduleInfo)
}

type ModuleOrProxy interface {
 info() *moduleInfo
 Name() string
 String() string
}

var _ ModuleOrProxy = (Module)(nil)
var _ ModuleOrProxy = ModuleProxy{}

type ModuleBase struct {
 moduleInfo *moduleInfo
 loadHooks  []LoadHookWithPriority
}

func (m ModuleBase) info() *moduleInfo {
 return m.moduleInfo
}

func (m *ModuleBase) setInfo(moduleInfo *moduleInfo) {
 m.moduleInfo = moduleInfo
}

func (m *ModuleBase) addLoadHook(hook LoadHookWithPriority) {
 m.loadHooks = append(m.loadHooks, hook)
}

func (m *ModuleBase) getAndClearloadHooks() []LoadHookWithPriority {
 hooks := m.loadHooks
 m.loadHooks = nil
 return hooks
}

type ModuleProxy struct {
 moduleInfo *moduleInfo
}

func (m ModuleProxy) info() *moduleInfo {
 return m.moduleInfo
}

func CreateModuleProxy(module Module) ModuleProxy {
 return ModuleProxy{
  moduleInfo: module.info(),
 }
}

func (m ModuleProxy) IsNil() bool {
 return m.moduleInfo == nil
}

type IncrementalMetricsInfo struct {
 IncrementalRestored        bool
 HasUnrestoredProvider      []bool
 ProviderInitialValueHashes []proptools.Hash
 IncrementalSupported       bool
}

func (m ModuleProxy) IncrementalInfo() *IncrementalMetricsInfo {
 return &IncrementalMetricsInfo{
  IncrementalRestored:        m.moduleInfo.incrementalRestored,
  HasUnrestoredProvider:      m.moduleInfo.hasUnrestoredProvider,
  ProviderInitialValueHashes: m.moduleInfo.providerInitialValueHashes,
  IncrementalSupported:       true,
 }
}

func (m ModuleProxy) Name() string {
 if m.moduleInfo.logicModule == nil {
  return m.moduleInfo.cachedName
 }
 return m.moduleInfo.logicModule.Name()
}

func (m ModuleProxy) String() string {
 if m.moduleInfo.logicModule == nil {
  return m.moduleInfo.cachedString
 }
 return m.moduleInfo.logicModule.String()
}

// A DynamicDependerModule is a Module that may add dependencies that do not
// appear in its "deps" property.  Any Module that implements this interface
// will have its DynamicDependencies method called by the Context that created
// it during generate phase.
//
// Deprecated, use a BottomUpMutator instead
type DynamicDependerModule interface {
 Module

 // DynamicDependencies is called by the Context that created the
 // DynamicDependerModule during its generate phase.  This call should return
 // the list of module names that the DynamicDependerModule depends on
 // dynamically.  Module names that already appear in the "deps" property may
 // but do not need to be included in the returned list.
 DynamicDependencies(DynamicDependerModuleContext) []string
}

type EarlyModuleContext interface {
 // Module returns the current module as a Module.  It should rarely be necessary, as the module already has a
 // reference to itself.
 Module() Module

 // ModuleName returns the name of the module.  This is generally the value that was returned by Module.Name() when
 // the module was created, but may have been modified by calls to BottomUpMutatorContext.Rename.
 ModuleName() string

 // ModuleDir returns the path to the directory that contains the definition of the module.
 ModuleDir() string

 // ModuleType returns the name of the module type that was used to create the module, as specified in
 // Context.RegisterModuleType().
 ModuleType() string

 // ModuleTags returns the tags for this module that should be passed to
 // ninja for analysis. For example:
 // [
 //   "module_name": "libfoo",
 //   "module_type": "cc_library",
 // ]
 ModuleTags() map[string]string

 // BlueprintsFile returns the name of the blueprint file that contains the definition of this
 // module.
 BlueprintsFile() string

 // Config returns the config object that was passed to Context.PrepareBuildActions.
 Config() interface{}

 // ContainsProperty returns true if the specified property name was set in the module definition.
 ContainsProperty(name string) bool

 // Errorf reports an error at the specified position of the module definition file.
 Errorf(pos scanner.Position, fmt string, args ...interface{})

 // ModuleErrorf reports an error at the line number of the module type in the module definition.
 ModuleErrorf(fmt string, args ...interface{})

 // PropertyErrorf reports an error at the line number of a property in the module definition.
 PropertyErrorf(property, fmt string, args ...interface{})

 // OtherModulePropertyErrorf reports an error at the line number of a property in the given module definition.
 OtherModulePropertyErrorf(logicModule ModuleOrProxy, property string, format string, args ...interface{})

 // Failed returns true if any errors have been reported.  In most cases the module can continue with generating
 // build rules after an error, allowing it to report additional errors in a single run, but in cases where the error
 // has prevented the module from creating necessary data it can return early when Failed returns true.
 Failed() bool

 // GlobWithDeps returns a list of files and directories that match the
 // specified pattern but do not match any of the patterns in excludes.
 // Any directories will have a '/' suffix.  It also adds efficient
 // dependencies to rerun the primary builder whenever a file matching
 // the pattern as added or removed, without rerunning if a file that
 // does not match the pattern is added to a searched directory.
 GlobWithDeps(pattern string, excludes []string) ([]string, error)

 // Fs returns a pathtools.Filesystem that can be used to interact with files.  Using the Filesystem interface allows
 // the module to be used in build system tests that run against a mock filesystem.
 Fs() pathtools.FileSystem

 // AddNinjaFileDeps adds dependencies on the specified files to the rule that creates the ninja manifest.  The
 // primary builder will be rerun whenever the specified files are modified.
 AddNinjaFileDeps(deps ...string)

 moduleInfo() *moduleInfo

 error(err error)

 // Namespace returns the Namespace object provided by the NameInterface set by Context.SetNameInterface, or the
 // default SimpleNameInterface if Context.SetNameInterface was not called.
 Namespace() Namespace
 OtherModuleNamespace(ModuleOrProxy) Namespace

 // ModuleFactories returns a map of all of the global ModuleFactories by name.
 ModuleFactories() map[string]ModuleFactory

 // HasMutatorFinished returns true if the given mutator has finished running.
 // It will panic if given an invalid mutator name.
 HasMutatorFinished(mutatorName string) bool
}

type BaseModuleContext interface {
 EarlyModuleContext

 GetDirectDepProxyWithTag(name string, tag DependencyTag) ModuleProxy

 // VisitDirectDeps calls visit for each direct dependency.  If there are multiple direct dependencies on the same
 // module visit will be called multiple times on that module and OtherModuleDependencyTag will return a different
 // tag for each.
 //
 // The Module passed to the visit function should not be retained outside of the visit function, it may be
 // invalidated by future mutators.
 VisitDirectDeps(visit func(Module))

 VisitDirectDepsProxy(visit func(proxy ModuleProxy))

 // WalkDeps calls visit for each transitive dependency, traversing the dependency tree in top down order.  visit may
 // be called multiple times for the same (child, parent) pair if there are multiple direct dependencies between the
 // child and parent with different tags.  OtherModuleDependencyTag will return the tag for the currently visited
 // (child, parent) pair.  If visit returns false WalkDeps will not continue recursing down to child.
 //
 // The Modules passed to the visit function should not be retained outside of the visit function, they may be
 // invalidated by future mutators.
 WalkDeps(visit func(Module, Module) bool)

 WalkDepsProxy(visit func(ModuleProxy, ModuleProxy) bool)

 // IsPrimaryModule returns if the current module is the first variant.  This can be used to perform singleton
 // actions that are only done once for all variants of a module.
 IsPrimaryModule() bool

 // IsFinalModule returns if the current module is the last variant.  This can be used to perform singleton
 // actions that are only done once for all variants of a module.
 IsFinalModule() bool

 // OtherModuleName returns the name of another Module.  See BaseModuleContext.ModuleName for more information.
 // It is intended for use inside the visit functions of Visit* and WalkDeps.
 OtherModuleName(m ModuleOrProxy) string

 // OtherModuleDir returns the directory of another Module.  See BaseModuleContext.ModuleDir for more information.
 // It is intended for use inside the visit functions of Visit* and WalkDeps.
 OtherModuleDir(m ModuleOrProxy) string

 // OtherModuleType returns the type of another Module.  See BaseModuleContext.ModuleType for more information.
 // It is intended for use inside the visit functions of Visit* and WalkDeps.
 OtherModuleType(m ModuleOrProxy) string

 // OtherModuleErrorf reports an error on another Module.  See BaseModuleContext.ModuleErrorf for more information.
 // It is intended for use inside the visit functions of Visit* and WalkDeps.
 OtherModuleErrorf(m ModuleOrProxy, fmt string, args ...interface{})

 // OtherModuleDependencyTag returns the dependency tag used to depend on a module, or nil if there is no dependency
 // on the module.  When called inside a Visit* method with current module being visited, and there are multiple
 // dependencies on the module being visited, it returns the dependency tag used for the current dependency.
 OtherModuleDependencyTag(m ModuleOrProxy) DependencyTag

 // OtherModuleSubDir returns the string representing the variations of the module.
 OtherModuleSubDir(m ModuleOrProxy) string

 // OtherModuleExists returns true if a module with the specified name exists, as determined by the NameInterface
 // passed to Context.SetNameInterface, or SimpleNameInterface if it was not called.
 OtherModuleExists(name string) bool

 // ModuleFromName returns (module, true) if a module exists by the given name and same context namespace,
 // or (nil, false) if it does not exist. It panics if there is either more than one
 // module of the given name, or if the given name refers to an alias instead of a module.
 // There are no guarantees about which variant of the module will be returned.
 // Prefer retrieving the module using GetDirectDep or a visit function, when possible, as
 // this will guarantee the appropriate module-variant dependency is returned.
 //
 // WARNING: This should _only_ be used within the context of bp2build, where variants and
 // dependencies are not created.
 ModuleFromName(name string) (Module, bool)

 // OtherModuleDependencyVariantExists returns true if a module with the
 // specified name and variant exists. The variant must match the given
 // variations. It must also match all the non-local variations of the current
 // module. In other words, it checks for the module that AddVariationDependencies
 // would add a dependency on with the same arguments.
 OtherModuleDependencyVariantExists(variations []Variation, name string) bool

 // OtherModuleFarDependencyVariantExists returns true if a module with the
 // specified name and variant exists. The variant must match the given
 // variations, but not the non-local variations of the current module. In
 // other words, it checks for the module that AddFarVariationDependencies
 // would add a dependency on with the same arguments.
 OtherModuleFarDependencyVariantExists(variations []Variation, name string) bool

 // OtherModuleReverseDependencyVariantExists returns true if a module with the
 // specified name exists with the same variations as the current module. In
 // other words, it checks for the module that AddReverseDependency would add a
 // dependency on with the same argument.
 OtherModuleReverseDependencyVariantExists(name string) bool

 // OtherModuleProvider returns the value for a provider for the given module.  If the value is
 // not set or the module is nil, it returns nil and false.  The value returned may be a deep
 // copy of the value originally passed to SetProvider.
 //
 // This method shouldn't be used directly, prefer the type-safe android.OtherModuleProvider instead.
 OtherModuleProvider(m ModuleOrProxy, provider AnyProviderKey) (any, bool)

 OtherModuleHasProvider(m ModuleOrProxy, provider AnyProviderKey) bool

 // OtherModuleIsAutoGenerated returns true if a module has been generated from another module,
 // instead of being defined in Android.bp file
 OtherModuleIsAutoGenerated(m ModuleOrProxy) bool

 // OtherModuleNamespace returns the namespace of the module.
 OtherModuleNamespace(m ModuleOrProxy) Namespace

 // Provider returns the value for a provider for the current module.  If the value is
 // not set it returns nil and false.  It panics if called before the appropriate
 // mutator or GenerateBuildActions pass for the provider.  The value returned may be a deep
 // copy of the value originally passed to SetProvider.
 //
 // This method shouldn't be used directly, prefer the type-safe android.ModuleProvider instead.
 Provider(provider AnyProviderKey) (any, bool)

 // SetProvider sets the value for a provider for the current module.  It panics if not called
 // during the appropriate mutator or GenerateBuildActions pass for the provider, if the value
 // is not of the appropriate type, or if the value has already been set.  The value should not
 // be modified after being passed to SetProvider.
 //
 // This method shouldn't be used directly, prefer the type-safe android.SetProvider instead.
 SetProvider(provider AnyProviderKey, value any)

 EarlyGetMissingDependencies() []string

 // RegisterConfigurableEvaluator registers the evaluator used for the proptools.Configurable's in
 // the module properties. It is used to dump their values in the json debug file
 // (out/soong/soong-debug-info.json), if it's enabled. This should be called from
 // GenerateBuildActions, but doing so is optional; if no evaluator has been registered then
 // configurable values are dumped as placeholder strings.
 RegisterConfigurableEvaluator(evaluator proptools.ConfigurableEvaluator)

 base() *baseModuleContext

 // Returns true if the module should be saved in the context object used by Android tests.
 CaptureModuleForTests() bool
}

type DynamicDependerModuleContext BottomUpMutatorContext

type ModuleContext interface {
 BaseModuleContext

 // ModuleSubDir returns a unique name for the current variant of a module that can be used as part of the path
 // to ensure that each variant of a module gets its own intermediates directory to write to.
 ModuleSubDir() string

 // Variable creates a new ninja variable scoped to the module.  It can be referenced by calls to Rule and Build
 // in the same module.
 Variable(pctx PackageContext, name, value string)

 // Rule creates a new ninja rule scoped to the module.  It can be referenced by calls to Build in the same module.
 Rule(pctx PackageContext, name string, params RuleParams, argNames ...string) Rule

 // Build creates a new ninja build statement.
 Build(pctx PackageContext, params BuildParams)

 // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods,
 // but do not exist.  It can be used with Context.SetAllowMissingDependencies to allow the primary builder to
 // handle missing dependencies on its own instead of having Blueprint treat them as an error.
 GetMissingDependencies() []string

 // FreeModuleAfterGenerateBuildActions marks this module as no longer necessary after the completion of
 // GenerateBuildActions, i.e. all later accesses to the module will be via ModuleProxy and not direct access
 // to the Module.
 FreeModuleAfterGenerateBuildActions()
}

var _ BaseModuleContext = (*baseModuleContext)(nil)

type baseModuleContext struct {
 context        *Context
 config         interface{}
 module         *moduleInfo
 errs           []error
 visitingParent *moduleInfo
 visitingDep    depInfo
 ninjaFileDeps  []string
 evaluator      proptools.ConfigurableEvaluator
}

func (d *baseModuleContext) moduleInfo() *moduleInfo {
 return d.module
}

func (d *baseModuleContext) Module() Module {
 return d.module.logicModule
}

func (d *baseModuleContext) ModuleName() string {
 return d.module.Name()
}

func (d *baseModuleContext) ModuleType() string {
 return d.module.typeName
}

func (d *baseModuleContext) ModuleTags() map[string]string {
 return map[string]string{
  "module_name": d.ModuleName(),
  "module_type": d.ModuleType(),
 }
}

func (d *baseModuleContext) ContainsProperty(name string) bool {
 _, ok := d.module.propertyPos[name]
 return ok
}

func (d *baseModuleContext) ModuleDir() string {
 return filepath.Dir(d.module.relBlueprintsFile)
}

func (d *baseModuleContext) BlueprintsFile() string {
 return d.module.relBlueprintsFile
}

func (d *baseModuleContext) Config() interface{} {
 return d.config
}

func (d *baseModuleContext) error(err error) {
 if err != nil {
  d.errs = append(d.errs, err)
 }
}

func (d *baseModuleContext) Errorf(pos scanner.Position,
 format string, args ...interface{}) {

 d.error(&BlueprintError{
  Err: fmt.Errorf(format, args...),
  Pos: pos,
 })
}

func (d *baseModuleContext) ModuleErrorf(format string,
 args ...interface{}) {

 d.error(d.context.moduleErrorf(d.module, format, args...))
}

func (d *baseModuleContext) PropertyErrorf(property, format string,
 args ...interface{}) {

 d.error(d.context.PropertyErrorf(d.module.logicModule, property, format, args...))
}

func (d *baseModuleContext) OtherModulePropertyErrorf(logicModule ModuleOrProxy, property string, format string,
 args ...interface{}) {

 d.error(d.context.PropertyErrorf(logicModule, property, format, args...))
}

func (d *baseModuleContext) Failed() bool {
 return len(d.errs) > 0
}

func (d *baseModuleContext) GlobWithDeps(pattern string,
 excludes []string) ([]string, error) {
 result, err := d.context.glob(pattern, excludes)
 if err == nil && d.context.incrementalEnabled {
  hash, err := proptools.CalculateHash(stringList(result))
  if err != nil {
   panic(newPanicErrorf(err, "failed to calculate hash for glob result: %s", d.ModuleName()))
  }
  cacheResult := globResultCache{
   Pattern:  pattern,
   Excludes: excludes,
   Result:   hash,
  }
  if !slices.ContainsFunc(d.module.globCache, func(entry globResultCache) bool { return entry.equal(cacheResult) }) {
   d.module.globCache = append(d.module.globCache, cacheResult)
  }
 }
 return result, err
}

func (d *baseModuleContext) Fs() pathtools.FileSystem {
 return d.context.fs
}

func (d *baseModuleContext) Namespace() Namespace {
 return d.context.nameInterface.GetNamespace(newNamespaceContext(d.module))
}

func (d *baseModuleContext) HasMutatorFinished(mutatorName string) bool {
 return d.context.HasMutatorFinished(mutatorName)
}

// storeCoreModuleInfo creates a copy of moduleInfo in moduleGroup.
// This is intended to be called at the end of defaults mutator.
func (d *baseModuleContext) storeCoreModuleInfo() {
 // Create a minimal copy of the properties of moduleInfo.
 // TODO (b/448182009): Add more properties if needed.
 newLogicModule, newProperties := d.context.cloneLogicModule(d.module)
 newModule := moduleInfo{
  logicModule:       newLogicModule,
  properties:        newProperties,
  directDeps:        slices.Clone(d.module.directDeps),
  factory:           d.module.factory,
  relBlueprintsFile: d.module.relBlueprintsFile,
  pos:               d.module.pos,
 }
 d.module.group.coreModuleInfo = newModule
}

var _ ModuleContext = (*moduleContext)(nil)

type moduleContext struct {
 baseModuleContext
 scope              *localScope
 actionDefs         localBuildActions
 handledMissingDeps bool
}

func EqualModules(m1, m2 ModuleOrProxy) bool {
 return m1.info() == m2.info()
}

func (m *baseModuleContext) OtherModuleName(logicModule ModuleOrProxy) string {
 module := logicModule.info()
 return module.Name()
}

func (m *baseModuleContext) OtherModuleDir(logicModule ModuleOrProxy) string {
 module := logicModule.info()
 return filepath.Dir(module.relBlueprintsFile)
}

func (m *baseModuleContext) OtherModuleType(logicModule ModuleOrProxy) string {
 module := logicModule.info()
 return module.typeName
}

func (m *baseModuleContext) OtherModuleErrorf(logicModule ModuleOrProxy, format string,
 args ...interface{}) {

 module := logicModule.info()
 m.errs = append(m.errs, &ModuleError{
  BlueprintError: BlueprintError{
   Err: fmt.Errorf(format, args...),
   Pos: module.pos,
  },
  module: module,
 })
}

func (m *baseModuleContext) OtherModuleDependencyTag(logicModule ModuleOrProxy) DependencyTag {
 // fast path for calling OtherModuleDependencyTag from inside VisitDirectDeps
 if m.visitingDep.module != nil && logicModule.info() == m.visitingDep.module {
  return m.visitingDep.tag
 }

 if m.visitingParent == nil {
  return nil
 }

 for _, dep := range m.visitingParent.directDeps {
  if dep.module == logicModule.info() {
   return dep.tag
  }
 }

 return nil
}

func (m *baseModuleContext) OtherModuleSubDir(logicModule ModuleOrProxy) string {
 return m.context.ModuleSubDir(logicModule)
}

func (m *baseModuleContext) ModuleFromName(name string) (Module, bool) {
 moduleGroup, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace())
 if exists {
  if len(moduleGroup.modules) != 1 {
   panic(fmt.Errorf("Expected exactly one module named %q, but got %d", name, len(moduleGroup.modules)))
  }
  moduleInfo := moduleGroup.modules[0]
  if moduleInfo != nil {
   return moduleInfo.logicModule, true
  } else {
   panic(fmt.Errorf(`Expected actual module named %q, but group did not contain a module.
    There may instead be an alias by that name.`, name))
  }
 }
 return nil, exists
}

func (m *baseModuleContext) OtherModuleExists(name string) bool {
 _, exists := m.context.nameInterface.ModuleFromName(name, m.module.namespace())
 return exists
}

func (m *baseModuleContext) OtherModuleDependencyVariantExists(variations []Variation, name string) bool {
 possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace())
 if possibleDeps == nil {
  return false
 }
 found, _, errs := m.context.findVariant(m.config, m.module, nil, possibleDeps, variations, false, false, m.module.startedMutator)
 if errs != nil {
  panic(errors.Join(errs...))
 }
 return found != nil
}

func (m *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []Variation, name string) bool {
 possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace())
 if possibleDeps == nil {
  return false
 }
 found, _, errs := m.context.findVariant(m.config, m.module, nil, possibleDeps, variations, true, false, m.module.startedMutator)
 if errs != nil {
  panic(errors.Join(errs...))
 }
 return found != nil
}

func (m *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool {
 possibleDeps := m.context.moduleGroupFromName(name, m.module.namespace())
 if possibleDeps == nil {
  return false
 }
 found, _, errs := m.context.findVariant(m.config, m.module, nil, possibleDeps, nil, false, true, m.module.startedMutator)
 if errs != nil {
  panic(errors.Join(errs...))
 }
 return found != nil
}

func (m *baseModuleContext) OtherModuleProvider(logicModule ModuleOrProxy, provider AnyProviderKey) (any, bool) {
 if logicModule == nil {
  return nil, false
 }
 return m.context.provider(logicModule.info(), provider.provider())
}

func (m *baseModuleContext) OtherModuleHasProvider(logicModule ModuleOrProxy, provider AnyProviderKey) bool {
 if logicModule == nil {
  return false
 }
 return m.context.hasProvider(logicModule.info(), provider.provider())
}

func (m *baseModuleContext) Provider(provider AnyProviderKey) (any, bool) {
 return m.context.provider(m.module, provider.provider())
}

func (m *baseModuleContext) SetProvider(provider AnyProviderKey, value interface{}) {
 m.context.setProvider(m.module, provider.provider(), value)
}

func (m *baseModuleContext) GetDirectDepProxyWithTag(name string, tag DependencyTag) ModuleProxy {
 var deps []depInfo
 for _, dep := range m.module.directDeps {
  if dep.module.Name() == name {
   if dep.tag == tag {
    return ModuleProxy{dep.module}
   }
   deps = append(deps, dep)
  }
 }

 if len(deps) != 0 {
  panic(fmt.Errorf("Unable to find dependency %q with requested tag %#v. Found: %#v", deps[0].module, tag, deps))
 }

 return ModuleProxy{}
}

func (m *baseModuleContext) VisitDirectDeps(visit func(Module)) {
 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s",
    m.module, funcName(visit), m.visitingDep.module))
  }
 }()

 m.visitingParent = m.module

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

 m.visitingParent = nil
 m.visitingDep = depInfo{}
}

func (m *baseModuleContext) VisitDirectDepsProxy(visit func(proxy ModuleProxy)) {
 defer func() {
  if r := recover(); r != nil {
   panic(newPanicErrorf(r, "VisitDirectDeps(%s, %s) for dependency %s",
    m.module, funcName(visit), m.visitingDep.module))
  }
 }()

 m.visitingParent = m.module

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

 m.visitingParent = nil
 m.visitingDep = depInfo{}
}

func (m *baseModuleContext) WalkDeps(visit func(child, parent Module) bool) {
 if m.module.startedGenerateBuildActions {
  if _, ok := m.module.logicModule.(moduleUsesIncrementalWalkDeps); !ok {
   panic(fmt.Errorf("WalkDeps called during GenerateBuildActions from a module not marked with ModuleUsesIncrementalWalkDeps"))
  }
 }
 m.context.walkDeps(m.module, true, func(dep depInfo, parent *moduleInfo) bool {
  m.visitingParent = parent
  m.visitingDep = dep
  if dep.module.logicModule == nil {
   panic(fmt.Errorf("WalkDeps visited module %s that called FreeAfterGenerateBuildActions()", dep.module))
  }
  return visit(dep.module.logicModule, parent.logicModule)
 }, nil)

 m.visitingParent = nil
 m.visitingDep = depInfo{}
}

func (m *baseModuleContext) WalkDepsProxy(visit func(child, parent ModuleProxy) bool) {
 if m.module.startedGenerateBuildActions {
  if _, ok := m.module.logicModule.(moduleUsesIncrementalWalkDeps); !ok {
   panic(fmt.Errorf("WalkDeps called during GenerateBuildActions from a module not marked with ModuleUsesIncrementalWalkDeps"))
  }
 }
 m.context.walkDeps(m.module, true, func(dep depInfo, parent *moduleInfo) bool {
  m.visitingParent = parent
  m.visitingDep = dep
  return visit(ModuleProxy{dep.module}, ModuleProxy{parent})
 }, nil)

 m.visitingParent = nil
 m.visitingDep = depInfo{}
}

func (m *baseModuleContext) IsPrimaryModule() bool {
 return m.module.group.modules.firstModule() == m.module
}

func (m *baseModuleContext) IsFinalModule() bool {
 return m.module.group.modules.lastModule() == m.module
}

func (m *baseModuleContext) AddNinjaFileDeps(deps ...string) {
 m.ninjaFileDeps = append(m.ninjaFileDeps, deps...)
}

func (m *baseModuleContext) ModuleFactories() map[string]ModuleFactory {
 return m.context.ModuleTypeFactories()
}

func (m *baseModuleContext) base() *baseModuleContext {
 return m
}

func (m *baseModuleContext) OtherModuleIsAutoGenerated(logicModule ModuleOrProxy) bool {
 module := logicModule.info()
 if module == nil {
  panic(fmt.Errorf("Module %s not found in baseModuleContext", logicModule.Name()))
 }
 return module.createdBy != nil
}

func (m *baseModuleContext) OtherModuleNamespace(logicModule ModuleOrProxy) Namespace {
 return m.context.nameInterface.GetNamespace(newNamespaceContext(logicModule.info()))
}

func (m *moduleContext) ModuleSubDir() string {
 return m.module.variant.name
}

func (m *moduleContext) Variable(pctx PackageContext, name, value string) {
 m.scope.ReparentTo(pctx)

 v, err := m.scope.AddLocalVariable(name, value)
 if err != nil {
  panic(err)
 }

 m.actionDefs.variables = append(m.actionDefs.variables, v)
}

func (m *moduleContext) Rule(pctx PackageContext, name string,
 params RuleParams, argNames ...string) Rule {

 m.scope.ReparentTo(pctx)

 config := m.baseModuleContext.Config()
 r, err := m.scope.AddLocalRule(config, name, ¶ms, argNames...)
 if err != nil {
  panic(err)
 }

 m.actionDefs.rules = append(m.actionDefs.rules, r)

 return r
}

func (m *moduleContext) Build(pctx PackageContext, params BuildParams) {
 m.scope.ReparentTo(pctx)

 if m.context.captureBuildParams {
  if m.module.buildParams == nil {
   m.module.buildParams = &[]BuildParams{}
  }
  *m.module.buildParams = append(*m.module.buildParams, params)
 }

 def, err := parseBuildParams(m.scope, ¶ms, m.ModuleTags())
 if err != nil {
  panic(err)
 }

 m.actionDefs.buildDefs = append(m.actionDefs.buildDefs, def)
 if def.OrderOnlyStrings.Len() > 0 {
  if info, loaded := m.context.orderOnlyStrings.LoadOrStore(def.OrderOnlyStrings, &orderOnlyStringsInfo{
   dedup:       false,
   incremental: m.module.buildActionCacheKey != nil,
  }); loaded {
   for {
    cpy := *info
    cpy.dedup = true
    cpy.incremental = cpy.incremental || m.module.buildActionCacheKey != nil
    if m.context.orderOnlyStrings.CompareAndSwap(def.OrderOnlyStrings, info, &cpy) {
     break
    }
    if info, loaded = m.context.orderOnlyStrings.Load(def.OrderOnlyStrings); !loaded {
     // This shouldn't happen
     panic("order only string was removed unexpectedly")
    }
   }
  }
 }
}

func (m *moduleContext) GetMissingDependencies() []string {
 m.handledMissingDeps = true
 return m.module.missingDeps
}

func (m *moduleContext) FreeModuleAfterGenerateBuildActions() {
 m.module.freeAfterGenerateBuildActions = true
}

func (m *baseModuleContext) EarlyGetMissingDependencies() []string {
 return m.module.missingDeps
}

func (m *baseModuleContext) RegisterConfigurableEvaluator(evaluator proptools.ConfigurableEvaluator) {
 m.evaluator = evaluator
}

// Do not capture on-demand variants when the mutators are being
// run on the variant till the mutator at which it was requested.
// This ensures that there are no dups.
// The final stage of GenerateBuildActions will add it to the context.
func (m *baseModuleContext) CaptureModuleForTests() bool {
 return !m.module.createdOnDemand
}

//
// MutatorContext
//

type mutatorContext struct {
 baseModuleContext
 mutator          *mutatorInfo
 reverseDeps      []reverseDep
 rename           []rename
 replace          []replace
 newVariations    moduleList    // new variants of existing modules
 newModules       []*moduleInfo // brand new modules
 defaultVariation *string
 pauseFunc        pauseFunc
}

type BottomUpMutatorContext interface {
 BaseModuleContext

 // AddDependency adds a dependency to the given module.  It returns a slice of modules for each
 // dependency (some entries may be nil).  Does not affect the ordering of the current mutator
 // pass, but will be ordered correctly for all future mutator passes.
 //
 // This method will pause until the new dependencies have had the current mutator called on them.
 AddDependency(module Module, tag DependencyTag, name ...string) []ModuleProxy

 // AddReverseDependency adds a dependency from the destination to the given module.
 // Does not affect the ordering of the current mutator pass, but will be ordered
 // correctly for all future mutator passes.  All reverse dependencies for a destination module are
 // collected until the end of the mutator pass, sorted by name, and then appended to the destination
 // module's dependency list.  May only  be called by mutators that were marked with
 // UsesReverseDependencies during registration.
 AddReverseDependency(module Module, tag DependencyTag, name string)

 // AddVariationDependencies adds deps as dependencies of the current module, but uses the variations
 // argument to select which variant of the dependency to use.  It returns a slice of modules for
 // each dependency (some entries may be nil).  A variant of the dependency must exist that matches
 // the all of the non-local variations of the current module, plus the variations argument.
 //
 //
 // This method will pause until the new dependencies have had the current mutator called on them.
 AddVariationDependencies([]Variation, DependencyTag, ...string) []ModuleProxy

 // AddReverseVariationDependency adds a dependency from the named module to the current
 // module. The given variations will be added to the current module's varations, and then the
 // result will be used to find the correct variation of the depending module, which must exist.
 //
 // Does not affect the ordering of the current mutator pass, but will be ordered
 // correctly for all future mutator passes.  All reverse dependencies for a destination module are
 // collected until the end of the mutator pass, sorted by name, and then appended to the destination
 // module's dependency list.  May only  be called by mutators that were marked with
 // UsesReverseDependencies during registration.
 AddReverseVariationDependency([]Variation, DependencyTag, string)

 // AddFarVariationDependencies adds deps as dependencies of the current module, but uses the
 // variations argument to select which variant of the dependency to use.  It returns a slice of
 // modules for each dependency (some entries may be nil).  A variant of the dependency must
 // exist that matches the variations argument, but may also have other variations.
 // For any unspecified variation the first variant will be used.
 //
 // Unlike AddVariationDependencies, the variations of the current module are ignored - the
 // dependency only needs to match the supplied variations.
 //
 //
 // This method will pause until the new dependencies have had the current mutator called on them.
 AddFarVariationDependencies([]Variation, DependencyTag, ...string) []ModuleProxy

 // ReplaceDependencies finds all the variants of the module with the specified name, then
 // replaces all dependencies onto those variants with the current variant of this module.
 // Replacements don't take effect until after the mutator pass is finished.  May only
 // be called by mutators that were marked with UsesReplaceDependencies during registration.
 ReplaceDependencies(string)

 // ReplaceDependenciesIf finds all the variants of the module with the specified name, then
 // replaces all dependencies onto those variants with the current variant of this module
 // as long as the supplied predicate returns true.
 // Replacements don't take effect until after the mutator pass is finished.  May only
 // be called by mutators that were marked with UsesReplaceDependencies during registration.
 ReplaceDependenciesIf(string, ReplaceDependencyPredicate)

 // Rename all variants of a module.  The new name is not visible to calls to ModuleName,
 // AddDependency or OtherModuleName until after this mutator pass is complete.  May only be called
 // by mutators that were marked with UsesRename during registration.
 Rename(name string)

 // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
 // the specified property structs to it as if the properties were set in a blueprint file.  May only
 // be called by mutators that were marked with UsesCreateModule during registration.
 CreateModule(ModuleFactory, string, ...interface{}) Module
}

// A Mutator function is called for each Module, and can modify properties on the modules.
// It is called after parsing all Blueprint files, but before generating any build rules,
// and is always called on dependencies before being called on the depending module.
//
// The Mutator function should only modify members of properties structs, and not
// members of the module struct itself, to ensure the modified values are copied
// if a second Mutator chooses to split the module a second time.
type BottomUpMutator func(mctx BottomUpMutatorContext)

// DependencyTag is an interface to an arbitrary object that embeds BaseDependencyTag.  It can be
// used to transfer information on a dependency between the mutator that called AddDependency
// and the GenerateBuildActions method.
type DependencyTag interface {
 dependencyTag(DependencyTag)
}

type BaseDependencyTag struct {
}

func (BaseDependencyTag) dependencyTag(DependencyTag) {
}

var _ DependencyTag = BaseDependencyTag{}

func (mctx *mutatorContext) createVariationsWithTransition(variationNames []string, outgoingTransitions [][]string) []*moduleInfo {
 depChooser := chooseDepByIndexes(mctx.mutator.name, outgoingTransitions)
 modules, errs := mctx.context.createVariations(mctx.module, mctx.mutator, depChooser, variationNames)
 if len(errs) > 0 {
  mctx.errs = append(mctx.errs, errs...)
 }

 if mctx.newVariations != nil {
  panic("module already has variations from this mutator")
 }
 mctx.newVariations = modules

 if len(modules) != len(variationNames) {
  panic("oops!")
 }

 return modules
}

func (mctx *mutatorContext) Module() Module {
 return mctx.module.logicModule
}

func (mctx *mutatorContext) AddDependency(module Module, tag DependencyTag, deps ...string) []ModuleProxy {
 depInfos := make([]ModuleProxy, 0, len(deps))
 for _, dep := range deps {
  modInfo := module.info()
  di, errs := mctx.context.addVariationDependency(modInfo, mctx.mutator, mctx.config, nil, tag, dep, false)
  if len(errs) > 0 {
   mctx.errs = append(mctx.errs, errs...)
  }
  if !mctx.pause(di) {
   // Pausing not supported by this mutator, new dependencies can't be returned.
   di = nil
  }
  if di != nil && di.createdOnDemand {
   modInfo.directDeps = append(modInfo.directDeps, depInfo{di.createdOnDemandReplaceWith, tag})
   modInfo.newOnDemandReverseDeps = append(modInfo.newOnDemandReverseDeps, reverseDep{
    module: modInfo,
    dep: depInfo{
     di.createdOnDemandReplaceWith,
     tag,
    },
   })
  }
  depInfos = append(depInfos, ModuleProxy{di})
 }
 return depInfos
}

func (m *mutatorContext) AddReverseDependency(module Module, tag DependencyTag, name string) {
 if !m.mutator.usesReverseDependencies {
  panic(fmt.Errorf("method AddReverseDependency called from mutator that was not marked UsesReverseDependencies"))
 }

 if _, ok := tag.(BaseDependencyTag); ok {
  panic("BaseDependencyTag is not allowed to be used directly!")
 }

 if module != m.module.logicModule {
  panic(fmt.Errorf("AddReverseDependency called with module that is not the current module"))
 }
 m.AddReverseVariationDependency(nil, tag, name)
}

func (mctx *mutatorContext) AddReverseVariationDependency(variations []Variation, tag DependencyTag, name string) {
 if !mctx.mutator.usesReverseDependencies {
  panic(fmt.Errorf("method AddReverseVariationDependency called from mutator that was not marked UsesReverseDependencies"))
 }

 if _, ok := tag.(BaseDependencyTag); ok {
  panic("BaseDependencyTag is not allowed to be used directly!")
 }

 possibleDeps := mctx.context.moduleGroupFromName(name, mctx.module.namespace())
 if possibleDeps == nil {
  mctx.errs = append(mctx.errs, &BlueprintError{
   Err: fmt.Errorf("%q has a reverse dependency on undefined module %q",
    mctx.module.Name(), name),
   Pos: mctx.module.pos,
  })
  return
 }

 found, newVariant, errs := mctx.context.findVariant(mctx.config, mctx.module, tag, possibleDeps, variations, false, true, -1)
 if errs != nil {
  mctx.errs = append(mctx.errs, errs...)
  return
 }

 if found == nil {
  if mctx.context.allowMissingDependencies {
   // Allow missing variants.
   mctx.errs = append(mctx.errs, mctx.context.discoveredMissingDependencies(mctx.module, name, newVariant)...)
  } else {
   mctx.errs = append(mctx.errs, &BlueprintError{
    Err: fmt.Errorf("reverse dependency %q of %q missing variant:\n  %s\navailable variants:\n  %s",
     name, mctx.module.Name(),
     mctx.context.prettyPrintVariant(newVariant),
     mctx.context.prettyPrintGroupVariants(possibleDeps)),
    Pos: mctx.module.pos,
   })
  }
  return
 }

 mctx.reverseDeps = append(mctx.reverseDeps, reverseDep{
  found,
  depInfo{mctx.module, tag},
 })
}

func (mctx *mutatorContext) AddVariationDependencies(variations []Variation, tag DependencyTag,
 deps ...string) []ModuleProxy {

 depInfos := make([]ModuleProxy, 0, len(deps))
 for _, dep := range deps {
  di, errs := mctx.context.addVariationDependency(mctx.module, mctx.mutator, mctx.config, variations, tag, dep, false)
  if len(errs) > 0 {
   mctx.errs = append(mctx.errs, errs...)
  }
  if !mctx.pause(di) {
   // Pausing not supported by this mutator, new dependencies can't be returned.
   di = nil
  }
  if di != nil && di.createdOnDemand {
   mctx.module.directDeps = append(mctx.module.directDeps, depInfo{di.createdOnDemandReplaceWith, tag})
   mctx.module.newOnDemandReverseDeps = append(mctx.module.newOnDemandReverseDeps, reverseDep{
    module: mctx.module,
    dep: depInfo{
     di.createdOnDemandReplaceWith,
     tag,
    },
   })
  }
  depInfos = append(depInfos, ModuleProxy{di})
 }
 return depInfos
}

func (mctx *mutatorContext) AddFarVariationDependencies(variations []Variation, tag DependencyTag,
 deps ...string) []ModuleProxy {

 depInfos := make([]ModuleProxy, 0, len(deps))
 for _, dep := range deps {
  di, errs := mctx.context.addVariationDependency(mctx.module, mctx.mutator, mctx.config, variations, tag, dep, true)
  if len(errs) > 0 {
   mctx.errs = append(mctx.errs, errs...)
  }
  if !mctx.pause(di) {
   // Pausing not supported by this mutator, new dependencies can't be returned.
   di = nil
  }
  if di != nil && di.createdOnDemand {
   mctx.module.directDeps = append(mctx.module.directDeps, depInfo{di.createdOnDemandReplaceWith, tag})
   mctx.module.newOnDemandReverseDeps = append(mctx.module.newOnDemandReverseDeps, reverseDep{
    module: mctx.module,
    dep: depInfo{
     di.createdOnDemandReplaceWith,
     tag,
    },
   })
  }
  depInfos = append(depInfos, ModuleProxy{di})
 }
 return depInfos
}

func (mctx *mutatorContext) ReplaceDependencies(name string) {
 mctx.ReplaceDependenciesIf(name, nil)
}

type ReplaceDependencyPredicate func(from Module, tag DependencyTag, to Module) bool

func (mctx *mutatorContext) ReplaceDependenciesIf(name string, predicate ReplaceDependencyPredicate) {
 if !mctx.mutator.usesReplaceDependencies {
  panic(fmt.Errorf("method ReplaceDependenciesIf called from mutator that was not marked UsesReplaceDependencies"))
 }

 targets := mctx.context.moduleVariantsThatDependOn(name, mctx.module)

 if len(targets) == 0 {
  panic(fmt.Errorf("ReplaceDependenciesIf could not find variant of %s that depends on %s variant %s",
   name,
   mctx.module.group.name,
   mctx.context.prettyPrintVariant(mctx.module.variant.variations),
  ))
 }

 for _, target := range targets {
  mctx.replace = append(mctx.replace, replace{target, mctx.module, predicate})
 }
}

func (mctx *mutatorContext) Rename(name string) {
 if !mctx.mutator.usesRename {
  panic(fmt.Errorf("method Rename called from mutator that was not marked UsesRename"))
 }
 mctx.rename = append(mctx.rename, rename{mctx.module.group, name})
}

func (mctx *mutatorContext) CreateModule(factory ModuleFactory, typeName string, props ...interface{}) Module {
 if !mctx.mutator.usesCreateModule {
  panic(fmt.Errorf("method CreateModule called from mutator that was not marked UsesCreateModule"))
 }

 module := newModule(factory)

 module.relBlueprintsFile = mctx.module.relBlueprintsFile
 module.pos = mctx.module.pos
 module.propertyPos = mctx.module.propertyPos
 module.createdBy = mctx.module
 module.typeName = typeName

 for _, p := range props {
  err := proptools.AppendMatchingProperties(module.properties, p, nil)
  if err != nil {
   panic(err)
  }
 }

 mctx.newModules = append(mctx.newModules, module)

 return module.logicModule
}

// pause waits until the given dependency has been visited by the mutator's parallelVisit call.
// It returns true if the pause was supported, false if the pause was not supported and did not
// occur, which will happen when the mutator is not parallelizable.  If the dependency is nil
// it returns true if pausing is supported or false if it is not.
func (mctx *mutatorContext) pause(dep *moduleInfo) bool {
 if mctx.pauseFunc != nil {
  if dep != nil {
   mctx.pauseFunc(dep)
  }
  return true
 }
 return false
}

// SimpleName is an embeddable object to implement the ModuleContext.Name method using a property
// called "name".  Modules that embed it must also add SimpleName.Properties to their property
// structure list.
type SimpleName struct {
 Properties struct {
  Name string
 }
}

func (s *SimpleName) Name() string {
 return s.Properties.Name
}

func (s *SimpleName) String() string {
 return s.Name()
}

// Load Hooks

type LoadHookContext interface {
 EarlyModuleContext

 // CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
 // the specified property structs to it as if the properties were set in a blueprint file.
 CreateModule(ModuleFactory, string, ...interface{}) Module

 // CreateModuleInDirectory creates a new module in the specified directory by calling the
 // factory method for the specified moduleType, and applies the specified property structs
 // to it as if the properties were set in a blueprint file.
 CreateModuleInDirectory(ModuleFactory, string, string, ...interface{}) Module

 // RegisterScopedModuleType creates a new module type that is scoped to the current Blueprints
 // file.
 RegisterScopedModuleType(name string, factory ModuleFactory)
}

func (l *loadHookContext) createModule(factory ModuleFactory, typeName, moduleDir string, props ...interface{}) Module {
 module := newModule(factory)

 module.relBlueprintsFile = moduleDir
 module.pos = l.module.pos
 module.propertyPos = l.module.propertyPos
 module.createdBy = l.module
 module.typeName = typeName

 for _, p := range props {
  err := proptools.AppendMatchingProperties(module.properties, p, nil)
  if err != nil {
   panic(err)
  }
 }

 l.newModules = append(l.newModules, module)

 return module.logicModule
}

func (l *loadHookContext) CreateModule(factory ModuleFactory, typeName string, props ...interface{}) Module {
 return l.createModule(factory, typeName, l.module.relBlueprintsFile, props...)
}

func (l *loadHookContext) CreateModuleInDirectory(factory ModuleFactory, typeName, moduleDir string, props ...interface{}) Module {
 if moduleDir != filepath.Clean(moduleDir) {
  panic(fmt.Errorf("Cannot create a module in %s", moduleDir))
 }

 filePath := filepath.Join(moduleDir, "Android.bp")
 return l.createModule(factory, typeName, filePath, props...)
}

func (l *loadHookContext) RegisterScopedModuleType(name string, factory ModuleFactory) {
 if _, exists := l.context.moduleFactories[name]; exists {
  panic(fmt.Errorf("A global module type named %q already exists", name))
 }

 if _, exists := (*l.scopedModuleFactories)[name]; exists {
  panic(fmt.Errorf("A module type named %q already exists in this scope", name))
 }

 if *l.scopedModuleFactories == nil {
  *l.scopedModuleFactories = make(map[string]ModuleFactory)
 }

 (*l.scopedModuleFactories)[name] = factory
}

type loadHookContext struct {
 baseModuleContext
 newModules            []*moduleInfo
 scopedModuleFactories *map[string]ModuleFactory
}

type LoadHook func(ctx LoadHookContext)

// LoadHookWithPriority is a wrapper around LoadHook and allows hooks to be sorted by priority.
// hooks with higher value of `priority` run last.
// hooks with equal value of `priority` run in the order they were registered.
type LoadHookWithPriority struct {
 priority int
 loadHook LoadHook
}

func AddLoadHook(module Module, hook LoadHook) {
 // default priority is 0
 AddLoadHookWithPriority(module, hook, 0)
}

// AddLoadhHookWithPriority adds a load hook with a specified priority.
// Hooks with higher priority run last.
// Hooks with equal priority run in the order they were registered.
func AddLoadHookWithPriority(module Module, hook LoadHook, priority int) {
 module.addLoadHook(LoadHookWithPriority{priority, hook})
}

func runAndRemoveLoadHooks(ctx *Context, config interface{}, module *moduleInfo,
 scopedModuleFactories *map[string]ModuleFactory) (newModules []*moduleInfo, deps []string, errs []error) {

 if hooks := module.logicModule.getAndClearloadHooks(); len(hooks) > 0 {
  // Sort the hooks by priority.
  // Use SliceStable so that hooks with equal priority run in the order they were registered.
  slices.SortStableFunc(hooks, func(i, j LoadHookWithPriority) int { return cmp.Compare(i.priority, j.priority) })

  for _, hook := range hooks {
   mctx := &loadHookContext{
    baseModuleContext: baseModuleContext{
     context: ctx,
     config:  config,
     module:  module,
    },
    scopedModuleFactories: scopedModuleFactories,
   }
   hook.loadHook(mctx)
   newModules = append(newModules, mctx.newModules...)
   deps = append(deps, mctx.ninjaFileDeps...)
   errs = append(errs, mctx.errs...)
  }

  return newModules, deps, errs
 }

 return nil, nil, nil
}

// Check the syntax of a generated blueprint file.
//
// This is intended to perform a quick syntactic check for generated blueprint
// code, where syntactically correct means:
// * No variable definitions.
// * Valid module types.
// * Valid property names.
// * Valid values for the property type.
//
// It does not perform any semantic checking of properties, existence of referenced
// files, or dependencies.
//
// At a low level it:
// * Parses the contents.
// * Invokes relevant factory to create Module instances.
// * Unpacks the properties into the Module.
// * Does not invoke load hooks or any mutators.
//
// The filename is only used for reporting errors.
func CheckBlueprintSyntax(moduleFactories map[string]ModuleFactory, filename string, contents string) []error {
 file, errs := parser.Parse(filename, strings.NewReader(contents))
 if len(errs) != 0 {
  return errs
 }

 for _, def := range file.Defs {
  switch def := def.(type) {
  case *parser.Module:
   _, moduleErrs := processModuleDef(def, filename, moduleFactories, nil, false)
   errs = append(errs, moduleErrs...)

  default:
   panic(fmt.Errorf("unknown definition type: %T", def))
  }
 }

 return errs
}

[Dauer der Verarbeitung: 0.7 Sekunden, vorverarbeitet 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