Merge "Soong: libbpf_prog: change output filetype to .bpf" into main
diff --git a/android/apex.go b/android/apex.go
index 1bfcf04..414d4e1 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -70,11 +70,6 @@
// prebuilt, the name here doesn't have the `prebuilt_` prefix.
InApexModules []string
- // Pointers to the ApexContents struct each of which is for apexBundle modules that this
- // module is part of. The ApexContents gives information about which modules the apexBundle
- // has and whether a module became part of the apexBundle via a direct dependency or not.
- ApexContents []*ApexContents
-
// True if this is for a prebuilt_apex.
//
// If true then this will customize the apex processing to make it suitable for handling
@@ -162,7 +157,6 @@
// ApexBundleInfo contains information about the dependencies of an apex
type ApexBundleInfo struct {
- Contents *ApexContents
}
var ApexBundleInfoProvider = blueprint.NewMutatorProvider[ApexBundleInfo]("apex_info")
@@ -220,10 +214,6 @@
// this after apex.apexMutator is run.
InAnyApex() bool
- // Returns true if this module is directly in any APEX. Call this AFTER apex.apexMutator is
- // run.
- DirectlyInAnyApex() bool
-
// NotInPlatform returns true if the module is not available to the platform due to
// apex_available being set and not containing "//apex_available:platform".
NotInPlatform() bool
@@ -285,9 +275,6 @@
// See ApexModule.InAnyApex()
InAnyApex bool `blueprint:"mutated"`
- // See ApexModule.DirectlyInAnyApex()
- DirectlyInAnyApex bool `blueprint:"mutated"`
-
// See ApexModule.NotAvailableForPlatform()
NotAvailableForPlatform bool `blueprint:"mutated"`
@@ -324,16 +311,6 @@
AlwaysRequireApexVariant() bool
}
-// Marker interface that identifies dependencies that should inherit the DirectlyInAnyApex state
-// from the parent to the child. For example, stubs libraries are marked as DirectlyInAnyApex if
-// their implementation is in an apex.
-type CopyDirectlyInAnyApexTag interface {
- blueprint.DependencyTag
-
- // Method that differentiates this interface from others.
- CopyDirectlyInAnyApex()
-}
-
// Interface that identifies dependencies to skip Apex dependency check
type SkipApexAllowedDependenciesCheck interface {
// Returns true to skip the Apex dependency check, which limits the allowed dependency in build.
@@ -411,11 +388,6 @@
}
// Implements ApexModule
-func (m *ApexModuleBase) DirectlyInAnyApex() bool {
- return m.ApexProperties.DirectlyInAnyApex
-}
-
-// Implements ApexModule
func (m *ApexModuleBase) NotInPlatform() bool {
return !m.AvailableFor(AvailableToPlatform)
}
@@ -575,7 +547,6 @@
// Variants having the same mergedName are deduped
merged[index].InApexVariants = append(merged[index].InApexVariants, variantName)
merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...)
- merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
// Platform APIs is allowed for this module only when all APEXes containing
// the module are with `use_platform_apis: true`.
@@ -586,7 +557,6 @@
apexInfo.ApexVariationName = mergedName
apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants)
apexInfo.InApexModules = CopyOf(apexInfo.InApexModules)
- apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...)
apexInfo.TestApexes = CopyOf(apexInfo.TestApexes)
merged = append(merged, apexInfo)
}
@@ -674,14 +644,7 @@
apexInfos, _ = mergeApexVariations(apexInfos)
}
- var inApex ApexMembership
- for _, a := range apexInfos {
- for _, apexContents := range a.ApexContents {
- inApex = inApex.merge(apexContents.contents[ctx.ModuleName()])
- }
- }
base.ApexProperties.InAnyApex = true
- base.ApexProperties.DirectlyInAnyApex = inApex == directlyInApex
if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() {
// Do not install the module for platform, but still allow it to output
@@ -772,77 +735,6 @@
})
}
-// UpdateDirectlyInAnyApex uses the final module to store if any variant of this module is directly
-// in any APEX, and then copies the final value to all the modules. It also copies the
-// DirectlyInAnyApex value to any transitive dependencies with a CopyDirectlyInAnyApexTag
-// dependency tag.
-func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) {
- base := am.apexModuleBase()
- // Copy DirectlyInAnyApex and InAnyApex from any transitive dependencies with a
- // CopyDirectlyInAnyApexTag dependency tag.
- mctx.WalkDeps(func(child, parent Module) bool {
- if _, ok := mctx.OtherModuleDependencyTag(child).(CopyDirectlyInAnyApexTag); ok {
- depBase := child.(ApexModule).apexModuleBase()
- depBase.apexPropertiesLock.Lock()
- defer depBase.apexPropertiesLock.Unlock()
- depBase.ApexProperties.DirectlyInAnyApex = base.ApexProperties.DirectlyInAnyApex
- depBase.ApexProperties.InAnyApex = base.ApexProperties.InAnyApex
- return true
- }
- return false
- })
-}
-
-// ApexMembership tells how a module became part of an APEX.
-type ApexMembership int
-
-const (
- notInApex ApexMembership = 0
- indirectlyInApex = iota
- directlyInApex
-)
-
-// ApexContents gives an information about member modules of an apexBundle. Each apexBundle has an
-// apexContents, and modules in that apex have a provider containing the apexContents of each
-// apexBundle they are part of.
-type ApexContents struct {
- // map from a module name to its membership in this apexBundle
- contents map[string]ApexMembership
-}
-
-// NewApexContents creates and initializes an ApexContents that is suitable
-// for use with an apex module.
-// - contents is a map from a module name to information about its membership within
-// the apex.
-func NewApexContents(contents map[string]ApexMembership) *ApexContents {
- return &ApexContents{
- contents: contents,
- }
-}
-
-// Updates an existing membership by adding a new direct (or indirect) membership
-func (i ApexMembership) Add(direct bool) ApexMembership {
- if direct || i == directlyInApex {
- return directlyInApex
- }
- return indirectlyInApex
-}
-
-// Merges two membership into one. Merging is needed because a module can be a part of an apexBundle
-// in many different paths. For example, it could be dependend on by the apexBundle directly, but at
-// the same time, there might be an indirect dependency to the module. In that case, the more
-// specific dependency (the direct one) is chosen.
-func (i ApexMembership) merge(other ApexMembership) ApexMembership {
- if other == directlyInApex || i == directlyInApex {
- return directlyInApex
- }
-
- if other == indirectlyInApex || i == indirectlyInApex {
- return indirectlyInApex
- }
- return notInApex
-}
-
////////////////////////////////////////////////////////////////////////////////////////////////////
//Below are routines for extra safety checks.
//
diff --git a/android/config.go b/android/config.go
index 27d3b87..dff3ea5 100644
--- a/android/config.go
+++ b/android/config.go
@@ -285,6 +285,10 @@
return c.config.productVariables.GetBuildFlagBool("RELEASE_CREATE_ACONFIG_STORAGE_FILE")
}
+func (c Config) ReleaseUseSystemFeatureBuildFlags() bool {
+ return c.config.productVariables.GetBuildFlagBool("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS")
+}
+
// A DeviceConfig object represents the configuration for a particular device
// being built. For now there will only be one of these, but in the future there
// may be multiple devices being built.
@@ -1834,6 +1838,10 @@
return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
}
+func (c *config) DefaultApexPayloadType() string {
+ return StringDefault(c.productVariables.DefaultApexPayloadType, "ext4")
+}
+
func (c *config) UseSoongSystemImage() bool {
return Bool(c.productVariables.UseSoongSystemImage)
}
diff --git a/android/sbom.go b/android/sbom.go
index 2a5499e..f2b9c0f 100644
--- a/android/sbom.go
+++ b/android/sbom.go
@@ -15,9 +15,7 @@
package android
import (
- "io"
"path/filepath"
- "strings"
"github.com/google/blueprint"
)
@@ -55,21 +53,7 @@
if !ctx.Config().HasDeviceProduct() {
return
}
- // Get all METADATA files and add them as implicit input
- metadataFileListFile := PathForArbitraryOutput(ctx, ".module_paths", "METADATA.list")
- f, err := ctx.Config().fs.Open(metadataFileListFile.String())
- if err != nil {
- panic(err)
- }
- b, err := io.ReadAll(f)
- if err != nil {
- panic(err)
- }
- allMetadataFiles := strings.Split(string(b), "\n")
- implicits := []Path{metadataFileListFile}
- for _, path := range allMetadataFiles {
- implicits = append(implicits, PathForSource(ctx, path))
- }
+ implicits := []Path{}
prodVars := ctx.Config().productVariables
buildFingerprintFile := PathForArbitraryOutput(ctx, "target", "product", String(prodVars.DeviceName), "build_fingerprint.txt")
implicits = append(implicits, buildFingerprintFile)
diff --git a/android/variable.go b/android/variable.go
index 88cf5a5..7ae9a43 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -408,9 +408,10 @@
Ndk_abis *bool `json:",omitempty"`
- ForceApexSymlinkOptimization *bool `json:",omitempty"`
- CompressedApex *bool `json:",omitempty"`
- Aml_abis *bool `json:",omitempty"`
+ ForceApexSymlinkOptimization *bool `json:",omitempty"`
+ CompressedApex *bool `json:",omitempty"`
+ DefaultApexPayloadType *string `json:",omitempty"`
+ Aml_abis *bool `json:",omitempty"`
DexpreoptGlobalConfig *string `json:",omitempty"`
@@ -612,6 +613,7 @@
// Boot image stuff
BuildingRamdiskImage bool `json:",omitempty"`
ProductBuildBootImage bool `json:",omitempty"`
+ ProductBuildVendorBootImage string `json:",omitempty"`
ProductBuildInitBootImage bool `json:",omitempty"`
BoardUsesRecoveryAsBoot bool `json:",omitempty"`
BoardPrebuiltBootimage string `json:",omitempty"`
@@ -620,6 +622,7 @@
BoardInitBootimagePartitionSize string `json:",omitempty"`
BoardBootHeaderVersion string `json:",omitempty"`
TargetKernelPath string `json:",omitempty"`
+ BoardUsesGenericKernelImage bool `json:",omitempty"`
// Avb (android verified boot) stuff
BoardAvbEnable bool `json:",omitempty"`
diff --git a/apex/apex.go b/apex/apex.go
index dc24df3..ed9e58a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -67,7 +67,6 @@
// it should create a platform variant.
ctx.BottomUp("mark_platform_availability", markPlatformAvailability)
ctx.Transition("apex", &apexTransitionMutator{})
- ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).MutatesDependencies()
}
type apexBundleProperties struct {
@@ -993,25 +992,7 @@
return true
}
- // Records whether a certain module is included in this apexBundle via direct dependency or
- // inndirect dependency.
- contents := make(map[string]android.ApexMembership)
- mctx.WalkDeps(func(child, parent android.Module) bool {
- if !continueApexDepsWalk(child, parent) {
- return false
- }
- // If the parent is apexBundle, this child is directly depended.
- _, directDep := parent.(*apexBundle)
- depName := mctx.OtherModuleName(child)
- contents[depName] = contents[depName].Add(directDep)
- return true
- })
-
- // The membership information is saved for later access
- apexContents := android.NewApexContents(contents)
- android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{
- Contents: apexContents,
- })
+ android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
minSdkVersion := a.minSdkVersion(mctx)
// When min_sdk_version is not set, the apex is built against FutureApiLevel.
@@ -1040,7 +1021,6 @@
UsePlatformApis: a.UsePlatformApis(),
InApexVariants: []string{apexVariationName},
InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo
- ApexContents: []*android.ApexContents{apexContents},
TestApexes: testApexes,
BaseApexName: mctx.ModuleName(),
ApexAvailableName: proptools.String(a.properties.Apex_available_name),
@@ -1242,14 +1222,6 @@
return true
}
-// See android.UpdateDirectlyInAnyApex
-// TODO(jiyong): move this to android/apex.go?
-func apexDirectlyInAnyMutator(mctx android.BottomUpMutatorContext) {
- if am, ok := mctx.Module().(android.ApexModule); ok {
- android.UpdateDirectlyInAnyApex(mctx, am)
- }
-}
-
const (
// File extensions of an APEX for different packaging methods
imageApexSuffix = ".apex"
@@ -1752,7 +1724,8 @@
}
func (a *apexBundle) setPayloadFsType(ctx android.ModuleContext) {
- switch proptools.StringDefault(a.properties.Payload_fs_type, ext4FsType) {
+ defaultFsType := ctx.Config().DefaultApexPayloadType()
+ switch proptools.StringDefault(a.properties.Payload_fs_type, defaultFsType) {
case ext4FsType:
a.payloadFsType = ext4
case f2fsFsType:
@@ -2082,7 +2055,7 @@
//
// Skip the dependency in unbundled builds where the device image is not
// being built.
- if ch.IsStubsImplementationRequired() && !am.DirectlyInAnyApex() && !ctx.Config().UnbundledBuild() {
+ if ch.IsStubsImplementationRequired() && !am.NotInPlatform() && !ctx.Config().UnbundledBuild() {
// we need a module name for Make
name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
if !android.InList(name, a.makeModulesToInstall) {
@@ -2179,8 +2152,6 @@
ctx.PropertyErrorf("systemserverclasspath_fragments",
"systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
}
- } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
- // nothing
} else if depTag == android.DarwinUniversalVariantTag {
// nothing
} else if depTag == android.RequiredDepTag {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index acf3b91..2bef0cc 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -306,10 +306,6 @@
// extra copying of files. Contrast that with source apex modules that has to build each variant
// from source.
func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) {
-
- // Collect direct dependencies into contents.
- contents := make(map[string]android.ApexMembership)
-
// Collect the list of dependencies.
var dependencies []android.ApexModule
mctx.WalkDeps(func(child, parent android.Module) bool {
@@ -347,21 +343,13 @@
// behavior whether there is a corresponding source module present or not.
depName = android.RemoveOptionalPrebuiltPrefix(depName)
- // Remember if this module was added as a direct dependency.
- direct := parent == mctx.Module()
- contents[depName] = contents[depName].Add(direct)
-
// Add the module to the list of dependencies that need to have an APEX variant.
dependencies = append(dependencies, child.(android.ApexModule))
return true
})
- // Create contents for the prebuilt_apex and store it away for later use.
- apexContents := android.NewApexContents(contents)
- android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{
- Contents: apexContents,
- })
+ android.SetProvider(mctx, android.ApexBundleInfoProvider, android.ApexBundleInfo{})
// Create an ApexInfo for the prebuilt_apex.
apexVariationName := p.ApexVariationName()
@@ -369,7 +357,6 @@
ApexVariationName: apexVariationName,
InApexVariants: []string{apexVariationName},
InApexModules: []string{p.BaseModuleName()}, // BaseModuleName() to avoid the prebuilt_ prefix.
- ApexContents: []*android.ApexContents{apexContents},
ForPrebuiltApex: true,
}
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 226d95c..2249506 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -36,6 +36,8 @@
output android.Path
installDir android.InstallPath
+
+ bootImageType bootImageType
}
type BootimgProperties struct {
@@ -56,9 +58,13 @@
// https://source.android.com/devices/bootloader/boot-image-header
Header_version *string
- // Determines if this image is for the vendor_boot partition. Default is false. Refer to
- // https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
- Vendor_boot *bool
+ // Determines the specific type of boot image this module is building. Can be boot,
+ // vendor_boot or init_boot. Defaults to boot.
+ // Refer to https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
+ // for vendor_boot.
+ // Refer to https://source.android.com/docs/core/architecture/partitions/generic-boot for
+ // init_boot.
+ Boot_image_type *string
// Optional kernel commandline arguments
Cmdline []string `android:"arch_variant"`
@@ -67,6 +73,10 @@
// and `header_version` is greater than or equal to 4.
Bootconfig *string `android:"arch_variant,path"`
+ // The size of the partition on the device. It will be a build error if this built partition
+ // image exceeds this size.
+ Partition_size *int64
+
// When set to true, sign the image with avbtool. Default is false.
Use_avb *bool
@@ -81,6 +91,41 @@
Avb_algorithm *string
}
+type bootImageType int
+
+const (
+ unsupported bootImageType = iota
+ boot
+ vendorBoot
+ initBoot
+)
+
+func toBootImageType(ctx android.ModuleContext, bootImageType string) bootImageType {
+ switch bootImageType {
+ case "boot":
+ return boot
+ case "vendor_boot":
+ return vendorBoot
+ case "init_boot":
+ return initBoot
+ default:
+ ctx.ModuleErrorf("Unknown boot_image_type %s. Must be one of \"boot\", \"vendor_boot\", or \"init_boot\"", bootImageType)
+ }
+ return unsupported
+}
+
+func (b bootImageType) isBoot() bool {
+ return b == boot
+}
+
+func (b bootImageType) isVendorBoot() bool {
+ return b == vendorBoot
+}
+
+func (b bootImageType) isInitBoot() bool {
+ return b == initBoot
+}
+
// bootimg is the image for the boot partition. It consists of header, kernel, ramdisk, and dtb.
func BootimgFactory() android.Module {
module := &bootimg{}
@@ -112,8 +157,8 @@
}
func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- vendor := proptools.Bool(b.properties.Vendor_boot)
- unsignedOutput := b.buildBootImage(ctx, vendor)
+ b.bootImageType = toBootImageType(ctx, proptools.StringDefault(b.properties.Boot_image_type, "boot"))
+ unsignedOutput := b.buildBootImage(ctx)
output := unsignedOutput
if proptools.Bool(b.properties.Use_avb) {
@@ -127,18 +172,19 @@
b.output = output
}
-func (b *bootimg) buildBootImage(ctx android.ModuleContext, vendor bool) android.Path {
+func (b *bootimg) buildBootImage(ctx android.ModuleContext) android.Path {
output := android.PathForModuleOut(ctx, "unsigned", b.installFileName())
builder := android.NewRuleBuilder(pctx, ctx)
cmd := builder.Command().BuiltTool("mkbootimg")
kernel := proptools.String(b.properties.Kernel_prebuilt)
- if vendor && kernel != "" {
+ if b.bootImageType.isVendorBoot() && kernel != "" {
ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel")
return output
}
- if !vendor && kernel == "" {
+
+ if b.bootImageType.isBoot() && kernel == "" {
ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel")
return output
}
@@ -146,6 +192,12 @@
cmd.FlagWithInput("--kernel ", android.PathForModuleSrc(ctx, kernel))
}
+ // These arguments are passed for boot.img and init_boot.img generation
+ if b.bootImageType.isBoot() || b.bootImageType.isInitBoot() {
+ cmd.FlagWithArg("--os_version ", ctx.Config().PlatformVersionLastStable())
+ cmd.FlagWithArg("--os_patch_level ", ctx.Config().PlatformSecurityPatch())
+ }
+
dtbName := proptools.String(b.properties.Dtb_prebuilt)
if dtbName != "" {
dtb := android.PathForModuleSrc(ctx, dtbName)
@@ -155,7 +207,7 @@
cmdline := strings.Join(b.properties.Cmdline, " ")
if cmdline != "" {
flag := "--cmdline "
- if vendor {
+ if b.bootImageType.isVendorBoot() {
flag = "--vendor_cmdline "
}
cmd.FlagWithArg(flag, proptools.ShellEscapeIncludingSpaces(cmdline))
@@ -182,7 +234,7 @@
ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
if filesystem, ok := ramdisk.(*filesystem); ok {
flag := "--ramdisk "
- if vendor {
+ if b.bootImageType.isVendorBoot() {
flag = "--vendor_ramdisk "
}
cmd.FlagWithInput(flag, filesystem.OutputPath())
@@ -194,7 +246,7 @@
bootconfig := proptools.String(b.properties.Bootconfig)
if bootconfig != "" {
- if !vendor {
+ if !b.bootImageType.isVendorBoot() {
ctx.PropertyErrorf("bootconfig", "requires vendor_boot: true")
return output
}
@@ -205,12 +257,17 @@
cmd.FlagWithInput("--vendor_bootconfig ", android.PathForModuleSrc(ctx, bootconfig))
}
+ // Output flag for boot.img and init_boot.img
flag := "--output "
- if vendor {
+ if b.bootImageType.isVendorBoot() {
flag = "--vendor_boot "
}
cmd.FlagWithOutput(flag, output)
+ if b.properties.Partition_size != nil {
+ assertMaxImageSize(builder, output, *b.properties.Partition_size, proptools.Bool(b.properties.Use_avb))
+ }
+
builder.Build("build_bootimg", fmt.Sprintf("Creating %s", b.BaseModuleName()))
return output
}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index ba5319f..dadacae 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -710,6 +710,7 @@
"odm_dlkm",
"system_dlkm",
"ramdisk",
+ "vendor_ramdisk",
}
func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) {
@@ -925,3 +926,26 @@
return provideModules, requireModules
}
+
+// Checks that the given file doesn't exceed the given size, and will also print a warning
+// if it's nearing the maximum size. Equivalent to assert-max-image-size in make:
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/definitions.mk;l=3455;drc=993c4de29a02a6accd60ceaaee153307e1a18d10
+func assertMaxImageSize(builder *android.RuleBuilder, image android.Path, maxSize int64, addAvbLater bool) {
+ if addAvbLater {
+ // The value 69632 is derived from MAX_VBMETA_SIZE + MAX_FOOTER_SIZE in avbtool.
+ // Logic copied from make:
+ // https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=228;drc=a6a0007ef24e16c0b79f439beac4a118416717e6
+ maxSize -= 69632
+ }
+ cmd := builder.Command()
+ cmd.Textf(`file="%s"; maxsize="%d";`+
+ `total=$(stat -c "%%s" "$file" | tr -d '\n');`+
+ `if [ "$total" -gt "$maxsize" ]; then `+
+ ` echo "error: $file too large ($total > $maxsize)";`+
+ ` false;`+
+ `elif [ "$total" -gt $((maxsize - 32768)) ]; then `+
+ ` echo "WARNING: $file approaching size limit ($total now; limit $maxsize)";`+
+ `fi`,
+ image, maxSize)
+ cmd.Implicit(image)
+}
diff --git a/fsgen/boot_imgs.go b/fsgen/boot_imgs.go
index 66d9107..630aaff 100644
--- a/fsgen/boot_imgs.go
+++ b/fsgen/boot_imgs.go
@@ -3,7 +3,9 @@
import (
"android/soong/android"
"android/soong/filesystem"
+ "fmt"
"path/filepath"
+ "strconv"
"github.com/google/blueprint/proptools"
)
@@ -34,12 +36,63 @@
},
)
+ var partitionSize *int64
+ if partitionVariables.BoardBootimagePartitionSize != "" {
+ parsed, err := strconv.ParseInt(partitionVariables.BoardBootimagePartitionSize, 10, 64)
+ if err != nil {
+ panic(fmt.Sprintf("BOARD_BOOTIMAGE_PARTITION_SIZE must be an int, got %s", partitionVariables.BoardBootimagePartitionSize))
+ }
+ partitionSize = &parsed
+ }
+
bootImageName := generatedModuleNameForPartition(ctx.Config(), "boot")
ctx.CreateModule(
filesystem.BootimgFactory,
&filesystem.BootimgProperties{
Kernel_prebuilt: proptools.StringPtr(":" + kernelFilegroupName),
+ Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+ Partition_size: partitionSize,
+ },
+ &struct {
+ Name *string
+ }{
+ Name: proptools.StringPtr(bootImageName),
+ },
+ )
+ return true
+}
+
+func createVendorBootImage(ctx android.LoadHookContext) bool {
+ partitionVariables := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+
+ bootImageName := generatedModuleNameForPartition(ctx.Config(), "vendor_boot")
+
+ ctx.CreateModule(
+ filesystem.BootimgFactory,
+ &filesystem.BootimgProperties{
+ Boot_image_type: proptools.StringPtr("vendor_boot"),
+ Ramdisk_module: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk")),
+ Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
+ },
+ &struct {
+ Name *string
+ }{
+ Name: proptools.StringPtr(bootImageName),
+ },
+ )
+ return true
+}
+
+func createInitBootImage(ctx android.LoadHookContext) bool {
+ partitionVariables := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+
+ bootImageName := generatedModuleNameForPartition(ctx.Config(), "init_boot")
+
+ ctx.CreateModule(
+ filesystem.BootimgFactory,
+ &filesystem.BootimgProperties{
+ Boot_image_type: proptools.StringPtr("init_boot"),
Ramdisk_module: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "ramdisk")),
Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion),
},
@@ -76,3 +129,44 @@
return false
}
+
+// Returns the equivalent of the BUILDING_VENDOR_BOOT_IMAGE variable in make. Derived from this logic:
+// https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=518;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+func buildingVendorBootImage(partitionVars android.PartitionVariables) bool {
+ if v, exists := boardBootHeaderVersion(partitionVars); exists && v >= 3 {
+ x := partitionVars.ProductBuildVendorBootImage
+ if x == "" || x == "true" {
+ return true
+ }
+ }
+
+ return false
+}
+
+// Derived from: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/board_config.mk;l=480;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+func buildingInitBootImage(partitionVars android.PartitionVariables) bool {
+ if !partitionVars.ProductBuildInitBootImage {
+ if partitionVars.BoardUsesRecoveryAsBoot || len(partitionVars.BoardPrebuiltInitBootimage) > 0 {
+ return false
+ } else if len(partitionVars.BoardInitBootimagePartitionSize) > 0 {
+ return true
+ }
+ } else {
+ if partitionVars.BoardUsesRecoveryAsBoot {
+ panic("PRODUCT_BUILD_INIT_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT. Use only one option.")
+ }
+ return true
+ }
+ return false
+}
+
+func boardBootHeaderVersion(partitionVars android.PartitionVariables) (int, bool) {
+ if len(partitionVars.BoardBootHeaderVersion) == 0 {
+ return 0, false
+ }
+ v, err := strconv.ParseInt(partitionVars.BoardBootHeaderVersion, 10, 32)
+ if err != nil {
+ panic(fmt.Sprintf("BOARD_BOOT_HEADER_VERSION must be an int, got: %q", partitionVars.BoardBootHeaderVersion))
+ }
+ return int(v), true
+}
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index b1b8f6a..6bfb097 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -48,7 +48,9 @@
Vbmeta_module_names []string `blueprint:"mutated"`
Vbmeta_partition_names []string `blueprint:"mutated"`
- Boot_image string `blueprint:"mutated" android:"path_device_first"`
+ Boot_image string `blueprint:"mutated" android:"path_device_first"`
+ Vendor_boot_image string `blueprint:"mutated" android:"path_device_first"`
+ Init_boot_image string `blueprint:"mutated" android:"path_device_first"`
}
type filesystemCreator struct {
@@ -74,6 +76,7 @@
}
func generatedPartitions(ctx android.LoadHookContext) []string {
+ partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
generatedPartitions := []string{"system"}
if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
generatedPartitions = append(generatedPartitions, "system_ext")
@@ -90,18 +93,21 @@
if ctx.DeviceConfig().BuildingUserdataImage() && ctx.DeviceConfig().UserdataPath() == "data" {
generatedPartitions = append(generatedPartitions, "userdata")
}
- if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingSystemDlkmImage {
+ if partitionVars.BuildingSystemDlkmImage {
generatedPartitions = append(generatedPartitions, "system_dlkm")
}
- if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingVendorDlkmImage {
+ if partitionVars.BuildingVendorDlkmImage {
generatedPartitions = append(generatedPartitions, "vendor_dlkm")
}
- if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingOdmDlkmImage {
+ if partitionVars.BuildingOdmDlkmImage {
generatedPartitions = append(generatedPartitions, "odm_dlkm")
}
- if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingRamdiskImage {
+ if partitionVars.BuildingRamdiskImage {
generatedPartitions = append(generatedPartitions, "ramdisk")
}
+ if buildingVendorBootImage(partitionVars) {
+ generatedPartitions = append(generatedPartitions, "vendor_ramdisk")
+ }
return generatedPartitions
}
@@ -117,13 +123,28 @@
}
}
- if buildingBootImage(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse) {
+ partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+ if buildingBootImage(partitionVars) {
if createBootImage(ctx) {
f.properties.Boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "boot")
} else {
f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "boot")
}
}
+ if buildingVendorBootImage(partitionVars) {
+ if createVendorBootImage(ctx) {
+ f.properties.Vendor_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot")
+ } else {
+ f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot")
+ }
+ }
+ if buildingInitBootImage(partitionVars) {
+ if createInitBootImage(ctx) {
+ f.properties.Init_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "init_boot")
+ } else {
+ f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "init_boot")
+ }
+ }
for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
@@ -182,13 +203,13 @@
ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
}
-func partitionSpecificFsProps(fsProps *filesystem.FilesystemProperties, partitionType string) {
+func partitionSpecificFsProps(fsProps *filesystem.FilesystemProperties, partitionVars android.PartitionVariables, partitionType string) {
switch partitionType {
case "system":
fsProps.Build_logtags = proptools.BoolPtr(true)
// https://source.corp.google.com/h/googleplex-android/platform/build//639d79f5012a6542ab1f733b0697db45761ab0f3:core/packaging/flags.mk;l=21;drc=5ba8a8b77507f93aa48cc61c5ba3f31a4d0cbf37;bpv=1;bpt=0
fsProps.Gen_aconfig_flags_pb = proptools.BoolPtr(true)
- // Identical to that of the generic_system_image
+ // Identical to that of the aosp_shared_system_image
fsProps.Fsverity.Inputs = []string{
"etc/boot-image.prof",
"etc/dirty-image-objects",
@@ -248,7 +269,28 @@
}
case "userdata":
fsProps.Base_dir = proptools.StringPtr("data")
-
+ case "ramdisk":
+ // Following the logic in https://cs.android.com/android/platform/superproject/main/+/c3c5063df32748a8806ce5da5dd0db158eab9ad9:build/make/core/Makefile;l=1307
+ fsProps.Dirs = android.NewSimpleConfigurable([]string{
+ "debug_ramdisk",
+ "dev",
+ "metadata",
+ "mnt",
+ "proc",
+ "second_stage_resources",
+ "sys",
+ })
+ if partitionVars.BoardUsesGenericKernelImage {
+ fsProps.Dirs.AppendSimpleValue([]string{
+ "first_stage_ramdisk/debug_ramdisk",
+ "first_stage_ramdisk/dev",
+ "first_stage_ramdisk/metadata",
+ "first_stage_ramdisk/mnt",
+ "first_stage_ramdisk/proc",
+ "first_stage_ramdisk/second_stage_resources",
+ "first_stage_ramdisk/sys",
+ })
+ }
}
}
@@ -564,11 +606,13 @@
fsProps.Partition_name = proptools.StringPtr(partitionType)
- fsProps.Base_dir = proptools.StringPtr(partitionType)
+ if !strings.Contains(partitionType, "ramdisk") {
+ fsProps.Base_dir = proptools.StringPtr(partitionType)
+ }
fsProps.Is_auto_generated = proptools.BoolPtr(true)
- partitionSpecificFsProps(fsProps, partitionType)
+ partitionSpecificFsProps(fsProps, partitionVars, partitionType)
// system_image properties that are not set:
// - filesystemProperties.Avb_hash_algorithm
@@ -699,6 +743,22 @@
diffTestFiles = append(diffTestFiles, diffTestFile)
ctx.Phony("soong_generated_boot_filesystem_test", diffTestFile)
}
+ if f.properties.Vendor_boot_image != "" {
+ diffTestFile := android.PathForModuleOut(ctx, "vendor_boot_diff_test.txt")
+ soongBootImg := android.PathForModuleSrc(ctx, f.properties.Vendor_boot_image)
+ makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/vendor_boot.img", ctx.Config().DeviceName()))
+ createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
+ diffTestFiles = append(diffTestFiles, diffTestFile)
+ ctx.Phony("soong_generated_vendor_boot_filesystem_test", diffTestFile)
+ }
+ if f.properties.Init_boot_image != "" {
+ diffTestFile := android.PathForModuleOut(ctx, "init_boot_diff_test.txt")
+ soongBootImg := android.PathForModuleSrc(ctx, f.properties.Init_boot_image)
+ makeBootImage := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/init_boot.img", ctx.Config().DeviceName()))
+ createDiffTest(ctx, diffTestFile, soongBootImg, makeBootImage)
+ diffTestFiles = append(diffTestFiles, diffTestFile)
+ ctx.Phony("soong_generated_init_boot_filesystem_test", diffTestFile)
+ }
ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
}
diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go
index 0d18660..12f9956 100644
--- a/fsgen/fsgen_mutators.go
+++ b/fsgen/fsgen_mutators.go
@@ -146,7 +146,8 @@
"fs_config_files_odm_dlkm": defaultDepCandidateProps(ctx.Config()),
"odm_dlkm-build.prop": defaultDepCandidateProps(ctx.Config()),
},
- "ramdisk": {},
+ "ramdisk": {},
+ "vendor_ramdisk": {},
},
fsDepsMutex: sync.Mutex{},
moduleToInstallationProps: map[string]installationProperties{},
diff --git a/java/app.go b/java/app.go
index 8bb73cb..7f80160 100644
--- a/java/app.go
+++ b/java/app.go
@@ -164,7 +164,7 @@
type overridableAppProperties struct {
// The name of a certificate in the default certificate directory, blank to use the default product certificate,
// or an android_app_certificate module name in the form ":module".
- Certificate *string
+ Certificate proptools.Configurable[string] `android:"replace_instead_of_append"`
// Name of the signing certificate lineage file or filegroup module.
Lineage *string `android:"path"`
@@ -1252,7 +1252,7 @@
if overridden {
return ":" + certificate
}
- return String(a.overridableAppProperties.Certificate)
+ return a.overridableAppProperties.Certificate.GetOrDefault(ctx, "")
}
func (a *AndroidApp) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
diff --git a/java/app_import.go b/java/app_import.go
index f044c68..8951c7d 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -97,7 +97,7 @@
// The name of a certificate in the default certificate directory or an android_app_certificate
// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
- Certificate *string
+ Certificate proptools.Configurable[string] `android:"replace_instead_of_append"`
// Names of extra android_app_certificate modules to sign the apk with in the form ":module".
Additional_certificates []string
@@ -240,7 +240,7 @@
}
func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
- cert := android.SrcIsModule(String(a.properties.Certificate))
+ cert := android.SrcIsModule(a.properties.Certificate.GetOrDefault(ctx, ""))
if cert != "" {
ctx.AddDependency(ctx.Module(), certificateTag, cert)
}
@@ -323,7 +323,7 @@
}
numCertPropsSet := 0
- if String(a.properties.Certificate) != "" {
+ if a.properties.Certificate.GetOrDefault(ctx, "") != "" {
numCertPropsSet++
}
if Bool(a.properties.Presigned) {
@@ -406,7 +406,7 @@
// If the certificate property is empty at this point, default_dev_cert must be set to true.
// Which makes processMainCert's behavior for the empty cert string WAI.
_, _, certificates := collectAppDeps(ctx, a, false, false)
- a.certificate, certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
+ a.certificate, certificates = processMainCert(a.ModuleBase, a.properties.Certificate.GetOrDefault(ctx, ""), certificates, ctx)
signed := android.PathForModuleOut(ctx, "signed", apkFilename)
var lineageFile android.Path
if lineage := String(a.properties.Lineage); lineage != "" {
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index c778f04..375a1aa 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -83,10 +83,6 @@
return true
}
-// Contents of bootclasspath fragments in an apex are considered to be directly in the apex, as if
-// they were listed in java_libs.
-func (b bootclasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
-
// Contents of bootclasspath fragments require files from prebuilt apex files.
func (b bootclasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {}
@@ -96,7 +92,6 @@
var _ android.ExcludeFromVisibilityEnforcementTag = bootclasspathFragmentContentDepTag
var _ android.ReplaceSourceWithPrebuilt = bootclasspathFragmentContentDepTag
var _ android.SdkMemberDependencyTag = bootclasspathFragmentContentDepTag
-var _ android.CopyDirectlyInAnyApexTag = bootclasspathFragmentContentDepTag
var _ android.RequiresFilesFromPrebuiltApexTag = bootclasspathFragmentContentDepTag
func IsBootclasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool {
diff --git a/java/rro.go b/java/rro.go
index 8bb9be2..f225e1f 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -50,7 +50,7 @@
type RuntimeResourceOverlayProperties struct {
// the name of a certificate in the default certificate directory or an android_app_certificate
// module name in the form ":module".
- Certificate *string
+ Certificate proptools.Configurable[string] `android:"replace_instead_of_append"`
// Name of the signing certificate lineage file.
Lineage *string
@@ -119,7 +119,7 @@
r.aapt.deps(ctx, sdkDep)
}
- cert := android.SrcIsModule(String(r.properties.Certificate))
+ cert := android.SrcIsModule(r.properties.Certificate.GetOrDefault(ctx, ""))
if cert != "" {
ctx.AddDependency(ctx.Module(), certificateTag, cert)
}
@@ -166,7 +166,7 @@
// Sign the built package
_, _, certificates := collectAppDeps(ctx, r, false, false)
- r.certificate, certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
+ r.certificate, certificates = processMainCert(r.ModuleBase, r.properties.Certificate.GetOrDefault(ctx, ""), certificates, ctx)
signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
var lineageFile android.Path
if lineage := String(r.properties.Lineage); lineage != "" {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index f6dfcdd..7891776 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1314,12 +1314,6 @@
var _ android.InstallNeededDependencyTag = sdkLibraryComponentTag{}
-// To satisfy the CopyDirectlyInAnyApexTag interface. Implementation library of the sdk library
-// in an apex is considered to be directly in the apex, as if it was listed in java_libs.
-func (t sdkLibraryComponentTag) CopyDirectlyInAnyApex() {}
-
-var _ android.CopyDirectlyInAnyApexTag = implLibraryTag
-
func (t sdkLibraryComponentTag) InstallDepNeeded() bool {
return t.name == "xml-permissions-file" || t.name == "impl-library"
}
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index 608a616..3176ad9 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -218,16 +218,11 @@
return true
}
-// Contents of system server fragments in an apex are considered to be directly in the apex, as if
-// they were listed in java_libs.
-func (systemServerClasspathFragmentContentDependencyTag) CopyDirectlyInAnyApex() {}
-
// Contents of system server fragments require files from prebuilt apex files.
func (systemServerClasspathFragmentContentDependencyTag) RequiresFilesFromPrebuiltApex() {}
var _ android.ReplaceSourceWithPrebuilt = systemServerClasspathFragmentContentDepTag
var _ android.SdkMemberDependencyTag = systemServerClasspathFragmentContentDepTag
-var _ android.CopyDirectlyInAnyApexTag = systemServerClasspathFragmentContentDepTag
var _ android.RequiresFilesFromPrebuiltApexTag = systemServerClasspathFragmentContentDepTag
// The tag used for the dependency between the systemserverclasspath_fragment module and its contents.
diff --git a/systemfeatures/Android.bp b/systemfeatures/Android.bp
new file mode 100644
index 0000000..a65a6b6
--- /dev/null
+++ b/systemfeatures/Android.bp
@@ -0,0 +1,18 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-systemfeatures",
+ pkgPath: "android/soong/systemfeatures",
+ deps: [
+ "blueprint",
+ "blueprint-proptools",
+ "soong",
+ "soong-android",
+ "soong-java",
+ ],
+ srcs: ["system_features.go"],
+ testSrcs: ["system_features_test.go"],
+ pluginFor: ["soong_build"],
+}
diff --git a/systemfeatures/OWNERS b/systemfeatures/OWNERS
new file mode 100644
index 0000000..3e44806
--- /dev/null
+++ b/systemfeatures/OWNERS
@@ -0,0 +1,2 @@
+jdduke@google.com
+shayba@google.com
diff --git a/systemfeatures/system_features.go b/systemfeatures/system_features.go
new file mode 100644
index 0000000..0c1a566
--- /dev/null
+++ b/systemfeatures/system_features.go
@@ -0,0 +1,102 @@
+// 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 systemfeatures
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "android/soong/android"
+ "android/soong/genrule"
+)
+
+var (
+ pctx = android.NewPackageContext("android/soong/systemfeatures")
+)
+
+func init() {
+ registerSystemFeaturesComponents(android.InitRegistrationContext)
+}
+
+func registerSystemFeaturesComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("java_system_features_srcs", JavaSystemFeaturesSrcsFactory)
+}
+
+type javaSystemFeaturesSrcs struct {
+ android.ModuleBase
+ properties struct {
+ // The fully qualified class name for the generated code, e.g., com.android.Foo
+ Full_class_name string
+ }
+ outputFiles android.WritablePaths
+}
+
+var _ genrule.SourceFileGenerator = (*javaSystemFeaturesSrcs)(nil)
+var _ android.SourceFileProducer = (*javaSystemFeaturesSrcs)(nil)
+
+func (m *javaSystemFeaturesSrcs) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ // Create a file name appropriate for the given fully qualified (w/ package) class name.
+ classNameParts := strings.Split(m.properties.Full_class_name, ".")
+ outputDir := android.PathForModuleGen(ctx)
+ outputFileName := classNameParts[len(classNameParts)-1] + ".java"
+ outputFile := android.PathForModuleGen(ctx, outputFileName).OutputPath
+
+ // Collect all RELEASE_SYSTEM_FEATURE_$K:$V build flags into a list of "$K:$V" pairs.
+ var features []string
+ for k, v := range ctx.Config().ProductVariables().BuildFlags {
+ if strings.HasPrefix(k, "RELEASE_SYSTEM_FEATURE_") {
+ shortFeatureName := strings.TrimPrefix(k, "RELEASE_SYSTEM_FEATURE_")
+ features = append(features, fmt.Sprintf("%s:%s", shortFeatureName, v))
+ }
+ }
+ // Ensure sorted outputs for consistency of flag ordering in ninja outputs.
+ sort.Strings(features)
+
+ rule := android.NewRuleBuilder(pctx, ctx)
+ rule.Command().Text("rm -rf").Text(outputDir.String())
+ rule.Command().Text("mkdir -p").Text(outputDir.String())
+ rule.Command().
+ BuiltTool("systemfeatures-gen-tool").
+ Flag(m.properties.Full_class_name).
+ FlagForEachArg("--feature=", features).
+ FlagWithArg("--readonly=", fmt.Sprint(ctx.Config().ReleaseUseSystemFeatureBuildFlags())).
+ FlagWithOutput(" > ", outputFile)
+ rule.Build(ctx.ModuleName(), "Generating systemfeatures srcs filegroup")
+
+ m.outputFiles = append(m.outputFiles, outputFile)
+}
+
+func (m *javaSystemFeaturesSrcs) Srcs() android.Paths {
+ return m.outputFiles.Paths()
+}
+
+func (m *javaSystemFeaturesSrcs) GeneratedSourceFiles() android.Paths {
+ return m.outputFiles.Paths()
+}
+
+func (m *javaSystemFeaturesSrcs) GeneratedDeps() android.Paths {
+ return m.outputFiles.Paths()
+}
+
+func (m *javaSystemFeaturesSrcs) GeneratedHeaderDirs() android.Paths {
+ return nil
+}
+
+func JavaSystemFeaturesSrcsFactory() android.Module {
+ module := &javaSystemFeaturesSrcs{}
+ module.AddProperties(&module.properties)
+ android.InitAndroidModule(module)
+ return module
+}
diff --git a/systemfeatures/system_features_test.go b/systemfeatures/system_features_test.go
new file mode 100644
index 0000000..558bb95
--- /dev/null
+++ b/systemfeatures/system_features_test.go
@@ -0,0 +1,51 @@
+// 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 systemfeatures
+
+import (
+ "android/soong/android"
+
+ "testing"
+)
+
+func TestJavaSystemFeaturesSrcs(t *testing.T) {
+ bp := `
+java_system_features_srcs {
+ name: "system-features-srcs",
+ full_class_name: "com.android.test.RoSystemFeatures",
+}
+`
+
+ res := android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(registerSystemFeaturesComponents),
+ android.PrepareForTestWithBuildFlag("RELEASE_USE_SYSTEM_FEATURE_BUILD_FLAGS", "true"),
+ android.PrepareForTestWithBuildFlag("RELEASE_SYSTEM_FEATURE_AUTOMOTIVE", "0"),
+ android.PrepareForTestWithBuildFlag("RELEASE_SYSTEM_FEATURE_TELEVISION", "UNAVAILABLE"),
+ android.PrepareForTestWithBuildFlag("RELEASE_SYSTEM_FEATURE_WATCH", ""),
+ android.PrepareForTestWithBuildFlag("RELEASE_NOT_SYSTEM_FEATURE_FOO", "BAR"),
+ ).RunTestWithBp(t, bp)
+
+ module := res.ModuleForTests("system-features-srcs", "")
+ cmd := module.Rule("system-features-srcs").RuleParams.Command
+ android.AssertStringDoesContain(t, "Expected fully class name", cmd, " com.android.test.RoSystemFeatures ")
+ android.AssertStringDoesContain(t, "Expected readonly flag", cmd, "--readonly=true")
+ android.AssertStringDoesContain(t, "Expected AUTOMOTIVE feature flag", cmd, "--feature=AUTOMOTIVE:0 ")
+ android.AssertStringDoesContain(t, "Expected TELEVISION feature flag", cmd, "--feature=TELEVISION:UNAVAILABLE ")
+ android.AssertStringDoesContain(t, "Expected WATCH feature flag", cmd, "--feature=WATCH: ")
+ android.AssertStringDoesNotContain(t, "Unexpected FOO arg from non-system feature flag", cmd, "FOO")
+
+ systemFeaturesModule := module.Module().(*javaSystemFeaturesSrcs)
+ expectedOutputPath := "out/soong/.intermediates/system-features-srcs/gen/RoSystemFeatures.java"
+ android.AssertPathsRelativeToTopEquals(t, "Expected output file", []string{expectedOutputPath}, systemFeaturesModule.Srcs())
+}