diff options
41 files changed, 1015 insertions, 152 deletions
diff --git a/Android.bp b/Android.bp index cbe1c7b3f..42b7d83f7 100644 --- a/Android.bp +++ b/Android.bp @@ -206,3 +206,30 @@ build_prop { 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 dd78ee42a..a8d3d4e2c 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 @@ func (p *androidInfoModule) GenerateAndroidBuildActions(ctx ModuleContext) { 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 79ab13caf..e73b3e662 100644 --- a/android/apex.go +++ b/android/apex.go @@ -833,7 +833,7 @@ func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) { // 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 02c01586b..060fae5bc 100644 --- a/android/base_module_context.go +++ b/android/base_module_context.go @@ -198,9 +198,15 @@ type BaseModuleContext interface { // 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)) @@ -592,6 +598,10 @@ func (b *baseModuleContext) FinalModule() Module { 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 554768077..838947045 100644 --- a/android/build_prop.go +++ b/android/build_prop.go @@ -109,6 +109,12 @@ func (p *buildPropModule) partition(config DeviceConfig) string { 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 @@ var validPartitions = []string{ "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 275c07fc4..d9db64ef6 100644 --- a/android/config.go +++ b/android/config.go @@ -867,7 +867,7 @@ func (c *config) IsEnvFalse(key string) bool { } 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/module.go b/android/module.go index 72c8b94c2..58ae885c8 100644 --- a/android/module.go +++ b/android/module.go @@ -81,6 +81,9 @@ type Module interface { InstallInProduct() bool InstallInVendor() bool InstallInSystemExt() bool + InstallInSystemDlkm() bool + InstallInVendorDlkm() bool + InstallInOdmDlkm() bool InstallForceOS() (*OsType, *ArchType) PartitionTag(DeviceConfig) string HideFromMake() @@ -386,6 +389,15 @@ type commonProperties struct { // 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 @@ func (m *ModuleBase) InstallInRoot() bool { 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 } @@ -2012,7 +2036,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) ctx.GetMissingDependencies() } - if m == ctx.FinalModule().(Module).base() { + if ctx.IsFinalModule(m.module) { m.generateModuleTarget(ctx) if ctx.Failed() { return diff --git a/android/module_context.go b/android/module_context.go index 1f5e706ec..41cb0ccb2 100644 --- a/android/module_context.go +++ b/android/module_context.go @@ -196,6 +196,9 @@ type ModuleContext interface { InstallInOdm() bool InstallInProduct() bool InstallInVendor() bool + InstallInSystemDlkm() bool + InstallInVendorDlkm() bool + InstallInOdmDlkm() bool InstallForceOS() (*OsType, *ArchType) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string @@ -493,6 +496,18 @@ func (m *moduleContext) InstallInVendor() bool { 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 a60a5a810..1f9679926 100644 --- a/android/module_proxy.go +++ b/android/module_proxy.go @@ -106,6 +106,18 @@ func (m ModuleProxy) InstallInSystemExt() bool { 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 4ddc606b1..fdd16a889 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -70,7 +70,7 @@ type RegisterMutatorsContext interface { 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 (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, } } -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 @@ func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) { } 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 @@ func (mutator *mutator) register(ctx *Context) { } 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 @@ type MutatorHandle interface { 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 @@ func (mutator *mutator) MutatesGlobalState() MutatorHandle { 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 6251e2b0d..6176a996a 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -336,6 +336,8 @@ func createPrebuiltEtcBpDefineRule() Rule { "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 a7ee7ac01..9cb872d6f 100644 --- a/android/paths.go +++ b/android/paths.go @@ -117,6 +117,9 @@ type ModuleInstallPathContext interface { InstallInOdm() bool InstallInProduct() bool InstallInVendor() bool + InstallInSystemDlkm() bool + InstallInVendorDlkm() bool + InstallInOdmDlkm() bool InstallForceOS() (*OsType, *ArchType) } @@ -170,6 +173,18 @@ func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendor() bool { 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 @@ func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath { 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 @@ func modulePartition(ctx ModuleInstallPathContext, device bool) string { 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 @@ type testModuleInstallPathContext struct { inOdm bool inProduct bool inVendor bool + inSystemDlkm bool + inVendorDlkm bool + inOdmDlkm bool forceOS *OsType forceArch *ArchType } @@ -2388,6 +2416,18 @@ func (m testModuleInstallPathContext) InstallInVendor() bool { 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 bb1ead73c..8d2f19e73 100644 --- a/android/register.go +++ b/android/register.go @@ -98,6 +98,7 @@ type mutator struct { usesCreateModule bool mutatesDependencies bool mutatesGlobalState bool + neverFar bool } var _ sortableComponent = &mutator{} diff --git a/android/singleton.go b/android/singleton.go index 64381828a..0754b0ccb 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -81,7 +81,7 @@ type SingletonContext interface { VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) PrimaryModule(module Module) Module - FinalModule(module Module) Module + IsFinalModule(module Module) bool AddNinjaFileDeps(deps ...string) @@ -273,8 +273,8 @@ 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 142fab95f..f82c9cae8 100644 --- a/android/variable.go +++ b/android/variable.go @@ -577,6 +577,14 @@ type PartitionQualifiedVariablesType struct { 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,7 +609,12 @@ type PartitionVariables struct { 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"` diff --git a/cc/Android.bp b/cc/Android.bp index a7b6d8161..3b29ae8cf 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -122,3 +122,31 @@ bootstrap_go_package { // 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", + ], +} @@ -1566,12 +1566,11 @@ func (ctx *moduleContextImpl) minSdkVersion() string { } if ctx.ctx.Device() { - config := ctx.ctx.Config() - if ctx.inVendor() { - // If building for vendor with final API, then use the latest _stable_ API as "current". - if config.VendorApiLevelFrozen() && (ver == "" || ver == "current") { - ver = config.PlatformSdkVersion().String() - } + // When building for vendor/product, use the latest _stable_ API as "current". + // This is passed to clang/aidl compilers so that compiled/generated code works + // with the system. + if (ctx.inVendor() || ctx.inProduct()) && (ver == "" || ver == "current") { + ver = ctx.ctx.Config().PlatformSdkVersion().String() } } diff --git a/cc/cc_test.go b/cc/cc_test.go index 22f7c9f35..df239cbb5 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -3258,7 +3258,7 @@ func TestImageVariants(t *testing.T) { testDepWithVariant("product") } -func TestVendorSdkVersion(t *testing.T) { +func TestVendorOrProductVariantUsesPlatformSdkVersionAsDefault(t *testing.T) { t.Parallel() bp := ` @@ -3266,31 +3266,29 @@ func TestVendorSdkVersion(t *testing.T) { name: "libfoo", srcs: ["libfoo.cc"], vendor_available: true, + product_available: true, } cc_library { name: "libbar", srcs: ["libbar.cc"], vendor_available: true, + product_available: true, min_sdk_version: "29", } ` ctx := prepareForCcTest.RunTestWithBp(t, bp) - testSdkVersionFlag := func(module, version string) { - flags := ctx.ModuleForTests(module, "android_vendor_arm64_armv8-a_static").Rule("cc").Args["cFlags"] - android.AssertStringDoesContain(t, "min sdk version", flags, "-target aarch64-linux-android"+version) + testSdkVersionFlag := func(module, variant, version string) { + flags := ctx.ModuleForTests(module, "android_"+variant+"_arm64_armv8-a_static").Rule("cc").Args["cFlags"] + android.AssertStringDoesContain(t, "target SDK version", flags, "-target aarch64-linux-android"+version) } - testSdkVersionFlag("libfoo", "10000") - testSdkVersionFlag("libbar", "29") - - ctx = android.GroupFixturePreparers( - prepareForCcTest, - android.PrepareForTestWithBuildFlag("RELEASE_BOARD_API_LEVEL_FROZEN", "true"), - ).RunTestWithBp(t, bp) - testSdkVersionFlag("libfoo", "30") - testSdkVersionFlag("libbar", "29") + testSdkVersionFlag("libfoo", "vendor", "30") + testSdkVersionFlag("libfoo", "product", "30") + // target SDK version can be set explicitly with min_sdk_version + testSdkVersionFlag("libbar", "vendor", "29") + testSdkVersionFlag("libbar", "product", "29") } func TestClangVerify(t *testing.T) { diff --git a/cc/compiler.go b/cc/compiler.go index 0fa058a35..91f107c29 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -78,11 +78,11 @@ type BaseCompilerProperties struct { // 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 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps } // 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 @@ func compileObjs(ctx ModuleContext, flags builderFlags, subdir string, 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/tidy.go b/cc/tidy.go index 89bae1780..5cbf8f076 100644 --- a/cc/tidy.go +++ b/cc/tidy.go @@ -254,7 +254,7 @@ func (m *tidyPhonySingleton) GenerateBuildActions(ctx android.SingletonContext) // 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/etc/prebuilt_etc.go b/etc/prebuilt_etc.go index 938f3bde3..be943a3ca 100644 --- a/etc/prebuilt_etc.go +++ b/etc/prebuilt_etc.go @@ -78,6 +78,8 @@ func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) { 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 @@ type prebuiltEtcProperties struct { // 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 @@ func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) { } 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 @@ func PrebuiltBtFirmwareFactory() android.Module { 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 9071272c1..2645dc498 100644 --- a/filesystem/android_device.go +++ b/filesystem/android_device.go @@ -34,6 +34,8 @@ type PartitionNameProperties struct { 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 @@ func AndroidDeviceFactory() android.Module { module := &androidDevice{} module.AddProperties(&module.partitionProps) android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) - return module } @@ -69,6 +70,9 @@ func (a *androidDevice) DepsMutator(ctx android.BottomUpMutatorContext) { 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 6ed962f13..e84139b9a 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -147,6 +147,8 @@ type FilesystemProperties struct { 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 @@ type ErofsProperties struct { 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 @@ type fsType int const ( ext4Type fsType = iota erofsType + f2fsType compressedCpioType cpioType // uncompressed unknown @@ -249,6 +257,8 @@ func GetFsTypeFromString(ctx android.EarlyModuleContext, typeStr string) fsType return ext4Type case "erofs": return erofsType + case "f2fs": + return f2fsType case "compressed_cpio": return compressedCpioType case "cpio": @@ -289,7 +299,7 @@ var pctx = android.NewPackageContext("android/soong/filesystem") 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 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. return "ext4" case erofsType: return "erofs" + case f2fsType: + return "f2fs" } panic(fmt.Errorf("unsupported fs type %v", t)) } @@ -554,8 +566,11 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. 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 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. // 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 29f9373d7..801a17545 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -585,6 +585,35 @@ func TestErofsPartition(t *testing.T) { 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 0bae479cf..6a3fc1f18 100644 --- a/filesystem/vbmeta.go +++ b/filesystem/vbmeta.go @@ -25,19 +25,19 @@ import ( ) 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 @@ type vbmetaProperties struct { // 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 @@ type vbmetaProperties struct { 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 @@ type avbProperty struct { Value *string } -type chainedPartitionProperties struct { +type ChainedPartitionProperties struct { // Name of the chained partition Name *string @@ -95,7 +94,7 @@ type chainedPartitionProperties struct { } // 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 @@ func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) { // 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 690ad2821..8cd7518cb 100644 --- a/fsgen/Android.bp +++ b/fsgen/Android.bp @@ -16,6 +16,7 @@ bootstrap_go_package { "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 bdffabf41..c9bbf3fa6 100644 --- a/fsgen/filesystem_creator.go +++ b/fsgen/filesystem_creator.go @@ -44,6 +44,9 @@ func registerBuildComponents(ctx android.RegistrationContext) { 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 filesystemCreatorFactory() android.Module { } 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 @@ func generatedModuleNameForPartition(cfg android.Config, partitionType string) s 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 @@ func (f *filesystemCreator) createDeviceModule(ctx android.LoadHookContext) { // 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 @@ func partitionSpecificFsProps(fsProps *filesystem.FilesystemProperties, partitio "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/*", @@ -211,38 +247,28 @@ func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partiti } // 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 @@ -334,12 +360,15 @@ func (f *filesystemCreator) createLinkerConfigSourceFilegroups(ctx android.LoadH 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__"}, } } @@ -406,18 +435,13 @@ func (f *filesystemCreator) createDiffTest(ctx android.ModuleContext, partitionT 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 @@ -435,16 +459,42 @@ func createFailingCommand(ctx android.ModuleContext, message string) android.Pat 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) { @@ -474,6 +524,11 @@ func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContex 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 bf07ab3a8..92ea1282a 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 @@ func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNam "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 @@ func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNam }, "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 1df7ec86d..362ac31b8 100644 --- a/fsgen/prebuilt_etc_modules_gen.go +++ b/fsgen/prebuilt_etc_modules_gen.go @@ -176,11 +176,13 @@ var ( "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 000000000..280e4052d --- /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 66ca00af8..b5cdde36a 100644 --- a/java/aar.go +++ b/java/aar.go @@ -77,7 +77,7 @@ type aaptProperties struct { // 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 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkConte 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 6572dbdf3..94c9e5b82 100644 --- a/java/app.go +++ b/java/app.go @@ -1361,7 +1361,7 @@ func AndroidAppFactory() android.Module { 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 1a3368057..7c0f54431 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -520,7 +520,7 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // 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 86f7845bc..1d572faef 100644 --- a/java/java.go +++ b/java/java.go @@ -606,10 +606,8 @@ func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext an } 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 diff --git a/java/sdk.go b/java/sdk.go index 4537f1913..036521c86 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -65,11 +65,11 @@ func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpe 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 394449574..d59057994 100644 --- a/rust/bindgen.go +++ b/rust/bindgen.go @@ -252,7 +252,7 @@ func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) andr // 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/scripts/gen_build_prop.py b/scripts/gen_build_prop.py index e0686ed19..0b7780e96 100644 --- a/scripts/gen_build_prop.py +++ b/scripts/gen_build_prop.py @@ -608,6 +608,8 @@ def main(): 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/tradefed_modules/Android.bp b/tradefed_modules/Android.bp index 9969ae280..67d91b27c 100644 --- a/tradefed_modules/Android.bp +++ b/tradefed_modules/Android.bp @@ -13,9 +13,11 @@ bootstrap_go_package { ], 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 7a04c1994..5c13d64e8 100644 --- a/tradefed_modules/test_module_config.go +++ b/tradefed_modules/test_module_config.go @@ -383,6 +383,19 @@ func (m *testModuleConfigModule) generateManifestAndConfig(ctx android.ModuleCon // 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 000000000..00585f5f6 --- /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 000000000..3c0a9eb2c --- /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", + ] + } + `) +} |