Merge "Handle several symlinks in system image generation" into main
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/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/module.go b/android/module.go
index 72c8b94..6217833 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
}
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/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/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/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/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/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 6572dbd..94c9e5b 100644
--- a/java/app.go
+++ b/java/app.go
@@ -1361,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/java.go b/java/java.go
index 86f7845..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
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/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_suite.go b/tradefed_modules/test_suite.go
new file mode 100644
index 0000000..a6c2727
--- /dev/null
+++ b/tradefed_modules/test_suite.go
@@ -0,0 +1,61 @@
+// 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 (
+ "fmt"
+
+ "android/soong/android"
+)
+
+func init() {
+ RegisterTestSuiteBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("test_suite", 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) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ suiteName := ctx.ModuleName()
+ manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
+ android.WriteFileRule(ctx, manifestPath, fmt.Sprintf(`{"name": %q}`, suiteName))
+ ctx.Phony(suiteName, manifestPath)
+}
+
+func TestSuiteFactory() android.Module {
+ module := &testSuiteModule{}
+ module.AddProperties(&module.testSuiteProperties)
+
+ android.InitAndroidModule(module)
+ android.InitDefaultableModule(module)
+
+ return module
+}
diff --git a/tradefed_modules/test_suite_test.go b/tradefed_modules/test_suite_test.go
new file mode 100644
index 0000000..647ba4d
--- /dev/null
+++ b/tradefed_modules/test_suite_test.go
@@ -0,0 +1,53 @@
+// 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"
+ "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", "").Output("out/soong/test_suites/my-suite/my-suite.json")
+ got := android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)
+ want := `{"name": "my-suite"}` + "\n"
+ if got != want {
+ t.Errorf("my-suite.json content was %q, want %q", got, want)
+ }
+}