Merge "cc: use platform sdk version for vendor/product variants" into main
diff --git a/Android.bp b/Android.bp
index cbe1c7b..42b7d83 100644
--- a/Android.bp
+++ b/Android.bp
@@ -206,3 +206,30 @@
relative_install_path: "etc", // odm/etc/build.prop
visibility: ["//visibility:private"],
}
+
+build_prop {
+ name: "system_dlkm-build.prop",
+ stem: "build.prop",
+ system_dlkm_specific: true,
+ product_config: ":product_config",
+ relative_install_path: "etc", // system_dlkm/etc/build.prop
+ visibility: ["//visibility:private"],
+}
+
+build_prop {
+ name: "vendor_dlkm-build.prop",
+ stem: "build.prop",
+ vendor_dlkm_specific: true,
+ product_config: ":product_config",
+ relative_install_path: "etc", // vendor_dlkm/etc/build.prop
+ visibility: ["//visibility:private"],
+}
+
+build_prop {
+ name: "odm_dlkm-build.prop",
+ stem: "build.prop",
+ odm_dlkm_specific: true,
+ product_config: ":product_config",
+ relative_install_path: "etc", // odm_dlkm/etc/build.prop
+ visibility: ["//visibility:private"],
+}
diff --git a/android/android_info.go b/android/android_info.go
index dd78ee4..a8d3d4e 100644
--- a/android/android_info.go
+++ b/android/android_info.go
@@ -15,9 +15,23 @@
package android
import (
+ "github.com/google/blueprint"
"github.com/google/blueprint/proptools"
)
+var (
+ mergeAndRemoveComments = pctx.AndroidStaticRule("merge_and_remove_comments",
+ blueprint.RuleParams{
+ Command: "cat $in | grep -v '#' > $out",
+ },
+ )
+ androidInfoTxtToProp = pctx.AndroidStaticRule("android_info_txt_to_prop",
+ blueprint.RuleParams{
+ Command: "grep 'require version-' $in | sed -e 's/require version-/ro.build.expect./g' > $out",
+ },
+ )
+)
+
type androidInfoProperties struct {
// Name of output file. Defaults to module name
Stem *string
@@ -41,28 +55,28 @@
ctx.ModuleErrorf("Either Board_info_files or Bootloader_board_name should be set. Please remove one of them\n")
return
}
- outName := proptools.StringDefault(p.properties.Stem, ctx.ModuleName()+".txt")
- androidInfoTxt := PathForModuleOut(ctx, outName).OutputPath
+ androidInfoTxtName := proptools.StringDefault(p.properties.Stem, ctx.ModuleName()+".txt")
+ androidInfoTxt := PathForModuleOut(ctx, androidInfoTxtName)
androidInfoProp := androidInfoTxt.ReplaceExtension(ctx, "prop")
- rule := NewRuleBuilder(pctx, ctx)
-
if boardInfoFiles := PathsForModuleSrc(ctx, p.properties.Board_info_files); len(boardInfoFiles) > 0 {
- rule.Command().Text("cat").Inputs(boardInfoFiles).
- Text(" | grep").FlagWithArg("-v ", "'#'").FlagWithOutput("> ", androidInfoTxt)
+ ctx.Build(pctx, BuildParams{
+ Rule: mergeAndRemoveComments,
+ Inputs: boardInfoFiles,
+ Output: androidInfoTxt,
+ })
} else if bootloaderBoardName := proptools.String(p.properties.Bootloader_board_name); bootloaderBoardName != "" {
- rule.Command().Text("echo").Text("'board="+bootloaderBoardName+"'").FlagWithOutput("> ", androidInfoTxt)
+ WriteFileRule(ctx, androidInfoTxt, "board="+bootloaderBoardName)
} else {
- rule.Command().Text("echo").Text("''").FlagWithOutput("> ", androidInfoTxt)
+ WriteFileRule(ctx, androidInfoTxt, "")
}
- rule.Build(ctx.ModuleName(), "generating android-info.prop")
-
// Create android_info.prop
- rule = NewRuleBuilder(pctx, ctx)
- rule.Command().Text("cat").Input(androidInfoTxt).
- Text(" | grep 'require version-' | sed -e 's/require version-/ro.build.expect./g' >").Output(androidInfoProp)
- rule.Build(ctx.ModuleName()+"prop", "generating android-info.prop")
+ ctx.Build(pctx, BuildParams{
+ Rule: androidInfoTxtToProp,
+ Input: androidInfoTxt,
+ Output: androidInfoProp,
+ })
ctx.SetOutputFiles(Paths{androidInfoProp}, "")
}
diff --git a/android/apex.go b/android/apex.go
index 79ab13c..e73b3e6 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -833,7 +833,7 @@
// If this is the FinalModule (last visited module) copy
// AnyVariantDirectlyInAnyApex to all the other variants
- if am == mctx.FinalModule().(ApexModule) {
+ if mctx.IsFinalModule(am) {
mctx.VisitAllModuleVariants(func(variant Module) {
variant.(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex =
base.ApexProperties.AnyVariantDirectlyInAnyApex
diff --git a/android/base_module_context.go b/android/base_module_context.go
index 223b534..060fae5 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -17,6 +17,7 @@
import (
"fmt"
"regexp"
+ "slices"
"strings"
"github.com/google/blueprint"
@@ -197,9 +198,15 @@
// singleton actions that are only done once for all variants of a module.
FinalModule() Module
+ // IsFinalModule returns if the current module is the last variant. 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 is the last one. This can be used to perform
+ // singleton actions that are only done once for all variants of a module.
+ IsFinalModule(module Module) bool
+
// 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
+ // from all variants if the current module is the last one. Otherwise, care must be taken to not access any
// data modified by the current mutator.
VisitAllModuleVariants(visit func(Module))
@@ -570,7 +577,7 @@
}
func (b *baseModuleContext) GetWalkPath() []Module {
- return b.walkPath
+ return slices.Clone(b.walkPath)
}
func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag {
@@ -591,6 +598,10 @@
return b.bp.FinalModule().(Module)
}
+func (b *baseModuleContext) IsFinalModule(module Module) bool {
+ return b.bp.IsFinalModule(module)
+}
+
// IsMetaDependencyTag returns true for cross-cutting metadata dependencies.
func IsMetaDependencyTag(tag blueprint.DependencyTag) bool {
if tag == licenseKindTag {
diff --git a/android/build_prop.go b/android/build_prop.go
index 5547680..8389470 100644
--- a/android/build_prop.go
+++ b/android/build_prop.go
@@ -109,6 +109,12 @@
return "product"
} else if p.SystemExtSpecific() {
return "system_ext"
+ } else if p.InstallInSystemDlkm() {
+ return "system_dlkm"
+ } else if p.InstallInVendorDlkm() {
+ return "vendor_dlkm"
+ } else if p.InstallInOdmDlkm() {
+ return "odm_dlkm"
}
return "system"
}
@@ -119,6 +125,9 @@
"product",
"odm",
"vendor",
+ "system_dlkm",
+ "vendor_dlkm",
+ "odm_dlkm",
}
func (p *buildPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
diff --git a/android/config.go b/android/config.go
index 275c07f..d9db64e 100644
--- a/android/config.go
+++ b/android/config.go
@@ -867,7 +867,7 @@
}
func (c *config) TargetsJava21() bool {
- return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_21")
+ return c.productVariables.GetBuildFlagBool("RELEASE_TARGET_JAVA_21")
}
// EnvDeps returns the environment variables this build depends on. The first
diff --git a/android/init.go b/android/init.go
index 1ace344..d3a13d0 100644
--- a/android/init.go
+++ b/android/init.go
@@ -20,6 +20,7 @@
gob.Register(extraFilesZip{})
gob.Register(InstallPath{})
gob.Register(ModuleGenPath{})
+ gob.Register(ModuleObjPath{})
gob.Register(ModuleOutPath{})
gob.Register(OutputPath{})
gob.Register(PhonyPath{})
diff --git a/android/module.go b/android/module.go
index a918d6e..58ae885 100644
--- a/android/module.go
+++ b/android/module.go
@@ -81,6 +81,9 @@
InstallInProduct() bool
InstallInVendor() bool
InstallInSystemExt() bool
+ InstallInSystemDlkm() bool
+ InstallInVendorDlkm() bool
+ InstallInOdmDlkm() bool
InstallForceOS() (*OsType, *ArchType)
PartitionTag(DeviceConfig) string
HideFromMake()
@@ -386,6 +389,15 @@
// Whether this module is installed to debug ramdisk
Debug_ramdisk *bool
+ // Install to partition system_dlkm when set to true.
+ System_dlkm_specific *bool
+
+ // Install to partition vendor_dlkm when set to true.
+ Vendor_dlkm_specific *bool
+
+ // Install to partition odm_dlkm when set to true.
+ Odm_dlkm_specific *bool
+
// Whether this module is built for non-native architectures (also known as native bridge binary)
Native_bridge_supported *bool `android:"arch_variant"`
@@ -1535,6 +1547,18 @@
return false
}
+func (m *ModuleBase) InstallInSystemDlkm() bool {
+ return Bool(m.commonProperties.System_dlkm_specific)
+}
+
+func (m *ModuleBase) InstallInVendorDlkm() bool {
+ return Bool(m.commonProperties.Vendor_dlkm_specific)
+}
+
+func (m *ModuleBase) InstallInOdmDlkm() bool {
+ return Bool(m.commonProperties.Odm_dlkm_specific)
+}
+
func (m *ModuleBase) InstallForceOS() (*OsType, *ArchType) {
return nil, nil
}
@@ -1822,6 +1846,8 @@
Enabled bool
// Whether the module has been replaced by a prebuilt
ReplacedByPrebuilt bool
+ // The Target of artifacts that this module variant is responsible for creating.
+ CompileTarget Target
}
var CommonPropertiesProviderKey = blueprint.NewProvider[CommonPropertiesProviderData]()
@@ -2010,7 +2036,7 @@
ctx.GetMissingDependencies()
}
- if m == ctx.FinalModule().(Module).base() {
+ if ctx.IsFinalModule(m.module) {
m.generateModuleTarget(ctx)
if ctx.Failed() {
return
@@ -2086,6 +2112,7 @@
commonData := CommonPropertiesProviderData{
ReplacedByPrebuilt: m.commonProperties.ReplacedByPrebuilt,
+ CompileTarget: m.commonProperties.CompileTarget,
}
if m.commonProperties.ForcedDisabled {
commonData.Enabled = false
diff --git a/android/module_context.go b/android/module_context.go
index 1f5e706..41cb0cc 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -196,6 +196,9 @@
InstallInOdm() bool
InstallInProduct() bool
InstallInVendor() bool
+ InstallInSystemDlkm() bool
+ InstallInVendorDlkm() bool
+ InstallInOdmDlkm() bool
InstallForceOS() (*OsType, *ArchType)
RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string
@@ -493,6 +496,18 @@
return m.module.InstallInVendor()
}
+func (m *moduleContext) InstallInSystemDlkm() bool {
+ return m.module.InstallInSystemDlkm()
+}
+
+func (m *moduleContext) InstallInVendorDlkm() bool {
+ return m.module.InstallInVendorDlkm()
+}
+
+func (m *moduleContext) InstallInOdmDlkm() bool {
+ return m.module.InstallInOdmDlkm()
+}
+
func (m *moduleContext) skipInstall() bool {
if m.module.base().commonProperties.SkipInstall {
return true
diff --git a/android/module_proxy.go b/android/module_proxy.go
index a60a5a8..1f96799 100644
--- a/android/module_proxy.go
+++ b/android/module_proxy.go
@@ -106,6 +106,18 @@
panic("method is not implemented on ModuleProxy")
}
+func (m ModuleProxy) InstallInSystemDlkm() bool {
+ panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendorDlkm() bool {
+ panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInOdmDlkm() bool {
+ panic("method is not implemented on ModuleProxy")
+}
+
func (m ModuleProxy) InstallForceOS() (*OsType, *ArchType) {
panic("method is not implemented on ModuleProxy")
}
diff --git a/android/mutator.go b/android/mutator.go
index 4ddc606..fdd16a8 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -70,7 +70,7 @@
TopDown(name string, m TopDownMutator) MutatorHandle
BottomUp(name string, m BottomUpMutator) MutatorHandle
BottomUpBlueprint(name string, m blueprint.BottomUpMutator) MutatorHandle
- Transition(name string, m TransitionMutator)
+ Transition(name string, m TransitionMutator) TransitionMutatorHandle
}
type RegisterMutatorFunc func(RegisterMutatorsContext)
@@ -579,7 +579,7 @@
}
}
-func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) {
+func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) TransitionMutatorHandle {
atm := &androidTransitionMutator{
finalPhase: x.finalPhase,
mutator: m,
@@ -587,8 +587,10 @@
}
mutator := &mutator{
name: name,
- transitionMutator: atm}
+ transitionMutator: atm,
+ }
x.mutators = append(x.mutators, mutator)
+ return mutator
}
func (x *registerMutatorsContext) mutatorName(name string) string {
@@ -625,7 +627,10 @@
} else if mutator.topDownMutator != nil {
handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
} else if mutator.transitionMutator != nil {
- blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
+ handle := blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
+ if mutator.neverFar {
+ handle.NeverFar()
+ }
}
// Forward booleans set on the MutatorHandle to the blueprint.MutatorHandle.
@@ -681,6 +686,14 @@
MutatesGlobalState() MutatorHandle
}
+type TransitionMutatorHandle interface {
+ // NeverFar causes the variations created by this mutator to never be ignored when adding
+ // far variation dependencies. Normally, far variation dependencies ignore all the variants
+ // of the source module, and only use the variants explicitly requested by the
+ // AddFarVariationDependencies call.
+ NeverFar() MutatorHandle
+}
+
func (mutator *mutator) Parallel() MutatorHandle {
return mutator
}
@@ -715,6 +728,11 @@
return mutator
}
+func (mutator *mutator) NeverFar() MutatorHandle {
+ mutator.neverFar = true
+ return mutator
+}
+
func RegisterComponentsMutator(ctx RegisterMutatorsContext) {
ctx.BottomUp("component-deps", componentDepsMutator)
}
diff --git a/android/neverallow.go b/android/neverallow.go
index 6251e2b..6176a99 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -336,6 +336,8 @@
"prebuilt_odm",
"prebuilt_vendor_dlkm",
"prebuilt_bt_firmware",
+ "prebuilt_tvservice",
+ "prebuilt_optee",
).
DefinedInBpFile().
Because("module type not allowed to be defined in bp file")
diff --git a/android/paths.go b/android/paths.go
index a7ee7ac..9cb872d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -117,6 +117,9 @@
InstallInOdm() bool
InstallInProduct() bool
InstallInVendor() bool
+ InstallInSystemDlkm() bool
+ InstallInVendorDlkm() bool
+ InstallInOdmDlkm() bool
InstallForceOS() (*OsType, *ArchType)
}
@@ -170,6 +173,18 @@
return ctx.Module().InstallInVendor()
}
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInSystemDlkm() bool {
+ return ctx.Module().InstallInSystemDlkm()
+}
+
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendorDlkm() bool {
+ return ctx.Module().InstallInVendorDlkm()
+}
+
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInOdmDlkm() bool {
+ return ctx.Module().InstallInOdmDlkm()
+}
+
func (ctx *baseModuleContextToModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
return ctx.Module().InstallForceOS()
}
@@ -2077,6 +2092,10 @@
return base.Join(ctx, paths...)
}
+func PathForSuiteInstall(ctx PathContext, suite string, pathComponents ...string) InstallPath {
+ return pathForPartitionInstallDir(ctx, "test_suites", "test_suites", false).Join(ctx, suite).Join(ctx, pathComponents...)
+}
+
func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
rel := Rel(ctx, strings.TrimSuffix(path.PartitionDir(), path.partition), path.String())
return "/" + rel
@@ -2131,6 +2150,12 @@
partition = ctx.DeviceConfig().SystemExtPath()
} else if ctx.InstallInRoot() {
partition = "root"
+ } else if ctx.InstallInSystemDlkm() {
+ partition = ctx.DeviceConfig().SystemDlkmPath()
+ } else if ctx.InstallInVendorDlkm() {
+ partition = ctx.DeviceConfig().VendorDlkmPath()
+ } else if ctx.InstallInOdmDlkm() {
+ partition = ctx.DeviceConfig().OdmDlkmPath()
} else {
partition = "system"
}
@@ -2334,6 +2359,9 @@
inOdm bool
inProduct bool
inVendor bool
+ inSystemDlkm bool
+ inVendorDlkm bool
+ inOdmDlkm bool
forceOS *OsType
forceArch *ArchType
}
@@ -2388,6 +2416,18 @@
return m.inVendor
}
+func (m testModuleInstallPathContext) InstallInSystemDlkm() bool {
+ return m.inSystemDlkm
+}
+
+func (m testModuleInstallPathContext) InstallInVendorDlkm() bool {
+ return m.inVendorDlkm
+}
+
+func (m testModuleInstallPathContext) InstallInOdmDlkm() bool {
+ return m.inOdmDlkm
+}
+
func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
return m.forceOS, m.forceArch
}
diff --git a/android/register.go b/android/register.go
index bb1ead7..8d2f19e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -98,6 +98,7 @@
usesCreateModule bool
mutatesDependencies bool
mutatesGlobalState bool
+ neverFar bool
}
var _ sortableComponent = &mutator{}
diff --git a/android/singleton.go b/android/singleton.go
index 913bf6a..0754b0c 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -64,6 +64,7 @@
VisitAllModulesBlueprint(visit func(blueprint.Module))
VisitAllModules(visit func(Module))
+ VisitAllModuleProxies(visit func(proxy ModuleProxy))
VisitAllModulesIf(pred func(Module) bool, visit func(Module))
VisitDirectDeps(module Module, visit func(Module))
@@ -77,8 +78,10 @@
VisitAllModuleVariants(module Module, visit func(Module))
+ VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy))
+
PrimaryModule(module Module) Module
- FinalModule(module Module) Module
+ IsFinalModule(module Module) bool
AddNinjaFileDeps(deps ...string)
@@ -193,7 +196,7 @@
}
// visitAdaptor wraps a visit function that takes an android.Module parameter into
-// a function that takes an blueprint.Module parameter and only calls the visit function if the
+// a function that takes a blueprint.Module parameter and only calls the visit function if the
// blueprint.Module is an android.Module.
func visitAdaptor(visit func(Module)) func(blueprint.Module) {
return func(module blueprint.Module) {
@@ -203,6 +206,16 @@
}
}
+// visitProxyAdaptor wraps a visit function that takes an android.ModuleProxy parameter into
+// a function that takes a blueprint.ModuleProxy parameter.
+func visitProxyAdaptor(visit func(proxy ModuleProxy)) func(proxy blueprint.ModuleProxy) {
+ return func(module blueprint.ModuleProxy) {
+ visit(ModuleProxy{
+ module: module,
+ })
+ }
+}
+
// predAdaptor wraps a pred function that takes an android.Module parameter
// into a function that takes an blueprint.Module parameter and only calls the visit function if the
// blueprint.Module is an android.Module, otherwise returns false.
@@ -224,6 +237,10 @@
s.SingletonContext.VisitAllModules(visitAdaptor(visit))
}
+func (s *singletonContextAdaptor) VisitAllModuleProxies(visit func(proxy ModuleProxy)) {
+ s.SingletonContext.VisitAllModuleProxies(visitProxyAdaptor(visit))
+}
+
func (s *singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
}
@@ -248,12 +265,16 @@
s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit))
}
+func (s *singletonContextAdaptor) VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) {
+ s.SingletonContext.VisitAllModuleVariantProxies(module, visitProxyAdaptor(visit))
+}
+
func (s *singletonContextAdaptor) PrimaryModule(module Module) Module {
return s.SingletonContext.PrimaryModule(module).(Module)
}
-func (s *singletonContextAdaptor) FinalModule(module Module) Module {
- return s.SingletonContext.FinalModule(module).(Module)
+func (s *singletonContextAdaptor) IsFinalModule(module Module) bool {
+ return s.SingletonContext.IsFinalModule(module)
}
func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module {
diff --git a/android/variable.go b/android/variable.go
index 2ca7714..f82c9ca 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -577,6 +577,14 @@
BoardAvbRollbackIndexLocation string `json:",omitempty"`
}
+type ChainedAvbPartitionProps struct {
+ Partitions []string `json:",omitempty"`
+ Key string `json:",omitempty"`
+ Algorithm string `json:",omitempty"`
+ RollbackIndex string `json:",omitempty"`
+ RollbackIndexLocation string `json:",omitempty"`
+}
+
type PartitionVariables struct {
ProductDirectory string `json:",omitempty"`
PartitionQualifiedVariables map[string]PartitionQualifiedVariablesType
@@ -601,13 +609,21 @@
ProductUseDynamicPartitionSize bool `json:",omitempty"`
CopyImagesForTargetFilesZip bool `json:",omitempty"`
- BoardAvbEnable bool `json:",omitempty"`
+ BoardAvbEnable bool `json:",omitempty"`
+ BoardAvbAlgorithm string `json:",omitempty"`
+ BoardAvbKeyPath string `json:",omitempty"`
+ BoardAvbRollbackIndex string `json:",omitempty"`
+ BuildingVbmetaImage bool `json:",omitempty"`
+ ChainedVbmetaPartitions map[string]ChainedAvbPartitionProps `json:",omitempty"`
ProductPackages []string `json:",omitempty"`
ProductPackagesDebug []string `json:",omitempty"`
VendorLinkerConfigSrcs []string `json:",omitempty"`
ProductLinkerConfigSrcs []string `json:",omitempty"`
+ BoardInfoFiles []string `json:",omitempty"`
+ BootLoaderBoardName string `json:",omitempty"`
+
ProductCopyFiles map[string]string `json:",omitempty"`
BuildingSystemDlkmImage bool `json:",omitempty"`
diff --git a/apex/Android.bp b/apex/Android.bp
index 0e2f564..870ca7e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -7,6 +7,7 @@
pkgPath: "android/soong/apex",
deps: [
"blueprint",
+ "blueprint-bpmodify",
"soong",
"soong-aconfig",
"soong-aconfig-codegen",
diff --git a/apex/apex.go b/apex/apex.go
index 80af9c5..587f63f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -20,10 +20,12 @@
"fmt"
"path/filepath"
"regexp"
+ "slices"
"sort"
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/depset"
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -464,6 +466,12 @@
// GenerateAndroidBuildActions.
filesInfo []apexFile
+ // List of files that were excluded by the unwanted_transitive_deps property.
+ unwantedTransitiveFilesInfo []apexFile
+
+ // List of files that were excluded due to conflicts with other variants of the same module.
+ duplicateTransitiveFilesInfo []apexFile
+
// List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
makeModulesToInstall []string
@@ -1877,6 +1885,14 @@
// visitor skips these from this list of module names
unwantedTransitiveDeps []string
+
+ // unwantedTransitiveFilesInfo contains files that would have been in the apex
+ // except that they were listed in unwantedTransitiveDeps.
+ unwantedTransitiveFilesInfo []apexFile
+
+ // duplicateTransitiveFilesInfo contains files that would ahve been in the apex
+ // except that another variant of the same module was already in the apex.
+ duplicateTransitiveFilesInfo []apexFile
}
func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) {
@@ -1887,6 +1903,7 @@
// Needs additional verification for the resulting APEX to ensure that skipped artifacts don't make problems.
// For example, DT_NEEDED modules should be found within the APEX unless they are marked in `requiredNativeLibs`.
if f.transitiveDep && f.module != nil && android.InList(mctx.OtherModuleName(f.module), vctx.unwantedTransitiveDeps) {
+ vctx.unwantedTransitiveFilesInfo = append(vctx.unwantedTransitiveFilesInfo, f)
continue
}
dest := filepath.Join(f.installDir, f.builtFile.Base())
@@ -1897,6 +1914,8 @@
mctx.ModuleErrorf("apex file %v is provided by two different files %v and %v",
dest, e.builtFile, f.builtFile)
return
+ } else {
+ vctx.duplicateTransitiveFilesInfo = append(vctx.duplicateTransitiveFilesInfo, f)
}
// If a module is directly included and also transitively depended on
// consider it as directly included.
@@ -1911,6 +1930,7 @@
for _, v := range encountered {
vctx.filesInfo = append(vctx.filesInfo, v)
}
+
sort.Slice(vctx.filesInfo, func(i, j int) bool {
// Sort by destination path so as to ensure consistent ordering even if the source of the files
// changes.
@@ -2341,6 +2361,8 @@
// 3) some fields in apexBundle struct are configured
a.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = vctx.filesInfo
+ a.unwantedTransitiveFilesInfo = vctx.unwantedTransitiveFilesInfo
+ a.duplicateTransitiveFilesInfo = vctx.duplicateTransitiveFilesInfo
a.setPayloadFsType(ctx)
a.setSystemLibLink(ctx)
@@ -2367,6 +2389,8 @@
a.setOutputFiles(ctx)
a.enforcePartitionTagOnApexSystemServerJar(ctx)
+
+ a.verifyNativeImplementationLibs(ctx)
}
// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -2920,3 +2944,105 @@
func (a *apexBundle) IsTestApex() bool {
return a.testApex
}
+
+// verifyNativeImplementationLibs compares the list of transitive implementation libraries used to link native
+// libraries in the apex against the list of implementation libraries in the apex, ensuring that none of the
+// libraries in the apex have references to private APIs from outside the apex.
+func (a *apexBundle) verifyNativeImplementationLibs(ctx android.ModuleContext) {
+ var directImplementationLibs android.Paths
+ var transitiveImplementationLibs []depset.DepSet[android.Path]
+
+ if a.properties.IsCoverageVariant {
+ return
+ }
+
+ if a.testApex {
+ return
+ }
+
+ if a.UsePlatformApis() {
+ return
+ }
+
+ checkApexTag := func(tag blueprint.DependencyTag) bool {
+ switch tag {
+ case sharedLibTag, jniLibTag, executableTag, androidAppTag:
+ return true
+ default:
+ return false
+ }
+ }
+
+ checkTransitiveTag := func(tag blueprint.DependencyTag) bool {
+ switch {
+ case cc.IsSharedDepTag(tag), java.IsJniDepTag(tag), rust.IsRlibDepTag(tag), rust.IsDylibDepTag(tag), checkApexTag(tag):
+ return true
+ default:
+ return false
+ }
+ }
+
+ var appEmbeddedJNILibs android.Paths
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ tag := ctx.OtherModuleDependencyTag(dep)
+ if !checkApexTag(tag) {
+ return
+ }
+ if tag == sharedLibTag || tag == jniLibTag {
+ outputFile := android.OutputFileForModule(ctx, dep, "")
+ directImplementationLibs = append(directImplementationLibs, outputFile)
+ }
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ transitiveImplementationLibs = append(transitiveImplementationLibs, info.ImplementationDeps)
+ }
+ if info, ok := android.OtherModuleProvider(ctx, dep, java.AppInfoProvider); ok {
+ appEmbeddedJNILibs = append(appEmbeddedJNILibs, info.EmbeddedJNILibs...)
+ }
+ })
+
+ depSet := depset.New(depset.PREORDER, directImplementationLibs, transitiveImplementationLibs)
+ allImplementationLibs := depSet.ToList()
+
+ allFileInfos := slices.Concat(a.filesInfo, a.unwantedTransitiveFilesInfo, a.duplicateTransitiveFilesInfo)
+
+ for _, lib := range allImplementationLibs {
+ inApex := slices.ContainsFunc(allFileInfos, func(fi apexFile) bool {
+ return fi.builtFile == lib
+ })
+ inApkInApex := slices.Contains(appEmbeddedJNILibs, lib)
+
+ if !inApex && !inApkInApex {
+ ctx.ModuleErrorf("library in apex transitively linked against implementation library %q not in apex", lib)
+ var depPath []android.Module
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ if depPath != nil {
+ return false
+ }
+
+ tag := ctx.OtherModuleDependencyTag(child)
+
+ if parent == ctx.Module() {
+ if !checkApexTag(tag) {
+ return false
+ }
+ }
+
+ if checkTransitiveTag(tag) {
+ if android.OutputFileForModule(ctx, child, "") == lib {
+ depPath = ctx.GetWalkPath()
+ }
+ return true
+ }
+
+ return false
+ })
+ if depPath != nil {
+ ctx.ModuleErrorf("dependency path:")
+ for _, m := range depPath {
+ ctx.ModuleErrorf(" %s", ctx.OtherModuleName(m))
+ }
+ return
+ }
+ }
+ }
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 988c1ce..54c1fac 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -20,6 +20,7 @@
"path/filepath"
"reflect"
"regexp"
+ "slices"
"sort"
"strconv"
"strings"
@@ -28,6 +29,7 @@
"android/soong/aconfig/codegen"
"github.com/google/blueprint"
+ "github.com/google/blueprint/bpmodify"
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -225,6 +227,10 @@
"system/sepolicy/apex/myapex-file_contexts": nil,
})
+var prepareForTestWithOtherapex = android.FixtureMergeMockFs(android.MockFS{
+ "system/sepolicy/apex/otherapex-file_contexts": nil,
+})
+
// ensure that 'result' equals 'expected'
func ensureEquals(t *testing.T, result string, expected string) {
t.Helper()
@@ -12114,3 +12120,398 @@
fileList := android.ContentFromFileRuleForTests(t, result, partition.Output("fileList"))
android.AssertDeepEquals(t, "filesystem with apex", "apex/myapex.apex\n", fileList)
}
+
+func TestApexVerifyNativeImplementationLibs(t *testing.T) {
+ t.Parallel()
+
+ extractDepenencyPathFromErrors := func(errs []error) []string {
+ i := slices.IndexFunc(errs, func(err error) bool {
+ return strings.Contains(err.Error(), "dependency path:")
+ })
+ if i < 0 {
+ return nil
+ }
+ var dependencyPath []string
+ for _, err := range errs[i+1:] {
+ s := err.Error()
+ lastSpace := strings.LastIndexByte(s, ' ')
+ if lastSpace >= 0 {
+ dependencyPath = append(dependencyPath, s[lastSpace+1:])
+ }
+ }
+ return dependencyPath
+ }
+
+ checkErrors := func(wantDependencyPath []string) func(t *testing.T, result *android.TestResult) {
+ return func(t *testing.T, result *android.TestResult) {
+ t.Helper()
+ if len(result.Errs) == 0 {
+ t.Fatalf("expected errors")
+ }
+ t.Log("found errors:")
+ for _, err := range result.Errs {
+ t.Log(err)
+ }
+ if g, w := result.Errs[0].Error(), "library in apex transitively linked against implementation library"; !strings.Contains(g, w) {
+ t.Fatalf("expected error %q, got %q", w, g)
+ }
+ dependencyPath := extractDepenencyPathFromErrors(result.Errs)
+ if g, w := dependencyPath, wantDependencyPath; !slices.Equal(g, w) {
+ t.Errorf("expected dependency path %q, got %q", w, g)
+ }
+ }
+ }
+
+ addToSharedLibs := func(module, lib string) func(bp *bpmodify.Blueprint) {
+ return func(bp *bpmodify.Blueprint) {
+ m := bp.ModulesByName(module)
+ props, err := m.GetOrCreateProperty(bpmodify.List, "shared_libs")
+ if err != nil {
+ panic(err)
+ }
+ props.AddStringToList(lib)
+ }
+ }
+
+ bpTemplate := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ rust_dyn_libs: ["libmyrust"],
+ binaries: ["mybin", "myrustbin"],
+ jni_libs: ["libjni"],
+ apps: ["myapp"],
+ updatable: false,
+ }
+
+ apex {
+ name: "otherapex",
+ key: "myapex.key",
+ native_shared_libs: ["libotherapex"],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["foo.cpp"],
+ apex_available: ["myapex"],
+ }
+
+ cc_binary {
+ name: "mybin",
+ srcs: ["foo.cpp"],
+ apex_available: ["myapex"],
+ }
+
+ rust_library {
+ name: "libmyrust",
+ crate_name: "myrust",
+ srcs: ["src/lib.rs"],
+ rustlibs: ["libmyrust_transitive_dylib"],
+ rlibs: ["libmyrust_transitive_rlib"],
+ apex_available: ["myapex"],
+ }
+
+ rust_library{
+ name: "libmyrust_transitive_dylib",
+ crate_name: "myrust_transitive_dylib",
+ srcs: ["src/lib.rs"],
+ apex_available: ["myapex"],
+ }
+
+ rust_library {
+ name: "libmyrust_transitive_rlib",
+ crate_name: "myrust_transitive_rlib",
+ srcs: ["src/lib.rs"],
+ apex_available: ["myapex"],
+ }
+
+ rust_binary {
+ name: "myrustbin",
+ srcs: ["src/main.rs"],
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "libbar",
+ sdk_version: "current",
+ srcs: ["bar.cpp"],
+ apex_available: ["myapex"],
+ stl: "none",
+ }
+
+ android_app {
+ name: "myapp",
+ jni_libs: ["libembeddedjni"],
+ use_embedded_native_libs: true,
+ sdk_version: "current",
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "libembeddedjni",
+ sdk_version: "current",
+ srcs: ["bar.cpp"],
+ apex_available: ["myapex"],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libjni",
+ sdk_version: "current",
+ srcs: ["bar.cpp"],
+ apex_available: ["myapex"],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libotherapex",
+ sdk_version: "current",
+ srcs: ["otherapex.cpp"],
+ apex_available: ["otherapex"],
+ stubs: {
+ symbol_file: "libotherapex.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libplatform",
+ sdk_version: "current",
+ srcs: ["libplatform.cpp"],
+ stubs: {
+ symbol_file: "libplatform.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ stl: "none",
+ system_shared_libs: [],
+ }
+ `
+
+ testCases := []struct {
+ name string
+ bpModifier func(bp *bpmodify.Blueprint)
+ dependencyPath []string
+ }{
+ {
+ name: "library dependency in other apex",
+ bpModifier: addToSharedLibs("mylib", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "mylib", "libotherapex"},
+ },
+ {
+ name: "transitive library dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("mylib", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "mylib", "libbar", "libotherapex"},
+ },
+ {
+ name: "library dependency in platform",
+ bpModifier: addToSharedLibs("mylib", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "mylib", "libplatform"},
+ },
+ {
+ name: "jni library dependency in other apex",
+ bpModifier: addToSharedLibs("libjni", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "libjni", "libotherapex"},
+ },
+ {
+ name: "transitive jni library dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libjni", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libjni", "libbar", "libotherapex"},
+ },
+ {
+ name: "jni library dependency in platform",
+ bpModifier: addToSharedLibs("libjni", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "libjni", "libplatform"},
+ },
+ {
+ name: "transitive jni library dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libjni", "libbar")(bp)
+ addToSharedLibs("libbar", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libjni", "libbar", "libplatform"},
+ },
+ // TODO: embedded JNI in apps should be checked too, but Soong currently just packages the transitive
+ // JNI libraries even if they came from another apex.
+ //{
+ // name: "app jni library dependency in other apex",
+ // bpModifier: addToSharedLibs("libembeddedjni", "libotherapex#impl"),
+ // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libotherapex"},
+ //},
+ //{
+ // name: "transitive app jni library dependency in other apex",
+ // bpModifier: func(bp *bpmodify.Blueprint) {
+ // addToSharedLibs("libembeddedjni", "libbar")(bp)
+ // addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ // },
+ // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libotherapex"},
+ //},
+ //{
+ // name: "app jni library dependency in platform",
+ // bpModifier: addToSharedLibs("libembeddedjni", "libplatform#impl"),
+ // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libplatform"},
+ //},
+ //{
+ // name: "transitive app jni library dependency in platform",
+ // bpModifier: func(bp *bpmodify.Blueprint) {
+ // addToSharedLibs("libembeddedjni", "libbar")(bp)
+ // addToSharedLibs("libbar", "libplatform#impl")(bp)
+ // },
+ // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libplatform"},
+ //},
+ {
+ name: "binary dependency in other apex",
+ bpModifier: addToSharedLibs("mybin", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "mybin", "libotherapex"},
+ },
+ {
+ name: "transitive binary dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("mybin", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "mybin", "libbar", "libotherapex"},
+ },
+ {
+ name: "binary dependency in platform",
+ bpModifier: addToSharedLibs("mybin", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "mybin", "libplatform"},
+ },
+ {
+ name: "transitive binary dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("mybin", "libbar")(bp)
+ addToSharedLibs("libbar", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "mybin", "libbar", "libplatform"},
+ },
+
+ {
+ name: "rust library dependency in other apex",
+ bpModifier: addToSharedLibs("libmyrust", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "libmyrust", "libotherapex"},
+ },
+ {
+ name: "transitive rust library dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libbar", "libotherapex"},
+ },
+ {
+ name: "rust library dependency in platform",
+ bpModifier: addToSharedLibs("libmyrust", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "libmyrust", "libplatform"},
+ },
+ {
+ name: "transitive rust library dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust", "libbar")(bp)
+ addToSharedLibs("libbar", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libbar", "libplatform"},
+ },
+ {
+ name: "transitive rust library dylib dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust_transitive_dylib", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libotherapex"},
+ },
+ {
+ name: "transitive rust library dylib dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust_transitive_dylib", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libplatform"},
+ },
+ {
+ name: "transitive rust library rlib dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust_transitive_rlib", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libotherapex"},
+ },
+ {
+ name: "transitive rust library rlib dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust_transitive_rlib", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libplatform"},
+ },
+ {
+ name: "rust binary dependency in other apex",
+ bpModifier: addToSharedLibs("myrustbin", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "myrustbin", "libotherapex"},
+ },
+ {
+ name: "transitive rust binary dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("myrustbin", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "myrustbin", "libbar", "libotherapex"},
+ },
+ {
+ name: "rust binary dependency in platform",
+ bpModifier: addToSharedLibs("myrustbin", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "myrustbin", "libplatform"},
+ },
+ {
+ name: "transitive rust binary dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("myrustbin", "libbar")(bp)
+ addToSharedLibs("libbar", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "myrustbin", "libbar", "libplatform"},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ t.Parallel()
+ bp, err := bpmodify.NewBlueprint("", []byte(bpTemplate))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if testCase.bpModifier != nil {
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Fatalf("panic in bpModifier: %v", r)
+ }
+ }()
+ testCase.bpModifier(bp)
+ }()
+ }
+ android.GroupFixturePreparers(
+ android.PrepareForTestWithAndroidBuildComponents,
+ cc.PrepareForTestWithCcBuildComponents,
+ java.PrepareForTestWithDexpreopt,
+ rust.PrepareForTestWithRustDefaultModules,
+ PrepareForTestWithApexBuildComponents,
+ prepareForTestWithMyapex,
+ prepareForTestWithOtherapex,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BuildId = proptools.StringPtr("TEST.BUILD_ID")
+ }),
+ ).ExtendWithErrorHandler(android.FixtureCustomErrorHandler(checkErrors(testCase.dependencyPath))).
+ RunTestWithBp(t, bp.String())
+ })
+ }
+}
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index f8e8899..f367174 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -45,10 +45,7 @@
prepareForTestWithPlatformBootclasspath,
prepareForTestWithArtApex,
prepareForTestWithMyapex,
- // For otherapex.
- android.FixtureMergeMockFs(android.MockFS{
- "system/sepolicy/apex/otherapex-file_contexts": nil,
- }),
+ prepareForTestWithOtherapex,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
java.FixtureConfigureApexBootJars("myapex:bar"),
diff --git a/cc/Android.bp b/cc/Android.bp
index a7b6d81..3b29ae8 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -122,3 +122,31 @@
// Used by plugins
visibility: ["//visibility:public"],
}
+
+phony {
+ name: "llndk_libs",
+ required: [
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libGLESv3",
+ "libRS",
+ "libandroid_net",
+ "libapexsupport",
+ "libbinder_ndk",
+ "libc",
+ "libcgrouprc",
+ "libclang_rt.asan",
+ "libdl",
+ "libft2",
+ "liblog",
+ "libm",
+ "libmediandk",
+ "libnativewindow",
+ "libselinux",
+ "libsync",
+ "libvendorsupport",
+ "libvndksupport",
+ "libvulkan",
+ ],
+}
diff --git a/cc/cc.go b/cc/cc.go
index 0ed6238..becff0f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -19,8 +19,10 @@
// is handled in builder.go
import (
+ "errors"
"fmt"
"io"
+ "slices"
"strconv"
"strings"
@@ -43,6 +45,14 @@
var CcMakeVarsInfoProvider = blueprint.NewProvider[*CcMakeVarsInfo]()
+type CcObjectInfo struct {
+ objFiles android.Paths
+ tidyFiles android.Paths
+ kytheFiles android.Paths
+}
+
+var CcObjectInfoProvider = blueprint.NewProvider[CcObjectInfo]()
+
func init() {
RegisterCCBuildComponents(android.InitRegistrationContext)
@@ -221,6 +231,9 @@
// LLNDK headers for the ABI checker to check LLNDK implementation library.
LlndkIncludeDirs android.Paths
LlndkSystemIncludeDirs android.Paths
+
+ directImplementationDeps android.Paths
+ transitiveImplementationDeps []depset.DepSet[android.Path]
}
// LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -648,10 +661,6 @@
installInRoot() bool
}
-type xref interface {
- XrefCcFiles() android.Paths
-}
-
type overridable interface {
overriddenModules() []string
}
@@ -900,12 +909,6 @@
staticAnalogue *StaticLibraryInfo
makeLinkType string
- // Kythe (source file indexer) paths for this compilation module
- kytheFiles android.Paths
- // Object .o file output paths for this compilation module
- objFiles android.Paths
- // Tidy .tidy file output paths for this compilation module
- tidyFiles android.Paths
// For apex variants, this is set as apex.min_sdk_version
apexSdkVersion android.ApiLevel
@@ -1473,10 +1476,6 @@
return isBionic(name)
}
-func (c *Module) XrefCcFiles() android.Paths {
- return c.kytheFiles
-}
-
func (c *Module) isCfiAssemblySupportEnabled() bool {
return c.sanitize != nil &&
Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
@@ -2047,9 +2046,6 @@
if ctx.Failed() {
return
}
- c.kytheFiles = objs.kytheFiles
- c.objFiles = objs.objFiles
- c.tidyFiles = objs.tidyFiles
}
if c.linker != nil {
@@ -2060,6 +2056,10 @@
c.outputFile = android.OptionalPathForPath(outputFile)
c.maybeUnhideFromMake()
+
+ android.SetProvider(ctx, ImplementationDepInfoProvider, &ImplementationDepInfo{
+ ImplementationDeps: depset.New(depset.PREORDER, deps.directImplementationDeps, deps.transitiveImplementationDeps),
+ })
}
android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
@@ -2114,6 +2114,17 @@
c.hasYacc = b.hasSrcExt(ctx, ".y") || b.hasSrcExt(ctx, ".yy")
}
+ ccObjectInfo := CcObjectInfo{
+ kytheFiles: objs.kytheFiles,
+ }
+ if !ctx.Config().KatiEnabled() || !android.ShouldSkipAndroidMkProcessing(ctx, c) {
+ ccObjectInfo.objFiles = objs.objFiles
+ ccObjectInfo.tidyFiles = objs.tidyFiles
+ }
+ if len(ccObjectInfo.kytheFiles)+len(ccObjectInfo.objFiles)+len(ccObjectInfo.tidyFiles) > 0 {
+ android.SetProvider(ctx, CcObjectInfoProvider, ccObjectInfo)
+ }
+
c.setOutputFiles(ctx)
if c.makeVarsInfo != nil {
@@ -2121,6 +2132,12 @@
}
}
+func setOutputFilesIfNotEmpty(ctx ModuleContext, files android.Paths, tag string) {
+ if len(files) > 0 {
+ ctx.SetOutputFiles(files, tag)
+ }
+}
+
func (c *Module) setOutputFiles(ctx ModuleContext) {
if c.outputFile.Valid() {
ctx.SetOutputFiles(android.Paths{c.outputFile.Path()}, "")
@@ -2283,6 +2300,10 @@
deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
deps.LlndkHeaderLibs = android.LastUniqueStrings(deps.LlndkHeaderLibs)
+ if err := checkConflictingExplicitVersions(deps.SharedLibs); err != nil {
+ ctx.PropertyErrorf("shared_libs", "%s", err.Error())
+ }
+
for _, lib := range deps.ReexportSharedLibHeaders {
if !inList(lib, deps.SharedLibs) {
ctx.PropertyErrorf("export_shared_lib_headers", "Shared library not in shared_libs: '%s'", lib)
@@ -2310,6 +2331,26 @@
return deps
}
+func checkConflictingExplicitVersions(libs []string) error {
+ withoutVersion := func(s string) string {
+ name, _ := StubsLibNameAndVersion(s)
+ return name
+ }
+ var errs []error
+ for i, lib := range libs {
+ libName := withoutVersion(lib)
+ libsToCompare := libs[i+1:]
+ j := slices.IndexFunc(libsToCompare, func(s string) bool {
+ return withoutVersion(s) == libName
+ })
+ if j >= 0 {
+ errs = append(errs, fmt.Errorf("duplicate shared libraries with different explicit versions: %q and %q",
+ lib, libsToCompare[j]))
+ }
+ }
+ return errors.Join(errs...)
+}
+
func (c *Module) beginMutator(actx android.BottomUpMutatorContext) {
ctx := &baseModuleContext{
BaseModuleContext: actx,
@@ -2371,6 +2412,9 @@
if version != "" && canBeOrLinkAgainstVersionVariants(mod) {
// Version is explicitly specified. i.e. libFoo#30
+ if version == "impl" {
+ version = ""
+ }
variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
if tag, ok := depTag.(libraryDependencyTag); ok {
tag.explicitlyVersioned = true
@@ -3069,6 +3113,13 @@
linkFile = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
depFile = sharedLibraryInfo.TableOfContents
+ if !sharedLibraryInfo.IsStubs {
+ depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+ if info, ok := android.OtherModuleProvider(ctx, dep, ImplementationDepInfoProvider); ok {
+ depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+ }
+ }
+
ptr = &depPaths.SharedLibs
switch libDepTag.Order {
case earlyLibraryDependency:
@@ -3957,9 +4008,10 @@
func (ks *kytheExtractAllSingleton) GenerateBuildActions(ctx android.SingletonContext) {
var xrefTargets android.Paths
- ctx.VisitAllModules(func(module android.Module) {
- if ccModule, ok := module.(xref); ok {
- xrefTargets = append(xrefTargets, ccModule.XrefCcFiles()...)
+ ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+ files := android.OtherModuleProviderOrDefault(ctx, module, CcObjectInfoProvider).kytheFiles
+ if len(files) > 0 {
+ xrefTargets = append(xrefTargets, files...)
}
})
// TODO(asmundak): Perhaps emit a rule to output a warning if there were no xrefTargets
diff --git a/cc/cc_test.go b/cc/cc_test.go
index f170d0a..df239cb 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3319,3 +3319,20 @@
t.Errorf("expected %q in cflags, got %q", "-Xclang -verify", cFlags_cv)
}
}
+
+func TestCheckConflictingExplicitVersions(t *testing.T) {
+ PrepareForIntegrationTestWithCc.
+ ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+ `shared_libs: duplicate shared libraries with different explicit versions: "libbar" and "libbar#impl"`,
+ )).
+ RunTestWithBp(t, `
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libbar", "libbar#impl"],
+ }
+
+ cc_library {
+ name: "libbar",
+ }
+ `)
+}
diff --git a/cc/compiler.go b/cc/compiler.go
index 0fa058a..91f107c 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -78,11 +78,11 @@
// If possible, don't use this. If adding paths from the current directory use
// local_include_dirs, if adding paths from other modules use export_include_dirs in
// that module.
- Include_dirs []string `android:"arch_variant,variant_prepend"`
+ Include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
// list of directories relative to the Blueprints file that will
// be added to the include path using -I
- Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+ Local_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
// Add the directory containing the Android.bp file to the list of include
// directories. Defaults to true.
@@ -411,13 +411,13 @@
}
// Include dir cflags
- localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
+ localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs.GetOrDefault(ctx, nil))
if len(localIncludeDirs) > 0 {
f := includeDirsToFlags(localIncludeDirs)
flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
}
- rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs)
+ rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs.GetOrDefault(ctx, nil))
if len(rootIncludeDirs) > 0 {
f := includeDirsToFlags(rootIncludeDirs)
flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
@@ -807,7 +807,7 @@
type RustBindgenClangProperties struct {
// list of directories relative to the Blueprints file that will
// be added to the include path using -I
- Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+ Local_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
// list of static libraries that provide headers for this binding.
Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
diff --git a/cc/library.go b/cc/library.go
index 1f21614..7dffa72 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1194,6 +1194,7 @@
SharedLibrary: unstrippedOutputFile,
TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering,
Target: ctx.Target(),
+ IsStubs: library.buildStubs(),
})
addStubDependencyProviders(ctx)
diff --git a/cc/linkable.go b/cc/linkable.go
index cd33e28..ef204eb 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -317,7 +317,9 @@
SharedLibrary android.Path
Target android.Target
- TableOfContents android.OptionalPath
+ TableOfContents android.OptionalPath
+ IsStubs bool
+ ImplementationDeps depset.DepSet[string]
// should be obtained from static analogue
TransitiveStaticLibrariesForOrdering depset.DepSet[android.Path]
@@ -386,3 +388,9 @@
}
var FlagExporterInfoProvider = blueprint.NewProvider[FlagExporterInfo]()
+
+var ImplementationDepInfoProvider = blueprint.NewProvider[*ImplementationDepInfo]()
+
+type ImplementationDepInfo struct {
+ ImplementationDeps depset.DepSet[android.Path]
+}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 7c87297..ba4c662 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -221,6 +221,7 @@
Target: ctx.Target(),
TableOfContents: p.tocFile,
+ IsStubs: p.buildStubs(),
})
return outputFile
@@ -232,6 +233,7 @@
android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
SharedLibrary: latestStub,
Target: ctx.Target(),
+ IsStubs: true,
})
return latestStub
diff --git a/cc/tidy.go b/cc/tidy.go
index ec1e8a2..5cbf8f0 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -219,15 +219,11 @@
subsetTidyFileGroups := make(map[string]android.Paths) // subset group name => tidy file Paths
// (1) Collect all obj/tidy files into OS-specific groups.
- ctx.VisitAllModuleVariants(module, func(variant android.Module) {
- if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(ctx, variant) {
- return
- }
- if m, ok := variant.(*Module); ok {
- osName := variant.Target().Os.Name
- addToOSGroup(osName, m.objFiles, allObjFileGroups, subsetObjFileGroups)
- addToOSGroup(osName, m.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
- }
+ ctx.VisitAllModuleVariantProxies(module, func(variant android.ModuleProxy) {
+ osName := android.OtherModuleProviderOrDefault(ctx, variant, android.CommonPropertiesProviderKey).CompileTarget.Os.Name
+ info := android.OtherModuleProviderOrDefault(ctx, variant, CcObjectInfoProvider)
+ addToOSGroup(osName, info.objFiles, allObjFileGroups, subsetObjFileGroups)
+ addToOSGroup(osName, info.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
})
// (2) Add an all-OS group, with "" or "subset" name, to include all os-specific phony targets.
@@ -258,7 +254,7 @@
// Collect tidy/obj targets from the 'final' modules.
ctx.VisitAllModules(func(module android.Module) {
- if module == ctx.FinalModule(module) {
+ if ctx.IsFinalModule(module) {
collectTidyObjModuleTargets(ctx, module, tidyModulesInDirGroup, objModulesInDirGroup)
}
})
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index e7dff40..4a2adf0 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -166,6 +166,7 @@
Target: ctx.Target(),
TableOfContents: p.tocFile,
+ IsStubs: false,
})
p.libraryDecorator.flagExporter.setProvider(ctx)
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 938f3bd..be943a3 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -78,6 +78,8 @@
ctx.RegisterModuleType("prebuilt_odm", PrebuiltOdmFactory)
ctx.RegisterModuleType("prebuilt_vendor_dlkm", PrebuiltVendorDlkmFactory)
ctx.RegisterModuleType("prebuilt_bt_firmware", PrebuiltBtFirmwareFactory)
+ ctx.RegisterModuleType("prebuilt_tvservice", PrebuiltTvServiceFactory)
+ ctx.RegisterModuleType("prebuilt_optee", PrebuiltOpteeFactory)
ctx.RegisterModuleType("prebuilt_defaults", defaultsFactory)
@@ -135,15 +137,6 @@
// Install symlinks to the installed file.
Symlinks []string `android:"arch_variant"`
- // Install to partition system_dlkm when set to true.
- System_dlkm_specific *bool `android:"arch_variant"`
-
- // Install to partition vendor_dlkm when set to true.
- Vendor_dlkm_specific *bool `android:"arch_variant"`
-
- // Install to partition odm_dlkm when set to true.
- Odm_dlkm_specific *bool `android:"arch_variant"`
-
// Install to partition oem when set to true.
Oem_specific *bool `android:"arch_variant"`
}
@@ -384,13 +377,7 @@
}
baseInstallDirPath := android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir())
// TODO(b/377304441)
- if android.Bool(p.properties.System_dlkm_specific) {
- baseInstallDirPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().SystemDlkmPath(), p.installBaseDir(ctx), p.SubDir())
- } else if android.Bool(p.properties.Vendor_dlkm_specific) {
- baseInstallDirPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().VendorDlkmPath(), p.installBaseDir(ctx), p.SubDir())
- } else if android.Bool(p.properties.Odm_dlkm_specific) {
- baseInstallDirPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().OdmDlkmPath(), p.installBaseDir(ctx), p.SubDir())
- } else if android.Bool(p.properties.Oem_specific) {
+ if android.Bool(p.properties.Oem_specific) {
baseInstallDirPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().OemPath(), p.installBaseDir(ctx), p.SubDir())
}
@@ -954,3 +941,23 @@
android.InitDefaultableModule(module)
return module
}
+
+// prebuilt_tvservice installs files in <partition>/tvservice directory.
+func PrebuiltTvServiceFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "tvservice")
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+// prebuilt_optee installs files in <partition>/optee directory.
+func PrebuiltOpteeFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "optee")
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 9071272..2645dc4 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -34,6 +34,8 @@
Vendor_partition_name *string
// Name of the Odm partition filesystem module
Odm_partition_name *string
+ // The vbmeta partition and its "chained" partitions
+ Vbmeta_partitions []string
}
type androidDevice struct {
@@ -46,7 +48,6 @@
module := &androidDevice{}
module.AddProperties(&module.partitionProps)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-
return module
}
@@ -69,6 +70,9 @@
addDependencyIfDefined(a.partitionProps.Product_partition_name)
addDependencyIfDefined(a.partitionProps.Vendor_partition_name)
addDependencyIfDefined(a.partitionProps.Odm_partition_name)
+ for _, vbmetaPartition := range a.partitionProps.Vbmeta_partitions {
+ ctx.AddDependency(ctx.Module(), filesystemDepTag, vbmetaPartition)
+ }
}
func (a *androidDevice) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 6ed962f..e84139b 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -147,6 +147,8 @@
Erofs ErofsProperties
+ F2fs F2fsProperties
+
Linkerconfig LinkerConfigProperties
// Determines if the module is auto-generated from Soong or not. If the module is
@@ -166,6 +168,11 @@
Sparse *bool
}
+// Additional properties required to generate f2fs FS partitions.
+type F2fsProperties struct {
+ Sparse *bool
+}
+
type LinkerConfigProperties struct {
// Build a linker.config.pb file
@@ -227,6 +234,7 @@
const (
ext4Type fsType = iota
erofsType
+ f2fsType
compressedCpioType
cpioType // uncompressed
unknown
@@ -249,6 +257,8 @@
return ext4Type
case "erofs":
return erofsType
+ case "f2fs":
+ return f2fsType
case "compressed_cpio":
return compressedCpioType
case "cpio":
@@ -289,7 +299,7 @@
func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
validatePartitionType(ctx, f)
switch f.fsType(ctx) {
- case ext4Type, erofsType:
+ case ext4Type, erofsType, f2fsType:
f.output = f.buildImageUsingBuildImage(ctx)
case compressedCpioType:
f.output = f.buildCpioImage(ctx, true)
@@ -505,6 +515,8 @@
return "ext4"
case erofsType:
return "erofs"
+ case f2fsType:
+ return "f2fs"
}
panic(fmt.Errorf("unsupported fs type %v", t))
}
@@ -554,8 +566,11 @@
addStr("uuid", uuid)
addStr("hash_seed", uuid)
}
- // Add erofs properties
- if f.fsType(ctx) == erofsType {
+
+ fst := f.fsType(ctx)
+ switch fst {
+ case erofsType:
+ // Add erofs properties
if compressor := f.properties.Erofs.Compressor; compressor != nil {
addStr("erofs_default_compressor", proptools.String(compressor))
}
@@ -566,17 +581,39 @@
// https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b
addStr("erofs_sparse_flag", "-s")
}
- } else if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil {
- // Raise an exception if the propfile contains erofs properties, but the fstype is not erofs
- fs := fsTypeStr(f.fsType(ctx))
- ctx.PropertyErrorf("erofs", "erofs is non-empty, but FS type is %s\n. Please delete erofs properties if this partition should use %s\n", fs, fs)
+ case f2fsType:
+ if proptools.BoolDefault(f.properties.F2fs.Sparse, true) {
+ // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2294;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
+ addStr("f2fs_sparse_flag", "-S")
+ }
}
+ f.checkFsTypePropertyError(ctx, fst, fsTypeStr(fst))
propFile = android.PathForModuleOut(ctx, "prop").OutputPath
android.WriteFileRuleVerbatim(ctx, propFile, propFileString.String())
return propFile, deps
}
+// This method checks if there is any property set for the fstype(s) other than
+// the current fstype.
+func (f *filesystem) checkFsTypePropertyError(ctx android.ModuleContext, t fsType, fs string) {
+ raiseError := func(otherFsType, currentFsType string) {
+ errMsg := fmt.Sprintf("%s is non-empty, but FS type is %s\n. Please delete %s properties if this partition should use %s\n", otherFsType, currentFsType, otherFsType, currentFsType)
+ ctx.PropertyErrorf(otherFsType, errMsg)
+ }
+
+ if t != erofsType {
+ if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil {
+ raiseError("erofs", fs)
+ }
+ }
+ if t != f2fsType {
+ if f.properties.F2fs.Sparse != nil {
+ raiseError("f2fs", fs)
+ }
+ }
+}
+
func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.OutputPath {
if proptools.Bool(f.properties.Use_avb) {
ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 29f9373..801a175 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -585,6 +585,35 @@
android.AssertStringDoesContain(t, "erofs fs type sparse", buildImageConfig, "erofs_sparse_flag=-s")
}
+func TestF2fsPartition(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ android_filesystem {
+ name: "f2fs_partition",
+ type: "f2fs",
+ }
+ `)
+
+ partition := result.ModuleForTests("f2fs_partition", "android_common")
+ buildImageConfig := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("prop"))
+ android.AssertStringDoesContain(t, "f2fs fs type", buildImageConfig, "fs_type=f2fs")
+ android.AssertStringDoesContain(t, "f2fs fs type sparse", buildImageConfig, "f2fs_sparse_flag=-S")
+}
+
+func TestFsTypesPropertyError(t *testing.T) {
+ fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+ "erofs: erofs is non-empty, but FS type is f2fs\n. Please delete erofs properties if this partition should use f2fs\n")).
+ RunTestWithBp(t, `
+ android_filesystem {
+ name: "f2fs_partition",
+ type: "f2fs",
+ erofs: {
+ compressor: "lz4hc,9",
+ compress_hints: "compress_hints.txt",
+ },
+ }
+ `)
+}
+
// If a system_ext/ module depends on system/ module, the dependency should *not*
// be installed in system_ext/
func TestDoNotPackageCrossPartitionDependencies(t *testing.T) {
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 0bae479..6a3fc1f 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -25,19 +25,19 @@
)
func init() {
- android.RegisterModuleType("vbmeta", vbmetaFactory)
+ android.RegisterModuleType("vbmeta", VbmetaFactory)
}
type vbmeta struct {
android.ModuleBase
- properties vbmetaProperties
+ properties VbmetaProperties
output android.OutputPath
installDir android.InstallPath
}
-type vbmetaProperties struct {
+type VbmetaProperties struct {
// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
Partition_name *string
@@ -50,9 +50,8 @@
// Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096.
Algorithm *string
- // File whose content will provide the rollback index. If unspecified, the rollback index
- // is from PLATFORM_SECURITY_PATCH
- Rollback_index_file *string `android:"path"`
+ // The rollback index. If unspecified, the rollback index is from PLATFORM_SECURITY_PATCH
+ Rollback_index *int64
// Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0.
Rollback_index_location *int64
@@ -62,7 +61,7 @@
Partitions proptools.Configurable[[]string]
// List of chained partitions that this vbmeta deletages the verification.
- Chained_partitions []chainedPartitionProperties
+ Chained_partitions []ChainedPartitionProperties
// List of key-value pair of avb properties
Avb_properties []avbProperty
@@ -76,7 +75,7 @@
Value *string
}
-type chainedPartitionProperties struct {
+type ChainedPartitionProperties struct {
// Name of the chained partition
Name *string
@@ -95,7 +94,7 @@
}
// vbmeta is the partition image that has the verification information for other partitions.
-func vbmetaFactory() android.Module {
+func VbmetaFactory() android.Module {
module := &vbmeta{}
module.AddProperties(&module.properties)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
@@ -217,15 +216,12 @@
// Returns the embedded shell command that prints the rollback index
func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string {
- var cmd string
- if v.properties.Rollback_index_file != nil {
- f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file))
- cmd = "cat " + f.String()
+ if v.properties.Rollback_index != nil {
+ return fmt.Sprintf("%d", *v.properties.Rollback_index)
} else {
- cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s"
+ // Take the first line and remove the newline char
+ return "$(date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s | head -1 | tr -d '\n'" + ")"
}
- // Take the first line and remove the newline char
- return "$(" + cmd + " | head -1 | tr -d '\n'" + ")"
}
// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
index 690ad28..8cd7518 100644
--- a/fsgen/Android.bp
+++ b/fsgen/Android.bp
@@ -16,6 +16,7 @@
"filesystem_creator.go",
"fsgen_mutators.go",
"prebuilt_etc_modules_gen.go",
+ "vbmeta_partitions.go",
],
testSrcs: [
"filesystem_creator_test.go",
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 5d88767..c9bbf3f 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -44,6 +44,9 @@
type filesystemCreatorProps struct {
Generated_partition_types []string `blueprint:"mutated"`
Unsupported_partition_types []string `blueprint:"mutated"`
+
+ Vbmeta_module_names []string `blueprint:"mutated"`
+ Vbmeta_partition_names []string `blueprint:"mutated"`
}
type filesystemCreator struct {
@@ -67,16 +70,24 @@
}
func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
- soongGeneratedPartitions := &ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions
- for _, partitionType := range *soongGeneratedPartitions {
+ soongGeneratedPartitions := generatedPartitions(ctx)
+ finalSoongGeneratedPartitions := make([]string, 0, len(soongGeneratedPartitions))
+ for _, partitionType := range soongGeneratedPartitions {
if f.createPartition(ctx, partitionType) {
f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
+ finalSoongGeneratedPartitions = append(finalSoongGeneratedPartitions, partitionType)
} else {
f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
- _, *soongGeneratedPartitions = android.RemoveFromList(partitionType, *soongGeneratedPartitions)
}
}
- f.createDeviceModule(ctx)
+
+ for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
+ f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
+ f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
+ }
+
+ ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
+ f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names)
}
func generatedModuleName(cfg android.Config, suffix string) string {
@@ -91,7 +102,11 @@
return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
}
-func (f *filesystemCreator) createDeviceModule(ctx android.LoadHookContext) {
+func (f *filesystemCreator) createDeviceModule(
+ ctx android.LoadHookContext,
+ generatedPartitionTypes []string,
+ vbmetaPartitions []string,
+) {
baseProps := &struct {
Name *string
}{
@@ -100,21 +115,22 @@
// Currently, only the system and system_ext partition module is created.
partitionProps := &filesystem.PartitionNameProperties{}
- if android.InList("system", f.properties.Generated_partition_types) {
+ if android.InList("system", generatedPartitionTypes) {
partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
}
- if android.InList("system_ext", f.properties.Generated_partition_types) {
+ if android.InList("system_ext", generatedPartitionTypes) {
partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
}
- if android.InList("vendor", f.properties.Generated_partition_types) {
+ if android.InList("vendor", generatedPartitionTypes) {
partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
}
- if android.InList("product", f.properties.Generated_partition_types) {
+ if android.InList("product", generatedPartitionTypes) {
partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
}
- if android.InList("odm", f.properties.Generated_partition_types) {
+ if android.InList("odm", generatedPartitionTypes) {
partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
}
+ partitionProps.Vbmeta_partitions = vbmetaPartitions
ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
}
@@ -136,6 +152,26 @@
"framework/oat/*/*", // framework/oat/{arch}
}
fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"}
+ // TODO(b/377734331): only generate the symlinks if the relevant partitions exist
+ fsProps.Symlinks = []filesystem.SymlinkDefinition{
+ filesystem.SymlinkDefinition{
+ Target: proptools.StringPtr("/product"),
+ Name: proptools.StringPtr("system/product"),
+ },
+ filesystem.SymlinkDefinition{
+ Target: proptools.StringPtr("/system_ext"),
+ Name: proptools.StringPtr("system/system_ext"),
+ },
+ filesystem.SymlinkDefinition{
+ Target: proptools.StringPtr("/vendor"),
+ Name: proptools.StringPtr("system/vendor"),
+ },
+ filesystem.SymlinkDefinition{
+ Target: proptools.StringPtr("/system_dlkm/lib/modules"),
+ Name: proptools.StringPtr("system/lib/modules"),
+ },
+ }
+ fsProps.Base_dir = proptools.StringPtr("system")
case "system_ext":
fsProps.Fsverity.Inputs = []string{
"framework/*",
@@ -205,60 +241,78 @@
}
module.HideFromMake()
if partitionType == "vendor" {
- // Create a build prop for vendor
- vendorBuildProps := &struct {
- Name *string
- Vendor *bool
- Stem *string
- Product_config *string
- }{
- Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
- Vendor: proptools.BoolPtr(true),
- Stem: proptools.StringPtr("build.prop"),
- Product_config: proptools.StringPtr(":product_config"),
- }
- vendorBuildProp := ctx.CreateModule(
- android.BuildPropFactory,
- vendorBuildProps,
- )
- vendorBuildProp.HideFromMake()
+ f.createVendorBuildProp(ctx)
}
return true
}
// createPrebuiltKernelModules creates `prebuilt_kernel_modules`. These modules will be added to deps of the
-// autogenerated *_dlkm filsystem modules.
-// The input `kernelModules` is a space separated list of .ko files in the workspace. This will be partitioned per directory
-// and a `prebuilt_kernel_modules` will be created per partition.
-// These autogenerated modules will be subsequently added to the deps of the top level *_dlkm android_filesystem
+// autogenerated *_dlkm filsystem modules. Each _dlkm partition should have a single prebuilt_kernel_modules dependency.
+// This ensures that the depmod artifacts (modules.* installed in /lib/modules/) are generated with a complete view.
+
+// The input `kernelModules` is a space separated list of .ko files in the workspace.
func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string, kernelModules []string) {
- // Partition the files per directory
- dirToFiles := map[string][]string{}
- for _, kernelModule := range kernelModules {
- dir := filepath.Dir(kernelModule)
- base := filepath.Base(kernelModule)
- dirToFiles[dir] = append(dirToFiles[dir], base)
- }
- // Create a prebuilt_kernel_modules module per partition
fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
- for index, dir := range android.SortedKeys(dirToFiles) {
- name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules-%s", partitionType, strconv.Itoa(index)))
- props := &struct {
- Name *string
- Srcs []string
- }{
- Name: proptools.StringPtr(name),
- Srcs: dirToFiles[dir],
- }
- kernelModule := ctx.CreateModuleInDirectory(
- kernel.PrebuiltKernelModulesFactory,
- dir,
- props,
- )
- kernelModule.HideFromMake()
- // Add to deps
- (*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
+ name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules", partitionType))
+ props := &struct {
+ Name *string
+ Srcs []string
+ }{
+ Name: proptools.StringPtr(name),
+ Srcs: kernelModules,
}
+ kernelModule := ctx.CreateModuleInDirectory(
+ kernel.PrebuiltKernelModulesFactory,
+ ".", // create in root directory for now
+ props,
+ )
+ kernelModule.HideFromMake()
+ // Add to deps
+ (*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
+}
+
+// Create a build_prop and android_info module. This will be used to create /vendor/build.prop
+func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
+ // Create a android_info for vendor
+ // The board info files might be in a directory outside the root soong namespace, so create
+ // the module in "."
+ partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+ androidInfoProps := &struct {
+ Name *string
+ Board_info_files []string
+ Bootloader_board_name *string
+ }{
+ Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "android-info.prop")),
+ Board_info_files: partitionVars.BoardInfoFiles,
+ }
+ if len(androidInfoProps.Board_info_files) == 0 {
+ androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
+ }
+ androidInfoProp := ctx.CreateModuleInDirectory(
+ android.AndroidInfoFactory,
+ ".",
+ androidInfoProps,
+ )
+ androidInfoProp.HideFromMake()
+ // Create a build prop for vendor
+ vendorBuildProps := &struct {
+ Name *string
+ Vendor *bool
+ Stem *string
+ Product_config *string
+ Android_info *string
+ }{
+ Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
+ Vendor: proptools.BoolPtr(true),
+ Stem: proptools.StringPtr("build.prop"),
+ Product_config: proptools.StringPtr(":product_config"),
+ Android_info: proptools.StringPtr(":" + androidInfoProp.Name()),
+ }
+ vendorBuildProp := ctx.CreateModule(
+ android.BuildPropFactory,
+ vendorBuildProps,
+ )
+ vendorBuildProp.HideFromMake()
}
// createLinkerConfigSourceFilegroups creates filegroup modules to generate linker.config.pb for the following partitions
@@ -306,12 +360,15 @@
type filesystemBaseProperty struct {
Name *string
Compile_multilib *string
+ Visibility []string
}
func generateBaseProps(namePtr *string) *filesystemBaseProperty {
return &filesystemBaseProperty{
Name: namePtr,
Compile_multilib: proptools.StringPtr("both"),
+ // The vbmeta modules are currently in the root directory and depend on the partitions
+ Visibility: []string{"//.", "//build/soong:__subpackages__"},
}
}
@@ -378,18 +435,13 @@
ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
}
makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
- // For now, don't allowlist anything. The test will fail, but that's fine in the current
- // early stages where we're just figuring out what we need
- emptyAllowlistFile := android.PathForModuleOut(ctx, fmt.Sprintf("allowlist_%s.txt", partitionModuleName))
- android.WriteFileRule(ctx, emptyAllowlistFile, "")
diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().BuiltTool("file_list_diff").
Input(makeFileList).
Input(filesystemInfo.FileListFile).
- Text(partitionModuleName).
- FlagWithInput("--allowlists ", emptyAllowlistFile)
+ Text(partitionModuleName)
builder.Command().Text("touch").Output(diffTestResultFile)
builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test")
return diffTestResultFile
@@ -407,16 +459,42 @@
return file
}
+func createVbmetaDiff(ctx android.ModuleContext, vbmetaModuleName string, vbmetaPartitionName string) android.Path {
+ vbmetaModule := ctx.GetDirectDepWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag)
+ outputFilesProvider, ok := android.OtherModuleProvider(ctx, vbmetaModule, android.OutputFilesProvider)
+ if !ok {
+ ctx.ModuleErrorf("Expected module %s to provide OutputFiles", vbmetaModule)
+ }
+ if len(outputFilesProvider.DefaultOutputFiles) != 1 {
+ ctx.ModuleErrorf("Expected 1 output file from module %s", vbmetaModule)
+ }
+ soongVbMetaFile := outputFilesProvider.DefaultOutputFiles[0]
+ makeVbmetaFile := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/%s.img", ctx.Config().DeviceName(), vbmetaPartitionName))
+
+ diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", vbmetaModuleName))
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("diff").
+ Input(soongVbMetaFile).
+ Input(makeVbmetaFile)
+ builder.Command().Text("touch").Output(diffTestResultFile)
+ builder.Build(vbmetaModuleName+" diff test", vbmetaModuleName+" diff test")
+ return diffTestResultFile
+}
+
type systemImageDepTagType struct {
blueprint.BaseDependencyTag
}
var generatedFilesystemDepTag systemImageDepTagType
+var generatedVbmetaPartitionDepTag systemImageDepTagType
func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
for _, partitionType := range f.properties.Generated_partition_types {
ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType))
}
+ for _, vbmetaModule := range f.properties.Vbmeta_module_names {
+ ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
+ }
}
func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -446,6 +524,11 @@
diffTestFiles = append(diffTestFiles, diffTestFile)
ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
}
+ for i, vbmetaModule := range f.properties.Vbmeta_module_names {
+ diffTestFile := createVbmetaDiff(ctx, vbmetaModule, f.properties.Vbmeta_partition_names[i])
+ diffTestFiles = append(diffTestFiles, diffTestFile)
+ ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", f.properties.Vbmeta_partition_names[i]), diffTestFile)
+ }
ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
}
diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go
index bf07ab3..92ea128 100644
--- a/fsgen/fsgen_mutators.go
+++ b/fsgen/fsgen_mutators.go
@@ -15,11 +15,12 @@
package fsgen
import (
- "android/soong/android"
"fmt"
"slices"
"sync"
+ "android/soong/android"
+
"github.com/google/blueprint/proptools"
)
@@ -113,7 +114,6 @@
"dex_bootjars": defaultDepCandidateProps(ctx.Config()),
"framework_compatibility_matrix.device.xml": defaultDepCandidateProps(ctx.Config()),
"init.environ.rc-soong": defaultDepCandidateProps(ctx.Config()),
- "libclang_rt.asan": defaultDepCandidateProps(ctx.Config()),
"libcompiler_rt": defaultDepCandidateProps(ctx.Config()),
"libdmabufheap": defaultDepCandidateProps(ctx.Config()),
"libgsi": defaultDepCandidateProps(ctx.Config()),
@@ -145,7 +145,6 @@
},
"system_dlkm": {},
},
- soongGeneratedPartitions: generatedPartitions(ctx),
fsDepsMutex: sync.Mutex{},
moduleToInstallationProps: map[string]installationProperties{},
}
diff --git a/fsgen/prebuilt_etc_modules_gen.go b/fsgen/prebuilt_etc_modules_gen.go
index 1df7ec8..362ac31 100644
--- a/fsgen/prebuilt_etc_modules_gen.go
+++ b/fsgen/prebuilt_etc_modules_gen.go
@@ -176,11 +176,13 @@
"lib/rfsa": etc.PrebuiltRFSAFactory,
"media": etc.PrebuiltMediaFactory,
"odm": etc.PrebuiltOdmFactory,
+ "optee": etc.PrebuiltOpteeFactory,
"overlay": etc.PrebuiltOverlayFactory,
"priv-app": etc.PrebuiltPrivAppFactory,
"res": etc.PrebuiltResFactory,
"rfs": etc.PrebuiltRfsFactory,
"tts": etc.PrebuiltVoicepackFactory,
+ "tvservice": etc.PrebuiltTvServiceFactory,
"usr/share": etc.PrebuiltUserShareFactory,
"usr/hyphen-data": etc.PrebuiltUserHyphenDataFactory,
"usr/keylayout": etc.PrebuiltUserKeyLayoutFactory,
diff --git a/fsgen/vbmeta_partitions.go b/fsgen/vbmeta_partitions.go
new file mode 100644
index 0000000..280e405
--- /dev/null
+++ b/fsgen/vbmeta_partitions.go
@@ -0,0 +1,184 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+ "android/soong/android"
+ "android/soong/filesystem"
+ "slices"
+ "strconv"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type vbmetaModuleInfo struct {
+ // The name of the generated vbmeta module
+ moduleName string
+ // The name of the module that avb understands. This is the name passed to --chain_partition,
+ // and also the basename of the output file. (the output file is called partitionName + ".img")
+ partitionName string
+}
+
+// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names
+// that the function created. May return nil if the product isn't using avb.
+//
+// AVB is Android Verified Boot: https://source.android.com/docs/security/features/verifiedboot
+// It works by signing all the partitions, but then also including an extra metadata paritition
+// called vbmeta that depends on all the other signed partitions. This creates a requirement
+// that you update all those partitions and the vbmeta partition together, so in order to relax
+// that requirement products can set up "chained" vbmeta partitions, where a chained partition
+// like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish
+// vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions,
+// that group of partitions can be updated independently from the other signed partitions.
+func createVbmetaPartitions(ctx android.LoadHookContext, generatedPartitionTypes []string) []vbmetaModuleInfo {
+ partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+ // Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false
+ if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable {
+ return nil
+ }
+
+ var result []vbmetaModuleInfo
+
+ var chainedPartitions []filesystem.ChainedPartitionProperties
+ var partitionTypesHandledByChainedPartitions []string
+ for chainedName, props := range partitionVars.ChainedVbmetaPartitions {
+ chainedName = "vbmeta_" + chainedName
+ if len(props.Partitions) == 0 {
+ continue
+ }
+ if len(props.Key) == 0 {
+ ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName)
+ continue
+ }
+ if len(props.Algorithm) == 0 {
+ ctx.ModuleErrorf("No algorithm found for chained avb partition %q", chainedName)
+ continue
+ }
+ if len(props.RollbackIndex) == 0 {
+ ctx.ModuleErrorf("No rollback index found for chained avb partition %q", chainedName)
+ continue
+ }
+ ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32)
+ if err != nil {
+ ctx.ModuleErrorf("Rollback index location must be an int, got %q", props.RollbackIndexLocation)
+ continue
+ }
+ // The default is to use the PlatformSecurityPatch, and a lot of product config files
+ // just set it to the platform security patch, so detect that and don't set the property
+ // in soong.
+ var rollbackIndex *int64
+ if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() {
+ i, err := strconv.ParseInt(props.RollbackIndex, 10, 32)
+ if err != nil {
+ ctx.ModuleErrorf("Rollback index must be an int, got %q", props.RollbackIndex)
+ continue
+ }
+ rollbackIndex = &i
+ }
+
+ var partitionModules []string
+ for _, partition := range props.Partitions {
+ partitionTypesHandledByChainedPartitions = append(partitionTypesHandledByChainedPartitions, partition)
+ if !slices.Contains(generatedPartitionTypes, partition) {
+ // The partition is probably unsupported.
+ continue
+ }
+ partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partition))
+ }
+
+ name := generatedModuleName(ctx.Config(), chainedName)
+ ctx.CreateModuleInDirectory(
+ filesystem.VbmetaFactory,
+ ".", // Create in the root directory for now so its easy to get the key
+ &filesystem.VbmetaProperties{
+ Partition_name: proptools.StringPtr(chainedName),
+ Stem: proptools.StringPtr(chainedName + ".img"),
+ Private_key: proptools.StringPtr(props.Key),
+ Algorithm: &props.Algorithm,
+ Rollback_index: rollbackIndex,
+ Rollback_index_location: &ril,
+ Partitions: proptools.NewSimpleConfigurable(partitionModules),
+ }, &struct {
+ Name *string
+ }{
+ Name: &name,
+ },
+ ).HideFromMake()
+
+ chainedPartitions = append(chainedPartitions, filesystem.ChainedPartitionProperties{
+ Name: &chainedName,
+ Rollback_index_location: &ril,
+ Private_key: &props.Key,
+ })
+
+ result = append(result, vbmetaModuleInfo{
+ moduleName: name,
+ partitionName: chainedName,
+ })
+ }
+
+ vbmetaModuleName := generatedModuleName(ctx.Config(), "vbmeta")
+
+ var algorithm *string
+ var ri *int64
+ var key *string
+ if len(partitionVars.BoardAvbKeyPath) == 0 {
+ // Match make's defaults: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4568;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+ key = proptools.StringPtr("external/avb/test/data/testkey_rsa4096.pem")
+ algorithm = proptools.StringPtr("SHA256_RSA4096")
+ } else {
+ key = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
+ algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
+ }
+ if len(partitionVars.BoardAvbRollbackIndex) > 0 {
+ parsedRi, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32)
+ if err != nil {
+ ctx.ModuleErrorf("Rollback index location must be an int, got %q", partitionVars.BoardAvbRollbackIndex)
+ }
+ ri = &parsedRi
+ }
+
+ var partitionModules []string
+ for _, partitionType := range generatedPartitionTypes {
+ if slices.Contains(partitionTypesHandledByChainedPartitions, partitionType) {
+ // Already handled by a chained vbmeta partition
+ continue
+ }
+ partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partitionType))
+ }
+
+ ctx.CreateModuleInDirectory(
+ filesystem.VbmetaFactory,
+ ".", // Create in the root directory for now so its easy to get the key
+ &filesystem.VbmetaProperties{
+ Stem: proptools.StringPtr("vbmeta.img"),
+ Algorithm: algorithm,
+ Private_key: key,
+ Rollback_index: ri,
+ Chained_partitions: chainedPartitions,
+ Partitions: proptools.NewSimpleConfigurable(partitionModules),
+ }, &struct {
+ Name *string
+ }{
+ Name: &vbmetaModuleName,
+ },
+ ).HideFromMake()
+
+ result = append(result, vbmetaModuleInfo{
+ moduleName: vbmetaModuleName,
+ partitionName: "vbmeta",
+ })
+ return result
+}
diff --git a/java/aar.go b/java/aar.go
index 66ca00a..b5cdde3 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -77,7 +77,7 @@
// list of directories relative to the Blueprints file containing
// Android resources. Defaults to ["res"] if a directory called res exists.
// Set to [] to disable the default.
- Resource_dirs []string `android:"path"`
+ Resource_dirs proptools.Configurable[[]string] `android:"path"`
// list of zip files containing Android resources.
Resource_zips []string `android:"path"`
@@ -275,7 +275,7 @@
IncludeDirs: false,
})
assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
- resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
+ resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs.GetOrDefault(ctx, nil), "res")
resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
// Glob directories into lists of paths
diff --git a/java/app.go b/java/app.go
index e01a2ba..94c9e5b 100644
--- a/java/app.go
+++ b/java/app.go
@@ -67,6 +67,9 @@
// TestHelperApp is true if the module is a android_test_helper_app
TestHelperApp bool
+
+ // EmbeddedJNILibs is the list of paths to JNI libraries that were embedded in the APK.
+ EmbeddedJNILibs android.Paths
}
var AppInfoProvider = blueprint.NewProvider[*AppInfo]()
@@ -405,9 +408,18 @@
a.checkEmbedJnis(ctx)
a.generateAndroidBuildActions(ctx)
a.generateJavaUsedByApex(ctx)
+
+ var embeddedJniLibs []android.Path
+
+ if a.embeddedJniLibs {
+ for _, jni := range a.jniLibs {
+ embeddedJniLibs = append(embeddedJniLibs, jni.path)
+ }
+ }
android.SetProvider(ctx, AppInfoProvider, &AppInfo{
- Updatable: Bool(a.appProperties.Updatable),
- TestHelperApp: false,
+ Updatable: Bool(a.appProperties.Updatable),
+ TestHelperApp: false,
+ EmbeddedJNILibs: embeddedJniLibs,
})
}
@@ -1070,12 +1082,12 @@
app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
}
jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps,
- checkNativeSdkVersion, func(dep cc.LinkableInterface) bool {
- return !dep.IsNdk(ctx.Config()) && !dep.IsStubs()
- })
+ checkNativeSdkVersion, func(dep cc.LinkableInterface) bool { return !dep.IsNdk(ctx.Config()) && !dep.IsStubs() })
var certificates []Certificate
+ var directImplementationDeps android.Paths
+ var transitiveImplementationDeps []depset.DepSet[android.Path]
ctx.VisitDirectDeps(func(module android.Module) {
otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
@@ -1087,7 +1099,18 @@
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
}
}
+
+ if IsJniDepTag(tag) {
+ directImplementationDeps = append(directImplementationDeps, android.OutputFileForModule(ctx, module, ""))
+ if info, ok := android.OtherModuleProvider(ctx, module, cc.ImplementationDepInfoProvider); ok {
+ transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps)
+ }
+ }
})
+ android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+ ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps),
+ })
+
return jniLib, prebuiltJniPackages, certificates
}
@@ -1338,7 +1361,7 @@
Filter_product *string
Aaptflags []string
Manifest *string
- Resource_dirs []string
+ Resource_dirs proptools.Configurable[[]string]
Flags_packages []string
}{
Name: proptools.StringPtr(rroPackageName),
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 1a33680..7c0f544 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -520,7 +520,7 @@
// be output to Make but it does not really matter which variant is output. The default/platform
// variant is the first (ctx.PrimaryModule()) and is usually hidden from make so this just picks
// the last variant (ctx.FinalModule()).
- if ctx.Module() != ctx.FinalModule() {
+ if !ctx.IsFinalModule(ctx.Module()) {
b.HideFromMake()
}
}
diff --git a/java/java.go b/java/java.go
index 53b3481..1d572fa 100644
--- a/java/java.go
+++ b/java/java.go
@@ -606,10 +606,8 @@
} else if ctx.Device() {
return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
} else if ctx.Config().TargetsJava21() {
- // Temporary experimental flag to be able to try and build with
- // java version 21 options. The flag, if used, just sets Java
- // 21 as the default version, leaving any components that
- // target an older version intact.
+ // Build flag that controls whether Java 21 is used as the default
+ // target version, or Java 17.
return JAVA_VERSION_21
} else {
return JAVA_VERSION_17
@@ -1610,6 +1608,8 @@
j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
})
+ var directImplementationDeps android.Paths
+ var transitiveImplementationDeps []depset.DepSet[android.Path]
ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) {
sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider)
if sharedLibInfo.SharedLibrary != nil {
@@ -1628,11 +1628,20 @@
Output: relocatedLib,
})
j.data = append(j.data, relocatedLib)
+
+ directImplementationDeps = append(directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps)
+ }
} else {
ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
}
})
+ android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+ ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps),
+ })
+
j.Library.GenerateAndroidBuildActions(ctx)
}
diff --git a/java/sdk.go b/java/sdk.go
index 4537f19..036521c 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -65,11 +65,11 @@
return JAVA_VERSION_9
} else if sdk.FinalOrFutureInt() <= 33 {
return JAVA_VERSION_11
+ } else if sdk.FinalOrFutureInt() <= 35 {
+ return JAVA_VERSION_17
} else if ctx.Config().TargetsJava21() {
- // Temporary experimental flag to be able to try and build with
- // java version 21 options. The flag, if used, just sets Java
- // 21 as the default version, leaving any components that
- // target an older version intact.
+ // Build flag that controls whether Java 21 is used as the
+ // default target version, or Java 17.
return JAVA_VERSION_21
} else {
return JAVA_VERSION_17
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 3944495..d590579 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -252,7 +252,7 @@
// Module defined clang flags and include paths
cflags = append(cflags, esc(cflagsProp)...)
- for _, include := range b.ClangProperties.Local_include_dirs {
+ for _, include := range b.ClangProperties.Local_include_dirs.GetOrDefault(ctx, nil) {
cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String())
implicits = append(implicits, android.PathForModuleSrc(ctx, include))
}
diff --git a/rust/library.go b/rust/library.go
index 20cd2af..bd3359b 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -617,6 +617,9 @@
TableOfContents: android.OptionalPathForPath(tocFile),
SharedLibrary: outputFile,
Target: ctx.Target(),
+ // TODO: when rust supports stubs uses the stubs state rather than inferring it from
+ // apex_exclude.
+ IsStubs: Bool(library.Properties.Apex_exclude),
})
}
diff --git a/rust/rust.go b/rust/rust.go
index 9e06cd4..ed38ad7 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -454,6 +454,9 @@
// Paths to generated source files
SrcDeps android.Paths
srcProviderFiles android.Paths
+
+ directImplementationDeps android.Paths
+ transitiveImplementationDeps []depset.DepSet[android.Path]
}
type RustLibraries []RustLibrary
@@ -991,6 +994,10 @@
}
+ android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+ ImplementationDeps: depset.New(depset.PREORDER, deps.directImplementationDeps, deps.transitiveImplementationDeps),
+ })
+
ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
}
@@ -1244,6 +1251,11 @@
mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName))
+ depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+ }
+
case depTag == rlibDepTag:
rlib, ok := rustDep.compiler.(libraryInterface)
if !ok || !rlib.rlib() {
@@ -1259,6 +1271,11 @@
depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+ // rlibs are not installed, so don't add the output file to directImplementationDeps
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+ }
+
case depTag == procMacroDepTag:
directProcMacroDeps = append(directProcMacroDeps, rustDep)
mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
@@ -1391,6 +1408,13 @@
// dependency crosses the APEX boundaries).
sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep)
+ if !sharedLibraryInfo.IsStubs {
+ depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+ }
+ }
+
// Re-get linkObject as ChooseStubOrImpl actually tells us which
// object (either from stub or non-stub) to use.
linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
index e0686ed..0b7780e 100644
--- a/scripts/gen_build_prop.py
+++ b/scripts/gen_build_prop.py
@@ -608,6 +608,8 @@
build_product_prop(args)
case "vendor":
build_vendor_prop(args)
+ case "system_dlkm" | "vendor_dlkm" | "odm_dlkm":
+ build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=[])
case _:
sys.exit(f"not supported partition {args.partition}")
diff --git a/tests/lib.sh b/tests/lib.sh
index 4c320d0..0e26de5 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -91,7 +91,6 @@
}
function create_mock_soong {
- create_mock_bazel
copy_directory build/blueprint
copy_directory build/soong
copy_directory build/make
@@ -143,41 +142,6 @@
USE_RBE=false TARGET_PRODUCT=aosp_arm TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=userdebug build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
}
-function create_mock_bazel {
- copy_directory build/bazel
- copy_directory build/bazel_common_rules
-
- # This requires pulling more tools into the mock top to build partitions
- delete_directory build/bazel/examples/partitions
-
- symlink_directory packages/modules/common/build
- symlink_directory prebuilts/bazel
- symlink_directory prebuilts/clang
- symlink_directory prebuilts/jdk
- symlink_directory external/bazel-skylib
- symlink_directory external/bazelbuild-rules_android
- symlink_directory external/bazelbuild-rules_go
- symlink_directory external/bazelbuild-rules_license
- symlink_directory external/bazelbuild-kotlin-rules
- symlink_directory external/bazelbuild-rules_cc
- symlink_directory external/bazelbuild-rules_python
- symlink_directory external/bazelbuild-rules_java
- symlink_directory external/bazelbuild-rules_rust
- symlink_directory external/bazelbuild-rules_testing
- symlink_directory external/rust/crates/tinyjson
-
- symlink_file WORKSPACE
- symlink_file BUILD
-}
-
-function run_bazel {
- # Remove the ninja_build output marker file to communicate to buildbot that this is not a regular Ninja build, and its
- # output should not be parsed as such.
- rm -rf out/ninja_build
-
- build/bazel/bin/bazel "$@"
-}
-
function run_ninja {
build/soong/soong_ui.bash --make-mode --skip-config --soong-only --skip-soong-tests "$@"
}
diff --git a/tradefed_modules/Android.bp b/tradefed_modules/Android.bp
index 9969ae2..67d91b2 100644
--- a/tradefed_modules/Android.bp
+++ b/tradefed_modules/Android.bp
@@ -13,9 +13,11 @@
],
srcs: [
"test_module_config.go",
+ "test_suite.go",
],
testSrcs: [
"test_module_config_test.go",
+ "test_suite_test.go",
],
pluginFor: ["soong_build"],
}
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index 7a04c19..5c13d64 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -383,6 +383,19 @@
// 4) Module.config / AndroidTest.xml
m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
+
+ // 5) We provide so we can be listed in test_suites.
+ android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
+ InstalledFiles: m.supportFiles.Paths(),
+ OutputFile: baseApk,
+ TestConfig: m.testConfig,
+ HostRequiredModuleNames: m.provider.HostRequiredModuleNames,
+ RequiredModuleNames: m.provider.RequiredModuleNames,
+ TestSuites: m.tradefedProperties.Test_suites,
+ IsHost: m.provider.IsHost,
+ LocalCertificate: m.provider.LocalCertificate,
+ IsUnitTest: m.provider.IsUnitTest,
+ })
}
var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
diff --git a/tradefed_modules/test_suite.go b/tradefed_modules/test_suite.go
new file mode 100644
index 0000000..00585f5
--- /dev/null
+++ b/tradefed_modules/test_suite.go
@@ -0,0 +1,173 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tradefed_modules
+
+import (
+ "encoding/json"
+ "path"
+ "path/filepath"
+
+ "android/soong/android"
+ "android/soong/tradefed"
+ "github.com/google/blueprint"
+)
+
+const testSuiteModuleType = "test_suite"
+
+type testSuiteTag struct{
+ blueprint.BaseDependencyTag
+}
+
+type testSuiteManifest struct {
+ Name string `json:"name"`
+ Files []string `json:"files"`
+}
+
+func init() {
+ RegisterTestSuiteBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType(testSuiteModuleType, TestSuiteFactory)
+}
+
+var PrepareForTestWithTestSuiteBuildComponents = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+)
+
+type testSuiteProperties struct {
+ Description string
+ Tests []string `android:"path,arch_variant"`
+}
+
+type testSuiteModule struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ testSuiteProperties
+}
+
+func (t *testSuiteModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ for _, test := range t.Tests {
+ if ctx.OtherModuleDependencyVariantExists(ctx.Config().BuildOSCommonTarget.Variations(), test) {
+ // Host tests.
+ ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testSuiteTag{}, test)
+ } else {
+ // Target tests.
+ ctx.AddDependency(ctx.Module(), testSuiteTag{}, test)
+ }
+ }
+}
+
+func (t *testSuiteModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ suiteName := ctx.ModuleName()
+ modulesByName := make(map[string]android.Module)
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ // Recurse into test_suite dependencies.
+ if ctx.OtherModuleType(child) == testSuiteModuleType {
+ ctx.Phony(suiteName, android.PathForPhony(ctx, child.Name()))
+ return true
+ }
+
+ // Only write out top level test suite dependencies here.
+ if _, ok := ctx.OtherModuleDependencyTag(child).(testSuiteTag); !ok {
+ return false
+ }
+
+ if !child.InstallInTestcases() {
+ ctx.ModuleErrorf("test_suite only supports modules installed in testcases. %q is not installed in testcases.", child.Name())
+ return false
+ }
+
+ modulesByName[child.Name()] = child
+ return false
+ })
+
+ var files []string
+ for name, module := range modulesByName {
+ // Get the test provider data from the child.
+ tp, ok := android.OtherModuleProvider(ctx, module, tradefed.BaseTestProviderKey)
+ if !ok {
+ // TODO: Consider printing out a list of all module types.
+ ctx.ModuleErrorf("%q is not a test module.", name)
+ continue
+ }
+
+ files = append(files, packageModuleFiles(ctx, suiteName, module, tp)...)
+ ctx.Phony(suiteName, android.PathForPhony(ctx, name))
+ }
+
+ manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
+ b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: files})
+ if err != nil {
+ ctx.ModuleErrorf("Failed to marshal manifest: %v", err)
+ return
+ }
+ android.WriteFileRule(ctx, manifestPath, string(b))
+
+ ctx.Phony(suiteName, manifestPath)
+}
+
+func TestSuiteFactory() android.Module {
+ module := &testSuiteModule{}
+ module.AddProperties(&module.testSuiteProperties)
+
+ android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+
+ return module
+}
+
+func packageModuleFiles(ctx android.ModuleContext, suiteName string, module android.Module, tp tradefed.BaseTestProviderData) []string {
+
+ hostOrTarget := "target"
+ if tp.IsHost {
+ hostOrTarget = "host"
+ }
+
+ // suiteRoot at out/soong/packaging/<suiteName>.
+ suiteRoot := android.PathForSuiteInstall(ctx, suiteName)
+
+ var installed android.InstallPaths
+ // Install links to installed files from the module.
+ if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
+ for _, f := range installFilesInfo.InstallFiles {
+ // rel is anything under .../<partition>, normally under .../testcases.
+ rel := android.Rel(ctx, f.PartitionDir(), f.String())
+
+ // Install the file under <suiteRoot>/<host|target>/<partition>.
+ installDir := suiteRoot.Join(ctx, hostOrTarget, f.Partition(), path.Dir(rel))
+ linkTo, err := filepath.Rel(installDir.String(), f.String())
+ if err != nil {
+ ctx.ModuleErrorf("Failed to get relative path from %s to %s: %v", installDir.String(), f.String(), err)
+ continue
+ }
+ installed = append(installed, ctx.InstallAbsoluteSymlink(installDir, path.Base(rel), linkTo))
+ }
+ }
+
+ // Install config file.
+ if tp.TestConfig != nil {
+ moduleRoot := suiteRoot.Join(ctx, hostOrTarget, "testcases", module.Name())
+ installed = append(installed, ctx.InstallFile(moduleRoot, module.Name() + ".config", tp.TestConfig))
+ }
+
+ // Add to phony and manifest, manifestpaths are relative to suiteRoot.
+ var manifestEntries []string
+ for _, f := range installed {
+ manifestEntries = append(manifestEntries, android.Rel(ctx, suiteRoot.String(), f.String()))
+ ctx.Phony(suiteName, f)
+ }
+ return manifestEntries
+}
diff --git a/tradefed_modules/test_suite_test.go b/tradefed_modules/test_suite_test.go
new file mode 100644
index 0000000..3c0a9eb
--- /dev/null
+++ b/tradefed_modules/test_suite_test.go
@@ -0,0 +1,151 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package tradefed_modules
+
+import (
+ "android/soong/android"
+ "android/soong/java"
+ "encoding/json"
+ "slices"
+ "testing"
+)
+
+func TestTestSuites(t *testing.T) {
+ t.Parallel()
+ ctx := android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+ ).RunTestWithBp(t, `
+ android_test {
+ name: "TestModule1",
+ sdk_version: "current",
+ }
+
+ android_test {
+ name: "TestModule2",
+ sdk_version: "current",
+ }
+
+ test_suite {
+ name: "my-suite",
+ description: "a test suite",
+ tests: [
+ "TestModule1",
+ "TestModule2",
+ ]
+ }
+ `)
+ manifestPath := ctx.ModuleForTests("my-suite", "android_common").Output("out/soong/test_suites/my-suite/my-suite.json")
+ var actual testSuiteManifest
+ if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
+ t.Errorf("failed to unmarshal manifest: %v", err)
+ }
+ slices.Sort(actual.Files)
+
+ expected := testSuiteManifest{
+ Name: "my-suite",
+ Files: []string{
+ "target/testcases/TestModule1/TestModule1.config",
+ "target/testcases/TestModule1/arm64/TestModule1.apk",
+ "target/testcases/TestModule2/TestModule2.config",
+ "target/testcases/TestModule2/arm64/TestModule2.apk",
+ },
+ }
+
+ android.AssertDeepEquals(t, "manifests differ", expected, actual)
+}
+
+func TestTestSuitesWithNested(t *testing.T) {
+ t.Parallel()
+ ctx := android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+ ).RunTestWithBp(t, `
+ android_test {
+ name: "TestModule1",
+ sdk_version: "current",
+ }
+
+ android_test {
+ name: "TestModule2",
+ sdk_version: "current",
+ }
+
+ android_test {
+ name: "TestModule3",
+ sdk_version: "current",
+ }
+
+ test_suite {
+ name: "my-child-suite",
+ description: "a child test suite",
+ tests: [
+ "TestModule1",
+ "TestModule2",
+ ]
+ }
+
+ test_suite {
+ name: "my-all-tests-suite",
+ description: "a parent test suite",
+ tests: [
+ "TestModule1",
+ "TestModule3",
+ "my-child-suite",
+ ]
+ }
+ `)
+ manifestPath := ctx.ModuleForTests("my-all-tests-suite", "android_common").Output("out/soong/test_suites/my-all-tests-suite/my-all-tests-suite.json")
+ var actual testSuiteManifest
+ if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
+ t.Errorf("failed to unmarshal manifest: %v", err)
+ }
+ slices.Sort(actual.Files)
+
+ expected := testSuiteManifest{
+ Name: "my-all-tests-suite",
+ Files: []string{
+ "target/testcases/TestModule1/TestModule1.config",
+ "target/testcases/TestModule1/arm64/TestModule1.apk",
+ "target/testcases/TestModule2/TestModule2.config",
+ "target/testcases/TestModule2/arm64/TestModule2.apk",
+ "target/testcases/TestModule3/TestModule3.config",
+ "target/testcases/TestModule3/arm64/TestModule3.apk",
+ },
+ }
+
+ android.AssertDeepEquals(t, "manifests differ", expected, actual)
+}
+
+func TestTestSuitesNotInstalledInTestcases(t *testing.T) {
+ t.Parallel()
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{
+ `"SomeHostTest" is not installed in testcases`,
+ })).RunTestWithBp(t, `
+ java_test_host {
+ name: "SomeHostTest",
+ srcs: ["a.java"],
+ }
+ test_suite {
+ name: "my-suite",
+ description: "a test suite",
+ tests: [
+ "SomeHostTest",
+ ]
+ }
+ `)
+}