Merge "droidstubs: clarify how to address API lints" into main
diff --git a/Android.bp b/Android.bp
index cbe1c7b..42b7d83 100644
--- a/Android.bp
+++ b/Android.bp
@@ -206,3 +206,30 @@
relative_install_path: "etc", // odm/etc/build.prop
visibility: ["//visibility:private"],
}
+
+build_prop {
+ name: "system_dlkm-build.prop",
+ stem: "build.prop",
+ system_dlkm_specific: true,
+ product_config: ":product_config",
+ relative_install_path: "etc", // system_dlkm/etc/build.prop
+ visibility: ["//visibility:private"],
+}
+
+build_prop {
+ name: "vendor_dlkm-build.prop",
+ stem: "build.prop",
+ vendor_dlkm_specific: true,
+ product_config: ":product_config",
+ relative_install_path: "etc", // vendor_dlkm/etc/build.prop
+ visibility: ["//visibility:private"],
+}
+
+build_prop {
+ name: "odm_dlkm-build.prop",
+ stem: "build.prop",
+ odm_dlkm_specific: true,
+ product_config: ":product_config",
+ relative_install_path: "etc", // odm_dlkm/etc/build.prop
+ visibility: ["//visibility:private"],
+}
diff --git a/android/Android.bp b/android/Android.bp
index cf707bd..a9a3564 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -31,6 +31,7 @@
srcs: [
"aconfig_providers.go",
"all_teams.go",
+ "android_info.go",
"androidmk.go",
"apex.go",
"apex_contributions.go",
@@ -121,6 +122,7 @@
"apex_test.go",
"arch_test.go",
"blueprint_e2e_test.go",
+ "build_prop_test.go",
"config_test.go",
"configured_jars_test.go",
"csuite_config_test.go",
diff --git a/android/android_info.go b/android/android_info.go
new file mode 100644
index 0000000..a8d3d4e
--- /dev/null
+++ b/android/android_info.go
@@ -0,0 +1,91 @@
+// Copyright 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
+)
+
+var (
+ mergeAndRemoveComments = pctx.AndroidStaticRule("merge_and_remove_comments",
+ blueprint.RuleParams{
+ Command: "cat $in | grep -v '#' > $out",
+ },
+ )
+ androidInfoTxtToProp = pctx.AndroidStaticRule("android_info_txt_to_prop",
+ blueprint.RuleParams{
+ Command: "grep 'require version-' $in | sed -e 's/require version-/ro.build.expect./g' > $out",
+ },
+ )
+)
+
+type androidInfoProperties struct {
+ // Name of output file. Defaults to module name
+ Stem *string
+
+ // Paths of board-info.txt files.
+ Board_info_files []string `android:"path"`
+
+ // Name of bootloader board. If board_info_files is empty, `board={bootloader_board_name}` will
+ // be printed to output. Ignored if board_info_files is not empty.
+ Bootloader_board_name *string
+}
+
+type androidInfoModule struct {
+ ModuleBase
+
+ properties androidInfoProperties
+}
+
+func (p *androidInfoModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+ if len(p.properties.Board_info_files) > 0 && p.properties.Bootloader_board_name != nil {
+ ctx.ModuleErrorf("Either Board_info_files or Bootloader_board_name should be set. Please remove one of them\n")
+ return
+ }
+ androidInfoTxtName := proptools.StringDefault(p.properties.Stem, ctx.ModuleName()+".txt")
+ androidInfoTxt := PathForModuleOut(ctx, androidInfoTxtName)
+ androidInfoProp := androidInfoTxt.ReplaceExtension(ctx, "prop")
+
+ if boardInfoFiles := PathsForModuleSrc(ctx, p.properties.Board_info_files); len(boardInfoFiles) > 0 {
+ ctx.Build(pctx, BuildParams{
+ Rule: mergeAndRemoveComments,
+ Inputs: boardInfoFiles,
+ Output: androidInfoTxt,
+ })
+ } else if bootloaderBoardName := proptools.String(p.properties.Bootloader_board_name); bootloaderBoardName != "" {
+ WriteFileRule(ctx, androidInfoTxt, "board="+bootloaderBoardName)
+ } else {
+ WriteFileRule(ctx, androidInfoTxt, "")
+ }
+
+ // Create android_info.prop
+ ctx.Build(pctx, BuildParams{
+ Rule: androidInfoTxtToProp,
+ Input: androidInfoTxt,
+ Output: androidInfoProp,
+ })
+
+ ctx.SetOutputFiles(Paths{androidInfoProp}, "")
+}
+
+// android_info module generate a file named android-info.txt that contains various information
+// about the device we're building for. This file is typically packaged up with everything else.
+func AndroidInfoFactory() Module {
+ module := &androidInfoModule{}
+ module.AddProperties(&module.properties)
+ InitAndroidModule(module)
+ return module
+}
diff --git a/android/apex.go b/android/apex.go
index 79ab13c..3486350 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -160,14 +160,6 @@
reflect.DeepEqual(i.InApexModules, otherApexInfo.InApexModules)
}
-// ApexTestForInfo stores the contents of APEXes for which this module is a test - although this
-// module is not part of the APEX - and thus has access to APEX internals.
-type ApexTestForInfo struct {
- ApexContents []*ApexContents
-}
-
-var ApexTestForInfoProvider = blueprint.NewMutatorProvider[ApexTestForInfo]("apex_test_for")
-
// ApexBundleInfo contains information about the dependencies of an apex
type ApexBundleInfo struct {
Contents *ApexContents
@@ -269,12 +261,6 @@
// check-platform-availability mutator in the apex package.
SetNotAvailableForPlatform()
- // Returns the list of APEXes that this module is a test for. The module has access to the
- // private part of the listed APEXes even when it is not included in the APEXes. This by
- // default returns nil. A module type should override the default implementation. For
- // example, cc_test module type returns the value of test_for here.
- TestFor() []string
-
// Returns nil (success) if this module should support the given sdk version. Returns an
// error if not. No default implementation is provided for this method. A module type
// implementing this interface should provide an implementation. A module supports an sdk
@@ -457,13 +443,6 @@
return false
}
-// Implements ApexModule
-func (m *ApexModuleBase) TestFor() []string {
- // If needed, this will be overridden by concrete types inheriting
- // ApexModuleBase
- return nil
-}
-
// Returns the test apexes that this module is included in.
func (m *ApexModuleBase) TestApexes() []string {
return m.ApexProperties.TestApexes
@@ -833,7 +812,7 @@
// If this is the FinalModule (last visited module) copy
// AnyVariantDirectlyInAnyApex to all the other variants
- if am == mctx.FinalModule().(ApexModule) {
+ if mctx.IsFinalModule(am) {
mctx.VisitAllModuleVariants(func(variant Module) {
variant.(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex =
base.ApexProperties.AnyVariantDirectlyInAnyApex
@@ -1062,12 +1041,6 @@
return apiLevel
}
-// Implemented by apexBundle.
-type ApexTestInterface interface {
- // Return true if the apex bundle is an apex_test
- IsTestApex() bool
-}
-
var ApexExportsInfoProvider = blueprint.NewProvider[ApexExportsInfo]()
// ApexExportsInfo contains information about the artifacts provided by apexes to dexpreopt and hiddenapi
diff --git a/android/base_module_context.go b/android/base_module_context.go
index 223b534..060fae5 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -17,6 +17,7 @@
import (
"fmt"
"regexp"
+ "slices"
"strings"
"github.com/google/blueprint"
@@ -197,9 +198,15 @@
// singleton actions that are only done once for all variants of a module.
FinalModule() Module
+ // IsFinalModule returns if the current module is the last variant. Variants of a module are always visited in
+ // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all
+ // variants using VisitAllModuleVariants if the current module is the last one. This can be used to perform
+ // singleton actions that are only done once for all variants of a module.
+ IsFinalModule(module Module) bool
+
// VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always
// visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read
- // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any
+ // from all variants if the current module is the last one. Otherwise, care must be taken to not access any
// data modified by the current mutator.
VisitAllModuleVariants(visit func(Module))
@@ -570,7 +577,7 @@
}
func (b *baseModuleContext) GetWalkPath() []Module {
- return b.walkPath
+ return slices.Clone(b.walkPath)
}
func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag {
@@ -591,6 +598,10 @@
return b.bp.FinalModule().(Module)
}
+func (b *baseModuleContext) IsFinalModule(module Module) bool {
+ return b.bp.IsFinalModule(module)
+}
+
// IsMetaDependencyTag returns true for cross-cutting metadata dependencies.
func IsMetaDependencyTag(tag blueprint.DependencyTag) bool {
if tag == licenseKindTag {
diff --git a/android/build_prop.go b/android/build_prop.go
index 7c3c506..8389470 100644
--- a/android/build_prop.go
+++ b/android/build_prop.go
@@ -19,8 +19,12 @@
)
func init() {
- ctx := InitRegistrationContext
+ registerBuildPropComponents(InitRegistrationContext)
+}
+
+func registerBuildPropComponents(ctx RegistrationContext) {
ctx.RegisterModuleType("build_prop", BuildPropFactory)
+ ctx.RegisterModuleType("android_info", AndroidInfoFactory)
}
type buildPropProperties struct {
@@ -38,6 +42,10 @@
// Path to a JSON file containing product configs.
Product_config *string `android:"path"`
+ // Path to android-info.txt file containing board specific info.
+ // This is empty for build.prop of all partitions except vendor.
+ Android_info *string `android:"path"`
+
// Optional subdirectory under which this file is installed into
Relative_install_path *string
}
@@ -66,7 +74,10 @@
} else if partition == "odm" {
return ctx.Config().OdmPropFiles(ctx)
} else if partition == "vendor" {
- // TODO (b/375500423): Add android-info.txt to prop files
+ if p.properties.Android_info != nil {
+ androidInfo := PathForModuleSrc(ctx, proptools.String(p.properties.Android_info))
+ return append(ctx.Config().VendorPropFiles(ctx), androidInfo)
+ }
return ctx.Config().VendorPropFiles(ctx)
}
return nil
@@ -98,6 +109,12 @@
return "product"
} else if p.SystemExtSpecific() {
return "system_ext"
+ } else if p.InstallInSystemDlkm() {
+ return "system_dlkm"
+ } else if p.InstallInVendorDlkm() {
+ return "vendor_dlkm"
+ } else if p.InstallInOdmDlkm() {
+ return "odm_dlkm"
}
return "system"
}
@@ -108,16 +125,18 @@
"product",
"odm",
"vendor",
+ "system_dlkm",
+ "vendor_dlkm",
+ "odm_dlkm",
}
func (p *buildPropModule) GenerateAndroidBuildActions(ctx ModuleContext) {
- p.outputFilePath = PathForModuleOut(ctx, "build.prop").OutputPath
- if !ctx.Config().KatiEnabled() {
- WriteFileRule(ctx, p.outputFilePath, "# no build.prop if kati is disabled")
- ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
- return
+ if !p.SocSpecific() && p.properties.Android_info != nil {
+ ctx.ModuleErrorf("Android_info cannot be set if build.prop is not installed in vendor partition")
}
+ p.outputFilePath = PathForModuleOut(ctx, "build.prop").OutputPath
+
partition := p.partition(ctx.DeviceConfig())
if !InList(partition, validPartitions) {
ctx.PropertyErrorf("partition", "unsupported partition %q: only %q are supported", partition, validPartitions)
diff --git a/android/build_prop_test.go b/android/build_prop_test.go
new file mode 100644
index 0000000..e75975a
--- /dev/null
+++ b/android/build_prop_test.go
@@ -0,0 +1,41 @@
+// 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 android
+
+import (
+ "testing"
+)
+
+func TestPropFileInputs(t *testing.T) {
+ bp := `
+build_prop {
+ name: "vendor-build.prop",
+ stem: "build.prop",
+ vendor: true,
+ android_info: ":board-info",
+ //product_config: ":product_config",
+}
+android_info {
+ name: "board-info",
+ stem: "android-info.txt",
+}
+`
+
+ res := GroupFixturePreparers(
+ FixtureRegisterWithContext(registerBuildPropComponents),
+ ).RunTestWithBp(t, bp)
+ buildPropCmd := res.ModuleForTests("vendor-build.prop", "").Rule("vendor-build.prop_.vendor-build.prop").RuleParams.Command
+ AssertStringDoesContain(t, "Could not find android-info in prop files of vendor build.prop", buildPropCmd, "--prop-files=out/soong/.intermediates/board-info/android-info.prop")
+}
diff --git a/android/config.go b/android/config.go
index 16d77db..27d3b87 100644
--- a/android/config.go
+++ b/android/config.go
@@ -867,7 +867,7 @@
}
func (c *config) TargetsJava21() bool {
- return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_21")
+ return c.productVariables.GetBuildFlagBool("RELEASE_TARGET_JAVA_21")
}
// EnvDeps returns the environment variables this build depends on. The first
@@ -1522,6 +1522,13 @@
return "vendor"
}
+func (c *deviceConfig) VendorDlkmPath() string {
+ if c.config.productVariables.VendorDlkmPath != nil {
+ return *c.config.productVariables.VendorDlkmPath
+ }
+ return "vendor_dlkm"
+}
+
func (c *deviceConfig) BuildingVendorImage() bool {
return proptools.Bool(c.config.productVariables.BuildingVendorImage)
}
@@ -1553,6 +1560,13 @@
return proptools.Bool(c.config.productVariables.BuildingOdmImage)
}
+func (c *deviceConfig) OdmDlkmPath() string {
+ if c.config.productVariables.OdmDlkmPath != nil {
+ return *c.config.productVariables.OdmDlkmPath
+ }
+ return "odm_dlkm"
+}
+
func (c *deviceConfig) ProductPath() string {
if c.config.productVariables.ProductPath != nil {
return *c.config.productVariables.ProductPath
@@ -1571,6 +1585,31 @@
return "system_ext"
}
+func (c *deviceConfig) SystemDlkmPath() string {
+ if c.config.productVariables.SystemDlkmPath != nil {
+ return *c.config.productVariables.SystemDlkmPath
+ }
+ return "system_dlkm"
+}
+
+func (c *deviceConfig) OemPath() string {
+ if c.config.productVariables.OemPath != nil {
+ return *c.config.productVariables.OemPath
+ }
+ return "oem"
+}
+
+func (c *deviceConfig) UserdataPath() string {
+ if c.config.productVariables.UserdataPath != nil {
+ return *c.config.productVariables.UserdataPath
+ }
+ return "data"
+}
+
+func (c *deviceConfig) BuildingUserdataImage() bool {
+ return proptools.Bool(c.config.productVariables.BuildingUserdataImage)
+}
+
func (c *deviceConfig) BtConfigIncludeDir() string {
return String(c.config.productVariables.BtConfigIncludeDir)
}
@@ -1795,10 +1834,6 @@
return Bool(c.productVariables.CompressedApex) && !c.UnbundledBuildApps()
}
-func (c *config) ApexTrimEnabled() bool {
- return Bool(c.productVariables.TrimmedApex)
-}
-
func (c *config) UseSoongSystemImage() bool {
return Bool(c.productVariables.UseSoongSystemImage)
}
diff --git a/android/filegroup_test.go b/android/filegroup_test.go
index 14e9368..670037d 100644
--- a/android/filegroup_test.go
+++ b/android/filegroup_test.go
@@ -1,55 +1,9 @@
package android
import (
- "path/filepath"
"testing"
)
-func TestFileGroupWithPathProp(t *testing.T) {
- // TODO(b/247782695), TODO(b/242847534) Fix mixed builds for filegroups
- t.Skip("Re-enable once filegroups are corrected for mixed builds")
- outBaseDir := "outputbase"
- pathPrefix := outBaseDir + "/execroot/__main__"
- expectedOutputfile := filepath.Join(pathPrefix, "a/b/c/d/test.aidl")
-
- testCases := []struct {
- bp string
- rel string
- }{
- {
- bp: `
- filegroup {
- name: "baz",
- srcs: ["a/b/c/d/test.aidl"],
- path: "a/b",
- bazel_module: { label: "//:baz" },
- }
-`,
- rel: "c/d/test.aidl",
- },
- {
- bp: `
- filegroup {
- name: "baz",
- srcs: ["a/b/c/d/test.aidl"],
- bazel_module: { label: "//:baz" },
- }
-`,
- rel: "a/b/c/d/test.aidl",
- },
- }
-
- for _, testCase := range testCases {
- result := GroupFixturePreparers(
- PrepareForTestWithFilegroup,
- ).RunTestWithBp(t, testCase.bp)
-
- fg := result.Module("baz", "").(*fileGroup)
- AssertStringEquals(t, "src relativeRoot", testCase.rel, fg.srcs[0].Rel())
- AssertStringEquals(t, "src full path", expectedOutputfile, fg.srcs[0].String())
- }
-}
-
func TestFilegroupDefaults(t *testing.T) {
bp := FixtureAddTextFile("p/Android.bp", `
filegroup_defaults {
diff --git a/android/init.go b/android/init.go
index 1ace344..d3a13d0 100644
--- a/android/init.go
+++ b/android/init.go
@@ -20,6 +20,7 @@
gob.Register(extraFilesZip{})
gob.Register(InstallPath{})
gob.Register(ModuleGenPath{})
+ gob.Register(ModuleObjPath{})
gob.Register(ModuleOutPath{})
gob.Register(OutputPath{})
gob.Register(PhonyPath{})
diff --git a/android/module.go b/android/module.go
index a918d6e..3b30c11 100644
--- a/android/module.go
+++ b/android/module.go
@@ -81,6 +81,9 @@
InstallInProduct() bool
InstallInVendor() bool
InstallInSystemExt() bool
+ InstallInSystemDlkm() bool
+ InstallInVendorDlkm() bool
+ InstallInOdmDlkm() bool
InstallForceOS() (*OsType, *ArchType)
PartitionTag(DeviceConfig) string
HideFromMake()
@@ -386,6 +389,15 @@
// Whether this module is installed to debug ramdisk
Debug_ramdisk *bool
+ // Install to partition system_dlkm when set to true.
+ System_dlkm_specific *bool
+
+ // Install to partition vendor_dlkm when set to true.
+ Vendor_dlkm_specific *bool
+
+ // Install to partition odm_dlkm when set to true.
+ Odm_dlkm_specific *bool
+
// Whether this module is built for non-native architectures (also known as native bridge binary)
Native_bridge_supported *bool `android:"arch_variant"`
@@ -1370,6 +1382,8 @@
if config.SystemExtPath() == "system_ext" {
partition = "system_ext"
}
+ } else if m.InstallInRamdisk() {
+ partition = "ramdisk"
}
return partition
}
@@ -1535,6 +1549,18 @@
return false
}
+func (m *ModuleBase) InstallInSystemDlkm() bool {
+ return Bool(m.commonProperties.System_dlkm_specific)
+}
+
+func (m *ModuleBase) InstallInVendorDlkm() bool {
+ return Bool(m.commonProperties.Vendor_dlkm_specific)
+}
+
+func (m *ModuleBase) InstallInOdmDlkm() bool {
+ return Bool(m.commonProperties.Odm_dlkm_specific)
+}
+
func (m *ModuleBase) InstallForceOS() (*OsType, *ArchType) {
return nil, nil
}
@@ -1822,6 +1848,8 @@
Enabled bool
// Whether the module has been replaced by a prebuilt
ReplacedByPrebuilt bool
+ // The Target of artifacts that this module variant is responsible for creating.
+ CompileTarget Target
}
var CommonPropertiesProviderKey = blueprint.NewProvider[CommonPropertiesProviderData]()
@@ -2010,7 +2038,7 @@
ctx.GetMissingDependencies()
}
- if m == ctx.FinalModule().(Module).base() {
+ if ctx.IsFinalModule(m.module) {
m.generateModuleTarget(ctx)
if ctx.Failed() {
return
@@ -2086,6 +2114,7 @@
commonData := CommonPropertiesProviderData{
ReplacedByPrebuilt: m.commonProperties.ReplacedByPrebuilt,
+ CompileTarget: m.commonProperties.CompileTarget,
}
if m.commonProperties.ForcedDisabled {
commonData.Enabled = false
diff --git a/android/module_context.go b/android/module_context.go
index 1f5e706..41cb0cc 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -196,6 +196,9 @@
InstallInOdm() bool
InstallInProduct() bool
InstallInVendor() bool
+ InstallInSystemDlkm() bool
+ InstallInVendorDlkm() bool
+ InstallInOdmDlkm() bool
InstallForceOS() (*OsType, *ArchType)
RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string
@@ -493,6 +496,18 @@
return m.module.InstallInVendor()
}
+func (m *moduleContext) InstallInSystemDlkm() bool {
+ return m.module.InstallInSystemDlkm()
+}
+
+func (m *moduleContext) InstallInVendorDlkm() bool {
+ return m.module.InstallInVendorDlkm()
+}
+
+func (m *moduleContext) InstallInOdmDlkm() bool {
+ return m.module.InstallInOdmDlkm()
+}
+
func (m *moduleContext) skipInstall() bool {
if m.module.base().commonProperties.SkipInstall {
return true
diff --git a/android/module_proxy.go b/android/module_proxy.go
index a60a5a8..1f96799 100644
--- a/android/module_proxy.go
+++ b/android/module_proxy.go
@@ -106,6 +106,18 @@
panic("method is not implemented on ModuleProxy")
}
+func (m ModuleProxy) InstallInSystemDlkm() bool {
+ panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInVendorDlkm() bool {
+ panic("method is not implemented on ModuleProxy")
+}
+
+func (m ModuleProxy) InstallInOdmDlkm() bool {
+ panic("method is not implemented on ModuleProxy")
+}
+
func (m ModuleProxy) InstallForceOS() (*OsType, *ArchType) {
panic("method is not implemented on ModuleProxy")
}
diff --git a/android/mutator.go b/android/mutator.go
index 4ddc606..fdd16a8 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -70,7 +70,7 @@
TopDown(name string, m TopDownMutator) MutatorHandle
BottomUp(name string, m BottomUpMutator) MutatorHandle
BottomUpBlueprint(name string, m blueprint.BottomUpMutator) MutatorHandle
- Transition(name string, m TransitionMutator)
+ Transition(name string, m TransitionMutator) TransitionMutatorHandle
}
type RegisterMutatorFunc func(RegisterMutatorsContext)
@@ -579,7 +579,7 @@
}
}
-func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) {
+func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) TransitionMutatorHandle {
atm := &androidTransitionMutator{
finalPhase: x.finalPhase,
mutator: m,
@@ -587,8 +587,10 @@
}
mutator := &mutator{
name: name,
- transitionMutator: atm}
+ transitionMutator: atm,
+ }
x.mutators = append(x.mutators, mutator)
+ return mutator
}
func (x *registerMutatorsContext) mutatorName(name string) string {
@@ -625,7 +627,10 @@
} else if mutator.topDownMutator != nil {
handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator)
} else if mutator.transitionMutator != nil {
- blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
+ handle := blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator)
+ if mutator.neverFar {
+ handle.NeverFar()
+ }
}
// Forward booleans set on the MutatorHandle to the blueprint.MutatorHandle.
@@ -681,6 +686,14 @@
MutatesGlobalState() MutatorHandle
}
+type TransitionMutatorHandle interface {
+ // NeverFar causes the variations created by this mutator to never be ignored when adding
+ // far variation dependencies. Normally, far variation dependencies ignore all the variants
+ // of the source module, and only use the variants explicitly requested by the
+ // AddFarVariationDependencies call.
+ NeverFar() MutatorHandle
+}
+
func (mutator *mutator) Parallel() MutatorHandle {
return mutator
}
@@ -715,6 +728,11 @@
return mutator
}
+func (mutator *mutator) NeverFar() MutatorHandle {
+ mutator.neverFar = true
+ return mutator
+}
+
func RegisterComponentsMutator(ctx RegisterMutatorsContext) {
ctx.BottomUp("component-deps", componentDepsMutator)
}
diff --git a/android/neverallow.go b/android/neverallow.go
index 7fb22bf..326150b 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -334,6 +334,11 @@
"prebuilt_res",
"prebuilt_wlc_upt",
"prebuilt_odm",
+ "prebuilt_vendor_dlkm",
+ "prebuilt_bt_firmware",
+ "prebuilt_tvservice",
+ "prebuilt_optee",
+ "prebuilt_tvconfig",
).
DefinedInBpFile().
Because("module type not allowed to be defined in bp file")
diff --git a/android/paths.go b/android/paths.go
index a7ee7ac..9cb872d 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -117,6 +117,9 @@
InstallInOdm() bool
InstallInProduct() bool
InstallInVendor() bool
+ InstallInSystemDlkm() bool
+ InstallInVendorDlkm() bool
+ InstallInOdmDlkm() bool
InstallForceOS() (*OsType, *ArchType)
}
@@ -170,6 +173,18 @@
return ctx.Module().InstallInVendor()
}
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInSystemDlkm() bool {
+ return ctx.Module().InstallInSystemDlkm()
+}
+
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendorDlkm() bool {
+ return ctx.Module().InstallInVendorDlkm()
+}
+
+func (ctx *baseModuleContextToModuleInstallPathContext) InstallInOdmDlkm() bool {
+ return ctx.Module().InstallInOdmDlkm()
+}
+
func (ctx *baseModuleContextToModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
return ctx.Module().InstallForceOS()
}
@@ -2077,6 +2092,10 @@
return base.Join(ctx, paths...)
}
+func PathForSuiteInstall(ctx PathContext, suite string, pathComponents ...string) InstallPath {
+ return pathForPartitionInstallDir(ctx, "test_suites", "test_suites", false).Join(ctx, suite).Join(ctx, pathComponents...)
+}
+
func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
rel := Rel(ctx, strings.TrimSuffix(path.PartitionDir(), path.partition), path.String())
return "/" + rel
@@ -2131,6 +2150,12 @@
partition = ctx.DeviceConfig().SystemExtPath()
} else if ctx.InstallInRoot() {
partition = "root"
+ } else if ctx.InstallInSystemDlkm() {
+ partition = ctx.DeviceConfig().SystemDlkmPath()
+ } else if ctx.InstallInVendorDlkm() {
+ partition = ctx.DeviceConfig().VendorDlkmPath()
+ } else if ctx.InstallInOdmDlkm() {
+ partition = ctx.DeviceConfig().OdmDlkmPath()
} else {
partition = "system"
}
@@ -2334,6 +2359,9 @@
inOdm bool
inProduct bool
inVendor bool
+ inSystemDlkm bool
+ inVendorDlkm bool
+ inOdmDlkm bool
forceOS *OsType
forceArch *ArchType
}
@@ -2388,6 +2416,18 @@
return m.inVendor
}
+func (m testModuleInstallPathContext) InstallInSystemDlkm() bool {
+ return m.inSystemDlkm
+}
+
+func (m testModuleInstallPathContext) InstallInVendorDlkm() bool {
+ return m.inVendorDlkm
+}
+
+func (m testModuleInstallPathContext) InstallInOdmDlkm() bool {
+ return m.inOdmDlkm
+}
+
func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) {
return m.forceOS, m.forceArch
}
diff --git a/android/register.go b/android/register.go
index bb1ead7..8d2f19e 100644
--- a/android/register.go
+++ b/android/register.go
@@ -98,6 +98,7 @@
usesCreateModule bool
mutatesDependencies bool
mutatesGlobalState bool
+ neverFar bool
}
var _ sortableComponent = &mutator{}
diff --git a/android/singleton.go b/android/singleton.go
index 913bf6a..0754b0c 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -64,6 +64,7 @@
VisitAllModulesBlueprint(visit func(blueprint.Module))
VisitAllModules(visit func(Module))
+ VisitAllModuleProxies(visit func(proxy ModuleProxy))
VisitAllModulesIf(pred func(Module) bool, visit func(Module))
VisitDirectDeps(module Module, visit func(Module))
@@ -77,8 +78,10 @@
VisitAllModuleVariants(module Module, visit func(Module))
+ VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy))
+
PrimaryModule(module Module) Module
- FinalModule(module Module) Module
+ IsFinalModule(module Module) bool
AddNinjaFileDeps(deps ...string)
@@ -193,7 +196,7 @@
}
// visitAdaptor wraps a visit function that takes an android.Module parameter into
-// a function that takes an blueprint.Module parameter and only calls the visit function if the
+// a function that takes a blueprint.Module parameter and only calls the visit function if the
// blueprint.Module is an android.Module.
func visitAdaptor(visit func(Module)) func(blueprint.Module) {
return func(module blueprint.Module) {
@@ -203,6 +206,16 @@
}
}
+// visitProxyAdaptor wraps a visit function that takes an android.ModuleProxy parameter into
+// a function that takes a blueprint.ModuleProxy parameter.
+func visitProxyAdaptor(visit func(proxy ModuleProxy)) func(proxy blueprint.ModuleProxy) {
+ return func(module blueprint.ModuleProxy) {
+ visit(ModuleProxy{
+ module: module,
+ })
+ }
+}
+
// predAdaptor wraps a pred function that takes an android.Module parameter
// into a function that takes an blueprint.Module parameter and only calls the visit function if the
// blueprint.Module is an android.Module, otherwise returns false.
@@ -224,6 +237,10 @@
s.SingletonContext.VisitAllModules(visitAdaptor(visit))
}
+func (s *singletonContextAdaptor) VisitAllModuleProxies(visit func(proxy ModuleProxy)) {
+ s.SingletonContext.VisitAllModuleProxies(visitProxyAdaptor(visit))
+}
+
func (s *singletonContextAdaptor) VisitAllModulesIf(pred func(Module) bool, visit func(Module)) {
s.SingletonContext.VisitAllModulesIf(predAdaptor(pred), visitAdaptor(visit))
}
@@ -248,12 +265,16 @@
s.SingletonContext.VisitAllModuleVariants(module, visitAdaptor(visit))
}
+func (s *singletonContextAdaptor) VisitAllModuleVariantProxies(module Module, visit func(proxy ModuleProxy)) {
+ s.SingletonContext.VisitAllModuleVariantProxies(module, visitProxyAdaptor(visit))
+}
+
func (s *singletonContextAdaptor) PrimaryModule(module Module) Module {
return s.SingletonContext.PrimaryModule(module).(Module)
}
-func (s *singletonContextAdaptor) FinalModule(module Module) Module {
- return s.SingletonContext.FinalModule(module).(Module)
+func (s *singletonContextAdaptor) IsFinalModule(module Module) bool {
+ return s.SingletonContext.IsFinalModule(module)
}
func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module {
diff --git a/android/testing.go b/android/testing.go
index 23aadda..f243e81 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -1132,11 +1132,6 @@
config.katiEnabled = true
}
-func SetTrimmedApexEnabledForTests(config Config) {
- config.productVariables.TrimmedApex = new(bool)
- *config.productVariables.TrimmedApex = true
-}
-
func AndroidMkEntriesForTest(t *testing.T, ctx *TestContext, mod blueprint.Module) []AndroidMkEntries {
t.Helper()
var p AndroidMkEntriesProvider
diff --git a/android/variable.go b/android/variable.go
index df9db7c..2d43c6d 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -338,13 +338,19 @@
HWASanIncludePaths []string `json:",omitempty"`
HWASanExcludePaths []string `json:",omitempty"`
- VendorPath *string `json:",omitempty"`
- BuildingVendorImage *bool `json:",omitempty"`
- OdmPath *string `json:",omitempty"`
- BuildingOdmImage *bool `json:",omitempty"`
- ProductPath *string `json:",omitempty"`
- BuildingProductImage *bool `json:",omitempty"`
- SystemExtPath *string `json:",omitempty"`
+ VendorPath *string `json:",omitempty"`
+ VendorDlkmPath *string `json:",omitempty"`
+ BuildingVendorImage *bool `json:",omitempty"`
+ OdmPath *string `json:",omitempty"`
+ BuildingOdmImage *bool `json:",omitempty"`
+ OdmDlkmPath *string `json:",omitempty"`
+ ProductPath *string `json:",omitempty"`
+ BuildingProductImage *bool `json:",omitempty"`
+ SystemExtPath *string `json:",omitempty"`
+ SystemDlkmPath *string `json:",omitempty"`
+ OemPath *string `json:",omitempty"`
+ UserdataPath *string `json:",omitempty"`
+ BuildingUserdataImage *bool `json:",omitempty"`
ClangTidy *bool `json:",omitempty"`
TidyChecks *string `json:",omitempty"`
@@ -402,7 +408,6 @@
Ndk_abis *bool `json:",omitempty"`
- TrimmedApex *bool `json:",omitempty"`
ForceApexSymlinkOptimization *bool `json:",omitempty"`
CompressedApex *bool `json:",omitempty"`
Aml_abis *bool `json:",omitempty"`
@@ -573,6 +578,14 @@
BoardAvbRollbackIndexLocation string `json:",omitempty"`
}
+type ChainedAvbPartitionProps struct {
+ Partitions []string `json:",omitempty"`
+ Key string `json:",omitempty"`
+ Algorithm string `json:",omitempty"`
+ RollbackIndex string `json:",omitempty"`
+ RollbackIndexLocation string `json:",omitempty"`
+}
+
type PartitionVariables struct {
ProductDirectory string `json:",omitempty"`
PartitionQualifiedVariables map[string]PartitionQualifiedVariablesType
@@ -593,21 +606,47 @@
BoardExt4ShareDupBlocks string `json:",omitempty"`
BoardFlashLogicalBlockSize string `json:",omitempty"`
BoardFlashEraseBlockSize string `json:",omitempty"`
- BoardUsesRecoveryAsBoot bool `json:",omitempty"`
ProductUseDynamicPartitionSize bool `json:",omitempty"`
CopyImagesForTargetFilesZip bool `json:",omitempty"`
- BoardAvbEnable bool `json:",omitempty"`
+ // Boot image stuff
+ ProductBuildBootImage bool `json:",omitempty"`
+ ProductBuildInitBootImage bool `json:",omitempty"`
+ BoardUsesRecoveryAsBoot bool `json:",omitempty"`
+ BoardPrebuiltBootimage string `json:",omitempty"`
+ BoardPrebuiltInitBootimage string `json:",omitempty"`
+ BoardBootimagePartitionSize string `json:",omitempty"`
+ BoardInitBootimagePartitionSize string `json:",omitempty"`
+ BoardBootHeaderVersion string `json:",omitempty"`
+
+ // Avb (android verified boot) stuff
+ BoardAvbEnable bool `json:",omitempty"`
+ BoardAvbAlgorithm string `json:",omitempty"`
+ BoardAvbKeyPath string `json:",omitempty"`
+ BoardAvbRollbackIndex string `json:",omitempty"`
+ BuildingVbmetaImage bool `json:",omitempty"`
+ ChainedVbmetaPartitions map[string]ChainedAvbPartitionProps `json:",omitempty"`
ProductPackages []string `json:",omitempty"`
ProductPackagesDebug []string `json:",omitempty"`
VendorLinkerConfigSrcs []string `json:",omitempty"`
ProductLinkerConfigSrcs []string `json:",omitempty"`
- ProductCopyFiles map[string]string `json:",omitempty"`
+ BoardInfoFiles []string `json:",omitempty"`
+ BootLoaderBoardName string `json:",omitempty"`
- BuildingSystemDlkmImage bool `json:",omitempty"`
- SystemKernelModules []string `json:",omitempty"`
+ ProductCopyFiles []string `json:",omitempty"`
+
+ BuildingSystemDlkmImage bool `json:",omitempty"`
+ SystemKernelModules []string `json:",omitempty"`
+ SystemKernelBlocklistFile string `json:",omitempty"`
+ SystemKernelLoadModules []string `json:",omitempty"`
+ BuildingVendorDlkmImage bool `json:",omitempty"`
+ VendorKernelModules []string `json:",omitempty"`
+ VendorKernelBlocklistFile string `json:",omitempty"`
+ BuildingOdmDlkmImage bool `json:",omitempty"`
+ OdmKernelModules []string `json:",omitempty"`
+ OdmKernelBlocklistFile string `json:",omitempty"`
}
func boolPtr(v bool) *bool {
@@ -658,7 +697,6 @@
Malloc_zero_contents: boolPtr(true),
Malloc_pattern_fill_contents: boolPtr(false),
Safestack: boolPtr(false),
- TrimmedApex: boolPtr(false),
Build_from_text_stub: boolPtr(false),
BootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
diff --git a/apex/Android.bp b/apex/Android.bp
index 0e2f564..870ca7e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -7,6 +7,7 @@
pkgPath: "android/soong/apex",
deps: [
"blueprint",
+ "blueprint-bpmodify",
"soong",
"soong-aconfig",
"soong-aconfig-codegen",
diff --git a/apex/apex.go b/apex/apex.go
index 80af9c5..04b5a07 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -20,10 +20,12 @@
"fmt"
"path/filepath"
"regexp"
+ "slices"
"sort"
"strings"
"github.com/google/blueprint"
+ "github.com/google/blueprint/depset"
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -61,14 +63,11 @@
func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
ctx.TopDown("apex_info", apexInfoMutator)
ctx.BottomUp("apex_unique", apexUniqueVariationsMutator)
- ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator)
- ctx.BottomUp("apex_test_for", apexTestForMutator)
// Run mark_platform_availability before the apexMutator as the apexMutator needs to know whether
// it should create a platform variant.
ctx.BottomUp("mark_platform_availability", markPlatformAvailability)
ctx.Transition("apex", &apexTransitionMutator{})
ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).MutatesDependencies()
- ctx.BottomUp("apex_dcla_deps", apexDCLADepsMutator)
}
type apexBundleProperties struct {
@@ -106,11 +105,10 @@
Rros []string
// List of bootclasspath fragments that are embedded inside this APEX bundle.
- Bootclasspath_fragments []string
+ Bootclasspath_fragments proptools.Configurable[[]string]
// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
- Systemserverclasspath_fragments proptools.Configurable[[]string]
- ResolvedSystemserverclasspathFragments []string `blueprint:"mutated"`
+ Systemserverclasspath_fragments proptools.Configurable[[]string]
// List of java libraries that are embedded inside this APEX bundle.
Java_libs []string
@@ -464,6 +462,12 @@
// GenerateAndroidBuildActions.
filesInfo []apexFile
+ // List of files that were excluded by the unwanted_transitive_deps property.
+ unwantedTransitiveFilesInfo []apexFile
+
+ // List of files that were excluded due to conflicts with other variants of the same module.
+ duplicateTransitiveFilesInfo []apexFile
+
// List of other module names that should be installed when this APEX gets installed (LOCAL_REQUIRED_MODULES).
makeModulesToInstall []string
@@ -726,7 +730,6 @@
androidAppTag = &dependencyTag{name: "androidApp", payload: true}
bpfTag = &dependencyTag{name: "bpf", payload: true}
certificateTag = &dependencyTag{name: "certificate"}
- dclaTag = &dependencyTag{name: "dcla"}
executableTag = &dependencyTag{name: "executable", payload: true}
fsTag = &dependencyTag{name: "filesystem", payload: true}
bcpfTag = &dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true, memberType: java.BootclasspathFragmentSdkMemberType}
@@ -739,7 +742,6 @@
prebuiltTag = &dependencyTag{name: "prebuilt", payload: true}
rroTag = &dependencyTag{name: "rro", payload: true}
sharedLibTag = &dependencyTag{name: "sharedLib", payload: true}
- testForTag = &dependencyTag{name: "test for"}
testTag = &dependencyTag{name: "test", payload: true}
shBinaryTag = &dependencyTag{name: "shBinary", payload: true}
)
@@ -888,13 +890,11 @@
}
}
- a.properties.ResolvedSystemserverclasspathFragments = a.properties.Systemserverclasspath_fragments.GetOrDefault(ctx, nil)
-
// Common-arch dependencies come next
commonVariation := ctx.Config().AndroidCommonTarget.Variations()
ctx.AddFarVariationDependencies(commonVariation, rroTag, a.properties.Rros...)
- ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
- ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.ResolvedSystemserverclasspathFragments...)
+ ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments.GetOrDefault(ctx, nil)...)
+ ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments.GetOrDefault(ctx, nil)...)
ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
@@ -944,33 +944,6 @@
}
}
-func apexDCLADepsMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Config().ApexTrimEnabled() {
- return
- }
- if a, ok := mctx.Module().(*apexBundle); ok && a.overridableProperties.Trim_against != nil {
- commonVariation := mctx.Config().AndroidCommonTarget.Variations()
- mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(a.overridableProperties.Trim_against))
- } else if o, ok := mctx.Module().(*OverrideApex); ok {
- for _, p := range o.GetProperties() {
- properties, ok := p.(*overridableProperties)
- if !ok {
- continue
- }
- if properties.Trim_against != nil {
- commonVariation := mctx.Config().AndroidCommonTarget.Variations()
- mctx.AddFarVariationDependencies(commonVariation, dclaTag, String(properties.Trim_against))
- }
- }
- }
-}
-
-type DCLAInfo struct {
- ProvidedLibs []string
-}
-
-var DCLAInfoProvider = blueprint.NewMutatorProvider[DCLAInfo]("apex_info")
-
var _ ApexInfoMutator = (*apexBundle)(nil)
func (a *apexBundle) ApexVariationName() string {
@@ -1079,12 +1052,6 @@
child.(android.ApexModule).BuildForApex(apexInfo) // leave a mark!
return true
})
-
- if a.dynamic_common_lib_apex() {
- android.SetProvider(mctx, DCLAInfoProvider, DCLAInfo{
- ProvidedLibs: a.properties.Native_shared_libs.GetOrDefault(mctx, nil),
- })
- }
}
type ApexInfoMutator interface {
@@ -1177,40 +1144,6 @@
}
}
-// apexTestForDepsMutator checks if this module is a test for an apex. If so, add a dependency on
-// the apex in order to retrieve its contents later.
-// TODO(jiyong): move this to android/apex.go?
-func apexTestForDepsMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled(mctx) {
- return
- }
- if am, ok := mctx.Module().(android.ApexModule); ok {
- if testFor := am.TestFor(); len(testFor) > 0 {
- mctx.AddFarVariationDependencies([]blueprint.Variation{
- {Mutator: "os", Variation: am.Target().OsVariation()},
- {"arch", "common"},
- }, testForTag, testFor...)
- }
- }
-}
-
-// TODO(jiyong): move this to android/apex.go?
-func apexTestForMutator(mctx android.BottomUpMutatorContext) {
- if !mctx.Module().Enabled(mctx) {
- return
- }
- if _, ok := mctx.Module().(android.ApexModule); ok {
- var contents []*android.ApexContents
- for _, testFor := range mctx.GetDirectDepsWithTag(testForTag) {
- abInfo, _ := android.OtherModuleProvider(mctx, testFor, android.ApexBundleInfoProvider)
- contents = append(contents, abInfo.Contents)
- }
- android.SetProvider(mctx, android.ApexTestForInfoProvider, android.ApexTestForInfo{
- ApexContents: contents,
- })
- }
-}
-
// markPlatformAvailability marks whether or not a module can be available to platform. A module
// cannot be available to platform if 1) it is explicitly marked as not available (i.e.
// "//apex_available:platform" is absent) or 2) it depends on another module that isn't (or can't
@@ -1434,19 +1367,6 @@
return proptools.BoolDefault(a.properties.Dynamic_common_lib_apex, false)
}
-// See the list of libs to trim
-func (a *apexBundle) libs_to_trim(ctx android.ModuleContext) []string {
- dclaModules := ctx.GetDirectDepsWithTag(dclaTag)
- if len(dclaModules) > 1 {
- panic(fmt.Errorf("expected exactly at most one dcla dependency, got %d", len(dclaModules)))
- }
- if len(dclaModules) > 0 {
- DCLAInfo, _ := android.OtherModuleProvider(ctx, dclaModules[0], DCLAInfoProvider)
- return DCLAInfo.ProvidedLibs
- }
- return []string{}
-}
-
// These functions are interfacing with cc/sanitizer.go. The entire APEX (along with all of its
// members) can be sanitized, either forcibly, or by the global configuration. For some of the
// sanitizers, extra dependencies can be forcibly added as well.
@@ -1877,6 +1797,14 @@
// visitor skips these from this list of module names
unwantedTransitiveDeps []string
+
+ // unwantedTransitiveFilesInfo contains files that would have been in the apex
+ // except that they were listed in unwantedTransitiveDeps.
+ unwantedTransitiveFilesInfo []apexFile
+
+ // duplicateTransitiveFilesInfo contains files that would ahve been in the apex
+ // except that another variant of the same module was already in the apex.
+ duplicateTransitiveFilesInfo []apexFile
}
func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) {
@@ -1887,6 +1815,7 @@
// Needs additional verification for the resulting APEX to ensure that skipped artifacts don't make problems.
// For example, DT_NEEDED modules should be found within the APEX unless they are marked in `requiredNativeLibs`.
if f.transitiveDep && f.module != nil && android.InList(mctx.OtherModuleName(f.module), vctx.unwantedTransitiveDeps) {
+ vctx.unwantedTransitiveFilesInfo = append(vctx.unwantedTransitiveFilesInfo, f)
continue
}
dest := filepath.Join(f.installDir, f.builtFile.Base())
@@ -1897,6 +1826,8 @@
mctx.ModuleErrorf("apex file %v is provided by two different files %v and %v",
dest, e.builtFile, f.builtFile)
return
+ } else {
+ vctx.duplicateTransitiveFilesInfo = append(vctx.duplicateTransitiveFilesInfo, f)
}
// If a module is directly included and also transitively depended on
// consider it as directly included.
@@ -1911,6 +1842,7 @@
for _, v := range encountered {
vctx.filesInfo = append(vctx.filesInfo, v)
}
+
sort.Slice(vctx.filesInfo, func(i, j int) bool {
// Sort by destination path so as to ensure consistent ordering even if the source of the files
// changes.
@@ -2341,6 +2273,8 @@
// 3) some fields in apexBundle struct are configured
a.installDir = android.PathForModuleInstall(ctx, "apex")
a.filesInfo = vctx.filesInfo
+ a.unwantedTransitiveFilesInfo = vctx.unwantedTransitiveFilesInfo
+ a.duplicateTransitiveFilesInfo = vctx.duplicateTransitiveFilesInfo
a.setPayloadFsType(ctx)
a.setSystemLibLink(ctx)
@@ -2367,6 +2301,8 @@
a.setOutputFiles(ctx)
a.enforcePartitionTagOnApexSystemServerJar(ctx)
+
+ a.verifyNativeImplementationLibs(ctx)
}
// Set prebuiltInfoProvider. This will be used by `apex_prebuiltinfo_singleton` to print out a metadata file
@@ -2837,8 +2773,8 @@
// Collect information for opening IDE project files in java/jdeps.go.
func (a *apexBundle) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
- dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
- dpInfo.Deps = append(dpInfo.Deps, a.properties.ResolvedSystemserverclasspathFragments...)
+ dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments.GetOrDefault(ctx, nil)...)
+ dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments.GetOrDefault(ctx, nil)...)
}
func init() {
@@ -2917,6 +2853,104 @@
}
}
-func (a *apexBundle) IsTestApex() bool {
- return a.testApex
+// verifyNativeImplementationLibs compares the list of transitive implementation libraries used to link native
+// libraries in the apex against the list of implementation libraries in the apex, ensuring that none of the
+// libraries in the apex have references to private APIs from outside the apex.
+func (a *apexBundle) verifyNativeImplementationLibs(ctx android.ModuleContext) {
+ var directImplementationLibs android.Paths
+ var transitiveImplementationLibs []depset.DepSet[android.Path]
+
+ if a.properties.IsCoverageVariant {
+ return
+ }
+
+ if a.testApex {
+ return
+ }
+
+ if a.UsePlatformApis() {
+ return
+ }
+
+ checkApexTag := func(tag blueprint.DependencyTag) bool {
+ switch tag {
+ case sharedLibTag, jniLibTag, executableTag, androidAppTag:
+ return true
+ default:
+ return false
+ }
+ }
+
+ checkTransitiveTag := func(tag blueprint.DependencyTag) bool {
+ switch {
+ case cc.IsSharedDepTag(tag), java.IsJniDepTag(tag), rust.IsRlibDepTag(tag), rust.IsDylibDepTag(tag), checkApexTag(tag):
+ return true
+ default:
+ return false
+ }
+ }
+
+ var appEmbeddedJNILibs android.Paths
+ ctx.VisitDirectDeps(func(dep android.Module) {
+ tag := ctx.OtherModuleDependencyTag(dep)
+ if !checkApexTag(tag) {
+ return
+ }
+ if tag == sharedLibTag || tag == jniLibTag {
+ outputFile := android.OutputFileForModule(ctx, dep, "")
+ directImplementationLibs = append(directImplementationLibs, outputFile)
+ }
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ transitiveImplementationLibs = append(transitiveImplementationLibs, info.ImplementationDeps)
+ }
+ if info, ok := android.OtherModuleProvider(ctx, dep, java.AppInfoProvider); ok {
+ appEmbeddedJNILibs = append(appEmbeddedJNILibs, info.EmbeddedJNILibs...)
+ }
+ })
+
+ depSet := depset.New(depset.PREORDER, directImplementationLibs, transitiveImplementationLibs)
+ allImplementationLibs := depSet.ToList()
+
+ allFileInfos := slices.Concat(a.filesInfo, a.unwantedTransitiveFilesInfo, a.duplicateTransitiveFilesInfo)
+
+ for _, lib := range allImplementationLibs {
+ inApex := slices.ContainsFunc(allFileInfos, func(fi apexFile) bool {
+ return fi.builtFile == lib
+ })
+ inApkInApex := slices.Contains(appEmbeddedJNILibs, lib)
+
+ if !inApex && !inApkInApex {
+ ctx.ModuleErrorf("library in apex transitively linked against implementation library %q not in apex", lib)
+ var depPath []android.Module
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ if depPath != nil {
+ return false
+ }
+
+ tag := ctx.OtherModuleDependencyTag(child)
+
+ if parent == ctx.Module() {
+ if !checkApexTag(tag) {
+ return false
+ }
+ }
+
+ if checkTransitiveTag(tag) {
+ if android.OutputFileForModule(ctx, child, "") == lib {
+ depPath = ctx.GetWalkPath()
+ }
+ return true
+ }
+
+ return false
+ })
+ if depPath != nil {
+ ctx.ModuleErrorf("dependency path:")
+ for _, m := range depPath {
+ ctx.ModuleErrorf(" %s", ctx.OtherModuleName(m))
+ }
+ return
+ }
+ }
+ }
}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 988c1ce..17cea5e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -20,6 +20,7 @@
"path/filepath"
"reflect"
"regexp"
+ "slices"
"sort"
"strconv"
"strings"
@@ -28,6 +29,7 @@
"android/soong/aconfig/codegen"
"github.com/google/blueprint"
+ "github.com/google/blueprint/bpmodify"
"github.com/google/blueprint/proptools"
"android/soong/android"
@@ -225,6 +227,10 @@
"system/sepolicy/apex/myapex-file_contexts": nil,
})
+var prepareForTestWithOtherapex = android.FixtureMergeMockFs(android.MockFS{
+ "system/sepolicy/apex/otherapex-file_contexts": nil,
+})
+
// ensure that 'result' equals 'expected'
func ensureEquals(t *testing.T, result string, expected string) {
t.Helper()
@@ -8707,196 +8713,6 @@
}
}
-func TestTestFor(t *testing.T) {
- t.Parallel()
- ctx := testApex(t, `
- apex {
- name: "myapex",
- key: "myapex.key",
- native_shared_libs: ["mylib", "myprivlib"],
- updatable: false,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "mylib",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- stl: "none",
- stubs: {
- versions: ["1"],
- },
- apex_available: ["myapex"],
- }
-
- cc_library {
- name: "myprivlib",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- stl: "none",
- apex_available: ["myapex"],
- }
-
-
- cc_test {
- name: "mytest",
- gtest: false,
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- stl: "none",
- shared_libs: ["mylib", "myprivlib", "mytestlib"],
- test_for: ["myapex"]
- }
-
- cc_library {
- name: "mytestlib",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- shared_libs: ["mylib", "myprivlib"],
- stl: "none",
- test_for: ["myapex"],
- }
-
- cc_benchmark {
- name: "mybench",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- shared_libs: ["mylib", "myprivlib"],
- stl: "none",
- test_for: ["myapex"],
- }
- `)
-
- ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
- ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
- mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
- android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
- }
-
- // These modules are tests for the apex, therefore are linked to the
- // actual implementation of mylib instead of its stub.
- ensureLinkedLibIs("mytest", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
- ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
- ensureLinkedLibIs("mybench", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
-}
-
-func TestIndirectTestFor(t *testing.T) {
- t.Parallel()
- ctx := testApex(t, `
- apex {
- name: "myapex",
- key: "myapex.key",
- native_shared_libs: ["mylib", "myprivlib"],
- updatable: false,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "mylib",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- stl: "none",
- stubs: {
- versions: ["1"],
- },
- apex_available: ["myapex"],
- }
-
- cc_library {
- name: "myprivlib",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- stl: "none",
- shared_libs: ["mylib"],
- apex_available: ["myapex"],
- }
-
- cc_library {
- name: "mytestlib",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- shared_libs: ["myprivlib"],
- stl: "none",
- test_for: ["myapex"],
- }
- `)
-
- ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
- ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
- mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
- android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
- }
-
- // The platform variant of mytestlib links to the platform variant of the
- // internal myprivlib.
- ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/myprivlib/", "android_arm64_armv8-a_shared/myprivlib.so")
-
- // The platform variant of myprivlib links to the platform variant of mylib
- // and bypasses its stubs.
- ensureLinkedLibIs("myprivlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
-}
-
-func TestTestForForLibInOtherApex(t *testing.T) {
- t.Parallel()
- // This case is only allowed for known overlapping APEXes, i.e. the ART APEXes.
- _ = testApex(t, `
- apex {
- name: "com.android.art",
- key: "myapex.key",
- native_shared_libs: ["libnativebridge"],
- updatable: false,
- }
-
- apex {
- name: "com.android.art.debug",
- key: "myapex.key",
- native_shared_libs: ["libnativebridge", "libnativebrdige_test"],
- updatable: false,
- }
-
- apex_key {
- name: "myapex.key",
- public_key: "testkey.avbpubkey",
- private_key: "testkey.pem",
- }
-
- cc_library {
- name: "libnativebridge",
- srcs: ["libnativebridge.cpp"],
- system_shared_libs: [],
- stl: "none",
- stubs: {
- versions: ["1"],
- },
- apex_available: ["com.android.art", "com.android.art.debug"],
- }
-
- cc_library {
- name: "libnativebrdige_test",
- srcs: ["mylib.cpp"],
- system_shared_libs: [],
- shared_libs: ["libnativebridge"],
- stl: "none",
- apex_available: ["com.android.art.debug"],
- test_for: ["com.android.art"],
- }
- `,
- android.MockFS{
- "system/sepolicy/apex/com.android.art-file_contexts": nil,
- "system/sepolicy/apex/com.android.art.debug-file_contexts": nil,
- }.AddToFixture())
-}
-
// TODO(jungjw): Move this to proptools
func intPtr(i int) *int {
return &i
@@ -10237,61 +10053,6 @@
RunTestWithBp(t, bp)
}
-func TestTrimmedApex(t *testing.T) {
- t.Parallel()
- bp := `
- apex {
- name: "myapex",
- key: "myapex.key",
- native_shared_libs: ["libfoo","libbaz"],
- min_sdk_version: "29",
- trim_against: "mydcla",
- }
- apex {
- name: "mydcla",
- key: "myapex.key",
- native_shared_libs: ["libfoo","libbar"],
- min_sdk_version: "29",
- file_contexts: ":myapex-file_contexts",
- dynamic_common_lib_apex: true,
- }
- apex_key {
- name: "myapex.key",
- }
- cc_library {
- name: "libfoo",
- shared_libs: ["libc"],
- apex_available: ["myapex","mydcla"],
- min_sdk_version: "29",
- }
- cc_library {
- name: "libbar",
- shared_libs: ["libc"],
- apex_available: ["myapex","mydcla"],
- min_sdk_version: "29",
- }
- cc_library {
- name: "libbaz",
- shared_libs: ["libc"],
- apex_available: ["myapex","mydcla"],
- min_sdk_version: "29",
- }
- `
- ctx := testApex(t, bp)
- module := ctx.ModuleForTests("myapex", "android_common_myapex")
- apexRule := module.MaybeRule("apexRule")
- if apexRule.Rule == nil {
- t.Errorf("Expecting regular apex rule but a non regular apex rule found")
- }
-
- ctx = testApex(t, bp, android.FixtureModifyConfig(android.SetTrimmedApexEnabledForTests))
- trimmedApexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("TrimmedApexRule")
- libs_to_trim := trimmedApexRule.Args["libs_to_trim"]
- android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libfoo")
- android.AssertStringDoesContain(t, "missing lib to trim", libs_to_trim, "libbar")
- android.AssertStringDoesNotContain(t, "unexpected libs in the libs to trim", libs_to_trim, "libbaz")
-}
-
func TestCannedFsConfig(t *testing.T) {
t.Parallel()
ctx := testApex(t, `
@@ -10426,7 +10187,10 @@
deps: [
"libfoo",
],
- linker_config_src: "linker.config.json",
+ linker_config: {
+ gen_linker_config: true,
+ linker_config_srcs: ["linker.config.json"],
+ },
}
cc_library {
@@ -12114,3 +11878,398 @@
fileList := android.ContentFromFileRuleForTests(t, result, partition.Output("fileList"))
android.AssertDeepEquals(t, "filesystem with apex", "apex/myapex.apex\n", fileList)
}
+
+func TestApexVerifyNativeImplementationLibs(t *testing.T) {
+ t.Parallel()
+
+ extractDepenencyPathFromErrors := func(errs []error) []string {
+ i := slices.IndexFunc(errs, func(err error) bool {
+ return strings.Contains(err.Error(), "dependency path:")
+ })
+ if i < 0 {
+ return nil
+ }
+ var dependencyPath []string
+ for _, err := range errs[i+1:] {
+ s := err.Error()
+ lastSpace := strings.LastIndexByte(s, ' ')
+ if lastSpace >= 0 {
+ dependencyPath = append(dependencyPath, s[lastSpace+1:])
+ }
+ }
+ return dependencyPath
+ }
+
+ checkErrors := func(wantDependencyPath []string) func(t *testing.T, result *android.TestResult) {
+ return func(t *testing.T, result *android.TestResult) {
+ t.Helper()
+ if len(result.Errs) == 0 {
+ t.Fatalf("expected errors")
+ }
+ t.Log("found errors:")
+ for _, err := range result.Errs {
+ t.Log(err)
+ }
+ if g, w := result.Errs[0].Error(), "library in apex transitively linked against implementation library"; !strings.Contains(g, w) {
+ t.Fatalf("expected error %q, got %q", w, g)
+ }
+ dependencyPath := extractDepenencyPathFromErrors(result.Errs)
+ if g, w := dependencyPath, wantDependencyPath; !slices.Equal(g, w) {
+ t.Errorf("expected dependency path %q, got %q", w, g)
+ }
+ }
+ }
+
+ addToSharedLibs := func(module, lib string) func(bp *bpmodify.Blueprint) {
+ return func(bp *bpmodify.Blueprint) {
+ m := bp.ModulesByName(module)
+ props, err := m.GetOrCreateProperty(bpmodify.List, "shared_libs")
+ if err != nil {
+ panic(err)
+ }
+ props.AddStringToList(lib)
+ }
+ }
+
+ bpTemplate := `
+ apex {
+ name: "myapex",
+ key: "myapex.key",
+ native_shared_libs: ["mylib"],
+ rust_dyn_libs: ["libmyrust"],
+ binaries: ["mybin", "myrustbin"],
+ jni_libs: ["libjni"],
+ apps: ["myapp"],
+ updatable: false,
+ }
+
+ apex {
+ name: "otherapex",
+ key: "myapex.key",
+ native_shared_libs: ["libotherapex"],
+ updatable: false,
+ }
+
+ apex_key {
+ name: "myapex.key",
+ public_key: "testkey.avbpubkey",
+ private_key: "testkey.pem",
+ }
+
+ cc_library {
+ name: "mylib",
+ srcs: ["foo.cpp"],
+ apex_available: ["myapex"],
+ }
+
+ cc_binary {
+ name: "mybin",
+ srcs: ["foo.cpp"],
+ apex_available: ["myapex"],
+ }
+
+ rust_library {
+ name: "libmyrust",
+ crate_name: "myrust",
+ srcs: ["src/lib.rs"],
+ rustlibs: ["libmyrust_transitive_dylib"],
+ rlibs: ["libmyrust_transitive_rlib"],
+ apex_available: ["myapex"],
+ }
+
+ rust_library{
+ name: "libmyrust_transitive_dylib",
+ crate_name: "myrust_transitive_dylib",
+ srcs: ["src/lib.rs"],
+ apex_available: ["myapex"],
+ }
+
+ rust_library {
+ name: "libmyrust_transitive_rlib",
+ crate_name: "myrust_transitive_rlib",
+ srcs: ["src/lib.rs"],
+ apex_available: ["myapex"],
+ }
+
+ rust_binary {
+ name: "myrustbin",
+ srcs: ["src/main.rs"],
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "libbar",
+ sdk_version: "current",
+ srcs: ["bar.cpp"],
+ apex_available: ["myapex"],
+ stl: "none",
+ }
+
+ android_app {
+ name: "myapp",
+ jni_libs: ["libembeddedjni"],
+ use_embedded_native_libs: true,
+ sdk_version: "current",
+ apex_available: ["myapex"],
+ }
+
+ cc_library {
+ name: "libembeddedjni",
+ sdk_version: "current",
+ srcs: ["bar.cpp"],
+ apex_available: ["myapex"],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libjni",
+ sdk_version: "current",
+ srcs: ["bar.cpp"],
+ apex_available: ["myapex"],
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libotherapex",
+ sdk_version: "current",
+ srcs: ["otherapex.cpp"],
+ apex_available: ["otherapex"],
+ stubs: {
+ symbol_file: "libotherapex.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ stl: "none",
+ }
+
+ cc_library {
+ name: "libplatform",
+ sdk_version: "current",
+ srcs: ["libplatform.cpp"],
+ stubs: {
+ symbol_file: "libplatform.map.txt",
+ versions: ["1", "2", "3"],
+ },
+ stl: "none",
+ system_shared_libs: [],
+ }
+ `
+
+ testCases := []struct {
+ name string
+ bpModifier func(bp *bpmodify.Blueprint)
+ dependencyPath []string
+ }{
+ {
+ name: "library dependency in other apex",
+ bpModifier: addToSharedLibs("mylib", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "mylib", "libotherapex"},
+ },
+ {
+ name: "transitive library dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("mylib", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "mylib", "libbar", "libotherapex"},
+ },
+ {
+ name: "library dependency in platform",
+ bpModifier: addToSharedLibs("mylib", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "mylib", "libplatform"},
+ },
+ {
+ name: "jni library dependency in other apex",
+ bpModifier: addToSharedLibs("libjni", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "libjni", "libotherapex"},
+ },
+ {
+ name: "transitive jni library dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libjni", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libjni", "libbar", "libotherapex"},
+ },
+ {
+ name: "jni library dependency in platform",
+ bpModifier: addToSharedLibs("libjni", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "libjni", "libplatform"},
+ },
+ {
+ name: "transitive jni library dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libjni", "libbar")(bp)
+ addToSharedLibs("libbar", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libjni", "libbar", "libplatform"},
+ },
+ // TODO: embedded JNI in apps should be checked too, but Soong currently just packages the transitive
+ // JNI libraries even if they came from another apex.
+ //{
+ // name: "app jni library dependency in other apex",
+ // bpModifier: addToSharedLibs("libembeddedjni", "libotherapex#impl"),
+ // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libotherapex"},
+ //},
+ //{
+ // name: "transitive app jni library dependency in other apex",
+ // bpModifier: func(bp *bpmodify.Blueprint) {
+ // addToSharedLibs("libembeddedjni", "libbar")(bp)
+ // addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ // },
+ // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libotherapex"},
+ //},
+ //{
+ // name: "app jni library dependency in platform",
+ // bpModifier: addToSharedLibs("libembeddedjni", "libplatform#impl"),
+ // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libplatform"},
+ //},
+ //{
+ // name: "transitive app jni library dependency in platform",
+ // bpModifier: func(bp *bpmodify.Blueprint) {
+ // addToSharedLibs("libembeddedjni", "libbar")(bp)
+ // addToSharedLibs("libbar", "libplatform#impl")(bp)
+ // },
+ // dependencyPath: []string{"myapex", "myapp", "libembeddedjni", "libbar", "libplatform"},
+ //},
+ {
+ name: "binary dependency in other apex",
+ bpModifier: addToSharedLibs("mybin", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "mybin", "libotherapex"},
+ },
+ {
+ name: "transitive binary dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("mybin", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "mybin", "libbar", "libotherapex"},
+ },
+ {
+ name: "binary dependency in platform",
+ bpModifier: addToSharedLibs("mybin", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "mybin", "libplatform"},
+ },
+ {
+ name: "transitive binary dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("mybin", "libbar")(bp)
+ addToSharedLibs("libbar", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "mybin", "libbar", "libplatform"},
+ },
+
+ {
+ name: "rust library dependency in other apex",
+ bpModifier: addToSharedLibs("libmyrust", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "libmyrust", "libotherapex"},
+ },
+ {
+ name: "transitive rust library dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libbar", "libotherapex"},
+ },
+ {
+ name: "rust library dependency in platform",
+ bpModifier: addToSharedLibs("libmyrust", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "libmyrust", "libplatform"},
+ },
+ {
+ name: "transitive rust library dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust", "libbar")(bp)
+ addToSharedLibs("libbar", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libbar", "libplatform"},
+ },
+ {
+ name: "transitive rust library dylib dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust_transitive_dylib", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libotherapex"},
+ },
+ {
+ name: "transitive rust library dylib dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust_transitive_dylib", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_dylib", "libplatform"},
+ },
+ {
+ name: "transitive rust library rlib dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust_transitive_rlib", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libotherapex"},
+ },
+ {
+ name: "transitive rust library rlib dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("libmyrust_transitive_rlib", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "libmyrust", "libmyrust_transitive_rlib", "libplatform"},
+ },
+ {
+ name: "rust binary dependency in other apex",
+ bpModifier: addToSharedLibs("myrustbin", "libotherapex#impl"),
+ dependencyPath: []string{"myapex", "myrustbin", "libotherapex"},
+ },
+ {
+ name: "transitive rust binary dependency in other apex",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("myrustbin", "libbar")(bp)
+ addToSharedLibs("libbar", "libotherapex#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "myrustbin", "libbar", "libotherapex"},
+ },
+ {
+ name: "rust binary dependency in platform",
+ bpModifier: addToSharedLibs("myrustbin", "libplatform#impl"),
+ dependencyPath: []string{"myapex", "myrustbin", "libplatform"},
+ },
+ {
+ name: "transitive rust binary dependency in platform",
+ bpModifier: func(bp *bpmodify.Blueprint) {
+ addToSharedLibs("myrustbin", "libbar")(bp)
+ addToSharedLibs("libbar", "libplatform#impl")(bp)
+ },
+ dependencyPath: []string{"myapex", "myrustbin", "libbar", "libplatform"},
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ t.Parallel()
+ bp, err := bpmodify.NewBlueprint("", []byte(bpTemplate))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if testCase.bpModifier != nil {
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Fatalf("panic in bpModifier: %v", r)
+ }
+ }()
+ testCase.bpModifier(bp)
+ }()
+ }
+ android.GroupFixturePreparers(
+ android.PrepareForTestWithAndroidBuildComponents,
+ cc.PrepareForTestWithCcBuildComponents,
+ java.PrepareForTestWithDexpreopt,
+ rust.PrepareForTestWithRustDefaultModules,
+ PrepareForTestWithApexBuildComponents,
+ prepareForTestWithMyapex,
+ prepareForTestWithOtherapex,
+ android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+ variables.BuildId = proptools.StringPtr("TEST.BUILD_ID")
+ }),
+ ).ExtendWithErrorHandler(android.FixtureCustomErrorHandler(checkErrors(testCase.dependencyPath))).
+ RunTestWithBp(t, bp.String())
+ })
+ }
+}
diff --git a/apex/builder.go b/apex/builder.go
index 20b4dbe..305d509 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -43,7 +43,6 @@
pctx.Import("android/soong/java")
pctx.HostBinToolVariable("apexer", "apexer")
pctx.HostBinToolVariable("apexer_with_DCLA_preprocessing", "apexer_with_DCLA_preprocessing")
- pctx.HostBinToolVariable("apexer_with_trim_preprocessing", "apexer_with_trim_preprocessing")
// ART minimal builds (using the master-art manifest) do not have the "frameworks/base"
// projects, and hence cannot build 'aapt2'. Use the SDK prebuilt instead.
@@ -173,34 +172,6 @@
}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
"opt_flags", "manifest", "is_DCLA")
- TrimmedApexRule = pctx.StaticRule("TrimmedApexRule", blueprint.RuleParams{
- Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
- `(. ${out}.copy_commands) && ` +
- `APEXER_TOOL_PATH=${tool_path} ` +
- `${apexer_with_trim_preprocessing} ` +
- `--apexer ${apexer} ` +
- `--canned_fs_config ${canned_fs_config} ` +
- `--manifest ${manifest} ` +
- `--libs_to_trim ${libs_to_trim} ` +
- `${image_dir} ` +
- `${out} ` +
- `-- ` +
- `--include_build_info ` +
- `--force ` +
- `--payload_type image ` +
- `--key ${key} ` +
- `--file_contexts ${file_contexts} ` +
- `${opt_flags} `,
- CommandDeps: []string{"${apexer_with_trim_preprocessing}", "${apexer}", "${avbtool}", "${e2fsdroid}",
- "${merge_zips}", "${mke2fs}", "${resize2fs}", "${sefcontext_compile}", "${make_f2fs}",
- "${sload_f2fs}", "${make_erofs}", "${soong_zip}", "${zipalign}", "${aapt2}",
- "prebuilts/sdk/current/public/android.jar"},
- Rspfile: "${out}.copy_commands",
- RspfileContent: "${copy_commands}",
- Description: "APEX ${image_dir} => ${out}",
- }, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key",
- "opt_flags", "manifest", "libs_to_trim")
-
apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
blueprint.RuleParams{
Command: `${aapt2} convert --output-format proto $in -o $out`,
@@ -831,24 +802,6 @@
"opt_flags": strings.Join(optFlags, " "),
},
})
- } else if ctx.Config().ApexTrimEnabled() && len(a.libs_to_trim(ctx)) > 0 {
- ctx.Build(pctx, android.BuildParams{
- Rule: TrimmedApexRule,
- Implicits: implicitInputs,
- Output: unsignedOutputFile,
- Description: "apex",
- Args: map[string]string{
- "tool_path": outHostBinDir + ":" + prebuiltSdkToolsBinDir,
- "image_dir": imageDir.String(),
- "copy_commands": strings.Join(copyCommands, " && "),
- "manifest": a.manifestPbOut.String(),
- "file_contexts": fileContexts.String(),
- "canned_fs_config": cannedFsConfig.String(),
- "key": a.privateKeyFile.String(),
- "opt_flags": strings.Join(optFlags, " "),
- "libs_to_trim": strings.Join(a.libs_to_trim(ctx), ","),
- },
- })
} else {
ctx.Build(pctx, android.BuildParams{
Rule: apexRule,
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index f8e8899..f367174 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -45,10 +45,7 @@
prepareForTestWithPlatformBootclasspath,
prepareForTestWithArtApex,
prepareForTestWithMyapex,
- // For otherapex.
- android.FixtureMergeMockFs(android.MockFS{
- "system/sepolicy/apex/otherapex-file_contexts": nil,
- }),
+ prepareForTestWithOtherapex,
java.PrepareForTestWithJavaSdkLibraryFiles,
java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
java.FixtureConfigureApexBootJars("myapex:bar"),
diff --git a/cc/Android.bp b/cc/Android.bp
index a7b6d81..3b29ae8 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -122,3 +122,31 @@
// Used by plugins
visibility: ["//visibility:public"],
}
+
+phony {
+ name: "llndk_libs",
+ required: [
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libGLESv3",
+ "libRS",
+ "libandroid_net",
+ "libapexsupport",
+ "libbinder_ndk",
+ "libc",
+ "libcgrouprc",
+ "libclang_rt.asan",
+ "libdl",
+ "libft2",
+ "liblog",
+ "libm",
+ "libmediandk",
+ "libnativewindow",
+ "libselinux",
+ "libsync",
+ "libvendorsupport",
+ "libvndksupport",
+ "libvulkan",
+ ],
+}
diff --git a/cc/cc.go b/cc/cc.go
index 5dee32e..08a93cb9 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -19,8 +19,10 @@
// is handled in builder.go
import (
+ "errors"
"fmt"
"io"
+ "slices"
"strconv"
"strings"
@@ -43,6 +45,14 @@
var CcMakeVarsInfoProvider = blueprint.NewProvider[*CcMakeVarsInfo]()
+type CcObjectInfo struct {
+ objFiles android.Paths
+ tidyFiles android.Paths
+ kytheFiles android.Paths
+}
+
+var CcObjectInfoProvider = blueprint.NewProvider[CcObjectInfo]()
+
func init() {
RegisterCCBuildComponents(android.InitRegistrationContext)
@@ -221,6 +231,9 @@
// LLNDK headers for the ABI checker to check LLNDK implementation library.
LlndkIncludeDirs android.Paths
LlndkSystemIncludeDirs android.Paths
+
+ directImplementationDeps android.Paths
+ transitiveImplementationDeps []depset.DepSet[android.Path]
}
// LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -397,11 +410,6 @@
// variant to have a ".sdk" suffix.
SdkAndPlatformVariantVisibleToMake bool `blueprint:"mutated"`
- // List of APEXes that this module has private access to for testing purpose. The module
- // can depend on libraries that are not exported by the APEXes and use private symbols
- // from the exported libraries.
- Test_for []string `android:"arch_variant"`
-
Target struct {
Platform struct {
// List of modules required by the core variant.
@@ -648,10 +656,6 @@
installInRoot() bool
}
-type xref interface {
- XrefCcFiles() android.Paths
-}
-
type overridable interface {
overriddenModules() []string
}
@@ -900,12 +904,6 @@
staticAnalogue *StaticLibraryInfo
makeLinkType string
- // Kythe (source file indexer) paths for this compilation module
- kytheFiles android.Paths
- // Object .o file output paths for this compilation module
- objFiles android.Paths
- // Tidy .tidy file output paths for this compilation module
- tidyFiles android.Paths
// For apex variants, this is set as apex.min_sdk_version
apexSdkVersion android.ApiLevel
@@ -962,7 +960,6 @@
"IsLlndk": c.IsLlndk(),
"IsVendorPublicLibrary": c.IsVendorPublicLibrary(),
"ApexSdkVersion": c.apexSdkVersion,
- "TestFor": c.TestFor(),
"AidlSrcs": c.hasAidl,
"LexSrcs": c.hasLex,
"ProtoSrcs": c.hasProto,
@@ -1473,10 +1470,6 @@
return isBionic(name)
}
-func (c *Module) XrefCcFiles() android.Paths {
- return c.kytheFiles
-}
-
func (c *Module) isCfiAssemblySupportEnabled() bool {
return c.sanitize != nil &&
Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
@@ -1567,12 +1560,11 @@
}
if ctx.ctx.Device() {
- config := ctx.ctx.Config()
- if ctx.inVendor() {
- // If building for vendor with final API, then use the latest _stable_ API as "current".
- if config.VendorApiLevelFrozen() && (ver == "" || ver == "current") {
- ver = config.PlatformSdkVersion().String()
- }
+ // When building for vendor/product, use the latest _stable_ API as "current".
+ // This is passed to clang/aidl compilers so that compiled/generated code works
+ // with the system.
+ if (ctx.inVendor() || ctx.inProduct()) && (ver == "" || ver == "current") {
+ ver = ctx.ctx.Config().PlatformSdkVersion().String()
}
}
@@ -2048,9 +2040,6 @@
if ctx.Failed() {
return
}
- c.kytheFiles = objs.kytheFiles
- c.objFiles = objs.objFiles
- c.tidyFiles = objs.tidyFiles
}
if c.linker != nil {
@@ -2061,6 +2050,10 @@
c.outputFile = android.OptionalPathForPath(outputFile)
c.maybeUnhideFromMake()
+
+ android.SetProvider(ctx, ImplementationDepInfoProvider, &ImplementationDepInfo{
+ ImplementationDeps: depset.New(depset.PREORDER, deps.directImplementationDeps, deps.transitiveImplementationDeps),
+ })
}
android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
@@ -2115,6 +2108,17 @@
c.hasYacc = b.hasSrcExt(ctx, ".y") || b.hasSrcExt(ctx, ".yy")
}
+ ccObjectInfo := CcObjectInfo{
+ kytheFiles: objs.kytheFiles,
+ }
+ if !ctx.Config().KatiEnabled() || !android.ShouldSkipAndroidMkProcessing(ctx, c) {
+ ccObjectInfo.objFiles = objs.objFiles
+ ccObjectInfo.tidyFiles = objs.tidyFiles
+ }
+ if len(ccObjectInfo.kytheFiles)+len(ccObjectInfo.objFiles)+len(ccObjectInfo.tidyFiles) > 0 {
+ android.SetProvider(ctx, CcObjectInfoProvider, ccObjectInfo)
+ }
+
c.setOutputFiles(ctx)
if c.makeVarsInfo != nil {
@@ -2122,6 +2126,12 @@
}
}
+func setOutputFilesIfNotEmpty(ctx ModuleContext, files android.Paths, tag string) {
+ if len(files) > 0 {
+ ctx.SetOutputFiles(files, tag)
+ }
+}
+
func (c *Module) setOutputFiles(ctx ModuleContext) {
if c.outputFile.Valid() {
ctx.SetOutputFiles(android.Paths{c.outputFile.Path()}, "")
@@ -2284,6 +2294,10 @@
deps.RuntimeLibs = android.LastUniqueStrings(deps.RuntimeLibs)
deps.LlndkHeaderLibs = android.LastUniqueStrings(deps.LlndkHeaderLibs)
+ if err := checkConflictingExplicitVersions(deps.SharedLibs); err != nil {
+ ctx.PropertyErrorf("shared_libs", "%s", err.Error())
+ }
+
for _, lib := range deps.ReexportSharedLibHeaders {
if !inList(lib, deps.SharedLibs) {
ctx.PropertyErrorf("export_shared_lib_headers", "Shared library not in shared_libs: '%s'", lib)
@@ -2311,6 +2325,26 @@
return deps
}
+func checkConflictingExplicitVersions(libs []string) error {
+ withoutVersion := func(s string) string {
+ name, _ := StubsLibNameAndVersion(s)
+ return name
+ }
+ var errs []error
+ for i, lib := range libs {
+ libName := withoutVersion(lib)
+ libsToCompare := libs[i+1:]
+ j := slices.IndexFunc(libsToCompare, func(s string) bool {
+ return withoutVersion(s) == libName
+ })
+ if j >= 0 {
+ errs = append(errs, fmt.Errorf("duplicate shared libraries with different explicit versions: %q and %q",
+ lib, libsToCompare[j]))
+ }
+ }
+ return errors.Join(errs...)
+}
+
func (c *Module) beginMutator(actx android.BottomUpMutatorContext) {
ctx := &baseModuleContext{
BaseModuleContext: actx,
@@ -2372,6 +2406,9 @@
if version != "" && canBeOrLinkAgainstVersionVariants(mod) {
// Version is explicitly specified. i.e. libFoo#30
+ if version == "impl" {
+ version = ""
+ }
variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
if tag, ok := depTag.(libraryDependencyTag); ok {
tag.explicitlyVersioned = true
@@ -3070,6 +3107,13 @@
linkFile = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
depFile = sharedLibraryInfo.TableOfContents
+ if !sharedLibraryInfo.IsStubs {
+ depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+ if info, ok := android.OtherModuleProvider(ctx, dep, ImplementationDepInfoProvider); ok {
+ depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+ }
+ }
+
ptr = &depPaths.SharedLibs
switch libDepTag.Order {
case earlyLibraryDependency:
@@ -3310,10 +3354,6 @@
func ShouldUseStubForApex(ctx android.ModuleContext, dep android.Module) bool {
depName := ctx.OtherModuleName(dep)
- thisModule, ok := ctx.Module().(android.ApexModule)
- if !ok {
- panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName()))
- }
inVendorOrProduct := false
bootstrap := false
@@ -3343,34 +3383,6 @@
isNotInPlatform := dep.(android.ApexModule).NotInPlatform()
useStubs = isNotInPlatform && !bootstrap
-
- if useStubs {
- // Another exception: if this module is a test for an APEX, then
- // it is linked with the non-stub variant of a module in the APEX
- // as if this is part of the APEX.
- testFor, _ := android.ModuleProvider(ctx, android.ApexTestForInfoProvider)
- for _, apexContents := range testFor.ApexContents {
- if apexContents.DirectlyInApex(depName) {
- useStubs = false
- break
- }
- }
- }
- if useStubs {
- // Yet another exception: If this module and the dependency are
- // available to the same APEXes then skip stubs between their
- // platform variants. This complements the test_for case above,
- // which avoids the stubs on a direct APEX library dependency, by
- // avoiding stubs for indirect test dependencies as well.
- //
- // TODO(b/183882457): This doesn't work if the two libraries have
- // only partially overlapping apex_available. For that test_for
- // modules would need to be split into APEX variants and resolved
- // separately for each APEX they have access to.
- if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
- useStubs = false
- }
- }
} else {
// If building for APEX, use stubs when the parent is in any APEX that
// the child is not in.
@@ -3671,10 +3683,6 @@
}
}
-func (c *Module) TestFor() []string {
- return c.Properties.Test_for
-}
-
func (c *Module) EverInstallable() bool {
return c.installer != nil &&
// Check to see whether the module is actually ever installable.
@@ -3958,9 +3966,10 @@
func (ks *kytheExtractAllSingleton) GenerateBuildActions(ctx android.SingletonContext) {
var xrefTargets android.Paths
- ctx.VisitAllModules(func(module android.Module) {
- if ccModule, ok := module.(xref); ok {
- xrefTargets = append(xrefTargets, ccModule.XrefCcFiles()...)
+ ctx.VisitAllModuleProxies(func(module android.ModuleProxy) {
+ files := android.OtherModuleProviderOrDefault(ctx, module, CcObjectInfoProvider).kytheFiles
+ if len(files) > 0 {
+ xrefTargets = append(xrefTargets, files...)
}
})
// TODO(asmundak): Perhaps emit a rule to output a warning if there were no xrefTargets
diff --git a/cc/cc_test.go b/cc/cc_test.go
index e906706..144b90b 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1008,7 +1008,7 @@
android.AssertArrayString(t, "variants for llndk stubs", expected, actual)
params := result.ModuleForTests("libllndk", "android_vendor_arm_armv7-a-neon_shared").Description("generate stub")
- android.AssertSame(t, "use Vendor API level for default stubs", "202404", params.Args["apiLevel"])
+ android.AssertSame(t, "use Vendor API level for default stubs", "999999", params.Args["apiLevel"])
checkExportedIncludeDirs := func(module, variant string, expectedSystemDirs []string, expectedDirs ...string) {
t.Helper()
@@ -1505,111 +1505,6 @@
}
}
-func TestStubsForLibraryInMultipleApexes(t *testing.T) {
- t.Parallel()
- ctx := testCc(t, `
- cc_library_shared {
- name: "libFoo",
- srcs: ["foo.c"],
- stubs: {
- symbol_file: "foo.map.txt",
- versions: ["current"],
- },
- apex_available: ["bar", "a1"],
- }
-
- cc_library_shared {
- name: "libBar",
- srcs: ["bar.c"],
- shared_libs: ["libFoo"],
- apex_available: ["a1"],
- }
-
- cc_library_shared {
- name: "libA1",
- srcs: ["a1.c"],
- shared_libs: ["libFoo"],
- apex_available: ["a1"],
- }
-
- cc_library_shared {
- name: "libBarA1",
- srcs: ["bara1.c"],
- shared_libs: ["libFoo"],
- apex_available: ["bar", "a1"],
- }
-
- cc_library_shared {
- name: "libAnyApex",
- srcs: ["anyApex.c"],
- shared_libs: ["libFoo"],
- apex_available: ["//apex_available:anyapex"],
- }
-
- cc_library_shared {
- name: "libBaz",
- srcs: ["baz.c"],
- shared_libs: ["libFoo"],
- apex_available: ["baz"],
- }
-
- cc_library_shared {
- name: "libQux",
- srcs: ["qux.c"],
- shared_libs: ["libFoo"],
- apex_available: ["qux", "bar"],
- }`)
-
- variants := ctx.ModuleVariantsForTests("libFoo")
- expectedVariants := []string{
- "android_arm64_armv8-a_shared",
- "android_arm64_armv8-a_shared_current",
- "android_arm_armv7-a-neon_shared",
- "android_arm_armv7-a-neon_shared_current",
- }
- variantsMismatch := false
- if len(variants) != len(expectedVariants) {
- variantsMismatch = true
- } else {
- for _, v := range expectedVariants {
- if !inList(v, variants) {
- variantsMismatch = false
- }
- }
- }
- if variantsMismatch {
- t.Errorf("variants of libFoo expected:\n")
- for _, v := range expectedVariants {
- t.Errorf("%q\n", v)
- }
- t.Errorf(", but got:\n")
- for _, v := range variants {
- t.Errorf("%q\n", v)
- }
- }
-
- linkAgainstFoo := []string{"libBarA1"}
- linkAgainstFooStubs := []string{"libBar", "libA1", "libBaz", "libQux", "libAnyApex"}
-
- libFooPath := "libFoo/android_arm64_armv8-a_shared/libFoo.so"
- for _, lib := range linkAgainstFoo {
- libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
- libFlags := libLinkRule.Args["libFlags"]
- if !strings.Contains(libFlags, libFooPath) {
- t.Errorf("%q: %q is not found in %q", lib, libFooPath, libFlags)
- }
- }
-
- libFooStubPath := "libFoo/android_arm64_armv8-a_shared_current/libFoo.so"
- for _, lib := range linkAgainstFooStubs {
- libLinkRule := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld")
- libFlags := libLinkRule.Args["libFlags"]
- if !strings.Contains(libFlags, libFooStubPath) {
- t.Errorf("%q: %q is not found in %q", lib, libFooStubPath, libFlags)
- }
- }
-}
-
func TestVersioningMacro(t *testing.T) {
t.Parallel()
for _, tc := range []struct{ moduleName, expected string }{
@@ -3258,7 +3153,7 @@
testDepWithVariant("product")
}
-func TestVendorSdkVersion(t *testing.T) {
+func TestVendorOrProductVariantUsesPlatformSdkVersionAsDefault(t *testing.T) {
t.Parallel()
bp := `
@@ -3266,31 +3161,29 @@
name: "libfoo",
srcs: ["libfoo.cc"],
vendor_available: true,
+ product_available: true,
}
cc_library {
name: "libbar",
srcs: ["libbar.cc"],
vendor_available: true,
+ product_available: true,
min_sdk_version: "29",
}
`
ctx := prepareForCcTest.RunTestWithBp(t, bp)
- testSdkVersionFlag := func(module, version string) {
- flags := ctx.ModuleForTests(module, "android_vendor_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
- android.AssertStringDoesContain(t, "min sdk version", flags, "-target aarch64-linux-android"+version)
+ testSdkVersionFlag := func(module, variant, version string) {
+ flags := ctx.ModuleForTests(module, "android_"+variant+"_arm64_armv8-a_static").Rule("cc").Args["cFlags"]
+ android.AssertStringDoesContain(t, "target SDK version", flags, "-target aarch64-linux-android"+version)
}
- testSdkVersionFlag("libfoo", "10000")
- testSdkVersionFlag("libbar", "29")
-
- ctx = android.GroupFixturePreparers(
- prepareForCcTest,
- android.PrepareForTestWithBuildFlag("RELEASE_BOARD_API_LEVEL_FROZEN", "true"),
- ).RunTestWithBp(t, bp)
- testSdkVersionFlag("libfoo", "30")
- testSdkVersionFlag("libbar", "29")
+ testSdkVersionFlag("libfoo", "vendor", "30")
+ testSdkVersionFlag("libfoo", "product", "30")
+ // target SDK version can be set explicitly with min_sdk_version
+ testSdkVersionFlag("libbar", "vendor", "29")
+ testSdkVersionFlag("libbar", "product", "29")
}
func TestClangVerify(t *testing.T) {
@@ -3321,3 +3214,20 @@
t.Errorf("expected %q in cflags, got %q", "-Xclang -verify", cFlags_cv)
}
}
+
+func TestCheckConflictingExplicitVersions(t *testing.T) {
+ PrepareForIntegrationTestWithCc.
+ ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+ `shared_libs: duplicate shared libraries with different explicit versions: "libbar" and "libbar#impl"`,
+ )).
+ RunTestWithBp(t, `
+ cc_library {
+ name: "libfoo",
+ shared_libs: ["libbar", "libbar#impl"],
+ }
+
+ cc_library {
+ name: "libbar",
+ }
+ `)
+}
diff --git a/cc/compdb.go b/cc/compdb.go
index b33f490..4132e09 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -146,6 +146,8 @@
isAsm = false
isCpp = true
clangPath = cxxPath
+ case ".o":
+ return nil
default:
log.Print("Unknown file extension " + src.Ext() + " on file " + src.String())
isAsm = true
@@ -185,6 +187,10 @@
}
for _, src := range srcs {
if _, ok := builds[src.String()]; !ok {
+ args := getArguments(src, ctx, ccModule, ccPath, cxxPath)
+ if args == nil {
+ continue
+ }
builds[src.String()] = compDbEntry{
Directory: android.AbsSrcDirForExistingUseCases(),
Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath),
diff --git a/cc/compiler.go b/cc/compiler.go
index 0fa058a..91f107c 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -78,11 +78,11 @@
// If possible, don't use this. If adding paths from the current directory use
// local_include_dirs, if adding paths from other modules use export_include_dirs in
// that module.
- Include_dirs []string `android:"arch_variant,variant_prepend"`
+ Include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
// list of directories relative to the Blueprints file that will
// be added to the include path using -I
- Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+ Local_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
// Add the directory containing the Android.bp file to the list of include
// directories. Defaults to true.
@@ -411,13 +411,13 @@
}
// Include dir cflags
- localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
+ localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs.GetOrDefault(ctx, nil))
if len(localIncludeDirs) > 0 {
f := includeDirsToFlags(localIncludeDirs)
flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
}
- rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs)
+ rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs.GetOrDefault(ctx, nil))
if len(rootIncludeDirs) > 0 {
f := includeDirsToFlags(rootIncludeDirs)
flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
@@ -807,7 +807,7 @@
type RustBindgenClangProperties struct {
// list of directories relative to the Blueprints file that will
// be added to the include path using -I
- Local_include_dirs []string `android:"arch_variant,variant_prepend"`
+ Local_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
// list of static libraries that provide headers for this binding.
Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"`
diff --git a/cc/library.go b/cc/library.go
index 1f21614..4ce506e 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -566,16 +566,10 @@
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
if ctx.IsLlndk() {
- vendorApiLevel := ctx.Config().VendorApiLevel()
- if vendorApiLevel == "" {
- // TODO(b/321892570): Some tests relying on old fixtures which
- // doesn't set vendorApiLevel. Needs to fix them.
- vendorApiLevel = ctx.Config().PlatformSdkVersion().String()
- }
- // This is the vendor variant of an LLNDK library, build the LLNDK stubs.
+ futureVendorApiLevel := android.ApiLevelOrPanic(ctx, "999999")
nativeAbiResult := parseNativeAbiDefinition(ctx,
String(library.Properties.Llndk.Symbol_file),
- android.ApiLevelOrPanic(ctx, vendorApiLevel), "--llndk")
+ futureVendorApiLevel, "--llndk")
objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
if !Bool(library.Properties.Llndk.Unversioned) {
library.versionScriptPath = android.OptionalPathForPath(
@@ -1194,6 +1188,7 @@
SharedLibrary: unstrippedOutputFile,
TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering,
Target: ctx.Target(),
+ IsStubs: library.buildStubs(),
})
addStubDependencyProviders(ctx)
diff --git a/cc/linkable.go b/cc/linkable.go
index cd33e28..ef204eb 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -317,7 +317,9 @@
SharedLibrary android.Path
Target android.Target
- TableOfContents android.OptionalPath
+ TableOfContents android.OptionalPath
+ IsStubs bool
+ ImplementationDeps depset.DepSet[string]
// should be obtained from static analogue
TransitiveStaticLibrariesForOrdering depset.DepSet[android.Path]
@@ -386,3 +388,9 @@
}
var FlagExporterInfoProvider = blueprint.NewProvider[FlagExporterInfo]()
+
+var ImplementationDepInfoProvider = blueprint.NewProvider[*ImplementationDepInfo]()
+
+type ImplementationDepInfo struct {
+ ImplementationDeps depset.DepSet[android.Path]
+}
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 7c87297..ba4c662 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -221,6 +221,7 @@
Target: ctx.Target(),
TableOfContents: p.tocFile,
+ IsStubs: p.buildStubs(),
})
return outputFile
@@ -232,6 +233,7 @@
android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{
SharedLibrary: latestStub,
Target: ctx.Target(),
+ IsStubs: true,
})
return latestStub
diff --git a/cc/tidy.go b/cc/tidy.go
index ec1e8a2..5cbf8f0 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -219,15 +219,11 @@
subsetTidyFileGroups := make(map[string]android.Paths) // subset group name => tidy file Paths
// (1) Collect all obj/tidy files into OS-specific groups.
- ctx.VisitAllModuleVariants(module, func(variant android.Module) {
- if ctx.Config().KatiEnabled() && android.ShouldSkipAndroidMkProcessing(ctx, variant) {
- return
- }
- if m, ok := variant.(*Module); ok {
- osName := variant.Target().Os.Name
- addToOSGroup(osName, m.objFiles, allObjFileGroups, subsetObjFileGroups)
- addToOSGroup(osName, m.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
- }
+ ctx.VisitAllModuleVariantProxies(module, func(variant android.ModuleProxy) {
+ osName := android.OtherModuleProviderOrDefault(ctx, variant, android.CommonPropertiesProviderKey).CompileTarget.Os.Name
+ info := android.OtherModuleProviderOrDefault(ctx, variant, CcObjectInfoProvider)
+ addToOSGroup(osName, info.objFiles, allObjFileGroups, subsetObjFileGroups)
+ addToOSGroup(osName, info.tidyFiles, allTidyFileGroups, subsetTidyFileGroups)
})
// (2) Add an all-OS group, with "" or "subset" name, to include all os-specific phony targets.
@@ -258,7 +254,7 @@
// Collect tidy/obj targets from the 'final' modules.
ctx.VisitAllModules(func(module android.Module) {
- if module == ctx.FinalModule(module) {
+ if ctx.IsFinalModule(module) {
collectTidyObjModuleTargets(ctx, module, tidyModulesInDirGroup, objModulesInDirGroup)
}
})
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index e7dff40..4a2adf0 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -166,6 +166,7 @@
Target: ctx.Target(),
TableOfContents: p.tocFile,
+ IsStubs: false,
})
p.libraryDecorator.flagExporter.setProvider(ctx)
diff --git a/cmd/find_input_delta/find_input_delta/Android.bp b/cmd/find_input_delta/find_input_delta/Android.bp
new file mode 100644
index 0000000..93a7708
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta/Android.bp
@@ -0,0 +1,18 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+ name: "find_input_delta",
+ deps: [
+ "golang-protobuf-encoding-prototext",
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ "soong-cmd-find_input_delta-lib",
+ "soong-cmd-find_input_delta-proto",
+ "soong-cmd-find_input_delta-proto_internal",
+ ],
+ srcs: [
+ "main.go",
+ ],
+}
diff --git a/cmd/find_input_delta/find_input_delta/main.go b/cmd/find_input_delta/find_input_delta/main.go
new file mode 100644
index 0000000..6b657ea
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta/main.go
@@ -0,0 +1,88 @@
+// 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 main
+
+import (
+ "flag"
+ "os"
+ "strings"
+
+ fid_lib "android/soong/cmd/find_input_delta/find_input_delta_lib"
+)
+
+func main() {
+ var top string
+ var prior_state_file string
+ var new_state_file string
+ var target string
+ var inputs_file string
+ var template string
+ var inputs []string
+ var inspect bool
+ var err error
+
+ flag.StringVar(&top, "top", ".", "path to top of workspace")
+ flag.StringVar(&prior_state_file, "prior_state", "", "prior internal state file")
+ flag.StringVar(&new_state_file, "new_state", "", "new internal state file")
+ flag.StringVar(&target, "target", "", "name of ninja output file for build action")
+ flag.StringVar(&inputs_file, "inputs_file", "", "file containing list of input files")
+ flag.StringVar(&template, "template", fid_lib.DefaultTemplate, "output template for FileList")
+ flag.BoolVar(&inspect, "inspect", false, "whether to inspect file contents")
+
+ flag.Parse()
+
+ if target == "" {
+ panic("must specify --target")
+ }
+ if prior_state_file == "" {
+ prior_state_file = target + ".pc_state"
+ }
+ if new_state_file == "" {
+ new_state_file = prior_state_file + ".new"
+ }
+
+ if err = os.Chdir(top); err != nil {
+ panic(err)
+ }
+
+ inputs = flag.Args()
+ if inputs_file != "" {
+ data, err := os.ReadFile(inputs_file)
+ if err != nil {
+ panic(err)
+ }
+ inputs = append(inputs, strings.Split(string(data), "\n")...)
+ }
+
+ // Read the prior state
+ prior_state, err := fid_lib.LoadState(prior_state_file, fid_lib.OsFs)
+ if err != nil {
+ panic(err)
+ }
+ // Create the new state
+ new_state, err := fid_lib.CreateState(inputs, inspect, fid_lib.OsFs)
+ if err != nil {
+ panic(err)
+ }
+ if err = fid_lib.WriteState(new_state, new_state_file); err != nil {
+ panic(err)
+ }
+
+ file_list := *fid_lib.CompareInternalState(prior_state, new_state, target)
+
+ if err = file_list.Format(os.Stdout, template); err != nil {
+ panic(err)
+ }
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/Android.bp b/cmd/find_input_delta/find_input_delta_lib/Android.bp
new file mode 100644
index 0000000..95bdba8
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/Android.bp
@@ -0,0 +1,35 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-cmd-find_input_delta-lib",
+ pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_lib",
+ deps: [
+ "golang-protobuf-encoding-prototext",
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ "soong-cmd-find_input_delta-proto",
+ "soong-cmd-find_input_delta-proto_internal",
+ "blueprint-pathtools",
+ ],
+ srcs: [
+ "fs.go",
+ "file_list.go",
+ "internal_state.go",
+ ],
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/file_list.go b/cmd/find_input_delta/find_input_delta_lib/file_list.go
new file mode 100644
index 0000000..23337ad
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/file_list.go
@@ -0,0 +1,78 @@
+// 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 find_input_delta_lib
+
+import (
+ "io"
+ "text/template"
+
+ fid_exp "android/soong/cmd/find_input_delta/find_input_delta_proto"
+ "google.golang.org/protobuf/proto"
+)
+
+var DefaultTemplate = `
+ {{- define "contents"}}
+ {{- range .Deletions}}-{{.}} {{end}}
+ {{- range .Additions}}+{{.}} {{end}}
+ {{- range .Changes}}+{{- .Name}} {{end}}
+ {{- range .Changes}}
+ {{- if or .Additions .Deletions .Changes}}--file {{.Name}} {{template "contents" .}}--endfile {{end}}
+ {{- end}}
+ {{- end}}
+ {{- template "contents" .}}`
+
+type FileList struct {
+ // The name of the parent for the list of file differences.
+ // For the outermost FileList, this is the name of the ninja target.
+ // Under `Changes`, it is the name of the changed file.
+ Name string
+
+ // The added files
+ Additions []string
+
+ // The deleted files
+ Deletions []string
+
+ // The modified files
+ Changes []FileList
+}
+
+func (fl FileList) Marshal() (*fid_exp.FileList, error) {
+ ret := &fid_exp.FileList{
+ Name: proto.String(fl.Name),
+ }
+ if len(fl.Additions) > 0 {
+ ret.Additions = fl.Additions
+ }
+ for _, ch := range fl.Changes {
+ change, err := ch.Marshal()
+ if err != nil {
+ return nil, err
+ }
+ ret.Changes = append(ret.Changes, change)
+ }
+ if len(fl.Deletions) > 0 {
+ ret.Deletions = fl.Deletions
+ }
+ return ret, nil
+}
+
+func (fl FileList) Format(wr io.Writer, format string) error {
+ tmpl, err := template.New("filelist").Parse(format)
+ if err != nil {
+ return err
+ }
+ return tmpl.Execute(wr, fl)
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/file_list_test.go b/cmd/find_input_delta/find_input_delta_lib/file_list_test.go
new file mode 100644
index 0000000..2459f1e
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/file_list_test.go
@@ -0,0 +1,131 @@
+// 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 find_input_delta_lib
+
+import (
+ "bytes"
+ "slices"
+ "testing"
+
+ // For Assert*.
+ "android/soong/android"
+)
+
+func (fl *FileList) Equal(other *FileList) bool {
+ if fl.Name != other.Name {
+ return false
+ }
+ if !slices.Equal(fl.Additions, other.Additions) {
+ return false
+ }
+ if !slices.Equal(fl.Deletions, other.Deletions) {
+ return false
+ }
+ if len(fl.Changes) != len(other.Changes) {
+ return false
+ }
+ for idx, ch := range fl.Changes {
+ if !ch.Equal(&other.Changes[idx]) {
+ return false
+ }
+ }
+ return true
+}
+
+func TestFormat(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Template string
+ Input FileList
+ Expected string
+ Err error
+ }{
+ {
+ Name: "no contents",
+ Template: DefaultTemplate,
+ Input: FileList{
+ Name: "target",
+ Additions: []string{"add1", "add2"},
+ Deletions: []string{"del1", "del2"},
+ Changes: []FileList{
+ FileList{Name: "mod1"},
+ FileList{Name: "mod2"},
+ },
+ },
+ Expected: "-del1 -del2 +add1 +add2 +mod1 +mod2 ",
+ Err: nil,
+ },
+ {
+ Name: "adds",
+ Template: DefaultTemplate,
+ Input: FileList{
+ Name: "target",
+ Additions: []string{"add1", "add2"},
+ },
+ Expected: "+add1 +add2 ",
+ Err: nil,
+ },
+ {
+ Name: "deletes",
+ Template: DefaultTemplate,
+ Input: FileList{
+ Name: "target",
+ Deletions: []string{"del1", "del2"},
+ },
+ Expected: "-del1 -del2 ",
+ Err: nil,
+ },
+ {
+ Name: "changes",
+ Template: DefaultTemplate,
+ Input: FileList{
+ Name: "target",
+ Changes: []FileList{
+ FileList{Name: "mod1"},
+ FileList{Name: "mod2"},
+ },
+ },
+ Expected: "+mod1 +mod2 ",
+ Err: nil,
+ },
+ {
+ Name: "with contents",
+ Template: DefaultTemplate,
+ Input: FileList{
+ Name: "target",
+ Additions: []string{"add1", "add2"},
+ Deletions: []string{"del1", "del2"},
+ Changes: []FileList{
+ FileList{
+ Name: "mod1",
+ },
+ FileList{
+ Name: "mod2",
+ Additions: []string{"a1"},
+ Deletions: []string{"d1"},
+ },
+ },
+ },
+ Expected: "-del1 -del2 +add1 +add2 +mod1 +mod2 --file mod2 -d1 +a1 --endfile ",
+ Err: nil,
+ },
+ }
+ for _, tc := range testCases {
+ buf := bytes.NewBuffer([]byte{})
+ err := tc.Input.Format(buf, tc.Template)
+ android.AssertSame(t, tc.Name, tc.Err, err)
+ android.AssertSame(t, tc.Name, tc.Expected, buf.String())
+ }
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/fs.go b/cmd/find_input_delta/find_input_delta_lib/fs.go
new file mode 100644
index 0000000..4a83ed7
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/fs.go
@@ -0,0 +1,46 @@
+// 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 find_input_delta_lib
+
+import (
+ "io"
+ "io/fs"
+ "os"
+)
+
+// OsFs provides a minimal implementation so that we can use testing/fstest for
+// unit tests.
+var OsFs fileSystem = osFS{}
+
+type fileSystem interface {
+ Open(path string) (fs.File, error)
+ Stat(path string) (os.FileInfo, error)
+ ReadFile(path string) ([]byte, error)
+}
+
+type file interface {
+ io.Closer
+ io.Reader
+ io.ReaderAt
+ io.Seeker
+ Stat() (os.FileInfo, error)
+}
+
+// osFS implements fileSystem using the local disk.
+type osFS struct{}
+
+func (osFS) Open(path string) (fs.File, error) { return os.Open(path) }
+func (osFS) Stat(path string) (os.FileInfo, error) { return os.Stat(path) }
+func (osFS) ReadFile(path string) ([]byte, error) { return os.ReadFile(path) }
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
new file mode 100644
index 0000000..b2ff8c7
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/internal_state.go
@@ -0,0 +1,122 @@
+// 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 find_input_delta_lib
+
+import (
+ "errors"
+ "fmt"
+ "io/fs"
+ "slices"
+
+ fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto_internal"
+ "github.com/google/blueprint/pathtools"
+ "google.golang.org/protobuf/proto"
+)
+
+// Load the internal state from a file.
+// If the file does not exist, an empty state is returned.
+func LoadState(filename string, fsys fs.ReadFileFS) (*fid_proto.PartialCompileInputs, error) {
+ var message = &fid_proto.PartialCompileInputs{}
+ data, err := fsys.ReadFile(filename)
+ if err != nil && !errors.Is(err, fs.ErrNotExist) {
+ return message, err
+ }
+ proto.Unmarshal(data, message)
+ return message, nil
+}
+
+type StatReadFileFS interface {
+ fs.StatFS
+ fs.ReadFileFS
+}
+
+// Create the internal state by examining the inputs.
+func CreateState(inputs []string, inspect_contents bool, fsys StatReadFileFS) (*fid_proto.PartialCompileInputs, error) {
+ ret := &fid_proto.PartialCompileInputs{}
+ slices.Sort(inputs)
+ for _, input := range inputs {
+ stat, err := fs.Stat(fsys, input)
+ if err != nil {
+ return ret, err
+ }
+ pci := &fid_proto.PartialCompileInput{
+ Name: proto.String(input),
+ MtimeNsec: proto.Int64(stat.ModTime().UnixNano()),
+ // If we ever have an easy hash, assign it here.
+ }
+ if inspect_contents {
+ contents, err := InspectFileContents(input)
+ if err != nil {
+ return ret, err
+ }
+ if contents != nil {
+ pci.Contents = contents
+ }
+ }
+ ret.InputFiles = append(ret.InputFiles, pci)
+ }
+ return ret, nil
+}
+
+// Inspect the file and extract the state of the elements in the archive.
+// If this is not an archive of some sort, nil is returned.
+func InspectFileContents(name string) ([]*fid_proto.PartialCompileInput, error) {
+ // TODO: Actually inspect the contents.
+ fmt.Printf("inspecting contents for %s\n", name)
+ return nil, nil
+}
+
+func WriteState(s *fid_proto.PartialCompileInputs, path string) error {
+ data, err := proto.Marshal(s)
+ if err != nil {
+ return err
+ }
+ return pathtools.WriteFileIfChanged(path, data, 0644)
+}
+
+func CompareInternalState(prior, other *fid_proto.PartialCompileInputs, target string) *FileList {
+ return CompareInputFiles(prior.GetInputFiles(), other.GetInputFiles(), target)
+}
+
+func CompareInputFiles(prior, other []*fid_proto.PartialCompileInput, name string) *FileList {
+ fl := &FileList{
+ Name: name,
+ }
+ PriorMap := make(map[string]*fid_proto.PartialCompileInput, len(prior))
+ // We know that the lists are properly sorted, so we can simply compare them.
+ for _, v := range prior {
+ PriorMap[v.GetName()] = v
+ }
+ otherMap := make(map[string]*fid_proto.PartialCompileInput, len(other))
+ for _, v := range other {
+ name = v.GetName()
+ otherMap[name] = v
+ if _, ok := PriorMap[name]; !ok {
+ // Added file
+ fl.Additions = append(fl.Additions, name)
+ } else if !proto.Equal(PriorMap[name], v) {
+ // Changed file
+ fl.Changes = append(fl.Changes, *CompareInputFiles(PriorMap[name].GetContents(), v.GetContents(), name))
+ }
+ }
+ for _, v := range prior {
+ name := v.GetName()
+ if _, ok := otherMap[name]; !ok {
+ // Deleted file
+ fl.Deletions = append(fl.Deletions, name)
+ }
+ }
+ return fl
+}
diff --git a/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go b/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go
new file mode 100644
index 0000000..20b8efa
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_lib/internal_state_test.go
@@ -0,0 +1,232 @@
+// 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 find_input_delta_lib
+
+import (
+ "errors"
+ "io/fs"
+ "testing"
+ "testing/fstest"
+ "time"
+
+ // For Assert*.
+ "android/soong/android"
+
+ fid_proto "android/soong/cmd/find_input_delta/find_input_delta_proto_internal"
+ "google.golang.org/protobuf/proto"
+)
+
+// Various state files
+
+func marshalProto(t *testing.T, message proto.Message) []byte {
+ data, err := proto.Marshal(message)
+ if err != nil {
+ t.Errorf("%v", err)
+ }
+ return data
+}
+
+func protoFile(name string, mtime_nsec int64, hash string, contents []*fid_proto.PartialCompileInput) (pci *fid_proto.PartialCompileInput) {
+ pci = &fid_proto.PartialCompileInput{
+ Name: proto.String(name),
+ }
+ if mtime_nsec != 0 {
+ pci.MtimeNsec = proto.Int64(mtime_nsec)
+ }
+ if len(hash) > 0 {
+ pci.Hash = proto.String(hash)
+ }
+ if contents != nil {
+ pci.Contents = contents
+ }
+ return
+}
+
+func TestLoadState(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Filename string
+ Mapfs fs.ReadFileFS
+ Expected *fid_proto.PartialCompileInputs
+ Err error
+ }{
+ {
+ Name: "missing file",
+ Filename: "missing",
+ Mapfs: fstest.MapFS{},
+ Expected: &fid_proto.PartialCompileInputs{},
+ Err: nil,
+ },
+ {
+ Name: "bad file",
+ Filename: ".",
+ Mapfs: OsFs,
+ Expected: &fid_proto.PartialCompileInputs{},
+ Err: errors.New("read failed"),
+ },
+ {
+ Name: "file with mtime",
+ Filename: "state.old",
+ Mapfs: fstest.MapFS{
+ "state.old": &fstest.MapFile{
+ Data: marshalProto(t, &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("input1", 100, "", nil),
+ },
+ }),
+ },
+ },
+ Expected: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("input1", 100, "", nil),
+ },
+ },
+ Err: nil,
+ },
+ {
+ Name: "file with mtime and hash",
+ Filename: "state.old",
+ Mapfs: fstest.MapFS{
+ "state.old": &fstest.MapFile{
+ Data: marshalProto(t, &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("input1", 100, "crc:crc_value", nil),
+ },
+ }),
+ },
+ },
+ Expected: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("input1", 100, "crc:crc_value", nil),
+ },
+ },
+ Err: nil,
+ },
+ }
+ for _, tc := range testCases {
+ actual, err := LoadState(tc.Filename, tc.Mapfs)
+ if tc.Err == nil {
+ android.AssertSame(t, tc.Name, tc.Err, err)
+ } else if err == nil {
+ t.Errorf("%s: expected error, did not get one", tc.Name)
+ }
+ if !proto.Equal(tc.Expected, actual) {
+ t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
+ }
+ }
+}
+
+func TestCreateState(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Inputs []string
+ Inspect bool
+ Mapfs StatReadFileFS
+ Expected *fid_proto.PartialCompileInputs
+ Err error
+ }{
+ {
+ Name: "no inputs",
+ Inputs: []string{},
+ Mapfs: fstest.MapFS{},
+ Expected: &fid_proto.PartialCompileInputs{},
+ Err: nil,
+ },
+ {
+ Name: "files found",
+ Inputs: []string{"baz", "foo", "bar"},
+ Mapfs: fstest.MapFS{
+ "foo": &fstest.MapFile{ModTime: time.Unix(0, 100).UTC()},
+ "baz": &fstest.MapFile{ModTime: time.Unix(0, 300).UTC()},
+ "bar": &fstest.MapFile{ModTime: time.Unix(0, 200).UTC()},
+ },
+ Expected: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ // Files are always sorted.
+ protoFile("bar", 200, "", nil),
+ protoFile("baz", 300, "", nil),
+ protoFile("foo", 100, "", nil),
+ },
+ },
+ Err: nil,
+ },
+ }
+ for _, tc := range testCases {
+ actual, err := CreateState(tc.Inputs, tc.Inspect, tc.Mapfs)
+ if tc.Err == nil {
+ android.AssertSame(t, tc.Name, tc.Err, err)
+ } else if err == nil {
+ t.Errorf("%s: expected error, did not get one", tc.Name)
+ }
+ if !proto.Equal(tc.Expected, actual) {
+ t.Errorf("%s: expected %v, actual %v", tc.Name, tc.Expected, actual)
+ }
+ }
+}
+
+func TestCompareInternalState(t *testing.T) {
+ testCases := []struct {
+ Name string
+ Target string
+ Prior *fid_proto.PartialCompileInputs
+ New *fid_proto.PartialCompileInputs
+ Expected *FileList
+ }{
+ {
+ Name: "prior is empty",
+ Target: "foo",
+ Prior: &fid_proto.PartialCompileInputs{},
+ New: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("file1", 100, "", nil),
+ },
+ },
+ Expected: &FileList{
+ Name: "foo",
+ Additions: []string{"file1"},
+ },
+ },
+ {
+ Name: "one of each",
+ Target: "foo",
+ Prior: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("file0", 100, "", nil),
+ protoFile("file1", 100, "", nil),
+ protoFile("file2", 200, "", nil),
+ },
+ },
+ New: &fid_proto.PartialCompileInputs{
+ InputFiles: []*fid_proto.PartialCompileInput{
+ protoFile("file0", 100, "", nil),
+ protoFile("file1", 200, "", nil),
+ protoFile("file3", 300, "", nil),
+ },
+ },
+ Expected: &FileList{
+ Name: "foo",
+ Additions: []string{"file3"},
+ Changes: []FileList{FileList{Name: "file1"}},
+ Deletions: []string{"file2"},
+ },
+ },
+ }
+ for _, tc := range testCases {
+ actual := CompareInternalState(tc.Prior, tc.New, tc.Target)
+ if !tc.Expected.Equal(actual) {
+ t.Errorf("%s: expected %q, actual %q", tc.Name, tc.Expected, actual)
+ }
+ }
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/Android.bp b/cmd/find_input_delta/find_input_delta_proto/Android.bp
new file mode 100644
index 0000000..1a05b9e
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/Android.bp
@@ -0,0 +1,29 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-cmd-find_input_delta-proto",
+ pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_proto",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "file_list.pb.go",
+ ],
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go b/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
new file mode 100644
index 0000000..648ef22
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/file_list.pb.go
@@ -0,0 +1,198 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.33.0
+// protoc v3.21.12
+// source: file_list.proto
+
+package proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type FileList struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The name of the file.
+ // In the outermost message, this is the name of the Ninja target.
+ // When used in `changes`, this is the name of the changed file.
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // The added files.
+ Additions []string `protobuf:"bytes,2,rep,name=additions" json:"additions,omitempty"`
+ // The deleted files.
+ Deletions []string `protobuf:"bytes,3,rep,name=deletions" json:"deletions,omitempty"`
+ // The changed files.
+ Changes []*FileList `protobuf:"bytes,4,rep,name=changes" json:"changes,omitempty"`
+}
+
+func (x *FileList) Reset() {
+ *x = FileList{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_file_list_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *FileList) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FileList) ProtoMessage() {}
+
+func (x *FileList) ProtoReflect() protoreflect.Message {
+ mi := &file_file_list_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use FileList.ProtoReflect.Descriptor instead.
+func (*FileList) Descriptor() ([]byte, []int) {
+ return file_file_list_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *FileList) GetName() string {
+ if x != nil && x.Name != nil {
+ return *x.Name
+ }
+ return ""
+}
+
+func (x *FileList) GetAdditions() []string {
+ if x != nil {
+ return x.Additions
+ }
+ return nil
+}
+
+func (x *FileList) GetDeletions() []string {
+ if x != nil {
+ return x.Deletions
+ }
+ return nil
+}
+
+func (x *FileList) GetChanges() []*FileList {
+ if x != nil {
+ return x.Changes
+ }
+ return nil
+}
+
+var File_file_list_proto protoreflect.FileDescriptor
+
+var file_file_list_proto_rawDesc = []byte{
+ 0x0a, 0x0f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x12, 0x1e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f,
+ 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x22, 0x9e, 0x01, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x12,
+ 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+ 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42,
+ 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69,
+ 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x42, 0x26, 0x5a, 0x24, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f,
+ 0x6f, 0x6e, 0x67, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64,
+ 0x65, 0x6c, 0x74, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+ file_file_list_proto_rawDescOnce sync.Once
+ file_file_list_proto_rawDescData = file_file_list_proto_rawDesc
+)
+
+func file_file_list_proto_rawDescGZIP() []byte {
+ file_file_list_proto_rawDescOnce.Do(func() {
+ file_file_list_proto_rawDescData = protoimpl.X.CompressGZIP(file_file_list_proto_rawDescData)
+ })
+ return file_file_list_proto_rawDescData
+}
+
+var file_file_list_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_file_list_proto_goTypes = []interface{}{
+ (*FileList)(nil), // 0: android.find_input_delta_proto.FileList
+}
+var file_file_list_proto_depIdxs = []int32{
+ 0, // 0: android.find_input_delta_proto.FileList.changes:type_name -> android.find_input_delta_proto.FileList
+ 1, // [1:1] is the sub-list for method output_type
+ 1, // [1:1] is the sub-list for method input_type
+ 1, // [1:1] is the sub-list for extension type_name
+ 1, // [1:1] is the sub-list for extension extendee
+ 0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_file_list_proto_init() }
+func file_file_list_proto_init() {
+ if File_file_list_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_file_list_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*FileList); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_file_list_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 1,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_file_list_proto_goTypes,
+ DependencyIndexes: file_file_list_proto_depIdxs,
+ MessageInfos: file_file_list_proto_msgTypes,
+ }.Build()
+ File_file_list_proto = out.File
+ file_file_list_proto_rawDesc = nil
+ file_file_list_proto_goTypes = nil
+ file_file_list_proto_depIdxs = nil
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/file_list.proto b/cmd/find_input_delta/find_input_delta_proto/file_list.proto
new file mode 100644
index 0000000..d7faca9
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/file_list.proto
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.find_input_delta_proto;
+option go_package = "android/soong/find_input_delta/proto";
+
+message FileList {
+ // The name of the file.
+ // In the outermost message, this is the name of the Ninja target.
+ // When used in `changes`, this is the name of the changed file.
+ optional string name = 1;
+
+ // The added files.
+ repeated string additions = 2;
+
+ // The deleted files.
+ repeated string deletions = 3;
+
+ // The changed files.
+ repeated FileList changes = 4;
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto/regen.sh b/cmd/find_input_delta/find_input_delta_proto/regen.sh
new file mode 100644
index 0000000..d773659
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. file_list.proto
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/Android.bp b/cmd/find_input_delta/find_input_delta_proto_internal/Android.bp
new file mode 100644
index 0000000..00ba9ff
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/Android.bp
@@ -0,0 +1,29 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+ name: "soong-cmd-find_input_delta-proto_internal",
+ pkgPath: "android/soong/cmd/find_input_delta/find_input_delta_proto_internal",
+ deps: [
+ "golang-protobuf-reflect-protoreflect",
+ "golang-protobuf-runtime-protoimpl",
+ ],
+ srcs: [
+ "internal_state.pb.go",
+ ],
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go
new file mode 100644
index 0000000..2229a32
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.pb.go
@@ -0,0 +1,268 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.33.0
+// protoc v3.21.12
+// source: internal_state.proto
+
+package proto
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// The state of all inputs.
+type PartialCompileInputs struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The status of each file.
+ InputFiles []*PartialCompileInput `protobuf:"bytes,1,rep,name=input_files,json=inputFiles" json:"input_files,omitempty"`
+}
+
+func (x *PartialCompileInputs) Reset() {
+ *x = PartialCompileInputs{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_internal_state_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PartialCompileInputs) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PartialCompileInputs) ProtoMessage() {}
+
+func (x *PartialCompileInputs) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_state_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PartialCompileInputs.ProtoReflect.Descriptor instead.
+func (*PartialCompileInputs) Descriptor() ([]byte, []int) {
+ return file_internal_state_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *PartialCompileInputs) GetInputFiles() []*PartialCompileInput {
+ if x != nil {
+ return x.InputFiles
+ }
+ return nil
+}
+
+// The state of one input.
+type PartialCompileInput struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The name of the file.
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // The timestamp of the file in (Unix) nanoseconds.
+ MtimeNsec *int64 `protobuf:"varint,2,opt,name=mtime_nsec,json=mtimeNsec" json:"mtime_nsec,omitempty"`
+ // The hash of the file, in the form ‘{HASHNAME}:{VALUE}’
+ Hash *string `protobuf:"bytes,3,opt,name=hash" json:"hash,omitempty"`
+ // Contents of the file, if the file was inspected (such as jar files, etc).
+ Contents []*PartialCompileInput `protobuf:"bytes,4,rep,name=contents" json:"contents,omitempty"`
+}
+
+func (x *PartialCompileInput) Reset() {
+ *x = PartialCompileInput{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_internal_state_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *PartialCompileInput) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PartialCompileInput) ProtoMessage() {}
+
+func (x *PartialCompileInput) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_state_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use PartialCompileInput.ProtoReflect.Descriptor instead.
+func (*PartialCompileInput) Descriptor() ([]byte, []int) {
+ return file_internal_state_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *PartialCompileInput) GetName() string {
+ if x != nil && x.Name != nil {
+ return *x.Name
+ }
+ return ""
+}
+
+func (x *PartialCompileInput) GetMtimeNsec() int64 {
+ if x != nil && x.MtimeNsec != nil {
+ return *x.MtimeNsec
+ }
+ return 0
+}
+
+func (x *PartialCompileInput) GetHash() string {
+ if x != nil && x.Hash != nil {
+ return *x.Hash
+ }
+ return ""
+}
+
+func (x *PartialCompileInput) GetContents() []*PartialCompileInput {
+ if x != nil {
+ return x.Contents
+ }
+ return nil
+}
+
+var File_internal_state_proto protoreflect.FileDescriptor
+
+var file_internal_state_proto_rawDesc = []byte{
+ 0x0a, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+ 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+ 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6c, 0x0a, 0x14, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61,
+ 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x54,
+ 0x0a, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x66, 0x69,
+ 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x5f, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f, 0x6d, 0x70,
+ 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46,
+ 0x69, 0x6c, 0x65, 0x73, 0x22, 0xad, 0x01, 0x0a, 0x13, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c,
+ 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6e, 0x73, 0x65, 0x63, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6d, 0x74, 0x69, 0x6d, 0x65, 0x4e, 0x73, 0x65, 0x63, 0x12,
+ 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68,
+ 0x61, 0x73, 0x68, 0x12, 0x4f, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18,
+ 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+ 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61,
+ 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6c, 0x43, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74,
+ 0x65, 0x6e, 0x74, 0x73, 0x42, 0x26, 0x5a, 0x24, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
+ 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x66, 0x69, 0x6e, 0x64, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74,
+ 0x5f, 0x64, 0x65, 0x6c, 0x74, 0x61, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+ file_internal_state_proto_rawDescOnce sync.Once
+ file_internal_state_proto_rawDescData = file_internal_state_proto_rawDesc
+)
+
+func file_internal_state_proto_rawDescGZIP() []byte {
+ file_internal_state_proto_rawDescOnce.Do(func() {
+ file_internal_state_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_state_proto_rawDescData)
+ })
+ return file_internal_state_proto_rawDescData
+}
+
+var file_internal_state_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_internal_state_proto_goTypes = []interface{}{
+ (*PartialCompileInputs)(nil), // 0: android.find_input_delta_proto.PartialCompileInputs
+ (*PartialCompileInput)(nil), // 1: android.find_input_delta_proto.PartialCompileInput
+}
+var file_internal_state_proto_depIdxs = []int32{
+ 1, // 0: android.find_input_delta_proto.PartialCompileInputs.input_files:type_name -> android.find_input_delta_proto.PartialCompileInput
+ 1, // 1: android.find_input_delta_proto.PartialCompileInput.contents:type_name -> android.find_input_delta_proto.PartialCompileInput
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_internal_state_proto_init() }
+func file_internal_state_proto_init() {
+ if File_internal_state_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_internal_state_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PartialCompileInputs); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_internal_state_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*PartialCompileInput); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_internal_state_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 2,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_internal_state_proto_goTypes,
+ DependencyIndexes: file_internal_state_proto_depIdxs,
+ MessageInfos: file_internal_state_proto_msgTypes,
+ }.Build()
+ File_internal_state_proto = out.File
+ file_internal_state_proto_rawDesc = nil
+ file_internal_state_proto_goTypes = nil
+ file_internal_state_proto_depIdxs = nil
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto
new file mode 100644
index 0000000..113fc64
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/internal_state.proto
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2024 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto2";
+package android.find_input_delta_proto;
+option go_package = "android/soong/find_input_delta/proto";
+
+// The state of all inputs.
+message PartialCompileInputs {
+ // The status of each file.
+ repeated PartialCompileInput input_files = 1;
+}
+
+// The state of one input.
+message PartialCompileInput {
+ // The name of the file.
+ optional string name = 1;
+
+ // The timestamp of the file in (Unix) nanoseconds.
+ optional int64 mtime_nsec = 2;
+
+ // The hash of the file, in the form ‘{HASHNAME}:{VALUE}’
+ optional string hash = 3;
+
+ // Contents of the file, if the file was inspected (such as jar files, etc).
+ repeated PartialCompileInput contents = 4;
+}
diff --git a/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh b/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh
new file mode 100644
index 0000000..cbaf7d0
--- /dev/null
+++ b/cmd/find_input_delta/find_input_delta_proto_internal/regen.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+aprotoc --go_out=paths=source_relative:. internal_state.proto
diff --git a/cmd/symbols_map/symbols_map_proto/symbols_map.proto b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
index 693fe3e..a76d171 100644
--- a/cmd/symbols_map/symbols_map_proto/symbols_map.proto
+++ b/cmd/symbols_map/symbols_map_proto/symbols_map.proto
@@ -37,6 +37,21 @@
// type is the type of the mapping, either ELF or R8.
optional Type type = 3;
+
+ // LocationType is the place where to look for the file with the given
+ // identifier.
+ Enum LocationType {
+ // ZIP denotes the file with the given identifier is in the distribuited
+ // symbols.zip or proguard_dict.zip files, or the local disc.
+ ZIP = 0;
+ // AB denotes the file with the given identifier is in the AB artifacts but
+ // not in a symbols.zip or proguard_dict.zip.
+ AB = 1;
+ }
+
+ // location_type is the Location Type that dictates where to search for the
+ // file with the given identifier. Defaults to ZIP if not present.
+ optional LocationType location_type = 4;
}
message Mappings {
diff --git a/compliance/notice.go b/compliance/notice.go
index 4fc83ab..edd1b34 100644
--- a/compliance/notice.go
+++ b/compliance/notice.go
@@ -18,6 +18,7 @@
"path/filepath"
"android/soong/android"
+
"github.com/google/blueprint"
)
@@ -62,8 +63,7 @@
props noticeXmlProperties
- outputFile android.OutputPath
- installPath android.InstallPath
+ outputFile android.OutputPath
}
type noticeXmlProperties struct {
@@ -86,10 +86,8 @@
nx.outputFile = output.OutputPath
- if android.Bool(ctx.Config().ProductVariables().UseSoongSystemImage) {
- nx.installPath = android.PathForModuleInPartitionInstall(ctx, nx.props.Partition_name, "etc")
- ctx.InstallFile(nx.installPath, "NOTICE.xml.gz", nx.outputFile)
- }
+ installPath := android.PathForModuleInPartitionInstall(ctx, nx.props.Partition_name, "etc")
+ ctx.PackageFile(installPath, "NOTICE.xml.gz", nx.outputFile)
}
func (nx *NoticeXmlModule) AndroidMkEntries() []android.AndroidMkEntries {
diff --git a/etc/adb_keys.go b/etc/adb_keys.go
index a2df41c..73bc347 100644
--- a/etc/adb_keys.go
+++ b/etc/adb_keys.go
@@ -36,6 +36,11 @@
func (m *AdbKeysModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
productVariables := ctx.Config().ProductVariables()
+
+ if !m.ProductSpecific() {
+ ctx.ModuleErrorf("adb_keys module type must set product_specific to true")
+ }
+
if !(android.Bool(productVariables.Debuggable) && len(android.String(productVariables.AdbKeys)) > 0) {
m.SkipInstall()
return
@@ -48,7 +53,7 @@
Output: m.outputPath,
Input: input.Path(),
})
- m.installPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().ProductPath(), "etc/security")
+ m.installPath = android.PathForModuleInstall(ctx, "etc/security")
ctx.InstallFile(m.installPath, "adb_keys", m.outputPath)
}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index a46da77..47b391c 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -76,6 +76,11 @@
ctx.RegisterModuleType("prebuilt_res", PrebuiltResFactory)
ctx.RegisterModuleType("prebuilt_wlc_upt", PrebuiltWlcUptFactory)
ctx.RegisterModuleType("prebuilt_odm", PrebuiltOdmFactory)
+ ctx.RegisterModuleType("prebuilt_vendor_dlkm", PrebuiltVendorDlkmFactory)
+ ctx.RegisterModuleType("prebuilt_bt_firmware", PrebuiltBtFirmwareFactory)
+ ctx.RegisterModuleType("prebuilt_tvservice", PrebuiltTvServiceFactory)
+ ctx.RegisterModuleType("prebuilt_optee", PrebuiltOpteeFactory)
+ ctx.RegisterModuleType("prebuilt_tvconfig", PrebuiltTvConfigFactory)
ctx.RegisterModuleType("prebuilt_defaults", defaultsFactory)
@@ -132,6 +137,9 @@
// Install symlinks to the installed file.
Symlinks []string `android:"arch_variant"`
+
+ // Install to partition oem when set to true.
+ Oem_specific *bool `android:"arch_variant"`
}
type prebuiltSubdirProperties struct {
@@ -369,6 +377,10 @@
ctx.PropertyErrorf("sub_dir", "relative_install_path is set. Cannot set sub_dir")
}
baseInstallDirPath := android.PathForModuleInstall(ctx, p.installBaseDir(ctx), p.SubDir())
+ // TODO(b/377304441)
+ if android.Bool(p.properties.Oem_specific) {
+ baseInstallDirPath = android.PathForModuleInPartitionInstall(ctx, ctx.DeviceConfig().OemPath(), p.installBaseDir(ctx), p.SubDir())
+ }
filename := proptools.String(p.properties.Filename)
filenameFromSrc := proptools.Bool(p.properties.Filename_from_src)
@@ -910,3 +922,53 @@
android.InitDefaultableModule(module)
return module
}
+
+// prebuilt_vendor_dlkm installs files in <partition>/vendor_dlkm directory.
+func PrebuiltVendorDlkmFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "vendor_dlkm")
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+// prebuilt_bt_firmware installs files in <partition>/bt_firmware directory.
+func PrebuiltBtFirmwareFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "bt_firmware")
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+// prebuilt_tvservice installs files in <partition>/tvservice directory.
+func PrebuiltTvServiceFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "tvservice")
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+// prebuilt_optee installs files in <partition>/optee directory.
+func PrebuiltOpteeFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "optee")
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
+
+// prebuilt_tvconfig installs files in <partition>/tvconfig directory.
+func PrebuiltTvConfigFactory() android.Module {
+ module := &PrebuiltEtc{}
+ InitPrebuiltEtcModule(module, "tvconfig")
+ // This module is device-only
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+ return module
+}
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index 9071272..ab1b96e 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -34,6 +34,10 @@
Vendor_partition_name *string
// Name of the Odm partition filesystem module
Odm_partition_name *string
+ // The vbmeta partition and its "chained" partitions
+ Vbmeta_partitions []string
+ // Name of the Userdata partition filesystem module
+ Userdata_partition_name *string
}
type androidDevice struct {
@@ -46,7 +50,6 @@
module := &androidDevice{}
module.AddProperties(&module.partitionProps)
android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
-
return module
}
@@ -69,6 +72,10 @@
addDependencyIfDefined(a.partitionProps.Product_partition_name)
addDependencyIfDefined(a.partitionProps.Vendor_partition_name)
addDependencyIfDefined(a.partitionProps.Odm_partition_name)
+ addDependencyIfDefined(a.partitionProps.Userdata_partition_name)
+ for _, vbmetaPartition := range a.partitionProps.Vbmeta_partitions {
+ ctx.AddDependency(ctx.Module(), filesystemDepTag, vbmetaPartition)
+ }
}
func (a *androidDevice) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 6ed962f..78e24e2 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -52,12 +52,6 @@
properties FilesystemProperties
- // Function that builds extra files under the root directory and returns the files
- buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths
-
- // Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
- filterPackagingSpec func(spec android.PackagingSpec) bool
-
output android.OutputPath
installDir android.InstallPath
@@ -65,8 +59,18 @@
// Keeps the entries installed from this filesystem
entries []string
+
+ filesystemBuilder filesystemBuilder
}
+type filesystemBuilder interface {
+ BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath)
+ // Function that filters PackagingSpec in PackagingBase.GatherPackagingSpecs()
+ FilterPackagingSpec(spec android.PackagingSpec) bool
+}
+
+var _ filesystemBuilder = (*filesystem)(nil)
+
type SymlinkDefinition struct {
Target *string
Name *string
@@ -147,7 +151,9 @@
Erofs ErofsProperties
- Linkerconfig LinkerConfigProperties
+ F2fs F2fsProperties
+
+ Linker_config LinkerConfigProperties
// Determines if the module is auto-generated from Soong or not. If the module is
// auto-generated, its deps are exempted from visibility enforcement.
@@ -166,6 +172,11 @@
Sparse *bool
}
+// Additional properties required to generate f2fs FS partitions.
+type F2fsProperties struct {
+ Sparse *bool
+}
+
type LinkerConfigProperties struct {
// Build a linker.config.pb file
@@ -183,7 +194,7 @@
// partitions like system.img. For example, cc_library modules are placed under ./lib[64] directory.
func FilesystemFactory() android.Module {
module := &filesystem{}
- module.filterPackagingSpec = module.filterInstallablePackagingSpec
+ module.filesystemBuilder = module
initFilesystemModule(module, module)
return module
}
@@ -227,6 +238,7 @@
const (
ext4Type fsType = iota
erofsType
+ f2fsType
compressedCpioType
cpioType // uncompressed
unknown
@@ -249,6 +261,8 @@
return ext4Type
case "erofs":
return erofsType
+ case "f2fs":
+ return f2fsType
case "compressed_cpio":
return compressedCpioType
case "cpio":
@@ -275,13 +289,17 @@
return proptools.StringDefault(f.properties.Partition_name, f.Name())
}
-func (f *filesystem) filterInstallablePackagingSpec(ps android.PackagingSpec) bool {
+func (f *filesystem) FilterPackagingSpec(ps android.PackagingSpec) bool {
// Filesystem module respects the installation semantic. A PackagingSpec from a module with
// IsSkipInstall() is skipped.
- if proptools.Bool(f.properties.Is_auto_generated) { // TODO (spandandas): Remove this.
- return !ps.SkipInstall() && (ps.Partition() == f.PartitionType())
+ if ps.SkipInstall() {
+ return false
}
- return !ps.SkipInstall()
+ if proptools.Bool(f.properties.Is_auto_generated) { // TODO (spandandas): Remove this.
+ pt := f.PartitionType()
+ return pt == "ramdisk" || ps.Partition() == pt
+ }
+ return true
}
var pctx = android.NewPackageContext("android/soong/filesystem")
@@ -289,7 +307,7 @@
func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) {
validatePartitionType(ctx, f)
switch f.fsType(ctx) {
- case ext4Type, erofsType:
+ case ext4Type, erofsType, f2fsType:
f.output = f.buildImageUsingBuildImage(ctx)
case compressedCpioType:
f.output = f.buildCpioImage(ctx, true)
@@ -377,25 +395,6 @@
builder.Command().Text("ln -sf").Text(proptools.ShellEscape(target)).Text(dst.String())
f.appendToEntry(ctx, dst)
}
-
- // create extra files if there's any
- if f.buildExtraFiles != nil {
- rootForExtraFiles := android.PathForModuleGen(ctx, "root-extra").OutputPath
- extraFiles := f.buildExtraFiles(ctx, rootForExtraFiles)
- for _, extraFile := range extraFiles {
- rel, err := filepath.Rel(rootForExtraFiles.String(), extraFile.String())
- if err != nil || strings.HasPrefix(rel, "..") {
- ctx.ModuleErrorf("can't make %q relative to %q", extraFile, rootForExtraFiles)
- }
- f.appendToEntry(ctx, rootDir.Join(ctx, rel))
- }
- if len(extraFiles) > 0 {
- builder.Command().BuiltTool("merge_directories").
- Implicits(extraFiles.Paths()).
- Text(rootDir.String()).
- Text(rootForExtraFiles.String())
- }
- }
}
func (f *filesystem) copyPackagingSpecs(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir, rebasedDir android.WritablePath) []string {
@@ -442,7 +441,7 @@
f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
f.buildEventLogtagsFile(ctx, builder, rebasedDir)
f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
- f.buildLinkerConfigFile(ctx, builder, rebasedDir)
+ f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir)
f.copyFilesToProductOut(ctx, builder, rebasedDir)
// run host_init_verifier
@@ -505,6 +504,8 @@
return "ext4"
case erofsType:
return "erofs"
+ case f2fsType:
+ return "f2fs"
}
panic(fmt.Errorf("unsupported fs type %v", t))
}
@@ -554,8 +555,11 @@
addStr("uuid", uuid)
addStr("hash_seed", uuid)
}
- // Add erofs properties
- if f.fsType(ctx) == erofsType {
+
+ fst := f.fsType(ctx)
+ switch fst {
+ case erofsType:
+ // Add erofs properties
if compressor := f.properties.Erofs.Compressor; compressor != nil {
addStr("erofs_default_compressor", proptools.String(compressor))
}
@@ -566,17 +570,39 @@
// https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b
addStr("erofs_sparse_flag", "-s")
}
- } else if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil {
- // Raise an exception if the propfile contains erofs properties, but the fstype is not erofs
- fs := fsTypeStr(f.fsType(ctx))
- ctx.PropertyErrorf("erofs", "erofs is non-empty, but FS type is %s\n. Please delete erofs properties if this partition should use %s\n", fs, fs)
+ case f2fsType:
+ if proptools.BoolDefault(f.properties.F2fs.Sparse, true) {
+ // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2294;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0
+ addStr("f2fs_sparse_flag", "-S")
+ }
}
+ f.checkFsTypePropertyError(ctx, fst, fsTypeStr(fst))
propFile = android.PathForModuleOut(ctx, "prop").OutputPath
android.WriteFileRuleVerbatim(ctx, propFile, propFileString.String())
return propFile, deps
}
+// This method checks if there is any property set for the fstype(s) other than
+// the current fstype.
+func (f *filesystem) checkFsTypePropertyError(ctx android.ModuleContext, t fsType, fs string) {
+ raiseError := func(otherFsType, currentFsType string) {
+ errMsg := fmt.Sprintf("%s is non-empty, but FS type is %s\n. Please delete %s properties if this partition should use %s\n", otherFsType, currentFsType, otherFsType, currentFsType)
+ ctx.PropertyErrorf(otherFsType, errMsg)
+ }
+
+ if t != erofsType {
+ if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil {
+ raiseError("erofs", fs)
+ }
+ }
+ if t != f2fsType {
+ if f.properties.F2fs.Sparse != nil {
+ raiseError("f2fs", fs)
+ }
+ }
+}
+
func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.OutputPath {
if proptools.Bool(f.properties.Use_avb) {
ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+
@@ -606,7 +632,7 @@
f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir)
f.buildEventLogtagsFile(ctx, builder, rebasedDir)
f.buildAconfigFlagsFiles(ctx, builder, specs, rebasedDir)
- f.buildLinkerConfigFile(ctx, builder, rebasedDir)
+ f.filesystemBuilder.BuildLinkerConfigFile(ctx, builder, rebasedDir)
f.copyFilesToProductOut(ctx, builder, rebasedDir)
output := android.PathForModuleOut(ctx, f.installFileName()).OutputPath
@@ -642,6 +668,7 @@
"vendor_dlkm",
"odm_dlkm",
"system_dlkm",
+ "ramdisk",
}
func (f *filesystem) addMakeBuiltFiles(ctx android.ModuleContext, builder *android.RuleBuilder, rootDir android.Path) {
@@ -698,14 +725,14 @@
f.appendToEntry(ctx, eventLogtagsPath)
}
-func (f *filesystem) buildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
- if !proptools.Bool(f.properties.Linkerconfig.Gen_linker_config) {
+func (f *filesystem) BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+ if !proptools.Bool(f.properties.Linker_config.Gen_linker_config) {
return
}
provideModules, _ := f.getLibsForLinkerConfig(ctx)
output := rebasedDir.Join(ctx, "etc", "linker.config.pb")
- linkerconfig.BuildLinkerConfig(ctx, builder, android.PathsForModuleSrc(ctx, f.properties.Linkerconfig.Linker_config_srcs), provideModules, nil, output)
+ linkerconfig.BuildLinkerConfig(ctx, builder, android.PathsForModuleSrc(ctx, f.properties.Linker_config.Linker_config_srcs), provideModules, nil, output)
f.appendToEntry(ctx, output)
}
@@ -765,7 +792,7 @@
// Note that "apex" module installs its contents to "apex"(fake partition) as well
// for symbol lookup by imitating "activated" paths.
func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
- specs := f.PackagingBase.GatherPackagingSpecsWithFilter(ctx, f.filterPackagingSpec)
+ specs := f.PackagingBase.GatherPackagingSpecsWithFilter(ctx, f.filesystemBuilder.FilterPackagingSpec)
return specs
}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index 29f9373..f325d96 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -156,11 +156,15 @@
result := fixture.RunTestWithBp(t, `
android_system_image {
name: "myfilesystem",
+ base_dir: "system",
deps: [
"libfoo",
"libbar",
],
- linker_config_src: "linker.config.json",
+ linker_config: {
+ gen_linker_config: true,
+ linker_config_srcs: ["linker.config.json"],
+ },
}
cc_library {
@@ -176,7 +180,7 @@
`)
module := result.ModuleForTests("myfilesystem", "android_common")
- output := module.Output("system/etc/linker.config.pb")
+ output := module.Output("out/soong/.intermediates/myfilesystem/android_common/root/system/etc/linker.config.pb")
android.AssertStringDoesContain(t, "linker.config.pb should have libfoo",
output.RuleParams.Command, "libfoo.so")
@@ -223,7 +227,10 @@
deps: ["foo"],
},
},
- linker_config_src: "linker.config.json",
+ linker_config: {
+ gen_linker_config: true,
+ linker_config_srcs: ["linker.config.json"],
+ },
}
component {
name: "foo",
@@ -318,7 +325,10 @@
deps: [
"libfoo",
],
- linker_config_src: "linker.config.json",
+ linker_config: {
+ gen_linker_config: true,
+ linker_config_srcs: ["linker.config.json"],
+ },
}
cc_library {
@@ -585,6 +595,35 @@
android.AssertStringDoesContain(t, "erofs fs type sparse", buildImageConfig, "erofs_sparse_flag=-s")
}
+func TestF2fsPartition(t *testing.T) {
+ result := fixture.RunTestWithBp(t, `
+ android_filesystem {
+ name: "f2fs_partition",
+ type: "f2fs",
+ }
+ `)
+
+ partition := result.ModuleForTests("f2fs_partition", "android_common")
+ buildImageConfig := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("prop"))
+ android.AssertStringDoesContain(t, "f2fs fs type", buildImageConfig, "fs_type=f2fs")
+ android.AssertStringDoesContain(t, "f2fs fs type sparse", buildImageConfig, "f2fs_sparse_flag=-S")
+}
+
+func TestFsTypesPropertyError(t *testing.T) {
+ fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(
+ "erofs: erofs is non-empty, but FS type is f2fs\n. Please delete erofs properties if this partition should use f2fs\n")).
+ RunTestWithBp(t, `
+ android_filesystem {
+ name: "f2fs_partition",
+ type: "f2fs",
+ erofs: {
+ compressor: "lz4hc,9",
+ compress_hints: "compress_hints.txt",
+ },
+ }
+ `)
+}
+
// If a system_ext/ module depends on system/ module, the dependency should *not*
// be installed in system_ext/
func TestDoNotPackageCrossPartitionDependencies(t *testing.T) {
@@ -670,9 +709,9 @@
android_filesystem {
name: "myfilesystem",
deps: ["libfoo_has_no_stubs", "libfoo_has_stubs"],
- linkerconfig: {
- gen_linker_config: true,
- linker_config_srcs: ["linker.config.json"],
+ linker_config: {
+ gen_linker_config: true,
+ linker_config_srcs: ["linker.config.json"],
},
partition_type: "vendor",
}
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 898987d..672458c 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -17,27 +17,22 @@
import (
"android/soong/android"
"android/soong/linkerconfig"
+
+ "github.com/google/blueprint/proptools"
)
type systemImage struct {
filesystem
-
- properties systemImageProperties
}
-type systemImageProperties struct {
- // Path to the input linker config json file.
- Linker_config_src *string `android:"path"`
-}
+var _ filesystemBuilder = (*systemImage)(nil)
// android_system_image is a specialization of android_filesystem for the 'system' partition.
// Currently, the only difference is the inclusion of linker.config.pb file which specifies
// the provided and the required libraries to and from APEXes.
func SystemImageFactory() android.Module {
module := &systemImage{}
- module.AddProperties(&module.properties)
- module.filesystem.buildExtraFiles = module.buildExtraFiles
- module.filesystem.filterPackagingSpec = module.filterPackagingSpec
+ module.filesystemBuilder = module
initFilesystemModule(module, &module.filesystem)
return module
}
@@ -46,30 +41,22 @@
return s.filesystem.properties
}
-func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths {
- if s.filesystem.properties.Partition_type != nil {
- ctx.PropertyErrorf("partition_type", "partition_type must be unset on an android_system_image module. It is assumed to be 'system'.")
+func (s *systemImage) BuildLinkerConfigFile(ctx android.ModuleContext, builder *android.RuleBuilder, rebasedDir android.OutputPath) {
+ if !proptools.Bool(s.filesystem.properties.Linker_config.Gen_linker_config) {
+ return
}
- lc := s.buildLinkerConfigFile(ctx, root)
- // Add more files if needed
- return []android.OutputPath{lc}
-}
-
-func (s *systemImage) buildLinkerConfigFile(ctx android.ModuleContext, root android.OutputPath) android.OutputPath {
- input := android.PathForModuleSrc(ctx, android.String(s.properties.Linker_config_src))
- output := root.Join(ctx, "system", "etc", "linker.config.pb")
provideModules, requireModules := s.getLibsForLinkerConfig(ctx)
- builder := android.NewRuleBuilder(pctx, ctx)
- linkerconfig.BuildLinkerConfig(ctx, builder, android.Paths{input}, provideModules, requireModules, output)
- builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
- return output
+ output := rebasedDir.Join(ctx, "etc", "linker.config.pb")
+ linkerconfig.BuildLinkerConfig(ctx, builder, android.PathsForModuleSrc(ctx, s.filesystem.properties.Linker_config.Linker_config_srcs), provideModules, requireModules, output)
+
+ s.appendToEntry(ctx, output)
}
// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" / "root"
// partition. Note that "apex" module installs its contents to "apex"(fake partition) as well
// for symbol lookup by imitating "activated" paths.
-func (s *systemImage) filterPackagingSpec(ps android.PackagingSpec) bool {
+func (s *systemImage) FilterPackagingSpec(ps android.PackagingSpec) bool {
return !ps.SkipInstall() &&
(ps.Partition() == "system" || ps.Partition() == "root")
}
diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go
index 0bae479..6a3fc1f 100644
--- a/filesystem/vbmeta.go
+++ b/filesystem/vbmeta.go
@@ -25,19 +25,19 @@
)
func init() {
- android.RegisterModuleType("vbmeta", vbmetaFactory)
+ android.RegisterModuleType("vbmeta", VbmetaFactory)
}
type vbmeta struct {
android.ModuleBase
- properties vbmetaProperties
+ properties VbmetaProperties
output android.OutputPath
installDir android.InstallPath
}
-type vbmetaProperties struct {
+type VbmetaProperties struct {
// Name of the partition stored in vbmeta desc. Defaults to the name of this module.
Partition_name *string
@@ -50,9 +50,8 @@
// Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096.
Algorithm *string
- // File whose content will provide the rollback index. If unspecified, the rollback index
- // is from PLATFORM_SECURITY_PATCH
- Rollback_index_file *string `android:"path"`
+ // The rollback index. If unspecified, the rollback index is from PLATFORM_SECURITY_PATCH
+ Rollback_index *int64
// Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0.
Rollback_index_location *int64
@@ -62,7 +61,7 @@
Partitions proptools.Configurable[[]string]
// List of chained partitions that this vbmeta deletages the verification.
- Chained_partitions []chainedPartitionProperties
+ Chained_partitions []ChainedPartitionProperties
// List of key-value pair of avb properties
Avb_properties []avbProperty
@@ -76,7 +75,7 @@
Value *string
}
-type chainedPartitionProperties struct {
+type ChainedPartitionProperties struct {
// Name of the chained partition
Name *string
@@ -95,7 +94,7 @@
}
// vbmeta is the partition image that has the verification information for other partitions.
-func vbmetaFactory() android.Module {
+func VbmetaFactory() android.Module {
module := &vbmeta{}
module.AddProperties(&module.properties)
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
@@ -217,15 +216,12 @@
// Returns the embedded shell command that prints the rollback index
func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string {
- var cmd string
- if v.properties.Rollback_index_file != nil {
- f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file))
- cmd = "cat " + f.String()
+ if v.properties.Rollback_index != nil {
+ return fmt.Sprintf("%d", *v.properties.Rollback_index)
} else {
- cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s"
+ // Take the first line and remove the newline char
+ return "$(date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s | head -1 | tr -d '\n'" + ")"
}
- // Take the first line and remove the newline char
- return "$(" + cmd + " | head -1 | tr -d '\n'" + ")"
}
// Extract public keys from chained_partitions.private_key. The keys are indexed with the partition
diff --git a/fsgen/Android.bp b/fsgen/Android.bp
index e3cbdb3..8cd7518 100644
--- a/fsgen/Android.bp
+++ b/fsgen/Android.bp
@@ -14,6 +14,9 @@
],
srcs: [
"filesystem_creator.go",
+ "fsgen_mutators.go",
+ "prebuilt_etc_modules_gen.go",
+ "vbmeta_partitions.go",
],
testSrcs: [
"filesystem_creator_test.go",
diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go
index 7ef7d99..0a65c6c 100644
--- a/fsgen/filesystem_creator.go
+++ b/fsgen/filesystem_creator.go
@@ -18,10 +18,8 @@
"crypto/sha256"
"fmt"
"path/filepath"
- "slices"
"strconv"
"strings"
- "sync"
"android/soong/android"
"android/soong/filesystem"
@@ -43,328 +41,12 @@
ctx.PreDepsMutators(RegisterCollectFileSystemDepsMutators)
}
-func RegisterCollectFileSystemDepsMutators(ctx android.RegisterMutatorsContext) {
- ctx.BottomUp("fs_collect_deps", collectDepsMutator).MutatesGlobalState()
- ctx.BottomUp("fs_set_deps", setDepsMutator)
-}
-
-var fsGenStateOnceKey = android.NewOnceKey("FsGenState")
-var fsGenRemoveOverridesOnceKey = android.NewOnceKey("FsGenRemoveOverrides")
-
-// Map of partition module name to its partition that may be generated by Soong.
-// Note that it is not guaranteed that all modules returned by this function are successfully
-// created.
-func getAllSoongGeneratedPartitionNames(config android.Config, partitions []string) map[string]string {
- ret := map[string]string{}
- for _, partition := range partitions {
- ret[generatedModuleNameForPartition(config, partition)] = partition
- }
- return ret
-}
-
-type depCandidateProps struct {
- Namespace string
- Multilib string
- Arch []android.ArchType
-}
-
-// Map of module name to depCandidateProps
-type multilibDeps map[string]*depCandidateProps
-
-// Information necessary to generate the filesystem modules, including details about their
-// dependencies
-type FsGenState struct {
- // List of modules in `PRODUCT_PACKAGES` and `PRODUCT_PACKAGES_DEBUG`
- depCandidates []string
- // Map of names of partition to the information of modules to be added as deps
- fsDeps map[string]*multilibDeps
- // List of name of partitions to be generated by the filesystem_creator module
- soongGeneratedPartitions []string
- // Mutex to protect the fsDeps
- fsDepsMutex sync.Mutex
- // Map of _all_ soong module names to their corresponding installation properties
- moduleToInstallationProps map[string]installationProperties
-}
-
-type installationProperties struct {
- Required []string
- Overrides []string
-}
-
-func defaultDepCandidateProps(config android.Config) *depCandidateProps {
- return &depCandidateProps{
- Namespace: ".",
- Arch: []android.ArchType{config.BuildArch},
- }
-}
-
-func createFsGenState(ctx android.LoadHookContext) *FsGenState {
- return ctx.Config().Once(fsGenStateOnceKey, func() interface{} {
- partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
- candidates := android.FirstUniqueStrings(android.Concat(partitionVars.ProductPackages, partitionVars.ProductPackagesDebug))
-
- generatedPartitions := []string{"system"}
- if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
- generatedPartitions = append(generatedPartitions, "system_ext")
- }
- if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
- generatedPartitions = append(generatedPartitions, "vendor")
- }
- if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
- generatedPartitions = append(generatedPartitions, "product")
- }
- if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
- generatedPartitions = append(generatedPartitions, "odm")
- }
- if partitionVars.BuildingSystemDlkmImage {
- generatedPartitions = append(generatedPartitions, "system_dlkm")
- }
-
- return &FsGenState{
- depCandidates: candidates,
- fsDeps: map[string]*multilibDeps{
- // These additional deps are added according to the cuttlefish system image bp.
- "system": {
- "com.android.apex.cts.shim.v1_prebuilt": defaultDepCandidateProps(ctx.Config()),
- "dex_bootjars": defaultDepCandidateProps(ctx.Config()),
- "framework_compatibility_matrix.device.xml": defaultDepCandidateProps(ctx.Config()),
- "idc_data": defaultDepCandidateProps(ctx.Config()),
- "init.environ.rc-soong": defaultDepCandidateProps(ctx.Config()),
- "keychars_data": defaultDepCandidateProps(ctx.Config()),
- "keylayout_data": defaultDepCandidateProps(ctx.Config()),
- "libclang_rt.asan": defaultDepCandidateProps(ctx.Config()),
- "libcompiler_rt": defaultDepCandidateProps(ctx.Config()),
- "libdmabufheap": defaultDepCandidateProps(ctx.Config()),
- "libgsi": defaultDepCandidateProps(ctx.Config()),
- "llndk.libraries.txt": defaultDepCandidateProps(ctx.Config()),
- "logpersist.start": defaultDepCandidateProps(ctx.Config()),
- "preloaded-classes": defaultDepCandidateProps(ctx.Config()),
- "public.libraries.android.txt": defaultDepCandidateProps(ctx.Config()),
- "update_engine_sideload": defaultDepCandidateProps(ctx.Config()),
- },
- "vendor": {
- "fs_config_files_vendor": defaultDepCandidateProps(ctx.Config()),
- "fs_config_dirs_vendor": defaultDepCandidateProps(ctx.Config()),
- generatedModuleName(ctx.Config(), "vendor-build.prop"): defaultDepCandidateProps(ctx.Config()),
- },
- "odm": {
- // fs_config_* files are automatically installed for all products with odm partitions.
- // https://cs.android.com/android/_/android/platform/build/+/e4849e87ab660b59a6501b3928693db065ee873b:tools/fs_config/Android.mk;l=34;drc=8d6481b92c4b4e9b9f31a61545b6862090fcc14b;bpv=1;bpt=0
- "fs_config_files_odm": defaultDepCandidateProps(ctx.Config()),
- "fs_config_dirs_odm": defaultDepCandidateProps(ctx.Config()),
- },
- "product": {},
- "system_ext": {
- // VNDK apexes are automatically included.
- // This hardcoded list will need to be updated if `PRODUCT_EXTRA_VNDK_VERSIONS` is updated.
- // https://cs.android.com/android/_/android/platform/build/+/adba533072b00c53ac0f198c550a3cbd7a00e4cd:core/main.mk;l=984;bpv=1;bpt=0;drc=174db7b179592cf07cbfd2adb0119486fda911e7
- "com.android.vndk.v30": defaultDepCandidateProps(ctx.Config()),
- "com.android.vndk.v31": defaultDepCandidateProps(ctx.Config()),
- "com.android.vndk.v32": defaultDepCandidateProps(ctx.Config()),
- "com.android.vndk.v33": defaultDepCandidateProps(ctx.Config()),
- "com.android.vndk.v34": defaultDepCandidateProps(ctx.Config()),
- },
- "system_dlkm": {},
- },
- soongGeneratedPartitions: generatedPartitions,
- fsDepsMutex: sync.Mutex{},
- moduleToInstallationProps: map[string]installationProperties{},
- }
- }).(*FsGenState)
-}
-
-func checkDepModuleInMultipleNamespaces(mctx android.BottomUpMutatorContext, foundDeps multilibDeps, module string, partitionName string) {
- otherNamespace := mctx.Namespace().Path
- if val, found := foundDeps[module]; found && otherNamespace != "." && !android.InList(val.Namespace, []string{".", otherNamespace}) {
- mctx.ModuleErrorf("found in multiple namespaces(%s and %s) when including in %s partition", val.Namespace, otherNamespace, partitionName)
- }
-}
-
-func appendDepIfAppropriate(mctx android.BottomUpMutatorContext, deps *multilibDeps, installPartition string) {
- checkDepModuleInMultipleNamespaces(mctx, *deps, mctx.Module().Name(), installPartition)
- if _, ok := (*deps)[mctx.Module().Name()]; ok {
- // Prefer the namespace-specific module over the platform module
- if mctx.Namespace().Path != "." {
- (*deps)[mctx.Module().Name()].Namespace = mctx.Namespace().Path
- }
- (*deps)[mctx.Module().Name()].Arch = append((*deps)[mctx.Module().Name()].Arch, mctx.Module().Target().Arch.ArchType)
- } else {
- multilib, _ := mctx.Module().DecodeMultilib(mctx)
- (*deps)[mctx.Module().Name()] = &depCandidateProps{
- Namespace: mctx.Namespace().Path,
- Multilib: multilib,
- Arch: []android.ArchType{mctx.Module().Target().Arch.ArchType},
- }
- }
-}
-
-func collectDepsMutator(mctx android.BottomUpMutatorContext) {
- fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
-
- m := mctx.Module()
- if m.Target().Os.Class == android.Device && slices.Contains(fsGenState.depCandidates, m.Name()) {
- installPartition := m.PartitionTag(mctx.DeviceConfig())
- fsGenState.fsDepsMutex.Lock()
- // Only add the module as dependency when:
- // - its enabled
- // - its namespace is included in PRODUCT_SOONG_NAMESPACES
- if m.Enabled(mctx) && m.ExportedToMake() {
- appendDepIfAppropriate(mctx, fsGenState.fsDeps[installPartition], installPartition)
- }
- fsGenState.fsDepsMutex.Unlock()
- }
- // store the map of module to (required,overrides) even if the module is not in PRODUCT_PACKAGES.
- // the module might be installed transitively.
- if m.Target().Os.Class == android.Device && m.Enabled(mctx) && m.ExportedToMake() {
- fsGenState.fsDepsMutex.Lock()
- fsGenState.moduleToInstallationProps[m.Name()] = installationProperties{
- Required: m.RequiredModuleNames(mctx),
- Overrides: m.Overrides(),
- }
- fsGenState.fsDepsMutex.Unlock()
- }
-}
-
-type depsStruct struct {
- Deps []string
-}
-
-type multilibDepsStruct struct {
- Common depsStruct
- Lib32 depsStruct
- Lib64 depsStruct
- Both depsStruct
- Prefer32 depsStruct
-}
-
-type packagingPropsStruct struct {
- High_priority_deps []string
- Deps []string
- Multilib multilibDepsStruct
-}
-
-func fullyQualifiedModuleName(moduleName, namespace string) string {
- if namespace == "." {
- return moduleName
- }
- return fmt.Sprintf("//%s:%s", namespace, moduleName)
-}
-
-func getBitness(archTypes []android.ArchType) (ret []string) {
- for _, archType := range archTypes {
- if archType.Multilib == "" {
- ret = append(ret, android.COMMON_VARIANT)
- } else {
- ret = append(ret, archType.Bitness())
- }
- }
- return ret
-}
-
-func setDepsMutator(mctx android.BottomUpMutatorContext) {
- removeOverriddenDeps(mctx)
- fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
- fsDeps := fsGenState.fsDeps
- soongGeneratedPartitionMap := getAllSoongGeneratedPartitionNames(mctx.Config(), fsGenState.soongGeneratedPartitions)
- m := mctx.Module()
- if partition, ok := soongGeneratedPartitionMap[m.Name()]; ok {
- depsStruct := generateDepStruct(*fsDeps[partition])
- if err := proptools.AppendMatchingProperties(m.GetProperties(), depsStruct, nil); err != nil {
- mctx.ModuleErrorf(err.Error())
- }
- }
-}
-
-// removeOverriddenDeps collects PRODUCT_PACKAGES and (transitive) required deps.
-// it then removes any modules which appear in `overrides` of the above list.
-func removeOverriddenDeps(mctx android.BottomUpMutatorContext) {
- mctx.Config().Once(fsGenRemoveOverridesOnceKey, func() interface{} {
- fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
- fsDeps := fsGenState.fsDeps
- overridden := map[string]bool{}
- allDeps := []string{}
-
- // Step 1: Initialization: Append PRODUCT_PACKAGES to the queue
- for _, fsDep := range fsDeps {
- for depName, _ := range *fsDep {
- allDeps = append(allDeps, depName)
- }
- }
-
- // Step 2: Process the queue, and add required modules to the queue.
- i := 0
- for {
- if i == len(allDeps) {
- break
- }
- depName := allDeps[i]
- for _, overrides := range fsGenState.moduleToInstallationProps[depName].Overrides {
- overridden[overrides] = true
- }
- // add required dep to the queue.
- allDeps = append(allDeps, fsGenState.moduleToInstallationProps[depName].Required...)
- i += 1
- }
-
- // Step 3: Delete all the overridden modules.
- for overridden, _ := range overridden {
- for partition, _ := range fsDeps {
- delete(*fsDeps[partition], overridden)
- }
- }
- return nil
- })
-}
-
-var HighPriorityDeps = []string{}
-
-func generateDepStruct(deps map[string]*depCandidateProps) *packagingPropsStruct {
- depsStruct := packagingPropsStruct{}
- for depName, depProps := range deps {
- bitness := getBitness(depProps.Arch)
- fullyQualifiedDepName := fullyQualifiedModuleName(depName, depProps.Namespace)
- if android.InList(depName, HighPriorityDeps) {
- depsStruct.High_priority_deps = append(depsStruct.High_priority_deps, fullyQualifiedDepName)
- } else if android.InList("32", bitness) && android.InList("64", bitness) {
- // If both 32 and 64 bit variants are enabled for this module
- switch depProps.Multilib {
- case string(android.MultilibBoth):
- depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
- case string(android.MultilibCommon), string(android.MultilibFirst):
- depsStruct.Deps = append(depsStruct.Deps, fullyQualifiedDepName)
- case "32":
- depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
- case "64", "darwin_universal":
- depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
- case "prefer32", "first_prefer32":
- depsStruct.Multilib.Prefer32.Deps = append(depsStruct.Multilib.Prefer32.Deps, fullyQualifiedDepName)
- default:
- depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
- }
- } else if android.InList("64", bitness) {
- // If only 64 bit variant is enabled
- depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
- } else if android.InList("32", bitness) {
- // If only 32 bit variant is enabled
- depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
- } else {
- // If only common variant is enabled
- depsStruct.Multilib.Common.Deps = append(depsStruct.Multilib.Common.Deps, fullyQualifiedDepName)
- }
- }
- depsStruct.Deps = android.SortedUniqueStrings(depsStruct.Deps)
- depsStruct.Multilib.Lib32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib32.Deps)
- depsStruct.Multilib.Lib64.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib64.Deps)
- depsStruct.Multilib.Prefer32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Prefer32.Deps)
- depsStruct.Multilib.Both.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Both.Deps)
- depsStruct.Multilib.Common.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Common.Deps)
-
- return &depsStruct
-}
-
type filesystemCreatorProps struct {
Generated_partition_types []string `blueprint:"mutated"`
Unsupported_partition_types []string `blueprint:"mutated"`
+
+ Vbmeta_module_names []string `blueprint:"mutated"`
+ Vbmeta_partition_names []string `blueprint:"mutated"`
}
type filesystemCreator struct {
@@ -379,7 +61,8 @@
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
module.AddProperties(&module.properties)
android.AddLoadHook(module, func(ctx android.LoadHookContext) {
- createFsGenState(ctx)
+ generatedPrebuiltEtcModuleNames := createPrebuiltEtcModules(ctx)
+ createFsGenState(ctx, generatedPrebuiltEtcModuleNames)
module.createInternalModules(ctx)
})
@@ -387,16 +70,24 @@
}
func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) {
- soongGeneratedPartitions := &ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions
- for _, partitionType := range *soongGeneratedPartitions {
+ soongGeneratedPartitions := generatedPartitions(ctx)
+ finalSoongGeneratedPartitions := make([]string, 0, len(soongGeneratedPartitions))
+ for _, partitionType := range soongGeneratedPartitions {
if f.createPartition(ctx, partitionType) {
f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType)
+ finalSoongGeneratedPartitions = append(finalSoongGeneratedPartitions, partitionType)
} else {
f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType)
- _, *soongGeneratedPartitions = android.RemoveFromList(partitionType, *soongGeneratedPartitions)
}
}
- f.createDeviceModule(ctx)
+
+ for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) {
+ f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName)
+ f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName)
+ }
+
+ ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions
+ f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names)
}
func generatedModuleName(cfg android.Config, suffix string) string {
@@ -411,7 +102,11 @@
return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType))
}
-func (f *filesystemCreator) createDeviceModule(ctx android.LoadHookContext) {
+func (f *filesystemCreator) createDeviceModule(
+ ctx android.LoadHookContext,
+ generatedPartitionTypes []string,
+ vbmetaPartitions []string,
+) {
baseProps := &struct {
Name *string
}{
@@ -420,21 +115,25 @@
// Currently, only the system and system_ext partition module is created.
partitionProps := &filesystem.PartitionNameProperties{}
- if android.InList("system", f.properties.Generated_partition_types) {
+ if android.InList("system", generatedPartitionTypes) {
partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system"))
}
- if android.InList("system_ext", f.properties.Generated_partition_types) {
+ if android.InList("system_ext", generatedPartitionTypes) {
partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext"))
}
- if android.InList("vendor", f.properties.Generated_partition_types) {
+ if android.InList("vendor", generatedPartitionTypes) {
partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor"))
}
- if android.InList("product", f.properties.Generated_partition_types) {
+ if android.InList("product", generatedPartitionTypes) {
partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product"))
}
- if android.InList("odm", f.properties.Generated_partition_types) {
+ if android.InList("odm", generatedPartitionTypes) {
partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm"))
}
+ if android.InList("userdata", f.properties.Generated_partition_types) {
+ partitionProps.Userdata_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "userdata"))
+ }
+ partitionProps.Vbmeta_partitions = vbmetaPartitions
ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps)
}
@@ -456,6 +155,26 @@
"framework/oat/*/*", // framework/oat/{arch}
}
fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"}
+ // TODO(b/377734331): only generate the symlinks if the relevant partitions exist
+ fsProps.Symlinks = []filesystem.SymlinkDefinition{
+ filesystem.SymlinkDefinition{
+ Target: proptools.StringPtr("/product"),
+ Name: proptools.StringPtr("system/product"),
+ },
+ filesystem.SymlinkDefinition{
+ Target: proptools.StringPtr("/system_ext"),
+ Name: proptools.StringPtr("system/system_ext"),
+ },
+ filesystem.SymlinkDefinition{
+ Target: proptools.StringPtr("/vendor"),
+ Name: proptools.StringPtr("system/vendor"),
+ },
+ filesystem.SymlinkDefinition{
+ Target: proptools.StringPtr("/system_dlkm/lib/modules"),
+ Name: proptools.StringPtr("system/lib/modules"),
+ },
+ }
+ fsProps.Base_dir = proptools.StringPtr("system")
case "system_ext":
fsProps.Fsverity.Inputs = []string{
"framework/*",
@@ -486,10 +205,20 @@
},
}
fsProps.Base_dir = proptools.StringPtr("odm")
+ case "userdata":
+ fsProps.Base_dir = proptools.StringPtr("data")
}
}
+var (
+ dlkmPartitions = []string{
+ "system_dlkm",
+ "vendor_dlkm",
+ "odm_dlkm",
+ }
+)
+
// Creates a soong module to build the given partition. Returns false if we can't support building
// it.
func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partitionType string) bool {
@@ -501,23 +230,17 @@
}
if partitionType == "vendor" || partitionType == "product" {
- fsProps.Linkerconfig.Gen_linker_config = proptools.BoolPtr(true)
- fsProps.Linkerconfig.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType)
+ fsProps.Linker_config.Gen_linker_config = proptools.BoolPtr(true)
+ fsProps.Linker_config.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType)
}
- if partitionType == "system_dlkm" {
- kernelModules := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules
- f.createPrebuiltKernelModules(ctx, partitionType, kernelModules)
+ if android.InList(partitionType, dlkmPartitions) {
+ f.createPrebuiltKernelModules(ctx, partitionType)
}
var module android.Module
if partitionType == "system" {
module = ctx.CreateModule(filesystem.SystemImageFactory, baseProps, fsProps)
- } else if partitionType == "system_dlkm" {
- // Do not set partition_type. build/soong/android/paths#modulePartition currently does not support dlkm
- // partitions. Since `android_filesystem` uses a partition based filter, setting the partition here
- // would result in missing in entries.
- module = ctx.CreateModule(filesystem.FilesystemFactory, baseProps, fsProps)
} else {
// Explicitly set the partition.
fsProps.Partition_type = proptools.StringPtr(partitionType)
@@ -525,60 +248,116 @@
}
module.HideFromMake()
if partitionType == "vendor" {
- // Create a build prop for vendor
- vendorBuildProps := &struct {
- Name *string
- Vendor *bool
- Stem *string
- Product_config *string
- }{
- Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
- Vendor: proptools.BoolPtr(true),
- Stem: proptools.StringPtr("build.prop"),
- Product_config: proptools.StringPtr(":product_config"),
- }
- vendorBuildProp := ctx.CreateModule(
- android.BuildPropFactory,
- vendorBuildProps,
- )
- vendorBuildProp.HideFromMake()
+ f.createVendorBuildProp(ctx)
}
return true
}
// createPrebuiltKernelModules creates `prebuilt_kernel_modules`. These modules will be added to deps of the
-// autogenerated *_dlkm filsystem modules.
-// The input `kernelModules` is a space separated list of .ko files in the workspace. This will be partitioned per directory
-// and a `prebuilt_kernel_modules` will be created per partition.
-// These autogenerated modules will be subsequently added to the deps of the top level *_dlkm android_filesystem
-func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string, kernelModules []string) {
- // Partition the files per directory
- dirToFiles := map[string][]string{}
- for _, kernelModule := range kernelModules {
- dir := filepath.Dir(kernelModule)
- base := filepath.Base(kernelModule)
- dirToFiles[dir] = append(dirToFiles[dir], base)
- }
- // Create a prebuilt_kernel_modules module per partition
+// autogenerated *_dlkm filsystem modules. Each _dlkm partition should have a single prebuilt_kernel_modules dependency.
+// This ensures that the depmod artifacts (modules.* installed in /lib/modules/) are generated with a complete view.
+func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string) {
fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
- for index, dir := range android.SortedKeys(dirToFiles) {
- name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules-%s", partitionType, strconv.Itoa(index)))
- props := &struct {
- Name *string
- Srcs []string
- }{
- Name: proptools.StringPtr(name),
- Srcs: dirToFiles[dir],
- }
- kernelModule := ctx.CreateModuleInDirectory(
- kernel.PrebuiltKernelModulesFactory,
- dir,
- props,
- )
- kernelModule.HideFromMake()
- // Add to deps
- (*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
+ name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules", partitionType))
+ props := &struct {
+ Name *string
+ Srcs []string
+ System_deps []string
+ System_dlkm_specific *bool
+ Vendor_dlkm_specific *bool
+ Odm_dlkm_specific *bool
+ Load_by_default *bool
+ Blocklist_file *string
+ }{
+ Name: proptools.StringPtr(name),
}
+ switch partitionType {
+ case "system_dlkm":
+ props.Srcs = ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules
+ props.System_dlkm_specific = proptools.BoolPtr(true)
+ if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelLoadModules) == 0 {
+ // Create empty modules.load file for system
+ // https://source.corp.google.com/h/googleplex-android/platform/build/+/ef55daac9954896161b26db4f3ef1781b5a5694c:core/Makefile;l=695-700;drc=549fe2a5162548bd8b47867d35f907eb22332023;bpv=1;bpt=0
+ props.Load_by_default = proptools.BoolPtr(false)
+ }
+ if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelBlocklistFile; blocklistFile != "" {
+ props.Blocklist_file = proptools.StringPtr(blocklistFile)
+ }
+ case "vendor_dlkm":
+ props.Srcs = ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelModules
+ if len(ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.SystemKernelModules) > 0 {
+ props.System_deps = []string{":" + generatedModuleName(ctx.Config(), "system_dlkm-kernel-modules") + "{.modules}"}
+ }
+ props.Vendor_dlkm_specific = proptools.BoolPtr(true)
+ if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.VendorKernelBlocklistFile; blocklistFile != "" {
+ props.Blocklist_file = proptools.StringPtr(blocklistFile)
+ }
+ case "odm_dlkm":
+ props.Srcs = ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelModules
+ props.Odm_dlkm_specific = proptools.BoolPtr(true)
+ if blocklistFile := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.OdmKernelBlocklistFile; blocklistFile != "" {
+ props.Blocklist_file = proptools.StringPtr(blocklistFile)
+ }
+ default:
+ ctx.ModuleErrorf("DLKM is not supported for %s\n", partitionType)
+ }
+
+ if len(props.Srcs) == 0 {
+ return // do not generate `prebuilt_kernel_modules` if there are no sources
+ }
+
+ kernelModule := ctx.CreateModuleInDirectory(
+ kernel.PrebuiltKernelModulesFactory,
+ ".", // create in root directory for now
+ props,
+ )
+ kernelModule.HideFromMake()
+ // Add to deps
+ (*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config())
+}
+
+// Create a build_prop and android_info module. This will be used to create /vendor/build.prop
+func (f *filesystemCreator) createVendorBuildProp(ctx android.LoadHookContext) {
+ // Create a android_info for vendor
+ // The board info files might be in a directory outside the root soong namespace, so create
+ // the module in "."
+ partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+ androidInfoProps := &struct {
+ Name *string
+ Board_info_files []string
+ Bootloader_board_name *string
+ }{
+ Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "android-info.prop")),
+ Board_info_files: partitionVars.BoardInfoFiles,
+ }
+ if len(androidInfoProps.Board_info_files) == 0 {
+ androidInfoProps.Bootloader_board_name = proptools.StringPtr(partitionVars.BootLoaderBoardName)
+ }
+ androidInfoProp := ctx.CreateModuleInDirectory(
+ android.AndroidInfoFactory,
+ ".",
+ androidInfoProps,
+ )
+ androidInfoProp.HideFromMake()
+ // Create a build prop for vendor
+ vendorBuildProps := &struct {
+ Name *string
+ Vendor *bool
+ Stem *string
+ Product_config *string
+ Android_info *string
+ }{
+ Name: proptools.StringPtr(generatedModuleName(ctx.Config(), "vendor-build.prop")),
+ Vendor: proptools.BoolPtr(true),
+ Stem: proptools.StringPtr("build.prop"),
+ Product_config: proptools.StringPtr(":product_config"),
+ Android_info: proptools.StringPtr(":" + androidInfoProp.Name()),
+ }
+ vendorBuildProp := ctx.CreateModule(
+ android.BuildPropFactory,
+ vendorBuildProps,
+ )
+ vendorBuildProp.HideFromMake()
}
// createLinkerConfigSourceFilegroups creates filegroup modules to generate linker.config.pb for the following partitions
@@ -626,12 +405,15 @@
type filesystemBaseProperty struct {
Name *string
Compile_multilib *string
+ Visibility []string
}
func generateBaseProps(namePtr *string) *filesystemBaseProperty {
return &filesystemBaseProperty{
Name: namePtr,
Compile_multilib: proptools.StringPtr("both"),
+ // The vbmeta modules are currently in the root directory and depend on the partitions
+ Visibility: []string{"//.", "//build/soong:__subpackages__"},
}
}
@@ -639,13 +421,20 @@
fsProps := &filesystem.FilesystemProperties{}
partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
- specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType]
-
- // BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE
- fsType := specificPartitionVars.BoardFileSystemType
+ var specificPartitionVars android.PartitionQualifiedVariablesType
+ var boardAvbEnable bool
+ var fsType string
+ if strings.Contains(partitionType, "ramdisk") {
+ fsType = "compressed_cpio"
+ } else {
+ specificPartitionVars = partitionVars.PartitionQualifiedVariables[partitionType]
+ boardAvbEnable = partitionVars.BoardAvbEnable
+ fsType = specificPartitionVars.BoardFileSystemType
+ }
if fsType == "" {
fsType = "ext4" //default
}
+
fsProps.Type = proptools.StringPtr(fsType)
if filesystem.GetFsTypeFromString(ctx, *fsProps.Type).IsUnknown() {
// Currently the android_filesystem module type only supports a handful of FS types like ext4, erofs
@@ -657,7 +446,7 @@
fsProps.Unchecked_module = proptools.BoolPtr(true)
// BOARD_AVB_ENABLE
- fsProps.Use_avb = proptools.BoolPtr(partitionVars.BoardAvbEnable)
+ fsProps.Use_avb = proptools.BoolPtr(boardAvbEnable)
// BOARD_AVB_KEY_PATH
fsProps.Avb_private_key = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath)
// BOARD_AVB_ALGORITHM
@@ -698,18 +487,13 @@
ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName)
}
makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType))
- // For now, don't allowlist anything. The test will fail, but that's fine in the current
- // early stages where we're just figuring out what we need
- emptyAllowlistFile := android.PathForModuleOut(ctx, fmt.Sprintf("allowlist_%s.txt", partitionModuleName))
- android.WriteFileRule(ctx, emptyAllowlistFile, "")
diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName))
builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().BuiltTool("file_list_diff").
Input(makeFileList).
Input(filesystemInfo.FileListFile).
- Text(partitionModuleName).
- FlagWithInput("--allowlists ", emptyAllowlistFile)
+ Text(partitionModuleName)
builder.Command().Text("touch").Output(diffTestResultFile)
builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test")
return diffTestResultFile
@@ -727,16 +511,42 @@
return file
}
+func createVbmetaDiff(ctx android.ModuleContext, vbmetaModuleName string, vbmetaPartitionName string) android.Path {
+ vbmetaModule := ctx.GetDirectDepWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag)
+ outputFilesProvider, ok := android.OtherModuleProvider(ctx, vbmetaModule, android.OutputFilesProvider)
+ if !ok {
+ ctx.ModuleErrorf("Expected module %s to provide OutputFiles", vbmetaModule)
+ }
+ if len(outputFilesProvider.DefaultOutputFiles) != 1 {
+ ctx.ModuleErrorf("Expected 1 output file from module %s", vbmetaModule)
+ }
+ soongVbMetaFile := outputFilesProvider.DefaultOutputFiles[0]
+ makeVbmetaFile := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/%s.img", ctx.Config().DeviceName(), vbmetaPartitionName))
+
+ diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", vbmetaModuleName))
+ builder := android.NewRuleBuilder(pctx, ctx)
+ builder.Command().Text("diff").
+ Input(soongVbMetaFile).
+ Input(makeVbmetaFile)
+ builder.Command().Text("touch").Output(diffTestResultFile)
+ builder.Build(vbmetaModuleName+" diff test", vbmetaModuleName+" diff test")
+ return diffTestResultFile
+}
+
type systemImageDepTagType struct {
blueprint.BaseDependencyTag
}
var generatedFilesystemDepTag systemImageDepTagType
+var generatedVbmetaPartitionDepTag systemImageDepTagType
func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) {
for _, partitionType := range f.properties.Generated_partition_types {
ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType))
}
+ for _, vbmetaModule := range f.properties.Vbmeta_module_names {
+ ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule)
+ }
}
func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -766,6 +576,11 @@
diffTestFiles = append(diffTestFiles, diffTestFile)
ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile)
}
+ for i, vbmetaModule := range f.properties.Vbmeta_module_names {
+ diffTestFile := createVbmetaDiff(ctx, vbmetaModule, f.properties.Vbmeta_partition_names[i])
+ diffTestFiles = append(diffTestFiles, diffTestFile)
+ ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", f.properties.Vbmeta_partition_names[i]), diffTestFile)
+ }
ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...)
}
@@ -774,9 +589,6 @@
if !fsTypeSupported {
return ""
}
- if partitionType == "vendor" || partitionType == "odm" {
- return "" // TODO: Handle struct props
- }
baseProps := generateBaseProps(proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), partitionType)))
deps := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).fsDeps[partitionType]
@@ -784,7 +596,8 @@
result, err := proptools.RepackProperties([]interface{}{baseProps, fsProps, depProps})
if err != nil {
- ctx.ModuleErrorf(err.Error())
+ ctx.ModuleErrorf("%s", err.Error())
+ return ""
}
moduleType := "android_filesystem"
diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go
new file mode 100644
index 0000000..e9fd513
--- /dev/null
+++ b/fsgen/fsgen_mutators.go
@@ -0,0 +1,382 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+ "fmt"
+ "slices"
+ "strings"
+ "sync"
+
+ "android/soong/android"
+
+ "github.com/google/blueprint/proptools"
+)
+
+func RegisterCollectFileSystemDepsMutators(ctx android.RegisterMutatorsContext) {
+ ctx.BottomUp("fs_collect_deps", collectDepsMutator).MutatesGlobalState()
+ ctx.BottomUp("fs_set_deps", setDepsMutator)
+}
+
+var fsGenStateOnceKey = android.NewOnceKey("FsGenState")
+var fsGenRemoveOverridesOnceKey = android.NewOnceKey("FsGenRemoveOverrides")
+
+// Map of partition module name to its partition that may be generated by Soong.
+// Note that it is not guaranteed that all modules returned by this function are successfully
+// created.
+func getAllSoongGeneratedPartitionNames(config android.Config, partitions []string) map[string]string {
+ ret := map[string]string{}
+ for _, partition := range partitions {
+ ret[generatedModuleNameForPartition(config, partition)] = partition
+ }
+ return ret
+}
+
+type depCandidateProps struct {
+ Namespace string
+ Multilib string
+ Arch []android.ArchType
+}
+
+// Map of module name to depCandidateProps
+type multilibDeps map[string]*depCandidateProps
+
+// Information necessary to generate the filesystem modules, including details about their
+// dependencies
+type FsGenState struct {
+ // List of modules in `PRODUCT_PACKAGES` and `PRODUCT_PACKAGES_DEBUG`
+ depCandidates []string
+ // Map of names of partition to the information of modules to be added as deps
+ fsDeps map[string]*multilibDeps
+ // List of name of partitions to be generated by the filesystem_creator module
+ soongGeneratedPartitions []string
+ // Mutex to protect the fsDeps
+ fsDepsMutex sync.Mutex
+ // Map of _all_ soong module names to their corresponding installation properties
+ moduleToInstallationProps map[string]installationProperties
+}
+
+type installationProperties struct {
+ Required []string
+ Overrides []string
+}
+
+func defaultDepCandidateProps(config android.Config) *depCandidateProps {
+ return &depCandidateProps{
+ Namespace: ".",
+ Arch: []android.ArchType{config.BuildArch},
+ }
+}
+
+func generatedPartitions(ctx android.LoadHookContext) []string {
+ generatedPartitions := []string{"system", "ramdisk"}
+ if ctx.DeviceConfig().SystemExtPath() == "system_ext" {
+ generatedPartitions = append(generatedPartitions, "system_ext")
+ }
+ if ctx.DeviceConfig().BuildingVendorImage() && ctx.DeviceConfig().VendorPath() == "vendor" {
+ generatedPartitions = append(generatedPartitions, "vendor")
+ }
+ if ctx.DeviceConfig().BuildingProductImage() && ctx.DeviceConfig().ProductPath() == "product" {
+ generatedPartitions = append(generatedPartitions, "product")
+ }
+ if ctx.DeviceConfig().BuildingOdmImage() && ctx.DeviceConfig().OdmPath() == "odm" {
+ generatedPartitions = append(generatedPartitions, "odm")
+ }
+ if ctx.DeviceConfig().BuildingUserdataImage() && ctx.DeviceConfig().UserdataPath() == "data" {
+ generatedPartitions = append(generatedPartitions, "userdata")
+ }
+ if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingSystemDlkmImage {
+ generatedPartitions = append(generatedPartitions, "system_dlkm")
+ }
+ if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingVendorDlkmImage {
+ generatedPartitions = append(generatedPartitions, "vendor_dlkm")
+ }
+ if ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingOdmDlkmImage {
+ generatedPartitions = append(generatedPartitions, "odm_dlkm")
+ }
+
+ return generatedPartitions
+}
+
+func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNames []string) *FsGenState {
+ return ctx.Config().Once(fsGenStateOnceKey, func() interface{} {
+ partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+ candidates := android.FirstUniqueStrings(android.Concat(partitionVars.ProductPackages, partitionVars.ProductPackagesDebug))
+ candidates = android.Concat(candidates, generatedPrebuiltEtcModuleNames)
+
+ return &FsGenState{
+ depCandidates: candidates,
+ fsDeps: map[string]*multilibDeps{
+ // These additional deps are added according to the cuttlefish system image bp.
+ "system": {
+ "com.android.apex.cts.shim.v1_prebuilt": defaultDepCandidateProps(ctx.Config()),
+ "dex_bootjars": defaultDepCandidateProps(ctx.Config()),
+ "framework_compatibility_matrix.device.xml": defaultDepCandidateProps(ctx.Config()),
+ "init.environ.rc-soong": defaultDepCandidateProps(ctx.Config()),
+ "libcompiler_rt": defaultDepCandidateProps(ctx.Config()),
+ "libdmabufheap": defaultDepCandidateProps(ctx.Config()),
+ "libgsi": defaultDepCandidateProps(ctx.Config()),
+ "llndk.libraries.txt": defaultDepCandidateProps(ctx.Config()),
+ "logpersist.start": defaultDepCandidateProps(ctx.Config()),
+ "update_engine_sideload": defaultDepCandidateProps(ctx.Config()),
+ },
+ "vendor": {
+ "fs_config_files_vendor": defaultDepCandidateProps(ctx.Config()),
+ "fs_config_dirs_vendor": defaultDepCandidateProps(ctx.Config()),
+ generatedModuleName(ctx.Config(), "vendor-build.prop"): defaultDepCandidateProps(ctx.Config()),
+ },
+ "odm": {
+ // fs_config_* files are automatically installed for all products with odm partitions.
+ // https://cs.android.com/android/_/android/platform/build/+/e4849e87ab660b59a6501b3928693db065ee873b:tools/fs_config/Android.mk;l=34;drc=8d6481b92c4b4e9b9f31a61545b6862090fcc14b;bpv=1;bpt=0
+ "fs_config_files_odm": defaultDepCandidateProps(ctx.Config()),
+ "fs_config_dirs_odm": defaultDepCandidateProps(ctx.Config()),
+ },
+ "product": {},
+ "system_ext": {
+ // VNDK apexes are automatically included.
+ // This hardcoded list will need to be updated if `PRODUCT_EXTRA_VNDK_VERSIONS` is updated.
+ // https://cs.android.com/android/_/android/platform/build/+/adba533072b00c53ac0f198c550a3cbd7a00e4cd:core/main.mk;l=984;bpv=1;bpt=0;drc=174db7b179592cf07cbfd2adb0119486fda911e7
+ "com.android.vndk.v30": defaultDepCandidateProps(ctx.Config()),
+ "com.android.vndk.v31": defaultDepCandidateProps(ctx.Config()),
+ "com.android.vndk.v32": defaultDepCandidateProps(ctx.Config()),
+ "com.android.vndk.v33": defaultDepCandidateProps(ctx.Config()),
+ "com.android.vndk.v34": defaultDepCandidateProps(ctx.Config()),
+ },
+ "userdata": {},
+ "system_dlkm": {
+ // these are phony required deps of the phony fs_config_dirs_nonsystem
+ "fs_config_dirs_system_dlkm": defaultDepCandidateProps(ctx.Config()),
+ "fs_config_files_system_dlkm": defaultDepCandidateProps(ctx.Config()),
+ // build props are automatically added to `ALL_DEFAULT_INSTALLED_MODULES`
+ "system_dlkm-build.prop": defaultDepCandidateProps(ctx.Config()),
+ },
+ "vendor_dlkm": {
+ "fs_config_dirs_vendor_dlkm": defaultDepCandidateProps(ctx.Config()),
+ "fs_config_files_vendor_dlkm": defaultDepCandidateProps(ctx.Config()),
+ "vendor_dlkm-build.prop": defaultDepCandidateProps(ctx.Config()),
+ },
+ "odm_dlkm": {
+ "fs_config_dirs_odm_dlkm": defaultDepCandidateProps(ctx.Config()),
+ "fs_config_files_odm_dlkm": defaultDepCandidateProps(ctx.Config()),
+ "odm_dlkm-build.prop": defaultDepCandidateProps(ctx.Config()),
+ },
+ "ramdisk": {},
+ },
+ fsDepsMutex: sync.Mutex{},
+ moduleToInstallationProps: map[string]installationProperties{},
+ }
+ }).(*FsGenState)
+}
+
+func checkDepModuleInMultipleNamespaces(mctx android.BottomUpMutatorContext, foundDeps multilibDeps, module string, partitionName string) {
+ otherNamespace := mctx.Namespace().Path
+ if val, found := foundDeps[module]; found && otherNamespace != "." && !android.InList(val.Namespace, []string{".", otherNamespace}) {
+ mctx.ModuleErrorf("found in multiple namespaces(%s and %s) when including in %s partition", val.Namespace, otherNamespace, partitionName)
+ }
+}
+
+func appendDepIfAppropriate(mctx android.BottomUpMutatorContext, deps *multilibDeps, installPartition string) {
+ moduleName := mctx.ModuleName()
+ checkDepModuleInMultipleNamespaces(mctx, *deps, moduleName, installPartition)
+ if _, ok := (*deps)[moduleName]; ok {
+ // Prefer the namespace-specific module over the platform module
+ if mctx.Namespace().Path != "." {
+ (*deps)[moduleName].Namespace = mctx.Namespace().Path
+ }
+ (*deps)[moduleName].Arch = append((*deps)[moduleName].Arch, mctx.Module().Target().Arch.ArchType)
+ } else {
+ multilib, _ := mctx.Module().DecodeMultilib(mctx)
+ (*deps)[moduleName] = &depCandidateProps{
+ Namespace: mctx.Namespace().Path,
+ Multilib: multilib,
+ Arch: []android.ArchType{mctx.Module().Target().Arch.ArchType},
+ }
+ }
+}
+
+func collectDepsMutator(mctx android.BottomUpMutatorContext) {
+ m := mctx.Module()
+ if m.Target().Os.Class != android.Device {
+ return
+ }
+ fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+
+ fsGenState.fsDepsMutex.Lock()
+ defer fsGenState.fsDepsMutex.Unlock()
+
+ if slices.Contains(fsGenState.depCandidates, mctx.ModuleName()) {
+ installPartition := m.PartitionTag(mctx.DeviceConfig())
+ // Only add the module as dependency when:
+ // - its enabled
+ // - its namespace is included in PRODUCT_SOONG_NAMESPACES
+ if m.Enabled(mctx) && m.ExportedToMake() {
+ appendDepIfAppropriate(mctx, fsGenState.fsDeps[installPartition], installPartition)
+ }
+ }
+ // store the map of module to (required,overrides) even if the module is not in PRODUCT_PACKAGES.
+ // the module might be installed transitively.
+ if m.Enabled(mctx) && m.ExportedToMake() {
+ fsGenState.moduleToInstallationProps[m.Name()] = installationProperties{
+ Required: m.RequiredModuleNames(mctx),
+ Overrides: m.Overrides(),
+ }
+ }
+}
+
+type depsStruct struct {
+ Deps []string
+}
+
+type multilibDepsStruct struct {
+ Common depsStruct
+ Lib32 depsStruct
+ Lib64 depsStruct
+ Both depsStruct
+ Prefer32 depsStruct
+}
+
+type packagingPropsStruct struct {
+ High_priority_deps []string
+ Deps []string
+ Multilib multilibDepsStruct
+}
+
+func fullyQualifiedModuleName(moduleName, namespace string) string {
+ if namespace == "." {
+ return moduleName
+ }
+ return fmt.Sprintf("//%s:%s", namespace, moduleName)
+}
+
+func getBitness(archTypes []android.ArchType) (ret []string) {
+ for _, archType := range archTypes {
+ if archType.Multilib == "" {
+ ret = append(ret, android.COMMON_VARIANT)
+ } else {
+ ret = append(ret, archType.Bitness())
+ }
+ }
+ return ret
+}
+
+func setDepsMutator(mctx android.BottomUpMutatorContext) {
+ removeOverriddenDeps(mctx)
+ fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+ fsDeps := fsGenState.fsDeps
+ soongGeneratedPartitionMap := getAllSoongGeneratedPartitionNames(mctx.Config(), fsGenState.soongGeneratedPartitions)
+ m := mctx.Module()
+ if partition, ok := soongGeneratedPartitionMap[m.Name()]; ok {
+ depsStruct := generateDepStruct(*fsDeps[partition])
+ if err := proptools.AppendMatchingProperties(m.GetProperties(), depsStruct, nil); err != nil {
+ mctx.ModuleErrorf(err.Error())
+ }
+ }
+}
+
+// removeOverriddenDeps collects PRODUCT_PACKAGES and (transitive) required deps.
+// it then removes any modules which appear in `overrides` of the above list.
+func removeOverriddenDeps(mctx android.BottomUpMutatorContext) {
+ mctx.Config().Once(fsGenRemoveOverridesOnceKey, func() interface{} {
+ fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState)
+ fsDeps := fsGenState.fsDeps
+ overridden := map[string]bool{}
+ allDeps := []string{}
+
+ // Step 1: Initialization: Append PRODUCT_PACKAGES to the queue
+ for _, fsDep := range fsDeps {
+ for depName, _ := range *fsDep {
+ allDeps = append(allDeps, depName)
+ }
+ }
+
+ // Step 2: Process the queue, and add required modules to the queue.
+ i := 0
+ for {
+ if i == len(allDeps) {
+ break
+ }
+ depName := allDeps[i]
+ for _, overrides := range fsGenState.moduleToInstallationProps[depName].Overrides {
+ overridden[overrides] = true
+ }
+ // add required dep to the queue.
+ allDeps = append(allDeps, fsGenState.moduleToInstallationProps[depName].Required...)
+ i += 1
+ }
+
+ // Step 3: Delete all the overridden modules.
+ for overridden, _ := range overridden {
+ for partition, _ := range fsDeps {
+ delete(*fsDeps[partition], overridden)
+ }
+ }
+ return nil
+ })
+}
+
+var HighPriorityDeps = []string{}
+
+func isHighPriorityDep(depName string) bool {
+ for _, highPriorityDeps := range HighPriorityDeps {
+ if strings.HasPrefix(depName, highPriorityDeps) {
+ return true
+ }
+ }
+ return false
+}
+
+func generateDepStruct(deps map[string]*depCandidateProps) *packagingPropsStruct {
+ depsStruct := packagingPropsStruct{}
+ for depName, depProps := range deps {
+ bitness := getBitness(depProps.Arch)
+ fullyQualifiedDepName := fullyQualifiedModuleName(depName, depProps.Namespace)
+ if isHighPriorityDep(depName) {
+ depsStruct.High_priority_deps = append(depsStruct.High_priority_deps, fullyQualifiedDepName)
+ } else if android.InList("32", bitness) && android.InList("64", bitness) {
+ // If both 32 and 64 bit variants are enabled for this module
+ switch depProps.Multilib {
+ case string(android.MultilibBoth):
+ depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
+ case string(android.MultilibCommon), string(android.MultilibFirst):
+ depsStruct.Deps = append(depsStruct.Deps, fullyQualifiedDepName)
+ case "32":
+ depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
+ case "64", "darwin_universal":
+ depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
+ case "prefer32", "first_prefer32":
+ depsStruct.Multilib.Prefer32.Deps = append(depsStruct.Multilib.Prefer32.Deps, fullyQualifiedDepName)
+ default:
+ depsStruct.Multilib.Both.Deps = append(depsStruct.Multilib.Both.Deps, fullyQualifiedDepName)
+ }
+ } else if android.InList("64", bitness) {
+ // If only 64 bit variant is enabled
+ depsStruct.Multilib.Lib64.Deps = append(depsStruct.Multilib.Lib64.Deps, fullyQualifiedDepName)
+ } else if android.InList("32", bitness) {
+ // If only 32 bit variant is enabled
+ depsStruct.Multilib.Lib32.Deps = append(depsStruct.Multilib.Lib32.Deps, fullyQualifiedDepName)
+ } else {
+ // If only common variant is enabled
+ depsStruct.Multilib.Common.Deps = append(depsStruct.Multilib.Common.Deps, fullyQualifiedDepName)
+ }
+ }
+ depsStruct.Deps = android.SortedUniqueStrings(depsStruct.Deps)
+ depsStruct.Multilib.Lib32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib32.Deps)
+ depsStruct.Multilib.Lib64.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Lib64.Deps)
+ depsStruct.Multilib.Prefer32.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Prefer32.Deps)
+ depsStruct.Multilib.Both.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Both.Deps)
+ depsStruct.Multilib.Common.Deps = android.SortedUniqueStrings(depsStruct.Multilib.Common.Deps)
+
+ return &depsStruct
+}
diff --git a/fsgen/prebuilt_etc_modules_gen.go b/fsgen/prebuilt_etc_modules_gen.go
new file mode 100644
index 0000000..983dcfb
--- /dev/null
+++ b/fsgen/prebuilt_etc_modules_gen.go
@@ -0,0 +1,346 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+ "android/soong/android"
+ "android/soong/etc"
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type srcBaseFileInstallBaseFileTuple struct {
+ srcBaseFile string
+ installBaseFile string
+}
+
+// prebuilt src files grouped by the install partitions.
+// Each groups are a mapping of the relative install path to the name of the files
+type prebuiltSrcGroupByInstallPartition struct {
+ system map[string][]srcBaseFileInstallBaseFileTuple
+ system_ext map[string][]srcBaseFileInstallBaseFileTuple
+ product map[string][]srcBaseFileInstallBaseFileTuple
+ vendor map[string][]srcBaseFileInstallBaseFileTuple
+}
+
+func newPrebuiltSrcGroupByInstallPartition() *prebuiltSrcGroupByInstallPartition {
+ return &prebuiltSrcGroupByInstallPartition{
+ system: map[string][]srcBaseFileInstallBaseFileTuple{},
+ system_ext: map[string][]srcBaseFileInstallBaseFileTuple{},
+ product: map[string][]srcBaseFileInstallBaseFileTuple{},
+ vendor: map[string][]srcBaseFileInstallBaseFileTuple{},
+ }
+}
+
+func isSubdirectory(parent, child string) bool {
+ rel, err := filepath.Rel(parent, child)
+ if err != nil {
+ return false
+ }
+ return !strings.HasPrefix(rel, "..")
+}
+
+func appendIfCorrectInstallPartition(partitionToInstallPathList []partitionToInstallPath, destPath, srcPath string, srcGroup *prebuiltSrcGroupByInstallPartition) {
+ for _, part := range partitionToInstallPathList {
+ partition := part.name
+ installPath := part.installPath
+
+ if isSubdirectory(installPath, destPath) {
+ relativeInstallPath, _ := filepath.Rel(installPath, destPath)
+ relativeInstallDir := filepath.Dir(relativeInstallPath)
+ var srcMap map[string][]srcBaseFileInstallBaseFileTuple
+ switch partition {
+ case "system":
+ srcMap = srcGroup.system
+ case "system_ext":
+ srcMap = srcGroup.system_ext
+ case "product":
+ srcMap = srcGroup.product
+ case "vendor":
+ srcMap = srcGroup.vendor
+ }
+ if srcMap != nil {
+ srcMap[relativeInstallDir] = append(srcMap[relativeInstallDir], srcBaseFileInstallBaseFileTuple{
+ srcBaseFile: filepath.Base(srcPath),
+ installBaseFile: filepath.Base(destPath),
+ })
+ }
+ return
+ }
+ }
+}
+
+// Create a map of source files to the list of destination files from PRODUCT_COPY_FILES entries.
+// Note that the value of the map is a list of string, given that a single source file can be
+// copied to multiple files.
+// This function also checks the existence of the source files, and validates that there is no
+// multiple source files copying to the same dest file.
+func uniqueExistingProductCopyFileMap(ctx android.LoadHookContext) map[string][]string {
+ seen := make(map[string]bool)
+ filtered := make(map[string][]string)
+
+ for _, copyFilePair := range ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ProductCopyFiles {
+ srcDestList := strings.Split(copyFilePair, ":")
+ if len(srcDestList) < 2 {
+ ctx.ModuleErrorf("PRODUCT_COPY_FILES must follow the format \"src:dest\", got: %s", copyFilePair)
+ }
+ src, dest := srcDestList[0], srcDestList[1]
+ if _, ok := seen[dest]; !ok {
+ if optionalPath := android.ExistentPathForSource(ctx, src); optionalPath.Valid() {
+ seen[dest] = true
+ filtered[src] = append(filtered[src], dest)
+ }
+ }
+ }
+
+ return filtered
+}
+
+type partitionToInstallPath struct {
+ name string
+ installPath string
+}
+
+func processProductCopyFiles(ctx android.LoadHookContext) map[string]*prebuiltSrcGroupByInstallPartition {
+ // Filter out duplicate dest entries and non existing src entries
+ productCopyFileMap := uniqueExistingProductCopyFileMap(ctx)
+
+ // System is intentionally added at the last to consider the scenarios where
+ // non-system partitions are installed as part of the system partition
+ partitionToInstallPathList := []partitionToInstallPath{
+ {name: "vendor", installPath: ctx.DeviceConfig().VendorPath()},
+ {name: "product", installPath: ctx.DeviceConfig().ProductPath()},
+ {name: "system_ext", installPath: ctx.DeviceConfig().SystemExtPath()},
+ {name: "system", installPath: "system"},
+ }
+
+ groupedSources := map[string]*prebuiltSrcGroupByInstallPartition{}
+ for _, src := range android.SortedKeys(productCopyFileMap) {
+ destFiles := productCopyFileMap[src]
+ srcFileDir := filepath.Dir(src)
+ if _, ok := groupedSources[srcFileDir]; !ok {
+ groupedSources[srcFileDir] = newPrebuiltSrcGroupByInstallPartition()
+ }
+ for _, dest := range destFiles {
+ appendIfCorrectInstallPartition(partitionToInstallPathList, dest, filepath.Base(src), groupedSources[srcFileDir])
+ }
+ }
+
+ return groupedSources
+}
+
+type prebuiltModuleProperties struct {
+ Name *string
+
+ Soc_specific *bool
+ Product_specific *bool
+ System_ext_specific *bool
+
+ Srcs []string
+ Dsts []string
+
+ No_full_install *bool
+
+ NamespaceExportedToMake bool
+
+ Visibility []string
+}
+
+// Split relative_install_path to a separate struct, because it is not supported for every
+// modules listed in [etcInstallPathToFactoryMap]
+type prebuiltSubdirProperties struct {
+ // If the base file name of the src and dst all match, dsts property does not need to be
+ // set, and only relative_install_path can be set.
+ Relative_install_path *string
+}
+
+var (
+ etcInstallPathToFactoryList = map[string]android.ModuleFactory{
+ "": etc.PrebuiltRootFactory,
+ "avb": etc.PrebuiltAvbFactory,
+ "bin": etc.PrebuiltBinaryFactory,
+ "bt_firmware": etc.PrebuiltBtFirmwareFactory,
+ "cacerts": etc.PrebuiltEtcCaCertsFactory,
+ "dsp": etc.PrebuiltDSPFactory,
+ "etc": etc.PrebuiltEtcFactory,
+ "etc/dsp": etc.PrebuiltDSPFactory,
+ "etc/firmware": etc.PrebuiltFirmwareFactory,
+ "firmware": etc.PrebuiltFirmwareFactory,
+ "fonts": etc.PrebuiltFontFactory,
+ "framework": etc.PrebuiltFrameworkFactory,
+ "lib": etc.PrebuiltRenderScriptBitcodeFactory,
+ "lib64": etc.PrebuiltRenderScriptBitcodeFactory,
+ "lib/rfsa": etc.PrebuiltRFSAFactory,
+ "media": etc.PrebuiltMediaFactory,
+ "odm": etc.PrebuiltOdmFactory,
+ "optee": etc.PrebuiltOpteeFactory,
+ "overlay": etc.PrebuiltOverlayFactory,
+ "priv-app": etc.PrebuiltPrivAppFactory,
+ "res": etc.PrebuiltResFactory,
+ "rfs": etc.PrebuiltRfsFactory,
+ "tts": etc.PrebuiltVoicepackFactory,
+ "tvconfig": etc.PrebuiltTvConfigFactory,
+ "tvservice": etc.PrebuiltTvServiceFactory,
+ "usr/share": etc.PrebuiltUserShareFactory,
+ "usr/hyphen-data": etc.PrebuiltUserHyphenDataFactory,
+ "usr/keylayout": etc.PrebuiltUserKeyLayoutFactory,
+ "usr/keychars": etc.PrebuiltUserKeyCharsFactory,
+ "usr/srec": etc.PrebuiltUserSrecFactory,
+ "usr/idc": etc.PrebuiltUserIdcFactory,
+ "vendor_dlkm": etc.PrebuiltVendorDlkmFactory,
+ "wallpaper": etc.PrebuiltWallpaperFactory,
+ "wlc_upt": etc.PrebuiltWlcUptFactory,
+ }
+)
+
+func generatedPrebuiltEtcModuleName(partition, srcDir, destDir string, count int) string {
+ // generated module name follows the pattern:
+ // <install partition>-<src file path>-<relative install path from partition root>-<number>
+ // Note that all path separators are replaced with "_" in the name
+ moduleName := partition
+ if !android.InList(srcDir, []string{"", "."}) {
+ moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(srcDir, string(filepath.Separator), "_"))
+ }
+ if !android.InList(destDir, []string{"", "."}) {
+ moduleName += fmt.Sprintf("-%s", strings.ReplaceAll(destDir, string(filepath.Separator), "_"))
+ }
+ moduleName += fmt.Sprintf("-%d", count)
+
+ return moduleName
+}
+
+func groupDestFilesBySrc(destFiles []srcBaseFileInstallBaseFileTuple) (ret map[string][]srcBaseFileInstallBaseFileTuple, maxLen int) {
+ ret = map[string][]srcBaseFileInstallBaseFileTuple{}
+ maxLen = 0
+ for _, tuple := range destFiles {
+ if _, ok := ret[tuple.srcBaseFile]; !ok {
+ ret[tuple.srcBaseFile] = []srcBaseFileInstallBaseFileTuple{}
+ }
+ ret[tuple.srcBaseFile] = append(ret[tuple.srcBaseFile], tuple)
+ maxLen = max(maxLen, len(ret[tuple.srcBaseFile]))
+ }
+ return ret, maxLen
+}
+
+func prebuiltEtcModuleProps(moduleName, partition string) prebuiltModuleProperties {
+ moduleProps := prebuiltModuleProperties{}
+ moduleProps.Name = proptools.StringPtr(moduleName)
+
+ // Set partition specific properties
+ switch partition {
+ case "system_ext":
+ moduleProps.System_ext_specific = proptools.BoolPtr(true)
+ case "product":
+ moduleProps.Product_specific = proptools.BoolPtr(true)
+ case "vendor":
+ moduleProps.Soc_specific = proptools.BoolPtr(true)
+ }
+
+ moduleProps.No_full_install = proptools.BoolPtr(true)
+ moduleProps.NamespaceExportedToMake = true
+ moduleProps.Visibility = []string{"//visibility:public"}
+
+ return moduleProps
+}
+
+func createPrebuiltEtcModulesInDirectory(ctx android.LoadHookContext, partition, srcDir, destDir string, destFiles []srcBaseFileInstallBaseFileTuple) (moduleNames []string) {
+ groupedDestFiles, maxLen := groupDestFilesBySrc(destFiles)
+
+ // Find out the most appropriate module type to generate
+ var etcInstallPathKey string
+ for _, etcInstallPath := range android.SortedKeys(etcInstallPathToFactoryList) {
+ // Do not break when found but iterate until the end to find a module with more
+ // specific install path
+ if strings.HasPrefix(destDir, etcInstallPath) {
+ etcInstallPathKey = etcInstallPath
+ }
+ }
+ relDestDirFromInstallDirBase, _ := filepath.Rel(etcInstallPathKey, destDir)
+
+ for fileIndex := range maxLen {
+ srcTuple := []srcBaseFileInstallBaseFileTuple{}
+ for _, groupedDestFile := range groupedDestFiles {
+ if len(groupedDestFile) > fileIndex {
+ srcTuple = append(srcTuple, groupedDestFile[fileIndex])
+ }
+ }
+
+ moduleName := generatedPrebuiltEtcModuleName(partition, srcDir, destDir, fileIndex)
+ moduleProps := prebuiltEtcModuleProps(moduleName, partition)
+ modulePropsPtr := &moduleProps
+ propsList := []interface{}{modulePropsPtr}
+
+ allCopyFileNamesUnchanged := true
+ var srcBaseFiles, installBaseFiles []string
+ for _, tuple := range srcTuple {
+ if tuple.srcBaseFile != tuple.installBaseFile {
+ allCopyFileNamesUnchanged = false
+ }
+ srcBaseFiles = append(srcBaseFiles, tuple.srcBaseFile)
+ installBaseFiles = append(installBaseFiles, tuple.installBaseFile)
+ }
+
+ // Set appropriate srcs, dsts, and releative_install_path based on
+ // the source and install file names
+ if allCopyFileNamesUnchanged {
+ modulePropsPtr.Srcs = srcBaseFiles
+
+ // Specify relative_install_path if it is not installed in the root directory of the
+ // partition
+ if !android.InList(relDestDirFromInstallDirBase, []string{"", "."}) {
+ propsList = append(propsList, &prebuiltSubdirProperties{
+ Relative_install_path: proptools.StringPtr(relDestDirFromInstallDirBase),
+ })
+ }
+ } else {
+ modulePropsPtr.Srcs = srcBaseFiles
+ dsts := []string{}
+ for _, installBaseFile := range installBaseFiles {
+ dsts = append(dsts, filepath.Join(relDestDirFromInstallDirBase, installBaseFile))
+ }
+ modulePropsPtr.Dsts = dsts
+ }
+
+ ctx.CreateModuleInDirectory(etcInstallPathToFactoryList[etcInstallPathKey], srcDir, propsList...)
+ moduleNames = append(moduleNames, moduleName)
+ }
+
+ return moduleNames
+}
+
+func createPrebuiltEtcModulesForPartition(ctx android.LoadHookContext, partition, srcDir string, destDirFilesMap map[string][]srcBaseFileInstallBaseFileTuple) (ret []string) {
+ for _, destDir := range android.SortedKeys(destDirFilesMap) {
+ ret = append(ret, createPrebuiltEtcModulesInDirectory(ctx, partition, srcDir, destDir, destDirFilesMap[destDir])...)
+ }
+ return ret
+}
+
+// Creates prebuilt_* modules based on the install paths and returns the list of generated
+// module names
+func createPrebuiltEtcModules(ctx android.LoadHookContext) (ret []string) {
+ groupedSources := processProductCopyFiles(ctx)
+ for _, srcDir := range android.SortedKeys(groupedSources) {
+ groupedSource := groupedSources[srcDir]
+ ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system", srcDir, groupedSource.system)...)
+ ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "system_ext", srcDir, groupedSource.system_ext)...)
+ ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "product", srcDir, groupedSource.product)...)
+ ret = append(ret, createPrebuiltEtcModulesForPartition(ctx, "vendor", srcDir, groupedSource.vendor)...)
+ }
+
+ return ret
+}
diff --git a/fsgen/vbmeta_partitions.go b/fsgen/vbmeta_partitions.go
new file mode 100644
index 0000000..f7b4638
--- /dev/null
+++ b/fsgen/vbmeta_partitions.go
@@ -0,0 +1,188 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsgen
+
+import (
+ "android/soong/android"
+ "android/soong/filesystem"
+ "slices"
+ "strconv"
+
+ "github.com/google/blueprint/proptools"
+)
+
+type vbmetaModuleInfo struct {
+ // The name of the generated vbmeta module
+ moduleName string
+ // The name of the module that avb understands. This is the name passed to --chain_partition,
+ // and also the basename of the output file. (the output file is called partitionName + ".img")
+ partitionName string
+}
+
+// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names
+// that the function created. May return nil if the product isn't using avb.
+//
+// AVB is Android Verified Boot: https://source.android.com/docs/security/features/verifiedboot
+// It works by signing all the partitions, but then also including an extra metadata paritition
+// called vbmeta that depends on all the other signed partitions. This creates a requirement
+// that you update all those partitions and the vbmeta partition together, so in order to relax
+// that requirement products can set up "chained" vbmeta partitions, where a chained partition
+// like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish
+// vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions,
+// that group of partitions can be updated independently from the other signed partitions.
+func createVbmetaPartitions(ctx android.LoadHookContext, generatedPartitionTypes []string) []vbmetaModuleInfo {
+ partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse
+ // Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false
+ if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable {
+ return nil
+ }
+
+ var result []vbmetaModuleInfo
+
+ var chainedPartitions []filesystem.ChainedPartitionProperties
+ var partitionTypesHandledByChainedPartitions []string
+ for chainedName, props := range partitionVars.ChainedVbmetaPartitions {
+ chainedName = "vbmeta_" + chainedName
+ if len(props.Partitions) == 0 {
+ continue
+ }
+ if len(props.Key) == 0 {
+ ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName)
+ continue
+ }
+ if len(props.Algorithm) == 0 {
+ ctx.ModuleErrorf("No algorithm found for chained avb partition %q", chainedName)
+ continue
+ }
+ if len(props.RollbackIndex) == 0 {
+ ctx.ModuleErrorf("No rollback index found for chained avb partition %q", chainedName)
+ continue
+ }
+ ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32)
+ if err != nil {
+ ctx.ModuleErrorf("Rollback index location must be an int, got %q", props.RollbackIndexLocation)
+ continue
+ }
+ // The default is to use the PlatformSecurityPatch, and a lot of product config files
+ // just set it to the platform security patch, so detect that and don't set the property
+ // in soong.
+ var rollbackIndex *int64
+ if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() {
+ i, err := strconv.ParseInt(props.RollbackIndex, 10, 32)
+ if err != nil {
+ ctx.ModuleErrorf("Rollback index must be an int, got %q", props.RollbackIndex)
+ continue
+ }
+ rollbackIndex = &i
+ }
+
+ var partitionModules []string
+ for _, partition := range props.Partitions {
+ partitionTypesHandledByChainedPartitions = append(partitionTypesHandledByChainedPartitions, partition)
+ if !slices.Contains(generatedPartitionTypes, partition) {
+ // The partition is probably unsupported.
+ continue
+ }
+ partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partition))
+ }
+
+ name := generatedModuleName(ctx.Config(), chainedName)
+ ctx.CreateModuleInDirectory(
+ filesystem.VbmetaFactory,
+ ".", // Create in the root directory for now so its easy to get the key
+ &filesystem.VbmetaProperties{
+ Partition_name: proptools.StringPtr(chainedName),
+ Stem: proptools.StringPtr(chainedName + ".img"),
+ Private_key: proptools.StringPtr(props.Key),
+ Algorithm: &props.Algorithm,
+ Rollback_index: rollbackIndex,
+ Rollback_index_location: &ril,
+ Partitions: proptools.NewSimpleConfigurable(partitionModules),
+ }, &struct {
+ Name *string
+ }{
+ Name: &name,
+ },
+ ).HideFromMake()
+
+ chainedPartitions = append(chainedPartitions, filesystem.ChainedPartitionProperties{
+ Name: &chainedName,
+ Rollback_index_location: &ril,
+ Private_key: &props.Key,
+ })
+
+ result = append(result, vbmetaModuleInfo{
+ moduleName: name,
+ partitionName: chainedName,
+ })
+ }
+
+ vbmetaModuleName := generatedModuleName(ctx.Config(), "vbmeta")
+
+ var algorithm *string
+ var ri *int64
+ var key *string
+ if len(partitionVars.BoardAvbKeyPath) == 0 {
+ // Match make's defaults: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4568;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0
+ key = proptools.StringPtr("external/avb/test/data/testkey_rsa4096.pem")
+ algorithm = proptools.StringPtr("SHA256_RSA4096")
+ } else {
+ key = proptools.StringPtr(partitionVars.BoardAvbKeyPath)
+ algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm)
+ }
+ if len(partitionVars.BoardAvbRollbackIndex) > 0 {
+ parsedRi, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32)
+ if err != nil {
+ ctx.ModuleErrorf("Rollback index location must be an int, got %q", partitionVars.BoardAvbRollbackIndex)
+ }
+ ri = &parsedRi
+ }
+
+ var partitionModules []string
+ for _, partitionType := range generatedPartitionTypes {
+ if slices.Contains(partitionTypesHandledByChainedPartitions, partitionType) {
+ // Already handled by a chained vbmeta partition
+ continue
+ }
+ if partitionType == "ramdisk" {
+ // ramdisk is never signed with avb information
+ continue
+ }
+ partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partitionType))
+ }
+
+ ctx.CreateModuleInDirectory(
+ filesystem.VbmetaFactory,
+ ".", // Create in the root directory for now so its easy to get the key
+ &filesystem.VbmetaProperties{
+ Stem: proptools.StringPtr("vbmeta.img"),
+ Algorithm: algorithm,
+ Private_key: key,
+ Rollback_index: ri,
+ Chained_partitions: chainedPartitions,
+ Partitions: proptools.NewSimpleConfigurable(partitionModules),
+ }, &struct {
+ Name *string
+ }{
+ Name: &vbmetaModuleName,
+ },
+ ).HideFromMake()
+
+ result = append(result, vbmetaModuleInfo{
+ moduleName: vbmetaModuleName,
+ partitionName: "vbmeta",
+ })
+ return result
+}
diff --git a/java/aar.go b/java/aar.go
index 66ca00a..b5cdde3 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -77,7 +77,7 @@
// list of directories relative to the Blueprints file containing
// Android resources. Defaults to ["res"] if a directory called res exists.
// Set to [] to disable the default.
- Resource_dirs []string `android:"path"`
+ Resource_dirs proptools.Configurable[[]string] `android:"path"`
// list of zip files containing Android resources.
Resource_zips []string `android:"path"`
@@ -275,7 +275,7 @@
IncludeDirs: false,
})
assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets")
- resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res")
+ resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs.GetOrDefault(ctx, nil), "res")
resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips)
// Glob directories into lists of paths
diff --git a/java/app.go b/java/app.go
index e01a2ba..0939d17 100644
--- a/java/app.go
+++ b/java/app.go
@@ -67,6 +67,9 @@
// TestHelperApp is true if the module is a android_test_helper_app
TestHelperApp bool
+
+ // EmbeddedJNILibs is the list of paths to JNI libraries that were embedded in the APK.
+ EmbeddedJNILibs android.Paths
}
var AppInfoProvider = blueprint.NewProvider[*AppInfo]()
@@ -405,9 +408,18 @@
a.checkEmbedJnis(ctx)
a.generateAndroidBuildActions(ctx)
a.generateJavaUsedByApex(ctx)
+
+ var embeddedJniLibs []android.Path
+
+ if a.embeddedJniLibs {
+ for _, jni := range a.jniLibs {
+ embeddedJniLibs = append(embeddedJniLibs, jni.path)
+ }
+ }
android.SetProvider(ctx, AppInfoProvider, &AppInfo{
- Updatable: Bool(a.appProperties.Updatable),
- TestHelperApp: false,
+ Updatable: Bool(a.appProperties.Updatable),
+ TestHelperApp: false,
+ EmbeddedJNILibs: embeddedJniLibs,
})
}
@@ -1070,12 +1082,12 @@
app.SdkVersion(ctx).Kind != android.SdkCorePlatform && !app.RequiresStableAPIs(ctx)
}
jniLib, prebuiltJniPackages := collectJniDeps(ctx, shouldCollectRecursiveNativeDeps,
- checkNativeSdkVersion, func(dep cc.LinkableInterface) bool {
- return !dep.IsNdk(ctx.Config()) && !dep.IsStubs()
- })
+ checkNativeSdkVersion, func(dep cc.LinkableInterface) bool { return !dep.IsNdk(ctx.Config()) && !dep.IsStubs() })
var certificates []Certificate
+ var directImplementationDeps android.Paths
+ var transitiveImplementationDeps []depset.DepSet[android.Path]
ctx.VisitDirectDeps(func(module android.Module) {
otherName := ctx.OtherModuleName(module)
tag := ctx.OtherModuleDependencyTag(module)
@@ -1087,7 +1099,18 @@
ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", otherName)
}
}
+
+ if IsJniDepTag(tag) {
+ directImplementationDeps = append(directImplementationDeps, android.OutputFileForModule(ctx, module, ""))
+ if info, ok := android.OtherModuleProvider(ctx, module, cc.ImplementationDepInfoProvider); ok {
+ transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps)
+ }
+ }
})
+ android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+ ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps),
+ })
+
return jniLib, prebuiltJniPackages, certificates
}
@@ -1338,7 +1361,7 @@
Filter_product *string
Aaptflags []string
Manifest *string
- Resource_dirs []string
+ Resource_dirs proptools.Configurable[[]string]
Flags_packages []string
}{
Name: proptools.StringPtr(rroPackageName),
@@ -1442,8 +1465,9 @@
a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_common_data)...)
a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_data)...)
a.data = append(a.data, android.PathsForModuleSrc(ctx, a.testProperties.Device_first_prefer32_data)...)
+
android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
- InstalledFiles: a.data,
+ TestcaseRelDataFiles: testcaseRel(a.data),
OutputFile: a.OutputFile(),
TestConfig: a.testConfig,
HostRequiredModuleNames: a.HostRequiredModuleNames(),
@@ -1451,6 +1475,8 @@
IsHost: false,
LocalCertificate: a.certificate.AndroidMkString(),
IsUnitTest: Bool(a.testProperties.Test_options.Unit_test),
+ MkInclude: "$(BUILD_SYSTEM)/soong_app_prebuilt.mk",
+ MkAppClass: "APPS",
})
android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
TestOnly: true,
@@ -1459,6 +1485,14 @@
}
+func testcaseRel(paths android.Paths) []string {
+ relPaths := []string{}
+ for _, p := range paths {
+ relPaths = append(relPaths, p.Rel())
+ }
+ return relPaths
+}
+
func (a *AndroidTest) FixTestConfig(ctx android.ModuleContext, testConfig android.Path) android.Path {
if testConfig == nil {
return nil
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 1a33680..c778f04 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -111,7 +111,7 @@
// property.
//
// The order of this list matters as it is the order that is used in the bootclasspath.
- Contents []string
+ Contents proptools.Configurable[[]string] `android:"arch_variant"`
// The properties for specifying the API stubs provided by this fragment.
BootclasspathAPIProperties
@@ -295,8 +295,8 @@
return m
}
-func (m *BootclasspathFragmentModule) bootclasspathFragmentPropertyCheck(ctx android.EarlyModuleContext) {
- contents := m.properties.Contents
+func (m *BootclasspathFragmentModule) bootclasspathFragmentPropertyCheck(ctx android.ModuleContext) {
+ contents := m.properties.Contents.GetOrDefault(ctx, nil)
if len(contents) == 0 {
ctx.PropertyErrorf("contents", "required property is missing")
return
@@ -434,7 +434,7 @@
module := ctx.Module()
_, isSourceModule := module.(*BootclasspathFragmentModule)
- for _, name := range b.properties.Contents {
+ for _, name := range b.properties.Contents.GetOrDefault(ctx, nil) {
// A bootclasspath_fragment must depend only on other source modules, while the
// prebuilt_bootclasspath_fragment must only depend on other prebuilt modules.
//
@@ -520,7 +520,7 @@
// be output to Make but it does not really matter which variant is output. The default/platform
// variant is the first (ctx.PrimaryModule()) and is usually hidden from make so this just picks
// the last variant (ctx.FinalModule()).
- if ctx.Module() != ctx.FinalModule() {
+ if !ctx.IsFinalModule(ctx.Module()) {
b.HideFromMake()
}
}
@@ -588,7 +588,7 @@
return global.ArtApexJars
}
- possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag)
+ possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, b.properties.Contents.GetOrDefault(ctx, nil), bootclasspathFragmentContentDepTag)
jars, unknown := global.ApexBootJars.Filter(possibleUpdatableModules)
// TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths
@@ -600,7 +600,7 @@
} else if android.InList("test_framework-apexd", possibleUpdatableModules) {
jars = jars.Append("com.android.apex.test_package", "test_framework-apexd")
} else if global.ApexBootJars.Len() != 0 {
- unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents)
+ unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents.GetOrDefault(ctx, nil))
_, unknown = android.RemoveFromList("core-icu4j", unknown)
// This module only exists in car products.
// So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS.
@@ -847,7 +847,7 @@
// Collect information for opening IDE project files in java/jdeps.go.
func (b *BootclasspathFragmentModule) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
- dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)
+ dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents.GetOrDefault(ctx, nil)...)
}
type bootclasspathFragmentMemberType struct {
@@ -923,7 +923,7 @@
module := variant.(*BootclasspathFragmentModule)
b.Image_name = module.properties.Image_name
- b.Contents = module.properties.Contents
+ b.Contents = module.properties.Contents.GetOrDefault(ctx.SdkModuleContext(), nil)
// Get the hidden API information from the module.
mctx := ctx.SdkModuleContext()
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 60f1a50..3aa1258 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -191,7 +191,8 @@
checkContents := func(t *testing.T, result *android.TestResult, expected ...string) {
module := result.Module("myfragment", "android_common").(*BootclasspathFragmentModule)
- android.AssertArrayString(t, "contents property", expected, module.properties.Contents)
+ eval := module.ConfigurableEvaluator(android.PanickingConfigAndErrorContext(result.TestContext))
+ android.AssertArrayString(t, "contents property", expected, module.properties.Contents.GetOrDefault(eval, nil))
}
preparer := android.GroupFixturePreparers(
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index da86540..8c808e4 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -132,10 +132,10 @@
// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk
// directory.
java_library {
- name: "core-current-stubs-for-system-modules",
+ name: "core-current-stubs-for-system-modules-exportable",
visibility: ["//development/sdk"],
static_libs: [
- "core.current.stubs",
+ "core.current.stubs.exportable",
// This one is not on device but it's needed when javac compiles code
// containing lambdas.
"core-lambda-stubs-for-system-modules",
@@ -155,6 +155,19 @@
],
}
+java_library {
+ name: "core-current-stubs-for-system-modules",
+ visibility: ["//development/sdk"],
+ static_libs: [
+ "core.current.stubs",
+ // This one is not on device but it's needed when javac compiles code
+ // containing lambdas.
+ "core-lambda-stubs-for-system-modules",
+ ],
+ sdk_version: "none",
+ system_modules: "none",
+}
+
// Defaults module to strip out android annotations
java_defaults {
name: "system-modules-no-annotations",
diff --git a/java/java.go b/java/java.go
index 53b3481..078f578 100644
--- a/java/java.go
+++ b/java/java.go
@@ -606,10 +606,8 @@
} else if ctx.Device() {
return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx))
} else if ctx.Config().TargetsJava21() {
- // Temporary experimental flag to be able to try and build with
- // java version 21 options. The flag, if used, just sets Java
- // 21 as the default version, leaving any components that
- // target an older version intact.
+ // Build flag that controls whether Java 21 is used as the default
+ // target version, or Java 17.
return JAVA_VERSION_21
} else {
return JAVA_VERSION_17
@@ -1559,14 +1557,16 @@
j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
- InstalledFiles: j.data,
- OutputFile: j.outputFile,
- TestConfig: j.testConfig,
- RequiredModuleNames: j.RequiredModuleNames(ctx),
- TestSuites: j.testProperties.Test_suites,
- IsHost: true,
- LocalSdkVersion: j.sdkVersion.String(),
- IsUnitTest: Bool(j.testProperties.Test_options.Unit_test),
+ TestcaseRelDataFiles: testcaseRel(j.data),
+ OutputFile: j.outputFile,
+ TestConfig: j.testConfig,
+ RequiredModuleNames: j.RequiredModuleNames(ctx),
+ TestSuites: j.testProperties.Test_suites,
+ IsHost: true,
+ LocalSdkVersion: j.sdkVersion.String(),
+ IsUnitTest: Bool(j.testProperties.Test_options.Unit_test),
+ MkInclude: "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
+ MkAppClass: "JAVA_LIBRARIES",
})
}
@@ -1610,6 +1610,8 @@
j.data = append(j.data, android.OutputFileForModule(ctx, dep, ""))
})
+ var directImplementationDeps android.Paths
+ var transitiveImplementationDeps []depset.DepSet[android.Path]
ctx.VisitDirectDepsWithTag(jniLibTag, func(dep android.Module) {
sharedLibInfo, _ := android.OtherModuleProvider(ctx, dep, cc.SharedLibraryInfoProvider)
if sharedLibInfo.SharedLibrary != nil {
@@ -1628,11 +1630,20 @@
Output: relocatedLib,
})
j.data = append(j.data, relocatedLib)
+
+ directImplementationDeps = append(directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ transitiveImplementationDeps = append(transitiveImplementationDeps, info.ImplementationDeps)
+ }
} else {
ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
}
})
+ android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+ ImplementationDeps: depset.New(depset.PREORDER, directImplementationDeps, transitiveImplementationDeps),
+ })
+
j.Library.GenerateAndroidBuildActions(ctx)
}
diff --git a/java/sdk.go b/java/sdk.go
index 4537f19..bb2aa8d 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -66,10 +66,8 @@
} else if sdk.FinalOrFutureInt() <= 33 {
return JAVA_VERSION_11
} else if ctx.Config().TargetsJava21() {
- // Temporary experimental flag to be able to try and build with
- // java version 21 options. The flag, if used, just sets Java
- // 21 as the default version, leaving any components that
- // target an older version intact.
+ // Build flag that controls whether Java 21 is used as the
+ // default target version, or Java 17.
return JAVA_VERSION_21
} else {
return JAVA_VERSION_17
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index aad1060..608a616 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -19,6 +19,7 @@
"android/soong/dexpreopt"
"github.com/google/blueprint"
+ "github.com/google/blueprint/proptools"
)
func init() {
@@ -98,12 +99,12 @@
// List of system_server classpath jars, could be either java_library, or java_sdk_library.
//
// The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH.
- Contents []string
+ Contents proptools.Configurable[[]string] `android:"arch_variant"`
// List of jars that system_server loads dynamically using separate classloaders.
//
// The order does not matter.
- Standalone_contents []string
+ Standalone_contents proptools.Configurable[[]string] `android:"arch_variant"`
}
func systemServerClasspathFactory() android.Module {
@@ -116,7 +117,7 @@
}
func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- if len(s.properties.Contents) == 0 && len(s.properties.Standalone_contents) == 0 {
+ if len(s.properties.Contents.GetOrDefault(ctx, nil)) == 0 && len(s.properties.Standalone_contents.GetOrDefault(ctx, nil)) == 0 {
ctx.PropertyErrorf("contents", "Either contents or standalone_contents needs to be non-empty")
}
@@ -152,7 +153,7 @@
func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
global := dexpreopt.GetGlobalConfig(ctx)
- possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Contents, systemServerClasspathFragmentContentDepTag)
+ possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Contents.GetOrDefault(ctx, nil), systemServerClasspathFragmentContentDepTag)
jars, unknown := global.ApexSystemServerJars.Filter(possibleUpdatableModules)
// TODO(satayev): remove geotz ssc_fragment, since geotz is not part of SSCP anymore.
_, unknown = android.RemoveFromList("geotz", unknown)
@@ -184,7 +185,7 @@
func (s *SystemServerClasspathModule) standaloneConfiguredJars(ctx android.ModuleContext) android.ConfiguredJarList {
global := dexpreopt.GetGlobalConfig(ctx)
- possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Standalone_contents, systemServerClasspathFragmentContentDepTag)
+ possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Standalone_contents.GetOrDefault(ctx, nil), systemServerClasspathFragmentContentDepTag)
jars, _ := global.ApexStandaloneSystemServerJars.Filter(possibleUpdatableModules)
// TODO(jiakaiz): add a check to ensure that the contents are declared in make.
@@ -245,8 +246,8 @@
module := ctx.Module()
_, isSourceModule := module.(*SystemServerClasspathModule)
var deps []string
- deps = append(deps, s.properties.Contents...)
- deps = append(deps, s.properties.Standalone_contents...)
+ deps = append(deps, s.properties.Contents.GetOrDefault(ctx, nil)...)
+ deps = append(deps, s.properties.Standalone_contents.GetOrDefault(ctx, nil)...)
for _, name := range deps {
// A systemserverclasspath_fragment must depend only on other source modules, while the
@@ -260,8 +261,8 @@
// Collect information for opening IDE project files in java/jdeps.go.
func (s *SystemServerClasspathModule) IDEInfo(ctx android.BaseModuleContext, dpInfo *android.IdeInfo) {
- dpInfo.Deps = append(dpInfo.Deps, s.properties.Contents...)
- dpInfo.Deps = append(dpInfo.Deps, s.properties.Standalone_contents...)
+ dpInfo.Deps = append(dpInfo.Deps, s.properties.Contents.GetOrDefault(ctx, nil)...)
+ dpInfo.Deps = append(dpInfo.Deps, s.properties.Standalone_contents.GetOrDefault(ctx, nil)...)
}
type systemServerClasspathFragmentMemberType struct {
@@ -302,8 +303,8 @@
func (s *systemServerClasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
module := variant.(*SystemServerClasspathModule)
- s.Contents = module.properties.Contents
- s.Standalone_contents = module.properties.Standalone_contents
+ s.Contents = module.properties.Contents.GetOrDefault(ctx.SdkModuleContext(), nil)
+ s.Standalone_contents = module.properties.Standalone_contents.GetOrDefault(ctx.SdkModuleContext(), nil)
}
func (s *systemServerClasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go
index 4c0a911..13d6482 100644
--- a/kernel/prebuilt_kernel_modules.go
+++ b/kernel/prebuilt_kernel_modules.go
@@ -47,6 +47,17 @@
// List or filegroup of prebuilt kernel module files. Should have .ko suffix.
Srcs []string `android:"path,arch_variant"`
+ // List of system_dlkm kernel modules that the local kernel modules depend on.
+ // The deps will be assembled into intermediates directory for running depmod
+ // but will not be added to the current module's installed files.
+ System_deps []string `android:"path,arch_variant"`
+
+ // If false, then srcs will not be included in modules.load.
+ // This feature is used by system_dlkm
+ Load_by_default *bool
+
+ Blocklist_file *string `android:"path"`
+
// Kernel version that these modules are for. Kernel modules are installed to
// /lib/modules/<kernel_version> directory in the corresponding partition. Default is "".
Kernel_version *string
@@ -81,9 +92,11 @@
if !pkm.installable() {
pkm.SkipInstall()
}
- modules := android.PathsForModuleSrc(ctx, pkm.properties.Srcs)
- depmodOut := runDepmod(ctx, modules)
+ modules := android.PathsForModuleSrc(ctx, pkm.properties.Srcs)
+ systemModules := android.PathsForModuleSrc(ctx, pkm.properties.System_deps)
+
+ depmodOut := pkm.runDepmod(ctx, modules, systemModules)
strippedModules := stripDebugSymbols(ctx, modules)
installDir := android.PathForModuleInstall(ctx, "lib", "modules")
@@ -98,6 +111,23 @@
ctx.InstallFile(installDir, "modules.dep", depmodOut.modulesDep)
ctx.InstallFile(installDir, "modules.softdep", depmodOut.modulesSoftdep)
ctx.InstallFile(installDir, "modules.alias", depmodOut.modulesAlias)
+ pkm.installBlocklistFile(ctx, installDir)
+
+ ctx.SetOutputFiles(modules, ".modules")
+}
+
+func (pkm *prebuiltKernelModules) installBlocklistFile(ctx android.ModuleContext, installDir android.InstallPath) {
+ if pkm.properties.Blocklist_file == nil {
+ return
+ }
+ blocklistOut := android.PathForModuleOut(ctx, "modules.blocklist")
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: processBlocklistFile,
+ Input: android.PathForModuleSrc(ctx, proptools.String(pkm.properties.Blocklist_file)),
+ Output: blocklistOut,
+ })
+ ctx.InstallFile(installDir, "modules.blocklist", blocklistOut)
}
var (
@@ -137,35 +167,93 @@
modulesAlias android.OutputPath
}
-func runDepmod(ctx android.ModuleContext, modules android.Paths) depmodOutputs {
+var (
+ // system/lib/modules/foo.ko: system/lib/modules/bar.ko
+ // will be converted to
+ // /system/lib/modules/foo.ko: /system/lib/modules/bar.ko
+ addLeadingSlashToPaths = pctx.AndroidStaticRule("add_leading_slash",
+ blueprint.RuleParams{
+ Command: `sed -e 's|\([^: ]*lib/modules/[^: ]*\)|/\1|g' $in > $out`,
+ },
+ )
+ // Remove empty lines. Raise an exception if line is _not_ formatted as `blocklist $name.ko`
+ processBlocklistFile = pctx.AndroidStaticRule("process_blocklist_file",
+ blueprint.RuleParams{
+ Command: `rm -rf $out && awk <$in > $out` +
+ ` '/^#/ { print; next }` +
+ ` NF == 0 { next }` +
+ ` NF != 2 || $$1 != "blocklist"` +
+ ` { print "Invalid blocklist line " FNR ": " $$0 >"/dev/stderr";` +
+ ` exit_status = 1; next }` +
+ ` { $$1 = $$1; print }` +
+ ` END { exit exit_status }'`,
+ },
+ )
+)
+
+// This is the path in soong intermediates where the .ko files will be copied.
+// The layout should match the layout on device so that depmod can create meaningful modules.* files.
+func modulesDirForAndroidDlkm(ctx android.ModuleContext, modulesDir android.OutputPath, system bool) android.OutputPath {
+ if ctx.InstallInSystemDlkm() || system {
+ // The first component can be either system or system_dlkm
+ // system works because /system/lib/modules is a symlink to /system_dlkm/lib/modules.
+ // system was chosen to match the contents of the kati built modules.dep
+ return modulesDir.Join(ctx, "system", "lib", "modules")
+ } else if ctx.InstallInVendorDlkm() {
+ return modulesDir.Join(ctx, "vendor", "lib", "modules")
+ } else if ctx.InstallInOdmDlkm() {
+ return modulesDir.Join(ctx, "odm", "lib", "modules")
+ } else {
+ // not an android dlkm module.
+ return modulesDir
+ }
+}
+
+func (pkm *prebuiltKernelModules) runDepmod(ctx android.ModuleContext, modules android.Paths, systemModules android.Paths) depmodOutputs {
baseDir := android.PathForModuleOut(ctx, "depmod").OutputPath
fakeVer := "0.0" // depmod demands this anyway
modulesDir := baseDir.Join(ctx, "lib", "modules", fakeVer)
+ modulesCpDir := modulesDirForAndroidDlkm(ctx, modulesDir, false)
builder := android.NewRuleBuilder(pctx, ctx)
// Copy the module files to a temporary dir
- builder.Command().Text("rm").Flag("-rf").Text(modulesDir.String())
- builder.Command().Text("mkdir").Flag("-p").Text(modulesDir.String())
+ builder.Command().Text("rm").Flag("-rf").Text(modulesCpDir.String())
+ builder.Command().Text("mkdir").Flag("-p").Text(modulesCpDir.String())
for _, m := range modules {
- builder.Command().Text("cp").Input(m).Text(modulesDir.String())
+ builder.Command().Text("cp").Input(m).Text(modulesCpDir.String())
+ }
+
+ modulesDirForSystemDlkm := modulesDirForAndroidDlkm(ctx, modulesDir, true)
+ if len(systemModules) > 0 {
+ builder.Command().Text("mkdir").Flag("-p").Text(modulesDirForSystemDlkm.String())
+ }
+ for _, m := range systemModules {
+ builder.Command().Text("cp").Input(m).Text(modulesDirForSystemDlkm.String())
}
// Enumerate modules to load
modulesLoad := modulesDir.Join(ctx, "modules.load")
- var basenames []string
- for _, m := range modules {
- basenames = append(basenames, filepath.Base(m.String()))
+ // If Load_by_default is set to false explicitly, create an empty modules.load
+ if pkm.properties.Load_by_default != nil && !*pkm.properties.Load_by_default {
+ builder.Command().Text("rm").Flag("-rf").Text(modulesLoad.String())
+ builder.Command().Text("touch").Output(modulesLoad)
+ } else {
+ var basenames []string
+ for _, m := range modules {
+ basenames = append(basenames, filepath.Base(m.String()))
+ }
+ builder.Command().
+ Text("echo").Flag("\"" + strings.Join(basenames, " ") + "\"").
+ Text("|").Text("tr").Flag("\" \"").Flag("\"\\n\"").
+ Text(">").Output(modulesLoad)
}
- builder.Command().
- Text("echo").Flag("\"" + strings.Join(basenames, " ") + "\"").
- Text("|").Text("tr").Flag("\" \"").Flag("\"\\n\"").
- Text(">").Output(modulesLoad)
// Run depmod to build modules.dep/softdep/alias files
modulesDep := modulesDir.Join(ctx, "modules.dep")
modulesSoftdep := modulesDir.Join(ctx, "modules.softdep")
modulesAlias := modulesDir.Join(ctx, "modules.alias")
+ builder.Command().Text("mkdir").Flag("-p").Text(modulesDir.String())
builder.Command().
BuiltTool("depmod").
FlagWithArg("-b ", baseDir.String()).
@@ -176,5 +264,16 @@
builder.Build("depmod", fmt.Sprintf("depmod %s", ctx.ModuleName()))
- return depmodOutputs{modulesLoad, modulesDep, modulesSoftdep, modulesAlias}
+ finalModulesDep := modulesDep
+ // Add a leading slash to paths in modules.dep of android dlkm
+ if ctx.InstallInSystemDlkm() || ctx.InstallInVendorDlkm() || ctx.InstallInOdmDlkm() {
+ finalModulesDep := modulesDep.ReplaceExtension(ctx, "intermediates")
+ ctx.Build(pctx, android.BuildParams{
+ Rule: addLeadingSlashToPaths,
+ Input: modulesDep,
+ Output: finalModulesDep,
+ })
+ }
+
+ return depmodOutputs{modulesLoad, finalModulesDep, modulesSoftdep, modulesAlias}
}
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 3944495..d590579 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -252,7 +252,7 @@
// Module defined clang flags and include paths
cflags = append(cflags, esc(cflagsProp)...)
- for _, include := range b.ClangProperties.Local_include_dirs {
+ for _, include := range b.ClangProperties.Local_include_dirs.GetOrDefault(ctx, nil) {
cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String())
implicits = append(implicits, android.PathForModuleSrc(ctx, include))
}
diff --git a/rust/library.go b/rust/library.go
index 20cd2af..bd3359b 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -617,6 +617,9 @@
TableOfContents: android.OptionalPathForPath(tocFile),
SharedLibrary: outputFile,
Target: ctx.Target(),
+ // TODO: when rust supports stubs uses the stubs state rather than inferring it from
+ // apex_exclude.
+ IsStubs: Bool(library.Properties.Apex_exclude),
})
}
diff --git a/rust/rust.go b/rust/rust.go
index 9e06cd4..ed38ad7 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -454,6 +454,9 @@
// Paths to generated source files
SrcDeps android.Paths
srcProviderFiles android.Paths
+
+ directImplementationDeps android.Paths
+ transitiveImplementationDeps []depset.DepSet[android.Path]
}
type RustLibraries []RustLibrary
@@ -991,6 +994,10 @@
}
+ android.SetProvider(ctx, cc.ImplementationDepInfoProvider, &cc.ImplementationDepInfo{
+ ImplementationDeps: depset.New(depset.PREORDER, deps.directImplementationDeps, deps.transitiveImplementationDeps),
+ })
+
ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
}
@@ -1244,6 +1251,11 @@
mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName))
+ depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+ }
+
case depTag == rlibDepTag:
rlib, ok := rustDep.compiler.(libraryInterface)
if !ok || !rlib.rlib() {
@@ -1259,6 +1271,11 @@
depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path()))
+ // rlibs are not installed, so don't add the output file to directImplementationDeps
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+ }
+
case depTag == procMacroDepTag:
directProcMacroDeps = append(directProcMacroDeps, rustDep)
mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
@@ -1391,6 +1408,13 @@
// dependency crosses the APEX boundaries).
sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep)
+ if !sharedLibraryInfo.IsStubs {
+ depPaths.directImplementationDeps = append(depPaths.directImplementationDeps, android.OutputFileForModule(ctx, dep, ""))
+ if info, ok := android.OtherModuleProvider(ctx, dep, cc.ImplementationDepInfoProvider); ok {
+ depPaths.transitiveImplementationDeps = append(depPaths.transitiveImplementationDeps, info.ImplementationDeps)
+ }
+ }
+
// Re-get linkObject as ChooseStubOrImpl actually tells us which
// object (either from stub or non-stub) to use.
linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
index e0686ed..0b7780e 100644
--- a/scripts/gen_build_prop.py
+++ b/scripts/gen_build_prop.py
@@ -608,6 +608,8 @@
build_product_prop(args)
case "vendor":
build_vendor_prop(args)
+ case "system_dlkm" | "vendor_dlkm" | "odm_dlkm":
+ build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=[])
case _:
sys.exit(f"not supported partition {args.partition}")
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 853f3d3..320e97f 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -15,6 +15,7 @@
package sh
import (
+ "fmt"
"path/filepath"
"strings"
@@ -164,6 +165,9 @@
// Test options.
Test_options android.CommonTestOptions
+
+ // a list of extra test configuration files that should be installed with the module.
+ Extra_test_configs []string `android:"path,arch_variant"`
}
type ShBinary struct {
@@ -186,8 +190,9 @@
installDir android.InstallPath
- data []android.DataPath
- testConfig android.Path
+ data []android.DataPath
+ testConfig android.Path
+ extraTestConfigs android.Paths
dataModules map[string]android.Path
}
@@ -471,6 +476,7 @@
HostTemplate: "${ShellTestConfigTemplate}",
})
+ s.extraTestConfigs = android.PathsForModuleSrc(ctx, s.testProperties.Extra_test_configs)
s.dataModules = make(map[string]android.Path)
ctx.VisitDirectDeps(func(dep android.Module) {
depTag := ctx.OtherModuleDependencyTag(dep)
@@ -510,6 +516,27 @@
installedData := ctx.InstallTestData(s.installDir, s.data)
s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath, installedData...)
+
+ mkEntries := s.AndroidMkEntries()[0]
+ android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
+ TestcaseRelDataFiles: addArch(ctx.Arch().ArchType.String(), installedData.Paths()),
+ OutputFile: s.outputFilePath,
+ TestConfig: s.testConfig,
+ TestSuites: s.testProperties.Test_suites,
+ IsHost: false,
+ IsUnitTest: Bool(s.testProperties.Test_options.Unit_test),
+ MkInclude: mkEntries.Include,
+ MkAppClass: mkEntries.Class,
+ InstallDir: s.installDir,
+ })
+}
+
+func addArch(archType string, paths android.Paths) []string {
+ archRelPaths := []string{}
+ for _, p := range paths {
+ archRelPaths = append(archRelPaths, fmt.Sprintf("%s/%s", archType, p.Rel()))
+ }
+ return archRelPaths
}
func (s *ShTest) InstallInData() bool {
@@ -533,6 +560,9 @@
entries.AddStrings("LOCAL_TEST_DATA_BINS", s.testProperties.Data_bins...)
}
entries.SetBoolIfTrue("LOCAL_COMPATIBILITY_PER_TESTCASE_DIRECTORY", Bool(s.testProperties.Per_testcase_directory))
+ if len(s.extraTestConfigs) > 0 {
+ entries.AddStrings("LOCAL_EXTRA_FULL_TEST_CONFIGS", s.extraTestConfigs.Strings()...)
+ }
s.testProperties.Test_options.SetAndroidMkEntries(entries)
},
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 5a50439..28f997d 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -176,6 +176,22 @@
android.AssertBoolEquals(t, "LOCAL_IS_UNIT_TEST", true, actualData)
}
+func TestShTestExtraTestConfig(t *testing.T) {
+ result, _ := testShBinary(t, `
+ sh_test {
+ name: "foo",
+ src: "test.sh",
+ filename: "test.sh",
+ extra_test_configs: ["config1.xml", "config2.xml"],
+ }
+ `)
+
+ mod := result.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+ entries := android.AndroidMkEntriesForTest(t, result, mod)[0]
+ actualData := entries.EntryMap["LOCAL_EXTRA_FULL_TEST_CONFIGS"]
+ android.AssertStringPathsRelativeToTopEquals(t, "extra_configs", result.Config(), []string{"config1.xml", "config2.xml"}, actualData)
+}
+
func TestShTestHost_dataDeviceModules(t *testing.T) {
ctx, config := testShBinary(t, `
sh_test_host {
diff --git a/tests/lib.sh b/tests/lib.sh
index 4c320d0..0e26de5 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -91,7 +91,6 @@
}
function create_mock_soong {
- create_mock_bazel
copy_directory build/blueprint
copy_directory build/soong
copy_directory build/make
@@ -143,41 +142,6 @@
USE_RBE=false TARGET_PRODUCT=aosp_arm TARGET_RELEASE=trunk_staging TARGET_BUILD_VARIANT=userdebug build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
}
-function create_mock_bazel {
- copy_directory build/bazel
- copy_directory build/bazel_common_rules
-
- # This requires pulling more tools into the mock top to build partitions
- delete_directory build/bazel/examples/partitions
-
- symlink_directory packages/modules/common/build
- symlink_directory prebuilts/bazel
- symlink_directory prebuilts/clang
- symlink_directory prebuilts/jdk
- symlink_directory external/bazel-skylib
- symlink_directory external/bazelbuild-rules_android
- symlink_directory external/bazelbuild-rules_go
- symlink_directory external/bazelbuild-rules_license
- symlink_directory external/bazelbuild-kotlin-rules
- symlink_directory external/bazelbuild-rules_cc
- symlink_directory external/bazelbuild-rules_python
- symlink_directory external/bazelbuild-rules_java
- symlink_directory external/bazelbuild-rules_rust
- symlink_directory external/bazelbuild-rules_testing
- symlink_directory external/rust/crates/tinyjson
-
- symlink_file WORKSPACE
- symlink_file BUILD
-}
-
-function run_bazel {
- # Remove the ninja_build output marker file to communicate to buildbot that this is not a regular Ninja build, and its
- # output should not be parsed as such.
- rm -rf out/ninja_build
-
- build/bazel/bin/bazel "$@"
-}
-
function run_ninja {
build/soong/soong_ui.bash --make-mode --skip-config --soong-only --skip-soong-tests "$@"
}
diff --git a/tradefed/providers.go b/tradefed/providers.go
index 0abac12..0ae841d 100644
--- a/tradefed/providers.go
+++ b/tradefed/providers.go
@@ -9,8 +9,8 @@
// Data that test_module_config[_host] modules types will need from
// their dependencies to write out build rules and AndroidMkEntries.
type BaseTestProviderData struct {
- // data files and apps for android_test
- InstalledFiles android.Paths
+ // data files and apps installed for tests, relative to testcases dir.
+ TestcaseRelDataFiles []string
// apk for android_test
OutputFile android.Path
// Either handwritten or generated TF xml.
@@ -28,6 +28,12 @@
LocalCertificate string
// Indicates if the base module was a unit test.
IsUnitTest bool
+ // The .mk file is used AndroidMkEntries for base (soong_java_prebuilt, etc.)
+ MkInclude string
+ // The AppClass to use for the AndroidMkEntries for the base.
+ MkAppClass string
+ // value for LOCAL_MODULE_PATH. The directory where the module is installed.
+ InstallDir android.InstallPath
}
var BaseTestProviderKey = blueprint.NewProvider[BaseTestProviderData]()
diff --git a/tradefed_modules/Android.bp b/tradefed_modules/Android.bp
index 9969ae2..a765a05 100644
--- a/tradefed_modules/Android.bp
+++ b/tradefed_modules/Android.bp
@@ -9,13 +9,16 @@
"blueprint",
"soong-android",
"soong-java",
+ "soong-sh",
"soong-tradefed",
],
srcs: [
"test_module_config.go",
+ "test_suite.go",
],
testSrcs: [
"test_module_config_test.go",
+ "test_suite_test.go",
],
pluginFor: ["soong_build"],
}
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index 7a04c19..988352c 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -196,7 +196,7 @@
module := &testModuleConfigModule{}
module.AddProperties(&module.tradefedProperties)
- android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+ android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
android.InitDefaultableModule(module)
return module
@@ -216,13 +216,28 @@
// Implements android.AndroidMkEntriesProvider
var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil)
+func (m *testModuleConfigModule) nativeExtraEntries(entries *android.AndroidMkEntries) {
+ // TODO(ron) provider for suffix and STEM?
+ entries.SetString("LOCAL_MODULE_SUFFIX", "")
+ // Should the stem and path use the base name or our module name?
+ entries.SetString("LOCAL_MODULE_STEM", m.provider.OutputFile.Rel())
+ entries.SetPath("LOCAL_MODULE_PATH", m.provider.InstallDir)
+}
+
+func (m *testModuleConfigModule) javaExtraEntries(entries *android.AndroidMkEntries) {
+ // The app_prebuilt_internal.mk files try create a copy of the OutputFile as an .apk.
+ // Normally, this copies the "package.apk" from the intermediate directory here.
+ // To prevent the copy of the large apk and to prevent confusion with the real .apk we
+ // link to, we set the STEM here to a bogus name and we set OutputFile to a small file (our manifest).
+ // We do this so we don't have to add more conditionals to base_rules.mk
+ // soong_java_prebult has the same issue for .jars so use this in both module types.
+ entries.SetString("LOCAL_MODULE_STEM", fmt.Sprintf("UNUSED-%s", *m.Base))
+ entries.SetString("LOCAL_MODULE_TAGS", "tests")
+}
+
func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries {
- appClass := "APPS"
- include := "$(BUILD_SYSTEM)/soong_app_prebuilt.mk"
- if m.isHost {
- appClass = "JAVA_LIBRARIES"
- include = "$(BUILD_SYSTEM)/soong_java_prebuilt.mk"
- }
+ appClass := m.provider.MkAppClass
+ include := m.provider.MkInclude
return []android.AndroidMkEntries{{
Class: appClass,
OutputFile: android.OptionalPathForPath(m.manifest),
@@ -231,7 +246,6 @@
ExtraEntries: []android.AndroidMkExtraEntriesFunc{
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
- entries.SetString("LOCAL_MODULE_TAGS", "tests")
entries.SetString("LOCAL_TEST_MODULE_CONFIG_BASE", *m.Base)
if m.provider.LocalSdkVersion != "" {
entries.SetString("LOCAL_SDK_VERSION", m.provider.LocalSdkVersion)
@@ -244,13 +258,11 @@
entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...)
- // The app_prebuilt_internal.mk files try create a copy of the OutputFile as an .apk.
- // Normally, this copies the "package.apk" from the intermediate directory here.
- // To prevent the copy of the large apk and to prevent confusion with the real .apk we
- // link to, we set the STEM here to a bogus name and we set OutputFile to a small file (our manifest).
- // We do this so we don't have to add more conditionals to base_rules.mk
- // soong_java_prebult has the same issue for .jars so use this in both module types.
- entries.SetString("LOCAL_MODULE_STEM", fmt.Sprintf("UNUSED-%s", *m.Base))
+ if m.provider.MkAppClass == "NATIVE_TESTS" {
+ m.nativeExtraEntries(entries)
+ } else {
+ m.javaExtraEntries(entries)
+ }
// In normal java/app modules, the module writes LOCAL_COMPATIBILITY_SUPPORT_FILES
// and then base_rules.mk ends up copying each of those dependencies from .intermediates to the install directory.
@@ -357,16 +369,19 @@
// FrameworksServicesTests
// └── x86_64
// └── FrameworksServicesTests.apk
- symlinkName := fmt.Sprintf("%s/%s", ctx.DeviceConfig().DeviceArch(), baseApk.Base())
- // Only android_test, not java_host_test puts the output in the DeviceArch dir.
- if m.provider.IsHost || ctx.DeviceConfig().DeviceArch() == "" {
- // testcases/CtsDevicePolicyManagerTestCases
- // ├── CtsDevicePolicyManagerTestCases.jar
- symlinkName = baseApk.Base()
+ if m.provider.MkAppClass != "NATIVE_TESTS" {
+ symlinkName := fmt.Sprintf("%s/%s", ctx.DeviceConfig().DeviceArch(), baseApk.Base())
+ // Only android_test, not java_host_test puts the output in the DeviceArch dir.
+ if m.provider.IsHost || ctx.DeviceConfig().DeviceArch() == "" {
+ // testcases/CtsDevicePolicyManagerTestCases
+ // ├── CtsDevicePolicyManagerTestCases.jar
+ symlinkName = baseApk.Base()
+ }
+
+ target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
+ installedApk := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
+ m.supportFiles = append(m.supportFiles, installedApk)
}
- target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
- installedApk := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
- m.supportFiles = append(m.supportFiles, installedApk)
// 3) Symlink for all data deps
// And like this for data files and required modules
@@ -374,8 +389,7 @@
// ├── data
// │ └── broken_shortcut.xml
// ├── JobTestApp.apk
- for _, f := range m.provider.InstalledFiles {
- symlinkName := f.Rel()
+ for _, symlinkName := range m.provider.TestcaseRelDataFiles {
target := installedBaseRelativeToHere(symlinkName, *m.tradefedProperties.Base)
installedPath := ctx.InstallAbsoluteSymlink(installDir, symlinkName, target)
m.supportFiles = append(m.supportFiles, installedPath)
@@ -383,10 +397,31 @@
// 4) Module.config / AndroidTest.xml
m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
+
+ // 5) We provide so we can be listed in test_suites.
+ android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
+ TestcaseRelDataFiles: testcaseRel(m.supportFiles.Paths()),
+ OutputFile: baseApk,
+ TestConfig: m.testConfig,
+ HostRequiredModuleNames: m.provider.HostRequiredModuleNames,
+ RequiredModuleNames: m.provider.RequiredModuleNames,
+ TestSuites: m.tradefedProperties.Test_suites,
+ IsHost: m.provider.IsHost,
+ LocalCertificate: m.provider.LocalCertificate,
+ IsUnitTest: m.provider.IsUnitTest,
+ })
}
var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
+func testcaseRel(paths android.Paths) []string {
+ relPaths := []string{}
+ for _, p := range paths {
+ relPaths = append(relPaths, p.Rel())
+ }
+ return relPaths
+}
+
// Given a relative path to a file in the current directory or a subdirectory,
// return a relative path under our sibling directory named `base`.
// There should be one "../" for each subdir we descend plus one to backup to "base".
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index cf6c7d1..efd4a04 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -16,6 +16,7 @@
import (
"android/soong/android"
"android/soong/java"
+ "android/soong/sh"
"fmt"
"strconv"
"strings"
@@ -54,6 +55,8 @@
`
+const variant = "android_arm64_armv8-a"
+
// Ensure we create files needed and set the AndroidMkEntries needed
func TestModuleConfigAndroidTest(t *testing.T) {
@@ -62,7 +65,7 @@
android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
).RunTestWithBp(t, bp)
- derived := ctx.ModuleForTests("derived_test", "android_common")
+ derived := ctx.ModuleForTests("derived_test", variant)
// Assert there are rules to create these files.
derived.Output("test_module_config.manifest")
derived.Output("test_config_fixer/derived_test.config")
@@ -88,7 +91,7 @@
// And some new derived entries are there.
android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE_TAGS"], []string{"tests"})
- android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
+ android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], fmt.Sprintf("derived_test/%s/test_config_fixer/derived_test.config", variant))
// Check the footer lines. Our support files should depend on base's support files.
convertedActual := make([]string, 5)
@@ -105,6 +108,80 @@
})
}
+func TestModuleConfigShTest(t *testing.T) {
+ ctx := android.GroupFixturePreparers(
+ sh.PrepareForTestWithShBuildComponents,
+ android.PrepareForTestWithAndroidBuildComponents,
+ android.FixtureMergeMockFs(android.MockFS{
+ "test.sh": nil,
+ "testdata/data1": nil,
+ "testdata/sub/data2": nil,
+ }),
+ android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+ ).RunTestWithBp(t, `
+ sh_test {
+ name: "shell_test",
+ src: "test.sh",
+ filename: "test.sh",
+ test_suites: ["general-tests"],
+ data: [
+ "testdata/data1",
+ "testdata/sub/data2",
+ ],
+ }
+ test_module_config {
+ name: "conch",
+ base: "shell_test",
+ test_suites: ["general-tests"],
+ options: [{name: "SomeName", value: "OptionValue"}],
+ }
+ `)
+ derived := ctx.ModuleForTests("conch", variant) //
+ conch := derived.Module().(*testModuleConfigModule)
+ android.AssertArrayString(t, "TestcaseRelDataFiles", []string{"arm64/testdata/data1", "arm64/testdata/sub/data2"}, conch.provider.TestcaseRelDataFiles)
+ android.AssertStringEquals(t, "Rel OutputFile", "test.sh", conch.provider.OutputFile.Rel())
+
+ // Assert there are rules to create these files.
+ derived.Output("test_module_config.manifest")
+ derived.Output("test_config_fixer/conch.config")
+
+ // Ensure some basic rules exist.
+ entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
+
+ // Ensure some entries from base are there, specifically support files for data and helper apps.
+ // Do not use LOCAL_COMPATIBILITY_SUPPORT_FILES, but instead use LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES
+ android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
+ []string{"out/soong/target/product/test_device/testcases/conch/arm64/testdata/data1",
+ "out/soong/target/product/test_device/testcases/conch/arm64/testdata/sub/data2"},
+ entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
+ android.AssertArrayString(t, "", entries.EntryMap["LOCAL_COMPATIBILITY_SUPPORT_FILES"], []string{})
+
+ android.AssertStringEquals(t, "app class", "NATIVE_TESTS", entries.Class)
+ android.AssertArrayString(t, "required modules", []string{"shell_test"}, entries.EntryMap["LOCAL_REQUIRED_MODULES"])
+ android.AssertArrayString(t, "host required modules", []string{}, entries.EntryMap["LOCAL_HOST_REQUIRED_MODULES"])
+ android.AssertArrayString(t, "cert", []string{}, entries.EntryMap["LOCAL_CERTIFICATE"])
+
+ // And some new derived entries are there.
+ android.AssertArrayString(t, "tags", []string{}, entries.EntryMap["LOCAL_MODULE_TAGS"])
+
+ android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0],
+ fmt.Sprintf("conch/%s/test_config_fixer/conch.config", variant))
+
+ // Check the footer lines. Our support files should depend on base's support files.
+ convertedActual := make([]string, 4)
+ for i, e := range entries.FooterLinesForTests() {
+ // AssertStringPathsRelativeToTop doesn't replace both instances
+ convertedActual[i] = strings.Replace(e, ctx.Config.SoongOutDir(), "", 2)
+ }
+ android.AssertArrayString(t, fmt.Sprintf("%s", ctx.Config.SoongOutDir()), convertedActual, []string{
+ "include $(BUILD_SYSTEM)/soong_cc_rust_prebuilt.mk",
+ "/target/product/test_device/testcases/conch/arm64/testdata/data1: /target/product/test_device/testcases/shell_test/arm64/testdata/data1",
+ "/target/product/test_device/testcases/conch/arm64/testdata/sub/data2: /target/product/test_device/testcases/shell_test/arm64/testdata/sub/data2",
+ "",
+ })
+
+}
+
// Make sure we call test-config-fixer with the right args.
func TestModuleConfigOptions(t *testing.T) {
@@ -114,7 +191,7 @@
).RunTestWithBp(t, bp)
// Check that we generate a rule to make a new AndroidTest.xml/Module.config file.
- derived := ctx.ModuleForTests("derived_test", "android_common")
+ derived := ctx.ModuleForTests("derived_test", variant)
rule_cmd := derived.Rule("fix_test_config").RuleParams.Command
android.AssertStringDoesContain(t, "Bad FixConfig rule inputs", rule_cmd,
`--test-runner-options='[{"Name":"exclude-filter","Key":"","Value":"android.test.example.devcodelab.DevCodelabTest#testHelloFail"},{"Name":"include-annotation","Key":"","Value":"android.platform.test.annotations.LargeTest"}]'`)
@@ -211,8 +288,7 @@
).ExtendWithErrorHandler(
android.FixtureExpectsAtLeastOneErrorMatchingPattern("Test options must be given")).
RunTestWithBp(t, badBp)
-
- ctx.ModuleForTests("derived_test", "android_common")
+ ctx.ModuleForTests("derived_test", variant)
}
func TestModuleConfigMultipleDerivedTestsWriteDistinctMakeEntries(t *testing.T) {
@@ -250,7 +326,7 @@
).RunTestWithBp(t, multiBp)
{
- derived := ctx.ModuleForTests("derived_test", "android_common")
+ derived := ctx.ModuleForTests("derived_test", variant)
entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
// All these should be the same in both derived tests
android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
@@ -260,13 +336,13 @@
entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
// Except this one, which points to the updated tradefed xml file.
- android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "derived_test/android_common/test_config_fixer/derived_test.config")
+ android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], fmt.Sprintf("derived_test/%s/test_config_fixer/derived_test.config", variant))
// And this one, the module name.
android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"derived_test"})
}
{
- derived := ctx.ModuleForTests("another_derived_test", "android_common")
+ derived := ctx.ModuleForTests("another_derived_test", variant)
entries := android.AndroidMkEntriesForTest(t, ctx.TestContext, derived.Module())[0]
// All these should be the same in both derived tests
android.AssertStringPathsRelativeToTopEquals(t, "support-files", ctx.Config,
@@ -275,7 +351,8 @@
"out/soong/target/product/test_device/testcases/another_derived_test/data/testfile"},
entries.EntryMap["LOCAL_SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES"])
// Except this one, which points to the updated tradefed xml file.
- android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0], "another_derived_test/android_common/test_config_fixer/another_derived_test.config")
+ android.AssertStringMatches(t, "", entries.EntryMap["LOCAL_FULL_TEST_CONFIG"][0],
+ fmt.Sprintf("another_derived_test/%s/test_config_fixer/another_derived_test.config", variant))
// And this one, the module name.
android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"another_derived_test"})
}
diff --git a/tradefed_modules/test_suite.go b/tradefed_modules/test_suite.go
new file mode 100644
index 0000000..00585f5
--- /dev/null
+++ b/tradefed_modules/test_suite.go
@@ -0,0 +1,173 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tradefed_modules
+
+import (
+ "encoding/json"
+ "path"
+ "path/filepath"
+
+ "android/soong/android"
+ "android/soong/tradefed"
+ "github.com/google/blueprint"
+)
+
+const testSuiteModuleType = "test_suite"
+
+type testSuiteTag struct{
+ blueprint.BaseDependencyTag
+}
+
+type testSuiteManifest struct {
+ Name string `json:"name"`
+ Files []string `json:"files"`
+}
+
+func init() {
+ RegisterTestSuiteBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType(testSuiteModuleType, TestSuiteFactory)
+}
+
+var PrepareForTestWithTestSuiteBuildComponents = android.GroupFixturePreparers(
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+)
+
+type testSuiteProperties struct {
+ Description string
+ Tests []string `android:"path,arch_variant"`
+}
+
+type testSuiteModule struct {
+ android.ModuleBase
+ android.DefaultableModuleBase
+ testSuiteProperties
+}
+
+func (t *testSuiteModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ for _, test := range t.Tests {
+ if ctx.OtherModuleDependencyVariantExists(ctx.Config().BuildOSCommonTarget.Variations(), test) {
+ // Host tests.
+ ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testSuiteTag{}, test)
+ } else {
+ // Target tests.
+ ctx.AddDependency(ctx.Module(), testSuiteTag{}, test)
+ }
+ }
+}
+
+func (t *testSuiteModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ suiteName := ctx.ModuleName()
+ modulesByName := make(map[string]android.Module)
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ // Recurse into test_suite dependencies.
+ if ctx.OtherModuleType(child) == testSuiteModuleType {
+ ctx.Phony(suiteName, android.PathForPhony(ctx, child.Name()))
+ return true
+ }
+
+ // Only write out top level test suite dependencies here.
+ if _, ok := ctx.OtherModuleDependencyTag(child).(testSuiteTag); !ok {
+ return false
+ }
+
+ if !child.InstallInTestcases() {
+ ctx.ModuleErrorf("test_suite only supports modules installed in testcases. %q is not installed in testcases.", child.Name())
+ return false
+ }
+
+ modulesByName[child.Name()] = child
+ return false
+ })
+
+ var files []string
+ for name, module := range modulesByName {
+ // Get the test provider data from the child.
+ tp, ok := android.OtherModuleProvider(ctx, module, tradefed.BaseTestProviderKey)
+ if !ok {
+ // TODO: Consider printing out a list of all module types.
+ ctx.ModuleErrorf("%q is not a test module.", name)
+ continue
+ }
+
+ files = append(files, packageModuleFiles(ctx, suiteName, module, tp)...)
+ ctx.Phony(suiteName, android.PathForPhony(ctx, name))
+ }
+
+ manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
+ b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: files})
+ if err != nil {
+ ctx.ModuleErrorf("Failed to marshal manifest: %v", err)
+ return
+ }
+ android.WriteFileRule(ctx, manifestPath, string(b))
+
+ ctx.Phony(suiteName, manifestPath)
+}
+
+func TestSuiteFactory() android.Module {
+ module := &testSuiteModule{}
+ module.AddProperties(&module.testSuiteProperties)
+
+ android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+
+ return module
+}
+
+func packageModuleFiles(ctx android.ModuleContext, suiteName string, module android.Module, tp tradefed.BaseTestProviderData) []string {
+
+ hostOrTarget := "target"
+ if tp.IsHost {
+ hostOrTarget = "host"
+ }
+
+ // suiteRoot at out/soong/packaging/<suiteName>.
+ suiteRoot := android.PathForSuiteInstall(ctx, suiteName)
+
+ var installed android.InstallPaths
+ // Install links to installed files from the module.
+ if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
+ for _, f := range installFilesInfo.InstallFiles {
+ // rel is anything under .../<partition>, normally under .../testcases.
+ rel := android.Rel(ctx, f.PartitionDir(), f.String())
+
+ // Install the file under <suiteRoot>/<host|target>/<partition>.
+ installDir := suiteRoot.Join(ctx, hostOrTarget, f.Partition(), path.Dir(rel))
+ linkTo, err := filepath.Rel(installDir.String(), f.String())
+ if err != nil {
+ ctx.ModuleErrorf("Failed to get relative path from %s to %s: %v", installDir.String(), f.String(), err)
+ continue
+ }
+ installed = append(installed, ctx.InstallAbsoluteSymlink(installDir, path.Base(rel), linkTo))
+ }
+ }
+
+ // Install config file.
+ if tp.TestConfig != nil {
+ moduleRoot := suiteRoot.Join(ctx, hostOrTarget, "testcases", module.Name())
+ installed = append(installed, ctx.InstallFile(moduleRoot, module.Name() + ".config", tp.TestConfig))
+ }
+
+ // Add to phony and manifest, manifestpaths are relative to suiteRoot.
+ var manifestEntries []string
+ for _, f := range installed {
+ manifestEntries = append(manifestEntries, android.Rel(ctx, suiteRoot.String(), f.String()))
+ ctx.Phony(suiteName, f)
+ }
+ return manifestEntries
+}
diff --git a/tradefed_modules/test_suite_test.go b/tradefed_modules/test_suite_test.go
new file mode 100644
index 0000000..3c0a9eb
--- /dev/null
+++ b/tradefed_modules/test_suite_test.go
@@ -0,0 +1,151 @@
+// Copyright 2024 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package tradefed_modules
+
+import (
+ "android/soong/android"
+ "android/soong/java"
+ "encoding/json"
+ "slices"
+ "testing"
+)
+
+func TestTestSuites(t *testing.T) {
+ t.Parallel()
+ ctx := android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+ ).RunTestWithBp(t, `
+ android_test {
+ name: "TestModule1",
+ sdk_version: "current",
+ }
+
+ android_test {
+ name: "TestModule2",
+ sdk_version: "current",
+ }
+
+ test_suite {
+ name: "my-suite",
+ description: "a test suite",
+ tests: [
+ "TestModule1",
+ "TestModule2",
+ ]
+ }
+ `)
+ manifestPath := ctx.ModuleForTests("my-suite", "android_common").Output("out/soong/test_suites/my-suite/my-suite.json")
+ var actual testSuiteManifest
+ if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
+ t.Errorf("failed to unmarshal manifest: %v", err)
+ }
+ slices.Sort(actual.Files)
+
+ expected := testSuiteManifest{
+ Name: "my-suite",
+ Files: []string{
+ "target/testcases/TestModule1/TestModule1.config",
+ "target/testcases/TestModule1/arm64/TestModule1.apk",
+ "target/testcases/TestModule2/TestModule2.config",
+ "target/testcases/TestModule2/arm64/TestModule2.apk",
+ },
+ }
+
+ android.AssertDeepEquals(t, "manifests differ", expected, actual)
+}
+
+func TestTestSuitesWithNested(t *testing.T) {
+ t.Parallel()
+ ctx := android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+ ).RunTestWithBp(t, `
+ android_test {
+ name: "TestModule1",
+ sdk_version: "current",
+ }
+
+ android_test {
+ name: "TestModule2",
+ sdk_version: "current",
+ }
+
+ android_test {
+ name: "TestModule3",
+ sdk_version: "current",
+ }
+
+ test_suite {
+ name: "my-child-suite",
+ description: "a child test suite",
+ tests: [
+ "TestModule1",
+ "TestModule2",
+ ]
+ }
+
+ test_suite {
+ name: "my-all-tests-suite",
+ description: "a parent test suite",
+ tests: [
+ "TestModule1",
+ "TestModule3",
+ "my-child-suite",
+ ]
+ }
+ `)
+ manifestPath := ctx.ModuleForTests("my-all-tests-suite", "android_common").Output("out/soong/test_suites/my-all-tests-suite/my-all-tests-suite.json")
+ var actual testSuiteManifest
+ if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
+ t.Errorf("failed to unmarshal manifest: %v", err)
+ }
+ slices.Sort(actual.Files)
+
+ expected := testSuiteManifest{
+ Name: "my-all-tests-suite",
+ Files: []string{
+ "target/testcases/TestModule1/TestModule1.config",
+ "target/testcases/TestModule1/arm64/TestModule1.apk",
+ "target/testcases/TestModule2/TestModule2.config",
+ "target/testcases/TestModule2/arm64/TestModule2.apk",
+ "target/testcases/TestModule3/TestModule3.config",
+ "target/testcases/TestModule3/arm64/TestModule3.apk",
+ },
+ }
+
+ android.AssertDeepEquals(t, "manifests differ", expected, actual)
+}
+
+func TestTestSuitesNotInstalledInTestcases(t *testing.T) {
+ t.Parallel()
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{
+ `"SomeHostTest" is not installed in testcases`,
+ })).RunTestWithBp(t, `
+ java_test_host {
+ name: "SomeHostTest",
+ srcs: ["a.java"],
+ }
+ test_suite {
+ name: "my-suite",
+ description: "a test suite",
+ tests: [
+ "SomeHostTest",
+ ]
+ }
+ `)
+}