Merge "Revert "test_module_config for sh_test"" 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..142fab9 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -608,6 +608,9 @@
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 5dee32e..c6bc30e 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)
@@ -2048,9 +2047,6 @@
if ctx.Failed() {
return
}
- c.kytheFiles = objs.kytheFiles
- c.objFiles = objs.objFiles
- c.tidyFiles = objs.tidyFiles
}
if c.linker != nil {
@@ -2061,6 +2057,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()})
@@ -2115,6 +2115,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 {
@@ -2122,6 +2133,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()}, "")
@@ -2284,6 +2301,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)
@@ -2311,6 +2332,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,
@@ -2372,6 +2413,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
@@ -3070,6 +3114,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:
@@ -3958,9 +4009,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 e906706..22f7c9f 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -3321,3 +3321,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/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 5d88767..fe590a9 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -136,6 +136,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 +225,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
diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go
index bf07ab3..f3aec00 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()),
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/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",
+ ]
+ }
+ `)
+}