diff options
Diffstat (limited to 'android/module.go')
| -rw-r--r-- | android/module.go | 2429 |
1 files changed, 630 insertions, 1799 deletions
diff --git a/android/module.go b/android/module.go index ba474530d..dd560315c 100644 --- a/android/module.go +++ b/android/module.go @@ -17,16 +17,11 @@ package android import ( "fmt" "net/url" - "os" - "path" "path/filepath" "reflect" - "regexp" + "slices" "sort" "strings" - "text/scanner" - - "android/soong/bazel" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -35,467 +30,9 @@ import ( var ( DeviceSharedLibrary = "shared_library" DeviceStaticLibrary = "static_library" + jarJarPrefixHandler func(ctx ModuleContext) ) -// BuildParameters describes the set of potential parameters to build a Ninja rule. -// In general, these correspond to a Ninja concept. -type BuildParams struct { - // A Ninja Rule that will be written to the Ninja file. This allows factoring out common code - // among multiple modules to reduce repetition in the Ninja file of action requirements. A rule - // can contain variables that should be provided in Args. - Rule blueprint.Rule - // Deps represents the depfile format. When using RuleBuilder, this defaults to GCC when depfiles - // are used. - Deps blueprint.Deps - // Depfile is a writeable path that allows correct incremental builds when the inputs have not - // been fully specified by the Ninja rule. Ninja supports a subset of the Makefile depfile syntax. - Depfile WritablePath - // A description of the build action. - Description string - // Output is an output file of the action. When using this field, references to $out in the Ninja - // command will refer to this file. - Output WritablePath - // Outputs is a slice of output file of the action. When using this field, references to $out in - // the Ninja command will refer to these files. - Outputs WritablePaths - // SymlinkOutput is an output file specifically that is a symlink. - SymlinkOutput WritablePath - // SymlinkOutputs is a slice of output files specifically that is a symlink. - SymlinkOutputs WritablePaths - // ImplicitOutput is an output file generated by the action. Note: references to `$out` in the - // Ninja command will NOT include references to this file. - ImplicitOutput WritablePath - // ImplicitOutputs is a slice of output files generated by the action. Note: references to `$out` - // in the Ninja command will NOT include references to these files. - ImplicitOutputs WritablePaths - // Input is an input file to the Ninja action. When using this field, references to $in in the - // Ninja command will refer to this file. - Input Path - // Inputs is a slice of input files to the Ninja action. When using this field, references to $in - // in the Ninja command will refer to these files. - Inputs Paths - // Implicit is an input file to the Ninja action. Note: references to `$in` in the Ninja command - // will NOT include references to this file. - Implicit Path - // Implicits is a slice of input files to the Ninja action. Note: references to `$in` in the Ninja - // command will NOT include references to these files. - Implicits Paths - // OrderOnly are Ninja order-only inputs to the action. When these are out of date, the output is - // not rebuilt until they are built, but changes in order-only dependencies alone do not cause the - // output to be rebuilt. - OrderOnly Paths - // Validation is an output path for a validation action. Validation outputs imply lower - // non-blocking priority to building non-validation outputs. - Validation Path - // Validations is a slice of output path for a validation action. Validation outputs imply lower - // non-blocking priority to building non-validation outputs. - Validations Paths - // Whether to skip outputting a default target statement which will be built by Ninja when no - // targets are specified on Ninja's command line. - Default bool - // Args is a key value mapping for replacements of variables within the Rule - Args map[string]string -} - -type ModuleBuildParams BuildParams - -// EarlyModuleContext provides methods that can be called early, as soon as the properties have -// been parsed into the module and before any mutators have run. -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 BaseMutatorContext.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 - // RegisterModuleType. - ModuleType() string - - // BlueprintFile returns the name of the blueprint file that contains the definition of this - // module. - BlueprintsFile() string - - // 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{}) - - // 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 - - // 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) - - DeviceSpecific() bool - SocSpecific() bool - ProductSpecific() bool - SystemExtSpecific() bool - Platform() bool - - Config() Config - DeviceConfig() DeviceConfig - - // Deprecated: use Config() - AConfig() Config - - // GlobWithDeps returns a list of files that match the specified pattern but do not match any - // of the patterns in excludes. 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) - - Glob(globPattern string, excludes []string) Paths - GlobFiles(globPattern string, excludes []string) Paths - IsSymlink(path Path) bool - Readlink(path Path) string - - // 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 -} - -// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns -// a Config instead of an interface{}, and some methods have been wrapped to use an android.Module -// instead of a blueprint.Module, plus some extra methods that return Android-specific information -// about the current module. -type BaseModuleContext interface { - EarlyModuleContext - - blueprintBaseModuleContext() blueprint.BaseModuleContext - - // 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 blueprint.Module) 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 blueprint.Module) 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 blueprint.Module, 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 blueprint.Module) blueprint.DependencyTag - - // 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 - - // 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 []blueprint.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 []blueprint.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 - - // 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 blueprint.Module) string - - // OtherModuleProvider returns the value for a provider for the given module. If the value is - // not set it returns the zero value of the type of the provider, so the return value can always - // be type asserted to the type of the provider. The value returned may be a deep copy of the - // value originally passed to SetProvider. - OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} - - // OtherModuleHasProvider returns true if the provider for the given module has been set. - OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool - - // Provider returns the value for a provider for the current module. If the value is - // not set it returns the zero value of the type of the provider, so the return value can always - // be type asserted to the type of the provider. 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. - Provider(provider blueprint.ProviderKey) interface{} - - // HasProvider returns true if the provider for the current module has been set. - HasProvider(provider blueprint.ProviderKey) 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. - SetProvider(provider blueprint.ProviderKey, value interface{}) - - GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module - - // GetDirectDepWithTag returns the Module the direct dependency with the specified name, or nil if - // none exists. It panics if the dependency does not have the specified tag. It skips any - // dependencies that are not an android.Module. - GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module - - // GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified - // name, or nil if none exists. If there are multiple dependencies on the same module it returns - // the first DependencyTag. - GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) - - ModuleFromName(name string) (blueprint.Module, bool) - - // VisitDirectDepsBlueprint 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. - VisitDirectDepsBlueprint(visit func(blueprint.Module)) - - // 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. It raises an error if any of the - // dependencies are not an android.Module. - // - // 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)) - - VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) - - // VisitDirectDepsIf calls pred for each direct dependency, and if pred returns true calls visit. If there are - // multiple direct dependencies on the same module pred and visit will be called multiple times on that module and - // OtherModuleDependencyTag will return a different tag for each. It skips any - // dependencies that are not an android.Module. - // - // The Module passed to the visit function should not be retained outside of the visit function, it may be - // invalidated by future mutators. - VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) - // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module - VisitDepsDepthFirst(visit func(Module)) - // Deprecated: use WalkDeps instead to support multiple dependency tags on the same module - VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) - - // 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. It skips - // any dependencies that are not an android.Module. - // - // 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(child, parent Module) bool) - - // WalkDepsBlueprint 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. - WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) - - // GetWalkPath is supposed to be called in visit function passed in WalkDeps() - // and returns a top-down dependency path from a start module to current child module. - GetWalkPath() []Module - - // PrimaryModule returns the first variant of the current module. Variants of a module are always visited in - // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from the - // Module returned by PrimaryModule without data races. This can be used to perform singleton actions that are - // only done once for all variants of a module. - PrimaryModule() Module - - // FinalModule returns the last variant of the current module. Variants of a module are always visited in - // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all - // variants using VisitAllModuleVariants if the current module == FinalModule(). This can be used to perform - // singleton actions that are only done once for all variants of a module. - FinalModule() Module - - // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always - // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read - // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any - // data modified by the current mutator. - VisitAllModuleVariants(visit func(Module)) - - // GetTagPath is supposed to be called in visit function passed in WalkDeps() - // and returns a top-down dependency tags path from a start module to current child module. - // It has one less entry than GetWalkPath() as it contains the dependency tags that - // exist between each adjacent pair of modules in the GetWalkPath(). - // GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1] - GetTagPath() []blueprint.DependencyTag - - // GetPathString is supposed to be called in visit function passed in WalkDeps() - // and returns a multi-line string showing the modules and dependency tags - // among them along the top-down dependency path from a start module to current child module. - // skipFirst when set to true, the output doesn't include the start module, - // which is already printed when this function is used along with ModuleErrorf(). - GetPathString(skipFirst bool) string - - AddMissingDependencies(missingDeps []string) - - // AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build - AddUnconvertedBp2buildDep(dep string) - - // AddMissingBp2buildDep stores the module name of a direct dependency that was not found. - AddMissingBp2buildDep(dep string) - - Target() Target - TargetPrimary() bool - - // The additional arch specific targets (e.g. 32/64 bit) that this module variant is - // responsible for creating. - MultiTargets() []Target - Arch() Arch - Os() OsType - Host() bool - Device() bool - Darwin() bool - Windows() bool - Debug() bool - PrimaryArch() bool -} - -// Deprecated: use EarlyModuleContext instead -type BaseContext interface { - EarlyModuleContext -} - -type ModuleContext interface { - BaseModuleContext - - blueprintModuleContext() blueprint.ModuleContext - - // Deprecated: use ModuleContext.Build instead. - ModuleBuild(pctx PackageContext, params ModuleBuildParams) - - // Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must - // be tagged with `android:"path" to support automatic source module dependency resolution. - // - // Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. - ExpandSources(srcFiles, excludes []string) Paths - - // Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must - // be tagged with `android:"path" to support automatic source module dependency resolution. - // - // Deprecated: use PathForModuleSrc instead. - ExpandSource(srcFile, prop string) Path - - ExpandOptionalSource(srcFile *string, prop string) OptionalPath - - // InstallExecutable creates a rule to copy srcPath to name in the installPath directory, - // with the given additional dependencies. The file is marked executable after copying. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallExecutable(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath - - // InstallFile creates a rule to copy srcPath to name in the installPath directory, - // with the given additional dependencies. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallFile(installPath InstallPath, name string, srcPath Path, deps ...Path) InstallPath - - // InstallFileWithExtraFilesZip creates a rule to copy srcPath to name in the installPath - // directory, and also unzip a zip file containing extra files to install into the same - // directory. - // - // The installed file will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, extraZip Path, deps ...Path) InstallPath - - // InstallSymlink creates a rule to create a symlink from src srcPath to name in the installPath - // directory. - // - // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath - - // InstallAbsoluteSymlink creates a rule to create an absolute symlink from src srcPath to name - // in the installPath directory. - // - // The installed symlink will be returned by FilesToInstall(), and the PackagingSpec for the - // installed file will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath - - // PackageFile creates a PackagingSpec as if InstallFile was called, but without creating - // the rule to copy the file. This is useful to define how a module would be packaged - // without installing it into the global installation directories. - // - // The created PackagingSpec for the will be returned by PackagingSpecs() on this module or by - // TransitivePackagingSpecs() on modules that depend on this module through dependency tags - // for which IsInstallDepNeeded returns true. - PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec - - CheckbuildFile(srcPath Path) - - InstallInData() bool - InstallInTestcases() bool - InstallInSanitizerDir() bool - InstallInRamdisk() bool - InstallInVendorRamdisk() bool - InstallInDebugRamdisk() bool - InstallInRecovery() bool - InstallInRoot() bool - InstallInVendor() bool - InstallForceOS() (*OsType, *ArchType) - - RequiredModuleNames() []string - HostRequiredModuleNames() []string - TargetRequiredModuleNames() []string - - ModuleSubDir() string - - Variable(pctx PackageContext, name, value string) - Rule(pctx PackageContext, name string, params blueprint.RuleParams, argNames ...string) blueprint.Rule - // Similar to blueprint.ModuleContext.Build, but takes Paths instead of []string, - // and performs more verification. - Build(pctx PackageContext, params BuildParams) - // Phony creates a Make-style phony rule, a rule with no commands that can depend on other - // phony rules or real files. Phony can be called on the same name multiple times to add - // additional dependencies. - Phony(phony string, deps ...Path) - - // GetMissingDependencies returns the list of dependencies that were passed to AddDependencies or related methods, - // but do not exist. - GetMissingDependencies() []string - - // LicenseMetadataFile returns the path where the license metadata for this module will be - // generated. - LicenseMetadataFile() Path -} - type Module interface { blueprint.Module @@ -518,7 +55,7 @@ type Module interface { base() *ModuleBase Disable() - Enabled() bool + Enabled(ctx ConfigAndErrorContext) bool Target() Target MultiTargets() []Target @@ -538,6 +75,8 @@ type Module interface { InstallInDebugRamdisk() bool InstallInRecovery() bool InstallInRoot() bool + InstallInOdm() bool + InstallInProduct() bool InstallInVendor() bool InstallForceOS() (*OsType, *ArchType) PartitionTag(DeviceConfig) string @@ -556,13 +95,6 @@ type Module interface { AddProperties(props ...interface{}) GetProperties() []interface{} - // IsConvertedByBp2build returns whether this module was converted via bp2build - IsConvertedByBp2build() bool - // Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module - Bp2buildTargets() []bp2buildInfo - GetUnconvertedBp2buildDeps() []string - GetMissingBp2buildDeps() []string - BuildParamsForTests() []BuildParams RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams VariablesForTests() map[string]string @@ -576,7 +108,7 @@ type Module interface { // Get information about the properties that can contain visibility rules. visibilityProperties() []visibilityProperty - RequiredModuleNames() []string + RequiredModuleNames(ctx ConfigAndErrorContext) []string HostRequiredModuleNames() []string TargetRequiredModuleNames() []string @@ -586,6 +118,8 @@ type Module interface { // TransitivePackagingSpecs returns the PackagingSpecs for this module and any transitive // dependencies with dependency tags for which IsInstallDepNeeded() returns true. TransitivePackagingSpecs() []PackagingSpec + + ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator } // Qualified id for a module @@ -723,7 +257,7 @@ type commonProperties struct { // but are not usually required (e.g. superceded by a prebuilt) should not be // disabled as that will prevent them from being built by the checkbuild target // and so prevent early detection of changes that have broken those modules. - Enabled *bool `android:"arch_variant"` + Enabled proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"` // Controls the visibility of this module to other modules. Allowable values are one or more of // these formats: @@ -766,7 +300,7 @@ type commonProperties struct { // defaults module, use the `defaults_visibility` property on the defaults module; // not to be confused with the `default_visibility` property on the package module. // - // See https://android.googlesource.com/platform/build/soong/+/master/README.md#visibility for + // See https://android.googlesource.com/platform/build/soong/+/main/README.md#visibility for // more details. Visibility []string @@ -858,7 +392,7 @@ type commonProperties struct { Vintf_fragments []string `android:"path"` // names of other modules to install if this module is installed - Required []string `android:"arch_variant"` + Required proptools.Configurable[[]string] `android:"arch_variant"` // names of other modules to install on host if this module is installed Host_required []string `android:"arch_variant"` @@ -871,6 +405,10 @@ type commonProperties struct { // Set by osMutator CompileOS OsType `blueprint:"mutated"` + // Set to true after the arch mutator has run on this module and set CompileTarget, + // CompileMultiTargets, and CompilePrimary + ArchReady bool `blueprint:"mutated"` + // The Target of artifacts that this module variant is responsible for creating. // // Set by archMutator @@ -916,6 +454,11 @@ type commonProperties struct { // Set by osMutator. CommonOSVariant bool `blueprint:"mutated"` + // When set to true, this module is not installed to the full install path (ex: under + // out/target/product/<name>/<partition>). It can be installed only to the packaging + // modules like android_filesystem. + No_full_install *bool + // When HideFromMake is set to true, no entry for this variant will be emitted in the // generated Android.mk file. HideFromMake bool `blueprint:"mutated"` @@ -925,6 +468,12 @@ type commonProperties struct { // and don't create a rule to install the file. SkipInstall bool `blueprint:"mutated"` + // UninstallableApexPlatformVariant is set by MakeUninstallable called by the apex + // mutator. MakeUninstallable also sets HideFromMake. UninstallableApexPlatformVariant + // is used to avoid adding install or packaging dependencies into libraries provided + // by apexes. + UninstallableApexPlatformVariant bool `blueprint:"mutated"` + // Whether the module has been replaced by a prebuilt ReplacedByPrebuilt bool `blueprint:"mutated"` @@ -933,7 +482,8 @@ type commonProperties struct { NamespaceExportedToMake bool `blueprint:"mutated"` - MissingDeps []string `blueprint:"mutated"` + MissingDeps []string `blueprint:"mutated"` + CheckedMissingDeps bool `blueprint:"mutated"` // Name and variant strings stored by mutators to enable Module.String() DebugName string `blueprint:"mutated"` @@ -945,38 +495,8 @@ type commonProperties struct { // constants in image.go, but can also be set to a custom value by individual module types. ImageVariation string `blueprint:"mutated"` - // Bazel conversion status - BazelConversionStatus BazelConversionStatus `blueprint:"mutated"` -} - -// CommonAttributes represents the common Bazel attributes from which properties -// in `commonProperties` are translated/mapped; such properties are annotated in -// a list their corresponding attribute. It is embedded within `bp2buildInfo`. -type CommonAttributes struct { - // Soong nameProperties -> Bazel name - Name string - - // Data mapped from: Required - Data bazel.LabelListAttribute - - // SkipData is neither a Soong nor Bazel target attribute - // If true, this will not fill the data attribute automatically - // This is useful for Soong modules that have 1:many Bazel targets - // Some of the generated Bazel targets might not have a data attribute - SkipData *bool - - Tags bazel.StringListAttribute - - Applicable_licenses bazel.LabelListAttribute - - Testonly *bool -} - -// constraintAttributes represents Bazel attributes pertaining to build constraints, -// which make restrict building a Bazel target for some set of platforms. -type constraintAttributes struct { - // Constraint values this target can be built for. - Target_compatible_with bazel.LabelListAttribute + // The team (defined by the owner/vendor) who owns the property. + Team *string `android:"path"` } type distProperties struct { @@ -989,6 +509,21 @@ type distProperties struct { Dists []Dist `android:"arch_variant"` } +type TeamDepTagType struct { + blueprint.BaseDependencyTag +} + +var teamDepTag = TeamDepTagType{} + +// Dependency tag for required, host_required, and target_required modules. +var RequiredDepTag = struct { + blueprint.BaseDependencyTag + InstallAlwaysNeededDependencyTag + // Requiring disabled module has been supported (as a side effect of this being implemented + // in Make). We may want to make it an error, but for now, let's keep the existing behavior. + AlwaysAllowDisabledModuleDependencyTag +}{} + // CommonTestOptions represents the common `test_options` properties in // Android.bp. type CommonTestOptions struct { @@ -1216,189 +751,6 @@ func InitCommonOSAndroidMultiTargetsArchModule(m Module, hod HostOrDeviceSupport m.base().commonProperties.CreateCommonOSVariant = true } -func (attrs *CommonAttributes) fillCommonBp2BuildModuleAttrs(ctx *topDownMutatorContext, - enabledPropertyOverrides bazel.BoolAttribute) constraintAttributes { - - mod := ctx.Module().base() - // Assert passed-in attributes include Name - if len(attrs.Name) == 0 { - if ctx.ModuleType() != "package" { - ctx.ModuleErrorf("CommonAttributes in fillCommonBp2BuildModuleAttrs expects a `.Name`!") - } - } - - depsToLabelList := func(deps []string) bazel.LabelListAttribute { - return bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, deps)) - } - - var enabledProperty bazel.BoolAttribute - - onlyAndroid := false - neitherHostNorDevice := false - - osSupport := map[string]bool{} - - // if the target is enabled and supports arch variance, determine the defaults based on the module - // type's host or device property and host_supported/device_supported properties - if mod.commonProperties.ArchSpecific { - moduleSupportsDevice := mod.DeviceSupported() - moduleSupportsHost := mod.HostSupported() - if moduleSupportsHost && !moduleSupportsDevice { - // for host only, we specify as unsupported on android rather than listing all host osSupport - // TODO(b/220874839): consider replacing this with a constraint that covers all host osSupport - // instead - enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(false)) - } else if moduleSupportsDevice && !moduleSupportsHost { - enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, Android.Name, proptools.BoolPtr(true)) - // specify as a positive to ensure any target-specific enabled can be resolved - // also save that a target is only android, as if there is only the positive restriction on - // android, it'll be dropped, so we may need to add it back later - onlyAndroid = true - } else if !moduleSupportsHost && !moduleSupportsDevice { - neitherHostNorDevice = true - } - - for _, osType := range OsTypeList() { - if osType.Class == Host { - osSupport[osType.Name] = moduleSupportsHost - } else if osType.Class == Device { - osSupport[osType.Name] = moduleSupportsDevice - } - } - } - - if neitherHostNorDevice { - // we can't build this, disable - enabledProperty.Value = proptools.BoolPtr(false) - } else if mod.commonProperties.Enabled != nil { - enabledProperty.SetValue(mod.commonProperties.Enabled) - if !*mod.commonProperties.Enabled { - for oss, enabled := range osSupport { - if val := enabledProperty.SelectValue(bazel.OsConfigurationAxis, oss); enabled && val != nil && *val { - // if this should be disabled by default, clear out any enabling we've done - enabledProperty.SetSelectValue(bazel.OsConfigurationAxis, oss, nil) - } - } - } - } - - attrs.Applicable_licenses = bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, mod.commonProperties.Licenses)) - - // The required property can contain the module itself. This causes a cycle - // when generated as the 'data' label list attribute in Bazel. Remove it if - // it exists. See b/247985196. - _, requiredWithoutCycles := RemoveFromList(ctx.ModuleName(), mod.commonProperties.Required) - requiredWithoutCycles = FirstUniqueStrings(requiredWithoutCycles) - required := depsToLabelList(requiredWithoutCycles) - archVariantProps := mod.GetArchVariantProperties(ctx, &commonProperties{}) - for axis, configToProps := range archVariantProps { - for config, _props := range configToProps { - if archProps, ok := _props.(*commonProperties); ok { - _, requiredWithoutCycles := RemoveFromList(ctx.ModuleName(), archProps.Required) - requiredWithoutCycles = FirstUniqueStrings(requiredWithoutCycles) - required.SetSelectValue(axis, config, depsToLabelList(requiredWithoutCycles).Value) - if !neitherHostNorDevice { - if archProps.Enabled != nil { - if axis != bazel.OsConfigurationAxis || osSupport[config] { - enabledProperty.SetSelectValue(axis, config, archProps.Enabled) - } - } - } - } - } - } - - if !neitherHostNorDevice { - if enabledPropertyOverrides.Value != nil { - enabledProperty.Value = enabledPropertyOverrides.Value - } - for _, axis := range enabledPropertyOverrides.SortedConfigurationAxes() { - configToBools := enabledPropertyOverrides.ConfigurableValues[axis] - for cfg, val := range configToBools { - if axis != bazel.OsConfigurationAxis || osSupport[cfg] { - enabledProperty.SetSelectValue(axis, cfg, &val) - } - } - } - } - - productConfigEnabledLabels := []bazel.Label{} - // TODO(b/234497586): Soong config variables and product variables have different overriding behavior, we - // should handle it correctly - if !proptools.BoolDefault(enabledProperty.Value, true) && !neitherHostNorDevice { - // If the module is not enabled by default, then we can check if a - // product variable enables it - productConfigEnabledLabels = productVariableConfigEnableLabels(ctx) - - if len(productConfigEnabledLabels) > 0 { - // In this case, an existing product variable configuration overrides any - // module-level `enable: false` definition - newValue := true - enabledProperty.Value = &newValue - } - } - - productConfigEnabledAttribute := bazel.MakeLabelListAttribute(bazel.LabelList{ - productConfigEnabledLabels, nil, - }) - - platformEnabledAttribute, err := enabledProperty.ToLabelListAttribute( - bazel.LabelList{[]bazel.Label{{Label: "@platforms//:incompatible"}}, nil}, - bazel.LabelList{[]bazel.Label{}, nil}) - if err != nil { - ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err) - } - - // if android is the only arch/os enabled, then add a restriction to only be compatible with android - if platformEnabledAttribute.IsNil() && onlyAndroid { - l := bazel.LabelAttribute{} - l.SetValue(bazel.Label{Label: bazel.OsConfigurationAxis.SelectKey(Android.Name)}) - platformEnabledAttribute.Add(&l) - } - - if !proptools.Bool(attrs.SkipData) { - attrs.Data.Append(required) - } - // SkipData is not an attribute of any Bazel target - // Set this to nil so that it does not appear in the generated build file - attrs.SkipData = nil - - moduleEnableConstraints := bazel.LabelListAttribute{} - moduleEnableConstraints.Append(platformEnabledAttribute) - moduleEnableConstraints.Append(productConfigEnabledAttribute) - - return constraintAttributes{Target_compatible_with: moduleEnableConstraints} -} - -// Check product variables for `enabled: true` flag override. -// Returns a list of the constraint_value targets who enable this override. -func productVariableConfigEnableLabels(ctx *topDownMutatorContext) []bazel.Label { - productVariableProps := ProductVariableProperties(ctx, ctx.Module()) - productConfigEnablingTargets := []bazel.Label{} - const propName = "Enabled" - if productConfigProps, exists := productVariableProps[propName]; exists { - for productConfigProp, prop := range productConfigProps { - flag, ok := prop.(*bool) - if !ok { - ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName)) - } - - if *flag { - axis := productConfigProp.ConfigurationAxis() - targetLabel := axis.SelectKey(productConfigProp.SelectKey()) - productConfigEnablingTargets = append(productConfigEnablingTargets, bazel.Label{ - Label: targetLabel, - }) - } else { - // TODO(b/210546943): handle negative case where `enabled: false` - ctx.ModuleErrorf("`enabled: false` is not currently supported for configuration variables. See b/210546943", proptools.PropertyNameForField(propName)) - } - } - } - - return productConfigEnablingTargets -} - // A ModuleBase object contains the properties that are common to all Android // modules. It should be included as an anonymous field in every module // struct definition. InitAndroidModule should then be called from the module's @@ -1459,9 +811,6 @@ type ModuleBase struct { // archPropRoot that is filled with arch specific values by the arch mutator. archProperties [][]interface{} - // Properties specific to the Blueprint to BUILD migration. - bazelTargetModuleProperties bazel.BazelTargetModuleProperties - // Information about all the properties on the module that contains visibility rules that need // checking. visibilityPropertyInfo []visibilityProperty @@ -1474,14 +823,19 @@ type ModuleBase struct { noAddressSanitizer bool installFiles InstallPaths - installFilesDepSet *installPathsDepSet + installFilesDepSet *DepSet[InstallPath] checkbuildFiles Paths packagingSpecs []PackagingSpec - packagingSpecsDepSet *packagingSpecsDepSet + packagingSpecsDepSet *DepSet[PackagingSpec] // katiInstalls tracks the install rules that were created by Soong but are being exported // to Make to convert to ninja rules so that Make can add additional dependencies. katiInstalls katiInstalls - katiSymlinks katiInstalls + // katiInitRcInstalls and katiVintfInstalls track the install rules created by Soong that are + // allowed to have duplicates across modules and variants. + katiInitRcInstalls katiInstalls + katiVintfInstalls katiInstalls + katiSymlinks katiInstalls + testData []DataPath // The files to copy to the dist as explicitly specified in the .bp file. distFiles TaggedDistFiles @@ -1504,84 +858,30 @@ type ModuleBase struct { initRcPaths Paths vintfFragmentsPaths Paths + installedInitRcPaths InstallPaths + installedVintfFragmentsPaths InstallPaths + + // Merged Aconfig files for all transitive deps. + aconfigFilePaths Paths + // set of dependency module:location mappings used to populate the license metadata for // apex containers. licenseInstallMap []string // The path to the generated license metadata file for the module. licenseMetadataFile WritablePath -} - -// A struct containing all relevant information about a Bazel target converted via bp2build. -type bp2buildInfo struct { - Dir string - BazelProps bazel.BazelTargetModuleProperties - CommonAttrs CommonAttributes - ConstraintAttrs constraintAttributes - Attrs interface{} -} - -// TargetName returns the Bazel target name of a bp2build converted target. -func (b bp2buildInfo) TargetName() string { - return b.CommonAttrs.Name -} -// TargetPackage returns the Bazel package of a bp2build converted target. -func (b bp2buildInfo) TargetPackage() string { - return b.Dir -} + // moduleInfoJSON can be filled out by GenerateAndroidBuildActions to write a JSON file that will + // be included in the final module-info.json produced by Make. + moduleInfoJSON *ModuleInfoJSON -// BazelRuleClass returns the Bazel rule class of a bp2build converted target. -func (b bp2buildInfo) BazelRuleClass() string { - return b.BazelProps.Rule_class -} + // outputFiles stores the output of a module by tag and is used to set + // the OutputFilesProvider in GenerateBuildActions + outputFiles OutputFilesInfo -// BazelRuleLoadLocation returns the location of the Bazel rule of a bp2build converted target. -// This may be empty as native Bazel rules do not need to be loaded. -func (b bp2buildInfo) BazelRuleLoadLocation() string { - return b.BazelProps.Bzl_load_location -} - -// BazelAttributes returns the Bazel attributes of a bp2build converted target. -func (b bp2buildInfo) BazelAttributes() []interface{} { - return []interface{}{&b.CommonAttrs, &b.ConstraintAttrs, b.Attrs} -} - -func (m *ModuleBase) addBp2buildInfo(info bp2buildInfo) { - m.commonProperties.BazelConversionStatus.Bp2buildInfo = append(m.commonProperties.BazelConversionStatus.Bp2buildInfo, info) -} - -// IsConvertedByBp2build returns whether this module was converted via bp2build. -func (m *ModuleBase) IsConvertedByBp2build() bool { - return len(m.commonProperties.BazelConversionStatus.Bp2buildInfo) > 0 -} - -// Bp2buildTargets returns the Bazel targets bp2build generated for this module. -func (m *ModuleBase) Bp2buildTargets() []bp2buildInfo { - return m.commonProperties.BazelConversionStatus.Bp2buildInfo -} - -// AddUnconvertedBp2buildDep stores module name of a dependency that was not converted to Bazel. -func (b *baseModuleContext) AddUnconvertedBp2buildDep(dep string) { - unconvertedDeps := &b.Module().base().commonProperties.BazelConversionStatus.UnconvertedDeps - *unconvertedDeps = append(*unconvertedDeps, dep) -} - -// AddMissingBp2buildDep stores module name of a dependency that was not found in a Android.bp file. -func (b *baseModuleContext) AddMissingBp2buildDep(dep string) { - missingDeps := &b.Module().base().commonProperties.BazelConversionStatus.MissingDeps - *missingDeps = append(*missingDeps, dep) -} - -// GetUnconvertedBp2buildDeps returns the list of module names of this module's direct dependencies that -// were not converted to Bazel. -func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string { - return FirstUniqueStrings(m.commonProperties.BazelConversionStatus.UnconvertedDeps) -} - -// GetMissingBp2buildDeps returns the list of module names that were not found in Android.bp files. -func (m *ModuleBase) GetMissingBp2buildDeps() []string { - return FirstUniqueStrings(m.commonProperties.BazelConversionStatus.MissingDeps) + // complianceMetadataInfo is for different module types to dump metadata. + // See android.ModuleContext interface. + complianceMetadataInfo *ComplianceMetadataInfo } func (m *ModuleBase) AddJSONData(d *map[string]interface{}) { @@ -1693,6 +993,108 @@ func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {} func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {} +func (m *ModuleBase) baseDepsMutator(ctx BottomUpMutatorContext) { + if m.Team() != "" { + ctx.AddDependency(ctx.Module(), teamDepTag, m.Team()) + } + + // TODO(jiyong): remove below case. This is to work around build errors happening + // on branches with reduced manifest like aosp_kernel-build-tools. + // In the branch, a build error occurs as follows. + // 1. aosp_kernel-build-tools is a reduced manifest branch. It doesn't have some git + // projects like external/bouncycastle + // 2. `boot_signer` is `required` by modules like `build_image` which is explicitly list as + // the top-level build goal (in the shell file that invokes Soong). + // 3. `boot_signer` depends on `bouncycastle-unbundled` which is in the missing git project. + // 4. aosp_kernel-build-tools invokes soong with `--skip-make`. Therefore, the absence of + // ALLOW_MISSING_DEPENDENCIES didn't cause a problem. + // 5. Now, since Soong understands `required` deps, it tries to build `boot_signer` and the + // absence of external/bouncycastle fails the build. + // + // Unfortunately, there's no way for Soong to correctly determine if it's running in a + // reduced manifest branch. Instead, here, we use the absence of DeviceArch or DeviceName as + // a strong signal, because that's very common across reduced manifest branches. + pv := ctx.Config().productVariables + fullManifest := pv.DeviceArch != nil && pv.DeviceName != nil + if fullManifest { + addRequiredDeps(ctx) + } +} + +// addRequiredDeps adds required, target_required, and host_required as dependencies. +func addRequiredDeps(ctx BottomUpMutatorContext) { + addDep := func(target Target, depName string) { + if !ctx.OtherModuleExists(depName) { + if ctx.Config().AllowMissingDependencies() { + return + } + } + + // If Android native module requires another Android native module, ensure that + // they have the same bitness. This mimics the policy in select-bitness-of-required-modules + // in build/make/core/main.mk. + // TODO(jiyong): the Make-side does this only when the required module is a shared + // library or a native test. + bothInAndroid := ctx.Device() && target.Os.Class == Device + nativeArch := InList(ctx.Arch().ArchType.Multilib, []string{"lib32", "lib64"}) && + InList(target.Arch.ArchType.Multilib, []string{"lib32", "lib64"}) + sameBitness := ctx.Arch().ArchType.Multilib == target.Arch.ArchType.Multilib + if bothInAndroid && nativeArch && !sameBitness { + return + } + + // ... also don't make a dependency between native bridge arch and non-native bridge + // arches. b/342945184 + if ctx.Target().NativeBridge != target.NativeBridge { + return + } + + variation := target.Variations() + if ctx.OtherModuleFarDependencyVariantExists(variation, depName) { + ctx.AddFarVariationDependencies(variation, RequiredDepTag, depName) + } + } + + var deviceTargets []Target + deviceTargets = append(deviceTargets, ctx.Config().Targets[Android]...) + deviceTargets = append(deviceTargets, ctx.Config().AndroidCommonTarget) + + var hostTargets []Target + hostTargets = append(hostTargets, ctx.Config().Targets[ctx.Config().BuildOS]...) + hostTargets = append(hostTargets, ctx.Config().BuildOSCommonTarget) + + if ctx.Device() { + for _, depName := range ctx.Module().RequiredModuleNames(ctx) { + for _, target := range deviceTargets { + addDep(target, depName) + } + } + for _, depName := range ctx.Module().HostRequiredModuleNames() { + for _, target := range hostTargets { + addDep(target, depName) + } + } + } + + if ctx.Host() { + for _, depName := range ctx.Module().RequiredModuleNames(ctx) { + for _, target := range hostTargets { + // When a host module requires another host module, don't make a + // dependency if they have different OSes (i.e. hostcross). + if ctx.Target().HostCross != target.HostCross { + continue + } + addDep(target, depName) + } + } + for _, depName := range ctx.Module().TargetRequiredModuleNames() { + for _, target := range deviceTargets { + addDep(target, depName) + } + } + } +} + // AddProperties "registers" the provided props // each value in props MUST be a pointer to a struct func (m *ModuleBase) AddProperties(props ...interface{}) { @@ -1796,30 +1198,23 @@ func (m *ModuleBase) GenerateTaggedDistFiles(ctx BaseModuleContext) TaggedDistFi // the special tag name which represents that. tag := proptools.StringDefault(dist.Tag, DefaultDistTag) - if outputFileProducer, ok := m.module.(OutputFileProducer); ok { - // Call the OutputFiles(tag) method to get the paths associated with the tag. - distFilesForTag, err := outputFileProducer.OutputFiles(tag) - - // If the tag was not supported and is not DefaultDistTag then it is an error. - // Failing to find paths for DefaultDistTag is not an error. It just means - // that the module type requires the legacy behavior. + distFileForTagFromProvider, err := outputFilesForModuleFromProvider(ctx, m.module, tag) + if err != OutputFilesProviderNotSet { if err != nil && tag != DefaultDistTag { ctx.PropertyErrorf("dist.tag", "%s", err.Error()) + } else { + distFiles = distFiles.addPathsForTag(tag, distFileForTagFromProvider...) + continue } - - distFiles = distFiles.addPathsForTag(tag, distFilesForTag...) - } else if tag != DefaultDistTag { - // If the tag was specified then it is an error if the module does not - // implement OutputFileProducer because there is no other way of accessing - // the paths for the specified tag. - ctx.PropertyErrorf("dist.tag", - "tag %s not supported because the module does not implement OutputFileProducer", tag) } } - return distFiles } +func (m *ModuleBase) ArchReady() bool { + return m.commonProperties.ArchReady +} + func (m *ModuleBase) Target() Target { return m.commonProperties.CompileTarget } @@ -1968,14 +1363,11 @@ func (m *ModuleBase) PartitionTag(config DeviceConfig) string { return partition } -func (m *ModuleBase) Enabled() bool { +func (m *ModuleBase) Enabled(ctx ConfigAndErrorContext) bool { if m.commonProperties.ForcedDisabled { return false } - if m.commonProperties.Enabled == nil { - return !m.Os().DefaultDisabled - } - return *m.commonProperties.Enabled + return m.commonProperties.Enabled.GetOrDefault(m.ConfigurableEvaluator(ctx), !m.Os().DefaultDisabled) } func (m *ModuleBase) Disable() { @@ -2009,6 +1401,7 @@ func (m *ModuleBase) IsSkipInstall() bool { // have other side effects, in particular when it adds a NOTICE file target, // which other install targets might depend on. func (m *ModuleBase) MakeUninstallable() { + m.commonProperties.UninstallableApexPlatformVariant = true m.HideFromMake() } @@ -2038,13 +1431,19 @@ func (m *ModuleBase) EffectiveLicenseFiles() Paths { } // computeInstallDeps finds the installed paths of all dependencies that have a dependency -// tag that is annotated as needing installation via the IsInstallDepNeeded method. -func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) { - var installDeps []*installPathsDepSet - var packagingSpecs []*packagingSpecsDepSet +// tag that is annotated as needing installation via the isInstallDepNeeded method. +func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*DepSet[InstallPath], []*DepSet[PackagingSpec]) { + var installDeps []*DepSet[InstallPath] + var packagingSpecs []*DepSet[PackagingSpec] ctx.VisitDirectDeps(func(dep Module) { - if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) && !dep.IsHideFromMake() && !dep.IsSkipInstall() { - installDeps = append(installDeps, dep.base().installFilesDepSet) + if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) { + // Installation is still handled by Make, so anything hidden from Make is not + // installable. + if !dep.IsHideFromMake() && !dep.IsSkipInstall() { + installDeps = append(installDeps, dep.base().installFilesDepSet) + } + // Add packaging deps even when the dependency is not installed so that uninstallable + // modules can still be packaged. Often the package will be installed instead. packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet) } }) @@ -2052,6 +1451,17 @@ func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSe return installDeps, packagingSpecs } +// isInstallDepNeeded returns true if installing the output files of the current module +// should also install the output files of the given dependency and dependency tag. +func isInstallDepNeeded(dep Module, tag blueprint.DependencyTag) bool { + // Don't add a dependency from the platform to a library provided by an apex. + if dep.base().commonProperties.UninstallableApexPlatformVariant { + return false + } + // Only install modules if the dependency tag is an InstallDepNeeded tag. + return IsInstallDepNeededTag(tag) +} + func (m *ModuleBase) FilesToInstall() InstallPaths { return m.installFiles } @@ -2096,6 +1506,14 @@ func (m *ModuleBase) InstallInRecovery() bool { return Bool(m.commonProperties.Recovery) } +func (m *ModuleBase) InstallInOdm() bool { + return false +} + +func (m *ModuleBase) InstallInProduct() bool { + return false +} + func (m *ModuleBase) InstallInVendor() bool { return Bool(m.commonProperties.Vendor) || Bool(m.commonProperties.Soc_specific) || Bool(m.commonProperties.Proprietary) } @@ -2112,6 +1530,10 @@ func (m *ModuleBase) Owner() string { return String(m.commonProperties.Owner) } +func (m *ModuleBase) Team() string { + return String(m.commonProperties.Team) +} + func (m *ModuleBase) setImageVariation(variant string) { m.commonProperties.ImageVariation = variant } @@ -2149,8 +1571,8 @@ func (m *ModuleBase) InRecovery() bool { return m.base().commonProperties.ImageVariation == RecoveryVariation } -func (m *ModuleBase) RequiredModuleNames() []string { - return m.base().commonProperties.Required +func (m *ModuleBase) RequiredModuleNames(ctx ConfigAndErrorContext) []string { + return m.base().commonProperties.Required.GetOrDefault(m.ConfigurableEvaluator(ctx), nil) } func (m *ModuleBase) HostRequiredModuleNames() []string { @@ -2189,7 +1611,7 @@ func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) { // not be created if the module is not exported to make. // Those could depend on the build target and fail to compile // for the current build target. - if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(a) { + if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(ctx, a) { allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...) } }) @@ -2297,14 +1719,36 @@ func (m *ModuleBase) earlyModuleContextFactory(ctx blueprint.EarlyModuleContext) func (m *ModuleBase) baseModuleContextFactory(ctx blueprint.BaseModuleContext) baseModuleContext { return baseModuleContext{ bp: ctx, + archModuleContext: m.archModuleContextFactory(ctx), earlyModuleContext: m.earlyModuleContextFactory(ctx), - os: m.commonProperties.CompileOS, - target: m.commonProperties.CompileTarget, - targetPrimary: m.commonProperties.CompilePrimary, - multiTargets: m.commonProperties.CompileMultiTargets, } } +type archModuleContextFactoryContext interface { + Config() interface{} +} + +func (m *ModuleBase) archModuleContextFactory(ctx archModuleContextFactoryContext) archModuleContext { + config := ctx.Config().(Config) + target := m.Target() + primaryArch := false + if len(config.Targets[target.Os]) <= 1 { + primaryArch = true + } else { + primaryArch = target.Arch.ArchType == config.Targets[target.Os][0].Arch.ArchType + } + + return archModuleContext{ + ready: m.commonProperties.ArchReady, + os: m.commonProperties.CompileOS, + target: m.commonProperties.CompileTarget, + targetPrimary: m.commonProperties.CompilePrimary, + multiTargets: m.commonProperties.CompileMultiTargets, + primaryArch: primaryArch, + } + +} + func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) { ctx := &moduleContext{ module: m.module, @@ -2313,13 +1757,15 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) variables: make(map[string]string), } + setContainerInfo(ctx) + m.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic") dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx) // set m.installFilesDepSet to only the transitive dependencies to be used as the dependencies // of installed files of this module. It will be replaced by a depset including the installed // files of this module at the end for use by modules that depend on this one. - m.installFilesDepSet = newInstallPathsDepSet(nil, dependencyInstallFiles) + m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, nil, dependencyInstallFiles) // Temporarily continue to call blueprintCtx.GetMissingDependencies() to maintain the previous behavior of never // reporting missing dependency errors in Blueprint when AllowMissingDependencies == true. @@ -2345,7 +1791,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) if !ctx.PrimaryArch() { suffix = append(suffix, ctx.Arch().ArchType.String()) } - if apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo); !apexInfo.IsForPlatform() { + if apexInfo, _ := ModuleProvider(ctx, ApexInfoProvider); !apexInfo.IsForPlatform() { suffix = append(suffix, apexInfo.ApexVariationName) } @@ -2363,38 +1809,116 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) checkDistProperties(ctx, fmt.Sprintf("dists[%d]", i), &m.distProperties.Dists[i]) } - if m.Enabled() { + if m.Enabled(ctx) { // ensure all direct android.Module deps are enabled ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) { if m, ok := bm.(Module); ok { - ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps) + ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps, false) } }) + if m.Device() { + // Handle any init.rc and vintf fragment files requested by the module. All files installed by this + // module will automatically have a dependency on the installed init.rc or vintf fragment file. + // The same init.rc or vintf fragment file may be requested by multiple modules or variants, + // so instead of installing them now just compute the install path and store it for later. + // The full list of all init.rc and vintf fragment install rules will be deduplicated later + // so only a single rule is created for each init.rc or vintf fragment file. + + if !m.InVendorRamdisk() { + m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc) + rcDir := PathForModuleInstall(ctx, "etc", "init") + for _, src := range m.initRcPaths { + installedInitRc := rcDir.Join(ctx, src.Base()) + m.katiInitRcInstalls = append(m.katiInitRcInstalls, katiInstall{ + from: src, + to: installedInitRc, + }) + ctx.PackageFile(rcDir, src.Base(), src) + m.installedInitRcPaths = append(m.installedInitRcPaths, installedInitRc) + } + } + + m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments) + vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest") + for _, src := range m.vintfFragmentsPaths { + installedVintfFragment := vintfDir.Join(ctx, src.Base()) + m.katiVintfInstalls = append(m.katiVintfInstalls, katiInstall{ + from: src, + to: installedVintfFragment, + }) + ctx.PackageFile(vintfDir, src.Base(), src) + m.installedVintfFragmentsPaths = append(m.installedVintfFragmentsPaths, installedVintfFragment) + } + } + licensesPropertyFlattener(ctx) if ctx.Failed() { return } - if mixedBuildMod, handled := m.isHandledByBazel(ctx); handled { - mixedBuildMod.ProcessBazelQueryResponse(ctx) - } else { - m.module.GenerateAndroidBuildActions(ctx) + if jarJarPrefixHandler != nil { + jarJarPrefixHandler(ctx) + if ctx.Failed() { + return + } } + + // Call aconfigUpdateAndroidBuildActions to collect merged aconfig files before being used + // in m.module.GenerateAndroidBuildActions + aconfigUpdateAndroidBuildActions(ctx) if ctx.Failed() { return } - m.initRcPaths = PathsForModuleSrc(ctx, m.commonProperties.Init_rc) - rcDir := PathForModuleInstall(ctx, "etc", "init") - for _, src := range m.initRcPaths { - ctx.PackageFile(rcDir, filepath.Base(src.String()), src) + incrementalAnalysis := false + incrementalEnabled := false + var cacheKey *blueprint.BuildActionCacheKey = nil + var incrementalModule *blueprint.Incremental = nil + if ctx.bp.GetIncrementalEnabled() { + if im, ok := m.module.(blueprint.Incremental); ok { + incrementalModule = &im + incrementalEnabled = im.IncrementalSupported() + incrementalAnalysis = ctx.bp.GetIncrementalAnalysis() && incrementalEnabled + } + } + if incrementalEnabled { + hash, err := proptools.CalculateHash(m.GetProperties()) + if err != nil { + ctx.ModuleErrorf("failed to calculate properties hash: %s", err) + return + } + cacheInput := new(blueprint.BuildActionCacheInput) + cacheInput.PropertiesHash = hash + ctx.VisitDirectDeps(func(module Module) { + cacheInput.ProvidersHash = + append(cacheInput.ProvidersHash, ctx.bp.OtherModuleProviderInitialValueHashes(module)) + }) + hash, err = proptools.CalculateHash(&cacheInput) + if err != nil { + ctx.ModuleErrorf("failed to calculate cache input hash: %s", err) + return + } + cacheKey = &blueprint.BuildActionCacheKey{ + Id: ctx.bp.ModuleId(), + InputHash: hash, + } + } + + restored := false + if incrementalAnalysis && cacheKey != nil { + restored = ctx.bp.RestoreBuildActions(cacheKey, incrementalModule) } - m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments) - vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest") - for _, src := range m.vintfFragmentsPaths { - ctx.PackageFile(vintfDir, filepath.Base(src.String()), src) + if !restored { + m.module.GenerateAndroidBuildActions(ctx) + if ctx.Failed() { + return + } + } + + if incrementalEnabled && cacheKey != nil { + ctx.bp.CacheBuildActions(cacheKey, incrementalModule) } // Create the set of tagged dist files after calling GenerateAndroidBuildActions @@ -2411,6 +1935,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...) m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...) m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...) + m.testData = append(m.testData, ctx.testData...) } else if ctx.Config().AllowMissingDependencies() { // If the module is not enabled it will not create any build rules, nothing will call // ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled @@ -2426,23 +1951,107 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) } } - m.installFilesDepSet = newInstallPathsDepSet(m.installFiles, dependencyInstallFiles) - m.packagingSpecsDepSet = newPackagingSpecsDepSet(m.packagingSpecs, dependencyPackagingSpecs) + m.installFilesDepSet = NewDepSet[InstallPath](TOPOLOGICAL, m.installFiles, dependencyInstallFiles) + m.packagingSpecsDepSet = NewDepSet[PackagingSpec](TOPOLOGICAL, m.packagingSpecs, dependencyPackagingSpecs) buildLicenseMetadata(ctx, m.licenseMetadataFile) + if m.moduleInfoJSON != nil { + var installed InstallPaths + installed = append(installed, m.katiInstalls.InstallPaths()...) + installed = append(installed, m.katiSymlinks.InstallPaths()...) + installed = append(installed, m.katiInitRcInstalls.InstallPaths()...) + installed = append(installed, m.katiVintfInstalls.InstallPaths()...) + installedStrings := installed.Strings() + + var targetRequired, hostRequired []string + if ctx.Host() { + targetRequired = m.commonProperties.Target_required + } else { + hostRequired = m.commonProperties.Host_required + } + + var data []string + for _, d := range m.testData { + data = append(data, d.ToRelativeInstallPath()) + } + + if m.moduleInfoJSON.Uninstallable { + installedStrings = nil + if len(m.moduleInfoJSON.CompatibilitySuites) == 1 && m.moduleInfoJSON.CompatibilitySuites[0] == "null-suite" { + m.moduleInfoJSON.CompatibilitySuites = nil + m.moduleInfoJSON.TestConfig = nil + m.moduleInfoJSON.AutoTestConfig = nil + data = nil + } + } + + m.moduleInfoJSON.core = CoreModuleInfoJSON{ + RegisterName: m.moduleInfoRegisterName(ctx, m.moduleInfoJSON.SubName), + Path: []string{ctx.ModuleDir()}, + Installed: installedStrings, + ModuleName: m.BaseModuleName() + m.moduleInfoJSON.SubName, + SupportedVariants: []string{m.moduleInfoVariant(ctx)}, + TargetDependencies: targetRequired, + HostDependencies: hostRequired, + Data: data, + Required: m.RequiredModuleNames(ctx), + } + SetProvider(ctx, ModuleInfoJSONProvider, m.moduleInfoJSON) + } + m.buildParams = ctx.buildParams m.ruleParams = ctx.ruleParams m.variables = ctx.variables + + if m.outputFiles.DefaultOutputFiles != nil || m.outputFiles.TaggedOutputFiles != nil { + SetProvider(ctx, OutputFilesProvider, m.outputFiles) + } + + buildComplianceMetadataProvider(ctx, m) } -func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) { - if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok { - if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) { - return mixedBuildMod, true +func SetJarJarPrefixHandler(handler func(ModuleContext)) { + if jarJarPrefixHandler != nil { + panic("jarJarPrefixHandler already set") + } + jarJarPrefixHandler = handler +} + +func (m *ModuleBase) moduleInfoRegisterName(ctx ModuleContext, subName string) string { + name := m.BaseModuleName() + + prefix := "" + if ctx.Host() { + if ctx.Os() != ctx.Config().BuildOS { + prefix = "host_cross_" } } - return nil, false + suffix := "" + arches := slices.Clone(ctx.Config().Targets[ctx.Os()]) + arches = slices.DeleteFunc(arches, func(target Target) bool { + return target.NativeBridge != ctx.Target().NativeBridge + }) + if len(arches) > 0 && ctx.Arch().ArchType != arches[0].Arch.ArchType { + if ctx.Arch().ArchType.Multilib == "lib32" { + suffix = "_32" + } else { + suffix = "_64" + } + } + return prefix + name + subName + suffix +} + +func (m *ModuleBase) moduleInfoVariant(ctx ModuleContext) string { + variant := "DEVICE" + if ctx.Host() { + if ctx.Os() != ctx.Config().BuildOS { + variant = "HOST_CROSS" + } else { + variant = "HOST" + } + } + return variant } // Check the supplied dist structure to make sure that it is valid. @@ -2471,163 +2080,6 @@ func checkDistProperties(ctx *moduleContext, property string, dist *Dist) { } -type earlyModuleContext struct { - blueprint.EarlyModuleContext - - kind moduleKind - config Config -} - -func (e *earlyModuleContext) Glob(globPattern string, excludes []string) Paths { - return Glob(e, globPattern, excludes) -} - -func (e *earlyModuleContext) GlobFiles(globPattern string, excludes []string) Paths { - return GlobFiles(e, globPattern, excludes) -} - -func (e *earlyModuleContext) IsSymlink(path Path) bool { - fileInfo, err := e.config.fs.Lstat(path.String()) - if err != nil { - e.ModuleErrorf("os.Lstat(%q) failed: %s", path.String(), err) - } - return fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink -} - -func (e *earlyModuleContext) Readlink(path Path) string { - dest, err := e.config.fs.Readlink(path.String()) - if err != nil { - e.ModuleErrorf("os.Readlink(%q) failed: %s", path.String(), err) - } - return dest -} - -func (e *earlyModuleContext) Module() Module { - module, _ := e.EarlyModuleContext.Module().(Module) - return module -} - -func (e *earlyModuleContext) Config() Config { - return e.EarlyModuleContext.Config().(Config) -} - -func (e *earlyModuleContext) AConfig() Config { - return e.config -} - -func (e *earlyModuleContext) DeviceConfig() DeviceConfig { - return DeviceConfig{e.config.deviceConfig} -} - -func (e *earlyModuleContext) Platform() bool { - return e.kind == platformModule -} - -func (e *earlyModuleContext) DeviceSpecific() bool { - return e.kind == deviceSpecificModule -} - -func (e *earlyModuleContext) SocSpecific() bool { - return e.kind == socSpecificModule -} - -func (e *earlyModuleContext) ProductSpecific() bool { - return e.kind == productSpecificModule -} - -func (e *earlyModuleContext) SystemExtSpecific() bool { - return e.kind == systemExtSpecificModule -} - -func (e *earlyModuleContext) Namespace() *Namespace { - return e.EarlyModuleContext.Namespace().(*Namespace) -} - -type baseModuleContext struct { - bp blueprint.BaseModuleContext - earlyModuleContext - os OsType - target Target - multiTargets []Target - targetPrimary bool - debug bool - - walkPath []Module - tagPath []blueprint.DependencyTag - - strictVisitDeps bool // If true, enforce that all dependencies are enabled - - bazelConversionMode bool -} - -func (b *baseModuleContext) isBazelConversionMode() bool { - return b.bazelConversionMode -} -func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string { - return b.bp.OtherModuleName(m) -} -func (b *baseModuleContext) OtherModuleDir(m blueprint.Module) string { return b.bp.OtherModuleDir(m) } -func (b *baseModuleContext) OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{}) { - b.bp.OtherModuleErrorf(m, fmt, args...) -} -func (b *baseModuleContext) OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag { - return b.bp.OtherModuleDependencyTag(m) -} -func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) } -func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool { - return b.bp.OtherModuleDependencyVariantExists(variations, name) -} -func (b *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool { - return b.bp.OtherModuleFarDependencyVariantExists(variations, name) -} -func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { - return b.bp.OtherModuleReverseDependencyVariantExists(name) -} -func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string { - return b.bp.OtherModuleType(m) -} -func (b *baseModuleContext) OtherModuleProvider(m blueprint.Module, provider blueprint.ProviderKey) interface{} { - return b.bp.OtherModuleProvider(m, provider) -} -func (b *baseModuleContext) OtherModuleHasProvider(m blueprint.Module, provider blueprint.ProviderKey) bool { - return b.bp.OtherModuleHasProvider(m, provider) -} -func (b *baseModuleContext) Provider(provider blueprint.ProviderKey) interface{} { - return b.bp.Provider(provider) -} -func (b *baseModuleContext) HasProvider(provider blueprint.ProviderKey) bool { - return b.bp.HasProvider(provider) -} -func (b *baseModuleContext) SetProvider(provider blueprint.ProviderKey, value interface{}) { - b.bp.SetProvider(provider, value) -} - -func (b *baseModuleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { - return b.bp.GetDirectDepWithTag(name, tag) -} - -func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext { - return b.bp -} - -type moduleContext struct { - bp blueprint.ModuleContext - baseModuleContext - packagingSpecs []PackagingSpec - installFiles InstallPaths - checkbuildFiles Paths - module Module - phonies map[string]Paths - - katiInstalls []katiInstall - katiSymlinks []katiInstall - - // For tests - buildParams []BuildParams - ruleParams map[blueprint.Rule]blueprint.RuleParams - variables map[string]string -} - // katiInstall stores a request from Soong to Make to create an install rule. type katiInstall struct { from Path @@ -2671,499 +2123,6 @@ func (installs katiInstalls) InstallPaths() InstallPaths { return paths } -func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) { - return pctx, BuildParams{ - Rule: ErrorRule, - Description: params.Description, - Output: params.Output, - Outputs: params.Outputs, - ImplicitOutput: params.ImplicitOutput, - ImplicitOutputs: params.ImplicitOutputs, - Args: map[string]string{ - "error": err.Error(), - }, - } -} - -func (m *moduleContext) ModuleBuild(pctx PackageContext, params ModuleBuildParams) { - m.Build(pctx, BuildParams(params)) -} - -func validateBuildParams(params blueprint.BuildParams) error { - // Validate that the symlink outputs are declared outputs or implicit outputs - allOutputs := map[string]bool{} - for _, output := range params.Outputs { - allOutputs[output] = true - } - for _, output := range params.ImplicitOutputs { - allOutputs[output] = true - } - for _, symlinkOutput := range params.SymlinkOutputs { - if !allOutputs[symlinkOutput] { - return fmt.Errorf( - "Symlink output %s is not a declared output or implicit output", - symlinkOutput) - } - } - return nil -} - -// Convert build parameters from their concrete Android types into their string representations, -// and combine the singular and plural fields of the same type (e.g. Output and Outputs). -func convertBuildParams(params BuildParams) blueprint.BuildParams { - bparams := blueprint.BuildParams{ - Rule: params.Rule, - Description: params.Description, - Deps: params.Deps, - Outputs: params.Outputs.Strings(), - ImplicitOutputs: params.ImplicitOutputs.Strings(), - SymlinkOutputs: params.SymlinkOutputs.Strings(), - Inputs: params.Inputs.Strings(), - Implicits: params.Implicits.Strings(), - OrderOnly: params.OrderOnly.Strings(), - Validations: params.Validations.Strings(), - Args: params.Args, - Optional: !params.Default, - } - - if params.Depfile != nil { - bparams.Depfile = params.Depfile.String() - } - if params.Output != nil { - bparams.Outputs = append(bparams.Outputs, params.Output.String()) - } - if params.SymlinkOutput != nil { - bparams.SymlinkOutputs = append(bparams.SymlinkOutputs, params.SymlinkOutput.String()) - } - if params.ImplicitOutput != nil { - bparams.ImplicitOutputs = append(bparams.ImplicitOutputs, params.ImplicitOutput.String()) - } - if params.Input != nil { - bparams.Inputs = append(bparams.Inputs, params.Input.String()) - } - if params.Implicit != nil { - bparams.Implicits = append(bparams.Implicits, params.Implicit.String()) - } - if params.Validation != nil { - bparams.Validations = append(bparams.Validations, params.Validation.String()) - } - - bparams.Outputs = proptools.NinjaEscapeList(bparams.Outputs) - bparams.ImplicitOutputs = proptools.NinjaEscapeList(bparams.ImplicitOutputs) - bparams.SymlinkOutputs = proptools.NinjaEscapeList(bparams.SymlinkOutputs) - bparams.Inputs = proptools.NinjaEscapeList(bparams.Inputs) - bparams.Implicits = proptools.NinjaEscapeList(bparams.Implicits) - bparams.OrderOnly = proptools.NinjaEscapeList(bparams.OrderOnly) - bparams.Validations = proptools.NinjaEscapeList(bparams.Validations) - bparams.Depfile = proptools.NinjaEscape(bparams.Depfile) - - return bparams -} - -func (m *moduleContext) Variable(pctx PackageContext, name, value string) { - if m.config.captureBuild { - m.variables[name] = value - } - - m.bp.Variable(pctx.PackageContext, name, value) -} - -func (m *moduleContext) Rule(pctx PackageContext, name string, params blueprint.RuleParams, - argNames ...string) blueprint.Rule { - - if m.config.UseRemoteBuild() { - if params.Pool == nil { - // When USE_GOMA=true or USE_RBE=true are set and the rule is not supported by goma/RBE, restrict - // jobs to the local parallelism value - params.Pool = localPool - } else if params.Pool == remotePool { - // remotePool is a fake pool used to identify rule that are supported for remoting. If the rule's - // pool is the remotePool, replace with nil so that ninja runs it at NINJA_REMOTE_NUM_JOBS - // parallelism. - params.Pool = nil - } - } - - rule := m.bp.Rule(pctx.PackageContext, name, params, argNames...) - - if m.config.captureBuild { - m.ruleParams[rule] = params - } - - return rule -} - -func (m *moduleContext) Build(pctx PackageContext, params BuildParams) { - if params.Description != "" { - params.Description = "${moduleDesc}" + params.Description + "${moduleDescSuffix}" - } - - if missingDeps := m.GetMissingDependencies(); len(missingDeps) > 0 { - pctx, params = m.ninjaError(params, fmt.Errorf("module %s missing dependencies: %s\n", - m.ModuleName(), strings.Join(missingDeps, ", "))) - } - - if m.config.captureBuild { - m.buildParams = append(m.buildParams, params) - } - - bparams := convertBuildParams(params) - err := validateBuildParams(bparams) - if err != nil { - m.ModuleErrorf( - "%s: build parameter validation failed: %s", - m.ModuleName(), - err.Error()) - } - m.bp.Build(pctx.PackageContext, bparams) -} - -func (m *moduleContext) Phony(name string, deps ...Path) { - addPhony(m.config, name, deps...) -} - -func (m *moduleContext) GetMissingDependencies() []string { - var missingDeps []string - missingDeps = append(missingDeps, m.Module().base().commonProperties.MissingDeps...) - missingDeps = append(missingDeps, m.bp.GetMissingDependencies()...) - missingDeps = FirstUniqueStrings(missingDeps) - return missingDeps -} - -func (b *baseModuleContext) AddMissingDependencies(deps []string) { - if deps != nil { - missingDeps := &b.Module().base().commonProperties.MissingDeps - *missingDeps = append(*missingDeps, deps...) - *missingDeps = FirstUniqueStrings(*missingDeps) - } -} - -type AllowDisabledModuleDependency interface { - blueprint.DependencyTag - AllowDisabledModuleDependency(target Module) bool -} - -func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module { - aModule, _ := module.(Module) - - if !strict { - return aModule - } - - if aModule == nil { - b.ModuleErrorf("module %q (%#v) not an android module", b.OtherModuleName(module), tag) - return nil - } - - if !aModule.Enabled() { - if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) { - if b.Config().AllowMissingDependencies() { - b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) - } else { - b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) - } - } - return nil - } - return aModule -} - -type dep struct { - mod blueprint.Module - tag blueprint.DependencyTag -} - -func (b *baseModuleContext) getDirectDepsInternal(name string, tag blueprint.DependencyTag) []dep { - var deps []dep - b.VisitDirectDepsBlueprint(func(module blueprint.Module) { - if aModule, _ := module.(Module); aModule != nil { - if aModule.base().BaseModuleName() == name { - returnedTag := b.bp.OtherModuleDependencyTag(aModule) - if tag == nil || returnedTag == tag { - deps = append(deps, dep{aModule, returnedTag}) - } - } - } else if b.bp.OtherModuleName(module) == name { - returnedTag := b.bp.OtherModuleDependencyTag(module) - if tag == nil || returnedTag == tag { - deps = append(deps, dep{module, returnedTag}) - } - } - }) - return deps -} - -func (b *baseModuleContext) getDirectDepInternal(name string, tag blueprint.DependencyTag) (blueprint.Module, blueprint.DependencyTag) { - deps := b.getDirectDepsInternal(name, tag) - if len(deps) == 1 { - return deps[0].mod, deps[0].tag - } else if len(deps) >= 2 { - panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", - name, b.ModuleName())) - } else { - return nil, nil - } -} - -func (b *baseModuleContext) getDirectDepFirstTag(name string) (blueprint.Module, blueprint.DependencyTag) { - foundDeps := b.getDirectDepsInternal(name, nil) - deps := map[blueprint.Module]bool{} - for _, dep := range foundDeps { - deps[dep.mod] = true - } - if len(deps) == 1 { - return foundDeps[0].mod, foundDeps[0].tag - } else if len(deps) >= 2 { - // this could happen if two dependencies have the same name in different namespaces - // TODO(b/186554727): this should not occur if namespaces are handled within - // getDirectDepsInternal. - panic(fmt.Errorf("Multiple dependencies having same BaseModuleName() %q found from %q", - name, b.ModuleName())) - } else { - return nil, nil - } -} - -func (b *baseModuleContext) GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module { - var deps []Module - b.VisitDirectDepsBlueprint(func(module blueprint.Module) { - if aModule, _ := module.(Module); aModule != nil { - if b.bp.OtherModuleDependencyTag(aModule) == tag { - deps = append(deps, aModule) - } - } - }) - return deps -} - -func (m *moduleContext) GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module { - module, _ := m.getDirectDepInternal(name, tag) - return module -} - -// GetDirectDep returns the Module and DependencyTag for the direct dependency with the specified -// name, or nil if none exists. If there are multiple dependencies on the same module it returns the -// first DependencyTag. -func (b *baseModuleContext) GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag) { - return b.getDirectDepFirstTag(name) -} - -func (b *baseModuleContext) ModuleFromName(name string) (blueprint.Module, bool) { - if !b.isBazelConversionMode() { - panic("cannot call ModuleFromName if not in bazel conversion mode") - } - if moduleName, _ := SrcIsModuleWithTag(name); moduleName != "" { - return b.bp.ModuleFromName(moduleName) - } else { - return b.bp.ModuleFromName(name) - } -} - -func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) { - b.bp.VisitDirectDeps(visit) -} - -func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { - b.bp.VisitDirectDeps(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - }) -} - -func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { - b.bp.VisitDirectDeps(func(module blueprint.Module) { - if b.bp.OtherModuleDependencyTag(module) == tag { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - } - }) -} - -func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func(Module)) { - b.bp.VisitDirectDepsIf( - // pred - func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - return pred(aModule) - } else { - return false - } - }, - // visit - func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { - b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - visit(aModule) - } - }) -} - -func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit func(Module)) { - b.bp.VisitDepsDepthFirstIf( - // pred - func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { - return pred(aModule) - } else { - return false - } - }, - // visit - func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) WalkDepsBlueprint(visit func(blueprint.Module, blueprint.Module) bool) { - b.bp.WalkDeps(visit) -} - -func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) { - b.walkPath = []Module{b.Module()} - b.tagPath = []blueprint.DependencyTag{} - b.bp.WalkDeps(func(child, parent blueprint.Module) bool { - childAndroidModule, _ := child.(Module) - parentAndroidModule, _ := parent.(Module) - if childAndroidModule != nil && parentAndroidModule != nil { - // record walkPath before visit - for b.walkPath[len(b.walkPath)-1] != parentAndroidModule { - b.walkPath = b.walkPath[0 : len(b.walkPath)-1] - b.tagPath = b.tagPath[0 : len(b.tagPath)-1] - } - b.walkPath = append(b.walkPath, childAndroidModule) - b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule)) - return visit(childAndroidModule, parentAndroidModule) - } else { - return false - } - }) -} - -func (b *baseModuleContext) GetWalkPath() []Module { - return b.walkPath -} - -func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { - return b.tagPath -} - -func (b *baseModuleContext) VisitAllModuleVariants(visit func(Module)) { - b.bp.VisitAllModuleVariants(func(module blueprint.Module) { - visit(module.(Module)) - }) -} - -func (b *baseModuleContext) PrimaryModule() Module { - return b.bp.PrimaryModule().(Module) -} - -func (b *baseModuleContext) FinalModule() Module { - return b.bp.FinalModule().(Module) -} - -// IsMetaDependencyTag returns true for cross-cutting metadata dependencies. -func IsMetaDependencyTag(tag blueprint.DependencyTag) bool { - if tag == licenseKindTag { - return true - } else if tag == licensesTag { - return true - } - return false -} - -// A regexp for removing boilerplate from BaseDependencyTag from the string representation of -// a dependency tag. -var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:{}\E(, )?`) - -// PrettyPrintTag returns string representation of the tag, but prefers -// custom String() method if available. -func PrettyPrintTag(tag blueprint.DependencyTag) string { - // Use tag's custom String() method if available. - if stringer, ok := tag.(fmt.Stringer); ok { - return stringer.String() - } - - // Otherwise, get a default string representation of the tag's struct. - tagString := fmt.Sprintf("%T: %+v", tag, tag) - - // Remove the boilerplate from BaseDependencyTag as it adds no value. - tagString = tagCleaner.ReplaceAllString(tagString, "") - return tagString -} - -func (b *baseModuleContext) GetPathString(skipFirst bool) string { - sb := strings.Builder{} - tagPath := b.GetTagPath() - walkPath := b.GetWalkPath() - if !skipFirst { - sb.WriteString(walkPath[0].String()) - } - for i, m := range walkPath[1:] { - sb.WriteString("\n") - sb.WriteString(fmt.Sprintf(" via tag %s\n", PrettyPrintTag(tagPath[i]))) - sb.WriteString(fmt.Sprintf(" -> %s", m.String())) - } - return sb.String() -} - -func (m *moduleContext) ModuleSubDir() string { - return m.bp.ModuleSubDir() -} - -func (b *baseModuleContext) Target() Target { - return b.target -} - -func (b *baseModuleContext) TargetPrimary() bool { - return b.targetPrimary -} - -func (b *baseModuleContext) MultiTargets() []Target { - return b.multiTargets -} - -func (b *baseModuleContext) Arch() Arch { - return b.target.Arch -} - -func (b *baseModuleContext) Os() OsType { - return b.os -} - -func (b *baseModuleContext) Host() bool { - return b.os.Class == Host -} - -func (b *baseModuleContext) Device() bool { - return b.os.Class == Device -} - -func (b *baseModuleContext) Darwin() bool { - return b.os == Darwin -} - -func (b *baseModuleContext) Windows() bool { - return b.os == Windows -} - -func (b *baseModuleContext) Debug() bool { - return b.debug -} - -func (b *baseModuleContext) PrimaryArch() bool { - if len(b.config.Targets[b.target.Os]) <= 1 { - return true - } - return b.target.Arch.ArchType == b.config.Targets[b.target.Os][0].Arch.ArchType -} - // Makes this module a platform module, i.e. not specific to soc, device, // product, or system_ext. func (m *ModuleBase) MakeAsPlatform() { @@ -3187,272 +2146,149 @@ func (m *ModuleBase) IsNativeBridgeSupported() bool { return proptools.Bool(m.commonProperties.Native_bridge_supported) } -func (m *moduleContext) InstallInData() bool { - return m.module.InstallInData() -} - -func (m *moduleContext) InstallInTestcases() bool { - return m.module.InstallInTestcases() -} - -func (m *moduleContext) InstallInSanitizerDir() bool { - return m.module.InstallInSanitizerDir() -} - -func (m *moduleContext) InstallInRamdisk() bool { - return m.module.InstallInRamdisk() -} - -func (m *moduleContext) InstallInVendorRamdisk() bool { - return m.module.InstallInVendorRamdisk() -} - -func (m *moduleContext) InstallInDebugRamdisk() bool { - return m.module.InstallInDebugRamdisk() -} - -func (m *moduleContext) InstallInRecovery() bool { - return m.module.InstallInRecovery() -} - -func (m *moduleContext) InstallInRoot() bool { - return m.module.InstallInRoot() -} - -func (m *moduleContext) InstallForceOS() (*OsType, *ArchType) { - return m.module.InstallForceOS() +type ConfigAndErrorContext interface { + Config() Config + OtherModulePropertyErrorf(module Module, property string, fmt string, args ...interface{}) } -func (m *moduleContext) InstallInVendor() bool { - return m.module.InstallInVendor() +type configurationEvalutor struct { + ctx ConfigAndErrorContext + m Module } -func (m *moduleContext) skipInstall() bool { - if m.module.base().commonProperties.SkipInstall { - return true - } - - if m.module.base().commonProperties.HideFromMake { - return true - } - - // We'll need a solution for choosing which of modules with the same name in different - // namespaces to install. For now, reuse the list of namespaces exported to Make as the - // list of namespaces to install in a Soong-only build. - if !m.module.base().commonProperties.NamespaceExportedToMake { - return true +func (m *ModuleBase) ConfigurableEvaluator(ctx ConfigAndErrorContext) proptools.ConfigurableEvaluator { + return configurationEvalutor{ + ctx: ctx, + m: m.module, } - - return false } -func (m *moduleContext) InstallFile(installPath InstallPath, name string, srcPath Path, - deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, false, nil) +func (e configurationEvalutor) PropertyErrorf(property string, fmt string, args ...interface{}) { + e.ctx.OtherModulePropertyErrorf(e.m, property, fmt, args...) } -func (m *moduleContext) InstallExecutable(installPath InstallPath, name string, srcPath Path, - deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, true, nil) -} - -func (m *moduleContext) InstallFileWithExtraFilesZip(installPath InstallPath, name string, srcPath Path, - extraZip Path, deps ...Path) InstallPath { - return m.installFile(installPath, name, srcPath, deps, false, &extraFilesZip{ - zip: extraZip, - dir: installPath, - }) -} - -func (m *moduleContext) PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec { - fullInstallPath := installPath.Join(m, name) - return m.packageFile(fullInstallPath, srcPath, false) -} - -func (m *moduleContext) packageFile(fullInstallPath InstallPath, srcPath Path, executable bool) PackagingSpec { - licenseFiles := m.Module().EffectiveLicenseFiles() - spec := PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: srcPath, - symlinkTarget: "", - executable: executable, - effectiveLicenseFiles: &licenseFiles, - partition: fullInstallPath.partition, - } - m.packagingSpecs = append(m.packagingSpecs, spec) - return spec -} - -func (m *moduleContext) installFile(installPath InstallPath, name string, srcPath Path, deps []Path, - executable bool, extraZip *extraFilesZip) InstallPath { - - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false) - - if !m.skipInstall() { - deps = append(deps, m.module.base().installFilesDepSet.ToList().Paths()...) - - var implicitDeps, orderOnlyDeps Paths - - if m.Host() { - // Installed host modules might be used during the build, depend directly on their - // dependencies so their timestamp is updated whenever their dependency is updated - implicitDeps = deps - } else { - orderOnlyDeps = deps - } - - if m.Config().KatiEnabled() { - // When creating the install rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiInstalls = append(m.katiInstalls, katiInstall{ - from: srcPath, - to: fullInstallPath, - implicitDeps: implicitDeps, - orderOnlyDeps: orderOnlyDeps, - executable: executable, - extraFiles: extraZip, - }) - } else { - rule := Cp - if executable { - rule = CpExecutable +func (e configurationEvalutor) EvaluateConfiguration(condition proptools.ConfigurableCondition, property string) proptools.ConfigurableValue { + ctx := e.ctx + m := e.m + switch condition.FunctionName() { + case "release_flag": + if condition.NumArgs() != 1 { + ctx.OtherModulePropertyErrorf(m, property, "release_flag requires 1 argument, found %d", condition.NumArgs()) + return proptools.ConfigurableValueUndefined() + } + if ty, ok := ctx.Config().productVariables.BuildFlagTypes[condition.Arg(0)]; ok { + v := ctx.Config().productVariables.BuildFlags[condition.Arg(0)] + switch ty { + case "unspecified", "obsolete": + return proptools.ConfigurableValueUndefined() + case "string": + return proptools.ConfigurableValueString(v) + case "bool": + return proptools.ConfigurableValueBool(v == "true") + default: + panic("unhandled release flag type: " + ty) } + } + return proptools.ConfigurableValueUndefined() + case "product_variable": + if condition.NumArgs() != 1 { + ctx.OtherModulePropertyErrorf(m, property, "product_variable requires 1 argument, found %d", condition.NumArgs()) + return proptools.ConfigurableValueUndefined() + } + variable := condition.Arg(0) + switch variable { + case "debuggable": + return proptools.ConfigurableValueBool(ctx.Config().Debuggable()) + default: + // TODO(b/323382414): Might add these on a case-by-case basis + ctx.OtherModulePropertyErrorf(m, property, fmt.Sprintf("TODO(b/323382414): Product variable %q is not yet supported in selects", variable)) + return proptools.ConfigurableValueUndefined() + } + case "soong_config_variable": + if condition.NumArgs() != 2 { + ctx.OtherModulePropertyErrorf(m, property, "soong_config_variable requires 2 arguments, found %d", condition.NumArgs()) + return proptools.ConfigurableValueUndefined() + } + namespace := condition.Arg(0) + variable := condition.Arg(1) + if n, ok := ctx.Config().productVariables.VendorVars[namespace]; ok { + if v, ok := n[variable]; ok { + ty := "" + if namespaces, ok := ctx.Config().productVariables.VendorVarTypes[namespace]; ok { + ty = namespaces[variable] + } + switch ty { + case "": + // strings are the default, we don't bother writing them to the soong variables json file + return proptools.ConfigurableValueString(v) + case "bool": + return proptools.ConfigurableValueBool(v == "true") + default: + panic("unhandled soong config variable type: " + ty) + } - extraCmds := "" - if extraZip != nil { - extraCmds += fmt.Sprintf(" && ( unzip -qDD -d '%s' '%s' 2>&1 | grep -v \"zipfile is empty\"; exit $${PIPESTATUS[0]} )", - extraZip.dir.String(), extraZip.zip.String()) - extraCmds += " || ( code=$$?; if [ $$code -ne 0 -a $$code -ne 1 ]; then exit $$code; fi )" - implicitDeps = append(implicitDeps, extraZip.zip) } - - m.Build(pctx, BuildParams{ - Rule: rule, - Description: "install " + fullInstallPath.Base(), - Output: fullInstallPath, - Input: srcPath, - Implicits: implicitDeps, - OrderOnly: orderOnlyDeps, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "extraCmds": extraCmds, - }, - }) } - - m.installFiles = append(m.installFiles, fullInstallPath) - } - - m.packageFile(fullInstallPath, srcPath, executable) - - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) - - return fullInstallPath -} - -func (m *moduleContext) InstallSymlink(installPath InstallPath, name string, srcPath InstallPath) InstallPath { - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, true) - - relPath, err := filepath.Rel(path.Dir(fullInstallPath.String()), srcPath.String()) - if err != nil { - panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err)) - } - if !m.skipInstall() { - - if m.Config().KatiEnabled() { - // When creating the symlink rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiSymlinks = append(m.katiSymlinks, katiInstall{ - from: srcPath, - to: fullInstallPath, - }) - } else { - // The symlink doesn't need updating when the target is modified, but we sometimes - // have a dependency on a symlink to a binary instead of to the binary directly, and - // the mtime of the symlink must be updated when the binary is modified, so use a - // normal dependency here instead of an order-only dependency. - m.Build(pctx, BuildParams{ - Rule: Symlink, - Description: "install symlink " + fullInstallPath.Base(), - Output: fullInstallPath, - Input: srcPath, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "fromPath": relPath, - }, - }) + return proptools.ConfigurableValueUndefined() + case "arch": + if condition.NumArgs() != 0 { + ctx.OtherModulePropertyErrorf(m, property, "arch requires no arguments, found %d", condition.NumArgs()) + return proptools.ConfigurableValueUndefined() + } + if !m.base().ArchReady() { + ctx.OtherModulePropertyErrorf(m, property, "A select on arch was attempted before the arch mutator ran") + return proptools.ConfigurableValueUndefined() + } + return proptools.ConfigurableValueString(m.base().Arch().ArchType.Name) + case "os": + if condition.NumArgs() != 0 { + ctx.OtherModulePropertyErrorf(m, property, "os requires no arguments, found %d", condition.NumArgs()) + return proptools.ConfigurableValueUndefined() + } + // the arch mutator runs after the os mutator, we can just use this to enforce that os is ready. + if !m.base().ArchReady() { + ctx.OtherModulePropertyErrorf(m, property, "A select on os was attempted before the arch mutator ran (arch runs after os, we use it to lazily detect that os is ready)") + return proptools.ConfigurableValueUndefined() + } + return proptools.ConfigurableValueString(m.base().Os().Name) + case "boolean_var_for_testing": + // We currently don't have any other boolean variables (we should add support for typing + // the soong config variables), so add this fake one for testing the boolean select + // functionality. + if condition.NumArgs() != 0 { + ctx.OtherModulePropertyErrorf(m, property, "boolean_var_for_testing requires 0 arguments, found %d", condition.NumArgs()) + return proptools.ConfigurableValueUndefined() + } + + if n, ok := ctx.Config().productVariables.VendorVars["boolean_var"]; ok { + if v, ok := n["for_testing"]; ok { + switch v { + case "true": + return proptools.ConfigurableValueBool(true) + case "false": + return proptools.ConfigurableValueBool(false) + default: + ctx.OtherModulePropertyErrorf(m, property, "testing:my_boolean_var can only be true or false, found %q", v) + } + } } - - m.installFiles = append(m.installFiles, fullInstallPath) - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) + return proptools.ConfigurableValueUndefined() + default: + ctx.OtherModulePropertyErrorf(m, property, "Unknown select condition %s", condition.FunctionName) + return proptools.ConfigurableValueUndefined() } - - m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: nil, - symlinkTarget: relPath, - executable: false, - partition: fullInstallPath.partition, - }) - - return fullInstallPath } -// installPath/name -> absPath where absPath might be a path that is available only at runtime -// (e.g. /apex/...) -func (m *moduleContext) InstallAbsoluteSymlink(installPath InstallPath, name string, absPath string) InstallPath { - fullInstallPath := installPath.Join(m, name) - m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true) - - if !m.skipInstall() { - if m.Config().KatiEnabled() { - // When creating the symlink rule in Soong but embedding in Make, write the rule to a - // makefile instead of directly to the ninja file so that main.mk can add the - // dependencies from the `required` property that are hard to resolve in Soong. - m.katiSymlinks = append(m.katiSymlinks, katiInstall{ - absFrom: absPath, - to: fullInstallPath, - }) - } else { - m.Build(pctx, BuildParams{ - Rule: Symlink, - Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath, - Output: fullInstallPath, - Default: !m.Config().KatiEnabled(), - Args: map[string]string{ - "fromPath": absPath, - }, - }) +// ModuleNameWithPossibleOverride returns the name of the OverrideModule that overrides the current +// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule +// or if this variant is not overridden. +func ModuleNameWithPossibleOverride(ctx BaseModuleContext) string { + if overridable, ok := ctx.Module().(OverridableModule); ok { + if o := overridable.GetOverriddenBy(); o != "" { + return o } - - m.installFiles = append(m.installFiles, fullInstallPath) } - - m.packagingSpecs = append(m.packagingSpecs, PackagingSpec{ - relPathInPackage: Rel(m, fullInstallPath.PartitionDir(), fullInstallPath.String()), - srcPath: nil, - symlinkTarget: absPath, - executable: false, - partition: fullInstallPath.partition, - }) - - return fullInstallPath -} - -func (m *moduleContext) CheckbuildFile(srcPath Path) { - m.checkbuildFiles = append(m.checkbuildFiles, srcPath) -} - -func (m *moduleContext) blueprintModuleContext() blueprint.ModuleContext { - return m.bp -} - -func (m *moduleContext) LicenseMetadataFile() Path { - return m.module.base().licenseMetadataFile + return ctx.ModuleName() } // SrcIsModule decodes module references in the format ":unqualified-name" or "//namespace:name" @@ -3522,11 +2358,12 @@ func isUnqualifiedModuleName(module string) bool { // caused by prebuilt_ prefix, or fully qualified module names. type sourceOrOutputDependencyTag struct { blueprint.BaseDependencyTag + AlwaysPropagateAconfigValidationDependencyTag // The name of the module. moduleName string - // The tag that will be passed to the module's OutputFileProducer.OutputFiles(tag) method. + // The tag that will be used to get the specific output file(s). tag string } @@ -3580,14 +2417,7 @@ type SourceFileProducer interface { Srcs() Paths } -// A module that implements OutputFileProducer can be referenced from any property that is tagged with `android:"path"` -// using the ":module" syntax or ":module{.tag}" syntax and provides a list of output files to be used as if they were -// listed in the property. -type OutputFileProducer interface { - OutputFiles(tag string) (Paths, error) -} - -// OutputFilesForModule returns the paths from an OutputFileProducer with the given tag. On error, including if the +// OutputFilesForModule returns the output file paths with the given tag. On error, including if the // module produced zero paths, it reports errors to the ctx and returns nil. func OutputFilesForModule(ctx PathContext, module blueprint.Module, tag string) Paths { paths, err := outputFilesForModule(ctx, module, tag) @@ -3598,7 +2428,7 @@ func OutputFilesForModule(ctx PathContext, module blueprint.Module, tag string) return paths } -// OutputFileForModule returns the path from an OutputFileProducer with the given tag. On error, including if the +// OutputFileForModule returns the output file paths with the given tag. On error, including if the // module produced zero or multiple paths, it reports errors to the ctx and returns nil. func OutputFileForModule(ctx PathContext, module blueprint.Module, tag string) Path { paths, err := outputFilesForModule(ctx, module, tag) @@ -3634,73 +2464,94 @@ func OutputFileForModule(ctx PathContext, module blueprint.Module, tag string) P } func outputFilesForModule(ctx PathContext, module blueprint.Module, tag string) (Paths, error) { - if outputFileProducer, ok := module.(OutputFileProducer); ok { - paths, err := outputFileProducer.OutputFiles(tag) - if err != nil { - return nil, fmt.Errorf("failed to get output file from module %q: %s", - pathContextName(ctx, module), err.Error()) - } - return paths, nil - } else if sourceFileProducer, ok := module.(SourceFileProducer); ok { + outputFilesFromProvider, err := outputFilesForModuleFromProvider(ctx, module, tag) + if outputFilesFromProvider != nil || err != OutputFilesProviderNotSet { + return outputFilesFromProvider, err + } + if sourceFileProducer, ok := module.(SourceFileProducer); ok { if tag != "" { - return nil, fmt.Errorf("module %q is a SourceFileProducer, not an OutputFileProducer, and so does not support tag %q", pathContextName(ctx, module), tag) + return nil, fmt.Errorf("module %q is a SourceFileProducer, which does not support tag %q", pathContextName(ctx, module), tag) } paths := sourceFileProducer.Srcs() return paths, nil } else { - return nil, fmt.Errorf("module %q is not an OutputFileProducer", pathContextName(ctx, module)) + return nil, fmt.Errorf("module %q is not a SourceFileProducer or having valid output file for tag %q", pathContextName(ctx, module), tag) } } -// Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to -// specify that they can be used as a tool by a genrule module. -type HostToolProvider interface { - Module - // HostToolPath returns the path to the host tool for the module if it is one, or an invalid - // OptionalPath. - HostToolPath() OptionalPath -} +// This method uses OutputFilesProvider for output files +// *inter-module-communication*. +// If mctx module is the same as the param module the output files are obtained +// from outputFiles property of module base, to avoid both setting and +// reading OutputFilesProvider before GenerateBuildActions is finished. +// If a module doesn't have the OutputFilesProvider, nil is returned. +func outputFilesForModuleFromProvider(ctx PathContext, module blueprint.Module, tag string) (Paths, error) { + var outputFiles OutputFilesInfo + fromProperty := false -// Returns a list of paths expanded from globs and modules referenced using ":module" syntax. The property must -// be tagged with `android:"path" to support automatic source module dependency resolution. -// -// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead. -func (m *moduleContext) ExpandSources(srcFiles, excludes []string) Paths { - return PathsForModuleSrcExcludes(m, srcFiles, excludes) -} + type OutputFilesProviderModuleContext interface { + OtherModuleProviderContext + Module() Module + } -// Returns a single path expanded from globs and modules referenced using ":module" syntax. The property must -// be tagged with `android:"path" to support automatic source module dependency resolution. -// -// Deprecated: use PathForModuleSrc instead. -func (m *moduleContext) ExpandSource(srcFile, _ string) Path { - return PathForModuleSrc(m, srcFile) -} + if mctx, isMctx := ctx.(OutputFilesProviderModuleContext); isMctx { + if mctx.Module() != module { + outputFiles, _ = OtherModuleProvider(mctx, module, OutputFilesProvider) + } else { + outputFiles = mctx.Module().base().outputFiles + fromProperty = true + } + } else if cta, isCta := ctx.(*singletonContextAdaptor); isCta { + providerData, _ := cta.moduleProvider(module, OutputFilesProvider) + outputFiles, _ = providerData.(OutputFilesInfo) + } + // TODO: Add a check for skipped context -// Returns an optional single path expanded from globs and modules referenced using ":module" syntax if -// the srcFile is non-nil. The property must be tagged with `android:"path" to support automatic source module -// dependency resolution. -func (m *moduleContext) ExpandOptionalSource(srcFile *string, _ string) OptionalPath { - if srcFile != nil { - return OptionalPathForPath(PathForModuleSrc(m, *srcFile)) + if outputFiles.isEmpty() { + return nil, OutputFilesProviderNotSet + } + + if tag == "" { + return outputFiles.DefaultOutputFiles, nil + } else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag { + return taggedOutputFiles, nil + } else { + if fromProperty { + return nil, fmt.Errorf("unsupported tag %q for module getting its own output files", tag) + } else { + return nil, fmt.Errorf("unsupported module reference tag %q", tag) + } } - return OptionalPath{} } -func (m *moduleContext) RequiredModuleNames() []string { - return m.module.RequiredModuleNames() +func (o OutputFilesInfo) isEmpty() bool { + return o.DefaultOutputFiles == nil && o.TaggedOutputFiles == nil } -func (m *moduleContext) HostRequiredModuleNames() []string { - return m.module.HostRequiredModuleNames() +type OutputFilesInfo struct { + // default output files when tag is an empty string "" + DefaultOutputFiles Paths + + // the corresponding output files for given tags + TaggedOutputFiles map[string]Paths } -func (m *moduleContext) TargetRequiredModuleNames() []string { - return m.module.TargetRequiredModuleNames() +var OutputFilesProvider = blueprint.NewProvider[OutputFilesInfo]() + +// This is used to mark the case where OutputFilesProvider is not set on some modules. +var OutputFilesProviderNotSet = fmt.Errorf("No output files from provider") + +// Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to +// specify that they can be used as a tool by a genrule module. +type HostToolProvider interface { + Module + // HostToolPath returns the path to the host tool for the module if it is one, or an invalid + // OptionalPath. + HostToolPath() OptionalPath } func init() { - RegisterSingletonType("buildtarget", BuildTargetSingleton) + RegisterParallelSingletonType("buildtarget", BuildTargetSingleton) } func BuildTargetSingleton() Singleton { @@ -3795,7 +2646,7 @@ func (c *buildTargetSingleton) GenerateBuildActions(ctx SingletonContext) { } osDeps := map[osAndCross]Paths{} ctx.VisitAllModules(func(module Module) { - if module.Enabled() { + if module.Enabled(ctx) { key := osAndCross{os: module.Target().Os, hostCross: module.Target().HostCross} osDeps[key] = append(osDeps[key], module.base().checkbuildFiles...) } @@ -3862,23 +2713,3 @@ func CheckBlueprintSyntax(ctx BaseModuleContext, filename string, contents strin bpctx := ctx.blueprintBaseModuleContext() return blueprint.CheckBlueprintSyntax(bpctx.ModuleFactories(), filename, contents) } - -// installPathsDepSet is a thin type-safe wrapper around the generic depSet. It always uses -// topological order. -type installPathsDepSet struct { - depSet -} - -// newInstallPathsDepSet returns an immutable packagingSpecsDepSet with the given direct and -// transitive contents. -func newInstallPathsDepSet(direct InstallPaths, transitive []*installPathsDepSet) *installPathsDepSet { - return &installPathsDepSet{*newDepSet(TOPOLOGICAL, direct, transitive)} -} - -// ToList returns the installPathsDepSet flattened to a list in topological order. -func (d *installPathsDepSet) ToList() InstallPaths { - if d == nil { - return nil - } - return d.depSet.ToList().(InstallPaths) -} |