diff options
-rw-r--r-- | android/config.go | 162 | ||||
-rw-r--r-- | android/config_test.go | 69 | ||||
-rw-r--r-- | android/early_module_context.go | 7 | ||||
-rw-r--r-- | android/module.go | 11 | ||||
-rw-r--r-- | android/module_proxy.go | 4 | ||||
-rw-r--r-- | android/neverallow.go | 1 | ||||
-rw-r--r-- | android/paths.go | 2 | ||||
-rw-r--r-- | android/test_config.go | 17 | ||||
-rw-r--r-- | android/variable.go | 4 | ||||
-rw-r--r-- | apex/apex.go | 19 | ||||
-rw-r--r-- | cc/compdb.go | 2 | ||||
-rw-r--r-- | cmd/find_input_delta/find_input_delta_lib/internal_state.go | 7 | ||||
-rw-r--r-- | dexpreopt/config.go | 5 | ||||
-rw-r--r-- | etc/prebuilt_etc.go | 11 | ||||
-rw-r--r-- | filesystem/android_device.go | 77 | ||||
-rw-r--r-- | filesystem/bootimg.go | 2 | ||||
-rw-r--r-- | filesystem/filesystem.go | 4 | ||||
-rw-r--r-- | filesystem/super_image.go | 3 | ||||
-rw-r--r-- | fsgen/filesystem_creator_test.go | 159 | ||||
-rw-r--r-- | fsgen/prebuilt_etc_modules_gen.go | 30 | ||||
-rw-r--r-- | genrule/Android.bp | 1 | ||||
-rw-r--r-- | genrule/allowlists.go | 22 | ||||
-rw-r--r-- | genrule/genrule.go | 21 | ||||
-rw-r--r-- | tradefed_modules/Android.bp | 2 | ||||
-rw-r--r-- | tradefed_modules/test_suite.go | 173 | ||||
-rw-r--r-- | tradefed_modules/test_suite_test.go | 151 |
26 files changed, 500 insertions, 466 deletions
diff --git a/android/config.go b/android/config.go index d47f0d44a..52d7f3b41 100644 --- a/android/config.go +++ b/android/config.go @@ -22,6 +22,7 @@ import ( "fmt" "os" "path/filepath" + "reflect" "runtime" "strconv" "strings" @@ -108,6 +109,10 @@ const ( const testKeyDir = "build/make/target/product/security" +func (c Config) genericConfig() Config { + return Config{c.config.genericConfig} +} + // SoongOutDir returns the build output directory for the configuration. func (c Config) SoongOutDir() string { return c.soongOutDir @@ -372,7 +377,7 @@ type config struct { // regenerate build.ninja. ninjaFileDepsSet sync.Map - OncePer + *OncePer // If buildFromSourceStub is true then the Java API stubs are // built from the source Java files, not the signature text files. @@ -382,6 +387,17 @@ type config struct { // modules that aren't mixed-built for at least one variant will cause a build // failure ensureAllowlistIntegrity bool + + // If isGeneric is true, this config is the generic config. + isGeneric bool + + // InstallPath requires the device name. + // This is only for the installPath. + deviceNameToInstall *string + + // Copy of this config struct but some product-specific variables are + // replaced with the generic configuration values. + genericConfig *config } type partialCompileFlags struct { @@ -639,11 +655,9 @@ func NullConfig(outDir, soongOutDir string) Config { } } -// NewConfig creates a new Config object. The srcDir argument specifies the path -// to the root source directory. It also loads the config file, if found. -func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) { +func initConfig(cmdArgs CmdArgs, availableEnv map[string]string) (*config, error) { // Make a config with default options. - config := &config{ + newConfig := &config{ ProductVariablesFileName: cmdArgs.SoongVariables, env: availableEnv, @@ -657,70 +671,72 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) moduleListFile: cmdArgs.ModuleListFile, fs: pathtools.NewOsFs(absSrcDir), + OncePer: &OncePer{}, + buildFromSourceStub: cmdArgs.BuildFromSourceStub, } variant, ok := os.LookupEnv("TARGET_BUILD_VARIANT") isEngBuild := !ok || variant == "eng" - config.deviceConfig = &deviceConfig{ - config: config, + newConfig.deviceConfig = &deviceConfig{ + config: newConfig, } // Soundness check of the build and source directories. This won't catch strange // configurations with symlinks, but at least checks the obvious case. absBuildDir, err := filepath.Abs(cmdArgs.SoongOutDir) if err != nil { - return Config{}, err + return &config{}, err } absSrcDir, err := filepath.Abs(".") if err != nil { - return Config{}, err + return &config{}, err } if strings.HasPrefix(absSrcDir, absBuildDir) { - return Config{}, fmt.Errorf("Build dir must not contain source directory") + return &config{}, fmt.Errorf("Build dir must not contain source directory") } // Load any configurable options from the configuration file - err = loadConfig(config) + err = loadConfig(newConfig) if err != nil { - return Config{}, err + return &config{}, err } KatiEnabledMarkerFile := filepath.Join(cmdArgs.SoongOutDir, ".soong.kati_enabled") if _, err := os.Stat(absolutePath(KatiEnabledMarkerFile)); err == nil { - config.katiEnabled = true + newConfig.katiEnabled = true } - determineBuildOS(config) + determineBuildOS(newConfig) // Sets up the map of target OSes to the finer grained compilation targets // that are configured from the product variables. - targets, err := decodeTargetProductVariables(config) + targets, err := decodeTargetProductVariables(newConfig) if err != nil { - return Config{}, err + return &config{}, err } - config.partialCompileFlags, err = config.parsePartialCompileFlags(isEngBuild) + newConfig.partialCompileFlags, err = newConfig.parsePartialCompileFlags(isEngBuild) if err != nil { - return Config{}, err + return &config{}, err } // Make the CommonOS OsType available for all products. targets[CommonOS] = []Target{commonTargetMap[CommonOS.Name]} var archConfig []archConfig - if config.NdkAbis() { + if newConfig.NdkAbis() { archConfig = getNdkAbisConfig() - } else if config.AmlAbis() { + } else if newConfig.AmlAbis() { archConfig = getAmlAbisConfig() } if archConfig != nil { androidTargets, err := decodeAndroidArchSettings(archConfig) if err != nil { - return Config{}, err + return &config{}, err } targets[Android] = androidTargets } @@ -728,37 +744,113 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) multilib := make(map[string]bool) for _, target := range targets[Android] { if seen := multilib[target.Arch.ArchType.Multilib]; seen { - config.multilibConflicts[target.Arch.ArchType] = true + newConfig.multilibConflicts[target.Arch.ArchType] = true } multilib[target.Arch.ArchType.Multilib] = true } // Map of OS to compilation targets. - config.Targets = targets + newConfig.Targets = targets // Compilation targets for host tools. - config.BuildOSTarget = config.Targets[config.BuildOS][0] - config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0] + newConfig.BuildOSTarget = newConfig.Targets[newConfig.BuildOS][0] + newConfig.BuildOSCommonTarget = getCommonTargets(newConfig.Targets[newConfig.BuildOS])[0] // Compilation targets for Android. - if len(config.Targets[Android]) > 0 { - config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0] - config.AndroidFirstDeviceTarget = FirstTarget(config.Targets[Android], "lib64", "lib32")[0] + if len(newConfig.Targets[Android]) > 0 { + newConfig.AndroidCommonTarget = getCommonTargets(newConfig.Targets[Android])[0] + newConfig.AndroidFirstDeviceTarget = FirstTarget(newConfig.Targets[Android], "lib64", "lib32")[0] } setBuildMode := func(arg string, mode SoongBuildMode) { if arg != "" { - if config.BuildMode != AnalysisNoBazel { + if newConfig.BuildMode != AnalysisNoBazel { fmt.Fprintf(os.Stderr, "buildMode is already set, illegal argument: %s", arg) os.Exit(1) } - config.BuildMode = mode + newConfig.BuildMode = mode } } setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph) setBuildMode(cmdArgs.DocFile, GenerateDocFile) - config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub()) + newConfig.productVariables.Build_from_text_stub = boolPtr(newConfig.BuildFromTextStub()) + + newConfig.deviceNameToInstall = newConfig.productVariables.DeviceName + + return newConfig, err +} + +// Replace variables in config.productVariables that have tags with "generic" key. +// A generic tag may have a string or an int value for the generic configuration. +// If the value is "unset", generic configuration will unset the variable. +func overrideGenericConfig(config *config) { + config.genericConfig.isGeneric = true + type_pv := reflect.TypeOf(config.genericConfig.productVariables) + value_pv := reflect.ValueOf(&config.genericConfig.productVariables) + for i := range type_pv.NumField() { + type_pv_field := type_pv.Field(i) + generic_value := type_pv_field.Tag.Get("generic") + // If a product variable has an annotation of "generic" tag, use the + // value of the tag to set the generic variable. + if generic_value != "" { + value_pv_field := value_pv.Elem().Field(i) + + if generic_value == "unset" { + // unset the product variable + value_pv_field.SetZero() + continue + } + + kind_of_type_pv := type_pv_field.Type.Kind() + is_pointer := false + if kind_of_type_pv == reflect.Pointer { + is_pointer = true + kind_of_type_pv = type_pv_field.Type.Elem().Kind() + } + + switch kind_of_type_pv { + case reflect.String: + if is_pointer { + value_pv_field.Set(reflect.ValueOf(stringPtr(generic_value))) + } else { + value_pv_field.Set(reflect.ValueOf(generic_value)) + } + case reflect.Int: + generic_int, err := strconv.Atoi(generic_value) + if err != nil { + panic(fmt.Errorf("Only an int value can be assigned to int variable: %s", err)) + } + if is_pointer { + value_pv_field.Set(reflect.ValueOf(intPtr(generic_int))) + } else { + value_pv_field.Set(reflect.ValueOf(generic_int)) + } + default: + panic(fmt.Errorf("Unknown type to replace for generic variable: %s", &kind_of_type_pv)) + } + } + } + + // OncePer must be a singleton. + config.genericConfig.OncePer = config.OncePer + // keep the device name to get the install path. + config.genericConfig.deviceNameToInstall = config.deviceNameToInstall +} + +// NewConfig creates a new Config object. It also loads the config file, if +// found. The Config object includes a duplicated Config object in it for the +// generic configuration that does not provide any product specific information. +func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) { + config, err := initConfig(cmdArgs, availableEnv) + if err != nil { + return Config{}, err + } + + // Initialize generic configuration. + config.genericConfig, err = initConfig(cmdArgs, availableEnv) + // Update product specific variables with the generic configuration. + overrideGenericConfig(config) return Config{config}, err } @@ -946,7 +1038,7 @@ func (c *config) DisplayBuildNumber() bool { // require them to run and get the current build fingerprint. This ensures they // don't rebuild on every incremental build when the build number changes. func (c *config) BuildFingerprintFile(ctx PathContext) Path { - return PathForArbitraryOutput(ctx, "target", "product", c.DeviceName(), String(c.productVariables.BuildFingerprintFile)) + return PathForArbitraryOutput(ctx, "target", "product", *c.deviceNameToInstall, String(c.productVariables.BuildFingerprintFile)) } // BuildNumberFile returns the path to a text file containing metadata @@ -974,7 +1066,7 @@ func (c *config) BuildHostnameFile(ctx PathContext) Path { // require them to run and get the current build thumbprint. This ensures they // don't rebuild on every incremental build when the build thumbprint changes. func (c *config) BuildThumbprintFile(ctx PathContext) Path { - return PathForArbitraryOutput(ctx, "target", "product", c.DeviceName(), String(c.productVariables.BuildThumbprintFile)) + return PathForArbitraryOutput(ctx, "target", "product", *c.deviceNameToInstall, String(c.productVariables.BuildThumbprintFile)) } // DeviceName returns the name of the current device target. @@ -1207,6 +1299,10 @@ func (c *config) ExtraOtaKeys(ctx PathContext, recovery bool) []SourcePath { return otaPaths } +func (c *config) ExtraOtaRecoveryKeys() []string { + return c.productVariables.ExtraOtaRecoveryKeys +} + func (c *config) BuildKeys() string { defaultCert := String(c.productVariables.DefaultAppCertificate) if defaultCert == "" || defaultCert == filepath.Join(testKeyDir, "testkey") { diff --git a/android/config_test.go b/android/config_test.go index d1b26c12a..81b7c3eb5 100644 --- a/android/config_test.go +++ b/android/config_test.go @@ -281,3 +281,72 @@ func TestPartialCompile(t *testing.T) { }) } } + +type configTestProperties struct { + Use_generic_config *bool +} + +type configTestModule struct { + ModuleBase + properties configTestProperties +} + +func (d *configTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { + deviceName := ctx.Config().DeviceName() + if ctx.ModuleName() == "foo" { + if ctx.Module().UseGenericConfig() { + ctx.PropertyErrorf("use_generic_config", "must not be set for this test") + } + } else if ctx.ModuleName() == "bar" { + if !ctx.Module().UseGenericConfig() { + ctx.ModuleErrorf("\"use_generic_config: true\" must be set for this test") + } + } + + if ctx.Module().UseGenericConfig() { + if deviceName != "generic" { + ctx.ModuleErrorf("Device name for this module must be \"generic\" but %q\n", deviceName) + } + } else { + if deviceName == "generic" { + ctx.ModuleErrorf("Device name for this module must not be \"generic\"\n") + } + } +} + +func configTestModuleFactory() Module { + module := &configTestModule{} + module.AddProperties(&module.properties) + InitAndroidModule(module) + return module +} + +var prepareForConfigTest = GroupFixturePreparers( + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("test", configTestModuleFactory) + }), +) + +func TestGenericConfig(t *testing.T) { + bp := ` + test { + name: "foo", + } + + test { + name: "bar", + use_generic_config: true, + } + ` + + result := GroupFixturePreparers( + prepareForConfigTest, + FixtureWithRootAndroidBp(bp), + ).RunTest(t) + + foo := result.Module("foo", "").(*configTestModule) + bar := result.Module("bar", "").(*configTestModule) + + AssertBoolEquals(t, "Do not use generic config", false, foo.UseGenericConfig()) + AssertBoolEquals(t, "Use generic config", true, bar.UseGenericConfig()) +} diff --git a/android/early_module_context.go b/android/early_module_context.go index 8d2828545..300edf194 100644 --- a/android/early_module_context.go +++ b/android/early_module_context.go @@ -146,6 +146,13 @@ func (e *earlyModuleContext) Module() Module { } func (e *earlyModuleContext) Config() Config { + // Only the system image may use the generic config. + // If a module builds multiple image variations, provide the generic config only for the core + // variant which is installed in the system partition. Other image variant may still read the + // original configurations. + if e.Module().base().UseGenericConfig() && e.Module().base().commonProperties.ImageVariation == "" { + return e.EarlyModuleContext.Config().(Config).genericConfig() + } return e.EarlyModuleContext.Config().(Config) } diff --git a/android/module.go b/android/module.go index a3fe837a5..87377cc75 100644 --- a/android/module.go +++ b/android/module.go @@ -128,6 +128,9 @@ type Module interface { // WARNING: This should not be used outside build/soong/fsgen // Overrides returns the list of modules which should not be installed if this module is installed. Overrides() []string + + // If this is true, the module must not read product-specific configurations. + UseGenericConfig() bool } // Qualified id for a module @@ -507,6 +510,10 @@ type commonProperties struct { // List of module names that are prevented from being installed when this module gets // installed. Overrides []string + + // Set to true if this module must be generic and does not require product-specific information. + // To be included in the system image, this property must be set to true. + Use_generic_config *bool } // Properties common to all modules inheriting from ModuleBase. Unlike commonProperties, these @@ -2588,6 +2595,10 @@ func (m *ModuleBase) Overrides() []string { return m.commonProperties.Overrides } +func (m *ModuleBase) UseGenericConfig() bool { + return proptools.Bool(m.commonProperties.Use_generic_config) +} + type ConfigContext interface { Config() Config } diff --git a/android/module_proxy.go b/android/module_proxy.go index 77abc11e6..561c4770c 100644 --- a/android/module_proxy.go +++ b/android/module_proxy.go @@ -231,3 +231,7 @@ func (m ModuleProxy) Overrides() []string { func (m ModuleProxy) VintfFragments(ctx ConfigurableEvaluatorContext) []string { panic("method is not implemented on ModuleProxy") } + +func (m ModuleProxy) UseGenericConfig() bool { + panic("method is not implemented on ModuleProxy") +} diff --git a/android/neverallow.go b/android/neverallow.go index 5c90501d7..e693f2ddd 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -388,6 +388,7 @@ func createPrebuiltEtcBpDefineRule() Rule { "prebuilt_radio", "prebuilt_gpu", "prebuilt_vendor_overlay", + "prebuilt_tee", ). DefinedInBpFile(). Because("module type not allowed to be defined in bp file") diff --git a/android/paths.go b/android/paths.go index 977473fbd..6612d37d3 100644 --- a/android/paths.go +++ b/android/paths.go @@ -2054,7 +2054,7 @@ func pathForInstall(ctx PathContext, os OsType, arch ArchType, partition string, var partitionPaths []string if os.Class == Device { - partitionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition} + partitionPaths = []string{"target", "product", *ctx.Config().deviceNameToInstall, partition} } else { osName := os.String() if os == Linux { diff --git a/android/test_config.go b/android/test_config.go index 3609e6b78..5d79df099 100644 --- a/android/test_config.go +++ b/android/test_config.go @@ -23,8 +23,7 @@ import ( "github.com/google/blueprint/proptools" ) -// TestConfig returns a Config object for testing. -func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config { +func initTestConfig(buildDir string, env map[string]string) *config { envCopy := make(map[string]string) for k, v := range env { envCopy[k] = v @@ -58,6 +57,7 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string soongOutDir: filepath.Join(buildDir, "soong"), captureBuild: true, env: envCopy, + OncePer: &OncePer{}, // Set testAllowNonExistentPaths so that test contexts don't need to specify every path // passed to PathForSource or PathForModuleSrc. @@ -69,10 +69,21 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string config: config, } config.TestProductVariables = &config.productVariables + config.deviceNameToInstall = config.TestProductVariables.DeviceName + + determineBuildOS(config) + + return config +} + +// TestConfig returns a Config object for testing. +func TestConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config { + config := initTestConfig(buildDir, env) config.mockFileSystem(bp, fs) - determineBuildOS(config) + config.genericConfig = initTestConfig(buildDir, env) + overrideGenericConfig(config) return Config{config} } diff --git a/android/variable.go b/android/variable.go index 3d5a6ae12..8c9a0b1a5 100644 --- a/android/variable.go +++ b/android/variable.go @@ -225,8 +225,8 @@ type ProductVariables struct { Platform_version_last_stable *string `json:",omitempty"` Platform_version_known_codenames *string `json:",omitempty"` - DeviceName *string `json:",omitempty"` - DeviceProduct *string `json:",omitempty"` + DeviceName *string `json:",omitempty" generic:"generic"` + DeviceProduct *string `json:",omitempty" generic:"generic"` DeviceArch *string `json:",omitempty"` DeviceArchVariant *string `json:",omitempty"` DeviceCpuVariant *string `json:",omitempty"` diff --git a/apex/apex.go b/apex/apex.go index c6566e13b..a726098c6 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -569,19 +569,6 @@ const ( shBinary ) -var ( - classes = map[string]apexFileClass{ - "app": app, - "appSet": appSet, - "etc": etc, - "javaSharedLib": javaSharedLib, - "nativeExecutable": nativeExecutable, - "nativeSharedLib": nativeSharedLib, - "nativeTest": nativeTest, - "shBinary": shBinary, - } -) - // apexFile represents a file in an APEX bundle. This is created during the first half of // GenerateAndroidBuildActions by traversing the dependencies of the APEX. Then in the second half // of the function, this is used to create commands that copies the files into a staging directory, @@ -1970,7 +1957,11 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, case testTag: if ccInfo, ok := android.OtherModuleProvider(ctx, child, cc.CcInfoProvider); ok { af := apexFileForExecutable(ctx, child, commonInfo, ccInfo) - af.class = nativeTest + // We make this a nativeExecutable instead of a nativeTest because we don't want + // the androidmk modules generated in AndroidMkForFiles to be treated as real + // tests that are then packaged into suites. Our AndroidMkForFiles does not + // implement enough functionality to support real tests. + af.class = nativeExecutable vctx.filesInfo = append(vctx.filesInfo, af) return true // track transitive dependencies } else { diff --git a/cc/compdb.go b/cc/compdb.go index 4132e090b..3818e9c46 100644 --- a/cc/compdb.go +++ b/cc/compdb.go @@ -193,7 +193,7 @@ func generateCompdbProject(compiledModule CompiledInterface, ctx android.Singlet } builds[src.String()] = compDbEntry{ Directory: android.AbsSrcDirForExistingUseCases(), - Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath), + Arguments: args, File: src.String(), } } diff --git a/cmd/find_input_delta/find_input_delta_lib/internal_state.go b/cmd/find_input_delta/find_input_delta_lib/internal_state.go index bf4f86686..0f88159be 100644 --- a/cmd/find_input_delta/find_input_delta_lib/internal_state.go +++ b/cmd/find_input_delta/find_input_delta_lib/internal_state.go @@ -95,9 +95,14 @@ func inspectZipFileContents(name string) ([]*fid_proto.PartialCompileInput, erro } ret := []*fid_proto.PartialCompileInput{} for _, v := range rc.File { + // Only include timestamp when there is no CRC. + timeNsec := proto.Int64(v.ModTime().UnixNano()) + if v.CRC32 != 0 { + timeNsec = nil + } pci := &fid_proto.PartialCompileInput{ Name: proto.String(v.Name), - MtimeNsec: proto.Int64(v.ModTime().UnixNano()), + MtimeNsec: timeNsec, Hash: proto.String(fmt.Sprintf("%08x", v.CRC32)), } ret = append(ret, pci) diff --git a/dexpreopt/config.go b/dexpreopt/config.go index e57384fd7..af09dbcca 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -139,6 +139,11 @@ var allSystemServerJarsKey = android.NewOnceKey("allSystemServerJars") // Returns all jars that system_server loads. func (g *GlobalConfig) AllSystemServerJars(ctx android.PathContext) *android.ConfiguredJarList { + // dexpreopt does not initialize the soong config. + // Initialize the OncePer here. + if ctx.Config().OncePer == nil { + ctx.Config().OncePer = &android.OncePer{} + } return ctx.Config().Once(allSystemServerJarsKey, func() interface{} { res := g.AllPlatformSystemServerJars(ctx).AppendList(g.AllApexSystemServerJars(ctx)) return &res diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go index 3b0c0329d..7820047ab 100644 --- a/etc/prebuilt_etc.go +++ b/etc/prebuilt_etc.go @@ -78,6 +78,7 @@ func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_rfs", PrebuiltRfsFactory) ctx.RegisterModuleType("prebuilt_framework", PrebuiltFrameworkFactory) ctx.RegisterModuleType("prebuilt_res", PrebuiltResFactory) + ctx.RegisterModuleType("prebuilt_tee", PrebuiltTeeFactory) ctx.RegisterModuleType("prebuilt_wlc_upt", PrebuiltWlcUptFactory) ctx.RegisterModuleType("prebuilt_odm", PrebuiltOdmFactory) ctx.RegisterModuleType("prebuilt_vendor_dlkm", PrebuiltVendorDlkmFactory) @@ -910,6 +911,16 @@ func PrebuiltRFSAFactory() android.Module { return module } +// prebuilt_tee installs files in <partition>/tee directory. +func PrebuiltTeeFactory() android.Module { + module := &PrebuiltEtc{} + InitPrebuiltEtcModule(module, "tee") + // This module is device-only + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) + return module +} + // prebuilt_media installs media files in <partition>/media directory. func PrebuiltMediaFactory() android.Module { module := &PrebuiltEtc{} diff --git a/filesystem/android_device.go b/filesystem/android_device.go index 9cb90da12..443e80e67 100644 --- a/filesystem/android_device.go +++ b/filesystem/android_device.go @@ -748,23 +748,88 @@ func (a *androidDevice) copyMetadataToTargetZip(ctx android.ModuleContext, build } +var ( + // https://cs.android.com/android/_/android/platform/build/+/30f05352c3e6f4333c77d4af66c253572d3ea6c9:core/Makefile;l=2111-2120;drc=519f75666431ee2926e0ec8991c682b28a4c9521;bpv=1;bpt=0 + defaultTargetRecoveryFstypeMountOptions = "ext4=max_batch_time=0,commit=1,data=ordered,barrier=1,errors=panic,nodelalloc" +) + // A partial implementation of make's $PRODUCT_OUT/misc_info.txt // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=5894?q=misc_info.txt%20f:build%2Fmake%2Fcore%2FMakefile&ss=android%2Fplatform%2Fsuperproject%2Fmain // This file is subsequently used by add_img_to_target_files to create additioanl metadata files like apex_info.pb // TODO (b/399788119): Complete the migration of misc_info.txt func (a *androidDevice) addMiscInfo(ctx android.ModuleContext) android.Path { + buildType := func() string { + if ctx.Config().Debuggable() { + return "userdebug" + } else if ctx.Config().Eng() { + return "eng" + } else { + return "user" + } + } + defaultAppCertificate := func() string { + pem, _ := ctx.Config().DefaultAppCertificate(ctx) + return strings.TrimSuffix(pem.String(), ".x509.pem") + } + builder := android.NewRuleBuilder(pctx, ctx) miscInfo := android.PathForModuleOut(ctx, "misc_info.txt") builder.Command(). Textf("rm -f %s", miscInfo). Textf("&& echo recovery_api_version=%s >> %s", ctx.Config().VendorConfig("recovery").String("recovery_api_version"), miscInfo). Textf("&& echo fstab_version=%s >> %s", ctx.Config().VendorConfig("recovery").String("recovery_fstab_version"), miscInfo). + Textf("&& echo build_type=%s >> %s", buildType(), miscInfo). + Textf("&& echo default_system_dev_certificate=%s >> %s", defaultAppCertificate(), miscInfo). + Textf("&& echo root_dir=%s >> %s", android.PathForModuleInPartitionInstall(ctx, "root"), miscInfo). ImplicitOutput(miscInfo) + if len(ctx.Config().ExtraOtaRecoveryKeys()) > 0 { + builder.Command().Textf(`echo "extra_recovery_keys=%s" >> %s`, strings.Join(ctx.Config().ExtraOtaRecoveryKeys(), ""), miscInfo) + } else { + if a.partitionProps.Boot_partition_name != nil { + builder.Command(). + Textf("echo mkbootimg_args='--header_version %s' >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Boot_partition_name), miscInfo). + // TODO: Use boot's header version for recovery for now since cuttlefish does not set `BOARD_RECOVERY_MKBOOTIMG_ARGS` + Textf(" && echo recovery_mkbootimg_args='--header_version %s' >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Boot_partition_name), miscInfo) + } + if a.partitionProps.Init_boot_partition_name != nil { + builder.Command(). + Textf("echo mkbootimg_init_args='--header_version' %s >> %s", a.getBootimgHeaderVersion(ctx, a.partitionProps.Init_boot_partition_name), miscInfo) + } + builder.Command(). + Textf("echo mkbootimg_version_args='--os_version %s --os_patch_level %s' >> %s", ctx.Config().PlatformVersionLastStable(), ctx.Config().PlatformSecurityPatch(), miscInfo). + Textf(" && echo multistage_support=1 >> %s", miscInfo). + Textf(" && echo blockimgdiff_versions=3,4 >> %s", miscInfo) + } + fsInfos := a.getFsInfos(ctx) + if _, ok := fsInfos["vendor"]; ok { + builder.Command().Textf("echo board_uses_vendorimage=true >> %s", miscInfo) + } + if fsInfos["system"].ErofsCompressHints != nil { + builder.Command().Textf("echo erofs_default_compress_hints=%s >> %s", fsInfos["system"].ErofsCompressHints, miscInfo) + } + if releaseTools := android.PathForModuleSrc(ctx, proptools.String(a.deviceProps.Releasetools_extension)); releaseTools != nil { + builder.Command().Textf("echo tool_extensions=%s >> %s", filepath.Dir(releaseTools.String()), miscInfo) + } + // ramdisk uses `compressed_cpio` fs_type + // https://cs.android.com/android/_/android/platform/build/+/30f05352c3e6f4333c77d4af66c253572d3ea6c9:core/Makefile;l=5923-5925;drc=519f75666431ee2926e0ec8991c682b28a4c9521;bpv=1;bpt=0 + if _, ok := fsInfos["ramdisk"]; ok { + builder.Command().Textf("echo lz4_ramdisks=true >> %s", miscInfo) + } + // recovery_mount_options + // TODO: Add support for TARGET_RECOVERY_FSTYPE_MOUNT_OPTIONS which can be used to override the default + builder.Command().Textf("echo recovery_mount_options=%s >> %s", defaultTargetRecoveryFstypeMountOptions, miscInfo) + + // vintf information + if proptools.Bool(ctx.Config().ProductVariables().Enforce_vintf_manifest) { + builder.Command().Textf("echo vintf_enforce=true >> %s", miscInfo) + } + if len(ctx.Config().DeviceManifestFiles()) > 0 { + builder.Command().Textf("echo vintf_include_empty_vendor_sku=true >> %s", miscInfo) + } if a.partitionProps.Recovery_partition_name == nil { builder.Command().Textf("echo no_recovery=true >> %s", miscInfo) } - fsInfos := a.getFsInfos(ctx) for _, partition := range android.SortedKeys(fsInfos) { if fsInfos[partition].PropFileForMiscInfo != nil { builder.Command().Text("cat").Input(fsInfos[partition].PropFileForMiscInfo).Textf(" >> %s", miscInfo) @@ -785,6 +850,10 @@ func (a *androidDevice) addMiscInfo(ctx android.ModuleContext) android.Path { if info, ok := android.OtherModuleProvider(ctx, superPartition, SuperImageProvider); ok { // cat dynamic_partition_info.txt builder.Command().Text("cat").Input(info.DynamicPartitionsInfo).Textf(" >> %s", miscInfo) + if info.AbUpdate { + builder.Command().Textf("echo ab_update=true >> %s", miscInfo) + } + } else { ctx.ModuleErrorf("Super partition %s does set SuperImageProvider\n", superPartition.Name()) } @@ -813,6 +882,12 @@ func (a *androidDevice) addMiscInfo(ctx android.ModuleContext) android.Path { return miscInfo } +func (a *androidDevice) getBootimgHeaderVersion(ctx android.ModuleContext, bootImgName *string) string { + bootImg := ctx.GetDirectDepProxyWithTag(proptools.String(bootImgName), filesystemDepTag) + bootImgInfo, _ := android.OtherModuleProvider(ctx, bootImg, BootimgInfoProvider) + return bootImgInfo.HeaderVersion +} + // addImgToTargetFiles invokes `add_img_to_target_files` and creates the following files in META/ // - apex_info.pb // - care_map.pb diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go index a1c4bce58..5ab0c6899 100644 --- a/filesystem/bootimg.go +++ b/filesystem/bootimg.go @@ -244,6 +244,7 @@ func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { Bootconfig: b.getBootconfigPath(ctx), Output: output, PropFileForMiscInfo: b.buildPropFileForMiscInfo(ctx), + HeaderVersion: proptools.String(b.properties.Header_version), }) extractedPublicKey := android.PathForModuleOut(ctx, b.partitionName()+".avbpubkey") @@ -292,6 +293,7 @@ type BootimgInfo struct { Bootconfig android.Path Output android.Path PropFileForMiscInfo android.Path + HeaderVersion string } func (b *bootimg) getKernelPath(ctx android.ModuleContext) android.Path { diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index 846e2e5e4..f8faa496f 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -1175,6 +1175,10 @@ func (f *filesystem) buildPropFileForMiscInfo(ctx android.ModuleContext) android // effect as <partition name>_disable_sparse. if f.properties.Partition_size == nil { addStr(f.partitionName()+"_disable_sparse", "true") + } else if f.partitionName() == "userdata" { + // Add userdata's partition size to misc_info.txt. + // userdata has been special-cased to make the make packaging misc_info.txt implementation + addStr("userdata_size", strconv.FormatInt(*f.properties.Partition_size, 10)) } fst := f.fsType(ctx) diff --git a/filesystem/super_image.go b/filesystem/super_image.go index cd7df027a..cf7e125a0 100644 --- a/filesystem/super_image.go +++ b/filesystem/super_image.go @@ -122,6 +122,8 @@ type SuperImageInfo struct { DynamicPartitionsInfo android.Path SuperEmptyImage android.Path + + AbUpdate bool } var SuperImageProvider = blueprint.NewProvider[SuperImageInfo]() @@ -201,6 +203,7 @@ func (s *superImage) GenerateAndroidBuildActions(ctx android.ModuleContext) { SubImageInfo: subImageInfos, DynamicPartitionsInfo: s.generateDynamicPartitionsInfo(ctx), SuperEmptyImage: superEmptyImage, + AbUpdate: proptools.Bool(s.properties.Ab_update), }) ctx.SetOutputFiles([]android.Path{output}, "") ctx.CheckbuildFile(output) diff --git a/fsgen/filesystem_creator_test.go b/fsgen/filesystem_creator_test.go index 81236a0a1..651d2d1a9 100644 --- a/fsgen/filesystem_creator_test.go +++ b/fsgen/filesystem_creator_test.go @@ -312,14 +312,14 @@ func TestPrebuiltEtcModuleGen(t *testing.T) { }), ).RunTest(t) - checkModuleProp := func(m android.Module, matcher func(actual interface{}) bool) bool { + getModuleProp := func(m android.Module, matcher func(actual interface{}) string) string { for _, prop := range m.GetProperties() { - if matcher(prop) { - return true + if str := matcher(prop); str != "" { + return str } } - return false + return "" } // check generated prebuilt_* module type install path and install partition @@ -350,6 +350,37 @@ func TestPrebuiltEtcModuleGen(t *testing.T) { etcModule.SubDir(), ) + // check that generated prebuilt_* module sets correct srcs + eval := generatedModule.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext)) + android.AssertStringEquals( + t, + "module expected to set correct srcs property", + "Vendor_0079_Product_0011.kl", + getModuleProp(generatedModule, func(actual interface{}) string { + if p, ok := actual.(*etc.PrebuiltEtcProperties); ok { + srcs := p.Srcs.GetOrDefault(eval, nil) + if len(srcs) == 2 { + return srcs[0] + } + } + return "" + }), + ) + android.AssertStringEquals( + t, + "module expected to set correct srcs property", + "Vendor_0079_Product_18d4.kl", + getModuleProp(generatedModule, func(actual interface{}) string { + if p, ok := actual.(*etc.PrebuiltEtcProperties); ok { + srcs := p.Srcs.GetOrDefault(eval, nil) + if len(srcs) == 2 { + return srcs[1] + } + } + return "" + }), + ) + // check that prebuilt_* module is not generated for non existing source file android.AssertStringEquals( t, @@ -363,60 +394,130 @@ func TestPrebuiltEtcModuleGen(t *testing.T) { generatedModule1 := result.ModuleForTests(t, "product-device_sample_etc-etc-1", "android_arm64_armv8-a").Module() // check that generated prebuilt_* module sets correct srcs and dsts property - eval := generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext)) - android.AssertBoolEquals( + eval = generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext)) + android.AssertStringEquals( t, "module expected to set correct srcs property", - true, - checkModuleProp(generatedModule0, func(actual interface{}) bool { + "apns-full-conf.xml", + getModuleProp(generatedModule0, func(actual interface{}) string { if p, ok := actual.(*etc.PrebuiltEtcProperties); ok { srcs := p.Srcs.GetOrDefault(eval, nil) - return len(srcs) == 1 && - srcs[0] == "apns-full-conf.xml" + if len(srcs) == 1 { + return srcs[0] + } } - return false + return "" }), ) - android.AssertBoolEquals( + android.AssertStringEquals( t, "module expected to set correct dsts property", - true, - checkModuleProp(generatedModule0, func(actual interface{}) bool { + "apns-conf.xml", + getModuleProp(generatedModule0, func(actual interface{}) string { if p, ok := actual.(*etc.PrebuiltDstsProperties); ok { dsts := p.Dsts.GetOrDefault(eval, nil) - return len(dsts) == 1 && - dsts[0] == "apns-conf.xml" + if len(dsts) == 1 { + return dsts[0] + } } - return false + return "" }), ) // check that generated prebuilt_* module sets correct srcs and dsts property eval = generatedModule1.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext)) - android.AssertBoolEquals( + android.AssertStringEquals( t, "module expected to set correct srcs property", - true, - checkModuleProp(generatedModule1, func(actual interface{}) bool { + "apns-full-conf.xml", + getModuleProp(generatedModule1, func(actual interface{}) string { if p, ok := actual.(*etc.PrebuiltEtcProperties); ok { srcs := p.Srcs.GetOrDefault(eval, nil) - return len(srcs) == 1 && - srcs[0] == "apns-full-conf.xml" + if len(srcs) == 1 { + return srcs[0] + } } - return false + return "" }), ) - android.AssertBoolEquals( + android.AssertStringEquals( t, "module expected to set correct dsts property", - true, - checkModuleProp(generatedModule1, func(actual interface{}) bool { + "apns-conf-2.xml", + getModuleProp(generatedModule1, func(actual interface{}) string { if p, ok := actual.(*etc.PrebuiltDstsProperties); ok { dsts := p.Dsts.GetOrDefault(eval, nil) - return len(dsts) == 1 && - dsts[0] == "apns-conf-2.xml" + if len(dsts) == 1 { + return dsts[0] + } + } + return "" + }), + ) + + generatedModule0 = result.ModuleForTests(t, "system-device_sample_etc-foo-0", "android_common").Module() + generatedModule1 = result.ModuleForTests(t, "system-device_sample_etc-foo-1", "android_common").Module() + + // check that generated prebuilt_* module sets correct srcs and dsts property + eval = generatedModule0.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext)) + android.AssertStringEquals( + t, + "module expected to set correct srcs property", + "apns-full-conf.xml", + getModuleProp(generatedModule0, func(actual interface{}) string { + if p, ok := actual.(*etc.PrebuiltEtcProperties); ok { + srcs := p.Srcs.GetOrDefault(eval, nil) + if len(srcs) == 1 { + return srcs[0] + } + } + return "" + }), + ) + android.AssertStringEquals( + t, + "module expected to set correct dsts property", + "foo/file.txt", + getModuleProp(generatedModule0, func(actual interface{}) string { + if p, ok := actual.(*etc.PrebuiltDstsProperties); ok { + dsts := p.Dsts.GetOrDefault(eval, nil) + if len(dsts) == 1 { + return dsts[0] + } + } + return "" + }), + ) + + // check generated prebuilt_* module specifies correct install path and relative install path + etcModule, _ = generatedModule1.(*etc.PrebuiltEtc) + android.AssertStringEquals( + t, + "module expected to have . install path", + ".", + etcModule.BaseDir(), + ) + android.AssertStringEquals( + t, + "module expected to set correct relative_install_path properties", + "foo", + etcModule.SubDir(), + ) + + // check that generated prebuilt_* module sets correct srcs + eval = generatedModule1.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext)) + android.AssertStringEquals( + t, + "module expected to set correct srcs property", + "apns-full-conf.xml", + getModuleProp(generatedModule1, func(actual interface{}) string { + if p, ok := actual.(*etc.PrebuiltEtcProperties); ok { + srcs := p.Srcs.GetOrDefault(eval, nil) + if len(srcs) == 1 { + return srcs[0] + } } - return false + return "" }), ) } diff --git a/fsgen/prebuilt_etc_modules_gen.go b/fsgen/prebuilt_etc_modules_gen.go index df361976e..e9dabe44c 100644 --- a/fsgen/prebuilt_etc_modules_gen.go +++ b/fsgen/prebuilt_etc_modules_gen.go @@ -215,6 +215,7 @@ var ( "system": etc.PrebuiltSystemFactory, "res": etc.PrebuiltResFactory, "rfs": etc.PrebuiltRfsFactory, + "tee": etc.PrebuiltTeeFactory, "tts": etc.PrebuiltVoicepackFactory, "tvconfig": etc.PrebuiltTvConfigFactory, "tvservice": etc.PrebuiltTvServiceFactory, @@ -340,28 +341,35 @@ func createPrebuiltEtcModulesInDirectory(ctx android.LoadHookContext, partition, // Set appropriate srcs, dsts, and releative_install_path based on // the source and install file names - if allCopyFileNamesUnchanged { - modulePropsPtr.Srcs = srcBaseFiles + modulePropsPtr.Srcs = srcBaseFiles + + // prebuilt_root should only be used in very limited cases in prebuilt_etc moddule gen, where: + // - all source file names are identical to the installed file names, and + // - all source files are installed in root, not the subdirectories of root + // prebuilt_root currently does not have a good way to specify the names of the multiple + // installed files, and prebuilt_root does not allow installing files at a subdirectory + // of the root. + // Use prebuilt_any instead of prebuilt_root if either of the conditions are not met as + // a fallback behavior. + if etcInstallPathKey == "" { + if !(allCopyFileNamesUnchanged && android.InList(relDestDirFromInstallDirBase, []string{"", "."})) { + moduleFactory = etc.PrebuiltAnyFactory + } + } - // Specify relative_install_path if it is not installed in the root directory of the - // partition + if allCopyFileNamesUnchanged { + // Specify relative_install_path if it is not installed in the base directory of the module. + // In case of prebuilt_{root,any} this is equivalent to the root of the partition. if !android.InList(relDestDirFromInstallDirBase, []string{"", "."}) { propsList = append(propsList, &prebuiltSubdirProperties{ Relative_install_path: proptools.StringPtr(relDestDirFromInstallDirBase), }) } } else { - // If dsts property has to be set and the selected module type is prebuilt_root, - // use prebuilt_any instead. - if etcInstallPathKey == "" { - moduleFactory = etc.PrebuiltAnyFactory - } - modulePropsPtr.Srcs = srcBaseFiles dsts := proptools.NewConfigurable[[]string](nil, nil) for _, installBaseFile := range installBaseFiles { dsts.AppendSimpleValue([]string{filepath.Join(relDestDirFromInstallDirBase, installBaseFile)}) } - propsList = append(propsList, &etc.PrebuiltDstsProperties{ Dsts: dsts, }) diff --git a/genrule/Android.bp b/genrule/Android.bp index 49df48075..b82f2a9c3 100644 --- a/genrule/Android.bp +++ b/genrule/Android.bp @@ -14,7 +14,6 @@ bootstrap_go_package { "soong-shared", ], srcs: [ - "allowlists.go", "genrule.go", "locations.go", ], diff --git a/genrule/allowlists.go b/genrule/allowlists.go deleted file mode 100644 index 45a7f727c..000000000 --- a/genrule/allowlists.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2023 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 genrule - -var ( - SandboxingDenyModuleList = []string{ - // go/keep-sorted start - // go/keep-sorted end - } -) diff --git a/genrule/genrule.go b/genrule/genrule.go index 710ec9555..a7c09e76c 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -969,30 +969,9 @@ func DefaultsFactory(props ...interface{}) android.Module { return module } -var sandboxingAllowlistKey = android.NewOnceKey("genruleSandboxingAllowlistKey") - -type sandboxingAllowlistSets struct { - sandboxingDenyModuleSet map[string]bool -} - -func getSandboxingAllowlistSets(ctx android.PathContext) *sandboxingAllowlistSets { - return ctx.Config().Once(sandboxingAllowlistKey, func() interface{} { - sandboxingDenyModuleSet := map[string]bool{} - - android.AddToStringSet(sandboxingDenyModuleSet, SandboxingDenyModuleList) - return &sandboxingAllowlistSets{ - sandboxingDenyModuleSet: sandboxingDenyModuleSet, - } - }).(*sandboxingAllowlistSets) -} - func getSandboxedRuleBuilder(ctx android.ModuleContext, r *android.RuleBuilder) *android.RuleBuilder { if !ctx.DeviceConfig().GenruleSandboxing() { return r.SandboxTools() } - sandboxingAllowlistSets := getSandboxingAllowlistSets(ctx) - if sandboxingAllowlistSets.sandboxingDenyModuleSet[ctx.ModuleName()] { - return r.SandboxTools() - } return r.SandboxInputs() } diff --git a/tradefed_modules/Android.bp b/tradefed_modules/Android.bp index a765a0532..37bae3928 100644 --- a/tradefed_modules/Android.bp +++ b/tradefed_modules/Android.bp @@ -14,11 +14,9 @@ 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_suite.go b/tradefed_modules/test_suite.go deleted file mode 100644 index 8b7babf52..000000000 --- a/tradefed_modules/test_suite.go +++ /dev/null @@ -1,173 +0,0 @@ -// 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: android.SortedUniqueStrings(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 deleted file mode 100644 index 3e1472cee..000000000 --- a/tradefed_modules/test_suite_test.go +++ /dev/null @@ -1,151 +0,0 @@ -// 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(t, "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(t, "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", - ] - } - `) -} |