diff options
59 files changed, 2647 insertions, 928 deletions
diff --git a/Android.bp b/Android.bp index cbe1c7b3f..42b7d83f7 100644 --- a/Android.bp +++ b/Android.bp @@ -206,3 +206,30 @@ build_prop { relative_install_path: "etc", // odm/etc/build.prop visibility: ["//visibility:private"], } + +build_prop { + name: "system_dlkm-build.prop", + stem: "build.prop", + system_dlkm_specific: true, + product_config: ":product_config", + relative_install_path: "etc", // system_dlkm/etc/build.prop + visibility: ["//visibility:private"], +} + +build_prop { + name: "vendor_dlkm-build.prop", + stem: "build.prop", + vendor_dlkm_specific: true, + product_config: ":product_config", + relative_install_path: "etc", // vendor_dlkm/etc/build.prop + visibility: ["//visibility:private"], +} + +build_prop { + name: "odm_dlkm-build.prop", + stem: "build.prop", + odm_dlkm_specific: true, + product_config: ":product_config", + relative_install_path: "etc", // odm_dlkm/etc/build.prop + visibility: ["//visibility:private"], +} diff --git a/android/Android.bp b/android/Android.bp index cf707bd83..a9a3564ab 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -31,6 +31,7 @@ bootstrap_go_package { srcs: [ "aconfig_providers.go", "all_teams.go", + "android_info.go", "androidmk.go", "apex.go", "apex_contributions.go", @@ -121,6 +122,7 @@ bootstrap_go_package { "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 000000000..a8d3d4e2c --- /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 79ab13caf..e73b3e662 100644 --- a/android/apex.go +++ b/android/apex.go @@ -833,7 +833,7 @@ func UpdateDirectlyInAnyApex(mctx BottomUpMutatorContext, am ApexModule) { // If this is the FinalModule (last visited module) copy // AnyVariantDirectlyInAnyApex to all the other variants - if am == mctx.FinalModule().(ApexModule) { + if mctx.IsFinalModule(am) { mctx.VisitAllModuleVariants(func(variant Module) { variant.(ApexModule).apexModuleBase().ApexProperties.AnyVariantDirectlyInAnyApex = base.ApexProperties.AnyVariantDirectlyInAnyApex diff --git a/android/base_module_context.go b/android/base_module_context.go index 223b5341d..060fae5bc 100644 --- a/android/base_module_context.go +++ b/android/base_module_context.go @@ -17,6 +17,7 @@ package android import ( "fmt" "regexp" + "slices" "strings" "github.com/google/blueprint" @@ -197,9 +198,15 @@ type BaseModuleContext interface { // singleton actions that are only done once for all variants of a module. FinalModule() Module + // IsFinalModule returns if the current module is the last variant. Variants of a module are always visited in + // order by mutators and GenerateBuildActions, so the data created by the current mutator can be read from all + // variants using VisitAllModuleVariants if the current module is the last one. This can be used to perform + // singleton actions that are only done once for all variants of a module. + IsFinalModule(module Module) bool + // VisitAllModuleVariants calls visit for each variant of the current module. Variants of a module are always // visited in order by mutators and GenerateBuildActions, so the data created by the current mutator can be read - // from all variants if the current module == FinalModule(). Otherwise, care must be taken to not access any + // from all variants if the current module is the last one. Otherwise, care must be taken to not access any // data modified by the current mutator. VisitAllModuleVariants(visit func(Module)) @@ -570,7 +577,7 @@ func (b *baseModuleContext) WalkDepsProxy(visit func(ModuleProxy, ModuleProxy) b } func (b *baseModuleContext) GetWalkPath() []Module { - return b.walkPath + return slices.Clone(b.walkPath) } func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag { @@ -591,6 +598,10 @@ func (b *baseModuleContext) FinalModule() Module { return b.bp.FinalModule().(Module) } +func (b *baseModuleContext) IsFinalModule(module Module) bool { + return b.bp.IsFinalModule(module) +} + // IsMetaDependencyTag returns true for cross-cutting metadata dependencies. func IsMetaDependencyTag(tag blueprint.DependencyTag) bool { if tag == licenseKindTag { diff --git a/android/build_prop.go b/android/build_prop.go index 7c3c50645..838947045 100644 --- a/android/build_prop.go +++ b/android/build_prop.go @@ -19,8 +19,12 @@ import ( ) 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 @@ type buildPropProperties struct { // 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 @@ func (p *buildPropModule) propFiles(ctx ModuleContext) Paths { } 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 @@ func (p *buildPropModule) partition(config DeviceConfig) string { return "product" } else if p.SystemExtSpecific() { return "system_ext" + } else if p.InstallInSystemDlkm() { + return "system_dlkm" + } else if p.InstallInVendorDlkm() { + return "vendor_dlkm" + } else if p.InstallInOdmDlkm() { + return "odm_dlkm" } return "system" } @@ -108,16 +125,18 @@ var validPartitions = []string{ "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 000000000..e75975a0a --- /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 16d77db6b..d9db64ef6 100644 --- a/android/config.go +++ b/android/config.go @@ -867,7 +867,7 @@ func (c *config) IsEnvFalse(key string) bool { } func (c *config) TargetsJava21() bool { - return c.IsEnvTrue("EXPERIMENTAL_TARGET_JAVA_VERSION_21") + return c.productVariables.GetBuildFlagBool("RELEASE_TARGET_JAVA_21") } // EnvDeps returns the environment variables this build depends on. The first @@ -1522,6 +1522,13 @@ func (c *deviceConfig) VendorPath() string { 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 @@ func (c *deviceConfig) BuildingOdmImage() bool { 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,20 @@ func (c *deviceConfig) SystemExtPath() string { 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) BtConfigIncludeDir() string { return String(c.config.productVariables.BtConfigIncludeDir) } diff --git a/android/filegroup_test.go b/android/filegroup_test.go index 14e9368ca..670037d56 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 1ace34494..d3a13d0ed 100644 --- a/android/init.go +++ b/android/init.go @@ -20,6 +20,7 @@ func init() { 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 a918d6ea3..58ae885c8 100644 --- a/android/module.go +++ b/android/module.go @@ -81,6 +81,9 @@ type Module interface { InstallInProduct() bool InstallInVendor() bool InstallInSystemExt() bool + InstallInSystemDlkm() bool + InstallInVendorDlkm() bool + InstallInOdmDlkm() bool InstallForceOS() (*OsType, *ArchType) PartitionTag(DeviceConfig) string HideFromMake() @@ -386,6 +389,15 @@ type commonProperties struct { // Whether this module is installed to debug ramdisk Debug_ramdisk *bool + // Install to partition system_dlkm when set to true. + System_dlkm_specific *bool + + // Install to partition vendor_dlkm when set to true. + Vendor_dlkm_specific *bool + + // Install to partition odm_dlkm when set to true. + Odm_dlkm_specific *bool + // Whether this module is built for non-native architectures (also known as native bridge binary) Native_bridge_supported *bool `android:"arch_variant"` @@ -1535,6 +1547,18 @@ func (m *ModuleBase) InstallInRoot() bool { return false } +func (m *ModuleBase) InstallInSystemDlkm() bool { + return Bool(m.commonProperties.System_dlkm_specific) +} + +func (m *ModuleBase) InstallInVendorDlkm() bool { + return Bool(m.commonProperties.Vendor_dlkm_specific) +} + +func (m *ModuleBase) InstallInOdmDlkm() bool { + return Bool(m.commonProperties.Odm_dlkm_specific) +} + func (m *ModuleBase) InstallForceOS() (*OsType, *ArchType) { return nil, nil } @@ -1822,6 +1846,8 @@ type CommonPropertiesProviderData struct { 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 +2036,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) ctx.GetMissingDependencies() } - if m == ctx.FinalModule().(Module).base() { + if ctx.IsFinalModule(m.module) { m.generateModuleTarget(ctx) if ctx.Failed() { return @@ -2086,6 +2112,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) 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 1f5e706ec..41cb0ccb2 100644 --- a/android/module_context.go +++ b/android/module_context.go @@ -196,6 +196,9 @@ type ModuleContext interface { InstallInOdm() bool InstallInProduct() bool InstallInVendor() bool + InstallInSystemDlkm() bool + InstallInVendorDlkm() bool + InstallInOdmDlkm() bool InstallForceOS() (*OsType, *ArchType) RequiredModuleNames(ctx ConfigurableEvaluatorContext) []string @@ -493,6 +496,18 @@ func (m *moduleContext) InstallInVendor() bool { return m.module.InstallInVendor() } +func (m *moduleContext) InstallInSystemDlkm() bool { + return m.module.InstallInSystemDlkm() +} + +func (m *moduleContext) InstallInVendorDlkm() bool { + return m.module.InstallInVendorDlkm() +} + +func (m *moduleContext) InstallInOdmDlkm() bool { + return m.module.InstallInOdmDlkm() +} + func (m *moduleContext) skipInstall() bool { if m.module.base().commonProperties.SkipInstall { return true diff --git a/android/module_proxy.go b/android/module_proxy.go index a60a5a810..1f9679926 100644 --- a/android/module_proxy.go +++ b/android/module_proxy.go @@ -106,6 +106,18 @@ func (m ModuleProxy) InstallInSystemExt() bool { panic("method is not implemented on ModuleProxy") } +func (m ModuleProxy) InstallInSystemDlkm() bool { + panic("method is not implemented on ModuleProxy") +} + +func (m ModuleProxy) InstallInVendorDlkm() bool { + panic("method is not implemented on ModuleProxy") +} + +func (m ModuleProxy) InstallInOdmDlkm() bool { + panic("method is not implemented on ModuleProxy") +} + func (m ModuleProxy) InstallForceOS() (*OsType, *ArchType) { panic("method is not implemented on ModuleProxy") } diff --git a/android/mutator.go b/android/mutator.go index 4ddc606b1..fdd16a889 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -70,7 +70,7 @@ type RegisterMutatorsContext interface { TopDown(name string, m TopDownMutator) MutatorHandle BottomUp(name string, m BottomUpMutator) MutatorHandle BottomUpBlueprint(name string, m blueprint.BottomUpMutator) MutatorHandle - Transition(name string, m TransitionMutator) + Transition(name string, m TransitionMutator) TransitionMutatorHandle } type RegisterMutatorFunc func(RegisterMutatorsContext) @@ -579,7 +579,7 @@ func (a *androidTransitionMutator) Mutate(ctx blueprint.BottomUpMutatorContext, } } -func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) { +func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) TransitionMutatorHandle { atm := &androidTransitionMutator{ finalPhase: x.finalPhase, mutator: m, @@ -587,8 +587,10 @@ func (x *registerMutatorsContext) Transition(name string, m TransitionMutator) { } mutator := &mutator{ name: name, - transitionMutator: atm} + transitionMutator: atm, + } x.mutators = append(x.mutators, mutator) + return mutator } func (x *registerMutatorsContext) mutatorName(name string) string { @@ -625,7 +627,10 @@ func (mutator *mutator) register(ctx *Context) { } else if mutator.topDownMutator != nil { handle = blueprintCtx.RegisterTopDownMutator(mutator.name, mutator.topDownMutator) } else if mutator.transitionMutator != nil { - blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator) + handle := blueprintCtx.RegisterTransitionMutator(mutator.name, mutator.transitionMutator) + if mutator.neverFar { + handle.NeverFar() + } } // Forward booleans set on the MutatorHandle to the blueprint.MutatorHandle. @@ -681,6 +686,14 @@ type MutatorHandle interface { MutatesGlobalState() MutatorHandle } +type TransitionMutatorHandle interface { + // NeverFar causes the variations created by this mutator to never be ignored when adding + // far variation dependencies. Normally, far variation dependencies ignore all the variants + // of the source module, and only use the variants explicitly requested by the + // AddFarVariationDependencies call. + NeverFar() MutatorHandle +} + func (mutator *mutator) Parallel() MutatorHandle { return mutator } @@ -715,6 +728,11 @@ func (mutator *mutator) MutatesGlobalState() MutatorHandle { return mutator } +func (mutator *mutator) NeverFar() MutatorHandle { + mutator.neverFar = true + return mutator +} + func RegisterComponentsMutator(ctx RegisterMutatorsContext) { ctx.BottomUp("component-deps", componentDepsMutator) } diff --git a/android/neverallow.go b/android/neverallow.go index 7fb22bf13..6176a996a 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -334,6 +334,10 @@ func createPrebuiltEtcBpDefineRule() Rule { "prebuilt_res", "prebuilt_wlc_upt", "prebuilt_odm", + "prebuilt_vendor_dlkm", + "prebuilt_bt_firmware", + "prebuilt_tvservice", + "prebuilt_optee", ). DefinedInBpFile(). Because("module type not allowed to be defined in bp file") diff --git a/android/paths.go b/android/paths.go index a7ee7ac01..9cb872d6f 100644 --- a/android/paths.go +++ b/android/paths.go @@ -117,6 +117,9 @@ type ModuleInstallPathContext interface { InstallInOdm() bool InstallInProduct() bool InstallInVendor() bool + InstallInSystemDlkm() bool + InstallInVendorDlkm() bool + InstallInOdmDlkm() bool InstallForceOS() (*OsType, *ArchType) } @@ -170,6 +173,18 @@ func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendor() bool { return ctx.Module().InstallInVendor() } +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInSystemDlkm() bool { + return ctx.Module().InstallInSystemDlkm() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInVendorDlkm() bool { + return ctx.Module().InstallInVendorDlkm() +} + +func (ctx *baseModuleContextToModuleInstallPathContext) InstallInOdmDlkm() bool { + return ctx.Module().InstallInOdmDlkm() +} + func (ctx *baseModuleContextToModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) { return ctx.Module().InstallForceOS() } @@ -2077,6 +2092,10 @@ func PathForMainlineSdksInstall(ctx PathContext, paths ...string) InstallPath { return base.Join(ctx, paths...) } +func PathForSuiteInstall(ctx PathContext, suite string, pathComponents ...string) InstallPath { + return pathForPartitionInstallDir(ctx, "test_suites", "test_suites", false).Join(ctx, suite).Join(ctx, pathComponents...) +} + func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string { rel := Rel(ctx, strings.TrimSuffix(path.PartitionDir(), path.partition), path.String()) return "/" + rel @@ -2131,6 +2150,12 @@ func modulePartition(ctx ModuleInstallPathContext, device bool) string { partition = ctx.DeviceConfig().SystemExtPath() } else if ctx.InstallInRoot() { partition = "root" + } else if ctx.InstallInSystemDlkm() { + partition = ctx.DeviceConfig().SystemDlkmPath() + } else if ctx.InstallInVendorDlkm() { + partition = ctx.DeviceConfig().VendorDlkmPath() + } else if ctx.InstallInOdmDlkm() { + partition = ctx.DeviceConfig().OdmDlkmPath() } else { partition = "system" } @@ -2334,6 +2359,9 @@ type testModuleInstallPathContext struct { inOdm bool inProduct bool inVendor bool + inSystemDlkm bool + inVendorDlkm bool + inOdmDlkm bool forceOS *OsType forceArch *ArchType } @@ -2388,6 +2416,18 @@ func (m testModuleInstallPathContext) InstallInVendor() bool { return m.inVendor } +func (m testModuleInstallPathContext) InstallInSystemDlkm() bool { + return m.inSystemDlkm +} + +func (m testModuleInstallPathContext) InstallInVendorDlkm() bool { + return m.inVendorDlkm +} + +func (m testModuleInstallPathContext) InstallInOdmDlkm() bool { + return m.inOdmDlkm +} + func (m testModuleInstallPathContext) InstallForceOS() (*OsType, *ArchType) { return m.forceOS, m.forceArch } diff --git a/android/register.go b/android/register.go index bb1ead73c..8d2f19e73 100644 --- a/android/register.go +++ b/android/register.go @@ -98,6 +98,7 @@ type mutator struct { usesCreateModule bool mutatesDependencies bool mutatesGlobalState bool + neverFar bool } var _ sortableComponent = &mutator{} diff --git a/android/singleton.go b/android/singleton.go index 913bf6a56..0754b0ccb 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -64,6 +64,7 @@ type SingletonContext interface { 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 @@ type SingletonContext interface { 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 @@ func (s *singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (st } // 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 @@ func visitAdaptor(visit func(Module)) func(blueprint.Module) { } } +// 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 @@ func (s *singletonContextAdaptor) VisitAllModules(visit func(Module)) { 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 @@ func (s *singletonContextAdaptor) VisitAllModuleVariants(module Module, visit fu 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/variable.go b/android/variable.go index df9db7c22..f82c9cae8 100644 --- a/android/variable.go +++ b/android/variable.go @@ -339,12 +339,16 @@ type ProductVariables struct { HWASanExcludePaths []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"` ClangTidy *bool `json:",omitempty"` TidyChecks *string `json:",omitempty"` @@ -573,6 +577,14 @@ type PartitionQualifiedVariablesType struct { BoardAvbRollbackIndexLocation string `json:",omitempty"` } +type ChainedAvbPartitionProps struct { + Partitions []string `json:",omitempty"` + Key string `json:",omitempty"` + Algorithm string `json:",omitempty"` + RollbackIndex string `json:",omitempty"` + RollbackIndexLocation string `json:",omitempty"` +} + type PartitionVariables struct { ProductDirectory string `json:",omitempty"` PartitionQualifiedVariables map[string]PartitionQualifiedVariablesType @@ -597,13 +609,21 @@ type PartitionVariables struct { ProductUseDynamicPartitionSize bool `json:",omitempty"` CopyImagesForTargetFilesZip bool `json:",omitempty"` - BoardAvbEnable bool `json:",omitempty"` + BoardAvbEnable bool `json:",omitempty"` + BoardAvbAlgorithm string `json:",omitempty"` + BoardAvbKeyPath string `json:",omitempty"` + BoardAvbRollbackIndex string `json:",omitempty"` + BuildingVbmetaImage bool `json:",omitempty"` + ChainedVbmetaPartitions map[string]ChainedAvbPartitionProps `json:",omitempty"` ProductPackages []string `json:",omitempty"` ProductPackagesDebug []string `json:",omitempty"` VendorLinkerConfigSrcs []string `json:",omitempty"` ProductLinkerConfigSrcs []string `json:",omitempty"` + BoardInfoFiles []string `json:",omitempty"` + BootLoaderBoardName string `json:",omitempty"` + ProductCopyFiles map[string]string `json:",omitempty"` BuildingSystemDlkmImage bool `json:",omitempty"` diff --git a/apex/Android.bp b/apex/Android.bp index 0e2f564b9..870ca7e1e 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -7,6 +7,7 @@ bootstrap_go_package { 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 80af9c5b3..587f63fe1 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -20,10 +20,12 @@ import ( "fmt" "path/filepath" "regexp" + "slices" "sort" "strings" "github.com/google/blueprint" + "github.com/google/blueprint/depset" "github.com/google/blueprint/proptools" "android/soong/android" @@ -464,6 +466,12 @@ type apexBundle struct { // 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 @@ -1877,6 +1885,14 @@ type visitorContext struct { // 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 +1903,7 @@ func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { // 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 +1914,8 @@ func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { 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 +1930,7 @@ func (vctx *visitorContext) normalizeFileInfo(mctx android.ModuleContext) { 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 +2361,8 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // 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 +2389,8 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { 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 @@ -2920,3 +2944,105 @@ func rBcpPackages() map[string][]string { 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 988c1ce7e..4e6aa1353 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -20,6 +20,7 @@ import ( "path/filepath" "reflect" "regexp" + "slices" "sort" "strconv" "strings" @@ -28,6 +29,7 @@ import ( "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 @@ var prepareForTestWithMyapex = android.FixtureMergeMockFs(android.MockFS{ "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 TestApexPermittedPackagesRules(t *testing.T) { } } -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 @@ -12114,3 +11930,398 @@ func TestFilesystemWithApexDeps(t *testing.T) { 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/classpath_element_test.go b/apex/classpath_element_test.go index f8e889983..f3671743a 100644 --- a/apex/classpath_element_test.go +++ b/apex/classpath_element_test.go @@ -45,10 +45,7 @@ func TestCreateClasspathElements(t *testing.T) { 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 a7b6d8161..3b29ae8cf 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -122,3 +122,31 @@ bootstrap_go_package { // Used by plugins visibility: ["//visibility:public"], } + +phony { + name: "llndk_libs", + required: [ + "libEGL", + "libGLESv1_CM", + "libGLESv2", + "libGLESv3", + "libRS", + "libandroid_net", + "libapexsupport", + "libbinder_ndk", + "libc", + "libcgrouprc", + "libclang_rt.asan", + "libdl", + "libft2", + "liblog", + "libm", + "libmediandk", + "libnativewindow", + "libselinux", + "libsync", + "libvendorsupport", + "libvndksupport", + "libvulkan", + ], +} @@ -19,8 +19,10 @@ package cc // is handled in builder.go import ( + "errors" "fmt" "io" + "slices" "strconv" "strings" @@ -43,6 +45,14 @@ type CcMakeVarsInfo struct { 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 @@ type PathDeps struct { // 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 @@ -648,10 +661,6 @@ type installer interface { installInRoot() bool } -type xref interface { - XrefCcFiles() android.Paths -} - type overridable interface { overriddenModules() []string } @@ -900,12 +909,6 @@ type Module struct { 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 @@ -1473,10 +1476,6 @@ func InstallToBootstrap(name string, config android.Config) bool { 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 +1566,11 @@ func (ctx *moduleContextImpl) minSdkVersion() string { } if ctx.ctx.Device() { - config := ctx.ctx.Config() - if ctx.inVendor() { - // If building for vendor with final API, then use the latest _stable_ API as "current". - if config.VendorApiLevelFrozen() && (ver == "" || ver == "current") { - ver = config.PlatformSdkVersion().String() - } + // When building for vendor/product, use the latest _stable_ API as "current". + // This is passed to clang/aidl compilers so that compiled/generated code works + // with the system. + if (ctx.inVendor() || ctx.inProduct()) && (ver == "" || ver == "current") { + ver = ctx.ctx.Config().PlatformSdkVersion().String() } } @@ -2048,9 +2046,6 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { if ctx.Failed() { return } - c.kytheFiles = objs.kytheFiles - c.objFiles = objs.objFiles - c.tidyFiles = objs.tidyFiles } if c.linker != nil { @@ -2061,6 +2056,10 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { 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 +2114,17 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { 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 +2132,12 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } } +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 +2300,10 @@ func (c *Module) deps(ctx DepsContext) Deps { 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 +2331,26 @@ func (c *Module) deps(ctx DepsContext) Deps { 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 +2412,9 @@ func AddSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext, mo 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 +3113,13 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { 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 +3360,6 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { 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 +3389,6 @@ func ShouldUseStubForApex(ctx android.ModuleContext, dep android.Module) bool { 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. @@ -3958,9 +3976,10 @@ type kytheExtractAllSingleton struct { 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 e90670672..bcf0a2c5d 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -1505,111 +1505,6 @@ func TestVersionedStubs(t *testing.T) { } } -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 @@ func TestImageVariants(t *testing.T) { testDepWithVariant("product") } -func TestVendorSdkVersion(t *testing.T) { +func TestVendorOrProductVariantUsesPlatformSdkVersionAsDefault(t *testing.T) { t.Parallel() bp := ` @@ -3266,31 +3161,29 @@ func TestVendorSdkVersion(t *testing.T) { name: "libfoo", srcs: ["libfoo.cc"], vendor_available: true, + product_available: true, } cc_library { name: "libbar", srcs: ["libbar.cc"], vendor_available: true, + product_available: true, min_sdk_version: "29", } ` ctx := prepareForCcTest.RunTestWithBp(t, bp) - testSdkVersionFlag := func(module, version string) { - flags := ctx.ModuleForTests(module, "android_vendor_arm64_armv8-a_static").Rule("cc").Args["cFlags"] - android.AssertStringDoesContain(t, "min sdk version", flags, "-target aarch64-linux-android"+version) + testSdkVersionFlag := func(module, variant, version string) { + flags := ctx.ModuleForTests(module, "android_"+variant+"_arm64_armv8-a_static").Rule("cc").Args["cFlags"] + android.AssertStringDoesContain(t, "target SDK version", flags, "-target aarch64-linux-android"+version) } - testSdkVersionFlag("libfoo", "10000") - testSdkVersionFlag("libbar", "29") - - ctx = android.GroupFixturePreparers( - prepareForCcTest, - android.PrepareForTestWithBuildFlag("RELEASE_BOARD_API_LEVEL_FROZEN", "true"), - ).RunTestWithBp(t, bp) - testSdkVersionFlag("libfoo", "30") - testSdkVersionFlag("libbar", "29") + testSdkVersionFlag("libfoo", "vendor", "30") + testSdkVersionFlag("libfoo", "product", "30") + // target SDK version can be set explicitly with min_sdk_version + testSdkVersionFlag("libbar", "vendor", "29") + testSdkVersionFlag("libbar", "product", "29") } func TestClangVerify(t *testing.T) { @@ -3321,3 +3214,20 @@ func TestClangVerify(t *testing.T) { 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/compiler.go b/cc/compiler.go index 0fa058a35..91f107c29 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -78,11 +78,11 @@ type BaseCompilerProperties struct { // If possible, don't use this. If adding paths from the current directory use // local_include_dirs, if adding paths from other modules use export_include_dirs in // that module. - Include_dirs []string `android:"arch_variant,variant_prepend"` + Include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"` // list of directories relative to the Blueprints file that will // be added to the include path using -I - Local_include_dirs []string `android:"arch_variant,variant_prepend"` + Local_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"` // Add the directory containing the Android.bp file to the list of include // directories. Defaults to true. @@ -411,13 +411,13 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps } // Include dir cflags - localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs) + localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs.GetOrDefault(ctx, nil)) if len(localIncludeDirs) > 0 { f := includeDirsToFlags(localIncludeDirs) flags.Local.CommonFlags = append(flags.Local.CommonFlags, f) flags.Local.YasmFlags = append(flags.Local.YasmFlags, f) } - rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs) + rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs.GetOrDefault(ctx, nil)) if len(rootIncludeDirs) > 0 { f := includeDirsToFlags(rootIncludeDirs) flags.Local.CommonFlags = append(flags.Local.CommonFlags, f) @@ -807,7 +807,7 @@ func compileObjs(ctx ModuleContext, flags builderFlags, subdir string, type RustBindgenClangProperties struct { // list of directories relative to the Blueprints file that will // be added to the include path using -I - Local_include_dirs []string `android:"arch_variant,variant_prepend"` + Local_include_dirs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"` // list of static libraries that provide headers for this binding. Static_libs proptools.Configurable[[]string] `android:"arch_variant,variant_prepend"` diff --git a/cc/library.go b/cc/library.go index 1f2161494..7dffa7266 100644 --- a/cc/library.go +++ b/cc/library.go @@ -1194,6 +1194,7 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext, SharedLibrary: unstrippedOutputFile, TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering, Target: ctx.Target(), + IsStubs: library.buildStubs(), }) addStubDependencyProviders(ctx) diff --git a/cc/linkable.go b/cc/linkable.go index cd33e2855..ef204eb91 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -317,7 +317,9 @@ type SharedLibraryInfo struct { 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 @@ type FlagExporterInfo struct { } 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 7c8729709..ba4c662c6 100644 --- a/cc/prebuilt.go +++ b/cc/prebuilt.go @@ -221,6 +221,7 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext, Target: ctx.Target(), TableOfContents: p.tocFile, + IsStubs: p.buildStubs(), }) return outputFile @@ -232,6 +233,7 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext, 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 ec1e8a206..5cbf8f076 100644 --- a/cc/tidy.go +++ b/cc/tidy.go @@ -219,15 +219,11 @@ func collectTidyObjModuleTargets(ctx android.SingletonContext, module android.Mo 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 @@ func (m *tidyPhonySingleton) GenerateBuildActions(ctx android.SingletonContext) // Collect tidy/obj targets from the 'final' modules. ctx.VisitAllModules(func(module android.Module) { - if module == ctx.FinalModule(module) { + if ctx.IsFinalModule(module) { collectTidyObjModuleTargets(ctx, module, tidyModulesInDirGroup, objModulesInDirGroup) } }) diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go index e7dff4012..4a2adf00a 100644 --- a/cc/vndk_prebuilt.go +++ b/cc/vndk_prebuilt.go @@ -166,6 +166,7 @@ func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext, Target: ctx.Target(), TableOfContents: p.tocFile, + IsStubs: false, }) p.libraryDecorator.flagExporter.setProvider(ctx) diff --git a/compliance/notice.go b/compliance/notice.go index 4fc83ab70..edd1b3435 100644 --- a/compliance/notice.go +++ b/compliance/notice.go @@ -18,6 +18,7 @@ import ( "path/filepath" "android/soong/android" + "github.com/google/blueprint" ) @@ -62,8 +63,7 @@ type NoticeXmlModule struct { props noticeXmlProperties - outputFile android.OutputPath - installPath android.InstallPath + outputFile android.OutputPath } type noticeXmlProperties struct { @@ -86,10 +86,8 @@ func (nx *NoticeXmlModule) GenerateAndroidBuildActions(ctx android.ModuleContext 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 a2df41c8e..73bc3478f 100644 --- a/etc/adb_keys.go +++ b/etc/adb_keys.go @@ -36,6 +36,11 @@ func AdbKeysModuleFactory() android.Module { 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 @@ func (m *AdbKeysModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 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 a46da77e8..be943a3ca 100644 --- a/etc/prebuilt_etc.go +++ b/etc/prebuilt_etc.go @@ -76,6 +76,10 @@ func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) { 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_defaults", defaultsFactory) @@ -132,6 +136,9 @@ type prebuiltEtcProperties struct { // 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 +376,10 @@ func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) { 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 +921,43 @@ func PrebuiltOdmFactory() android.Module { 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 +} diff --git a/filesystem/android_device.go b/filesystem/android_device.go index 9071272c1..2645dc498 100644 --- a/filesystem/android_device.go +++ b/filesystem/android_device.go @@ -34,6 +34,8 @@ type PartitionNameProperties struct { Vendor_partition_name *string // Name of the Odm partition filesystem module Odm_partition_name *string + // The vbmeta partition and its "chained" partitions + Vbmeta_partitions []string } type androidDevice struct { @@ -46,7 +48,6 @@ func AndroidDeviceFactory() android.Module { module := &androidDevice{} module.AddProperties(&module.partitionProps) android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) - return module } @@ -69,6 +70,9 @@ func (a *androidDevice) DepsMutator(ctx android.BottomUpMutatorContext) { addDependencyIfDefined(a.partitionProps.Product_partition_name) addDependencyIfDefined(a.partitionProps.Vendor_partition_name) addDependencyIfDefined(a.partitionProps.Odm_partition_name) + for _, vbmetaPartition := range a.partitionProps.Vbmeta_partitions { + ctx.AddDependency(ctx.Module(), filesystemDepTag, vbmetaPartition) + } } func (a *androidDevice) GenerateAndroidBuildActions(ctx android.ModuleContext) { diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index 6ed962f13..e84139b9a 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -147,6 +147,8 @@ type FilesystemProperties struct { Erofs ErofsProperties + F2fs F2fsProperties + Linkerconfig LinkerConfigProperties // Determines if the module is auto-generated from Soong or not. If the module is @@ -166,6 +168,11 @@ type ErofsProperties struct { Sparse *bool } +// Additional properties required to generate f2fs FS partitions. +type F2fsProperties struct { + Sparse *bool +} + type LinkerConfigProperties struct { // Build a linker.config.pb file @@ -227,6 +234,7 @@ type fsType int const ( ext4Type fsType = iota erofsType + f2fsType compressedCpioType cpioType // uncompressed unknown @@ -249,6 +257,8 @@ func GetFsTypeFromString(ctx android.EarlyModuleContext, typeStr string) fsType return ext4Type case "erofs": return erofsType + case "f2fs": + return f2fsType case "compressed_cpio": return compressedCpioType case "cpio": @@ -289,7 +299,7 @@ var pctx = android.NewPackageContext("android/soong/filesystem") func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { validatePartitionType(ctx, f) switch f.fsType(ctx) { - case ext4Type, erofsType: + case ext4Type, erofsType, f2fsType: f.output = f.buildImageUsingBuildImage(ctx) case compressedCpioType: f.output = f.buildCpioImage(ctx, true) @@ -505,6 +515,8 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. return "ext4" case erofsType: return "erofs" + case f2fsType: + return "f2fs" } panic(fmt.Errorf("unsupported fs type %v", t)) } @@ -554,8 +566,11 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. addStr("uuid", uuid) addStr("hash_seed", uuid) } - // Add erofs properties - if f.fsType(ctx) == erofsType { + + fst := f.fsType(ctx) + switch fst { + case erofsType: + // Add erofs properties if compressor := f.properties.Erofs.Compressor; compressor != nil { addStr("erofs_default_compressor", proptools.String(compressor)) } @@ -566,17 +581,39 @@ func (f *filesystem) buildPropFile(ctx android.ModuleContext) (propFile android. // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2292;bpv=1;bpt=0;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b addStr("erofs_sparse_flag", "-s") } - } else if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil { - // Raise an exception if the propfile contains erofs properties, but the fstype is not erofs - fs := fsTypeStr(f.fsType(ctx)) - ctx.PropertyErrorf("erofs", "erofs is non-empty, but FS type is %s\n. Please delete erofs properties if this partition should use %s\n", fs, fs) + case f2fsType: + if proptools.BoolDefault(f.properties.F2fs.Sparse, true) { + // https://source.corp.google.com/h/googleplex-android/platform/build/+/88b1c67239ca545b11580237242774b411f2fed9:core/Makefile;l=2294;drc=ea8f34bc1d6e63656b4ec32f2391e9d54b3ebb6b;bpv=1;bpt=0 + addStr("f2fs_sparse_flag", "-S") + } } + f.checkFsTypePropertyError(ctx, fst, fsTypeStr(fst)) propFile = android.PathForModuleOut(ctx, "prop").OutputPath android.WriteFileRuleVerbatim(ctx, propFile, propFileString.String()) return propFile, deps } +// This method checks if there is any property set for the fstype(s) other than +// the current fstype. +func (f *filesystem) checkFsTypePropertyError(ctx android.ModuleContext, t fsType, fs string) { + raiseError := func(otherFsType, currentFsType string) { + errMsg := fmt.Sprintf("%s is non-empty, but FS type is %s\n. Please delete %s properties if this partition should use %s\n", otherFsType, currentFsType, otherFsType, currentFsType) + ctx.PropertyErrorf(otherFsType, errMsg) + } + + if t != erofsType { + if f.properties.Erofs.Compressor != nil || f.properties.Erofs.Compress_hints != nil || f.properties.Erofs.Sparse != nil { + raiseError("erofs", fs) + } + } + if t != f2fsType { + if f.properties.F2fs.Sparse != nil { + raiseError("f2fs", fs) + } + } +} + func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) android.OutputPath { if proptools.Bool(f.properties.Use_avb) { ctx.PropertyErrorf("use_avb", "signing compresed cpio image using avbtool is not supported."+ diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index 29f9373d7..801a17545 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -585,6 +585,35 @@ func TestErofsPartition(t *testing.T) { android.AssertStringDoesContain(t, "erofs fs type sparse", buildImageConfig, "erofs_sparse_flag=-s") } +func TestF2fsPartition(t *testing.T) { + result := fixture.RunTestWithBp(t, ` + android_filesystem { + name: "f2fs_partition", + type: "f2fs", + } + `) + + partition := result.ModuleForTests("f2fs_partition", "android_common") + buildImageConfig := android.ContentFromFileRuleForTests(t, result.TestContext, partition.Output("prop")) + android.AssertStringDoesContain(t, "f2fs fs type", buildImageConfig, "fs_type=f2fs") + android.AssertStringDoesContain(t, "f2fs fs type sparse", buildImageConfig, "f2fs_sparse_flag=-S") +} + +func TestFsTypesPropertyError(t *testing.T) { + fixture.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern( + "erofs: erofs is non-empty, but FS type is f2fs\n. Please delete erofs properties if this partition should use f2fs\n")). + RunTestWithBp(t, ` + android_filesystem { + name: "f2fs_partition", + type: "f2fs", + erofs: { + compressor: "lz4hc,9", + compress_hints: "compress_hints.txt", + }, + } + `) +} + // If a system_ext/ module depends on system/ module, the dependency should *not* // be installed in system_ext/ func TestDoNotPackageCrossPartitionDependencies(t *testing.T) { diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go index 0bae479cf..6a3fc1f18 100644 --- a/filesystem/vbmeta.go +++ b/filesystem/vbmeta.go @@ -25,19 +25,19 @@ import ( ) func init() { - android.RegisterModuleType("vbmeta", vbmetaFactory) + android.RegisterModuleType("vbmeta", VbmetaFactory) } type vbmeta struct { android.ModuleBase - properties vbmetaProperties + properties VbmetaProperties output android.OutputPath installDir android.InstallPath } -type vbmetaProperties struct { +type VbmetaProperties struct { // Name of the partition stored in vbmeta desc. Defaults to the name of this module. Partition_name *string @@ -50,9 +50,8 @@ type vbmetaProperties struct { // Algorithm that avbtool will use to sign this vbmeta image. Default is SHA256_RSA4096. Algorithm *string - // File whose content will provide the rollback index. If unspecified, the rollback index - // is from PLATFORM_SECURITY_PATCH - Rollback_index_file *string `android:"path"` + // The rollback index. If unspecified, the rollback index is from PLATFORM_SECURITY_PATCH + Rollback_index *int64 // Rollback index location of this vbmeta image. Must be 0, 1, 2, etc. Default is 0. Rollback_index_location *int64 @@ -62,7 +61,7 @@ type vbmetaProperties struct { Partitions proptools.Configurable[[]string] // List of chained partitions that this vbmeta deletages the verification. - Chained_partitions []chainedPartitionProperties + Chained_partitions []ChainedPartitionProperties // List of key-value pair of avb properties Avb_properties []avbProperty @@ -76,7 +75,7 @@ type avbProperty struct { Value *string } -type chainedPartitionProperties struct { +type ChainedPartitionProperties struct { // Name of the chained partition Name *string @@ -95,7 +94,7 @@ type chainedPartitionProperties struct { } // vbmeta is the partition image that has the verification information for other partitions. -func vbmetaFactory() android.Module { +func VbmetaFactory() android.Module { module := &vbmeta{} module.AddProperties(&module.properties) android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) @@ -217,15 +216,12 @@ func (v *vbmeta) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Returns the embedded shell command that prints the rollback index func (v *vbmeta) rollbackIndexCommand(ctx android.ModuleContext) string { - var cmd string - if v.properties.Rollback_index_file != nil { - f := android.PathForModuleSrc(ctx, proptools.String(v.properties.Rollback_index_file)) - cmd = "cat " + f.String() + if v.properties.Rollback_index != nil { + return fmt.Sprintf("%d", *v.properties.Rollback_index) } else { - cmd = "date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s" + // Take the first line and remove the newline char + return "$(date -d 'TZ=\"GMT\" " + ctx.Config().PlatformSecurityPatch() + "' +%s | head -1 | tr -d '\n'" + ")" } - // Take the first line and remove the newline char - return "$(" + cmd + " | head -1 | tr -d '\n'" + ")" } // Extract public keys from chained_partitions.private_key. The keys are indexed with the partition diff --git a/fsgen/Android.bp b/fsgen/Android.bp index e3cbdb375..8cd7518cb 100644 --- a/fsgen/Android.bp +++ b/fsgen/Android.bp @@ -14,6 +14,9 @@ bootstrap_go_package { ], 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 7ef7d9901..c9bbf3fa6 100644 --- a/fsgen/filesystem_creator.go +++ b/fsgen/filesystem_creator.go @@ -18,10 +18,8 @@ import ( "crypto/sha256" "fmt" "path/filepath" - "slices" "strconv" "strings" - "sync" "android/soong/android" "android/soong/filesystem" @@ -43,328 +41,12 @@ func registerBuildComponents(ctx android.RegistrationContext) { 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 @@ func filesystemCreatorFactory() android.Module { 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 filesystemCreatorFactory() android.Module { } func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) { - soongGeneratedPartitions := &ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions - for _, partitionType := range *soongGeneratedPartitions { + soongGeneratedPartitions := generatedPartitions(ctx) + finalSoongGeneratedPartitions := make([]string, 0, len(soongGeneratedPartitions)) + for _, partitionType := range soongGeneratedPartitions { if f.createPartition(ctx, partitionType) { f.properties.Generated_partition_types = append(f.properties.Generated_partition_types, partitionType) + finalSoongGeneratedPartitions = append(finalSoongGeneratedPartitions, partitionType) } else { f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, partitionType) - _, *soongGeneratedPartitions = android.RemoveFromList(partitionType, *soongGeneratedPartitions) } } - f.createDeviceModule(ctx) + + for _, x := range createVbmetaPartitions(ctx, finalSoongGeneratedPartitions) { + f.properties.Vbmeta_module_names = append(f.properties.Vbmeta_module_names, x.moduleName) + f.properties.Vbmeta_partition_names = append(f.properties.Vbmeta_partition_names, x.partitionName) + } + + ctx.Config().Get(fsGenStateOnceKey).(*FsGenState).soongGeneratedPartitions = finalSoongGeneratedPartitions + f.createDeviceModule(ctx, finalSoongGeneratedPartitions, f.properties.Vbmeta_module_names) } func generatedModuleName(cfg android.Config, suffix string) string { @@ -411,7 +102,11 @@ func generatedModuleNameForPartition(cfg android.Config, partitionType string) s return generatedModuleName(cfg, fmt.Sprintf("%s_image", partitionType)) } -func (f *filesystemCreator) createDeviceModule(ctx android.LoadHookContext) { +func (f *filesystemCreator) createDeviceModule( + ctx android.LoadHookContext, + generatedPartitionTypes []string, + vbmetaPartitions []string, +) { baseProps := &struct { Name *string }{ @@ -420,21 +115,22 @@ func (f *filesystemCreator) createDeviceModule(ctx android.LoadHookContext) { // Currently, only the system and system_ext partition module is created. partitionProps := &filesystem.PartitionNameProperties{} - if android.InList("system", f.properties.Generated_partition_types) { + if android.InList("system", generatedPartitionTypes) { partitionProps.System_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system")) } - if android.InList("system_ext", f.properties.Generated_partition_types) { + if android.InList("system_ext", generatedPartitionTypes) { partitionProps.System_ext_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "system_ext")) } - if android.InList("vendor", f.properties.Generated_partition_types) { + if android.InList("vendor", generatedPartitionTypes) { partitionProps.Vendor_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor")) } - if android.InList("product", f.properties.Generated_partition_types) { + if android.InList("product", generatedPartitionTypes) { partitionProps.Product_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "product")) } - if android.InList("odm", f.properties.Generated_partition_types) { + if android.InList("odm", generatedPartitionTypes) { partitionProps.Odm_partition_name = proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "odm")) } + partitionProps.Vbmeta_partitions = vbmetaPartitions ctx.CreateModule(filesystem.AndroidDeviceFactory, baseProps, partitionProps) } @@ -456,6 +152,26 @@ func partitionSpecificFsProps(fsProps *filesystem.FilesystemProperties, partitio "framework/oat/*/*", // framework/oat/{arch} } fsProps.Fsverity.Libs = []string{":framework-res{.export-package.apk}"} + // TODO(b/377734331): only generate the symlinks if the relevant partitions exist + fsProps.Symlinks = []filesystem.SymlinkDefinition{ + filesystem.SymlinkDefinition{ + Target: proptools.StringPtr("/product"), + Name: proptools.StringPtr("system/product"), + }, + filesystem.SymlinkDefinition{ + Target: proptools.StringPtr("/system_ext"), + Name: proptools.StringPtr("system/system_ext"), + }, + filesystem.SymlinkDefinition{ + Target: proptools.StringPtr("/vendor"), + Name: proptools.StringPtr("system/vendor"), + }, + filesystem.SymlinkDefinition{ + Target: proptools.StringPtr("/system_dlkm/lib/modules"), + Name: proptools.StringPtr("system/lib/modules"), + }, + } + fsProps.Base_dir = proptools.StringPtr("system") case "system_ext": fsProps.Fsverity.Inputs = []string{ "framework/*", @@ -525,60 +241,78 @@ func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partiti } 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 +// autogenerated *_dlkm filsystem modules. Each _dlkm partition should have a single prebuilt_kernel_modules dependency. +// This ensures that the depmod artifacts (modules.* installed in /lib/modules/) are generated with a complete view. + +// The input `kernelModules` is a space separated list of .ko files in the workspace. func (f *filesystemCreator) createPrebuiltKernelModules(ctx android.LoadHookContext, partitionType string, kernelModules []string) { - // Partition the files per directory - dirToFiles := map[string][]string{} - for _, kernelModule := range kernelModules { - dir := filepath.Dir(kernelModule) - base := filepath.Base(kernelModule) - dirToFiles[dir] = append(dirToFiles[dir], base) - } - // Create a prebuilt_kernel_modules module per partition fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState) - for index, dir := range android.SortedKeys(dirToFiles) { - name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules-%s", partitionType, strconv.Itoa(index))) - props := &struct { - Name *string - Srcs []string - }{ - Name: proptools.StringPtr(name), - Srcs: dirToFiles[dir], - } - kernelModule := ctx.CreateModuleInDirectory( - kernel.PrebuiltKernelModulesFactory, - dir, - props, - ) - kernelModule.HideFromMake() - // Add to deps - (*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config()) + name := generatedModuleName(ctx.Config(), fmt.Sprintf("%s-kernel-modules", partitionType)) + props := &struct { + Name *string + Srcs []string + }{ + Name: proptools.StringPtr(name), + Srcs: kernelModules, + } + kernelModule := ctx.CreateModuleInDirectory( + kernel.PrebuiltKernelModulesFactory, + ".", // create in root directory for now + props, + ) + kernelModule.HideFromMake() + // Add to deps + (*fsGenState.fsDeps[partitionType])[name] = defaultDepCandidateProps(ctx.Config()) +} + +// Create a build_prop and android_info module. This will be used to create /vendor/build.prop +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 +360,15 @@ func (f *filesystemCreator) createLinkerConfigSourceFilegroups(ctx android.LoadH type filesystemBaseProperty struct { Name *string Compile_multilib *string + Visibility []string } func generateBaseProps(namePtr *string) *filesystemBaseProperty { return &filesystemBaseProperty{ Name: namePtr, Compile_multilib: proptools.StringPtr("both"), + // The vbmeta modules are currently in the root directory and depend on the partitions + Visibility: []string{"//.", "//build/soong:__subpackages__"}, } } @@ -698,18 +435,13 @@ func (f *filesystemCreator) createDiffTest(ctx android.ModuleContext, partitionT ctx.ModuleErrorf("Expected module %s to provide FileysystemInfo", partitionModuleName) } makeFileList := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/obj/PACKAGING/%s_intermediates/file_list.txt", ctx.Config().DeviceName(), partitionType)) - // For now, don't allowlist anything. The test will fail, but that's fine in the current - // early stages where we're just figuring out what we need - emptyAllowlistFile := android.PathForModuleOut(ctx, fmt.Sprintf("allowlist_%s.txt", partitionModuleName)) - android.WriteFileRule(ctx, emptyAllowlistFile, "") diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", partitionModuleName)) builder := android.NewRuleBuilder(pctx, ctx) builder.Command().BuiltTool("file_list_diff"). Input(makeFileList). Input(filesystemInfo.FileListFile). - Text(partitionModuleName). - FlagWithInput("--allowlists ", emptyAllowlistFile) + Text(partitionModuleName) builder.Command().Text("touch").Output(diffTestResultFile) builder.Build(partitionModuleName+" diff test", partitionModuleName+" diff test") return diffTestResultFile @@ -727,16 +459,42 @@ func createFailingCommand(ctx android.ModuleContext, message string) android.Pat return file } +func createVbmetaDiff(ctx android.ModuleContext, vbmetaModuleName string, vbmetaPartitionName string) android.Path { + vbmetaModule := ctx.GetDirectDepWithTag(vbmetaModuleName, generatedVbmetaPartitionDepTag) + outputFilesProvider, ok := android.OtherModuleProvider(ctx, vbmetaModule, android.OutputFilesProvider) + if !ok { + ctx.ModuleErrorf("Expected module %s to provide OutputFiles", vbmetaModule) + } + if len(outputFilesProvider.DefaultOutputFiles) != 1 { + ctx.ModuleErrorf("Expected 1 output file from module %s", vbmetaModule) + } + soongVbMetaFile := outputFilesProvider.DefaultOutputFiles[0] + makeVbmetaFile := android.PathForArbitraryOutput(ctx, fmt.Sprintf("target/product/%s/%s.img", ctx.Config().DeviceName(), vbmetaPartitionName)) + + diffTestResultFile := android.PathForModuleOut(ctx, fmt.Sprintf("diff_test_%s.txt", vbmetaModuleName)) + builder := android.NewRuleBuilder(pctx, ctx) + builder.Command().Text("diff"). + Input(soongVbMetaFile). + Input(makeVbmetaFile) + builder.Command().Text("touch").Output(diffTestResultFile) + builder.Build(vbmetaModuleName+" diff test", vbmetaModuleName+" diff test") + return diffTestResultFile +} + type systemImageDepTagType struct { blueprint.BaseDependencyTag } var generatedFilesystemDepTag systemImageDepTagType +var generatedVbmetaPartitionDepTag systemImageDepTagType func (f *filesystemCreator) DepsMutator(ctx android.BottomUpMutatorContext) { for _, partitionType := range f.properties.Generated_partition_types { ctx.AddDependency(ctx.Module(), generatedFilesystemDepTag, generatedModuleNameForPartition(ctx.Config(), partitionType)) } + for _, vbmetaModule := range f.properties.Vbmeta_module_names { + ctx.AddDependency(ctx.Module(), generatedVbmetaPartitionDepTag, vbmetaModule) + } } func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -766,6 +524,11 @@ func (f *filesystemCreator) GenerateAndroidBuildActions(ctx android.ModuleContex diffTestFiles = append(diffTestFiles, diffTestFile) ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", partitionType), diffTestFile) } + for i, vbmetaModule := range f.properties.Vbmeta_module_names { + diffTestFile := createVbmetaDiff(ctx, vbmetaModule, f.properties.Vbmeta_partition_names[i]) + diffTestFiles = append(diffTestFiles, diffTestFile) + ctx.Phony(fmt.Sprintf("soong_generated_%s_filesystem_test", f.properties.Vbmeta_partition_names[i]), diffTestFile) + } ctx.Phony("soong_generated_filesystem_tests", diffTestFiles...) } @@ -774,9 +537,6 @@ func generateBpContent(ctx android.EarlyModuleContext, partitionType string) str 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 +544,8 @@ func generateBpContent(ctx android.EarlyModuleContext, partitionType string) str 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 000000000..92ea1282a --- /dev/null +++ b/fsgen/fsgen_mutators.go @@ -0,0 +1,342 @@ +// 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" + "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"} + 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.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.BuildingSystemDlkmImage { + generatedPartitions = append(generatedPartitions, "system_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()), + }, + "system_dlkm": {}, + }, + 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) { + fsGenState := mctx.Config().Get(fsGenStateOnceKey).(*FsGenState) + + m := mctx.Module() + if m.Target().Os.Class == android.Device && slices.Contains(fsGenState.depCandidates, mctx.ModuleName()) { + 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 +} diff --git a/fsgen/prebuilt_etc_modules_gen.go b/fsgen/prebuilt_etc_modules_gen.go new file mode 100644 index 000000000..362ac31b8 --- /dev/null +++ b/fsgen/prebuilt_etc_modules_gen.go @@ -0,0 +1,300 @@ +// 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 + } + } +} + +func uniqueExistingProductCopyFileMap(ctx android.LoadHookContext) map[string]string { + seen := make(map[string]bool) + filtered := make(map[string]string) + + for src, dest := range ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse.ProductCopyFiles { + if _, ok := seen[dest]; !ok { + if optionalPath := android.ExistentPathForSource(ctx, src); optionalPath.Valid() { + seen[dest] = true + 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) { + dest := productCopyFileMap[src] + srcFileDir := filepath.Dir(src) + if _, ok := groupedSources[srcFileDir]; !ok { + groupedSources[srcFileDir] = newPrebuiltSrcGroupByInstallPartition() + } + 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, + "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 createPrebuiltEtcModule(ctx android.LoadHookContext, partition, srcDir, destDir string, destFiles []srcBaseFileInstallBaseFileTuple) string { + moduleProps := &prebuiltModuleProperties{} + propsList := []interface{}{moduleProps} + + // generated module name follows the pattern: + // <install partition>-<src file path>-<relative install path from partition root>-<install file extension> + // 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), "_")) + } + if len(destFiles) > 0 { + if ext := filepath.Ext(destFiles[0].srcBaseFile); ext != "" { + moduleName += fmt.Sprintf("-%s", strings.TrimPrefix(ext, ".")) + } + } + moduleProps.Name = proptools.StringPtr(moduleName) + + allCopyFileNamesUnchanged := true + var srcBaseFiles, installBaseFiles []string + for _, tuple := range destFiles { + if tuple.srcBaseFile != tuple.installBaseFile { + allCopyFileNamesUnchanged = false + } + srcBaseFiles = append(srcBaseFiles, tuple.srcBaseFile) + installBaseFiles = append(installBaseFiles, tuple.installBaseFile) + } + + // 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 + } + } + destDir, _ = filepath.Rel(etcInstallPathKey, destDir) + + // 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) + } + + // Set appropriate srcs, dsts, and releative_install_path based on + // the source and install file names + if allCopyFileNamesUnchanged { + moduleProps.Srcs = srcBaseFiles + + // Specify relative_install_path if it is not installed in the root directory of the + // partition + if !android.InList(destDir, []string{"", "."}) { + propsList = append(propsList, &prebuiltSubdirProperties{ + Relative_install_path: proptools.StringPtr(destDir), + }) + } + } else { + moduleProps.Srcs = srcBaseFiles + dsts := []string{} + for _, installBaseFile := range installBaseFiles { + dsts = append(dsts, filepath.Join(destDir, installBaseFile)) + } + moduleProps.Dsts = dsts + } + + moduleProps.No_full_install = proptools.BoolPtr(true) + moduleProps.NamespaceExportedToMake = true + moduleProps.Visibility = []string{"//visibility:public"} + + ctx.CreateModuleInDirectory(etcInstallPathToFactoryList[etcInstallPathKey], srcDir, propsList...) + + return moduleName +} + +func createPrebuiltEtcModulesForPartition(ctx android.LoadHookContext, partition, srcDir string, destDirFilesMap map[string][]srcBaseFileInstallBaseFileTuple) (ret []string) { + for _, destDir := range android.SortedKeys(destDirFilesMap) { + ret = append(ret, createPrebuiltEtcModule(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 000000000..280e4052d --- /dev/null +++ b/fsgen/vbmeta_partitions.go @@ -0,0 +1,184 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fsgen + +import ( + "android/soong/android" + "android/soong/filesystem" + "slices" + "strconv" + + "github.com/google/blueprint/proptools" +) + +type vbmetaModuleInfo struct { + // The name of the generated vbmeta module + moduleName string + // The name of the module that avb understands. This is the name passed to --chain_partition, + // and also the basename of the output file. (the output file is called partitionName + ".img") + partitionName string +} + +// Creates the vbmeta partition and the chained vbmeta partitions. Returns the list of module names +// that the function created. May return nil if the product isn't using avb. +// +// AVB is Android Verified Boot: https://source.android.com/docs/security/features/verifiedboot +// It works by signing all the partitions, but then also including an extra metadata paritition +// called vbmeta that depends on all the other signed partitions. This creates a requirement +// that you update all those partitions and the vbmeta partition together, so in order to relax +// that requirement products can set up "chained" vbmeta partitions, where a chained partition +// like vbmeta_system might contain the avb metadata for just a few products. In cuttlefish +// vbmeta_system contains metadata about product, system, and system_ext. Using chained partitions, +// that group of partitions can be updated independently from the other signed partitions. +func createVbmetaPartitions(ctx android.LoadHookContext, generatedPartitionTypes []string) []vbmetaModuleInfo { + partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse + // Some products seem to have BuildingVbmetaImage as true even when BoardAvbEnable is false + if !partitionVars.BuildingVbmetaImage || !partitionVars.BoardAvbEnable { + return nil + } + + var result []vbmetaModuleInfo + + var chainedPartitions []filesystem.ChainedPartitionProperties + var partitionTypesHandledByChainedPartitions []string + for chainedName, props := range partitionVars.ChainedVbmetaPartitions { + chainedName = "vbmeta_" + chainedName + if len(props.Partitions) == 0 { + continue + } + if len(props.Key) == 0 { + ctx.ModuleErrorf("No key found for chained avb partition %q", chainedName) + continue + } + if len(props.Algorithm) == 0 { + ctx.ModuleErrorf("No algorithm found for chained avb partition %q", chainedName) + continue + } + if len(props.RollbackIndex) == 0 { + ctx.ModuleErrorf("No rollback index found for chained avb partition %q", chainedName) + continue + } + ril, err := strconv.ParseInt(props.RollbackIndexLocation, 10, 32) + if err != nil { + ctx.ModuleErrorf("Rollback index location must be an int, got %q", props.RollbackIndexLocation) + continue + } + // The default is to use the PlatformSecurityPatch, and a lot of product config files + // just set it to the platform security patch, so detect that and don't set the property + // in soong. + var rollbackIndex *int64 + if props.RollbackIndex != ctx.Config().PlatformSecurityPatch() { + i, err := strconv.ParseInt(props.RollbackIndex, 10, 32) + if err != nil { + ctx.ModuleErrorf("Rollback index must be an int, got %q", props.RollbackIndex) + continue + } + rollbackIndex = &i + } + + var partitionModules []string + for _, partition := range props.Partitions { + partitionTypesHandledByChainedPartitions = append(partitionTypesHandledByChainedPartitions, partition) + if !slices.Contains(generatedPartitionTypes, partition) { + // The partition is probably unsupported. + continue + } + partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partition)) + } + + name := generatedModuleName(ctx.Config(), chainedName) + ctx.CreateModuleInDirectory( + filesystem.VbmetaFactory, + ".", // Create in the root directory for now so its easy to get the key + &filesystem.VbmetaProperties{ + Partition_name: proptools.StringPtr(chainedName), + Stem: proptools.StringPtr(chainedName + ".img"), + Private_key: proptools.StringPtr(props.Key), + Algorithm: &props.Algorithm, + Rollback_index: rollbackIndex, + Rollback_index_location: &ril, + Partitions: proptools.NewSimpleConfigurable(partitionModules), + }, &struct { + Name *string + }{ + Name: &name, + }, + ).HideFromMake() + + chainedPartitions = append(chainedPartitions, filesystem.ChainedPartitionProperties{ + Name: &chainedName, + Rollback_index_location: &ril, + Private_key: &props.Key, + }) + + result = append(result, vbmetaModuleInfo{ + moduleName: name, + partitionName: chainedName, + }) + } + + vbmetaModuleName := generatedModuleName(ctx.Config(), "vbmeta") + + var algorithm *string + var ri *int64 + var key *string + if len(partitionVars.BoardAvbKeyPath) == 0 { + // Match make's defaults: https://cs.android.com/android/platform/superproject/main/+/main:build/make/core/Makefile;l=4568;drc=5b55f926830963c02ab1d2d91e46442f04ba3af0 + key = proptools.StringPtr("external/avb/test/data/testkey_rsa4096.pem") + algorithm = proptools.StringPtr("SHA256_RSA4096") + } else { + key = proptools.StringPtr(partitionVars.BoardAvbKeyPath) + algorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm) + } + if len(partitionVars.BoardAvbRollbackIndex) > 0 { + parsedRi, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 32) + if err != nil { + ctx.ModuleErrorf("Rollback index location must be an int, got %q", partitionVars.BoardAvbRollbackIndex) + } + ri = &parsedRi + } + + var partitionModules []string + for _, partitionType := range generatedPartitionTypes { + if slices.Contains(partitionTypesHandledByChainedPartitions, partitionType) { + // Already handled by a chained vbmeta partition + continue + } + partitionModules = append(partitionModules, generatedModuleNameForPartition(ctx.Config(), partitionType)) + } + + ctx.CreateModuleInDirectory( + filesystem.VbmetaFactory, + ".", // Create in the root directory for now so its easy to get the key + &filesystem.VbmetaProperties{ + Stem: proptools.StringPtr("vbmeta.img"), + Algorithm: algorithm, + Private_key: key, + Rollback_index: ri, + Chained_partitions: chainedPartitions, + Partitions: proptools.NewSimpleConfigurable(partitionModules), + }, &struct { + Name *string + }{ + Name: &vbmetaModuleName, + }, + ).HideFromMake() + + result = append(result, vbmetaModuleInfo{ + moduleName: vbmetaModuleName, + partitionName: "vbmeta", + }) + return result +} diff --git a/java/aar.go b/java/aar.go index 66ca00af8..b5cdde36a 100644 --- a/java/aar.go +++ b/java/aar.go @@ -77,7 +77,7 @@ type aaptProperties struct { // list of directories relative to the Blueprints file containing // Android resources. Defaults to ["res"] if a directory called res exists. // Set to [] to disable the default. - Resource_dirs []string `android:"path"` + Resource_dirs proptools.Configurable[[]string] `android:"path"` // list of zip files containing Android resources. Resource_zips []string `android:"path"` @@ -275,7 +275,7 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext android.SdkConte IncludeDirs: false, }) assetDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Asset_dirs, "assets") - resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs, "res") + resourceDirs := android.PathsWithOptionalDefaultForModuleSrc(ctx, a.aaptProperties.Resource_dirs.GetOrDefault(ctx, nil), "res") resourceZips := android.PathsForModuleSrc(ctx, a.aaptProperties.Resource_zips) // Glob directories into lists of paths diff --git a/java/app.go b/java/app.go index e01a2ba7b..94c9e5b82 100644 --- a/java/app.go +++ b/java/app.go @@ -67,6 +67,9 @@ type AppInfo struct { // 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 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { 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 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, 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 @@ func collectAppDeps(ctx android.ModuleContext, app appDepsInterface, 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 @@ func AndroidAppFactory() android.Module { Filter_product *string Aaptflags []string Manifest *string - Resource_dirs []string + Resource_dirs proptools.Configurable[[]string] Flags_packages []string }{ Name: proptools.StringPtr(rroPackageName), diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 1a3368057..7c0f54431 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -520,7 +520,7 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // be output to Make but it does not really matter which variant is output. The default/platform // variant is the first (ctx.PrimaryModule()) and is usually hidden from make so this just picks // the last variant (ctx.FinalModule()). - if ctx.Module() != ctx.FinalModule() { + if !ctx.IsFinalModule(ctx.Module()) { b.HideFromMake() } } diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp index da865404b..8c808e4b9 100644 --- a/java/core-libraries/Android.bp +++ b/java/core-libraries/Android.bp @@ -132,10 +132,10 @@ java_library { // 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 { ], } +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 53b3481d0..1d572faef 100644 --- a/java/java.go +++ b/java/java.go @@ -606,10 +606,8 @@ func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext an } else if ctx.Device() { return defaultJavaLanguageVersion(ctx, sdkContext.SdkVersion(ctx)) } else if ctx.Config().TargetsJava21() { - // Temporary experimental flag to be able to try and build with - // java version 21 options. The flag, if used, just sets Java - // 21 as the default version, leaving any components that - // target an older version intact. + // Build flag that controls whether Java 21 is used as the default + // target version, or Java 17. return JAVA_VERSION_21 } else { return JAVA_VERSION_17 @@ -1610,6 +1608,8 @@ func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, 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 +1628,20 @@ func (j *Test) generateAndroidBuildActionsWithConfig(ctx android.ModuleContext, 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 4537f1913..036521c86 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -65,11 +65,11 @@ func defaultJavaLanguageVersion(ctx android.EarlyModuleContext, s android.SdkSpe return JAVA_VERSION_9 } else if sdk.FinalOrFutureInt() <= 33 { return JAVA_VERSION_11 + } else if sdk.FinalOrFutureInt() <= 35 { + return JAVA_VERSION_17 } else if ctx.Config().TargetsJava21() { - // Temporary experimental flag to be able to try and build with - // java version 21 options. The flag, if used, just sets Java - // 21 as the default version, leaving any components that - // target an older version intact. + // Build flag that controls whether Java 21 is used as the + // default target version, or Java 17. return JAVA_VERSION_21 } else { return JAVA_VERSION_17 diff --git a/rust/bindgen.go b/rust/bindgen.go index 394449574..d59057994 100644 --- a/rust/bindgen.go +++ b/rust/bindgen.go @@ -252,7 +252,7 @@ func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) andr // Module defined clang flags and include paths cflags = append(cflags, esc(cflagsProp)...) - for _, include := range b.ClangProperties.Local_include_dirs { + for _, include := range b.ClangProperties.Local_include_dirs.GetOrDefault(ctx, nil) { cflags = append(cflags, "-I"+android.PathForModuleSrc(ctx, include).String()) implicits = append(implicits, android.PathForModuleSrc(ctx, include)) } diff --git a/rust/library.go b/rust/library.go index 20cd2af7d..bd3359b6a 100644 --- a/rust/library.go +++ b/rust/library.go @@ -617,6 +617,9 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa 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 9e06cd437..ed38ad7a7 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -454,6 +454,9 @@ type PathDeps struct { // 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 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } + 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 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { 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 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { 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 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { // 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 e0686ed19..0b7780e96 100644 --- a/scripts/gen_build_prop.py +++ b/scripts/gen_build_prop.py @@ -608,6 +608,8 @@ def main(): build_product_prop(args) case "vendor": build_vendor_prop(args) + case "system_dlkm" | "vendor_dlkm" | "odm_dlkm": + build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=[]) case _: sys.exit(f"not supported partition {args.partition}") diff --git a/tests/lib.sh b/tests/lib.sh index 4c320d08b..0e26de55f 100644 --- a/tests/lib.sh +++ b/tests/lib.sh @@ -91,7 +91,6 @@ function symlink_directory { } function create_mock_soong { - create_mock_bazel copy_directory build/blueprint copy_directory build/soong copy_directory build/make @@ -143,41 +142,6 @@ function run_soong { 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_modules/Android.bp b/tradefed_modules/Android.bp index 9969ae280..67d91b27c 100644 --- a/tradefed_modules/Android.bp +++ b/tradefed_modules/Android.bp @@ -13,9 +13,11 @@ bootstrap_go_package { ], srcs: [ "test_module_config.go", + "test_suite.go", ], testSrcs: [ "test_module_config_test.go", + "test_suite_test.go", ], pluginFor: ["soong_build"], } diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go index 7a04c1994..5c13d64e8 100644 --- a/tradefed_modules/test_module_config.go +++ b/tradefed_modules/test_module_config.go @@ -383,6 +383,19 @@ func (m *testModuleConfigModule) generateManifestAndConfig(ctx android.ModuleCon // 4) Module.config / AndroidTest.xml m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig) + + // 5) We provide so we can be listed in test_suites. + android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{ + InstalledFiles: m.supportFiles.Paths(), + OutputFile: baseApk, + TestConfig: m.testConfig, + HostRequiredModuleNames: m.provider.HostRequiredModuleNames, + RequiredModuleNames: m.provider.RequiredModuleNames, + TestSuites: m.tradefedProperties.Test_suites, + IsHost: m.provider.IsHost, + LocalCertificate: m.provider.LocalCertificate, + IsUnitTest: m.provider.IsUnitTest, + }) } var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil) diff --git a/tradefed_modules/test_suite.go b/tradefed_modules/test_suite.go new file mode 100644 index 000000000..00585f5f6 --- /dev/null +++ b/tradefed_modules/test_suite.go @@ -0,0 +1,173 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tradefed_modules + +import ( + "encoding/json" + "path" + "path/filepath" + + "android/soong/android" + "android/soong/tradefed" + "github.com/google/blueprint" +) + +const testSuiteModuleType = "test_suite" + +type testSuiteTag struct{ + blueprint.BaseDependencyTag +} + +type testSuiteManifest struct { + Name string `json:"name"` + Files []string `json:"files"` +} + +func init() { + RegisterTestSuiteBuildComponents(android.InitRegistrationContext) +} + +func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType(testSuiteModuleType, TestSuiteFactory) +} + +var PrepareForTestWithTestSuiteBuildComponents = android.GroupFixturePreparers( + android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents), +) + +type testSuiteProperties struct { + Description string + Tests []string `android:"path,arch_variant"` +} + +type testSuiteModule struct { + android.ModuleBase + android.DefaultableModuleBase + testSuiteProperties +} + +func (t *testSuiteModule) DepsMutator(ctx android.BottomUpMutatorContext) { + for _, test := range t.Tests { + if ctx.OtherModuleDependencyVariantExists(ctx.Config().BuildOSCommonTarget.Variations(), test) { + // Host tests. + ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testSuiteTag{}, test) + } else { + // Target tests. + ctx.AddDependency(ctx.Module(), testSuiteTag{}, test) + } + } +} + +func (t *testSuiteModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + suiteName := ctx.ModuleName() + modulesByName := make(map[string]android.Module) + ctx.WalkDeps(func(child, parent android.Module) bool { + // Recurse into test_suite dependencies. + if ctx.OtherModuleType(child) == testSuiteModuleType { + ctx.Phony(suiteName, android.PathForPhony(ctx, child.Name())) + return true + } + + // Only write out top level test suite dependencies here. + if _, ok := ctx.OtherModuleDependencyTag(child).(testSuiteTag); !ok { + return false + } + + if !child.InstallInTestcases() { + ctx.ModuleErrorf("test_suite only supports modules installed in testcases. %q is not installed in testcases.", child.Name()) + return false + } + + modulesByName[child.Name()] = child + return false + }) + + var files []string + for name, module := range modulesByName { + // Get the test provider data from the child. + tp, ok := android.OtherModuleProvider(ctx, module, tradefed.BaseTestProviderKey) + if !ok { + // TODO: Consider printing out a list of all module types. + ctx.ModuleErrorf("%q is not a test module.", name) + continue + } + + files = append(files, packageModuleFiles(ctx, suiteName, module, tp)...) + ctx.Phony(suiteName, android.PathForPhony(ctx, name)) + } + + manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json") + b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: files}) + if err != nil { + ctx.ModuleErrorf("Failed to marshal manifest: %v", err) + return + } + android.WriteFileRule(ctx, manifestPath, string(b)) + + ctx.Phony(suiteName, manifestPath) +} + +func TestSuiteFactory() android.Module { + module := &testSuiteModule{} + module.AddProperties(&module.testSuiteProperties) + + android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon) + android.InitDefaultableModule(module) + + return module +} + +func packageModuleFiles(ctx android.ModuleContext, suiteName string, module android.Module, tp tradefed.BaseTestProviderData) []string { + + hostOrTarget := "target" + if tp.IsHost { + hostOrTarget = "host" + } + + // suiteRoot at out/soong/packaging/<suiteName>. + suiteRoot := android.PathForSuiteInstall(ctx, suiteName) + + var installed android.InstallPaths + // Install links to installed files from the module. + if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok { + for _, f := range installFilesInfo.InstallFiles { + // rel is anything under .../<partition>, normally under .../testcases. + rel := android.Rel(ctx, f.PartitionDir(), f.String()) + + // Install the file under <suiteRoot>/<host|target>/<partition>. + installDir := suiteRoot.Join(ctx, hostOrTarget, f.Partition(), path.Dir(rel)) + linkTo, err := filepath.Rel(installDir.String(), f.String()) + if err != nil { + ctx.ModuleErrorf("Failed to get relative path from %s to %s: %v", installDir.String(), f.String(), err) + continue + } + installed = append(installed, ctx.InstallAbsoluteSymlink(installDir, path.Base(rel), linkTo)) + } + } + + // Install config file. + if tp.TestConfig != nil { + moduleRoot := suiteRoot.Join(ctx, hostOrTarget, "testcases", module.Name()) + installed = append(installed, ctx.InstallFile(moduleRoot, module.Name() + ".config", tp.TestConfig)) + } + + // Add to phony and manifest, manifestpaths are relative to suiteRoot. + var manifestEntries []string + for _, f := range installed { + manifestEntries = append(manifestEntries, android.Rel(ctx, suiteRoot.String(), f.String())) + ctx.Phony(suiteName, f) + } + return manifestEntries +} diff --git a/tradefed_modules/test_suite_test.go b/tradefed_modules/test_suite_test.go new file mode 100644 index 000000000..3c0a9eb2c --- /dev/null +++ b/tradefed_modules/test_suite_test.go @@ -0,0 +1,151 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package tradefed_modules + +import ( + "android/soong/android" + "android/soong/java" + "encoding/json" + "slices" + "testing" +) + +func TestTestSuites(t *testing.T) { + t.Parallel() + ctx := android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents), + ).RunTestWithBp(t, ` + android_test { + name: "TestModule1", + sdk_version: "current", + } + + android_test { + name: "TestModule2", + sdk_version: "current", + } + + test_suite { + name: "my-suite", + description: "a test suite", + tests: [ + "TestModule1", + "TestModule2", + ] + } + `) + manifestPath := ctx.ModuleForTests("my-suite", "android_common").Output("out/soong/test_suites/my-suite/my-suite.json") + var actual testSuiteManifest + if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil { + t.Errorf("failed to unmarshal manifest: %v", err) + } + slices.Sort(actual.Files) + + expected := testSuiteManifest{ + Name: "my-suite", + Files: []string{ + "target/testcases/TestModule1/TestModule1.config", + "target/testcases/TestModule1/arm64/TestModule1.apk", + "target/testcases/TestModule2/TestModule2.config", + "target/testcases/TestModule2/arm64/TestModule2.apk", + }, + } + + android.AssertDeepEquals(t, "manifests differ", expected, actual) +} + +func TestTestSuitesWithNested(t *testing.T) { + t.Parallel() + ctx := android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents), + ).RunTestWithBp(t, ` + android_test { + name: "TestModule1", + sdk_version: "current", + } + + android_test { + name: "TestModule2", + sdk_version: "current", + } + + android_test { + name: "TestModule3", + sdk_version: "current", + } + + test_suite { + name: "my-child-suite", + description: "a child test suite", + tests: [ + "TestModule1", + "TestModule2", + ] + } + + test_suite { + name: "my-all-tests-suite", + description: "a parent test suite", + tests: [ + "TestModule1", + "TestModule3", + "my-child-suite", + ] + } + `) + manifestPath := ctx.ModuleForTests("my-all-tests-suite", "android_common").Output("out/soong/test_suites/my-all-tests-suite/my-all-tests-suite.json") + var actual testSuiteManifest + if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil { + t.Errorf("failed to unmarshal manifest: %v", err) + } + slices.Sort(actual.Files) + + expected := testSuiteManifest{ + Name: "my-all-tests-suite", + Files: []string{ + "target/testcases/TestModule1/TestModule1.config", + "target/testcases/TestModule1/arm64/TestModule1.apk", + "target/testcases/TestModule2/TestModule2.config", + "target/testcases/TestModule2/arm64/TestModule2.apk", + "target/testcases/TestModule3/TestModule3.config", + "target/testcases/TestModule3/arm64/TestModule3.apk", + }, + } + + android.AssertDeepEquals(t, "manifests differ", expected, actual) +} + +func TestTestSuitesNotInstalledInTestcases(t *testing.T) { + t.Parallel() + android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents), + ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{ + `"SomeHostTest" is not installed in testcases`, + })).RunTestWithBp(t, ` + java_test_host { + name: "SomeHostTest", + srcs: ["a.java"], + } + test_suite { + name: "my-suite", + description: "a test suite", + tests: [ + "SomeHostTest", + ] + } + `) +} |