diff options
172 files changed, 4201 insertions, 3112 deletions
diff --git a/Android.bp b/Android.bp index 682711d42..432c7fc4f 100644 --- a/Android.bp +++ b/Android.bp @@ -104,7 +104,6 @@ cc_genrule { // Instantiate the dex_bootjars singleton module. dex_bootjars { name: "dex_bootjars", - no_full_install: true, } // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can @@ -121,20 +120,6 @@ dexpreopt_systemserver_check { name: "dexpreopt_systemserver_check", } -// buildinfo.prop contains common properties for system/build.prop, like ro.build.version.* -// TODO(b/322090587): merge this to gen_build_prop.py script. -buildinfo_prop { - name: "buildinfo.prop", - - // not installable because this will be included to system/build.prop - installable: false, - - product_config: ":product_config", - - // Currently, only microdroid can refer to buildinfo.prop - visibility: ["//packages/modules/Virtualization/microdroid"], -} - // container for apex_contributions selected using build flags all_apex_contributions { name: "all_apex_contributions", @@ -144,3 +129,41 @@ product_config { name: "product_config", visibility: ["//device/google/cuttlefish/system_image"], } + +build_prop { + name: "system-build.prop", + stem: "build.prop", + product_config: ":product_config", + // Currently, only microdroid and cf system image can refer to system-build.prop + visibility: [ + "//device/google/cuttlefish/system_image", + "//packages/modules/Virtualization/build/microdroid", + ], +} + +build_prop { + name: "system_ext-build.prop", + stem: "build.prop", + system_ext_specific: true, + product_config: ":product_config", + relative_install_path: "etc", // system_ext/etc/build.prop + visibility: ["//visibility:private"], +} + +build_prop { + name: "product-build.prop", + stem: "build.prop", + product_specific: true, + product_config: ":product_config", + relative_install_path: "etc", // product/etc/build.prop + visibility: ["//visibility:private"], +} + +build_prop { + name: "odm-build.prop", + stem: "build.prop", + device_specific: true, + product_config: ":product_config", + relative_install_path: "etc", // odm/etc/build.prop + visibility: ["//visibility:private"], +} diff --git a/aconfig/init.go b/aconfig/init.go index 256b213cc..de155ab52 100644 --- a/aconfig/init.go +++ b/aconfig/init.go @@ -47,7 +47,7 @@ var ( // For create-device-config-sysprops: Generate aconfig flag value map text file aconfigTextRule = pctx.AndroidStaticRule("aconfig_text", blueprint.RuleParams{ - Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}={state:bool}'` + + Command: `${aconfig} dump-cache --dedup --format='{fully_qualified_name}:{permission}={state:bool}'` + ` --cache ${in}` + ` --out ${out}.tmp` + ` && ( if cmp -s ${out}.tmp ${out} ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`, diff --git a/android/Android.bp b/android/Android.bp index 3c38148b6..841a6af6e 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -39,9 +39,9 @@ bootstrap_go_package { "arch_module_context.go", "base_module_context.go", "build_prop.go", - "buildinfo_prop.go", "compliance_metadata.go", "config.go", + "container.go", "test_config.go", "configurable_properties.go", "configured_jars.go", @@ -92,6 +92,7 @@ bootstrap_go_package { "register.go", "rule_builder.go", "sandbox.go", + "sbom.go", "sdk.go", "sdk_version.go", "shared_properties.go", diff --git a/android/apex_contributions.go b/android/apex_contributions.go index 8b72f8e4f..4cd8dda93 100644 --- a/android/apex_contributions.go +++ b/android/apex_contributions.go @@ -119,7 +119,10 @@ func (a *allApexContributions) DepsMutator(ctx BottomUpMutatorContext) { func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) { addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) { for _, content := range m.Contents() { - if !ctx.OtherModuleExists(content) && !ctx.Config().AllowMissingDependencies() { + // Verify that the module listed in contents exists in the tree + // Remove the prebuilt_ prefix to account for partner worksapces where the source module does not + // exist, and PrebuiltRenameMutator renames `prebuilt_foo` to `foo` + if !ctx.OtherModuleExists(content) && !ctx.OtherModuleExists(RemoveOptionalPrebuiltPrefix(content)) && !ctx.Config().AllowMissingDependencies() { ctx.ModuleErrorf("%s listed in apex_contributions %s does not exist\n", content, m.Name()) } pi := &PrebuiltSelectionInfo{ diff --git a/android/arch.go b/android/arch.go index e0c6908c1..6d896e5fc 100644 --- a/android/arch.go +++ b/android/arch.go @@ -19,6 +19,7 @@ import ( "fmt" "reflect" "runtime" + "slices" "strings" "github.com/google/blueprint" @@ -587,19 +588,21 @@ func archMutator(bpctx blueprint.BottomUpMutatorContext) { } osTargets := mctx.Config().Targets[os] + image := base.commonProperties.ImageVariation // Filter NativeBridge targets unless they are explicitly supported. // Skip creating native bridge variants for non-core modules. if os == Android && !(base.IsNativeBridgeSupported() && image == CoreVariation) { + osTargets = slices.DeleteFunc(slices.Clone(osTargets), func(t Target) bool { + return bool(t.NativeBridge) + }) + } - var targets []Target - for _, t := range osTargets { - if !t.NativeBridge { - targets = append(targets, t) - } - } - - osTargets = targets + // Filter HostCross targets if disabled. + if base.HostSupported() && !base.HostCrossSupported() { + osTargets = slices.DeleteFunc(slices.Clone(osTargets), func(t Target) bool { + return t.HostCross + }) } // only the primary arch in the ramdisk / vendor_ramdisk / recovery partition diff --git a/android/arch_list.go b/android/arch_list.go index 42334568f..f1289a3b6 100644 --- a/android/arch_list.go +++ b/android/arch_list.go @@ -16,7 +16,6 @@ package android var archVariants = map[ArchType][]string{ Arm: { - "armv7-a", "armv7-a-neon", "armv8-a", "armv8-2a", diff --git a/android/arch_test.go b/android/arch_test.go index f0a58a90b..6134a065f 100644 --- a/android/arch_test.go +++ b/android/arch_test.go @@ -332,6 +332,12 @@ func TestArchMutator(t *testing.T) { } module { + name: "nohostcross", + host_supported: true, + host_cross_supported: false, + } + + module { name: "baz", device_supported: false, } @@ -355,13 +361,14 @@ func TestArchMutator(t *testing.T) { ` testCases := []struct { - name string - preparer FixturePreparer - fooVariants []string - barVariants []string - bazVariants []string - quxVariants []string - firstVariants []string + name string + preparer FixturePreparer + fooVariants []string + barVariants []string + noHostCrossVariants []string + bazVariants []string + quxVariants []string + firstVariants []string multiTargetVariants []string multiTargetVariantsMap map[string][]string @@ -373,6 +380,7 @@ func TestArchMutator(t *testing.T) { preparer: nil, fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"}, barVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"), + noHostCrossVariants: append(buildOSVariants, "android_arm64_armv8-a", "android_arm_armv7-a-neon"), bazVariants: nil, quxVariants: append(buildOS32Variants, "android_arm_armv7-a-neon"), firstVariants: append(buildOS64Variants, "android_arm64_armv8-a"), @@ -390,6 +398,7 @@ func TestArchMutator(t *testing.T) { }), fooVariants: nil, barVariants: buildOSVariants, + noHostCrossVariants: buildOSVariants, bazVariants: nil, quxVariants: buildOS32Variants, firstVariants: buildOS64Variants, @@ -406,6 +415,7 @@ func TestArchMutator(t *testing.T) { }), fooVariants: []string{"android_arm64_armv8-a", "android_arm_armv7-a-neon"}, barVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"}, + noHostCrossVariants: []string{"linux_musl_x86_64", "linux_musl_x86", "android_arm64_armv8-a", "android_arm_armv7-a-neon"}, bazVariants: nil, quxVariants: []string{"linux_musl_x86", "android_arm_armv7-a-neon"}, firstVariants: []string{"linux_musl_x86_64", "linux_musl_arm64", "android_arm64_armv8-a"}, @@ -461,6 +471,10 @@ func TestArchMutator(t *testing.T) { t.Errorf("want bar variants:\n%q\ngot:\n%q\n", w, g) } + if g, w := enabledVariants(ctx, "nohostcross"), tt.noHostCrossVariants; !reflect.DeepEqual(w, g) { + t.Errorf("want nohostcross variants:\n%q\ngot:\n%q\n", w, g) + } + if g, w := enabledVariants(ctx, "baz"), tt.bazVariants; !reflect.DeepEqual(w, g) { t.Errorf("want baz variants:\n%q\ngot:\n%q\n", w, g) } diff --git a/android/build_prop.go b/android/build_prop.go index 45c17c35b..b127755c8 100644 --- a/android/build_prop.go +++ b/android/build_prop.go @@ -31,16 +31,15 @@ type buildPropProperties struct { // properties in prop_files. Block_list []string - // Path to the input prop files. The contents of the files are directly - // emitted to the output - Prop_files []string `android:"path"` - // Files to be appended at the end of build.prop. These files are appended after // post_process_props without any further checking. Footer_files []string `android:"path"` // Path to a JSON file containing product configs. Product_config *string `android:"path"` + + // Optional subdirectory under which this file is installed into + Relative_install_path *string } type buildPropModule struct { @@ -56,16 +55,66 @@ func (p *buildPropModule) stem() string { return proptools.StringDefault(p.properties.Stem, "build.prop") } +func (p *buildPropModule) propFiles(ctx ModuleContext) Paths { + partition := p.PartitionTag(ctx.DeviceConfig()) + if partition == "system" { + return ctx.Config().SystemPropFiles(ctx) + } else if partition == "system_ext" { + return ctx.Config().SystemExtPropFiles(ctx) + } else if partition == "product" { + return ctx.Config().ProductPropFiles(ctx) + } + return nil +} + +func shouldAddBuildThumbprint(config Config) bool { + knownOemProperties := []string{ + "ro.product.brand", + "ro.product.name", + "ro.product.device", + } + + for _, knownProp := range knownOemProperties { + if InList(knownProp, config.OemProperties()) { + return true + } + } + return false +} + +// Can't use PartitionTag() because PartitionTag() returns the partition this module is actually +// installed (e.g. odm module's partition tag can be either "odm" or "vendor") +func (p *buildPropModule) partition(config DeviceConfig) string { + if p.SocSpecific() { + return "vendor" + } else if p.DeviceSpecific() { + return "odm" + } else if p.ProductSpecific() { + return "product" + } else if p.SystemExtSpecific() { + return "system_ext" + } + return "system" +} + +var validPartitions = []string{ + "system", + "system_ext", + "product", + "odm", +} + 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 } - partition := p.PartitionTag(ctx.DeviceConfig()) - if partition != "system" { - ctx.PropertyErrorf("partition", "unsupported partition %q: only \"system\" is supported", partition) + partition := p.partition(ctx.DeviceConfig()) + if !InList(partition, validPartitions) { + ctx.PropertyErrorf("partition", "unsupported partition %q: only %q are supported", partition, validPartitions) return } @@ -93,6 +142,7 @@ func (p *buildPropModule) GenerateAndroidBuildActions(ctx ModuleContext) { cmd.FlagWithInput("--platform-preview-sdk-fingerprint-file=", ApiFingerprintPath(ctx)) cmd.FlagWithInput("--product-config=", PathForModuleSrc(ctx, proptools.String(p.properties.Product_config))) cmd.FlagWithArg("--partition=", partition) + cmd.FlagForEachInput("--prop-files=", p.propFiles(ctx)) cmd.FlagWithOutput("--out=", p.outputFilePath) postProcessCmd := rule.Command().BuiltTool("post_process_props") @@ -100,7 +150,12 @@ func (p *buildPropModule) GenerateAndroidBuildActions(ctx ModuleContext) { postProcessCmd.Flag("--allow-dup") } postProcessCmd.FlagWithArg("--sdk-version ", config.PlatformSdkVersion().String()) - postProcessCmd.FlagWithInput("--kernel-version-file-for-uffd-gc ", PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")) + if ctx.Config().EnableUffdGc() == "default" { + postProcessCmd.FlagWithInput("--kernel-version-file-for-uffd-gc ", PathForOutput(ctx, "dexpreopt/kernel_version_for_uffd_gc.txt")) + } else { + // still need to pass an empty string to kernel-version-file-for-uffd-gc + postProcessCmd.FlagWithArg("--kernel-version-file-for-uffd-gc ", `""`) + } postProcessCmd.Text(p.outputFilePath.String()) postProcessCmd.Flags(p.properties.Block_list) @@ -108,12 +163,25 @@ func (p *buildPropModule) GenerateAndroidBuildActions(ctx ModuleContext) { rule.Build(ctx.ModuleName(), "generating build.prop") - p.installPath = PathForModuleInstall(ctx) + p.installPath = PathForModuleInstall(ctx, proptools.String(p.properties.Relative_install_path)) ctx.InstallFile(p.installPath, p.stem(), p.outputFilePath) ctx.SetOutputFiles(Paths{p.outputFilePath}, "") } +func (p *buildPropModule) AndroidMkEntries() []AndroidMkEntries { + return []AndroidMkEntries{{ + Class: "ETC", + OutputFile: OptionalPathForPath(p.outputFilePath), + ExtraEntries: []AndroidMkExtraEntriesFunc{ + func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) { + entries.SetString("LOCAL_MODULE_PATH", p.installPath.String()) + entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base()) + }, + }, + }} +} + // build_prop module generates {partition}/build.prop file. At first common build properties are // printed based on Soong config variables. And then prop_files are printed as-is. Finally, // post_process_props tool is run to check if the result build.prop is valid or not. diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go deleted file mode 100644 index bba4c0d24..000000000 --- a/android/buildinfo_prop.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2022 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 ( - "github.com/google/blueprint/proptools" -) - -func init() { - ctx := InitRegistrationContext - ctx.RegisterModuleType("buildinfo_prop", buildinfoPropFactory) -} - -type buildinfoPropProperties struct { - // Whether this module is directly installable to one of the partitions. Default: true. - Installable *bool - - Product_config *string `android:"path"` -} - -type buildinfoPropModule struct { - ModuleBase - - properties buildinfoPropProperties - - outputFilePath OutputPath - installPath InstallPath -} - -func (p *buildinfoPropModule) installable() bool { - return proptools.BoolDefault(p.properties.Installable, true) -} - -func shouldAddBuildThumbprint(config Config) bool { - knownOemProperties := []string{ - "ro.product.brand", - "ro.product.name", - "ro.product.device", - } - - for _, knownProp := range knownOemProperties { - if InList(knownProp, config.OemProperties()) { - return true - } - } - return false -} - -func (p *buildinfoPropModule) GenerateAndroidBuildActions(ctx ModuleContext) { - if ctx.ModuleName() != "buildinfo.prop" || ctx.ModuleDir() != "build/soong" { - ctx.ModuleErrorf("There can only be one buildinfo_prop module in build/soong") - return - } - p.outputFilePath = PathForModuleOut(ctx, p.Name()).OutputPath - ctx.SetOutputFiles(Paths{p.outputFilePath}, "") - - if !ctx.Config().KatiEnabled() { - WriteFileRule(ctx, p.outputFilePath, "# no buildinfo.prop if kati is disabled") - return - } - - rule := NewRuleBuilder(pctx, ctx) - - config := ctx.Config() - - cmd := rule.Command().BuiltTool("buildinfo") - - cmd.FlagWithInput("--build-hostname-file=", config.BuildHostnameFile(ctx)) - // Note: depending on BuildNumberFile will cause the build.prop file to be rebuilt - // every build, but that's intentional. - cmd.FlagWithInput("--build-number-file=", config.BuildNumberFile(ctx)) - // Export build thumbprint only if the product has specified at least one oem fingerprint property - // b/17888863 - if shouldAddBuildThumbprint(config) { - // In the previous make implementation, a dependency was not added on the thumbprint file - cmd.FlagWithArg("--build-thumbprint-file=", config.BuildThumbprintFile(ctx).String()) - } - cmd.FlagWithArg("--build-username=", config.Getenv("BUILD_USERNAME")) - // Technically we should also have a dependency on BUILD_DATETIME_FILE, - // but it can be either an absolute or relative path, which is hard to turn into - // a Path object. So just rely on the BuildNumberFile always changing to cause - // us to rebuild. - cmd.FlagWithArg("--date-file=", ctx.Config().Getenv("BUILD_DATETIME_FILE")) - cmd.FlagWithInput("--platform-preview-sdk-fingerprint-file=", ApiFingerprintPath(ctx)) - cmd.FlagWithInput("--product-config=", PathForModuleSrc(ctx, proptools.String(p.properties.Product_config))) - cmd.FlagWithOutput("--out=", p.outputFilePath) - - rule.Build(ctx.ModuleName(), "generating buildinfo props") - - if !p.installable() { - p.SkipInstall() - } - - p.installPath = PathForModuleInstall(ctx) - ctx.InstallFile(p.installPath, p.Name(), p.outputFilePath) -} - -func (p *buildinfoPropModule) AndroidMkEntries() []AndroidMkEntries { - return []AndroidMkEntries{{ - Class: "ETC", - OutputFile: OptionalPathForPath(p.outputFilePath), - ExtraEntries: []AndroidMkExtraEntriesFunc{ - func(ctx AndroidMkExtraEntriesContext, entries *AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", p.installPath.String()) - entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base()) - entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable()) - }, - }, - }} -} - -// buildinfo_prop module generates a build.prop file, which contains a set of common -// system/build.prop properties, such as ro.build.version.*. Not all properties are implemented; -// currently this module is only for microdroid. -func buildinfoPropFactory() Module { - module := &buildinfoPropModule{} - module.AddProperties(&module.properties) - InitAndroidModule(module) - return module -} diff --git a/android/config.go b/android/config.go index d16377d7b..d13e5ab3f 100644 --- a/android/config.go +++ b/android/config.go @@ -343,9 +343,6 @@ type config struct { // modules that aren't mixed-built for at least one variant will cause a build // failure ensureAllowlistIntegrity bool - - // List of Api libraries that contribute to Api surfaces. - apiLibraries map[string]struct{} } type deviceConfig struct { @@ -595,40 +592,6 @@ func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) setBuildMode(cmdArgs.ModuleGraphFile, GenerateModuleGraph) setBuildMode(cmdArgs.DocFile, GenerateDocFile) - // TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop. - config.apiLibraries = map[string]struct{}{ - "android.net.ipsec.ike": {}, - "art.module.public.api": {}, - "conscrypt.module.public.api": {}, - "framework-adservices": {}, - "framework-appsearch": {}, - "framework-bluetooth": {}, - "framework-configinfrastructure": {}, - "framework-connectivity": {}, - "framework-connectivity-t": {}, - "framework-devicelock": {}, - "framework-graphics": {}, - "framework-healthfitness": {}, - "framework-location": {}, - "framework-media": {}, - "framework-mediaprovider": {}, - "framework-nfc": {}, - "framework-ondevicepersonalization": {}, - "framework-pdf": {}, - "framework-pdf-v": {}, - "framework-permission": {}, - "framework-permission-s": {}, - "framework-scheduling": {}, - "framework-sdkextensions": {}, - "framework-statsd": {}, - "framework-sdksandbox": {}, - "framework-tethering": {}, - "framework-uwb": {}, - "framework-virtualization": {}, - "framework-wifi": {}, - "i18n.module.public.api": {}, - } - config.productVariables.Build_from_text_stub = boolPtr(config.BuildFromTextStub()) return Config{config}, err @@ -1195,6 +1158,10 @@ func (c *config) UseGoma() bool { return Bool(c.productVariables.UseGoma) } +func (c *config) UseABFS() bool { + return Bool(c.productVariables.UseABFS) +} + func (c *config) UseRBE() bool { return Bool(c.productVariables.UseRBE) } @@ -1356,10 +1323,6 @@ func (c *config) FrameworksBaseDirExists(ctx PathGlobContext) bool { return ExistentPathForSource(ctx, "frameworks", "base", "Android.bp").Valid() } -func (c *config) VndkSnapshotBuildArtifacts() bool { - return Bool(c.productVariables.VndkSnapshotBuildArtifacts) -} - func (c *config) HasMultilibConflict(arch ArchType) bool { return c.multilibConflicts[arch] } @@ -1423,10 +1386,6 @@ func (c *deviceConfig) VendorPath() string { return "vendor" } -func (c *deviceConfig) RecoverySnapshotVersion() string { - return String(c.config.productVariables.RecoverySnapshotVersion) -} - func (c *deviceConfig) CurrentApiLevelForVendorModules() string { return StringDefault(c.config.productVariables.DeviceCurrentApiLevelForVendorModules, "current") } @@ -1804,22 +1763,6 @@ func (c *deviceConfig) IsPartnerTrebleSepolicyTestEnabled() bool { return c.SystemExtSepolicyPrebuiltApiDir() != "" || c.ProductSepolicyPrebuiltApiDir() != "" } -func (c *deviceConfig) DirectedVendorSnapshot() bool { - return c.config.productVariables.DirectedVendorSnapshot -} - -func (c *deviceConfig) VendorSnapshotModules() map[string]bool { - return c.config.productVariables.VendorSnapshotModules -} - -func (c *deviceConfig) DirectedRecoverySnapshot() bool { - return c.config.productVariables.DirectedRecoverySnapshot -} - -func (c *deviceConfig) RecoverySnapshotModules() map[string]bool { - return c.config.productVariables.RecoverySnapshotModules -} - func createDirsMap(previous map[string]bool, dirs []string) (map[string]bool, error) { var ret = make(map[string]bool) for _, dir := range dirs { @@ -1846,40 +1789,6 @@ func (c *deviceConfig) createDirsMapOnce(onceKey OnceKey, previous map[string]bo return dirMap.(map[string]bool) } -var vendorSnapshotDirsExcludedKey = NewOnceKey("VendorSnapshotDirsExcludedMap") - -func (c *deviceConfig) VendorSnapshotDirsExcludedMap() map[string]bool { - return c.createDirsMapOnce(vendorSnapshotDirsExcludedKey, nil, - c.config.productVariables.VendorSnapshotDirsExcluded) -} - -var vendorSnapshotDirsIncludedKey = NewOnceKey("VendorSnapshotDirsIncludedMap") - -func (c *deviceConfig) VendorSnapshotDirsIncludedMap() map[string]bool { - excludedMap := c.VendorSnapshotDirsExcludedMap() - return c.createDirsMapOnce(vendorSnapshotDirsIncludedKey, excludedMap, - c.config.productVariables.VendorSnapshotDirsIncluded) -} - -var recoverySnapshotDirsExcludedKey = NewOnceKey("RecoverySnapshotDirsExcludedMap") - -func (c *deviceConfig) RecoverySnapshotDirsExcludedMap() map[string]bool { - return c.createDirsMapOnce(recoverySnapshotDirsExcludedKey, nil, - c.config.productVariables.RecoverySnapshotDirsExcluded) -} - -var recoverySnapshotDirsIncludedKey = NewOnceKey("RecoverySnapshotDirsIncludedMap") - -func (c *deviceConfig) RecoverySnapshotDirsIncludedMap() map[string]bool { - excludedMap := c.RecoverySnapshotDirsExcludedMap() - return c.createDirsMapOnce(recoverySnapshotDirsIncludedKey, excludedMap, - c.config.productVariables.RecoverySnapshotDirsIncluded) -} - -func (c *deviceConfig) HostFakeSnapshotEnabled() bool { - return c.config.productVariables.HostFakeSnapshotEnabled -} - func (c *deviceConfig) ShippingApiLevel() ApiLevel { if c.config.productVariables.Shipping_api_level == nil { return NoneApiLevel @@ -2033,17 +1942,6 @@ func (c *config) SetBuildFromTextStub(b bool) { c.productVariables.Build_from_text_stub = boolPtr(b) } -func (c *config) SetApiLibraries(libs []string) { - c.apiLibraries = make(map[string]struct{}) - for _, lib := range libs { - c.apiLibraries[lib] = struct{}{} - } -} - -func (c *config) GetApiLibraries() map[string]struct{} { - return c.apiLibraries -} - func (c *deviceConfig) CheckVendorSeappViolations() bool { return Bool(c.config.productVariables.CheckVendorSeappViolations) } @@ -2058,41 +1956,41 @@ func (c *config) UseResourceProcessorByDefault() bool { } var ( - mainlineApexContributionBuildFlags = []string{ - "RELEASE_APEX_CONTRIBUTIONS_ADBD", - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", - "RELEASE_APEX_CONTRIBUTIONS_APPSEARCH", - "RELEASE_APEX_CONTRIBUTIONS_ART", - "RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH", - "RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN", - "RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST", - "RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE", - "RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY", - "RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT", - "RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY", - "RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK", - "RELEASE_APEX_CONTRIBUTIONS_DOCUMENTSUIGOOGLE", - "RELEASE_APEX_CONTRIBUTIONS_EXTSERVICES", - "RELEASE_APEX_CONTRIBUTIONS_HEALTHFITNESS", - "RELEASE_APEX_CONTRIBUTIONS_IPSEC", - "RELEASE_APEX_CONTRIBUTIONS_MEDIA", - "RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER", - "RELEASE_APEX_CONTRIBUTIONS_MODULE_METADATA", - "RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE", - "RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS", - "RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION", - "RELEASE_APEX_CONTRIBUTIONS_PERMISSION", - "RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS", - "RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING", - "RELEASE_APEX_CONTRIBUTIONS_RESOLV", - "RELEASE_APEX_CONTRIBUTIONS_SCHEDULING", - "RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS", - "RELEASE_APEX_CONTRIBUTIONS_SWCODEC", - "RELEASE_APEX_CONTRIBUTIONS_STATSD", - "RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP", - "RELEASE_APEX_CONTRIBUTIONS_TZDATA", - "RELEASE_APEX_CONTRIBUTIONS_UWB", - "RELEASE_APEX_CONTRIBUTIONS_WIFI", + mainlineApexContributionBuildFlagsToApexNames = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ADBD": "com.android.adbd", + "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "com.android.adservices", + "RELEASE_APEX_CONTRIBUTIONS_APPSEARCH": "com.android.appsearch", + "RELEASE_APEX_CONTRIBUTIONS_ART": "com.android.art", + "RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH": "com.android.btservices", + "RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN": "", + "RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST": "com.android.cellbroadcast", + "RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE": "com.android.configinfrastructure", + "RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY": "com.android.tethering", + "RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT": "com.android.conscrypt", + "RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY": "", + "RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK": "com.android.devicelock", + "RELEASE_APEX_CONTRIBUTIONS_DOCUMENTSUIGOOGLE": "", + "RELEASE_APEX_CONTRIBUTIONS_EXTSERVICES": "com.android.extservices", + "RELEASE_APEX_CONTRIBUTIONS_HEALTHFITNESS": "com.android.healthfitness", + "RELEASE_APEX_CONTRIBUTIONS_IPSEC": "com.android.ipsec", + "RELEASE_APEX_CONTRIBUTIONS_MEDIA": "com.android.media", + "RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER": "com.android.mediaprovider", + "RELEASE_APEX_CONTRIBUTIONS_MODULE_METADATA": "", + "RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE": "", + "RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS": "com.android.neuralnetworks", + "RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION": "com.android.ondevicepersonalization", + "RELEASE_APEX_CONTRIBUTIONS_PERMISSION": "com.android.permission", + "RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS": "", + "RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING": "com.android.rkpd", + "RELEASE_APEX_CONTRIBUTIONS_RESOLV": "com.android.resolv", + "RELEASE_APEX_CONTRIBUTIONS_SCHEDULING": "com.android.scheduling", + "RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS": "com.android.sdkext", + "RELEASE_APEX_CONTRIBUTIONS_SWCODEC": "com.android.media.swcodec", + "RELEASE_APEX_CONTRIBUTIONS_STATSD": "com.android.os.statsd", + "RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP": "", + "RELEASE_APEX_CONTRIBUTIONS_TZDATA": "com.android.tzdata", + "RELEASE_APEX_CONTRIBUTIONS_UWB": "com.android.uwb", + "RELEASE_APEX_CONTRIBUTIONS_WIFI": "com.android.wifi", } ) @@ -2100,7 +1998,7 @@ var ( // Each mainline module will have one entry in the list func (c *config) AllApexContributions() []string { ret := []string{} - for _, f := range mainlineApexContributionBuildFlags { + for _, f := range SortedKeys(mainlineApexContributionBuildFlagsToApexNames) { if val, exists := c.GetBuildFlag(f); exists && val != "" { ret = append(ret, val) } @@ -2108,6 +2006,10 @@ func (c *config) AllApexContributions() []string { return ret } +func (c *config) AllMainlineApexNames() []string { + return SortedStringValues(mainlineApexContributionBuildFlagsToApexNames) +} + func (c *config) BuildIgnoreApexContributionContents() *bool { return c.productVariables.BuildIgnoreApexContributionContents } @@ -2127,3 +2029,27 @@ func (c *config) BoardUseVbmetaDigestInFingerprint() bool { func (c *config) OemProperties() []string { return c.productVariables.OemProperties } + +func (c *config) UseDebugArt() bool { + if c.productVariables.ArtTargetIncludeDebugBuild != nil { + return Bool(c.productVariables.ArtTargetIncludeDebugBuild) + } + + return Bool(c.productVariables.Eng) +} + +func (c *config) SystemPropFiles(ctx PathContext) Paths { + return PathsForSource(ctx, c.productVariables.SystemPropFiles) +} + +func (c *config) SystemExtPropFiles(ctx PathContext) Paths { + return PathsForSource(ctx, c.productVariables.SystemExtPropFiles) +} + +func (c *config) ProductPropFiles(ctx PathContext) Paths { + return PathsForSource(ctx, c.productVariables.ProductPropFiles) +} + +func (c *config) EnableUffdGc() string { + return String(c.productVariables.EnableUffdGc) +} diff --git a/android/config_test.go b/android/config_test.go index ca7c7f8b4..773216844 100644 --- a/android/config_test.go +++ b/android/config_test.go @@ -150,12 +150,7 @@ func TestReleaseAconfigExtraReleaseConfigs(t *testing.T) { for _, tc := range testCases { fixture := GroupFixturePreparers( - FixtureModifyProductVariables(func(vars FixtureProductVariables) { - if vars.BuildFlags == nil { - vars.BuildFlags = make(map[string]string) - } - vars.BuildFlags["RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS"] = tc.flag - }), + PrepareForTestWithBuildFlag("RELEASE_ACONFIG_EXTRA_RELEASE_CONFIGS", tc.flag), ) actual := fixture.RunTest(t).Config.ReleaseAconfigExtraReleaseConfigs() AssertArrayString(t, tc.name, tc.expected, actual) diff --git a/android/configurable_properties.go b/android/configurable_properties.go index dad42fa1d..2c794a186 100644 --- a/android/configurable_properties.go +++ b/android/configurable_properties.go @@ -26,3 +26,9 @@ func CreateSelectOsToBool(cases map[string]*bool) proptools.Configurable[bool] { resultCases, ) } + +func NewSimpleConfigurable[T proptools.ConfigurableElements](value T) proptools.Configurable[T] { + return proptools.NewConfigurable(nil, []proptools.ConfigurableCase[T]{ + proptools.NewConfigurableCase(nil, &value), + }) +} diff --git a/android/container.go b/android/container.go new file mode 100644 index 000000000..c4fdd9c91 --- /dev/null +++ b/android/container.go @@ -0,0 +1,233 @@ +// 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 ( + "reflect" + "slices" + + "github.com/google/blueprint" +) + +type StubsAvailableModule interface { + IsStubsModule() bool +} + +// Returns true if the dependency module is a stubs module +var depIsStubsModule = func(_ ModuleContext, _, dep Module) bool { + if stubsModule, ok := dep.(StubsAvailableModule); ok { + return stubsModule.IsStubsModule() + } + return false +} + +// Labels of exception functions, which are used to determine special dependencies that allow +// otherwise restricted inter-container dependencies +type exceptionHandleFuncLabel int + +const ( + checkStubs exceptionHandleFuncLabel = iota +) + +// Functions cannot be used as a value passed in providers, because functions are not +// hashable. As a workaround, the exceptionHandleFunc enum values are passed using providers, +// and the corresponding functions are called from this map. +var exceptionHandleFunctionsTable = map[exceptionHandleFuncLabel]func(ModuleContext, Module, Module) bool{ + checkStubs: depIsStubsModule, +} + +type InstallableModule interface { + EnforceApiContainerChecks() bool +} + +type restriction struct { + // container of the dependency + dependency *container + + // Error message to be emitted to the user when the dependency meets this restriction + errorMessage string + + // List of labels of allowed exception functions that allows bypassing this restriction. + // If any of the functions mapped to each labels returns true, this dependency would be + // considered allowed and an error will not be thrown. + allowedExceptions []exceptionHandleFuncLabel +} +type container struct { + // The name of the container i.e. partition, api domain + name string + + // Map of dependency restricted containers. + restricted []restriction +} + +var ( + VendorContainer = &container{ + name: VendorVariation, + restricted: nil, + } + SystemContainer = &container{ + name: "system", + restricted: []restriction{ + { + dependency: VendorContainer, + errorMessage: "Module belonging to the system partition other than HALs is " + + "not allowed to depend on the vendor partition module, in order to support " + + "independent development/update cycles and to support the Generic System " + + "Image. Try depending on HALs, VNDK or AIDL instead.", + allowedExceptions: []exceptionHandleFuncLabel{}, + }, + }, + } + ProductContainer = &container{ + name: ProductVariation, + restricted: []restriction{ + { + dependency: VendorContainer, + errorMessage: "Module belonging to the product partition is not allowed to " + + "depend on the vendor partition module, as this may lead to security " + + "vulnerabilities. Try depending on the HALs or utilize AIDL instead.", + allowedExceptions: []exceptionHandleFuncLabel{}, + }, + }, + } + ApexContainer = initializeApexContainer() + CtsContainer = &container{ + name: "cts", + restricted: []restriction{ + { + dependency: SystemContainer, + errorMessage: "CTS module should not depend on the modules belonging to the " + + "system partition, including \"framework\". Depending on the system " + + "partition may lead to disclosure of implementation details and regression " + + "due to API changes across platform versions. Try depending on the stubs instead.", + allowedExceptions: []exceptionHandleFuncLabel{checkStubs}, + }, + }, + } +) + +func initializeApexContainer() *container { + apexContainer := &container{ + name: "apex", + restricted: []restriction{ + { + dependency: SystemContainer, + errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " + + "modules belonging to the system partition. Either statically depend on the " + + "module or convert the depending module to java_sdk_library and depend on " + + "the stubs.", + allowedExceptions: []exceptionHandleFuncLabel{checkStubs}, + }, + }, + } + + apexContainer.restricted = append(apexContainer.restricted, restriction{ + dependency: apexContainer, + errorMessage: "Module belonging to Apex(es) is not allowed to depend on the " + + "modules belonging to other Apex(es). Either include the depending " + + "module in the Apex or convert the depending module to java_sdk_library " + + "and depend on its stubs.", + allowedExceptions: []exceptionHandleFuncLabel{checkStubs}, + }) + + return apexContainer +} + +type ContainersInfo struct { + belongingContainers []*container + + belongingApexes []ApexInfo +} + +func (c *ContainersInfo) BelongingContainers() []*container { + return c.belongingContainers +} + +var ContainersInfoProvider = blueprint.NewProvider[ContainersInfo]() + +// Determines if the module can be installed in the system partition or not. +// Logic is identical to that of modulePartition(...) defined in paths.go +func installInSystemPartition(ctx ModuleContext) bool { + module := ctx.Module() + return !module.InstallInTestcases() && + !module.InstallInData() && + !module.InstallInRamdisk() && + !module.InstallInVendorRamdisk() && + !module.InstallInDebugRamdisk() && + !module.InstallInRecovery() && + !module.InstallInVendor() && + !module.InstallInOdm() && + !module.InstallInProduct() && + determineModuleKind(module.base(), ctx.blueprintBaseModuleContext()) == platformModule +} + +func generateContainerInfo(ctx ModuleContext) ContainersInfo { + inSystem := installInSystemPartition(ctx) + inProduct := ctx.Module().InstallInProduct() + inVendor := ctx.Module().InstallInVendor() + inCts := false + inApex := false + + if m, ok := ctx.Module().(ImageInterface); ok { + inProduct = inProduct || m.ProductVariantNeeded(ctx) + inVendor = inVendor || m.VendorVariantNeeded(ctx) + } + + props := ctx.Module().GetProperties() + for _, prop := range props { + val := reflect.ValueOf(prop).Elem() + if val.Kind() == reflect.Struct { + testSuites := val.FieldByName("Test_suites") + if testSuites.IsValid() && testSuites.Kind() == reflect.Slice && slices.Contains(testSuites.Interface().([]string), "cts") { + inCts = true + } + } + } + + var belongingApexes []ApexInfo + if apexInfo, ok := ModuleProvider(ctx, AllApexInfoProvider); ok { + belongingApexes = apexInfo.ApexInfos + inApex = true + } + + containers := []*container{} + if inSystem { + containers = append(containers, SystemContainer) + } + if inProduct { + containers = append(containers, ProductContainer) + } + if inVendor { + containers = append(containers, VendorContainer) + } + if inCts { + containers = append(containers, CtsContainer) + } + if inApex { + containers = append(containers, ApexContainer) + } + + return ContainersInfo{ + belongingContainers: containers, + belongingApexes: belongingApexes, + } +} + +func setContainerInfo(ctx ModuleContext) { + if _, ok := ctx.Module().(InstallableModule); ok { + containersInfo := generateContainerInfo(ctx) + SetProvider(ctx, ContainersInfoProvider, containersInfo) + } +} diff --git a/android/defaults.go b/android/defaults.go index ff7900232..c0a2fc68f 100644 --- a/android/defaults.go +++ b/android/defaults.go @@ -28,7 +28,7 @@ type defaultsDependencyTag struct { var DefaultsDepTag defaultsDependencyTag type defaultsProperties struct { - Defaults []string + Defaults proptools.Configurable[[]string] } type DefaultableModuleBase struct { @@ -278,13 +278,14 @@ func RegisterDefaultsPreArchMutators(ctx RegisterMutatorsContext) { func defaultsDepsMutator(ctx BottomUpMutatorContext) { if defaultable, ok := ctx.Module().(Defaultable); ok { - ctx.AddDependency(ctx.Module(), DefaultsDepTag, defaultable.defaults().Defaults...) + ctx.AddDependency(ctx.Module(), DefaultsDepTag, defaultable.defaults().Defaults.GetOrDefault(ctx, nil)...) } } func defaultsMutator(ctx TopDownMutatorContext) { if defaultable, ok := ctx.Module().(Defaultable); ok { - if len(defaultable.defaults().Defaults) > 0 { + defaults := defaultable.defaults().Defaults.GetOrDefault(ctx, nil) + if len(defaults) > 0 { var defaultsList []Defaults seen := make(map[Defaults]bool) @@ -294,7 +295,7 @@ func defaultsMutator(ctx TopDownMutatorContext) { if !seen[defaults] { seen[defaults] = true defaultsList = append(defaultsList, defaults) - return len(defaults.defaults().Defaults) > 0 + return len(defaults.defaults().Defaults.GetOrDefault(ctx, nil)) > 0 } } else { ctx.PropertyErrorf("defaults", "module %s is not an defaults module", diff --git a/android/image.go b/android/image.go index c278dcdf9..6e5a551df 100644 --- a/android/image.go +++ b/android/image.go @@ -14,7 +14,7 @@ package android -// ImageInterface is implemented by modules that need to be split by the imageMutator. +// ImageInterface is implemented by modules that need to be split by the imageTransitionMutator. type ImageInterface interface { // ImageMutatorBegin is called before any other method in the ImageInterface. ImageMutatorBegin(ctx BaseModuleContext) @@ -22,7 +22,7 @@ type ImageInterface interface { // VendorVariantNeeded should return true if the module needs a vendor variant (installed on the vendor image). VendorVariantNeeded(ctx BaseModuleContext) bool - // ProductVariantNeeded should return true if the module needs a product variant (unstalled on the product image). + // ProductVariantNeeded should return true if the module needs a product variant (installed on the product image). ProductVariantNeeded(ctx BaseModuleContext) bool // CoreVariantNeeded should return true if the module needs a core variant (installed on the system image). @@ -81,17 +81,15 @@ const ( DebugRamdiskVariation string = "debug_ramdisk" ) -// imageMutator creates variants for modules that implement the ImageInterface that +// imageTransitionMutator creates variants for modules that implement the ImageInterface that // allow them to build differently for each partition (recovery, core, vendor, etc.). -func imageMutator(ctx BottomUpMutatorContext) { - if ctx.Os() != Android { - return - } +type imageTransitionMutator struct{} - if m, ok := ctx.Module().(ImageInterface); ok { - m.ImageMutatorBegin(ctx) +func (imageTransitionMutator) Split(ctx BaseModuleContext) []string { + var variations []string - var variations []string + if m, ok := ctx.Module().(ImageInterface); ctx.Os() == Android && ok { + m.ImageMutatorBegin(ctx) if m.CoreVariantNeeded(ctx) { variations = append(variations, CoreVariation) @@ -117,15 +115,29 @@ func imageMutator(ctx BottomUpMutatorContext) { extraVariations := m.ExtraImageVariations(ctx) variations = append(variations, extraVariations...) + } - if len(variations) == 0 { - return - } + if len(variations) == 0 { + variations = append(variations, "") + } - mod := ctx.CreateVariations(variations...) - for i, v := range variations { - mod[i].base().setImageVariation(v) - mod[i].(ImageInterface).SetImageVariation(ctx, v) - } + return variations +} + +func (imageTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { + return sourceVariation +} + +func (imageTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { + if _, ok := ctx.Module().(ImageInterface); ctx.Os() != Android || !ok { + return CoreVariation + } + return incomingVariation +} + +func (imageTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { + ctx.Module().base().setImageVariation(variation) + if m, ok := ctx.Module().(ImageInterface); ok { + m.SetImageVariation(ctx, variation) } } diff --git a/android/module.go b/android/module.go index 9f7cb377d..f9fab96a9 100644 --- a/android/module.go +++ b/android/module.go @@ -389,7 +389,7 @@ type commonProperties struct { Init_rc []string `android:"arch_variant,path"` // VINTF manifest fragments to be installed if this module is installed - Vintf_fragments []string `android:"path"` + Vintf_fragments proptools.Configurable[[]string] `android:"path"` // names of other modules to install if this module is installed Required proptools.Configurable[[]string] `android:"arch_variant"` @@ -603,6 +603,11 @@ type hostAndDeviceProperties struct { Device_supported *bool } +type hostCrossProperties struct { + // If set to true, build a variant of the module for the host cross. Defaults to true. + Host_cross_supported *bool +} + type Multilib string const ( @@ -718,6 +723,10 @@ func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib m.AddProperties(&base.hostAndDeviceProperties) } + if hod&hostCrossSupported != 0 { + m.AddProperties(&base.hostCrossProperties) + } + initArchModule(m) } @@ -803,6 +812,7 @@ type ModuleBase struct { distProperties distProperties variableProperties interface{} hostAndDeviceProperties hostAndDeviceProperties + hostCrossProperties hostCrossProperties // Arch specific versions of structs in GetProperties() prior to // initialization in InitAndroidArchModule, lets call it `generalProperties`. @@ -1207,29 +1217,7 @@ func (m *ModuleBase) GenerateTaggedDistFiles(ctx BaseModuleContext) TaggedDistFi continue } } - - // if the tagged dist file cannot be obtained from OutputFilesProvider, - // fall back to use OutputFileProducer - // TODO: remove this part after OutputFilesProvider fully replaces OutputFileProducer - if outputFileProducer, ok := m.module.(OutputFileProducer); ok { - // Call the OutputFiles(tag) method to get the paths associated with the tag. - distFilesForTag, err := outputFileProducer.OutputFiles(tag) - // If the tag was not supported and is not DefaultDistTag then it is an error. - // Failing to find paths for DefaultDistTag is not an error. It just means - // that the module type requires the legacy behavior. - if err != nil && tag != DefaultDistTag { - ctx.PropertyErrorf("dist.tag", "%s", err.Error()) - } - distFiles = distFiles.addPathsForTag(tag, distFilesForTag...) - } else if tag != DefaultDistTag { - // If the tag was specified then it is an error if the module does not - // implement OutputFileProducer because there is no other way of accessing - // the paths for the specified tag. - ctx.PropertyErrorf("dist.tag", - "tag %s not supported because the module does not implement OutputFileProducer", tag) - } } - return distFiles } @@ -1321,7 +1309,11 @@ func (m *ModuleBase) HostCrossSupported() bool { // hostEnabled is true if the host_supported property is true or the HostOrDeviceSupported // value has the hostDefault bit set. hostEnabled := proptools.BoolDefault(m.hostAndDeviceProperties.Host_supported, hod&hostDefault != 0) - return hod&hostCrossSupported != 0 && hostEnabled + + // Default true for the Host_cross_supported property + hostCrossEnabled := proptools.BoolDefault(m.hostCrossProperties.Host_cross_supported, true) + + return hod&hostCrossSupported != 0 && hostEnabled && hostCrossEnabled } func (m *ModuleBase) Platform() bool { @@ -1779,6 +1771,8 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) variables: make(map[string]string), } + setContainerInfo(ctx) + m.licenseMetadataFile = PathForModuleOut(ctx, "meta_lic") dependencyInstallFiles, dependencyPackagingSpecs := m.computeInstallDeps(ctx) @@ -1859,7 +1853,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) } } - m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments) + m.vintfFragmentsPaths = PathsForModuleSrc(ctx, m.commonProperties.Vintf_fragments.GetOrDefault(ctx, nil)) vintfDir := PathForModuleInstall(ctx, "etc", "vintf", "manifest") for _, src := range m.vintfFragmentsPaths { installedVintfFragment := vintfDir.Join(ctx, src.Base()) @@ -1920,7 +1914,7 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) return } cacheKey = &blueprint.BuildActionCacheKey{ - Id: ctx.bp.ModuleId(), + Id: ctx.bp.ModuleCacheKey(), InputHash: hash, } } @@ -2219,6 +2213,9 @@ func (e configurationEvalutor) EvaluateConfiguration(condition proptools.Configu switch variable { case "debuggable": return proptools.ConfigurableValueBool(ctx.Config().Debuggable()) + case "use_debug_art": + // TODO(b/234351700): Remove once ART does not have separated debug APEX + return proptools.ConfigurableValueBool(ctx.Config().UseDebugArt()) default: // TODO(b/323382414): Might add these on a case-by-case basis ctx.OtherModulePropertyErrorf(m, property, fmt.Sprintf("TODO(b/323382414): Product variable %q is not yet supported in selects", variable)) @@ -2383,7 +2380,7 @@ type sourceOrOutputDependencyTag struct { // The name of the module. moduleName string - // The tag that will be passed to the module's OutputFileProducer.OutputFiles(tag) method. + // The tag that will be used to get the specific output file(s). tag string } @@ -2437,14 +2434,7 @@ type SourceFileProducer interface { Srcs() Paths } -// A module that implements OutputFileProducer can be referenced from any property that is tagged with `android:"path"` -// using the ":module" syntax or ":module{.tag}" syntax and provides a list of output files to be used as if they were -// listed in the property. -type OutputFileProducer interface { - OutputFiles(tag string) (Paths, error) -} - -// OutputFilesForModule returns the paths from an OutputFileProducer with the given tag. On error, including if the +// OutputFilesForModule returns the output file paths with the given tag. On error, including if the // module produced zero paths, it reports errors to the ctx and returns nil. func OutputFilesForModule(ctx PathContext, module blueprint.Module, tag string) Paths { paths, err := outputFilesForModule(ctx, module, tag) @@ -2455,7 +2445,7 @@ func OutputFilesForModule(ctx PathContext, module blueprint.Module, tag string) return paths } -// OutputFileForModule returns the path from an OutputFileProducer with the given tag. On error, including if the +// OutputFileForModule returns the output file paths with the given tag. On error, including if the // module produced zero or multiple paths, it reports errors to the ctx and returns nil. func OutputFileForModule(ctx PathContext, module blueprint.Module, tag string) Path { paths, err := outputFilesForModule(ctx, module, tag) @@ -2531,8 +2521,9 @@ func outputFilesForModuleFromProvider(ctx PathContext, module blueprint.Module, } else if cta, isCta := ctx.(*singletonContextAdaptor); isCta { providerData, _ := cta.moduleProvider(module, OutputFilesProvider) outputFiles, _ = providerData.(OutputFilesInfo) + } else { + return nil, fmt.Errorf("unsupported context %q in method outputFilesForModuleFromProvider", reflect.TypeOf(ctx)) } - // TODO: Add a check for skipped context if outputFiles.isEmpty() { return nil, OutputFilesProviderNotSet diff --git a/android/module_test.go b/android/module_test.go index 922ea21fe..829c07987 100644 --- a/android/module_test.go +++ b/android/module_test.go @@ -722,7 +722,6 @@ test { propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"}, propInfo{Name: "B", Type: "bool", Value: "true"}, propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}}, - propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}}, propInfo{Name: "Embedded_prop", Type: "string", Value: "a"}, propInfo{Name: "Name", Type: "string", Value: "foo"}, propInfo{Name: "Nested.E", Type: "string", Value: "nested e"}, @@ -746,7 +745,6 @@ test { foo := result.ModuleForTests("foo", "").Module().base() AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues()) - }) } } diff --git a/android/mutator.go b/android/mutator.go index b81dd124a..38fb857dd 100644 --- a/android/mutator.go +++ b/android/mutator.go @@ -149,7 +149,7 @@ var preArch = []RegisterMutatorFunc{ func registerArchMutator(ctx RegisterMutatorsContext) { ctx.BottomUpBlueprint("os", osMutator).Parallel() - ctx.BottomUp("image", imageMutator).Parallel() + ctx.Transition("image", &imageTransitionMutator{}) ctx.BottomUpBlueprint("arch", archMutator).Parallel() } diff --git a/android/neverallow.go b/android/neverallow.go index 62c5e595e..0f363e78f 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -212,7 +212,7 @@ func createCcSdkVariantRules() []Rule { func createCcStubsRule() Rule { ccStubsImplementationInstallableProjectsAllowedList := []string{ - "packages/modules/Virtualization/vm_payload", + "packages/modules/Virtualization/libs/libvm_payload", } return NeverAllow(). @@ -237,6 +237,7 @@ func createInitFirstStageRules() []Rule { Without("name", "init_first_stage"). Without("name", "init_first_stage.microdroid"). With("install_in_root", "true"). + NotModuleType("prebuilt_root"). Because("install_in_root is only for init_first_stage."), } } diff --git a/android/packaging.go b/android/packaging.go index ae412e1bb..c247ed2a4 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -17,6 +17,7 @@ package android import ( "fmt" "path/filepath" + "sort" "strings" "github.com/google/blueprint" @@ -377,31 +378,59 @@ func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]Packa // CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec // entries into the specified directory. func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir WritablePath) (entries []string) { - if len(specs) == 0 { + dirsToSpecs := make(map[WritablePath]map[string]PackagingSpec) + dirsToSpecs[dir] = specs + return p.CopySpecsToDirs(ctx, builder, dirsToSpecs) +} + +// CopySpecsToDirs is a helper that will add commands to the rule builder to copy the PackagingSpec +// entries into corresponding directories. +func (p *PackagingBase) CopySpecsToDirs(ctx ModuleContext, builder *RuleBuilder, dirsToSpecs map[WritablePath]map[string]PackagingSpec) (entries []string) { + empty := true + for _, specs := range dirsToSpecs { + if len(specs) > 0 { + empty = false + break + } + } + if empty { return entries } + seenDir := make(map[string]bool) preparerPath := PathForModuleOut(ctx, "preparer.sh") cmd := builder.Command().Tool(preparerPath) var sb strings.Builder sb.WriteString("set -e\n") - for _, k := range SortedKeys(specs) { - ps := specs[k] - destPath := filepath.Join(dir.String(), ps.relPathInPackage) - destDir := filepath.Dir(destPath) - entries = append(entries, ps.relPathInPackage) - if _, ok := seenDir[destDir]; !ok { - seenDir[destDir] = true - sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir)) - } - if ps.symlinkTarget == "" { - cmd.Implicit(ps.srcPath) - sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath)) - } else { - sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath)) - } - if ps.executable { - sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath)) + + dirs := make([]WritablePath, 0, len(dirsToSpecs)) + for dir, _ := range dirsToSpecs { + dirs = append(dirs, dir) + } + sort.Slice(dirs, func(i, j int) bool { + return dirs[i].String() < dirs[j].String() + }) + + for _, dir := range dirs { + specs := dirsToSpecs[dir] + for _, k := range SortedKeys(specs) { + ps := specs[k] + destPath := filepath.Join(dir.String(), ps.relPathInPackage) + destDir := filepath.Dir(destPath) + entries = append(entries, ps.relPathInPackage) + if _, ok := seenDir[destDir]; !ok { + seenDir[destDir] = true + sb.WriteString(fmt.Sprintf("mkdir -p %s\n", destDir)) + } + if ps.symlinkTarget == "" { + cmd.Implicit(ps.srcPath) + sb.WriteString(fmt.Sprintf("cp %s %s\n", ps.srcPath, destPath)) + } else { + sb.WriteString(fmt.Sprintf("ln -sf %s %s\n", ps.symlinkTarget, destPath)) + } + if ps.executable { + sb.WriteString(fmt.Sprintf("chmod a+x %s\n", destPath)) + } } } diff --git a/android/paths.go b/android/paths.go index 03772ebcb..d20b84a42 100644 --- a/android/paths.go +++ b/android/paths.go @@ -237,6 +237,9 @@ type Path interface { // directory, and OutputPath.Join("foo").Rel() would return "foo". Rel() string + // WithoutRel returns a new Path with no relative path, i.e. Rel() will return the same value as Base(). + WithoutRel() Path + // RelativeToTop returns a new path relative to the top, it is provided solely for use in tests. // // It is guaranteed to always return the same type as it is called on, e.g. if called on an @@ -245,13 +248,13 @@ type Path interface { // A standard build has the following structure: // ../top/ // out/ - make install files go here. - // out/soong - this is the soongOutDir passed to NewTestConfig() + // out/soong - this is the outDir passed to NewTestConfig() // ... - the source files // // This function converts a path so that it appears relative to the ../top/ directory, i.e. - // * Make install paths, which have the pattern "soongOutDir/../<path>" are converted into the top + // * Make install paths, which have the pattern "outDir/../<path>" are converted into the top // relative path "out/<path>" - // * Soong install paths and other writable paths, which have the pattern "soongOutDir/<path>" are + // * Soong install paths and other writable paths, which have the pattern "outDir/soong/<path>" are // converted into the top relative path "out/soong/<path>". // * Source paths are already relative to the top. // * Phony paths are not relative to anything. @@ -261,8 +264,9 @@ type Path interface { } const ( - OutDir = "out" - OutSoongDir = OutDir + "/soong" + testOutDir = "out" + testOutSoongSubDir = "/soong" + TestOutSoongDir = testOutDir + testOutSoongSubDir ) // WritablePath is a type of path that can be used as an output for build rules. @@ -463,8 +467,8 @@ func ExistentPathsForSources(ctx PathGlobContext, paths []string) Paths { // - glob, relative to the local module directory, resolves as filepath(s), relative to the local // source directory. // - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer -// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source -// filepath. +// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated +// source filepath. // // Properties passed as the paths argument must have been annotated with struct tag // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the @@ -491,8 +495,8 @@ type SourceInput struct { // - glob, relative to the local module directory, resolves as filepath(s), relative to the local // source directory. Not valid in excludes. // - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer -// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source -// filepath. +// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated +// source filepath. // // excluding the items (similarly resolved // Properties passed as the paths argument must have been annotated with struct tag @@ -620,8 +624,8 @@ func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) // - glob, relative to the local module directory, resolves as filepath(s), relative to the local // source directory. Not valid in excludes. // - other modules using the ":name{.tag}" syntax. These modules must implement SourceFileProducer -// or OutputFileProducer. These resolve as a filepath to an output filepath or generated source -// filepath. +// or set the OutputFilesProvider. These resolve as a filepath to an output filepath or generated +// source filepath. // // and a list of the module names of missing module dependencies are returned as the second return. // Properties passed as the paths argument must have been annotated with struct tag @@ -1118,8 +1122,8 @@ func (p basePath) withRel(rel string) basePath { return p } -func (p basePath) RelativeToTop() Path { - ensureTestOnly() +func (p basePath) withoutRel() basePath { + p.rel = filepath.Base(p.path) return p } @@ -1135,6 +1139,11 @@ func (p SourcePath) withRel(rel string) SourcePath { return p } +func (p SourcePath) RelativeToTop() Path { + ensureTestOnly() + return p +} + // safePathForSource is for paths that we expect are safe -- only for use by go // code that is embedding ninja variables in paths func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) { @@ -1218,11 +1227,13 @@ func PathForSource(ctx PathContext, pathComponents ...string) SourcePath { // PathForArbitraryOutput creates a path for the given components. Unlike PathForOutput, // the path is relative to the root of the output folder, not the out/soong folder. func PathForArbitraryOutput(ctx PathContext, pathComponents ...string) Path { - p, err := validatePath(pathComponents...) + path, err := validatePath(pathComponents...) if err != nil { reportPathError(ctx, err) } - return basePath{path: filepath.Join(ctx.Config().OutDir(), p)} + fullPath := filepath.Join(ctx.Config().OutDir(), path) + path = fullPath[len(fullPath)-len(path):] + return OutputPath{basePath{path, ""}, ctx.Config().OutDir(), fullPath} } // MaybeExistentPathForSource joins the provided path components and validates that the result @@ -1275,6 +1286,11 @@ func (p SourcePath) String() string { return p.path } +func (p SourcePath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() + return p +} + // Join creates a new SourcePath with paths... joined with the current path. The // provided paths... may not use '..' to escape from the current path. func (p SourcePath) Join(ctx PathContext, paths ...string) SourcePath { @@ -1325,8 +1341,8 @@ func (p SourcePath) OverlayPath(ctx ModuleMissingDepsPathContext, path Path) Opt type OutputPath struct { basePath - // The soong build directory, i.e. Config.SoongOutDir() - soongOutDir string + // The base out directory for this path, either Config.SoongOutDir() or Config.OutDir() + outDir string fullPath string } @@ -1334,7 +1350,7 @@ type OutputPath struct { func (p OutputPath) GobEncode() ([]byte, error) { w := new(bytes.Buffer) encoder := gob.NewEncoder(w) - err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.soongOutDir), encoder.Encode(p.fullPath)) + err := errors.Join(encoder.Encode(p.basePath), encoder.Encode(p.outDir), encoder.Encode(p.fullPath)) if err != nil { return nil, err } @@ -1345,7 +1361,7 @@ func (p OutputPath) GobEncode() ([]byte, error) { func (p *OutputPath) GobDecode(data []byte) error { r := bytes.NewBuffer(data) decoder := gob.NewDecoder(r) - err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.soongOutDir), decoder.Decode(&p.fullPath)) + err := errors.Join(decoder.Decode(&p.basePath), decoder.Decode(&p.outDir), decoder.Decode(&p.fullPath)) if err != nil { return err } @@ -1359,13 +1375,13 @@ func (p OutputPath) withRel(rel string) OutputPath { return p } -func (p OutputPath) WithoutRel() OutputPath { - p.basePath.rel = filepath.Base(p.basePath.path) +func (p OutputPath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() return p } func (p OutputPath) getSoongOutDir() string { - return p.soongOutDir + return p.outDir } func (p OutputPath) RelativeToTop() Path { @@ -1373,8 +1389,13 @@ func (p OutputPath) RelativeToTop() Path { } func (p OutputPath) outputPathRelativeToTop() OutputPath { - p.fullPath = StringPathRelativeToTop(p.soongOutDir, p.fullPath) - p.soongOutDir = OutSoongDir + p.fullPath = StringPathRelativeToTop(p.outDir, p.fullPath) + if strings.HasSuffix(p.outDir, testOutSoongSubDir) { + p.outDir = TestOutSoongDir + } else { + // Handle the PathForArbitraryOutput case + p.outDir = testOutDir + } return p } @@ -1391,6 +1412,11 @@ type toolDepPath struct { basePath } +func (t toolDepPath) WithoutRel() Path { + t.basePath = t.basePath.withoutRel() + return t +} + func (t toolDepPath) RelativeToTop() Path { ensureTestOnly() return t @@ -1420,7 +1446,7 @@ func PathForOutput(ctx PathContext, pathComponents ...string) OutputPath { return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath} } -// PathsForOutput returns Paths rooted from soongOutDir +// PathsForOutput returns Paths rooted from outDir func PathsForOutput(ctx PathContext, paths []string) WritablePaths { ret := make(WritablePaths, len(paths)) for i, path := range paths { @@ -1751,14 +1777,19 @@ func ensureTestOnly() { func (p InstallPath) RelativeToTop() Path { ensureTestOnly() if p.makePath { - p.soongOutDir = OutDir + p.soongOutDir = testOutDir } else { - p.soongOutDir = OutSoongDir + p.soongOutDir = TestOutSoongDir } p.fullPath = filepath.Join(p.soongOutDir, p.path) return p } +func (p InstallPath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() + return p +} + func (p InstallPath) getSoongOutDir() string { return p.soongOutDir } @@ -2079,6 +2110,11 @@ func (p PhonyPath) RelativeToTop() Path { return p } +func (p PhonyPath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() + return p +} + func (p PhonyPath) ReplaceExtension(ctx PathContext, ext string) OutputPath { panic("Not implemented") } @@ -2095,6 +2131,11 @@ func (p testPath) RelativeToTop() Path { return p } +func (p testPath) WithoutRel() Path { + p.basePath = p.basePath.withoutRel() + return p +} + func (p testPath) String() string { return p.path } diff --git a/android/prebuilt.go b/android/prebuilt.go index 51b86a56f..9c8c13093 100644 --- a/android/prebuilt.go +++ b/android/prebuilt.go @@ -61,7 +61,7 @@ var _ ExcludeFromApexContentsTag = PrebuiltDepTag type UserSuppliedPrebuiltProperties struct { // When prefer is set to true the prebuilt will be used instead of any source module with // a matching name. - Prefer *bool `android:"arch_variant"` + Prefer proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"` // When specified this names a Soong config variable that controls the prefer property. // @@ -148,11 +148,7 @@ func PrebuiltNameFromSource(name string) string { } func (p *Prebuilt) ForcePrefer() { - p.properties.Prefer = proptools.BoolPtr(true) -} - -func (p *Prebuilt) Prefer() bool { - return proptools.Bool(p.properties.Prefer) + p.properties.Prefer = NewSimpleConfigurable(true) } // SingleSourcePathFromSupplier invokes the supplied supplier for the current module in the @@ -423,15 +419,7 @@ func PrebuiltRenameMutator(ctx BottomUpMutatorContext) { // The metadata will be used for source vs prebuilts selection func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) { m := ctx.Module() - // If this module is a prebuilt, is enabled and has not been renamed to source then add a - // dependency onto the source if it is present. - if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled(ctx) && !p.properties.PrebuiltRenamedToSource { - bmn, _ := m.(baseModuleName) - name := bmn.BaseModuleName() - if ctx.OtherModuleReverseDependencyVariantExists(name) { - ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name) - p.properties.SourceExists = true - } + if p := GetEmbeddedPrebuilt(m); p != nil { // Add a dependency from the prebuilt to the `all_apex_contributions` // metadata module // TODO: When all branches contain this singleton module, make this strict @@ -439,7 +427,16 @@ func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) { if ctx.OtherModuleExists("all_apex_contributions") { ctx.AddDependency(m, AcDepTag, "all_apex_contributions") } - + if m.Enabled(ctx) && !p.properties.PrebuiltRenamedToSource { + // If this module is a prebuilt, is enabled and has not been renamed to source then add a + // dependency onto the source if it is present. + bmn, _ := m.(baseModuleName) + name := bmn.BaseModuleName() + if ctx.OtherModuleReverseDependencyVariantExists(name) { + ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name) + p.properties.SourceExists = true + } + } } } @@ -668,12 +665,37 @@ func (p *Prebuilt) variantIsDisabled(ctx BaseMutatorContext, prebuilt Module) bo return p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0 } +type apexVariationName interface { + ApexVariationName() string +} + // usePrebuilt returns true if a prebuilt should be used instead of the source module. The prebuilt // will be used if it is marked "prefer" or if the source module is disabled. func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt Module) bool { + isMainlinePrebuilt := func(prebuilt Module) bool { + apex, ok := prebuilt.(apexVariationName) + if !ok { + return false + } + // Prebuilts of aosp apexes in prebuilts/runtime + // Used in minimal art branches + if prebuilt.base().BaseModuleName() == apex.ApexVariationName() { + return false + } + return InList(apex.ApexVariationName(), ctx.Config().AllMainlineApexNames()) + } + // Use `all_apex_contributions` for source vs prebuilt selection. psi := PrebuiltSelectionInfoMap{} - ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(am Module) { + var psiDepTag blueprint.DependencyTag + if p := GetEmbeddedPrebuilt(ctx.Module()); p != nil { + // This is a prebuilt module, visit all_apex_contributions to get the info + psiDepTag = AcDepTag + } else { + // This is a source module, visit any of its prebuilts to get the info + psiDepTag = PrebuiltDepTag + } + ctx.VisitDirectDepsWithTag(psiDepTag, func(am Module) { psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider) }) @@ -686,6 +708,11 @@ func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt M return true } + // If this is a mainline prebuilt, but has not been flagged, hide it. + if isMainlinePrebuilt(prebuilt) { + return false + } + // If the baseModuleName could not be found in the metadata module, // fall back to the existing source vs prebuilt selection. // TODO: Drop the fallback mechanisms @@ -707,7 +734,7 @@ func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt M } // TODO: use p.Properties.Name and ctx.ModuleDir to override preference - return Bool(p.properties.Prefer) + return p.properties.Prefer.GetOrDefault(ctx, false) } func (p *Prebuilt) SourceExists() bool { diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go index 6e4fc0c2f..5e4af0ba5 100644 --- a/android/prebuilt_test.go +++ b/android/prebuilt_test.go @@ -574,11 +574,7 @@ func newOverrideSourceModule() Module { func TestPrebuiltErrorCannotListBothSourceAndPrebuiltInContributions(t *testing.T) { selectMainlineModuleContritbutions := GroupFixturePreparers( - FixtureModifyProductVariables(func(variables FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_apex_contributions", - } - }), + PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_apex_contributions"), ) testPrebuiltErrorWithFixture(t, `Found duplicate variations of the same module in apex_contributions: foo and prebuilt_foo. Please remove one of these`, ` source { diff --git a/android/rule_builder.go b/android/rule_builder.go index 85e29bd20..8b03124bf 100644 --- a/android/rule_builder.go +++ b/android/rule_builder.go @@ -58,6 +58,7 @@ type RuleBuilder struct { sboxInputs bool sboxManifestPath WritablePath missingDeps []string + args map[string]string } // NewRuleBuilder returns a newly created RuleBuilder. @@ -78,6 +79,17 @@ func (rb *RuleBuilder) SetSboxOutDirDirAsEmpty() *RuleBuilder { return rb } +// Set the phony_output argument. +// This causes the output files to be ignored. +// If the output isn't created, it's not treated as an error. +// The build rule is run every time whether or not the output is created. +func (rb *RuleBuilder) SetPhonyOutput() { + if rb.args == nil { + rb.args = make(map[string]string) + } + rb.args["phony_output"] = "true" +} + // RuleBuilderInstall is a tuple of install from and to locations. type RuleBuilderInstall struct { From Path @@ -726,6 +738,12 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b commandString = proptools.NinjaEscape(commandString) } + args_vars := make([]string, len(r.args)) + i := 0 + for k, _ := range r.args { + args_vars[i] = k + i++ + } r.ctx.Build(r.pctx, BuildParams{ Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{ Command: commandString, @@ -734,7 +752,7 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b Rspfile: proptools.NinjaEscape(rspFile), RspfileContent: rspFileContent, Pool: pool, - }), + }, args_vars...), Inputs: rspFileInputs, Implicits: inputs, OrderOnly: r.OrderOnlys(), @@ -744,6 +762,7 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b Depfile: depFile, Deps: depFormat, Description: desc, + Args: r.args, }) } diff --git a/android/sbom.go b/android/sbom.go new file mode 100644 index 000000000..2a5499ed8 --- /dev/null +++ b/android/sbom.go @@ -0,0 +1,111 @@ +// 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 ( + "io" + "path/filepath" + "strings" + + "github.com/google/blueprint" +) + +var ( + // Command line tool to generate SBOM in Soong + genSbom = pctx.HostBinToolVariable("genSbom", "gen_sbom") + + // Command to generate SBOM in Soong. + genSbomRule = pctx.AndroidStaticRule("genSbomRule", blueprint.RuleParams{ + Command: "rm -rf $out && ${genSbom} --output_file ${out} --metadata ${in} --product_out ${productOut} --soong_out ${soongOut} --build_version \"$$(cat ${buildFingerprintFile})\" --product_mfr \"${productManufacturer}\" --json", + CommandDeps: []string{"${genSbom}"}, + }, "productOut", "soongOut", "buildFingerprintFile", "productManufacturer") +) + +func init() { + RegisterSbomSingleton(InitRegistrationContext) +} + +func RegisterSbomSingleton(ctx RegistrationContext) { + ctx.RegisterParallelSingletonType("sbom_singleton", sbomSingletonFactory) +} + +// sbomSingleton is used to generate build actions of generating SBOM of products. +type sbomSingleton struct { + sbomFile OutputPath +} + +func sbomSingletonFactory() Singleton { + return &sbomSingleton{} +} + +// Generates SBOM of products +func (this *sbomSingleton) GenerateBuildActions(ctx SingletonContext) { + if !ctx.Config().HasDeviceProduct() { + return + } + // Get all METADATA files and add them as implicit input + metadataFileListFile := PathForArbitraryOutput(ctx, ".module_paths", "METADATA.list") + f, err := ctx.Config().fs.Open(metadataFileListFile.String()) + if err != nil { + panic(err) + } + b, err := io.ReadAll(f) + if err != nil { + panic(err) + } + allMetadataFiles := strings.Split(string(b), "\n") + implicits := []Path{metadataFileListFile} + for _, path := range allMetadataFiles { + implicits = append(implicits, PathForSource(ctx, path)) + } + prodVars := ctx.Config().productVariables + buildFingerprintFile := PathForArbitraryOutput(ctx, "target", "product", String(prodVars.DeviceName), "build_fingerprint.txt") + implicits = append(implicits, buildFingerprintFile) + + // Add installed_files.stamp as implicit input, which depends on all installed files of the product. + installedFilesStamp := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "installed_files.stamp") + implicits = append(implicits, installedFilesStamp) + + metadataDb := PathForOutput(ctx, "compliance-metadata", ctx.Config().DeviceProduct(), "compliance-metadata.db") + this.sbomFile = PathForOutput(ctx, "sbom", ctx.Config().DeviceProduct(), "sbom.spdx.json") + ctx.Build(pctx, BuildParams{ + Rule: genSbomRule, + Input: metadataDb, + Implicits: implicits, + Output: this.sbomFile, + Args: map[string]string{ + "productOut": filepath.Join(ctx.Config().OutDir(), "target", "product", String(prodVars.DeviceName)), + "soongOut": ctx.Config().soongOutDir, + "buildFingerprintFile": buildFingerprintFile.String(), + "productManufacturer": ctx.Config().ProductVariables().ProductManufacturer, + }, + }) + + if !ctx.Config().UnbundledBuildApps() { + // When building SBOM of products, phony rule "sbom" is for generating product SBOM in Soong. + ctx.Build(pctx, BuildParams{ + Rule: blueprint.Phony, + Inputs: []Path{this.sbomFile}, + Output: PathForPhony(ctx, "sbom"), + }) + } +} + +func (this *sbomSingleton) MakeVars(ctx MakeVarsContext) { + // When building SBOM of products + if !ctx.Config().UnbundledBuildApps() { + ctx.DistForGoalWithFilename("droid", this.sbomFile, "sbom/sbom.spdx.json") + } +} diff --git a/android/testing.go b/android/testing.go index 18fd3b31c..e3853b844 100644 --- a/android/testing.go +++ b/android/testing.go @@ -174,6 +174,16 @@ var PrepareForTestDisallowNonExistentPaths = FixtureModifyConfig(func(config Con config.TestAllowNonExistentPaths = false }) +// PrepareForTestWithBuildFlag returns a FixturePreparer that sets the given flag to the given value. +func PrepareForTestWithBuildFlag(flag, value string) FixturePreparer { + return FixtureModifyProductVariables(func(variables FixtureProductVariables) { + if variables.BuildFlags == nil { + variables.BuildFlags = make(map[string]string) + } + variables.BuildFlags[flag] = value + }) +} + func NewTestArchContext(config Config) *TestContext { ctx := NewTestContext(config) ctx.preDeps = append(ctx.preDeps, registerArchMutator) @@ -822,15 +832,15 @@ func newBaseTestingComponent(config Config, provider testBuildProvider) baseTest // containing at most one instance of the temporary build directory at the start of the path while // this assumes that there can be any number at any position. func normalizeStringRelativeToTop(config Config, s string) string { - // The soongOutDir usually looks something like: /tmp/testFoo2345/001 + // The outDir usually looks something like: /tmp/testFoo2345/001 // - // Replace any usage of the soongOutDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with + // Replace any usage of the outDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with // "out/soong". outSoongDir := filepath.Clean(config.soongOutDir) re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`) s = re.ReplaceAllString(s, "out/soong") - // Replace any usage of the soongOutDir/.. with out, e.g. replace "/tmp/testFoo2345" with + // Replace any usage of the outDir/.. with out, e.g. replace "/tmp/testFoo2345" with // "out". This must come after the previous replacement otherwise this would replace // "/tmp/testFoo2345/001" with "out/001" instead of "out/soong". outDir := filepath.Dir(outSoongDir) @@ -1018,31 +1028,21 @@ func (m TestingModule) VariablesForTestsRelativeToTop() map[string]string { return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests()) } -// OutputFiles first checks if module base outputFiles property has any output +// OutputFiles checks if module base outputFiles property has any output // files can be used to return. -// If not, it calls OutputFileProducer.OutputFiles on the -// encapsulated module, exits the test immediately if there is an error and +// Exits the test immediately if there is an error and // otherwise returns the result of calling Paths.RelativeToTop // on the returned Paths. func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths { - // TODO: remove OutputFileProducer part outputFiles := m.Module().base().outputFiles if tag == "" && outputFiles.DefaultOutputFiles != nil { return outputFiles.DefaultOutputFiles.RelativeToTop() } else if taggedOutputFiles, hasTag := outputFiles.TaggedOutputFiles[tag]; hasTag { - return taggedOutputFiles + return taggedOutputFiles.RelativeToTop() } - producer, ok := m.module.(OutputFileProducer) - if !ok { - t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name()) - } - paths, err := producer.OutputFiles(tag) - if err != nil { - t.Fatal(err) - } - - return paths.RelativeToTop() + t.Fatal(fmt.Errorf("No test output file has been set for tag %q", tag)) + return nil } // TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual @@ -1244,8 +1244,14 @@ func StringPathRelativeToTop(soongOutDir string, path string) string { } if isRel { - // The path is in the soong out dir so indicate that in the relative path. - return filepath.Join("out/soong", rel) + if strings.HasSuffix(soongOutDir, testOutSoongSubDir) { + // The path is in the soong out dir so indicate that in the relative path. + return filepath.Join(TestOutSoongDir, rel) + } else { + // Handle the PathForArbitraryOutput case + return filepath.Join(testOutDir, rel) + + } } // Check to see if the path is relative to the top level out dir. diff --git a/android/util.go b/android/util.go index e21e66b88..3c0af2f38 100644 --- a/android/util.go +++ b/android/util.go @@ -201,6 +201,12 @@ func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) { return listsDiffer, diff1, diff2 } +// Returns true if the two lists have common elements. +func HasIntersection[T comparable](l1, l2 []T) bool { + _, a, b := ListSetDifference(l1, l2) + return len(a)+len(b) < len(setFromList(l1))+len(setFromList(l2)) +} + // Returns true if the given string s is prefixed with any string in the given prefix list. func HasAnyPrefix(s string, prefixList []string) bool { for _, prefix := range prefixList { diff --git a/android/util_test.go b/android/util_test.go index 8e73d835c..6537d69b9 100644 --- a/android/util_test.go +++ b/android/util_test.go @@ -818,3 +818,52 @@ func TestReverseSlice(t *testing.T) { }) } } + +var hasIntersectionTestCases = []struct { + name string + l1 []string + l2 []string + expected bool +}{ + { + name: "empty", + l1: []string{"a", "b", "c"}, + l2: []string{}, + expected: false, + }, + { + name: "both empty", + l1: []string{}, + l2: []string{}, + expected: false, + }, + { + name: "identical", + l1: []string{"a", "b", "c"}, + l2: []string{"a", "b", "c"}, + expected: true, + }, + { + name: "duplicates", + l1: []string{"a", "a", "a"}, + l2: []string{"a", "b", "c"}, + expected: true, + }, + { + name: "duplicates with no intersection", + l1: []string{"d", "d", "d", "d"}, + l2: []string{"a", "b", "c"}, + expected: false, + }, +} + +func TestHasIntersection(t *testing.T) { + for _, testCase := range hasIntersectionTestCases { + t.Run(testCase.name, func(t *testing.T) { + hasIntersection := HasIntersection(testCase.l1, testCase.l2) + if !reflect.DeepEqual(hasIntersection, testCase.expected) { + t.Errorf("expected %#v, got %#v", testCase.expected, hasIntersection) + } + }) + } +} diff --git a/android/variable.go b/android/variable.go index d144f7d43..4025607d9 100644 --- a/android/variable.go +++ b/android/variable.go @@ -132,9 +132,11 @@ type variableProperties struct { Keep_symbols *bool Keep_symbols_and_debug_frame *bool } - Static_libs []string - Whole_static_libs []string - Shared_libs []string + Static_libs []string + Exclude_static_libs []string + Whole_static_libs []string + Shared_libs []string + Jni_libs []string Cmdline []string @@ -237,8 +239,6 @@ type ProductVariables struct { VendorApiLevel *string `json:",omitempty"` - RecoverySnapshotVersion *string `json:",omitempty"` - DeviceSecondaryArch *string `json:",omitempty"` DeviceSecondaryArchVariant *string `json:",omitempty"` DeviceSecondaryCpuVariant *string `json:",omitempty"` @@ -294,6 +294,7 @@ type ProductVariables struct { HostStaticBinaries *bool `json:",omitempty"` Binder32bit *bool `json:",omitempty"` UseGoma *bool `json:",omitempty"` + UseABFS *bool `json:",omitempty"` UseRBE *bool `json:",omitempty"` UseRBEJAVAC *bool `json:",omitempty"` UseRBER8 *bool `json:",omitempty"` @@ -371,20 +372,6 @@ type ProductVariables struct { PgoAdditionalProfileDirs []string `json:",omitempty"` - VndkSnapshotBuildArtifacts *bool `json:",omitempty"` - - DirectedVendorSnapshot bool `json:",omitempty"` - VendorSnapshotModules map[string]bool `json:",omitempty"` - - DirectedRecoverySnapshot bool `json:",omitempty"` - RecoverySnapshotModules map[string]bool `json:",omitempty"` - - VendorSnapshotDirsIncluded []string `json:",omitempty"` - VendorSnapshotDirsExcluded []string `json:",omitempty"` - RecoverySnapshotDirsExcluded []string `json:",omitempty"` - RecoverySnapshotDirsIncluded []string `json:",omitempty"` - HostFakeSnapshotEnabled bool `json:",omitempty"` - MultitreeUpdateMeta bool `json:",omitempty"` BoardVendorSepolicyDirs []string `json:",omitempty"` @@ -518,6 +505,14 @@ type ProductVariables struct { BoardUseVbmetaDigestInFingerprint *bool `json:",omitempty"` OemProperties []string `json:",omitempty"` + + ArtTargetIncludeDebugBuild *bool `json:",omitempty"` + + SystemPropFiles []string `json:",omitempty"` + SystemExtPropFiles []string `json:",omitempty"` + ProductPropFiles []string `json:",omitempty"` + + EnableUffdGc *string `json:",omitempty"` } type PartitionQualifiedVariablesType struct { diff --git a/androidmk/parser/parser_test.go b/androidmk/parser/parser_test.go index db3313d27..fb03c2324 100644 --- a/androidmk/parser/parser_test.go +++ b/androidmk/parser/parser_test.go @@ -86,20 +86,19 @@ endif`, }, { name: "Blank line in rule's command", - in: `all: + in: `all: echo first line echo second line`, out: []Node{ &Rule{ - Target: SimpleMakeString("all", NoPos), - RecipePos: NoPos, - Recipe: "echo first line\necho second line", + Target: SimpleMakeString("all", NoPos), + RecipePos: NoPos, + Recipe: "echo first line\necho second line", Prerequisites: SimpleMakeString("", NoPos), }, }, }, - } func TestParse(t *testing.T) { diff --git a/apex/Android.bp b/apex/Android.bp index abae9e261..17fdfc36a 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -37,6 +37,7 @@ bootstrap_go_package { "apex_test.go", "bootclasspath_fragment_test.go", "classpath_element_test.go", + "container_test.go", "dexpreopt_bootjars_test.go", "platform_bootclasspath_test.go", "systemserver_classpath_fragment_test.go", diff --git a/apex/apex.go b/apex/apex.go index 10fe372b7..fc0500aec 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -86,7 +86,7 @@ type apexBundleProperties struct { // AndroidManifest.xml file used for the zip container of this APEX bundle. If unspecified, // a default one is automatically generated. - AndroidManifest *string `android:"path"` + AndroidManifest proptools.Configurable[string] `android:"path,replace_instead_of_append"` // Determines the file contexts file for setting the security contexts to files in this APEX // bundle. For platform APEXes, this should points to a file under /system/sepolicy Default: @@ -104,7 +104,7 @@ type apexBundleProperties struct { // path_or_glob is a path or glob pattern for a file or set of files, // uid/gid are numerial values of user ID and group ID, mode is octal value // for the file mode, and cap is hexadecimal value for the capability. - Canned_fs_config *string `android:"path"` + Canned_fs_config proptools.Configurable[string] `android:"path,replace_instead_of_append"` ApexNativeDependencies @@ -117,7 +117,8 @@ type apexBundleProperties struct { Bootclasspath_fragments []string // List of systemserverclasspath fragments that are embedded inside this APEX bundle. - Systemserverclasspath_fragments []string + Systemserverclasspath_fragments proptools.Configurable[[]string] + ResolvedSystemserverclasspathFragments []string `blueprint:"mutated"` // List of java libraries that are embedded inside this APEX bundle. Java_libs []string @@ -221,7 +222,8 @@ type ApexNativeDependencies struct { Rust_dyn_libs []string // List of native executables that are embedded inside this APEX. - Binaries []string + Binaries proptools.Configurable[[]string] + ResolvedBinaries []string `blueprint:"mutated"` // List of native tests that are embedded inside this APEX. Tests []string @@ -230,7 +232,8 @@ type ApexNativeDependencies struct { Filesystems []string // List of prebuilt_etcs that are embedded inside this APEX bundle. - Prebuilts []string + Prebuilts proptools.Configurable[[]string] + ResolvedPrebuilts []string `blueprint:"mutated"` // List of native libraries to exclude from this APEX. Exclude_native_shared_libs []string @@ -255,14 +258,14 @@ type ApexNativeDependencies struct { } // Merge combines another ApexNativeDependencies into this one -func (a *ApexNativeDependencies) Merge(b ApexNativeDependencies) { +func (a *ApexNativeDependencies) Merge(ctx android.BaseMutatorContext, b ApexNativeDependencies) { a.Native_shared_libs = append(a.Native_shared_libs, b.Native_shared_libs...) a.Jni_libs = append(a.Jni_libs, b.Jni_libs...) a.Rust_dyn_libs = append(a.Rust_dyn_libs, b.Rust_dyn_libs...) - a.Binaries = append(a.Binaries, b.Binaries...) + a.ResolvedBinaries = append(a.ResolvedBinaries, b.Binaries.GetOrDefault(ctx, nil)...) a.Tests = append(a.Tests, b.Tests...) a.Filesystems = append(a.Filesystems, b.Filesystems...) - a.Prebuilts = append(a.Prebuilts, b.Prebuilts...) + a.ResolvedPrebuilts = append(a.ResolvedPrebuilts, b.Prebuilts.GetOrDefault(ctx, nil)...) a.Exclude_native_shared_libs = append(a.Exclude_native_shared_libs, b.Exclude_native_shared_libs...) a.Exclude_jni_libs = append(a.Exclude_jni_libs, b.Exclude_jni_libs...) @@ -338,10 +341,10 @@ type apexArchBundleProperties struct { // base apex. type overridableProperties struct { // List of APKs that are embedded inside this APEX. - Apps []string + Apps proptools.Configurable[[]string] // List of prebuilt files that are embedded inside this APEX bundle. - Prebuilts []string + Prebuilts proptools.Configurable[[]string] // List of BPF programs inside this APEX bundle. Bpfs []string @@ -703,7 +706,6 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM rustLibVariations := append( target.Variations(), []blueprint.Variation{ {Mutator: "rust_libraries", Variation: "dylib"}, - {Mutator: "link", Variation: ""}, }..., ) @@ -716,7 +718,7 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM // this module. This is required since arch variant of an APEX bundle is 'common' but it is // 'arm' or 'arm64' for native shared libs. ctx.AddFarVariationDependencies(binVariations, executableTag, - android.RemoveListFromList(nativeModules.Binaries, nativeModules.Exclude_binaries)...) + android.RemoveListFromList(nativeModules.ResolvedBinaries, nativeModules.Exclude_binaries)...) ctx.AddFarVariationDependencies(binVariations, testTag, android.RemoveListFromList(nativeModules.Tests, nativeModules.Exclude_tests)...) ctx.AddFarVariationDependencies(libVariations, jniLibTag, @@ -728,7 +730,7 @@ func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeM ctx.AddFarVariationDependencies(target.Variations(), fsTag, android.RemoveListFromList(nativeModules.Filesystems, nativeModules.Exclude_filesystems)...) ctx.AddFarVariationDependencies(target.Variations(), prebuiltTag, - android.RemoveListFromList(nativeModules.Prebuilts, nativeModules.Exclude_prebuilts)...) + android.RemoveListFromList(nativeModules.ResolvedPrebuilts, nativeModules.Exclude_prebuilts)...) } func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) { @@ -783,20 +785,19 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { // Add native modules targeting both ABIs. When multilib.* is omitted for // native_shared_libs/jni_libs/tests, it implies multilib.both - deps.Merge(a.properties.Multilib.Both) - deps.Merge(ApexNativeDependencies{ + deps.Merge(ctx, a.properties.Multilib.Both) + deps.Merge(ctx, ApexNativeDependencies{ Native_shared_libs: a.properties.Native_shared_libs, Tests: a.properties.Tests, Jni_libs: a.properties.Jni_libs, - Binaries: nil, }) // Add native modules targeting the first ABI When multilib.* is omitted for // binaries, it implies multilib.first isPrimaryAbi := i == 0 if isPrimaryAbi { - deps.Merge(a.properties.Multilib.First) - deps.Merge(ApexNativeDependencies{ + deps.Merge(ctx, a.properties.Multilib.First) + deps.Merge(ctx, ApexNativeDependencies{ Native_shared_libs: nil, Tests: nil, Jni_libs: nil, @@ -807,27 +808,27 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { // Add native modules targeting either 32-bit or 64-bit ABI switch target.Arch.ArchType.Multilib { case "lib32": - deps.Merge(a.properties.Multilib.Lib32) - deps.Merge(a.properties.Multilib.Prefer32) + deps.Merge(ctx, a.properties.Multilib.Lib32) + deps.Merge(ctx, a.properties.Multilib.Prefer32) case "lib64": - deps.Merge(a.properties.Multilib.Lib64) + deps.Merge(ctx, a.properties.Multilib.Lib64) if !has32BitTarget { - deps.Merge(a.properties.Multilib.Prefer32) + deps.Merge(ctx, a.properties.Multilib.Prefer32) } } // Add native modules targeting a specific arch variant switch target.Arch.ArchType { case android.Arm: - deps.Merge(a.archProperties.Arch.Arm.ApexNativeDependencies) + deps.Merge(ctx, a.archProperties.Arch.Arm.ApexNativeDependencies) case android.Arm64: - deps.Merge(a.archProperties.Arch.Arm64.ApexNativeDependencies) + deps.Merge(ctx, a.archProperties.Arch.Arm64.ApexNativeDependencies) case android.Riscv64: - deps.Merge(a.archProperties.Arch.Riscv64.ApexNativeDependencies) + deps.Merge(ctx, a.archProperties.Arch.Riscv64.ApexNativeDependencies) case android.X86: - deps.Merge(a.archProperties.Arch.X86.ApexNativeDependencies) + deps.Merge(ctx, a.archProperties.Arch.X86.ApexNativeDependencies) case android.X86_64: - deps.Merge(a.archProperties.Arch.X86_64.ApexNativeDependencies) + deps.Merge(ctx, a.archProperties.Arch.X86_64.ApexNativeDependencies) default: panic(fmt.Errorf("unsupported arch %v\n", ctx.Arch().ArchType)) } @@ -841,11 +842,13 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { } } + a.properties.ResolvedSystemserverclasspathFragments = a.properties.Systemserverclasspath_fragments.GetOrDefault(ctx, nil) + // Common-arch dependencies come next commonVariation := ctx.Config().AndroidCommonTarget.Variations() ctx.AddFarVariationDependencies(commonVariation, rroTag, a.properties.Rros...) ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...) - ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...) + ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.ResolvedSystemserverclasspathFragments...) ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...) ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...) ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...) @@ -858,9 +861,9 @@ func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutato } commonVariation := ctx.Config().AndroidCommonTarget.Variations() - ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...) + ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps.GetOrDefault(ctx, nil)...) ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...) - if prebuilts := a.overridableProperties.Prebuilts; len(prebuilts) > 0 { + if prebuilts := a.overridableProperties.Prebuilts.GetOrDefault(ctx, nil); len(prebuilts) > 0 { // For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device) // regardless of the TARGET_PREFER_* setting. See b/144532908 arches := ctx.DeviceConfig().Arches() @@ -1493,7 +1496,6 @@ func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext Native_shared_libs: []string{"libclang_rt.hwasan"}, Tests: nil, Jni_libs: nil, - Binaries: nil, }, target, imageVariation) break } @@ -2669,6 +2671,9 @@ func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) { if a.minSdkVersionValue(ctx) == "" { ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well") } + if a.minSdkVersion(ctx).IsCurrent() { + ctx.PropertyErrorf("updatable", "updatable APEXes should not set min_sdk_version to current. Please use a finalized API level or a recognized in-development codename") + } if a.UsePlatformApis() { ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs") } @@ -2804,7 +2809,7 @@ func isStaticExecutableAllowed(apex string, exec string) bool { func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) { dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...) dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...) - dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...) + dpInfo.Deps = append(dpInfo.Deps, a.properties.ResolvedSystemserverclasspathFragments...) } var ( diff --git a/apex/apex_test.go b/apex/apex_test.go index 15c713b46..d6c8a8a73 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -5244,7 +5244,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { myApex := ctx.ModuleForTests("myapex", "android_common_myapex").Module() overrideNames := []string{ - "myapex", + "", "myjavalib.myapex", "libfoo.myapex", "libbar.myapex", @@ -8993,6 +8993,30 @@ func TestCompressedApex(t *testing.T) { ensureContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.capex\n") } +func TestApexSet_ShouldRespectCompressedApexFlag(t *testing.T) { + for _, compressionEnabled := range []bool{true, false} { + t.Run(fmt.Sprintf("compressionEnabled=%v", compressionEnabled), func(t *testing.T) { + ctx := testApex(t, ` + apex_set { + name: "com.company.android.myapex", + apex_name: "com.android.myapex", + set: "company-myapex.apks", + } + `, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.CompressedApex = proptools.BoolPtr(compressionEnabled) + }), + ) + + build := ctx.ModuleForTests("com.company.android.myapex", "android_common_com.android.myapex").Output("com.company.android.myapex.apex") + if compressionEnabled { + ensureEquals(t, build.Rule.String(), "android/soong/android.Cp") + } else { + ensureEquals(t, build.Rule.String(), "android/apex.decompressApex") + } + }) + } +} + func TestPreferredPrebuiltSharedLibDep(t *testing.T) { ctx := testApex(t, ` apex { @@ -9992,9 +10016,6 @@ func TestApexLintBcpFragmentSdkLibDeps(t *testing.T) { java.PrepareForTestWithJavaSdkLibraryFiles, java.PrepareForTestWithJacocoInstrumentation, java.FixtureWithLastReleaseApis("foo"), - android.FixtureModifyConfig(func(config android.Config) { - config.SetApiLibraries([]string{"foo"}) - }), android.FixtureMergeMockFs(fs), ).RunTestWithBp(t, bp) @@ -11278,11 +11299,7 @@ func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) { fs["platform/Test.java"] = nil }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions, - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions), ) ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment) checkBootDexJarPath(t, ctx, "framework-foo", tc.expectedBootJar) @@ -11294,13 +11311,6 @@ func TestBootDexJarsMultipleApexPrebuilts(t *testing.T) { // Test that product packaging installs the selected mainline module (either source or a specific prebuilt) // RELEASE_APEX_CONTIRBUTIONS_* build flags will be used to select the correct prebuilt for a specific release config func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) { - // check that the LOCAL_MODULE in the generated mk file matches the name used in PRODUCT_PACKAGES - // Since the name used in PRODUCT_PACKAGES does not contain prebuilt_ prefix, LOCAL_MODULE should not contain any prefix either - checkLocalModuleName := func(t *testing.T, ctx *android.TestContext, soongApexModuleName string, expectedLocalModuleName string) { - // Variations are created based on apex_name - entries := android.AndroidMkEntriesForTest(t, ctx, ctx.ModuleForTests(soongApexModuleName, "android_common_com.android.foo").Module()) - android.AssertStringEquals(t, "LOCAL_MODULE of the prebuilt apex must match the name listed in PRODUCT_PACKAGES", expectedLocalModuleName, entries[0].EntryMap["LOCAL_MODULE"][0]) - } // for a mainline module family, check that only the flagged soong module is visible to make checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleName string, hiddenModuleNames []string) { variation := func(moduleName string) string { @@ -11355,7 +11365,7 @@ func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) { prebuilt_apex { name: "com.google.android.foo.v2", apex_name: "com.android.foo", - source_apex_name: "com.google.android.foo", // source_apex_name becomes LOCAL_MODULE in the generated mk file + source_apex_name: "com.google.android.foo", src: "com.android.foo-arm.apex", prefer: true, // prefer is set to true on both the prebuilts to induce an error if flagging is not present } @@ -11428,11 +11438,7 @@ func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) { android.FixtureMergeMockFs(map[string][]byte{ "system/sepolicy/apex/com.android.foo-file_contexts": nil, }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions, - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions), ) if tc.expectedError != "" { preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError)) @@ -11441,11 +11447,6 @@ func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) { } ctx := testApex(t, bp, preparer) - // Check that the LOCAL_MODULE of the two prebuilts is com.android.foo - // This ensures that product packaging can pick them for installation if it has been flagged by apex_contributions - checkLocalModuleName(t, ctx, "prebuilt_com.google.android.foo", "com.google.android.foo") - checkLocalModuleName(t, ctx, "prebuilt_com.google.android.foo.v2", "com.google.android.foo") - // Check that // 1. The contents of the selected apex_contributions are visible to make // 2. The rest of the apexes in the mainline module family (source or other prebuilt) is hidden from make @@ -11453,6 +11454,114 @@ func TestInstallationRulesForMultipleApexPrebuilts(t *testing.T) { } } +// Test that product packaging installs the selected mainline module in workspaces withtout source mainline module +func TestInstallationRulesForMultipleApexPrebuiltsWithoutSource(t *testing.T) { + // for a mainline module family, check that only the flagged soong module is visible to make + checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleNames []string, hiddenModuleNames []string) { + variation := func(moduleName string) string { + ret := "android_common_com.android.adservices" + if moduleName == "com.google.android.foo" { + ret = "android_common_com.google.android.foo_com.google.android.foo" + } + return ret + } + + for _, visibleModuleName := range visibleModuleNames { + visibleModule := ctx.ModuleForTests(visibleModuleName, variation(visibleModuleName)).Module() + android.AssertBoolEquals(t, "Apex "+visibleModuleName+" selected using apex_contributions should be visible to make", false, visibleModule.IsHideFromMake()) + } + + for _, hiddenModuleName := range hiddenModuleNames { + hiddenModule := ctx.ModuleForTests(hiddenModuleName, variation(hiddenModuleName)).Module() + android.AssertBoolEquals(t, "Apex "+hiddenModuleName+" not selected using apex_contributions should be hidden from make", true, hiddenModule.IsHideFromMake()) + + } + } + + bp := ` + apex_key { + name: "com.android.adservices.key", + public_key: "com.android.adservices.avbpubkey", + private_key: "com.android.adservices.pem", + } + + // AOSP source apex + apex { + name: "com.android.adservices", + key: "com.android.adservices.key", + updatable: false, + } + + // Prebuilt Google APEX. + + prebuilt_apex { + name: "com.google.android.adservices", + apex_name: "com.android.adservices", + src: "com.android.foo-arm.apex", + } + + // Another Prebuilt Google APEX + prebuilt_apex { + name: "com.google.android.adservices.v2", + apex_name: "com.android.adservices", + src: "com.android.foo-arm.apex", + } + + // APEX contribution modules + + + apex_contributions { + name: "adservices.prebuilt.contributions", + api_domain: "com.android.adservices", + contents: ["prebuilt_com.google.android.adservices"], + } + + apex_contributions { + name: "adservices.prebuilt.v2.contributions", + api_domain: "com.android.adservices", + contents: ["prebuilt_com.google.android.adservices.v2"], + } + ` + + testCases := []struct { + desc string + selectedApexContributions string + expectedVisibleModuleNames []string + expectedHiddenModuleNames []string + }{ + { + desc: "No apex contributions selected, source aosp apex should be visible, and mainline prebuilts should be hidden", + selectedApexContributions: "", + expectedVisibleModuleNames: []string{"com.android.adservices"}, + expectedHiddenModuleNames: []string{"com.google.android.adservices", "com.google.android.adservices.v2"}, + }, + { + desc: "Prebuilt apex prebuilt_com.android.foo is selected", + selectedApexContributions: "adservices.prebuilt.contributions", + expectedVisibleModuleNames: []string{"com.android.adservices", "com.google.android.adservices"}, + expectedHiddenModuleNames: []string{"com.google.android.adservices.v2"}, + }, + { + desc: "Prebuilt apex prebuilt_com.android.foo.v2 is selected", + selectedApexContributions: "adservices.prebuilt.v2.contributions", + expectedVisibleModuleNames: []string{"com.android.adservices", "com.google.android.adservices.v2"}, + expectedHiddenModuleNames: []string{"com.google.android.adservices"}, + }, + } + + for _, tc := range testCases { + preparer := android.GroupFixturePreparers( + android.FixtureMergeMockFs(map[string][]byte{ + "system/sepolicy/apex/com.android.adservices-file_contexts": nil, + }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", tc.selectedApexContributions), + ) + ctx := testApex(t, bp, preparer) + + checkHideFromMake(t, ctx, tc.expectedVisibleModuleNames, tc.expectedHiddenModuleNames) + } +} + func TestAconfifDeclarationsValidation(t *testing.T) { aconfigDeclarationLibraryString := func(moduleNames []string) (ret string) { for _, moduleName := range moduleNames { @@ -11478,9 +11587,6 @@ func TestAconfifDeclarationsValidation(t *testing.T) { prepareForApexTest, java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("foo"), - android.FixtureModifyConfig(func(config android.Config) { - config.SetApiLibraries([]string{"foo"}) - }), ).RunTestWithBp(t, ` java_library { name: "baz-java-lib", @@ -11721,3 +11827,138 @@ func TestOverrideApexWithPrebuiltApexPreferred(t *testing.T) { java.CheckModuleHasDependency(t, res.TestContext, "myoverrideapex", "android_common_myoverrideapex_myoverrideapex", "foo") } + +func TestUpdatableApexMinSdkVersionCurrent(t *testing.T) { + testApexError(t, `"myapex" .*: updatable: updatable APEXes should not set min_sdk_version to current. Please use a finalized API level or a recognized in-development codename`, ` + apex { + name: "myapex", + key: "myapex.key", + updatable: true, + min_sdk_version: "current", + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) +} + +func TestPrebuiltStubNoinstall(t *testing.T) { + testFunc := func(t *testing.T, expectLibfooOnSystemLib bool, fs android.MockFS) { + result := android.GroupFixturePreparers( + prepareForApexTest, + android.PrepareForTestWithAndroidMk, + android.PrepareForTestWithMakevars, + android.FixtureMergeMockFs(fs), + ).RunTest(t) + + ldRule := result.ModuleForTests("installedlib", "android_arm64_armv8-a_shared").Rule("ld") + android.AssertStringDoesContain(t, "", ldRule.Args["libFlags"], "android_arm64_armv8-a_shared/libfoo.so") + + installRules := result.InstallMakeRulesForTesting(t) + + var installedlibRule *android.InstallMakeRule + for i, rule := range installRules { + if rule.Target == "out/target/product/test_device/system/lib/installedlib.so" { + if installedlibRule != nil { + t.Errorf("Duplicate install rules for %s", rule.Target) + } + installedlibRule = &installRules[i] + } + } + if installedlibRule == nil { + t.Errorf("No install rule found for installedlib") + return + } + + if expectLibfooOnSystemLib { + android.AssertStringListContains(t, + "installedlib doesn't have install dependency on libfoo impl", + installedlibRule.OrderOnlyDeps, + "out/target/product/test_device/system/lib/libfoo.so") + } else { + android.AssertStringListDoesNotContain(t, + "installedlib has install dependency on libfoo stub", + installedlibRule.Deps, + "out/target/product/test_device/system/lib/libfoo.so") + android.AssertStringListDoesNotContain(t, + "installedlib has order-only install dependency on libfoo stub", + installedlibRule.OrderOnlyDeps, + "out/target/product/test_device/system/lib/libfoo.so") + } + } + + prebuiltLibfooBp := []byte(` + cc_prebuilt_library { + name: "libfoo", + prefer: true, + srcs: ["libfoo.so"], + stubs: { + versions: ["1"], + }, + apex_available: ["apexfoo"], + } + `) + + apexfooBp := []byte(` + apex { + name: "apexfoo", + key: "apexfoo.key", + native_shared_libs: ["libfoo"], + updatable: false, + compile_multilib: "both", + } + apex_key { + name: "apexfoo.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) + + installedlibBp := []byte(` + cc_library { + name: "installedlib", + shared_libs: ["libfoo"], + } + `) + + t.Run("prebuilt stub (without source): no install", func(t *testing.T) { + testFunc( + t, + /*expectLibfooOnSystemLib=*/ false, + android.MockFS{ + "prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp, + "apexfoo/Android.bp": apexfooBp, + "system/sepolicy/apex/apexfoo-file_contexts": nil, + "Android.bp": installedlibBp, + }, + ) + }) + + disabledSourceLibfooBp := []byte(` + cc_library { + name: "libfoo", + enabled: false, + stubs: { + versions: ["1"], + }, + apex_available: ["apexfoo"], + } + `) + + t.Run("prebuilt stub (with disabled source): no install", func(t *testing.T) { + testFunc( + t, + /*expectLibfooOnSystemLib=*/ false, + android.MockFS{ + "prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp, + "impl/Android.bp": disabledSourceLibfooBp, + "apexfoo/Android.bp": apexfooBp, + "system/sepolicy/apex/apexfoo-file_contexts": nil, + "Android.bp": installedlibBp, + }, + ) + }) +} diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index 919cb01ac..f8c823e94 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -53,11 +53,7 @@ func TestBootclasspathFragments_FragmentDependency(t *testing.T) { java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"), java.FixtureConfigureApexBootJars("someapex:foo", "someapex:bar"), prepareForTestWithArtApex, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("foo", "baz"), ).RunTestWithBp(t, ` @@ -561,6 +557,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { result := preparers.RunTestWithBp(t, fmt.Sprintf(bp, "enabled: false,")) java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{ + `all_apex_contributions`, `dex2oatd`, `prebuilt_art-bootclasspath-fragment`, `prebuilt_com.android.art.apex.selector`, @@ -568,6 +565,7 @@ func TestBootclasspathFragmentInPrebuiltArtApex(t *testing.T) { }) java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{ + `all_apex_contributions`, `dex2oatd`, `prebuilt_bar`, `prebuilt_com.android.art.deapexer`, @@ -729,11 +727,7 @@ func TestBootclasspathFragment_HiddenAPIList(t *testing.T) { java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("foo", "quuz"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` apex { name: "com.android.art", diff --git a/apex/builder.go b/apex/builder.go index 763ce4d20..bfe16922d 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -704,8 +704,9 @@ func (a *apexBundle) buildApex(ctx android.ModuleContext) { optFlags = append(optFlags, "--override_apk_package_name "+manifestPackageName) } - if a.properties.AndroidManifest != nil { - androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest)) + androidManifest := a.properties.AndroidManifest.GetOrDefault(ctx, "") + if androidManifest != "" { + androidManifestFile := android.PathForModuleSrc(ctx, androidManifest) if a.testApex { androidManifestFile = markManifestTestOnly(ctx, androidManifestFile) @@ -1195,8 +1196,9 @@ func (a *apexBundle) buildCannedFsConfig(ctx android.ModuleContext, defaultReadO } // Custom fs_config is "appended" to the last so that entries from the file are preferred // over default ones set above. - if a.properties.Canned_fs_config != nil { - cmd.Text("cat").Input(android.PathForModuleSrc(ctx, *a.properties.Canned_fs_config)) + customFsConfig := a.properties.Canned_fs_config.GetOrDefault(ctx, "") + if customFsConfig != "" { + cmd.Text("cat").Input(android.PathForModuleSrc(ctx, customFsConfig)) } cmd.Text(")").FlagWithOutput("> ", cannedFsConfig) builder.Build("generateFsConfig", fmt.Sprintf("Generating canned fs config for %s", a.BaseModuleName())) diff --git a/apex/container_test.go b/apex/container_test.go new file mode 100644 index 000000000..39311741d --- /dev/null +++ b/apex/container_test.go @@ -0,0 +1,329 @@ +// 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 apex + +import ( + "android/soong/android" + "android/soong/java" + "fmt" + "testing" +) + +var checkContainerMatch = func(t *testing.T, name string, container string, expected bool, actual bool) { + errorMessage := fmt.Sprintf("module %s container %s value differ", name, container) + android.AssertBoolEquals(t, errorMessage, expected, actual) +} + +func TestApexDepsContainers(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForApexTest, + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("mybootclasspathlib"), + ).RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspathfragment", + ], + updatable: true, + min_sdk_version: "30", + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: [ + "mybootclasspathlib", + ], + apex_available: [ + "myapex", + ], + hidden_api: { + split_packages: ["*"], + }, + } + java_sdk_library { + name: "mybootclasspathlib", + srcs: [ + "mybootclasspathlib.java", + ], + apex_available: [ + "myapex", + ], + compile_dex: true, + static_libs: [ + "foo", + "baz", + ], + libs: [ + "bar", + ], + min_sdk_version: "30", + } + java_library { + name: "foo", + srcs:[ + "A.java", + ], + apex_available: [ + "myapex", + ], + min_sdk_version: "30", + } + java_library { + name: "bar", + srcs:[ + "A.java", + ], + min_sdk_version: "30", + } + java_library { + name: "baz", + srcs:[ + "A.java", + ], + apex_available: [ + "//apex_available:platform", + "myapex", + ], + min_sdk_version: "30", + } + `) + testcases := []struct { + moduleName string + variant string + isSystemContainer bool + isApexContainer bool + }{ + { + moduleName: "mybootclasspathlib", + variant: "android_common_myapex", + isSystemContainer: true, + isApexContainer: true, + }, + { + moduleName: "mybootclasspathlib.impl", + variant: "android_common_apex30", + isSystemContainer: true, + isApexContainer: true, + }, + { + moduleName: "mybootclasspathlib.stubs", + variant: "android_common", + isSystemContainer: true, + isApexContainer: false, + }, + { + moduleName: "foo", + variant: "android_common_apex30", + isSystemContainer: true, + isApexContainer: true, + }, + { + moduleName: "bar", + variant: "android_common", + isSystemContainer: true, + isApexContainer: false, + }, + { + moduleName: "baz", + variant: "android_common_apex30", + isSystemContainer: true, + isApexContainer: true, + }, + } + + for _, c := range testcases { + m := result.ModuleForTests(c.moduleName, c.variant) + containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider) + belongingContainers := containers.BelongingContainers() + checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers)) + checkContainerMatch(t, c.moduleName, "apex", c.isApexContainer, android.InList(android.ApexContainer, belongingContainers)) + } +} + +func TestNonUpdatableApexDepsContainers(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForApexTest, + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("mybootclasspathlib"), + ).RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspathfragment", + ], + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + bootclasspath_fragment { + name: "mybootclasspathfragment", + contents: [ + "mybootclasspathlib", + ], + apex_available: [ + "myapex", + ], + hidden_api: { + split_packages: ["*"], + }, + } + java_sdk_library { + name: "mybootclasspathlib", + srcs: [ + "mybootclasspathlib.java", + ], + apex_available: [ + "myapex", + ], + compile_dex: true, + static_libs: [ + "foo", + ], + libs: [ + "bar", + ], + } + java_library { + name: "foo", + srcs:[ + "A.java", + ], + apex_available: [ + "myapex", + ], + } + java_library { + name: "bar", + srcs:[ + "A.java", + ], + } + `) + testcases := []struct { + moduleName string + variant string + isSystemContainer bool + isApexContainer bool + }{ + { + moduleName: "mybootclasspathlib", + variant: "android_common_myapex", + isSystemContainer: true, + isApexContainer: true, + }, + { + moduleName: "mybootclasspathlib.impl", + variant: "android_common_apex10000", + isSystemContainer: true, + isApexContainer: true, + }, + { + moduleName: "mybootclasspathlib.stubs", + variant: "android_common", + isSystemContainer: true, + isApexContainer: false, + }, + { + moduleName: "foo", + variant: "android_common_apex10000", + isSystemContainer: true, + isApexContainer: true, + }, + { + moduleName: "bar", + variant: "android_common", + isSystemContainer: true, + isApexContainer: false, + }, + } + + for _, c := range testcases { + m := result.ModuleForTests(c.moduleName, c.variant) + containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider) + belongingContainers := containers.BelongingContainers() + checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers)) + checkContainerMatch(t, c.moduleName, "apex", c.isApexContainer, android.InList(android.ApexContainer, belongingContainers)) + } +} + +func TestUpdatableAndNonUpdatableApexesIdenticalMinSdkVersion(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForApexTest, + java.PrepareForTestWithJavaSdkLibraryFiles, + android.FixtureMergeMockFs(android.MockFS{ + "system/sepolicy/apex/myapex_non_updatable-file_contexts": nil, + "system/sepolicy/apex/myapex_updatable-file_contexts": nil, + }), + ).RunTestWithBp(t, ` + apex { + name: "myapex_non_updatable", + key: "myapex_non_updatable.key", + java_libs: [ + "foo", + ], + updatable: false, + min_sdk_version: "30", + } + apex_key { + name: "myapex_non_updatable.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + apex { + name: "myapex_updatable", + key: "myapex_updatable.key", + java_libs: [ + "foo", + ], + updatable: true, + min_sdk_version: "30", + } + apex_key { + name: "myapex_updatable.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "foo", + srcs:[ + "A.java", + ], + apex_available: [ + "myapex_non_updatable", + "myapex_updatable", + ], + min_sdk_version: "30", + sdk_version: "current", + } + `) + + fooApexVariant := result.ModuleForTests("foo", "android_common_apex30") + containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), fooApexVariant.Module(), android.ContainersInfoProvider) + belongingContainers := containers.BelongingContainers() + checkContainerMatch(t, "foo", "system", true, android.InList(android.SystemContainer, belongingContainers)) + checkContainerMatch(t, "foo", "apex", true, android.InList(android.ApexContainer, belongingContainers)) +} diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go index 7a17f501c..95db37d0a 100644 --- a/apex/dexpreopt_bootjars_test.go +++ b/apex/dexpreopt_bootjars_test.go @@ -399,11 +399,7 @@ func TestDexpreoptProfileWithMultiplePrebuiltArtApexes(t *testing.T) { java.FixtureConfigureBootJars("com.android.art:core-oj"), PrepareForTestWithApexBuildComponents, prepareForTestWithArtApex, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ART": tc.selectedArtApexContributions, - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", tc.selectedArtApexContributions), ).RunTestWithBp(t, bp) dexBootJars := result.ModuleForTests("dex_bootjars", "android_common") diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index 4a20cf0e3..645eebb13 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -254,11 +254,7 @@ func TestPlatformBootclasspathDependencies(t *testing.T) { java.FixtureWithLastReleaseApis("foo"), java.PrepareForTestWithDexpreopt, dexpreopt.FixtureDisableDexpreoptBootImages(false), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` apex { name: "com.android.art", @@ -429,10 +425,9 @@ func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) { java.PrepareForTestWithJavaSdkLibraryFiles, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), + java.FixtureWithPrebuiltApis(map[string][]string{ "current": {}, "30": {"foo"}, @@ -935,11 +930,7 @@ func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) { PrepareForTestWithApexBuildComponents, prepareForTestWithMyapex, java.FixtureConfigureApexBootJars(tc.configuredBootJars...), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "my_apex_contributions"), ) if tc.errorExpected { fixture = fixture.ExtendWithErrorHandler( diff --git a/apex/prebuilt.go b/apex/prebuilt.go index 65c23d34b..20a13c38b 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -42,6 +42,11 @@ var ( CommandDeps: []string{"${extract_apks}"}, }, "abis", "allow-prereleased", "sdk-version", "skip-sdk-check") + decompressApex = pctx.StaticRule("decompressApex", blueprint.RuleParams{ + Command: `rm -rf $out && ${deapexer} decompress --copy-if-uncompressed --input ${in} --output ${out}`, + CommandDeps: []string{"${deapexer}"}, + Description: "decompress $out", + }) ) type prebuilt interface { @@ -246,7 +251,6 @@ func (p *prebuiltCommon) AndroidMkEntries() []android.AndroidMkEntries { OutputFile: android.OptionalPathForPath(p.outputApex), Include: "$(BUILD_PREBUILT)", Host_required: p.hostRequired, - OverrideName: p.BaseModuleName(), ExtraEntries: []android.AndroidMkExtraEntriesFunc{ func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { entries.SetString("LOCAL_MODULE_PATH", p.installDir.String()) @@ -1073,8 +1077,14 @@ func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { inputApex := android.OptionalPathForModuleSrc(ctx, a.prebuiltCommonProperties.Selected_apex).Path() a.outputApex = android.PathForModuleOut(ctx, a.installFilename) + + // Build the output APEX. If compression is not enabled, make sure the output is not compressed even if the input is compressed + buildRule := android.Cp + if !ctx.Config().ApexCompressionEnabled() { + buildRule = decompressApex + } ctx.Build(pctx, android.BuildParams{ - Rule: android.Cp, + Rule: buildRule, Input: inputApex, Output: a.outputApex, }) diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go index f6c53b270..452a43efa 100644 --- a/apex/systemserver_classpath_fragment_test.go +++ b/apex/systemserver_classpath_fragment_test.go @@ -274,6 +274,7 @@ func TestPrebuiltSystemserverclasspathFragmentContents(t *testing.T) { ctx := result.TestContext java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{ + `all_apex_contributions`, `dex2oatd`, `prebuilt_myapex.apex.selector`, `prebuilt_myapex.deapexer`, @@ -281,6 +282,7 @@ func TestPrebuiltSystemserverclasspathFragmentContents(t *testing.T) { }) java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{ + `all_apex_contributions`, `prebuilt_bar`, `prebuilt_foo`, `prebuilt_myapex.deapexer`, @@ -432,6 +434,7 @@ func TestPrebuiltStandaloneSystemserverclasspathFragmentContents(t *testing.T) { ctx := result.TestContext java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{ + `all_apex_contributions`, `prebuilt_bar`, `prebuilt_foo`, `prebuilt_myapex.deapexer`, @@ -14,6 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +# prevent glob expansion in this script +set -f + dir=${1:-.} shift diff --git a/bin/installmod b/bin/installmod index 1d0d836ff..1ad5b8412 100755 --- a/bin/installmod +++ b/bin/installmod @@ -28,7 +28,6 @@ if [[ $# -eq 0 ]]; then return 1 fi -local _path _path=$(outmod ${@:$#:1}) if [ $? -ne 0 ]; then return 1 @@ -39,7 +38,7 @@ if [ -z "$_path" ]; then echo "Module '$1' does not produce a file ending with .apk (try 'refreshmod' if there have been build changes?)" >&2 return 1 fi -local serial_device="" +serial_device="" if [[ "$1" == "-s" ]]; then if [[ $# -le 2 ]]; then echo "-s requires an argument" >&2 @@ -48,7 +47,7 @@ if [[ "$1" == "-s" ]]; then serial_device="-s $2" shift 2 fi -local length=$(( $# - 1 )) +length=$(( $# - 1 )) echo adb $serial_device install ${@:1:$length} $_path adb $serial_device install ${@:1:$length} $_path diff --git a/bin/soongdbg b/bin/soongdbg index a73bdf90d..98d31ebc9 100755 --- a/bin/soongdbg +++ b/bin/soongdbg @@ -216,7 +216,7 @@ def print_args(parser): help="jq query for each module metadata") parser.add_argument("--deptags", action="store_true", help="show dependency tags (makes the graph much more complex)") - parser.add_argument("--tag", action="append", + parser.add_argument("--tag", action="append", default=[], help="Limit output to these dependency tags.") group = parser.add_argument_group("output formats", diff --git a/bpf/bpf.go b/bpf/bpf.go index 09262e507..644539426 100644 --- a/bpf/bpf.go +++ b/bpf/bpf.go @@ -148,6 +148,10 @@ func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) { "-no-canonical-prefixes", "-O2", + "-Wall", + "-Werror", + "-Wextra", + "-isystem bionic/libc/include", "-isystem bionic/libc/kernel/uapi", // The architecture doesn't matter here, but asm/types.h is included by linux/types.h. @@ -165,7 +169,7 @@ func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) { cflags = append(cflags, bpf.properties.Cflags...) - if proptools.Bool(bpf.properties.Btf) { + if proptools.BoolDefault(bpf.properties.Btf, true) { cflags = append(cflags, "-g") if runtime.GOOS != "darwin" { cflags = append(cflags, "-fdebug-prefix-map=/proc/self/cwd=") @@ -190,7 +194,7 @@ func (bpf *bpf) GenerateAndroidBuildActions(ctx android.ModuleContext) { }, }) - if proptools.Bool(bpf.properties.Btf) { + if proptools.BoolDefault(bpf.properties.Btf, true) { objStripped := android.ObjPathWithExt(ctx, "", src, "o") ctx.Build(pctx, android.BuildParams{ Rule: stripRule, diff --git a/cc/Android.bp b/cc/Android.bp index 3bbcaa92e..e68e4a398 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -73,7 +73,6 @@ bootstrap_go_package { "ndk_abi.go", "ndk_headers.go", "ndk_library.go", - "ndk_prebuilt.go", "ndk_sysroot.go", "llndk_library.go", diff --git a/cc/afdo.go b/cc/afdo.go index 6921edfba..14d105e99 100644 --- a/cc/afdo.go +++ b/cc/afdo.go @@ -47,6 +47,10 @@ func (afdo *afdo) begin(ctx BaseModuleContext) { if ctx.Config().Eng() { afdo.Properties.Afdo = false } + // Disable for native coverage builds. + if ctx.DeviceConfig().NativeCoverageEnabled() { + afdo.Properties.Afdo = false + } } // afdoEnabled returns true for binaries and shared libraries @@ -76,6 +80,8 @@ func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags { } if afdo.Properties.Afdo || afdo.Properties.AfdoDep { + // Emit additional debug info for AutoFDO + flags.Local.CFlags = append([]string{"-fdebug-info-for-profiling"}, flags.Local.CFlags...) // We use `-funique-internal-linkage-names` to associate profiles to the right internal // functions. This option should be used before generating a profile. Because a profile // generated for a binary without unique names doesn't work well building a binary with diff --git a/cc/androidmk.go b/cc/androidmk.go index 41346538f..cecaae23a 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -451,10 +451,6 @@ func (c *vndkPrebuiltLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, en }) } -func (c *ndkPrebuiltStlLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { - entries.Class = "SHARED_LIBRARIES" -} - func (p *prebuiltLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) { entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { if p.properties.Check_elf_files != nil { diff --git a/cc/builder.go b/cc/builder.go index 367bda380..a05a16dcb 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -156,11 +156,17 @@ var ( "args") // Rule to invoke `strip` (to discard symbols and data from object files) on darwin architecture. - darwinStrip = pctx.AndroidStaticRule("darwinStrip", - blueprint.RuleParams{ - Command: "${config.MacStripPath} -u -r -o $out $in", - CommandDeps: []string{"${config.MacStripPath}"}, - }) + darwinStrip = func() blueprint.Rule { + if runtime.GOOS == "darwin" { + return pctx.AndroidStaticRule("darwinStrip", + blueprint.RuleParams{ + Command: "${config.MacStripPath} -u -r -o $out $in", + CommandDeps: []string{"${config.MacStripPath}"}, + }) + } else { + return nil + } + }() // b/132822437: objcopy uses a file descriptor per .o file when called on .a files, which runs the system out of // file descriptors on darwin. Limit concurrent calls to 5 on darwin. @@ -174,11 +180,17 @@ var ( } }() - darwinLipo = pctx.AndroidStaticRule("darwinLipo", - blueprint.RuleParams{ - Command: "${config.MacLipoPath} -create -output $out $in", - CommandDeps: []string{"${config.MacLipoPath}"}, - }) + darwinLipo = func() blueprint.Rule { + if runtime.GOOS == "darwin" { + return pctx.AndroidStaticRule("darwinLipo", + blueprint.RuleParams{ + Command: "${config.MacLipoPath} -create -output $out $in", + CommandDeps: []string{"${config.MacLipoPath}"}, + }) + } else { + return nil + } + }() _ = pctx.SourcePathVariable("archiveRepackPath", "build/soong/scripts/archive_repack.sh") @@ -48,10 +48,10 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("cc_defaults", defaultsFactory) ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("sdk", sdkMutator).Parallel() + ctx.Transition("sdk", &sdkTransitionMutator{}) ctx.BottomUp("llndk", llndkMutator).Parallel() - ctx.BottomUp("link", LinkageMutator).Parallel() - ctx.BottomUp("version", versionMutator).Parallel() + ctx.Transition("link", &linkageTransitionMutator{}) + ctx.Transition("version", &versionTransitionMutator{}) ctx.BottomUp("begin", BeginMutator).Parallel() }) @@ -1028,13 +1028,6 @@ func (c *Module) SelectedStl() string { return "" } -func (c *Module) NdkPrebuiltStl() bool { - if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok { - return true - } - return false -} - func (c *Module) StubDecorator() bool { if _, ok := c.linker.(*stubDecorator); ok { return true @@ -1088,16 +1081,6 @@ func (c *Module) CcLibraryInterface() bool { return false } -func (c *Module) IsNdkPrebuiltStl() bool { - if c.linker == nil { - return false - } - if _, ok := c.linker.(*ndkPrebuiltStlLinker); ok { - return true - } - return false -} - func (c *Module) RlibStd() bool { panic(fmt.Errorf("RlibStd called on non-Rust module: %q", c.BaseModuleName())) } @@ -1867,8 +1850,10 @@ var ( "libdl": true, "libz": true, // art apex + // TODO(b/234351700): Remove this when com.android.art.debug is gone. "libandroidio": true, "libdexfile": true, + "libdexfiled": true, // com.android.art.debug only "libnativebridge": true, "libnativehelper": true, "libnativeloader": true, @@ -2752,10 +2737,6 @@ func checkLinkType(ctx android.BaseModuleContext, from LinkableInterface, to Lin return } if c, ok := to.(*Module); ok { - if c.NdkPrebuiltStl() { - // These are allowed, but they don't set sdk_version - return - } if c.StubDecorator() { // These aren't real libraries, but are the stub shared libraries that are included in // the NDK. @@ -3925,7 +3906,6 @@ const ( headerLibrary testBin // testBinary already declared ndkLibrary - ndkPrebuiltStl ) func (c *Module) typ() moduleType { @@ -3964,8 +3944,6 @@ func (c *Module) typ() moduleType { return sharedLibrary } else if c.isNDKStubLibrary() { return ndkLibrary - } else if c.IsNdkPrebuiltStl() { - return ndkPrebuiltStl } return unknownType } @@ -4077,6 +4055,13 @@ func (c *Module) BaseModuleName() string { return c.ModuleBase.BaseModuleName() } +func (c *Module) stubsSymbolFilePath() android.Path { + if library, ok := c.linker.(*libraryDecorator); ok { + return library.stubsSymbolFilePath + } + return android.OptionalPath{}.Path() +} + var Bool = proptools.Bool var BoolDefault = proptools.BoolDefault var BoolPtr = proptools.BoolPtr diff --git a/cc/cc_test.go b/cc/cc_test.go index ccdaae58f..7372fff66 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -2760,7 +2760,7 @@ func TestIncludeDirectoryOrdering(t *testing.T) { "external/foo/libarm", "external/foo/lib32", "external/foo/libandroid_arm", - "defaults/cc/common/ndk_libc++_shared", + "defaults/cc/common/ndk_libc++_shared_include_dirs", } conly := []string{"-fPIC", "${config.CommonGlobalConlyflags}"} @@ -3194,12 +3194,7 @@ func TestVendorSdkVersion(t *testing.T) { ctx = android.GroupFixturePreparers( prepareForCcTest, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - if variables.BuildFlags == nil { - variables.BuildFlags = make(map[string]string) - } - variables.BuildFlags["RELEASE_BOARD_API_LEVEL_FROZEN"] = "true" - }), + android.PrepareForTestWithBuildFlag("RELEASE_BOARD_API_LEVEL_FROZEN", "true"), ).RunTestWithBp(t, bp) testSdkVersionFlag("libfoo", "30") testSdkVersionFlag("libbar", "29") diff --git a/cc/ccdeps.go b/cc/ccdeps.go index d30abbab7..469fe31fa 100644 --- a/cc/ccdeps.go +++ b/cc/ccdeps.go @@ -85,9 +85,8 @@ func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonCon moduleDeps := ccDeps{} moduleInfos := map[string]ccIdeInfo{} - // Track which projects have already had CMakeLists.txt generated to keep the first - // variant for each project. - seenProjects := map[string]bool{} + // Track if best variant (device arch match) has been found. + bestVariantFound := map[string]bool{} pathToCC, _ := evalVariable(ctx, "${config.ClangBin}/") moduleDeps.C_clang = fmt.Sprintf("%s%s", buildCMakePath(pathToCC), cClang) @@ -96,7 +95,7 @@ func (c *ccdepsGeneratorSingleton) GenerateBuildActions(ctx android.SingletonCon ctx.VisitAllModules(func(module android.Module) { if ccModule, ok := module.(*Module); ok { if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok { - generateCLionProjectData(ctx, compiledModule, ccModule, seenProjects, moduleInfos) + generateCLionProjectData(ctx, compiledModule, ccModule, bestVariantFound, moduleInfos) } } }) @@ -180,26 +179,30 @@ func parseCompilerCCParameters(ctx android.SingletonContext, params []string) cc } func generateCLionProjectData(ctx android.SingletonContext, compiledModule CompiledInterface, - ccModule *Module, seenProjects map[string]bool, moduleInfos map[string]ccIdeInfo) { + ccModule *Module, bestVariantFound map[string]bool, moduleInfos map[string]ccIdeInfo) { + moduleName := ccModule.ModuleBase.Name() srcs := compiledModule.Srcs() - if len(srcs) == 0 { + + // Skip if best variant has already been found. + if bestVariantFound[moduleName] { return } - // Only keep the DeviceArch variant module. - if ctx.DeviceConfig().DeviceArch() != ccModule.ModuleBase.Arch().ArchType.Name { + // Skip if sources are empty. + if len(srcs) == 0 { return } - clionProjectLocation := getCMakeListsForModule(ccModule, ctx) - if seenProjects[clionProjectLocation] { + // Check if device arch matches, in which case this is the best variant and takes precedence. + if ccModule.Device() && ccModule.ModuleBase.Arch().ArchType.Name == ctx.DeviceConfig().DeviceArch() { + bestVariantFound[moduleName] = true + } else if _, ok := moduleInfos[moduleName]; ok { + // Skip because this isn't the best variant and a previous one has already been added. + // Heuristically, ones that appear first are likely to be more relevant. return } - seenProjects[clionProjectLocation] = true - - name := ccModule.ModuleBase.Name() - dpInfo := moduleInfos[name] + dpInfo := ccIdeInfo{} dpInfo.Path = append(dpInfo.Path, path.Dir(ctx.BlueprintFile(ccModule))) dpInfo.Srcs = append(dpInfo.Srcs, srcs.Strings()...) @@ -216,9 +219,9 @@ func generateCLionProjectData(ctx android.SingletonContext, compiledModule Compi dpInfo.Local_Cpp_flags = parseCompilerCCParameters(ctx, ccModule.flags.Local.CppFlags) dpInfo.System_include_flags = parseCompilerCCParameters(ctx, ccModule.flags.SystemIncludeFlags) - dpInfo.Module_name = name + dpInfo.Module_name = moduleName - moduleInfos[name] = dpInfo + moduleInfos[moduleName] = dpInfo } type Deal struct { diff --git a/cc/cmake_snapshot.go b/cc/cmake_snapshot.go index fb2924a07..8f3ad9639 100644 --- a/cc/cmake_snapshot.go +++ b/cc/cmake_snapshot.go @@ -347,8 +347,11 @@ func (m *CmakeSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) { if slices.Contains(ignoredSystemLibs, moduleName) { return false // system libs built in-tree for Android } + if dep.IsPrebuilt() { + return false // prebuilts are not supported + } if dep.compiler == nil { - return false // unsupported module type (e.g. prebuilt) + return false // unsupported module type } isAidlModule := dep.compiler.baseCompilerProps().AidlInterface.Lang != "" diff --git a/cc/compiler.go b/cc/compiler.go index 03f9899d8..ed1053334 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -53,7 +53,7 @@ type BaseCompilerProperties struct { Cflags proptools.Configurable[[]string] `android:"arch_variant"` // list of module-specific flags that will be used for C++ compiles - Cppflags []string `android:"arch_variant"` + Cppflags proptools.Configurable[[]string] `android:"arch_variant"` // list of module-specific flags that will be used for C compiles Conlyflags []string `android:"arch_variant"` @@ -367,8 +367,9 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps compiler.srcsBeforeGen = append(compiler.srcsBeforeGen, deps.GeneratedSources...) cflags := compiler.Properties.Cflags.GetOrDefault(ctx, nil) + cppflags := compiler.Properties.Cppflags.GetOrDefault(ctx, nil) CheckBadCompilerFlags(ctx, "cflags", cflags) - CheckBadCompilerFlags(ctx, "cppflags", compiler.Properties.Cppflags) + CheckBadCompilerFlags(ctx, "cppflags", cppflags) CheckBadCompilerFlags(ctx, "conlyflags", compiler.Properties.Conlyflags) CheckBadCompilerFlags(ctx, "asflags", compiler.Properties.Asflags) CheckBadCompilerFlags(ctx, "vendor.cflags", compiler.Properties.Target.Vendor.Cflags) @@ -381,7 +382,7 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps esc := proptools.NinjaAndShellEscapeList flags.Local.CFlags = append(flags.Local.CFlags, esc(cflags)...) - flags.Local.CppFlags = append(flags.Local.CppFlags, esc(compiler.Properties.Cppflags)...) + flags.Local.CppFlags = append(flags.Local.CppFlags, esc(cppflags)...) flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, esc(compiler.Properties.Conlyflags)...) flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Asflags)...) flags.Local.YasmFlags = append(flags.Local.YasmFlags, esc(compiler.Properties.Asflags)...) @@ -808,7 +809,7 @@ type RustBindgenClangProperties struct { // list of c++ specific clang flags required to correctly interpret the headers. // This is provided primarily to make sure cppflags defined in cc_defaults are pulled in. - Cppflags []string `android:"arch_variant"` + Cppflags proptools.Configurable[[]string] `android:"arch_variant"` // C standard version to use. Can be a specific version (such as "gnu11"), // "experimental" (which will use draft versions like C1x when available), diff --git a/cc/config/darwin_host.go b/cc/config/darwin_host.go index 485666954..2ea607a9d 100644 --- a/cc/config/darwin_host.go +++ b/cc/config/darwin_host.go @@ -18,6 +18,7 @@ import ( "fmt" "os/exec" "path/filepath" + "runtime" "strings" "sync" @@ -73,31 +74,33 @@ var ( ) func init() { - pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string { - return getMacTools(ctx).sdkRoot - }) - pctx.StaticVariable("macMinVersion", "10.14") - pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string { - return getMacTools(ctx).arPath - }) - - pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string { - return getMacTools(ctx).lipoPath - }) - - pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string { - return getMacTools(ctx).stripPath - }) - - pctx.VariableFunc("MacToolPath", func(ctx android.PackageVarContext) string { - return getMacTools(ctx).toolPath - }) - - pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " ")) - pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " ")) - pctx.StaticVariable("DarwinLldflags", strings.Join(darwinLdflags, " ")) - - pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64") + if runtime.GOOS == "darwin" { + pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string { + return getMacTools(ctx).sdkRoot + }) + pctx.StaticVariable("macMinVersion", "10.14") + pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string { + return getMacTools(ctx).arPath + }) + + pctx.VariableFunc("MacLipoPath", func(ctx android.PackageVarContext) string { + return getMacTools(ctx).lipoPath + }) + + pctx.VariableFunc("MacStripPath", func(ctx android.PackageVarContext) string { + return getMacTools(ctx).stripPath + }) + + pctx.VariableFunc("MacToolPath", func(ctx android.PackageVarContext) string { + return getMacTools(ctx).toolPath + }) + + pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " ")) + pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " ")) + pctx.StaticVariable("DarwinLldflags", strings.Join(darwinLdflags, " ")) + + pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64") + } } func MacStripPath(ctx android.PathContext) string { diff --git a/cc/config/global.go b/cc/config/global.go index 62a4765f4..79507135e 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -136,11 +136,6 @@ var ( // displaying logs in web browsers. "-fmessage-length=0", - // Disable C++17 "relaxed template template argument matching" as a workaround for - // our out-dated libcxx. - // http://b/341084395 - "-fno-relaxed-template-template-args", - // Using simple template names reduces the size of debug builds. "-gsimple-template-names", @@ -149,9 +144,6 @@ var ( // Make paths in deps files relative. "-no-canonical-prefixes", - - // http://b/315250603 temporarily disabled - "-Wno-error=format", } commonGlobalConlyflags = []string{} @@ -181,9 +173,6 @@ var ( "-Werror=sequence-point", "-Werror=format-security", "-nostdlibinc", - - // Emit additional debug info for AutoFDO - "-fdebug-info-for-profiling", } commonGlobalLldflags = []string{ @@ -286,7 +275,7 @@ var ( "-Wno-zero-as-null-pointer-constant", // http://b/68236239 "-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485 "-Wno-deprecated-enum-enum-conversion", - "-Wno-pessimizing-move", // http://b/154270751 + "-Wno-error=pessimizing-move", // http://b/154270751 // New warnings to be fixed after clang-r399163 "-Wno-non-c-typedef-for-linkage", // http://b/161304145 // New warnings to be fixed after clang-r428724 @@ -350,6 +339,9 @@ var ( "-Wno-unused", "-Wno-deprecated", + + // http://b/315250603 temporarily disabled + "-Wno-error=format", } // Similar to noOverrideGlobalCflags, but applies only to third-party code @@ -376,6 +368,7 @@ var ( "-Wno-unqualified-std-cast-call", "-Wno-array-parameter", "-Wno-gnu-offsetof-extensions", + "-Wno-pessimizing-move", // TODO: Enable this warning http://b/315245071 "-Wno-fortify-source", } @@ -397,8 +390,8 @@ var ( // prebuilts/clang default settings. ClangDefaultBase = "prebuilts/clang/host" - ClangDefaultVersion = "clang-r522817" - ClangDefaultShortVersion = "18" + ClangDefaultVersion = "clang-r530567" + ClangDefaultShortVersion = "19" // Directories with warnings from Android.bp files. WarningAllowedProjects = []string{ diff --git a/cc/library.go b/cc/library.go index 560b1ae40..092b17779 100644 --- a/cc/library.go +++ b/cc/library.go @@ -19,6 +19,7 @@ import ( "io" "path/filepath" "regexp" + "slices" "strconv" "strings" "sync" @@ -63,7 +64,7 @@ type LibraryProperties struct { Stubs struct { // Relative path to the symbol map. The symbol map provides the list of // symbols that are exported for stubs variant of this library. - Symbol_file *string `android:"path"` + Symbol_file *string `android:"path,arch_variant"` // List versions to generate stubs libs for. The version name "current" is always // implicitly added. @@ -74,7 +75,7 @@ type LibraryProperties struct { // implementation is made available by some other means, e.g. in a Microdroid // virtual machine. Implementation_installable *bool - } + } `android:"arch_variant"` // set the name of the output Stem *string `android:"arch_variant"` @@ -117,7 +118,7 @@ type LibraryProperties struct { // If this is an LLNDK library, properties to describe the LLNDK stubs. Will be copied from // the module pointed to by llndk_stubs if it is set. - Llndk llndkLibraryProperties + Llndk llndkLibraryProperties `android:"arch_variant"` // If this is a vendor public library, properties to describe the vendor public library stubs. Vendor_public_library vendorPublicLibraryProperties @@ -427,6 +428,9 @@ type libraryDecorator struct { *baseInstaller apiListCoverageXmlPath android.ModuleOutPath + + // Path to the file containing the APIs exported by this library + stubsSymbolFilePath android.Path } // linkerProps returns the list of properties structs relevant for this library. (For example, if @@ -595,6 +599,7 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile) return Objects{} } + library.stubsSymbolFilePath = android.PathForModuleSrc(ctx, symbolFile) // b/239274367 --apex and --systemapi filters symbols tagged with # apex and # // systemapi, respectively. The former is for symbols defined in platform libraries // and the latter is for symbols defined in APEXes. @@ -711,7 +716,7 @@ type versionedInterface interface { setStubsVersion(string) stubsVersion() string - stubsVersions(ctx android.BaseMutatorContext) []string + stubsVersions(ctx android.BaseModuleContext) []string setAllStubsVersions([]string) allStubsVersions() []string @@ -1903,7 +1908,7 @@ func (library *libraryDecorator) isStubsImplementationRequired() bool { return BoolDefault(library.Properties.Stubs.Implementation_installable, true) } -func (library *libraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string { +func (library *libraryDecorator) stubsVersions(ctx android.BaseModuleContext) []string { if !library.hasStubsVariants() { return nil } @@ -2064,26 +2069,26 @@ func NewLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) // connects a shared library to a static library in order to reuse its .o files to avoid // compiling source files twice. -func reuseStaticLibrary(mctx android.BottomUpMutatorContext, static, shared *Module) { - if staticCompiler, ok := static.compiler.(*libraryDecorator); ok { - sharedCompiler := shared.compiler.(*libraryDecorator) +func reuseStaticLibrary(ctx android.BottomUpMutatorContext, shared *Module) { + if sharedCompiler, ok := shared.compiler.(*libraryDecorator); ok { // Check libraries in addition to cflags, since libraries may be exporting different // include directories. - if len(staticCompiler.StaticProperties.Static.Cflags.GetOrDefault(mctx, nil)) == 0 && - len(sharedCompiler.SharedProperties.Shared.Cflags.GetOrDefault(mctx, nil)) == 0 && - len(staticCompiler.StaticProperties.Static.Whole_static_libs) == 0 && + if len(sharedCompiler.StaticProperties.Static.Cflags.GetOrDefault(ctx, nil)) == 0 && + len(sharedCompiler.SharedProperties.Shared.Cflags.GetOrDefault(ctx, nil)) == 0 && + len(sharedCompiler.StaticProperties.Static.Whole_static_libs) == 0 && len(sharedCompiler.SharedProperties.Shared.Whole_static_libs) == 0 && - len(staticCompiler.StaticProperties.Static.Static_libs) == 0 && + len(sharedCompiler.StaticProperties.Static.Static_libs) == 0 && len(sharedCompiler.SharedProperties.Shared.Static_libs) == 0 && - len(staticCompiler.StaticProperties.Static.Shared_libs) == 0 && + len(sharedCompiler.StaticProperties.Static.Shared_libs) == 0 && len(sharedCompiler.SharedProperties.Shared.Shared_libs) == 0 && // Compare System_shared_libs properties with nil because empty lists are // semantically significant for them. - staticCompiler.StaticProperties.Static.System_shared_libs == nil && + sharedCompiler.StaticProperties.Static.System_shared_libs == nil && sharedCompiler.SharedProperties.Shared.System_shared_libs == nil { - mctx.AddInterVariantDependency(reuseObjTag, shared, static) + // TODO: namespaces? + ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, reuseObjTag, ctx.ModuleName()) sharedCompiler.baseCompiler.Properties.OriginalSrcs = sharedCompiler.baseCompiler.Properties.Srcs sharedCompiler.baseCompiler.Properties.Srcs = nil @@ -2091,19 +2096,21 @@ func reuseStaticLibrary(mctx android.BottomUpMutatorContext, static, shared *Mod } // This dep is just to reference static variant from shared variant - mctx.AddInterVariantDependency(staticVariantTag, shared, static) + ctx.AddVariationDependencies([]blueprint.Variation{{"link", "static"}}, staticVariantTag, ctx.ModuleName()) } } -// LinkageMutator adds "static" or "shared" variants for modules depending +// linkageTransitionMutator adds "static" or "shared" variants for modules depending // on whether the module can be built as a static library or a shared library. -func LinkageMutator(mctx android.BottomUpMutatorContext) { +type linkageTransitionMutator struct{} + +func (linkageTransitionMutator) Split(ctx android.BaseModuleContext) []string { ccPrebuilt := false - if m, ok := mctx.Module().(*Module); ok && m.linker != nil { + if m, ok := ctx.Module().(*Module); ok && m.linker != nil { _, ccPrebuilt = m.linker.(prebuiltLibraryInterface) } if ccPrebuilt { - library := mctx.Module().(*Module).linker.(prebuiltLibraryInterface) + library := ctx.Module().(*Module).linker.(prebuiltLibraryInterface) // Differentiate between header only and building an actual static/shared library buildStatic := library.buildStatic() @@ -2112,75 +2119,118 @@ func LinkageMutator(mctx android.BottomUpMutatorContext) { // Always create both the static and shared variants for prebuilt libraries, and then disable the one // that is not being used. This allows them to share the name of a cc_library module, which requires that // all the variants of the cc_library also exist on the prebuilt. - modules := mctx.CreateLocalVariations("static", "shared") - static := modules[0].(*Module) - shared := modules[1].(*Module) - - static.linker.(prebuiltLibraryInterface).setStatic() - shared.linker.(prebuiltLibraryInterface).setShared() - - if buildShared { - mctx.AliasVariation("shared") - } else if buildStatic { - mctx.AliasVariation("static") - } - - if !buildStatic { - static.linker.(prebuiltLibraryInterface).disablePrebuilt() - } - if !buildShared { - shared.linker.(prebuiltLibraryInterface).disablePrebuilt() - } + return []string{"static", "shared"} } else { // Header only } - - } else if library, ok := mctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) { + } else if library, ok := ctx.Module().(LinkableInterface); ok && (library.CcLibraryInterface() || library.RustLibraryInterface()) { // Non-cc.Modules may need an empty variant for their mutators. variations := []string{} if library.NonCcVariants() { variations = append(variations, "") } isLLNDK := false - if m, ok := mctx.Module().(*Module); ok { + if m, ok := ctx.Module().(*Module); ok { isLLNDK = m.IsLlndk() } buildStatic := library.BuildStaticVariant() && !isLLNDK buildShared := library.BuildSharedVariant() if buildStatic && buildShared { - variations := append([]string{"static", "shared"}, variations...) - - modules := mctx.CreateLocalVariations(variations...) - static := modules[0].(LinkableInterface) - shared := modules[1].(LinkableInterface) - static.SetStatic() - shared.SetShared() - - if _, ok := library.(*Module); ok { - reuseStaticLibrary(mctx, static.(*Module), shared.(*Module)) - } - mctx.AliasVariation("shared") + variations = append([]string{"static", "shared"}, variations...) + return variations } else if buildStatic { - variations := append([]string{"static"}, variations...) - - modules := mctx.CreateLocalVariations(variations...) - modules[0].(LinkableInterface).SetStatic() - mctx.AliasVariation("static") + variations = append([]string{"static"}, variations...) } else if buildShared { - variations := append([]string{"shared"}, variations...) + variations = append([]string{"shared"}, variations...) + } - modules := mctx.CreateLocalVariations(variations...) - modules[0].(LinkableInterface).SetShared() - mctx.AliasVariation("shared") - } else if len(variations) > 0 { - mctx.CreateLocalVariations(variations...) - mctx.AliasVariation(variations[0]) + if len(variations) > 0 { + return variations + } + } + return []string{""} +} + +func (linkageTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + return "" +} + +func (linkageTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + ccPrebuilt := false + if m, ok := ctx.Module().(*Module); ok && m.linker != nil { + _, ccPrebuilt = m.linker.(prebuiltLibraryInterface) + } + if ccPrebuilt { + if incomingVariation != "" { + return incomingVariation + } + library := ctx.Module().(*Module).linker.(prebuiltLibraryInterface) + if library.buildShared() { + return "shared" + } else if library.buildStatic() { + return "static" + } + return "" + } else if library, ok := ctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() { + isLLNDK := false + if m, ok := ctx.Module().(*Module); ok { + isLLNDK = m.IsLlndk() } - if library.BuildRlibVariant() && library.IsRustFFI() && !buildStatic { + buildStatic := library.BuildStaticVariant() && !isLLNDK + buildShared := library.BuildSharedVariant() + if library.BuildRlibVariant() && library.IsRustFFI() && !buildStatic && (incomingVariation == "static" || incomingVariation == "") { // Rust modules do not build static libs, but rlibs are used as if they // were via `static_libs`. Thus we need to alias the BuildRlibVariant // to "static" for Rust FFI libraries. - mctx.CreateAliasVariation("static", "") + return "" + } + if incomingVariation != "" { + return incomingVariation + } + if buildShared { + return "shared" + } else if buildStatic { + return "static" + } + return "" + } + return "" +} + +func (linkageTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { + ccPrebuilt := false + if m, ok := ctx.Module().(*Module); ok && m.linker != nil { + _, ccPrebuilt = m.linker.(prebuiltLibraryInterface) + } + if ccPrebuilt { + library := ctx.Module().(*Module).linker.(prebuiltLibraryInterface) + if variation == "static" { + library.setStatic() + if !library.buildStatic() { + library.disablePrebuilt() + } + } else if variation == "shared" { + library.setShared() + if !library.buildShared() { + library.disablePrebuilt() + } + } + } else if library, ok := ctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() { + if variation == "static" { + library.SetStatic() + } else if variation == "shared" { + library.SetShared() + var isLLNDK bool + if m, ok := ctx.Module().(*Module); ok { + isLLNDK = m.IsLlndk() + } + buildStatic := library.BuildStaticVariant() && !isLLNDK + buildShared := library.BuildSharedVariant() + if buildStatic && buildShared { + if _, ok := library.(*Module); ok { + reuseStaticLibrary(ctx, library.(*Module)) + } + } } } } @@ -2204,64 +2254,14 @@ func normalizeVersions(ctx android.BaseModuleContext, versions []string) { } } -func createVersionVariations(mctx android.BottomUpMutatorContext, versions []string) { - // "" is for the non-stubs (implementation) variant for system modules, or the LLNDK variant - // for LLNDK modules. - variants := append(android.CopyOf(versions), "") - - m := mctx.Module().(*Module) - isLLNDK := m.IsLlndk() - isVendorPublicLibrary := m.IsVendorPublicLibrary() - isImportedApiLibrary := m.isImportedApiLibrary() - - modules := mctx.CreateLocalVariations(variants...) - for i, m := range modules { - - if variants[i] != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary { - // A stubs or LLNDK stubs variant. - c := m.(*Module) - if c.sanitize != nil { - c.sanitize.Properties.ForceDisable = true - } - if c.stl != nil { - c.stl.Properties.Stl = StringPtr("none") - } - c.Properties.PreventInstall = true - lib := moduleLibraryInterface(m) - isLatest := i == (len(versions) - 1) - lib.setBuildStubs(isLatest) - - if variants[i] != "" { - // A non-LLNDK stubs module is hidden from make and has a dependency from the - // implementation module to the stubs module. - c.Properties.HideFromMake = true - lib.setStubsVersion(variants[i]) - mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i]) - } - } - } - mctx.AliasVariation("") - latestVersion := "" - if len(versions) > 0 { - latestVersion = versions[len(versions)-1] - } - mctx.CreateAliasVariation("latest", latestVersion) -} - -func createPerApiVersionVariations(mctx android.BottomUpMutatorContext, minSdkVersion string) { +func perApiVersionVariations(mctx android.BaseModuleContext, minSdkVersion string) []string { from, err := nativeApiLevelFromUser(mctx, minSdkVersion) if err != nil { mctx.PropertyErrorf("min_sdk_version", err.Error()) - return + return []string{""} } - versionStrs := ndkLibraryVersions(mctx, from) - modules := mctx.CreateLocalVariations(versionStrs...) - - for i, module := range modules { - module.(*Module).Properties.Sdk_version = StringPtr(versionStrs[i]) - module.(*Module).Properties.Min_sdk_version = StringPtr(versionStrs[i]) - } + return ndkLibraryVersions(mctx, from) } func canBeOrLinkAgainstVersionVariants(module interface { @@ -2291,7 +2291,7 @@ func moduleLibraryInterface(module blueprint.Module) libraryInterface { } // setStubsVersions normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions. -func setStubsVersions(mctx android.BottomUpMutatorContext, library libraryInterface, module *Module) { +func setStubsVersions(mctx android.BaseModuleContext, library libraryInterface, module *Module) { if !library.buildShared() || !canBeVersionVariant(module) { return } @@ -2304,25 +2304,98 @@ func setStubsVersions(mctx android.BottomUpMutatorContext, library libraryInterf library.setAllStubsVersions(versions) } -// versionMutator splits a module into the mandatory non-stubs variant +// versionTransitionMutator splits a module into the mandatory non-stubs variant // (which is unnamed) and zero or more stubs variants. -func versionMutator(mctx android.BottomUpMutatorContext) { - if mctx.Os() != android.Android { - return +type versionTransitionMutator struct{} + +func (versionTransitionMutator) Split(ctx android.BaseModuleContext) []string { + if ctx.Os() != android.Android { + return []string{""} } - m, ok := mctx.Module().(*Module) - if library := moduleLibraryInterface(mctx.Module()); library != nil && canBeVersionVariant(m) { - setStubsVersions(mctx, library, m) + m, ok := ctx.Module().(*Module) + if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) { + setStubsVersions(ctx, library, m) - createVersionVariations(mctx, library.allStubsVersions()) + return append(slices.Clone(library.allStubsVersions()), "") + } else if ok && m.SplitPerApiLevel() && m.IsSdkVariant() { + return perApiVersionVariations(ctx, m.MinSdkVersion()) + } + + return []string{""} +} + +func (versionTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + return "" +} + +func (versionTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + if ctx.Os() != android.Android { + return "" + } + m, ok := ctx.Module().(*Module) + if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) { + if incomingVariation == "latest" { + latestVersion := "" + versions := library.allStubsVersions() + if len(versions) > 0 { + latestVersion = versions[len(versions)-1] + } + return latestVersion + } + return incomingVariation + } else if ok && m.SplitPerApiLevel() && m.IsSdkVariant() { + // If this module only has variants with versions and the incoming dependency doesn't specify which one + // is needed then assume the latest version. + if incomingVariation == "" { + return android.FutureApiLevel.String() + } + return incomingVariation + } + + return "" +} + +func (versionTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { + // Optimization: return early if this module can't be affected. + if ctx.Os() != android.Android { return } - if ok { - if m.SplitPerApiLevel() && m.IsSdkVariant() { - createPerApiVersionVariations(mctx, m.MinSdkVersion()) + m, ok := ctx.Module().(*Module) + if library := moduleLibraryInterface(ctx.Module()); library != nil && canBeVersionVariant(m) { + isLLNDK := m.IsLlndk() + isVendorPublicLibrary := m.IsVendorPublicLibrary() + isImportedApiLibrary := m.isImportedApiLibrary() + + if variation != "" || isLLNDK || isVendorPublicLibrary || isImportedApiLibrary { + // A stubs or LLNDK stubs variant. + if m.sanitize != nil { + m.sanitize.Properties.ForceDisable = true + } + if m.stl != nil { + m.stl.Properties.Stl = StringPtr("none") + } + m.Properties.PreventInstall = true + lib := moduleLibraryInterface(m) + allStubsVersions := library.allStubsVersions() + isLatest := len(allStubsVersions) > 0 && variation == allStubsVersions[len(allStubsVersions)-1] + lib.setBuildStubs(isLatest) + } + if variation != "" { + // A non-LLNDK stubs module is hidden from make + library.setStubsVersion(variation) + m.Properties.HideFromMake = true + } else { + // A non-LLNDK implementation module has a dependency to all stubs versions + for _, version := range library.allStubsVersions() { + ctx.AddVariationDependencies([]blueprint.Variation{{"version", version}}, + stubImplDepTag, ctx.ModuleName()) + } } + } else if ok && m.SplitPerApiLevel() && m.IsSdkVariant() { + m.Properties.Sdk_version = StringPtr(variation) + m.Properties.Min_sdk_version = StringPtr(variation) } } diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go index 1f71c1922..e8a98274c 100644 --- a/cc/library_sdk_member.go +++ b/cc/library_sdk_member.go @@ -406,6 +406,9 @@ func addPossiblyArchSpecificProperties(sdkModuleContext android.ModuleContext, b if len(libInfo.StubsVersions) > 0 { stubsSet := outputProperties.AddPropertySet("stubs") stubsSet.AddProperty("versions", libInfo.StubsVersions) + // The symbol file will be copied next to the Android.bp file + stubsSet.AddProperty("symbol_file", libInfo.StubsSymbolFilePath.Base()) + builder.CopyToSnapshot(libInfo.StubsSymbolFilePath, libInfo.StubsSymbolFilePath.Base()) } } @@ -481,6 +484,9 @@ type nativeLibInfoProperties struct { // is written to does not vary by arch so cannot be android specific. StubsVersions []string `sdk:"ignored-on-host"` + // The symbol file containing the APIs exported by this library. + StubsSymbolFilePath android.Path `sdk:"ignored-on-host"` + // Value of SanitizeProperties.Sanitize. Several - but not all - of these // affect the expanded variants. All are propagated to avoid entangling the // sanitizer logic with the snapshot generation. @@ -549,6 +555,11 @@ func (p *nativeLibInfoProperties) PopulateFromVariant(ctx android.SdkMemberConte // the versioned stub libs are retained in the prebuilt tree; currently only // the stub corresponding to ccModule.StubsVersion() is. p.StubsVersions = lib.allStubsVersions() + if lib.buildStubs() && ccModule.stubsSymbolFilePath() == nil { + ctx.ModuleErrorf("Could not determine symbol_file") + } else { + p.StubsSymbolFilePath = ccModule.stubsSymbolFilePath() + } } } p.SystemSharedLibs = specifiedDeps.systemSharedLibs diff --git a/cc/library_stub.go b/cc/library_stub.go index 6f06333ac..636782581 100644 --- a/cc/library_stub.go +++ b/cc/library_stub.go @@ -303,7 +303,7 @@ func (d *apiLibraryDecorator) hasStubsVariants() bool { return d.hasApexStubs() } -func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseMutatorContext) []string { +func (d *apiLibraryDecorator) stubsVersions(ctx android.BaseModuleContext) []string { m, ok := ctx.Module().(*Module) if !ok { diff --git a/cc/llndk_library.go b/cc/llndk_library.go index 85c3edf8b..5ece78a83 100644 --- a/cc/llndk_library.go +++ b/cc/llndk_library.go @@ -30,7 +30,7 @@ var ( type llndkLibraryProperties struct { // Relative path to the symbol map. // An example file can be seen here: TODO(danalbert): Make an example. - Symbol_file *string + Symbol_file *string `android:"path,arch_variant"` // Whether to export any headers as -isystem instead of -I. Mainly for use by // bionic/libc. diff --git a/cc/makevars.go b/cc/makevars.go index cd1396521..c9352a4ad 100644 --- a/cc/makevars.go +++ b/cc/makevars.go @@ -140,7 +140,6 @@ func makeVarsProvider(ctx android.MakeVarsContext) { ctx.Strict("ADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(asanCflags, " ")) ctx.Strict("HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS", strings.Join(hwasanCflags, " ")) - ctx.Strict("HWADDRESS_SANITIZER_GLOBAL_OPTIONS", strings.Join(hwasanGlobalOptions, ",")) ctx.Strict("CFI_EXTRA_CFLAGS", strings.Join(cfiCflags, " ")) ctx.Strict("CFI_EXTRA_ASFLAGS", strings.Join(cfiAsflags, " ")) diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go index 5beeab1ee..8202cc05a 100644 --- a/cc/ndk_abi.go +++ b/cc/ndk_abi.go @@ -46,7 +46,7 @@ func (n *ndkAbiDumpSingleton) GenerateBuildActions(ctx android.SingletonContext) if m, ok := module.(*Module); ok { if installer, ok := m.installer.(*stubDecorator); ok { - if canDumpAbi(ctx.Config()) { + if canDumpAbi(ctx.Config(), ctx.ModuleDir(module)) { depPaths = append(depPaths, installer.abiDumpPath) } } diff --git a/cc/ndk_library.go b/cc/ndk_library.go index f32606814..3e35ef50a 100644 --- a/cc/ndk_library.go +++ b/cc/ndk_library.go @@ -133,7 +133,7 @@ func (stub *stubDecorator) implementationModuleName(name string) string { return strings.TrimSuffix(name, ndkLibrarySuffix) } -func ndkLibraryVersions(ctx android.BaseMutatorContext, from android.ApiLevel) []string { +func ndkLibraryVersions(ctx android.BaseModuleContext, from android.ApiLevel) []string { var versions []android.ApiLevel versionStrs := []string{} for _, version := range ctx.Config().AllSupportedApiLevels() { @@ -147,7 +147,7 @@ func ndkLibraryVersions(ctx android.BaseMutatorContext, from android.ApiLevel) [ return versionStrs } -func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string { +func (this *stubDecorator) stubsVersions(ctx android.BaseModuleContext) []string { if !ctx.Module().Enabled(ctx) { return nil } @@ -321,10 +321,19 @@ func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext, } // Feature flag. -func canDumpAbi(config android.Config) bool { +func canDumpAbi(config android.Config, moduleDir string) bool { if runtime.GOOS == "darwin" { return false } + if strings.HasPrefix(moduleDir, "bionic/") { + // Bionic has enough uncommon implementation details like ifuncs and asm + // code that the ABI tracking here has a ton of false positives. That's + // causing pretty extreme friction for development there, so disabling + // it until the workflow can be improved. + // + // http://b/358653811 + return false + } // http://b/156513478 return config.ReleaseNdkAbiMonitored() } @@ -460,7 +469,7 @@ func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) O nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "") objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc) c.versionScriptPath = nativeAbiResult.versionScript - if canDumpAbi(ctx.Config()) { + if canDumpAbi(ctx.Config(), ctx.ModuleDir()) { c.dumpAbi(ctx, nativeAbiResult.symbolList) if canDiffAbi(ctx.Config()) { c.diffAbi(ctx) diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go deleted file mode 100644 index f503982cd..000000000 --- a/cc/ndk_prebuilt.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2016 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 cc - -import ( - "strings" - - "android/soong/android" -) - -func init() { - android.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory) - android.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory) -} - -// NDK prebuilt libraries. -// -// These differ from regular prebuilts in that they aren't stripped and usually aren't installed -// either (with the exception of the shared STLs, which are installed to the app's directory rather -// than to the system image). - -type ndkPrebuiltStlLinker struct { - *libraryDecorator -} - -func (ndk *ndkPrebuiltStlLinker) linkerProps() []interface{} { - return append(ndk.libraryDecorator.linkerProps(), &ndk.Properties, &ndk.flagExporter.Properties) -} - -func (*ndkPrebuiltStlLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { - // NDK libraries can't have any dependencies - return deps -} - -func (*ndkPrebuiltStlLinker) availableFor(what string) bool { - // ndk prebuilt objects are available to everywhere - return true -} - -// ndk_prebuilt_shared_stl exports a precompiled ndk shared standard template -// library (stl) library for linking operation. The soong's module name format -// is ndk_<NAME>.so where the library is located under -// ./prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/$(HOST_ARCH)/<NAME>.so. -func NdkPrebuiltSharedStlFactory() android.Module { - module, library := NewLibrary(android.DeviceSupported) - library.BuildOnlyShared() - module.compiler = nil - module.linker = &ndkPrebuiltStlLinker{ - libraryDecorator: library, - } - module.installer = nil - module.Properties.Sdk_version = StringPtr("minimum") - module.Properties.AlwaysSdk = true - module.stl.Properties.Stl = StringPtr("none") - return module.Init() -} - -// ndk_prebuilt_static_stl exports a precompiled ndk static standard template -// library (stl) library for linking operation. The soong's module name format -// is ndk_<NAME>.a where the library is located under -// ./prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/$(HOST_ARCH)/<NAME>.a. -func NdkPrebuiltStaticStlFactory() android.Module { - module, library := NewLibrary(android.DeviceSupported) - library.BuildOnlyStatic() - module.compiler = nil - module.linker = &ndkPrebuiltStlLinker{ - libraryDecorator: library, - } - module.installer = nil - module.Properties.Sdk_version = StringPtr("minimum") - module.Properties.HideFromMake = true - module.Properties.AlwaysSdk = true - module.Properties.Sdk_version = StringPtr("current") - module.stl.Properties.Stl = StringPtr("none") - return module.Init() -} - -const ( - libDir = "current/sources/cxx-stl/llvm-libc++/libs" -) - -func getNdkStlLibDir(ctx android.ModuleContext) android.SourcePath { - return android.PathForSource(ctx, ctx.ModuleDir(), libDir).Join(ctx, ctx.Arch().Abi[0]) -} - -func (ndk *ndkPrebuiltStlLinker) link(ctx ModuleContext, flags Flags, - deps PathDeps, objs Objects) android.Path { - // A null build step, but it sets up the output path. - if !strings.HasPrefix(ctx.ModuleName(), "ndk_lib") { - ctx.ModuleErrorf("NDK prebuilt libraries must have an ndk_lib prefixed name") - } - - ndk.libraryDecorator.flagExporter.exportIncludesAsSystem(ctx) - - libName := strings.TrimPrefix(ctx.ModuleName(), "ndk_") - libExt := flags.Toolchain.ShlibSuffix() - if ndk.static() { - libExt = staticLibraryExtension - } - - libDir := getNdkStlLibDir(ctx) - lib := libDir.Join(ctx, libName+libExt) - - ndk.libraryDecorator.flagExporter.setProvider(ctx) - - if ndk.static() { - depSet := android.NewDepSetBuilder[android.Path](android.TOPOLOGICAL).Direct(lib).Build() - android.SetProvider(ctx, StaticLibraryInfoProvider, StaticLibraryInfo{ - StaticLibrary: lib, - - TransitiveStaticLibrariesForOrdering: depSet, - }) - } else { - android.SetProvider(ctx, SharedLibraryInfoProvider, SharedLibraryInfo{ - SharedLibrary: lib, - Target: ctx.Target(), - }) - } - - return lib -} diff --git a/cc/prebuilt.go b/cc/prebuilt.go index e9f790f73..e023a324c 100644 --- a/cc/prebuilt.go +++ b/cc/prebuilt.go @@ -205,17 +205,6 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext, TableOfContents: p.tocFile, }) - // TODO(b/220898484): Mainline module sdk prebuilts of stub libraries use a stub - // library as their source and must not be installed, but other prebuilts like - // libclang_rt.* libraries set `stubs` property because they are LLNDK libraries, - // but use an implementation library as their source and need to be installed. - // This discrepancy should be resolved without the prefix hack below. - isModuleSdkPrebuilts := android.HasAnyPrefix(ctx.ModuleDir(), []string{ - "prebuilts/runtime/mainline/", "prebuilts/module_sdk/"}) - if p.hasStubsVariants() && !p.buildStubs() && !ctx.Host() && isModuleSdkPrebuilts { - ctx.Module().MakeUninstallable() - } - return outputFile } } diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go index 71b7e4369..acbbabc06 100644 --- a/cc/prebuilt_test.go +++ b/cc/prebuilt_test.go @@ -385,112 +385,6 @@ func TestPrebuiltLibrarySanitized(t *testing.T) { assertString(t, static2.OutputFile().Path().Base(), "libf.hwasan.a") } -func TestPrebuiltStubNoinstall(t *testing.T) { - testFunc := func(t *testing.T, expectLibfooOnSystemLib bool, fs android.MockFS) { - result := android.GroupFixturePreparers( - prepareForPrebuiltTest, - android.PrepareForTestWithMakevars, - android.FixtureMergeMockFs(fs), - ).RunTest(t) - - ldRule := result.ModuleForTests("installedlib", "android_arm64_armv8-a_shared").Rule("ld") - android.AssertStringDoesContain(t, "", ldRule.Args["libFlags"], "android_arm64_armv8-a_shared/libfoo.so") - - installRules := result.InstallMakeRulesForTesting(t) - var installedlibRule *android.InstallMakeRule - for i, rule := range installRules { - if rule.Target == "out/target/product/test_device/system/lib/installedlib.so" { - if installedlibRule != nil { - t.Errorf("Duplicate install rules for %s", rule.Target) - } - installedlibRule = &installRules[i] - } - } - if installedlibRule == nil { - t.Errorf("No install rule found for installedlib") - return - } - - if expectLibfooOnSystemLib { - android.AssertStringListContains(t, - "installedlib doesn't have install dependency on libfoo impl", - installedlibRule.OrderOnlyDeps, - "out/target/product/test_device/system/lib/libfoo.so") - } else { - android.AssertStringListDoesNotContain(t, - "installedlib has install dependency on libfoo stub", - installedlibRule.Deps, - "out/target/product/test_device/system/lib/libfoo.so") - android.AssertStringListDoesNotContain(t, - "installedlib has order-only install dependency on libfoo stub", - installedlibRule.OrderOnlyDeps, - "out/target/product/test_device/system/lib/libfoo.so") - } - } - - prebuiltLibfooBp := []byte(` - cc_prebuilt_library { - name: "libfoo", - prefer: true, - srcs: ["libfoo.so"], - stubs: { - versions: ["1"], - }, - } - `) - - installedlibBp := []byte(` - cc_library { - name: "installedlib", - shared_libs: ["libfoo"], - } - `) - - t.Run("prebuilt stub (without source): no install", func(t *testing.T) { - testFunc( - t, - /*expectLibfooOnSystemLib=*/ false, - android.MockFS{ - "prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp, - "Android.bp": installedlibBp, - }, - ) - }) - - disabledSourceLibfooBp := []byte(` - cc_library { - name: "libfoo", - enabled: false, - stubs: { - versions: ["1"], - }, - } - `) - - t.Run("prebuilt stub (with disabled source): no install", func(t *testing.T) { - testFunc( - t, - /*expectLibfooOnSystemLib=*/ false, - android.MockFS{ - "prebuilts/module_sdk/art/current/Android.bp": prebuiltLibfooBp, - "impl/Android.bp": disabledSourceLibfooBp, - "Android.bp": installedlibBp, - }, - ) - }) - - t.Run("prebuilt impl (with `stubs` property set): install", func(t *testing.T) { - testFunc( - t, - /*expectLibfooOnSystemLib=*/ true, - android.MockFS{ - "impl/Android.bp": prebuiltLibfooBp, - "Android.bp": installedlibBp, - }, - ) - }) -} - func TestPrebuiltBinaryNoSrcsNoError(t *testing.T) { const bp = ` cc_prebuilt_binary { @@ -582,11 +476,7 @@ func TestMultiplePrebuilts(t *testing.T) { android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { android.RegisterApexContributionsBuildComponents(ctx) }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), ) ctx := testPrebuilt(t, fmt.Sprintf(bp, tc.selectedDependencyName), map[string][]byte{ "libbar.so": nil, @@ -680,11 +570,7 @@ func TestMultiplePrebuiltsPreferredUsingLegacyFlags(t *testing.T) { android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { android.RegisterApexContributionsBuildComponents(ctx) }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), ) if tc.expectedErr != "" { preparer = preparer.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(tc.expectedErr)) @@ -744,11 +630,7 @@ func TestMissingVariantInModuleSdk(t *testing.T) { android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { android.RegisterApexContributionsBuildComponents(ctx) }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), ) ctx := testPrebuilt(t, bp, map[string][]byte{ "libbar.so": nil, diff --git a/cc/proto.go b/cc/proto.go index 4d72f2665..93142b9fe 100644 --- a/cc/proto.go +++ b/cc/proto.go @@ -19,6 +19,8 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android" + + "strings" ) const ( @@ -35,13 +37,21 @@ func genProto(ctx android.ModuleContext, protoFile android.Path, flags builderFl srcSuffix = ".c" } + srcInfix := "pb" + for _, value := range flags.proto.Flags { + if strings.HasPrefix(value, "--plugin=") && strings.HasSuffix(value, "protoc-gen-grpc-cpp-plugin") { + srcInfix = "grpc.pb" + break + } + } + if flags.proto.CanonicalPathFromRoot { - ccFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb"+srcSuffix) - headerFile = android.GenPathWithExt(ctx, "proto", protoFile, "pb.h") + ccFile = android.GenPathWithExt(ctx, "proto", protoFile, srcInfix+srcSuffix) + headerFile = android.GenPathWithExt(ctx, "proto", protoFile, srcInfix+".h") } else { rel := protoFile.Rel() - ccFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb"+srcSuffix)) - headerFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, "pb.h")) + ccFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, srcInfix+srcSuffix)) + headerFile = android.PathForModuleGen(ctx, "proto", pathtools.ReplaceExtension(rel, srcInfix+".h")) } protoDeps := flags.proto.Deps diff --git a/cc/proto_test.go b/cc/proto_test.go index abcb27304..a905ea889 100644 --- a/cc/proto_test.go +++ b/cc/proto_test.go @@ -68,4 +68,36 @@ func TestProto(t *testing.T) { } }) + t.Run("grpc-cpp-plugin", func(t *testing.T) { + ctx := testCc(t, ` + cc_binary_host { + name: "protoc-gen-grpc-cpp-plugin", + stl: "none", + } + + cc_library_shared { + name: "libgrpc", + srcs: ["a.proto"], + proto: { + plugin: "grpc-cpp-plugin", + }, + }`) + + buildOS := ctx.Config().BuildOS.String() + + proto := ctx.ModuleForTests("libgrpc", "android_arm_armv7-a-neon_shared").Output("proto/a.grpc.pb.cc") + grpcCppPlugin := ctx.ModuleForTests("protoc-gen-grpc-cpp-plugin", buildOS+"_x86_64") + + cmd := proto.RuleParams.Command + if w := "--grpc-cpp-plugin_out="; !strings.Contains(cmd, w) { + t.Errorf("expected %q in %q", w, cmd) + } + + grpcCppPluginPath := grpcCppPlugin.Module().(android.HostToolProvider).HostToolPath().RelativeToTop().String() + + if w := "--plugin=protoc-gen-grpc-cpp-plugin=" + grpcCppPluginPath; !strings.Contains(cmd, w) { + t.Errorf("expected %q in %q", w, cmd) + } + }) + } diff --git a/cc/sanitize.go b/cc/sanitize.go index 64a313bc2..7b0652c38 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -79,8 +79,6 @@ var ( minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined", "-fno-sanitize-recover=integer,undefined"} - hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512", - "export_memory_stats=0", "max_malloc_fill_size=131072", "malloc_fill_byte=0"} memtagStackCommonFlags = []string{"-march=armv8-a+memtag"} memtagStackLlvmFlags = []string{"-dom-tree-reachability-max-bbs-to-explore=128"} @@ -19,73 +19,133 @@ import ( "android/soong/genrule" ) -// sdkMutator sets a creates a platform and an SDK variant for modules +// sdkTransitionMutator creates a platform and an SDK variant for modules // that set sdk_version, and ignores sdk_version for the platform // variant. The SDK variant will be used for embedding in APKs // that may be installed on older platforms. Apexes use their own // variants that enforce backwards compatibility. -func sdkMutator(ctx android.BottomUpMutatorContext) { +type sdkTransitionMutator struct{} + +func (sdkTransitionMutator) Split(ctx android.BaseModuleContext) []string { if ctx.Os() != android.Android { - return + return []string{""} } switch m := ctx.Module().(type) { case LinkableInterface: - ccModule, isCcModule := ctx.Module().(*Module) if m.AlwaysSdk() { if !m.UseSdk() && !m.SplitPerApiLevel() { ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?") } - modules := ctx.CreateVariations("sdk") - modules[0].(*Module).Properties.IsSdkVariant = true + return []string{"sdk"} } else if m.UseSdk() || m.SplitPerApiLevel() { - modules := ctx.CreateVariations("", "sdk") - - // Clear the sdk_version property for the platform (non-SDK) variant so later code - // doesn't get confused by it. - modules[0].(*Module).Properties.Sdk_version = nil - - // Mark the SDK variant. - modules[1].(*Module).Properties.IsSdkVariant = true - - if ctx.Config().UnbundledBuildApps() { - // For an unbundled apps build, hide the platform variant from Make - // so that other Make modules don't link against it, but against the - // SDK variant. - modules[0].(*Module).Properties.HideFromMake = true + return []string{"", "sdk"} + } else { + return []string{""} + } + case *genrule.Module: + if p, ok := m.Extra.(*GenruleExtraProperties); ok { + if String(p.Sdk_version) != "" { + return []string{"", "sdk"} } else { - // For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when - // exposed to Make. - modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true + return []string{""} } - // SDK variant never gets installed because the variant is to be embedded in - // APKs, not to be installed to the platform. - modules[1].(*Module).Properties.PreventInstall = true - ctx.AliasVariation("") + } + case *CcApiVariant: + ccApiVariant, _ := ctx.Module().(*CcApiVariant) + if String(ccApiVariant.properties.Variant) == "ndk" { + return []string{"sdk"} } else { - if isCcModule { - // Clear the sdk_version property for modules that don't have an SDK variant so - // later code doesn't get confused by it. - ccModule.Properties.Sdk_version = nil - } - ctx.CreateVariations("") - ctx.AliasVariation("") + return []string{""} + } + } + + return []string{""} +} + +func (sdkTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + return sourceVariation +} + +func (sdkTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + if ctx.Os() != android.Android { + return "" + } + switch m := ctx.Module().(type) { + case LinkableInterface: + if m.AlwaysSdk() { + return "sdk" + } else if m.UseSdk() || m.SplitPerApiLevel() { + return incomingVariation } case *genrule.Module: if p, ok := m.Extra.(*GenruleExtraProperties); ok { if String(p.Sdk_version) != "" { - ctx.CreateVariations("", "sdk") - } else { - ctx.CreateVariations("") + return incomingVariation } - ctx.AliasVariation("") } case *CcApiVariant: ccApiVariant, _ := ctx.Module().(*CcApiVariant) if String(ccApiVariant.properties.Variant) == "ndk" { - ctx.CreateVariations("sdk") + return "sdk" + } + } + + if ctx.IsAddingDependency() { + return incomingVariation + } else { + return "" + } +} + +func (sdkTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { + if ctx.Os() != android.Android { + return + } + + switch m := ctx.Module().(type) { + case LinkableInterface: + ccModule, isCcModule := ctx.Module().(*Module) + if m.AlwaysSdk() { + if variation != "sdk" { + ctx.ModuleErrorf("tried to create variation %q for module with AlwaysSdk set, expected \"sdk\"", variation) + } + + ccModule.Properties.IsSdkVariant = true + } else if m.UseSdk() || m.SplitPerApiLevel() { + if variation == "" { + // Clear the sdk_version property for the platform (non-SDK) variant so later code + // doesn't get confused by it. + ccModule.Properties.Sdk_version = nil + } else { + // Mark the SDK variant. + ccModule.Properties.IsSdkVariant = true + + // SDK variant never gets installed because the variant is to be embedded in + // APKs, not to be installed to the platform. + ccModule.Properties.PreventInstall = true + } + + if ctx.Config().UnbundledBuildApps() { + if variation == "" { + // For an unbundled apps build, hide the platform variant from Make + // so that other Make modules don't link against it, but against the + // SDK variant. + ccModule.Properties.HideFromMake = true + } + } else { + if variation == "sdk" { + // For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when + // exposed to Make. + ccModule.Properties.SdkAndPlatformVariantVisibleToMake = true + } + } } else { - ctx.CreateVariations("") + if isCcModule { + // Clear the sdk_version property for modules that don't have an SDK variant so + // later code doesn't get confused by it. + ccModule.Properties.Sdk_version = nil + } } } } @@ -177,7 +177,7 @@ func (stl *stl) deps(ctx BaseModuleContext, deps Deps) Deps { } else { deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl, "ndk_libc++abi") } - deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind") + deps.StaticLibs = append(deps.StaticLibs, "libunwind") default: panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl)) } diff --git a/cc/testing.go b/cc/testing.go index 02f992426..ed567af69 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -38,8 +38,6 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) { ctx.RegisterModuleType("cc_cmake_snapshot", CmakeSnapshotFactory) ctx.RegisterModuleType("cc_object", ObjectFactory) ctx.RegisterModuleType("cc_genrule", GenRuleFactory) - ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory) - ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory) ctx.RegisterModuleType("ndk_library", NdkLibraryFactory) ctx.RegisterModuleType("ndk_headers", NdkHeadersFactory) } @@ -312,6 +310,25 @@ func commonDefaultModules() string { ], } cc_library { + name: "ndk_libc++_shared", + export_include_dirs: ["ndk_libc++_shared_include_dirs"], + no_libcrt: true, + nocrt: true, + system_shared_libs: [], + stl: "none", + vendor_available: true, + vendor_ramdisk_available: true, + product_available: true, + recovery_available: true, + host_supported: false, + sdk_version: "minimum", + double_loadable: true, + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + } + cc_library { name: "libc++demangle", no_libcrt: true, nocrt: true, @@ -397,13 +414,6 @@ func commonDefaultModules() string { name: "libprotobuf-cpp-lite", } - cc_library { - name: "ndk_libunwind", - sdk_version: "minimum", - stl: "none", - system_shared_libs: [], - } - ndk_library { name: "libc", first_version: "minimum", @@ -422,11 +432,6 @@ func commonDefaultModules() string { symbol_file: "libdl.map.txt", } - ndk_prebuilt_shared_stl { - name: "ndk_libc++_shared", - export_include_dirs: ["ndk_libc++_shared"], - } - cc_library_static { name: "libgoogle-benchmark", sdk_version: "current", @@ -557,13 +562,6 @@ var PrepareForTestWithCcBuildComponents = android.GroupFixturePreparers( RegisterLlndkLibraryTxtType(ctx) }), - - // Additional files needed in tests that disallow non-existent source files. - // This includes files that are needed by all, or at least most, instances of a cc module type. - android.MockFS{ - // Needed for ndk_prebuilt_(shared|static)_stl. - "defaults/cc/common/current/sources/cxx-stl/llvm-libc++/libs": nil, - }.AddToFixture(), ) // Preparer that will define default cc modules, e.g. standard prebuilt modules. @@ -572,17 +570,17 @@ var PrepareForTestWithCcDefaultModules = android.GroupFixturePreparers( // Additional files needed in tests that disallow non-existent source. android.MockFS{ - "defaults/cc/common/libc.map.txt": nil, - "defaults/cc/common/libdl.map.txt": nil, - "defaults/cc/common/libft2.map.txt": nil, - "defaults/cc/common/libm.map.txt": nil, - "defaults/cc/common/ndk_libc++_shared": nil, - "defaults/cc/common/crtbegin_so.c": nil, - "defaults/cc/common/crtbegin.c": nil, - "defaults/cc/common/crtend_so.c": nil, - "defaults/cc/common/crtend.c": nil, - "defaults/cc/common/crtbrand.c": nil, - "external/compiler-rt/lib/cfi/cfi_blocklist.txt": nil, + "defaults/cc/common/libc.map.txt": nil, + "defaults/cc/common/libdl.map.txt": nil, + "defaults/cc/common/libft2.map.txt": nil, + "defaults/cc/common/libm.map.txt": nil, + "defaults/cc/common/ndk_libc++_shared_include_dirs": nil, + "defaults/cc/common/crtbegin_so.c": nil, + "defaults/cc/common/crtbegin.c": nil, + "defaults/cc/common/crtend_so.c": nil, + "defaults/cc/common/crtend.c": nil, + "defaults/cc/common/crtbrand.c": nil, + "external/compiler-rt/lib/cfi/cfi_blocklist.txt": nil, "defaults/cc/common/libclang_rt.ubsan_minimal.android_arm64.a": nil, "defaults/cc/common/libclang_rt.ubsan_minimal.android_arm.a": nil, diff --git a/cmd/release_config/release_config_lib/flag_declaration.go b/cmd/release_config/release_config_lib/flag_declaration.go index 97d4d4c76..22001bf09 100644 --- a/cmd/release_config/release_config_lib/flag_declaration.go +++ b/cmd/release_config/release_config_lib/flag_declaration.go @@ -18,10 +18,21 @@ import ( rc_proto "android/soong/cmd/release_config/release_config_proto" ) +var ( + // Allowlist: these flags may have duplicate (identical) declarations + // without generating an error. This will be removed once all such + // declarations have been fixed. + DuplicateDeclarationAllowlist = map[string]bool{} +) + func FlagDeclarationFactory(protoPath string) (fd *rc_proto.FlagDeclaration) { fd = &rc_proto.FlagDeclaration{} if protoPath != "" { LoadMessage(protoPath, fd) } + // If the input didn't specify a value, create one (== UnspecifiedValue). + if fd.Value == nil { + fd.Value = &rc_proto.Value{Val: &rc_proto.Value_UnspecifiedValue{false}} + } return fd } diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go index f2e138801..97eb8f156 100644 --- a/cmd/release_config/release_config_lib/release_configs.go +++ b/cmd/release_config/release_config_lib/release_configs.go @@ -42,6 +42,10 @@ type ReleaseConfigMap struct { // Flags declared this directory's flag_declarations/*.textproto FlagDeclarations []rc_proto.FlagDeclaration + + // Potential aconfig and build flag contributions in this map directory. + // This is used to detect errors. + FlagValueDirs map[string][]string } type ReleaseConfigDirMap map[string]int @@ -272,6 +276,20 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex configs.Aliases[name] = alias.Target } var err error + // Temporarily allowlist duplicate flag declaration files to prevent + // more from entering the tree while we work to clean up the duplicates + // that already exist. + dupFlagFile := filepath.Join(dir, "duplicate_allowlist.txt") + data, err := os.ReadFile(dupFlagFile) + if err == nil { + for _, flag := range strings.Split(string(data), "\n") { + flag = strings.TrimSpace(flag) + if strings.HasPrefix(flag, "//") || strings.HasPrefix(flag, "#") { + continue + } + DuplicateDeclarationAllowlist[flag] = true + } + } err = WalkTextprotoFiles(dir, "flag_declarations", func(path string, d fs.DirEntry, err error) error { flagDeclaration := FlagDeclarationFactory(path) // Container must be specified. @@ -285,14 +303,6 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex } } - // TODO: once we have namespaces initialized, we can throw an error here. - if flagDeclaration.Namespace == nil { - flagDeclaration.Namespace = proto.String("android_UNKNOWN") - } - // If the input didn't specify a value, create one (== UnspecifiedValue). - if flagDeclaration.Value == nil { - flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_UnspecifiedValue{false}} - } m.FlagDeclarations = append(m.FlagDeclarations, *flagDeclaration) name := *flagDeclaration.Name if name == "RELEASE_ACONFIG_VALUE_SETS" { @@ -300,8 +310,8 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex } if def, ok := configs.FlagArtifacts[name]; !ok { configs.FlagArtifacts[name] = &FlagArtifact{FlagDeclaration: flagDeclaration, DeclarationIndex: ConfigDirIndex} - } else if !proto.Equal(def.FlagDeclaration, flagDeclaration) { - return fmt.Errorf("Duplicate definition of %s", *flagDeclaration.Name) + } else if !proto.Equal(def.FlagDeclaration, flagDeclaration) || !DuplicateDeclarationAllowlist[name] { + return fmt.Errorf("Duplicate definition of %s in %s", *flagDeclaration.Name, path) } // Set the initial value in the flag artifact. configs.FilesUsedMap[path] = true @@ -317,6 +327,21 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex return err } + subDirs := func(subdir string) (ret []string) { + if flagVersions, err := os.ReadDir(filepath.Join(dir, subdir)); err == nil { + for _, e := range flagVersions { + if e.IsDir() && validReleaseConfigName(e.Name()) { + ret = append(ret, e.Name()) + } + } + } + return + } + m.FlagValueDirs = map[string][]string{ + "aconfig": subDirs("aconfig"), + "flag_values": subDirs("flag_values"), + } + err = WalkTextprotoFiles(dir, "release_configs", func(path string, d fs.DirEntry, err error) error { releaseConfigContribution := &ReleaseConfigContribution{path: path, DeclarationIndex: ConfigDirIndex} LoadMessage(path, &releaseConfigContribution.proto) @@ -424,6 +449,27 @@ func (configs *ReleaseConfigs) GenerateReleaseConfigs(targetRelease string) erro } } + // Look for ignored flagging values. Gather the entire list to make it easier to fix them. + errors := []string{} + for _, contrib := range configs.ReleaseConfigMaps { + dirName := filepath.Dir(contrib.path) + for k, names := range contrib.FlagValueDirs { + for _, rcName := range names { + if config, err := configs.GetReleaseConfig(rcName); err == nil { + rcPath := filepath.Join(dirName, "release_configs", fmt.Sprintf("%s.textproto", config.Name)) + if _, err := os.Stat(rcPath); err != nil { + errors = append(errors, fmt.Sprintf("%s exists but %s does not contribute to %s", + filepath.Join(dirName, k, rcName), dirName, config.Name)) + } + } + + } + } + } + if len(errors) > 0 { + return fmt.Errorf("%s", strings.Join(errors, "\n")) + } + releaseConfig, err := configs.GetReleaseConfig(targetRelease) if err != nil { return err diff --git a/cmd/release_config/release_config_lib/util.go b/cmd/release_config/release_config_lib/util.go index 9919c7081..b149293c2 100644 --- a/cmd/release_config/release_config_lib/util.go +++ b/cmd/release_config/release_config_lib/util.go @@ -31,8 +31,9 @@ import ( ) var ( - disableWarnings bool - containerRegexp, _ = regexp.Compile("^[a-z][a-z0-9]*([._][a-z][a-z0-9]*)*$") + disableWarnings bool + containerRegexp, _ = regexp.Compile("^[a-z][a-z0-9]*([._][a-z][a-z0-9]*)*$") + releaseConfigRegexp, _ = regexp.Compile("^[a-z][a-z0-9]*([._][a-z0-9]*)*$") ) type StringList []string @@ -179,6 +180,10 @@ func validContainer(container string) bool { return containerRegexp.MatchString(container) } +func validReleaseConfigName(name string) bool { + return releaseConfigRegexp.MatchString(name) +} + // Returns the default value for release config artifacts. func GetDefaultOutDir() string { outEnv := os.Getenv("OUT_DIR") diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index 201515f51..56164834a 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -244,6 +244,7 @@ func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig } odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex")) + odexSymbolsPath := odexPath.ReplaceExtension(ctx, "symbols.odex") odexInstallPath := ToOdexPath(module.DexLocation, arch) if odexOnSystemOther(module, global) { odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath) @@ -258,7 +259,8 @@ func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig systemServerClasspathJars := global.AllSystemServerClasspathJars(ctx) rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String())) - rule.Command().FlagWithOutput("rm -f ", odexPath) + rule.Command().FlagWithOutput("rm -f ", odexPath). + FlagWithArg("rm -f ", odexSymbolsPath.String()) if jarIndex := systemServerJars.IndexOfJar(module.Name); jarIndex >= 0 { // System server jars should be dexpreopted together: class loader context of each jar @@ -386,7 +388,9 @@ func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig FlagWithArg("--instruction-set=", arch.String()). FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]). FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]). - Flag("--no-generate-debug-info"). + FlagWithOutput("--oat-symbols=", odexSymbolsPath). + Flag("--generate-debug-info"). + Flag("--strip"). Flag("--generate-build-id"). Flag("--abort-on-hard-verifier-error"). Flag("--force-determinism"). @@ -532,7 +536,7 @@ func OdexOnSystemOtherByName(name string, dexLocation string, global *GlobalConf } for _, f := range global.PatternsOnSystemOther { - if makefileMatch("/" + f, dexLocation) || makefileMatch(filepath.Join(SystemPartition, f), dexLocation) { + if makefileMatch("/"+f, dexLocation) || makefileMatch(filepath.Join(SystemPartition, f), dexLocation) { return true } } diff --git a/docs/resources.md b/docs/resources.md new file mode 100644 index 000000000..c7cb0cf08 --- /dev/null +++ b/docs/resources.md @@ -0,0 +1,89 @@ +## Soong Android Resource Compilation + +The Android build process involves several steps to compile resources into a format that the Android app can use +efficiently in android_library, android_app and android_test modules. See the +[resources documentation](https://developer.android.com/guide/topics/resources/providing-resources) for general +information on resources (with a focus on building with Gradle). + +For all modules, AAPT2 compiles resources provided by directories listed in the resource_dirs directory (which is +implicitly set to `["res"]` if unset, but can be overridden by setting the `resource_dirs` property). + +## android_library with resource processor +For an android_library with resource processor enabled (currently by setting `use_resource_processor: true`, but will be +enabled by default in the future): +- AAPT2 generates the `package-res.apk` file with a resource table that contains all resources from the current +android_library module. `package-res.apk` files from transitive dependencies are passed to AAPT2 with the `-I` flag to +resolve references to resources from dependencies. +- AAPT2 generates an R.txt file that lists all the resources provided by the current android_library module. +- ResourceProcessorBusyBox reads the `R.txt` file for the current android_library and produces an `R.jar` with an +`R.class` in the package listed in the android_library's `AndroidManifest.xml` file that contains java fields for each +resource ID. The resource IDs are non-final, as the final IDs will not be known until the resource table of the final +android_app or android_test module is built. +- The android_library's java and/or kotlin code is compiled with the generated `R.jar` in the classpath, along with the +`R.jar` files from all transitive android_library dependencies. + +## android_app or android_test with resource processor +For an android_app or android_test with resource processor enabled (currently by setting `use_resource_processor: true`, +but will be enabled by default in the future): +- AAPT2 generates the `package-res.apk` file with a resource table that contains all resources from the current +android_app or android_test, as well as all transitive android_library modules referenced via `static_libs`. The +current module is overlaid on dependencies so that resources from the current module replace resources from dependencies +in the case of conflicts. +- AAPT2 generates an R.txt file that lists all the resources provided by the current android_app or android_test, as +well as all transitive android_library modules referenced via `static_libs`. The R.txt file contains the final resource +ID for each resource. +- ResourceProcessorBusyBox reads the `R.txt` file for the current android_app or android_test, as well as all transitive +android_library modules referenced via `static_libs`, and produces an `R.jar` with an `R.class` in the package listed in +the android_app or android_test's `AndroidManifest.xml` file that contains java fields for all local or transitive +resource IDs. In addition, it creates an `R.class` in the package listed in each android_library dependency's +`AndroidManifest.xml` file that contains final resource IDs for the resources that were found in that library. +- The android_app or android_test's java and/or kotlin code is compiled with the current module's `R.jar` in the +classpath, but not the `R.jar` files from transitive android_library dependencies. The `R.jar` file is also merged into +the program classes that are dexed and placed in the final APK. + +## android_app, android_test or android_library without resource processor +For an android_app, android_test or android_library without resource processor enabled (current the default, or +explicitly set with `use_resource_processor: false`): +- AAPT2 generates the `package-res.apk` file with a resource table that contains all resources from the current +android_app, android_test or android_library module, as well as all transitive android_library modules referenced via +`static_libs`. The current module is overlaid on dependencies so that resources from the current module replace +resources from dependencies in the case of conflicts. +- AAPT2 generates an `R.java` file in the package listed in each the current module's `AndroidManifest.xml` file that +contains resource IDs for all resources from the current module as well as all transitive android_library modules +referenced via `static_libs`. The same `R.java` containing all local and transitive resources is also duplicated into +every package listed in an `AndroidManifest.xml` file in any static `android_library` dependency. +- The module's java and/or kotlin code is compiled along with all the generated `R.java` files. + + +## Downsides of legacy resource compilation without resource processor + +Compiling resources without using the resource processor results in a generated R.java source file for every transitive +package that contains every transitive resource. For modules with large transitive dependency trees this can be tens of +thousands of resource IDs duplicated in tens to a hundred java sources. These java sources all have to be compiled in +every successive module in the dependency tree, and then the final R8 step has to drop hundreds of thousands of +unreferenced fields. This results in significant build time and disk usage increases over building with resource +processor. + +## Converting to compilation with resource processor + +### Reference resources using the package name of the module that includes them. +Converting an android_library module to build with resource processor requires fixing any references to resources +provided by android_library dependencies to reference the R classes using the package name found in the +`AndroidManifest.xml` file of the dependency. For example, when referencing an androidx resource: +```java +View.inflate(mContext, R.layout.preference, null)); +``` +must be replaced with: +```java +View.inflate(mContext, androidx.preference.R.layout.preference, null)); +``` + +### Use unique package names for each module in `AndroidManifest.xml` + +Each module will produce an `R.jar` containing an `R.class` in the package specified in it's `AndroidManifest.xml`. +If multiple modules use the same package name they will produce conflicting `R.class` files, which can cause some +resource IDs to appear to be missing. + +If existing code has multiple modules that contribute resources to the same package, one option is to move all the +resources into a single resources-only `android_library` module with no code, and then depend on that from all the other +modules.
\ No newline at end of file diff --git a/elf/Android.bp b/elf/Android.bp index 6450be137..6d3f4f0ed 100644 --- a/elf/Android.bp +++ b/elf/Android.bp @@ -20,6 +20,7 @@ bootstrap_go_package { name: "soong-elf", pkgPath: "android/soong/elf", srcs: [ + "build_id_dir.go", "elf.go", ], testSrcs: [ diff --git a/elf/build_id_dir.go b/elf/build_id_dir.go new file mode 100644 index 000000000..5fb7dda87 --- /dev/null +++ b/elf/build_id_dir.go @@ -0,0 +1,172 @@ +// 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 elf + +import ( + "io/fs" + "os" + "path/filepath" + "strings" + "sync" + "time" +) + +func UpdateBuildIdDir(path string) error { + path = filepath.Clean(path) + buildIdPath := path + "/.build-id" + + // Collect the list of files and build-id symlinks. If the symlinks are + // up to date (newer than the symbol files), there is nothing to do. + var buildIdFiles, symbolFiles []string + var buildIdMtime, symbolsMtime time.Time + filepath.WalkDir(path, func(path string, entry fs.DirEntry, err error) error { + if entry == nil || entry.IsDir() { + return nil + } + info, err := entry.Info() + if err != nil { + return err + } + mtime := info.ModTime() + if strings.HasPrefix(path, buildIdPath) { + if buildIdMtime.Compare(mtime) < 0 { + buildIdMtime = mtime + } + buildIdFiles = append(buildIdFiles, path) + } else { + if symbolsMtime.Compare(mtime) < 0 { + symbolsMtime = mtime + } + symbolFiles = append(symbolFiles, path) + } + return nil + }) + if symbolsMtime.Compare(buildIdMtime) < 0 { + return nil + } + + // Collect build-id -> file mapping from ELF files in the symbols directory. + concurrency := 8 + done := make(chan error) + buildIdToFile := make(map[string]string) + var mu sync.Mutex + for i := 0; i != concurrency; i++ { + go func(paths []string) { + for _, path := range paths { + id, err := Identifier(path, true) + if err != nil { + done <- err + return + } + if id == "" { + continue + } + mu.Lock() + oldPath := buildIdToFile[id] + if oldPath == "" || oldPath > path { + buildIdToFile[id] = path + } + mu.Unlock() + } + done <- nil + }(symbolFiles[len(symbolFiles)*i/concurrency : len(symbolFiles)*(i+1)/concurrency]) + } + + // Collect previously generated build-id -> file mapping from the .build-id directory. + // We will use this for incremental updates. If we see anything in the .build-id + // directory that we did not expect, we'll delete it and start over. + prevBuildIdToFile := make(map[string]string) +out: + for _, buildIdFile := range buildIdFiles { + if !strings.HasSuffix(buildIdFile, ".debug") { + prevBuildIdToFile = nil + break + } + buildId := buildIdFile[len(buildIdPath)+1 : len(buildIdFile)-6] + for i, ch := range buildId { + if i == 2 { + if ch != '/' { + prevBuildIdToFile = nil + break out + } + } else { + if (ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') { + prevBuildIdToFile = nil + break out + } + } + } + target, err := os.Readlink(buildIdFile) + if err != nil || !strings.HasPrefix(target, "../../") { + prevBuildIdToFile = nil + break + } + prevBuildIdToFile[buildId[0:2]+buildId[3:]] = path + target[5:] + } + if prevBuildIdToFile == nil { + err := os.RemoveAll(buildIdPath) + if err != nil { + return err + } + prevBuildIdToFile = make(map[string]string) + } + + // Wait for build-id collection from ELF files to finish. + for i := 0; i != concurrency; i++ { + err := <-done + if err != nil { + return err + } + } + + // Delete old symlinks. + for id, _ := range prevBuildIdToFile { + if buildIdToFile[id] == "" { + symlinkDir := buildIdPath + "/" + id[:2] + symlinkPath := symlinkDir + "/" + id[2:] + ".debug" + if err := os.Remove(symlinkPath); err != nil { + return err + } + } + } + + // Add new symlinks and update changed symlinks. + for id, path := range buildIdToFile { + prevPath := prevBuildIdToFile[id] + if prevPath == path { + continue + } + symlinkDir := buildIdPath + "/" + id[:2] + symlinkPath := symlinkDir + "/" + id[2:] + ".debug" + if prevPath == "" { + if err := os.MkdirAll(symlinkDir, 0755); err != nil { + return err + } + } else { + if err := os.Remove(symlinkPath); err != nil { + return err + } + } + + target, err := filepath.Rel(symlinkDir, path) + if err != nil { + return err + } + if err := os.Symlink(target, symlinkPath); err != nil { + return err + } + } + return nil +} diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go index d04b2d1f1..fc6d1f74e 100644 --- a/etc/prebuilt_etc.go +++ b/etc/prebuilt_etc.go @@ -126,6 +126,15 @@ type prebuiltSubdirProperties struct { Relative_install_path *string `android:"arch_variant"` } +type prebuiltRootProperties struct { + // Install this module to the root directory, without partition subdirs. When this module is + // added to PRODUCT_PACKAGES, this module will be installed to $PRODUCT_OUT/root, which will + // then be copied to the root of system.img. When this module is packaged by other modules like + // android_filesystem, this module will be installed to the root ("/"), unlike normal + // prebuilt_root modules which are installed to the partition subdir (e.g. "/system/"). + Install_in_root *bool +} + type PrebuiltEtcModule interface { android.Module @@ -140,7 +149,12 @@ type PrebuiltEtc struct { android.ModuleBase android.DefaultableModuleBase - properties prebuiltEtcProperties + properties prebuiltEtcProperties + + // rootProperties is used to return the value of the InstallInRoot() method. Currently, only + // prebuilt_avb and prebuilt_root modules use this. + rootProperties prebuiltRootProperties + subdirProperties prebuiltSubdirProperties sourceFilePaths android.Paths @@ -156,9 +170,6 @@ type PrebuiltEtc struct { additionalDependencies *android.Paths usedSrcsProperty bool - // installInRoot is used to return the value of the InstallInRoot() method. The default value is false. - // Currently, only prebuilt_avb can be set to true. - installInRoot bool makeClass string } @@ -246,7 +257,7 @@ func (p *PrebuiltEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) b } func (p *PrebuiltEtc) InstallInRoot() bool { - return p.installInRoot + return proptools.Bool(p.rootProperties.Install_in_root) } func (p *PrebuiltEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool { @@ -502,21 +513,20 @@ func (p *PrebuiltEtc) AndroidModuleBase() *android.ModuleBase { func InitPrebuiltEtcModule(p *PrebuiltEtc, dirBase string) { p.installDirBase = dirBase - p.installInRoot = false p.AddProperties(&p.properties) p.AddProperties(&p.subdirProperties) } func InitPrebuiltRootModule(p *PrebuiltEtc) { p.installDirBase = "." - p.installInRoot = false p.AddProperties(&p.properties) + p.AddProperties(&p.rootProperties) } func InitPrebuiltAvbModule(p *PrebuiltEtc) { p.installDirBase = "avb" - p.installInRoot = true p.AddProperties(&p.properties) + p.rootProperties.Install_in_root = proptools.BoolPtr(true) } // prebuilt_etc is for a prebuilt artifact that is installed in diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index 5add95441..5c7ef434f 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -312,6 +312,25 @@ func (f *filesystem) buildNonDepsFiles(ctx android.ModuleContext, builder *andro } } +func (f *filesystem) copyPackagingSpecs(ctx android.ModuleContext, builder *android.RuleBuilder, specs map[string]android.PackagingSpec, rootDir, rebasedDir android.WritablePath) []string { + rootDirSpecs := make(map[string]android.PackagingSpec) + rebasedDirSpecs := make(map[string]android.PackagingSpec) + + for rel, spec := range specs { + if spec.Partition() == "root" { + rootDirSpecs[rel] = spec + } else { + rebasedDirSpecs[rel] = spec + } + } + + dirsToSpecs := make(map[android.WritablePath]map[string]android.PackagingSpec) + dirsToSpecs[rootDir] = rootDirSpecs + dirsToSpecs[rebasedDir] = rebasedDirSpecs + + return f.CopySpecsToDirs(ctx, builder, dirsToSpecs) +} + func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath { rootDir := android.PathForModuleOut(ctx, "root").OutputPath rebasedDir := rootDir @@ -322,7 +341,7 @@ func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) androi // Wipe the root dir to get rid of leftover files from prior builds builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir) specs := f.gatherFilteredPackagingSpecs(ctx) - f.entries = f.CopySpecsToDir(ctx, builder, specs, rebasedDir) + f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir) f.buildNonDepsFiles(ctx, builder, rootDir) f.addMakeBuiltFiles(ctx, builder, rootDir) @@ -465,7 +484,7 @@ func (f *filesystem) buildCpioImage(ctx android.ModuleContext, compressed bool) // Wipe the root dir to get rid of leftover files from prior builds builder.Command().Textf("rm -rf %s && mkdir -p %s", rootDir, rootDir) specs := f.gatherFilteredPackagingSpecs(ctx) - f.entries = f.CopySpecsToDir(ctx, builder, specs, rebasedDir) + f.entries = f.copyPackagingSpecs(ctx, builder, specs, rootDir, rebasedDir) f.buildNonDepsFiles(ctx, builder, rootDir) f.buildFsverityMetadataFiles(ctx, builder, specs, rootDir, rebasedDir) diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index 2dc8c21e0..8c0d11178 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -16,7 +16,6 @@ package filesystem import ( "os" - "path/filepath" "testing" "android/soong/android" @@ -147,8 +146,8 @@ func TestIncludeMakeBuiltFiles(t *testing.T) { output := result.ModuleForTests("myfilesystem", "android_common").Output("myfilesystem.img") - stampFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp") - fileListFile := filepath.Join(result.Config.OutDir(), "target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt") + stampFile := "out/target/product/test_device/obj/PACKAGING/system_intermediates/staging_dir.stamp" + fileListFile := "out/target/product/test_device/obj/PACKAGING/system_intermediates/file_list.txt" android.AssertStringListContains(t, "deps of filesystem must include the staging dir stamp file", output.Implicits.Strings(), stampFile) android.AssertStringListContains(t, "deps of filesystem must include the staging dir file list", output.Implicits.Strings(), fileListFile) } diff --git a/filesystem/system_image.go b/filesystem/system_image.go index 15cacfb4f..69d922df9 100644 --- a/filesystem/system_image.go +++ b/filesystem/system_image.go @@ -94,9 +94,10 @@ func (s *systemImage) buildLinkerConfigFile(ctx android.ModuleContext, root andr return output } -// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition. -// Note that "apex" module installs its contents to "apex"(fake partition) as well +// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" / "root" +// partition. Note that "apex" module installs its contents to "apex"(fake partition) as well // for symbol lookup by imitating "activated" paths. func (s *systemImage) filterPackagingSpec(ps android.PackagingSpec) bool { - return s.filesystem.filterInstallablePackagingSpec(ps) && ps.Partition() == "system" + return s.filesystem.filterInstallablePackagingSpec(ps) && + (ps.Partition() == "system" || ps.Partition() == "root") } diff --git a/filesystem/vbmeta.go b/filesystem/vbmeta.go index 0c6e7f428..3a9a64daa 100644 --- a/filesystem/vbmeta.go +++ b/filesystem/vbmeta.go @@ -59,7 +59,7 @@ type vbmetaProperties struct { // List of filesystem modules that this vbmeta has descriptors for. The filesystem modules // have to be signed (use_avb: true). - Partitions []string + Partitions proptools.Configurable[[]string] // List of chained partitions that this vbmeta deletages the verification. Chained_partitions []chainedPartitionProperties @@ -110,7 +110,7 @@ type vbmetaDep struct { var vbmetaPartitionDep = vbmetaDep{kind: "partition"} func (v *vbmeta) DepsMutator(ctx android.BottomUpMutatorContext) { - ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions...) + ctx.AddDependency(ctx.Module(), vbmetaPartitionDep, v.properties.Partitions.GetOrDefault(ctx, nil)...) } func (v *vbmeta) installFileName() string { diff --git a/genrule/genrule.go b/genrule/genrule.go index 5b40768b4..25579220d 100644 --- a/genrule/genrule.go +++ b/genrule/genrule.go @@ -139,7 +139,8 @@ type generatorProperties struct { Export_include_dirs []string // list of input files - Srcs []string `android:"path,arch_variant"` + Srcs proptools.Configurable[[]string] `android:"path,arch_variant"` + ResolvedSrcs []string `blueprint:"mutated"` // input files to exclude Exclude_srcs []string `android:"path,arch_variant"` @@ -382,7 +383,8 @@ func (g *Module) generateCommonBuildActions(ctx android.ModuleContext) { } return srcFiles } - srcFiles := addLabelsForInputs("srcs", g.properties.Srcs, g.properties.Exclude_srcs) + g.properties.ResolvedSrcs = g.properties.Srcs.GetOrDefault(ctx, nil) + srcFiles := addLabelsForInputs("srcs", g.properties.ResolvedSrcs, g.properties.Exclude_srcs) android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: srcFiles.Strings()}) var copyFrom android.Paths @@ -589,7 +591,7 @@ func (g *Module) setOutputFiles(ctx android.ModuleContext) { // Collect information for opening IDE project files in java/jdeps.go. func (g *Module) IDEInfo(dpInfo *android.IdeInfo) { dpInfo.Srcs = append(dpInfo.Srcs, g.Srcs().Strings()...) - for _, src := range g.properties.Srcs { + for _, src := range g.properties.ResolvedSrcs { if strings.HasPrefix(src, ":") { src = strings.Trim(src, ":") dpInfo.Deps = append(dpInfo.Deps, src) diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go index fba9aec65..444aedb90 100644 --- a/genrule/genrule_test.go +++ b/genrule/genrule_test.go @@ -694,7 +694,7 @@ func TestGenruleDefaults(t *testing.T) { android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0]) expectedSrcs := []string{"in1"} - android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs) + android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.ResolvedSrcs) } func TestGenruleAllowMissingDependencies(t *testing.T) { diff --git a/java/Android.bp b/java/Android.bp index 54b36ab60..9603815a1 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -87,6 +87,7 @@ bootstrap_go_package { "app_set_test.go", "app_test.go", "code_metadata_test.go", + "container_test.go", "bootclasspath_fragment_test.go", "device_host_converter_test.go", "dex_test.go", @@ -100,6 +101,7 @@ bootstrap_go_package { "hiddenapi_singleton_test.go", "jacoco_test.go", "java_test.go", + "jarjar_test.go", "jdeps_test.go", "kotlin_test.go", "lint_test.go", diff --git a/java/aapt2.go b/java/aapt2.go index f704fc6fc..61cf37381 100644 --- a/java/aapt2.go +++ b/java/aapt2.go @@ -69,7 +69,7 @@ var aapt2CompileRule = pctx.AndroidStaticRule("aapt2Compile", // aapt2Compile compiles resources and puts the results in the requested directory. func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Paths, - flags []string, productToFilter string) android.WritablePaths { + flags []string, productToFilter string, featureFlagsPaths android.Paths) android.WritablePaths { if productToFilter != "" && productToFilter != "default" { // --filter-product leaves only product-specific resources. Product-specific resources only exist // in value resources (values/*.xml), so filter value resource files only. Ignore other types of @@ -85,6 +85,10 @@ func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Pat flags = append([]string{"--filter-product " + productToFilter}, flags...) } + for _, featureFlagsPath := range android.SortedUniquePaths(featureFlagsPaths) { + flags = append(flags, "--feature-flags", "@"+featureFlagsPath.String()) + } + // Shard the input paths so that they can be processed in parallel. If we shard them into too // small chunks, the additional cost of spinning up aapt2 outweighs the performance gain. The // current shard size, 100, seems to be a good balance between the added cost and the gain. @@ -112,6 +116,7 @@ func aapt2Compile(ctx android.ModuleContext, dir android.Path, paths android.Pat ctx.Build(pctx, android.BuildParams{ Rule: aapt2CompileRule, Description: "aapt2 compile " + dir.String() + shardDesc, + Implicits: featureFlagsPaths, Inputs: shard, Outputs: outPaths, Args: map[string]string{ diff --git a/java/aar.go b/java/aar.go index 2f49a959d..c8563c856 100644 --- a/java/aar.go +++ b/java/aar.go @@ -76,7 +76,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 + Resource_dirs []string `android:"path"` // list of zip files containing Android resources. Resource_zips []string `android:"path"` @@ -166,7 +166,11 @@ func propagateRROEnforcementMutator(ctx android.TopDownMutatorContext) { func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool { return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) && // TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries. - !slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") + !slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") && + // Use the legacy resource processor in kythe builds. + // The legacy resource processor creates an R.srcjar, which kythe can use for generating crossrefs. + // TODO(b/354854007): Re-enable BusyBox in kythe builds + !ctx.Config().EmitXrefRules() } func (a *aapt) filterProduct() string { @@ -440,7 +444,8 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio var compiledResDirs []android.Paths for _, dir := range resDirs { a.resourceFiles = append(a.resourceFiles, dir.files...) - compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags, a.filterProduct()).Paths()) + compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, + compileFlags, a.filterProduct(), opts.aconfigTextFiles).Paths()) } for i, zip := range resZips { @@ -499,7 +504,8 @@ func (a *aapt) buildActions(ctx android.ModuleContext, opts aaptBuildActionOptio } for _, dir := range overlayDirs { - compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, compileFlags, a.filterProduct()).Paths()...) + compiledOverlay = append(compiledOverlay, aapt2Compile(ctx, dir.dir, dir.files, + compileFlags, a.filterProduct(), opts.aconfigTextFiles).Paths()...) } var splitPackages android.WritablePaths @@ -989,17 +995,17 @@ type AARImport struct { properties AARImportProperties - headerJarFile android.WritablePath - implementationJarFile android.WritablePath - implementationAndResourcesJarFile android.WritablePath - proguardFlags android.WritablePath - exportPackage android.WritablePath + headerJarFile android.Path + implementationJarFile android.Path + implementationAndResourcesJarFile android.Path + proguardFlags android.Path + exportPackage android.Path transitiveAaptResourcePackagesFile android.Path - extraAaptPackagesFile android.WritablePath + extraAaptPackagesFile android.Path manifest android.Path - assetsPackage android.WritablePath - rTxt android.WritablePath - rJar android.WritablePath + assetsPackage android.Path + rTxt android.Path + rJar android.Path resourcesNodesDepSet *android.DepSet[*resourcesNode] manifestsDepSet *android.DepSet[android.Path] @@ -1164,14 +1170,14 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.manifest = extractedManifest } - a.rTxt = extractedAARDir.Join(ctx, "R.txt") - a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip") - a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt") + rTxt := extractedAARDir.Join(ctx, "R.txt") + assetsPackage := android.PathForModuleOut(ctx, "assets.zip") + proguardFlags := extractedAARDir.Join(ctx, "proguard.txt") transitiveProguardFlags, transitiveUnconditionalExportedFlags := collectDepProguardSpecInfo(ctx) android.SetProvider(ctx, ProguardSpecInfoProvider, ProguardSpecInfo{ ProguardFlagsFiles: android.NewDepSet[android.Path]( android.POSTORDER, - android.Paths{a.proguardFlags}, + android.Paths{proguardFlags}, transitiveProguardFlags, ), UnconditionallyExportedProguardFlags: android.NewDepSet[android.Path]( @@ -1184,15 +1190,19 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.Build(pctx, android.BuildParams{ Rule: unzipAAR, Input: a.aarPath, - Outputs: android.WritablePaths{classpathFile, a.proguardFlags, extractedManifest, a.assetsPackage, a.rTxt}, + Outputs: android.WritablePaths{classpathFile, proguardFlags, extractedManifest, assetsPackage, rTxt}, Description: "unzip AAR", Args: map[string]string{ "outDir": extractedAARDir.String(), "combinedClassesJar": classpathFile.String(), - "assetsPackage": a.assetsPackage.String(), + "assetsPackage": assetsPackage.String(), }, }) + a.proguardFlags = proguardFlags + a.assetsPackage = assetsPackage + a.rTxt = rTxt + // Always set --pseudo-localize, it will be stripped out later for release // builds that don't want it. compileFlags := []string{"--pseudo-localize"} @@ -1200,10 +1210,10 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { flata := compiledResDir.Join(ctx, "gen_res.flata") aapt2CompileZip(ctx, flata, a.aarPath, "res", compileFlags) - a.exportPackage = android.PathForModuleOut(ctx, "package-res.apk") + exportPackage := android.PathForModuleOut(ctx, "package-res.apk") proguardOptionsFile := android.PathForModuleGen(ctx, "proguard.options") aaptRTxt := android.PathForModuleOut(ctx, "R.txt") - a.extraAaptPackagesFile = android.PathForModuleOut(ctx, "extra_packages") + extraAaptPackagesFile := android.PathForModuleOut(ctx, "extra_packages") var linkDeps android.Paths @@ -1239,13 +1249,16 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { } transitiveAssets := android.ReverseSliceInPlace(staticDeps.assets()) - aapt2Link(ctx, a.exportPackage, nil, proguardOptionsFile, aaptRTxt, + aapt2Link(ctx, exportPackage, nil, proguardOptionsFile, aaptRTxt, linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil, nil) + a.exportPackage = exportPackage - a.rJar = android.PathForModuleOut(ctx, "busybox/R.jar") - resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, a.rJar, nil, true, nil, false) + rJar := android.PathForModuleOut(ctx, "busybox/R.jar") + resourceProcessorBusyBoxGenerateBinaryR(ctx, a.rTxt, a.manifest, rJar, nil, true, nil, false) + a.rJar = rJar - aapt2ExtractExtraPackages(ctx, a.extraAaptPackagesFile, a.rJar) + aapt2ExtractExtraPackages(ctx, extraAaptPackagesFile, a.rJar) + a.extraAaptPackagesFile = extraAaptPackagesFile resourcesNodesDepSetBuilder := android.NewDepSetBuilder[*resourcesNode](android.TOPOLOGICAL) resourcesNodesDepSetBuilder.Direct(&resourcesNode{ @@ -1328,13 +1341,14 @@ func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { if len(staticHeaderJars) > 0 { combineJars := append(android.Paths{classpathFile}, staticHeaderJars...) - a.headerJarFile = android.PathForModuleOut(ctx, "turbine-combined", jarName) - TransformJarsToJar(ctx, a.headerJarFile, "combine header jars", combineJars, android.OptionalPath{}, false, nil, nil) + headerJarFile := android.PathForModuleOut(ctx, "turbine-combined", jarName) + TransformJarsToJar(ctx, headerJarFile, "combine header jars", combineJars, android.OptionalPath{}, false, nil, nil) + a.headerJarFile = headerJarFile } else { a.headerJarFile = classpathFile } - android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{ HeaderJars: android.PathsIfNonNil(a.headerJarFile), ResourceJars: android.PathsIfNonNil(resourceJarFile), TransitiveLibsHeaderJars: a.transitiveLibsHeaderJars, @@ -1434,3 +1448,7 @@ func AARImportFactory() android.Module { InitJavaModuleMultiTargets(module, android.DeviceSupported) return module } + +func (a *AARImport) IDEInfo(dpInfo *android.IdeInfo) { + dpInfo.Jars = append(dpInfo.Jars, a.headerJarFile.String(), a.rJar.String()) +} diff --git a/java/app_import.go b/java/app_import.go index fa87997cf..045a89a34 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -431,6 +431,9 @@ func (a *AndroidAppImport) validatePresignedApk(ctx android.ModuleContext, srcAp var extraArgs []string if a.Privileged() { extraArgs = append(extraArgs, "--privileged") + if ctx.Config().UncompressPrivAppDex() { + extraArgs = append(extraArgs, "--uncompress-priv-app-dex") + } } if proptools.Bool(a.properties.Skip_preprocessed_apk_checks) { extraArgs = append(extraArgs, "--skip-preprocessed-apk-checks") diff --git a/java/app_import_test.go b/java/app_import_test.go index 496fc1308..54a5e7518 100644 --- a/java/app_import_test.go +++ b/java/app_import_test.go @@ -777,30 +777,79 @@ func TestAndroidTestImport_Preprocessed(t *testing.T) { } func TestAndroidAppImport_Preprocessed(t *testing.T) { - ctx, _ := testJava(t, ` - android_app_import { - name: "foo", - apk: "prebuilts/apk/app.apk", - presigned: true, - preprocessed: true, - } - `) + for _, dontUncompressPrivAppDexs := range []bool{false, true} { + name := fmt.Sprintf("dontUncompressPrivAppDexs:%t", dontUncompressPrivAppDexs) + t.Run(name, func(t *testing.T) { + result := android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModules, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.UncompressPrivAppDex = proptools.BoolPtr(!dontUncompressPrivAppDexs) + }), + ).RunTestWithBp(t, ` + android_app_import { + name: "foo", + apk: "prebuilts/apk/app.apk", + presigned: true, + preprocessed: true, + } - apkName := "foo.apk" - variant := ctx.ModuleForTests("foo", "android_common") - outputBuildParams := variant.Output(apkName).BuildParams - if outputBuildParams.Rule.String() != android.Cp.String() { - t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String()) - } + android_app_import { + name: "bar", + apk: "prebuilts/apk/app.apk", + presigned: true, + privileged: true, + preprocessed: true, + } + `) + + // non-privileged app + apkName := "foo.apk" + variant := result.ModuleForTests("foo", "android_common") + outputBuildParams := variant.Output(apkName).BuildParams + if outputBuildParams.Rule.String() != android.Cp.String() { + t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String()) + } - // Make sure compression and aligning were validated. - if outputBuildParams.Validation == nil { - t.Errorf("Expected validation rule, but was not found") - } + // Make sure compression and aligning were validated. + if outputBuildParams.Validation == nil { + t.Errorf("Expected validation rule, but was not found") + } + + validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams + if validationBuildParams.Rule.String() != checkPresignedApkRule.String() { + t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String()) + } - validationBuildParams := variant.Output("validated-prebuilt/check.stamp").BuildParams - if validationBuildParams.Rule.String() != checkPresignedApkRule.String() { - t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String()) + expectedScriptArgs := "--preprocessed" + actualScriptArgs := validationBuildParams.Args["extraArgs"] + android.AssertStringEquals(t, "check script extraArgs", expectedScriptArgs, actualScriptArgs) + + // privileged app + apkName = "bar.apk" + variant = result.ModuleForTests("bar", "android_common") + outputBuildParams = variant.Output(apkName).BuildParams + if outputBuildParams.Rule.String() != android.Cp.String() { + t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String()) + } + + // Make sure compression and aligning were validated. + if outputBuildParams.Validation == nil { + t.Errorf("Expected validation rule, but was not found") + } + + validationBuildParams = variant.Output("validated-prebuilt/check.stamp").BuildParams + if validationBuildParams.Rule.String() != checkPresignedApkRule.String() { + t.Errorf("Unexpected validation rule: " + validationBuildParams.Rule.String()) + } + + expectedScriptArgs = "--privileged" + if !dontUncompressPrivAppDexs { + expectedScriptArgs += " --uncompress-priv-app-dex" + } + expectedScriptArgs += " --preprocessed" + actualScriptArgs = validationBuildParams.Args["extraArgs"] + android.AssertStringEquals(t, "check script extraArgs", expectedScriptArgs, actualScriptArgs) + }) } } diff --git a/java/app_set.go b/java/app_set.go index 33d3adec2..7997570aa 100644 --- a/java/app_set.go +++ b/java/app_set.go @@ -35,7 +35,7 @@ func RegisterAppSetBuildComponents(ctx android.RegistrationContext) { type AndroidAppSetProperties struct { // APK Set path - Set *string + Set *string `android:"path"` // Specifies that this app should be installed to the priv-app directory, // where the system will grant it additional privileges not available to diff --git a/java/app_set_test.go b/java/app_set_test.go index 10bc5de92..c02b3593b 100644 --- a/java/app_set_test.go +++ b/java/app_set_test.go @@ -56,7 +56,7 @@ func TestAndroidAppSet(t *testing.T) { mkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, module.Module())[0] actualInstallFile := mkEntries.EntryMap["LOCAL_APK_SET_INSTALL_FILE"] expectedInstallFile := []string{ - strings.Replace(params.ImplicitOutputs[0].String(), android.OutSoongDir, result.Config.SoongOutDir(), 1), + strings.Replace(params.ImplicitOutputs[0].String(), android.TestOutSoongDir, result.Config.SoongOutDir(), 1), } if !reflect.DeepEqual(actualInstallFile, expectedInstallFile) { t.Errorf("Unexpected LOCAL_APK_SET_INSTALL_FILE value: '%s', expected: '%s',", diff --git a/java/app_test.go b/java/app_test.go index e878ccf6d..6b7d522f3 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -4364,7 +4364,16 @@ func TestPrivappAllowlistAndroidMk(t *testing.T) { } func TestAppFlagsPackages(t *testing.T) { - ctx := testApp(t, ` + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + android.FixtureMergeMockFs( + map[string][]byte{ + "res/layout/layout.xml": nil, + "res/values/strings.xml": nil, + "res/values-en-rUS/strings.xml": nil, + }, + ), + ).RunTestWithBp(t, ` android_app { name: "foo", srcs: ["a.java"], @@ -4396,10 +4405,10 @@ func TestAppFlagsPackages(t *testing.T) { // android_app module depends on aconfig_declarations listed in flags_packages android.AssertBoolEquals(t, "foo expected to depend on bar", true, - CheckModuleHasDependency(t, ctx, "foo", "android_common", "bar")) + CheckModuleHasDependency(t, ctx.TestContext, "foo", "android_common", "bar")) android.AssertBoolEquals(t, "foo expected to depend on baz", true, - CheckModuleHasDependency(t, ctx, "foo", "android_common", "baz")) + CheckModuleHasDependency(t, ctx.TestContext, "foo", "android_common", "baz")) aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link") linkInFlags := aapt2LinkRule.Args["inFlags"] @@ -4408,6 +4417,14 @@ func TestAppFlagsPackages(t *testing.T) { linkInFlags, "--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt", ) + + aapt2CompileRule := foo.Rule("android/soong/java.aapt2Compile") + compileFlags := aapt2CompileRule.Args["cFlags"] + android.AssertStringDoesContain(t, + "aapt2 compile command expected to pass feature flags arguments", + compileFlags, + "--feature-flags @out/soong/.intermediates/bar/intermediate.txt --feature-flags @out/soong/.intermediates/baz/intermediate.txt", + ) } func TestAppFlagsPackagesPropagation(t *testing.T) { diff --git a/java/base.go b/java/base.go index fc68d2018..8a4c64d71 100644 --- a/java/base.go +++ b/java/base.go @@ -91,6 +91,10 @@ type CommonProperties struct { // if not blank, run jarjar using the specified rules file Jarjar_rules *string `android:"path,arch_variant"` + // java class names to rename with jarjar when a reverse dependency has a jarjar_prefix + // property. + Jarjar_rename []string + // if not blank, used as prefix to generate repackage rule Jarjar_prefix *string @@ -550,6 +554,22 @@ type Module struct { // java_aconfig_library or java_library modules that are statically linked // to this module. Does not contain cache files from all transitive dependencies. aconfigCacheFiles android.Paths + + // List of soong module dependencies required to compile the current module. + // This information is printed out to `Dependencies` field in module_bp_java_deps.json + compileDepNames []string +} + +var _ android.InstallableModule = (*Module)(nil) + +// To satisfy the InstallableModule interface +func (j *Module) EnforceApiContainerChecks() bool { + return true +} + +// Overrides android.ModuleBase.InstallInProduct() +func (j *Module) InstallInProduct() bool { + return j.ProductSpecific() } func (j *Module) CheckStableSdkVersion(ctx android.BaseModuleContext) error { @@ -1191,6 +1211,7 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath var kotlinJars android.Paths var kotlinHeaderJars android.Paths + var kotlinExtraJars android.Paths // Prepend extraClasspathJars to classpath so that the resource processor R.jar comes before // any dependencies so that it can override any non-final R classes from dependencies with the @@ -1208,14 +1229,17 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath ctx.ModuleErrorf("headers_only is enabled but Turbine is disabled.") } - _, j.headerJarFile, _ = - j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, - extraCombinedJars) + _, combinedHeaderJarFile := j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, + extraCombinedJars) + + combinedHeaderJarFile = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine") + combinedHeaderJarFile = j.repackageFlagsIfNecessary(ctx, combinedHeaderJarFile, jarName, "repackage-turbine") if ctx.Failed() { return } + j.headerJarFile = combinedHeaderJarFile - android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{ HeaderJars: android.PathsIfNonNil(j.headerJarFile), TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, @@ -1291,24 +1315,15 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath return } - kotlinJarPath := j.repackageFlagsIfNecessary(ctx, kotlinJar.OutputPath, jarName, "kotlinc") + kotlinJarPath := j.repackageFlagsIfNecessary(ctx, kotlinJar, jarName, "kotlinc") // Make javac rule depend on the kotlinc rule flags.classpath = append(classpath{kotlinHeaderJar}, flags.classpath...) kotlinJars = append(kotlinJars, kotlinJarPath) kotlinHeaderJars = append(kotlinHeaderJars, kotlinHeaderJar) - - // Jar kotlin classes into the final jar after javac - if BoolDefault(j.properties.Static_kotlin_stdlib, true) { - kotlinJars = append(kotlinJars, deps.kotlinStdlib...) - kotlinJars = append(kotlinJars, deps.kotlinAnnotations...) - kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinStdlib...) - kotlinHeaderJars = append(kotlinHeaderJars, deps.kotlinAnnotations...) - } else { - flags.dexClasspath = append(flags.dexClasspath, deps.kotlinStdlib...) - flags.dexClasspath = append(flags.dexClasspath, deps.kotlinAnnotations...) - } + kotlinExtraJars = append(kotlinExtraJars, deps.kotlinStdlib...) + kotlinExtraJars = append(kotlinExtraJars, deps.kotlinAnnotations...) } jars := slices.Clone(kotlinJars) @@ -1326,12 +1341,17 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath // allow for the use of annotation processors that do function correctly // with sharding enabled. See: b/77284273. } - extraJars := append(slices.Clone(kotlinHeaderJars), extraCombinedJars...) - headerJarFileWithoutDepsOrJarjar, j.headerJarFile, j.repackagedHeaderJarFile = - j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars) - if ctx.Failed() { - return + extraJars := slices.Clone(kotlinHeaderJars) + if BoolDefault(j.properties.Static_kotlin_stdlib, true) { + extraJars = append(extraJars, kotlinExtraJars...) } + extraJars = append(extraJars, extraCombinedJars...) + var combinedHeaderJarFile android.Path + headerJarFileWithoutDepsOrJarjar, combinedHeaderJarFile = + j.compileJavaHeader(ctx, uniqueJavaFiles, srcJars, deps, flags, jarName, extraJars) + + j.headerJarFile = j.jarjarIfNecessary(ctx, combinedHeaderJarFile, jarName, "turbine") + j.repackagedHeaderJarFile = j.repackageFlagsIfNecessary(ctx, j.headerJarFile, jarName, "turbine") } if len(uniqueJavaFiles) > 0 || len(srcJars) > 0 { hasErrorproneableFiles := false @@ -1403,6 +1423,13 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath } } + // Jar kotlin classes into the final jar after javac + if BoolDefault(j.properties.Static_kotlin_stdlib, true) { + jars = append(jars, kotlinExtraJars...) + } else { + flags.dexClasspath = append(flags.dexClasspath, kotlinExtraJars...) + } + jars = append(jars, extraCombinedJars...) j.srcJarArgs, j.srcJarDeps = resourcePathsToJarArgs(srcFiles), srcFiles @@ -1493,7 +1520,7 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath // Combine the classes built from sources, any manifests, and any static libraries into // classes.jar. If there is only one input jar this step will be skipped. - var outputFile android.OutputPath + var outputFile android.Path if len(jars) == 1 && !manifest.Valid() { // Optimization: skip the combine step as there is nothing to do @@ -1509,49 +1536,36 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath // to the copy rules. stub, _ := moduleStubLinkType(ctx.ModuleName()) - // Transform the single path to the jar into an OutputPath as that is required by the following - // code. - if moduleOutPath, ok := jars[0].(android.ModuleOutPath); ok && !stub { - // The path contains an embedded OutputPath so reuse that. - outputFile = moduleOutPath.OutputPath - } else if outputPath, ok := jars[0].(android.OutputPath); ok && !stub { - // The path is an OutputPath so reuse it directly. - outputFile = outputPath - } else { - // The file is not in the out directory so create an OutputPath into which it can be copied - // and which the following code can use to refer to it. + if stub { combinedJar := android.PathForModuleOut(ctx, "combined", jarName) ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, Input: jars[0], Output: combinedJar, }) - outputFile = combinedJar.OutputPath + outputFile = combinedJar + } else { + outputFile = jars[0] } } else { combinedJar := android.PathForModuleOut(ctx, "combined", jarName) TransformJarsToJar(ctx, combinedJar, "for javac", jars, manifest, false, nil, nil) - outputFile = combinedJar.OutputPath + outputFile = combinedJar } // jarjar implementation jar if necessary - if j.expandJarjarRules != nil { - // Transform classes.jar into classes-jarjar.jar - jarjarFile := android.PathForModuleOut(ctx, "jarjar", jarName).OutputPath - TransformJarJar(ctx, jarjarFile, outputFile, j.expandJarjarRules) - outputFile = jarjarFile + jarjarFile := j.jarjarIfNecessary(ctx, outputFile, jarName, "") + outputFile = jarjarFile - // jarjar resource jar if necessary - if j.resourceJar != nil { - resourceJarJarFile := android.PathForModuleOut(ctx, "res-jarjar", jarName) - TransformJarJar(ctx, resourceJarJarFile, j.resourceJar, j.expandJarjarRules) - j.resourceJar = resourceJarJarFile - } + // jarjar resource jar if necessary + if j.resourceJar != nil { + resourceJarJarFile := j.jarjarIfNecessary(ctx, j.resourceJar, jarName, "resource") + j.resourceJar = resourceJarJarFile + } - if ctx.Failed() { - return - } + if ctx.Failed() { + return } // Check package restrictions if necessary. @@ -1563,15 +1577,16 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath // will check that the jar only contains the permitted packages. The new location will become // the output file of this module. inputFile := outputFile - outputFile = android.PathForModuleOut(ctx, "package-check", jarName).OutputPath + packageCheckOutputFile := android.PathForModuleOut(ctx, "package-check", jarName) ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, Input: inputFile, - Output: outputFile, + Output: packageCheckOutputFile, // Make sure that any dependency on the output file will cause ninja to run the package check // rule. Validation: pkgckFile, }) + outputFile = packageCheckOutputFile // Check packages and create a timestamp file when complete. CheckJarPackages(ctx, pkgckFile, outputFile, j.properties.Permitted_packages) @@ -1606,7 +1621,7 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath implementationAndResourcesJar := outputFile if j.resourceJar != nil { jars := android.Paths{j.resourceJar, implementationAndResourcesJar} - combinedJar := android.PathForModuleOut(ctx, "withres", jarName).OutputPath + combinedJar := android.PathForModuleOut(ctx, "withres", jarName) TransformJarsToJar(ctx, combinedJar, "for resources", jars, manifest, false, nil, nil) implementationAndResourcesJar = combinedJar @@ -1633,7 +1648,7 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags")) } // Dex compilation - var dexOutputFile android.OutputPath + var dexOutputFile android.Path params := &compileDexParams{ flags: flags, sdkVersion: j.SdkVersion(ctx), @@ -1660,17 +1675,17 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath // If r8/d8 provides a profile that matches the optimized dex, use that for dexpreopt. if dexArtProfileOutput != nil { - j.dexpreopter.SetRewrittenProfile(*dexArtProfileOutput) + j.dexpreopter.SetRewrittenProfile(dexArtProfileOutput) } // merge dex jar with resources if necessary if j.resourceJar != nil { jars := android.Paths{dexOutputFile, j.resourceJar} - combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName).OutputPath + combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName) TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{}, false, nil, nil) if *j.dexProperties.Uncompress_dex { - combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath + combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName) TransformZipAlign(ctx, combinedAlignedJar, combinedJar, nil) dexOutputFile = combinedAlignedJar } else { @@ -1740,7 +1755,7 @@ func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspath ctx.CheckbuildFile(outputFile) - android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{ HeaderJars: android.PathsIfNonNil(j.headerJarFile), RepackagedHeaderJars: android.PathsIfNonNil(j.repackagedHeaderJarFile), TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, @@ -1830,7 +1845,7 @@ func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags { } func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int, - srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath { + srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.Path { kzipName := pathtools.ReplaceExtension(jarName, "kzip") annoSrcJar := android.PathForModuleOut(ctx, "javac", "anno.srcjar") @@ -1840,7 +1855,7 @@ func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, i jarName += strconv.Itoa(idx) } - classes := android.PathForModuleOut(ctx, "javac", jarName).OutputPath + classes := android.PathForModuleOut(ctx, "javac", jarName) TransformJavaToClasses(ctx, classes, idx, srcFiles, srcJars, annoSrcJar, flags, extraJarDeps) if ctx.Config().EmitXrefRules() && ctx.Module() == ctx.PrimaryModule() { @@ -1883,16 +1898,13 @@ func CheckKotlincFlags(ctx android.ModuleContext, flags []string) { func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths, deps deps, flags javaBuilderFlags, jarName string, - extraJars android.Paths) (headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar android.Path) { + extraJars android.Paths) (headerJar android.Path, combinedHeaderJar android.Path) { var jars android.Paths if len(srcFiles) > 0 || len(srcJars) > 0 { // Compile java sources into turbine.jar. turbineJar := android.PathForModuleOut(ctx, "turbine", jarName) TransformJavaToHeaderClasses(ctx, turbineJar, srcFiles, srcJars, flags) - if ctx.Failed() { - return nil, nil, nil - } jars = append(jars, turbineJar) headerJar = turbineJar } @@ -1905,40 +1917,18 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars // we cannot skip the combine step for now if there is only one jar // since we have to strip META-INF/TRANSITIVE dir from turbine.jar - combinedJar := android.PathForModuleOut(ctx, "turbine-combined", jarName) - TransformJarsToJar(ctx, combinedJar, "for turbine", jars, android.OptionalPath{}, + combinedHeaderJarOutputPath := android.PathForModuleOut(ctx, "turbine-combined", jarName) + TransformJarsToJar(ctx, combinedHeaderJarOutputPath, "for turbine", jars, android.OptionalPath{}, false, nil, []string{"META-INF/TRANSITIVE"}) - jarjarAndDepsHeaderJar = combinedJar - - if j.expandJarjarRules != nil { - // Transform classes.jar into classes-jarjar.jar - jarjarFile := android.PathForModuleOut(ctx, "turbine-jarjar", jarName) - TransformJarJar(ctx, jarjarFile, jarjarAndDepsHeaderJar, j.expandJarjarRules) - jarjarAndDepsHeaderJar = jarjarFile - if ctx.Failed() { - return nil, nil, nil - } - } - if j.repackageJarjarRules != nil { - repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-turbine-jarjar", jarName) - TransformJarJar(ctx, repackagedJarjarFile, jarjarAndDepsHeaderJar, j.repackageJarjarRules) - jarjarAndDepsRepackagedHeaderJar = repackagedJarjarFile - if ctx.Failed() { - return nil, nil, nil - } - } else { - jarjarAndDepsRepackagedHeaderJar = jarjarAndDepsHeaderJar - } - - return headerJar, jarjarAndDepsHeaderJar, jarjarAndDepsRepackagedHeaderJar + return headerJar, combinedHeaderJarOutputPath } func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags, - classesJar android.Path, jarName string, specs string) android.OutputPath { + classesJar android.Path, jarName string, specs string) android.Path { jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName) - instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath + instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName) jacocoInstrumentJar(ctx, instrumentedJar, jacocoReportClassesFile, classesJar, specs) @@ -1973,23 +1963,24 @@ func (j *providesTransitiveHeaderJars) collectTransitiveHeaderJars(ctx android.M return } - dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider) - tag := ctx.OtherModuleDependencyTag(module) - _, isUsesLibDep := tag.(usesLibraryDependencyTag) - if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep { - directLibs = append(directLibs, dep.HeaderJars...) - } else if tag == staticLibTag { - directStaticLibs = append(directStaticLibs, dep.HeaderJars...) - } else { - // Don't propagate transitive libs for other kinds of dependencies. - return - } + if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { + tag := ctx.OtherModuleDependencyTag(module) + _, isUsesLibDep := tag.(usesLibraryDependencyTag) + if tag == libTag || tag == r8LibraryJarTag || isUsesLibDep { + directLibs = append(directLibs, dep.HeaderJars...) + } else if tag == staticLibTag { + directStaticLibs = append(directStaticLibs, dep.HeaderJars...) + } else { + // Don't propagate transitive libs for other kinds of dependencies. + return + } - if dep.TransitiveLibsHeaderJars != nil { - transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars) - } - if dep.TransitiveStaticLibsHeaderJars != nil { - transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars) + if dep.TransitiveLibsHeaderJars != nil { + transitiveLibs = append(transitiveLibs, dep.TransitiveLibsHeaderJars) + } + if dep.TransitiveStaticLibsHeaderJars != nil { + transitiveStaticLibs = append(transitiveStaticLibs, dep.TransitiveStaticLibsHeaderJars) + } } }) j.transitiveLibsHeaderJars = android.NewDepSet(android.POSTORDER, directLibs, transitiveLibs) @@ -2036,23 +2027,24 @@ func (j *Module) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { // Collect information for opening IDE project files in java/jdeps.go. func (j *Module) IDEInfo(dpInfo *android.IdeInfo) { - dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...) - dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...) - dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...) - dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...) + // jarjar rules will repackage the sources. To prevent misleading results, IdeInfo should contain the + // repackaged jar instead of the input sources. if j.expandJarjarRules != nil { dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String()) + dpInfo.Jars = append(dpInfo.Jars, j.headerJarFile.String()) + } else { + dpInfo.Srcs = append(dpInfo.Srcs, j.expandIDEInfoCompiledSrcs...) + dpInfo.SrcJars = append(dpInfo.SrcJars, j.compiledSrcJars.Strings()...) + dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...) } + dpInfo.Deps = append(dpInfo.Deps, j.CompilerDeps()...) + dpInfo.Aidl_include_dirs = append(dpInfo.Aidl_include_dirs, j.deviceProperties.Aidl.Include_dirs...) dpInfo.Static_libs = append(dpInfo.Static_libs, j.properties.Static_libs...) dpInfo.Libs = append(dpInfo.Libs, j.properties.Libs...) - dpInfo.SrcJars = append(dpInfo.SrcJars, j.annoSrcJars.Strings()...) } func (j *Module) CompilerDeps() []string { - jdeps := []string{} - jdeps = append(jdeps, j.properties.Libs...) - jdeps = append(jdeps, j.properties.Static_libs...) - return jdeps + return j.compileDepNames } func (j *Module) hasCode(ctx android.ModuleContext) bool { @@ -2098,9 +2090,10 @@ func (j *Module) collectTransitiveSrcFiles(ctx android.ModuleContext, mine andro ctx.VisitDirectDeps(func(module android.Module) { tag := ctx.OtherModuleDependencyTag(module) if tag == staticLibTag { - depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider) - if depInfo.TransitiveSrcFiles != nil { - fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles) + if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { + if depInfo.TransitiveSrcFiles != nil { + fromDeps = append(fromDeps, depInfo.TransitiveSrcFiles) + } } } }) @@ -2240,6 +2233,11 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { deps.classpath = append(deps.classpath, sdkDep.jars...) deps.dexClasspath = append(deps.dexClasspath, sdkDep.jars...) deps.aidlPreprocess = sdkDep.aidl + // Add the sdk module dependency to `compileDepNames`. + // This ensures that the dependency is reported in `module_bp_java_deps.json` + // TODO (b/358608607): Move this to decodeSdkDep + sdkSpec := android.SdkContext(j).SdkVersion(ctx) + j.compileDepNames = append(j.compileDepNames, fmt.Sprintf("sdk_%s_%s_android", sdkSpec.Kind.String(), sdkSpec.ApiLevel.String())) } else { deps.aidlPreprocess = sdkDep.aidl } @@ -2380,22 +2378,35 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case bootClasspathTag: // If a system modules dependency has been added to the bootclasspath // then add its libs to the bootclasspath. - sm := module.(SystemModulesProvider) - deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...) + if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok { + depHeaderJars := sm.HeaderJars + deps.bootClasspath = append(deps.bootClasspath, depHeaderJars...) + } else { + ctx.PropertyErrorf("boot classpath dependency %q does not provide SystemModulesProvider", + ctx.OtherModuleName(module)) + } case systemModulesTag: if deps.systemModules != nil { panic("Found two system module dependencies") } - sm := module.(SystemModulesProvider) - outputDir, outputDeps := sm.OutputDirAndDeps() - deps.systemModules = &systemModules{outputDir, outputDeps} + if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok { + deps.systemModules = &systemModules{sm.OutputDir, sm.OutputDirDeps} + } else { + ctx.PropertyErrorf("system modules dependency %q does not provide SystemModulesProvider", + ctx.OtherModuleName(module)) + } case instrumentationForTag: ctx.PropertyErrorf("instrumentation_for", "dependency %q of type %q does not provide JavaInfo so is unsuitable for use with this property", ctx.OtherModuleName(module), ctx.OtherModuleType(module)) } } + if android.InList(tag, compileDependencyTags) { + // Add the dependency name to compileDepNames so that it can be recorded in module_bp_java_deps.json + j.compileDepNames = append(j.compileDepNames, otherName) + } + addCLCFromDep(ctx, module, j.classLoaderContexts) addMissingOptionalUsesLibsFromDep(ctx, module, &j.usesLibrary) }) @@ -2637,8 +2648,7 @@ func (module *Module) collectJarJarRules(ctx android.ModuleContext) *JarJarProvi // Gather repackage information from deps result := collectDirectDepsProviders(ctx) - // Update that with entries we've stored for ourself - for orig, renamed := range module.jarjarRenameRules { + add := func(orig string, renamed string) { if result == nil { result = &JarJarProviderData{ Rename: make(map[string]string), @@ -2647,12 +2657,22 @@ func (module *Module) collectJarJarRules(ctx android.ModuleContext) *JarJarProvi if renamed != "" { if preexisting, exists := (*result).Rename[orig]; exists && preexisting != renamed { ctx.ModuleErrorf("Conflicting jarjar rules inherited for class: %s (%s and %s)", orig, renamed, preexisting) - continue + return } } (*result).Rename[orig] = renamed } + // Update that with entries we've stored for ourself + for orig, renamed := range module.jarjarRenameRules { + add(orig, renamed) + } + + // Update that with entries given in the jarjar_rename property. + for _, orig := range module.properties.Jarjar_rename { + add(orig, "") + } + // If there are no renamings, then jarjar_prefix does nothing, so skip the extra work. if result == nil { return nil @@ -2702,15 +2722,25 @@ func getJarJarRuleText(provider *JarJarProviderData) string { } // Repackage the flags if the jarjar rule txt for the flags is generated -func (j *Module) repackageFlagsIfNecessary(ctx android.ModuleContext, infile android.WritablePath, jarName, info string) android.WritablePath { +func (j *Module) repackageFlagsIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string) android.Path { if j.repackageJarjarRules == nil { return infile } - repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", info+jarName) + repackagedJarjarFile := android.PathForModuleOut(ctx, "repackaged-jarjar", info, jarName) TransformJarJar(ctx, repackagedJarjarFile, infile, j.repackageJarjarRules) return repackagedJarjarFile } +func (j *Module) jarjarIfNecessary(ctx android.ModuleContext, infile android.Path, jarName, info string) android.Path { + if j.expandJarjarRules == nil { + return infile + } + jarjarFile := android.PathForModuleOut(ctx, "jarjar", info, jarName) + TransformJarJar(ctx, jarjarFile, infile, j.expandJarjarRules) + return jarjarFile + +} + func addPlugins(deps *deps, pluginJars android.Paths, pluginClasses ...string) { deps.processorPath = append(deps.processorPath, pluginJars...) deps.processorClasses = append(deps.processorClasses, pluginClasses...) diff --git a/java/bootclasspath.go b/java/bootclasspath.go index 77ddf5c05..029f6f623 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -196,7 +196,7 @@ var platformBootclasspathDepTag = bootclasspathDependencyTag{name: "platform"} type BootclasspathNestedAPIProperties struct { // java_library or preferably, java_sdk_library modules providing stub classes that define the // APIs provided by this bootclasspath_fragment. - Stub_libs []string + Stub_libs proptools.Configurable[[]string] } // BootclasspathAPIProperties defines properties for defining the API provided by parts of the @@ -229,11 +229,11 @@ type BootclasspathAPIProperties struct { // apiScopeToStubLibs calculates the stub library modules for each relevant *HiddenAPIScope from the // Stub_libs properties. -func (p BootclasspathAPIProperties) apiScopeToStubLibs() map[*HiddenAPIScope][]string { +func (p BootclasspathAPIProperties) apiScopeToStubLibs(ctx android.BaseModuleContext) map[*HiddenAPIScope][]string { m := map[*HiddenAPIScope][]string{} for _, apiScope := range hiddenAPISdkLibrarySupportedScopes { - m[apiScope] = p.Api.Stub_libs + m[apiScope] = p.Api.Stub_libs.GetOrDefault(ctx, nil) } - m[CorePlatformHiddenAPIScope] = p.Core_platform_api.Stub_libs + m[CorePlatformHiddenAPIScope] = p.Core_platform_api.Stub_libs.GetOrDefault(ctx, nil) return m } diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 16209b72e..bce507a7d 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -445,7 +445,7 @@ func (b *BootclasspathFragmentModule) ComponentDepsMutator(ctx android.BottomUpM func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorContext) { // Add dependencies onto all the modules that provide the API stubs for classes on this // bootclasspath fragment. - hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs()) + hiddenAPIAddStubLibDependencies(ctx, b.properties.apiScopeToStubLibs(ctx)) for _, additionalStubModule := range b.properties.Additional_stubs { for _, apiScope := range hiddenAPISdkLibrarySupportedScopes { @@ -933,8 +933,8 @@ func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx andro b.Filtered_flags_path = android.OptionalPathForPath(hiddenAPIInfo.FilteredFlagsPath) // Copy stub_libs properties. - b.Stub_libs = module.properties.Api.Stub_libs - b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs + b.Stub_libs = module.properties.Api.Stub_libs.GetOrDefault(mctx, nil) + b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs.GetOrDefault(mctx, nil) // Copy fragment properties. b.Fragments = module.properties.Fragments diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go index 8bc0a7ef6..d72417afa 100644 --- a/java/bootclasspath_fragment_test.go +++ b/java/bootclasspath_fragment_test.go @@ -222,11 +222,7 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"), FixtureConfigureApexBootJars("someapex:mysdklibrary"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` bootclasspath_fragment { name: "myfragment", diff --git a/java/config/config.go b/java/config/config.go index 2bb50f62a..66e857c64 100644 --- a/java/config/config.go +++ b/java/config/config.go @@ -47,6 +47,7 @@ var ( "services", "android.car", "android.car7", + "android.car.builtin", "conscrypt", "core-icu4j", "core-oj", diff --git a/java/container_test.go b/java/container_test.go new file mode 100644 index 000000000..344185553 --- /dev/null +++ b/java/container_test.go @@ -0,0 +1,129 @@ +// 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 java + +import ( + "android/soong/android" + "fmt" + "testing" +) + +var checkContainerMatch = func(t *testing.T, name string, container string, expected bool, actual bool) { + errorMessage := fmt.Sprintf("module %s container %s value differ", name, container) + android.AssertBoolEquals(t, errorMessage, expected, actual) +} + +func TestJavaContainersModuleProperties(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + java_library { + name: "foo", + srcs: ["A.java"], + } + java_library { + name: "foo_vendor", + srcs: ["A.java"], + vendor: true, + sdk_version: "current", + } + java_library { + name: "foo_soc_specific", + srcs: ["A.java"], + soc_specific: true, + sdk_version: "current", + } + java_library { + name: "foo_product_specific", + srcs: ["A.java"], + product_specific: true, + sdk_version: "current", + } + java_test { + name: "foo_cts_test", + srcs: ["A.java"], + test_suites: [ + "cts", + ], + } + java_test { + name: "foo_non_cts_test", + srcs: ["A.java"], + test_suites: [ + "general-tests", + ], + } + `) + + testcases := []struct { + moduleName string + isSystemContainer bool + isVendorContainer bool + isProductContainer bool + isCts bool + }{ + { + moduleName: "foo", + isSystemContainer: true, + isVendorContainer: false, + isProductContainer: false, + isCts: false, + }, + { + moduleName: "foo_vendor", + isSystemContainer: false, + isVendorContainer: true, + isProductContainer: false, + isCts: false, + }, + { + moduleName: "foo_soc_specific", + isSystemContainer: false, + isVendorContainer: true, + isProductContainer: false, + isCts: false, + }, + { + moduleName: "foo_product_specific", + isSystemContainer: false, + isVendorContainer: false, + isProductContainer: true, + isCts: false, + }, + { + moduleName: "foo_cts_test", + isSystemContainer: false, + isVendorContainer: false, + isProductContainer: false, + isCts: true, + }, + { + moduleName: "foo_non_cts_test", + isSystemContainer: false, + isVendorContainer: false, + isProductContainer: false, + isCts: false, + }, + } + + for _, c := range testcases { + m := result.ModuleForTests(c.moduleName, "android_common") + containers, _ := android.OtherModuleProvider(result.TestContext.OtherModuleProviderAdaptor(), m.Module(), android.ContainersInfoProvider) + belongingContainers := containers.BelongingContainers() + checkContainerMatch(t, c.moduleName, "system", c.isSystemContainer, android.InList(android.SystemContainer, belongingContainers)) + checkContainerMatch(t, c.moduleName, "vendor", c.isVendorContainer, android.InList(android.VendorContainer, belongingContainers)) + checkContainerMatch(t, c.moduleName, "product", c.isProductContainer, android.InList(android.ProductContainer, belongingContainers)) + } +} diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp index ab72e8b6d..cee7a192e 100644 --- a/java/core-libraries/Android.bp +++ b/java/core-libraries/Android.bp @@ -41,7 +41,7 @@ java_defaults { } java_library { - name: "core.current.stubs.from-source", + name: "core.current.stubs", defaults: [ "core.current.stubs.defaults", ], @@ -52,8 +52,12 @@ java_library { ], } +// Used for bootstrapping ART system modules java_api_library { name: "core.current.stubs.from-text", + defaults: [ + "core.current.stubs.defaults", + ], api_surface: "core", api_contributions: [ "art.module.public.api.stubs.source.api.contribution", @@ -68,27 +72,7 @@ java_api_library { } java_library { - name: "core.current.stubs", - defaults: [ - "core.current.stubs.defaults", - ], - static_libs: [ - "core.current.stubs.from-source", - ], - product_variables: { - build_from_text_stub: { - static_libs: [ - "core.current.stubs.from-text", - ], - exclude_static_libs: [ - "core.current.stubs.from-source", - ], - }, - }, -} - -java_library { - name: "core.current.stubs.exportable.from-source", + name: "core.current.stubs.exportable", defaults: [ "core.current.stubs.defaults", ], @@ -103,16 +87,6 @@ java_library { }, } -java_library { - name: "core.current.stubs.exportable", - defaults: [ - "core.current.stubs.defaults", - ], - static_libs: [ - "core.current.stubs.exportable.from-source", - ], -} - // Distributed with the SDK for turning into system modules to compile apps // against. // @@ -201,26 +175,6 @@ java_library { "core.module_lib.stubs.defaults", ], static_libs: [ - "core.module_lib.stubs.from-source", - ], - product_variables: { - build_from_text_stub: { - static_libs: [ - "core.module_lib.stubs.from-text", - ], - exclude_static_libs: [ - "core.module_lib.stubs.from-source", - ], - }, - }, -} - -java_library { - name: "core.module_lib.stubs.from-source", - defaults: [ - "core.module_lib.stubs.defaults", - ], - static_libs: [ "art.module.public.api.stubs.module_lib", // Replace the following with the module-lib correspondence when Conscrypt or i18N module @@ -231,27 +185,6 @@ java_library { ], } -java_api_library { - name: "core.module_lib.stubs.from-text", - api_surface: "module-lib", - api_contributions: [ - "art.module.public.api.stubs.source.api.contribution", - "art.module.public.api.stubs.source.system.api.contribution", - "art.module.public.api.stubs.source.module_lib.api.contribution", - - // Add the module-lib correspondence when Conscrypt or i18N module - // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides - // @SystemApi(MODULE_LIBRARIES). - "conscrypt.module.public.api.stubs.source.api.contribution", - "i18n.module.public.api.stubs.source.api.contribution", - ], - libs: [ - "stub-annotations", - ], - visibility: ["//visibility:private"], - stubs_type: "everything", -} - // Produces a dist file that is used by the // prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk // directory. @@ -311,7 +244,7 @@ core_platform_visibility = ["//visibility:public"] // API annotations are available to the dex tools that enable enforcement of runtime // accessibility. b/119068555 java_library { - name: "legacy.core.platform.api.stubs.from-source", + name: "legacy.core.platform.api.stubs", visibility: core_platform_visibility, defaults: [ "core.platform.api.stubs.defaults", @@ -324,7 +257,7 @@ java_library { } java_library { - name: "legacy.core.platform.api.stubs.exportable.from-source", + name: "legacy.core.platform.api.stubs.exportable", visibility: core_platform_visibility, defaults: [ "core.platform.api.stubs.defaults", @@ -348,53 +281,6 @@ java_defaults { ], } -java_api_library { - name: "legacy.core.platform.api.stubs.from-text", - api_surface: "core_platform", - defaults: [ - "android_core_platform_stubs_current_contributions", - ], - api_contributions: [ - "legacy.i18n.module.platform.api.stubs.source.api.contribution", - ], - libs: [ - "stub-annotations", - ], - stubs_type: "everything", -} - -java_library { - name: "legacy.core.platform.api.stubs", - visibility: core_platform_visibility, - defaults: [ - "core.platform.api.stubs.defaults", - ], - static_libs: [ - "legacy.core.platform.api.stubs.from-source", - ], - product_variables: { - build_from_text_stub: { - static_libs: [ - "legacy.core.platform.api.stubs.from-text", - ], - exclude_static_libs: [ - "legacy.core.platform.api.stubs.from-source", - ], - }, - }, -} - -java_library { - name: "legacy.core.platform.api.stubs.exportable", - visibility: core_platform_visibility, - defaults: [ - "core.platform.api.stubs.defaults", - ], - static_libs: [ - "legacy.core.platform.api.stubs.exportable.from-source", - ], -} - java_defaults { name: "core.platform.api.stubs.defaults", hostdex: true, @@ -424,7 +310,7 @@ java_library { } java_library { - name: "stable.core.platform.api.stubs.from-source", + name: "stable.core.platform.api.stubs", visibility: core_platform_visibility, defaults: [ "core.platform.api.stubs.defaults", @@ -437,42 +323,6 @@ java_library { ], } -java_api_library { - name: "stable.core.platform.api.stubs.from-text", - api_surface: "core_platform", - defaults: [ - "android_core_platform_stubs_current_contributions", - ], - api_contributions: [ - "stable.i18n.module.platform.api.stubs.source.api.contribution", - ], - libs: [ - "stub-annotations", - ], - stubs_type: "everything", -} - -java_library { - name: "stable.core.platform.api.stubs", - visibility: core_platform_visibility, - defaults: [ - "core.platform.api.stubs.defaults", - ], - static_libs: [ - "stable.core.platform.api.stubs.from-source", - ], - product_variables: { - build_from_text_stub: { - static_libs: [ - "stable.core.platform.api.stubs.from-text", - ], - exclude_static_libs: [ - "stable.core.platform.api.stubs.from-source", - ], - }, - }, -} - // Same as stable.core.platform.api.stubs, but android annotations are // stripped. This is used by the Java toolchain, while the annotated stub is to // be used by Kotlin one. diff --git a/java/device_host_converter.go b/java/device_host_converter.go index 3f8735c0c..63b69d0a8 100644 --- a/java/device_host_converter.go +++ b/java/device_host_converter.go @@ -130,7 +130,7 @@ func (d *DeviceHostConverter) GenerateAndroidBuildActions(ctx android.ModuleCont d.combinedHeaderJar = d.headerJars[0] } - android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{ HeaderJars: d.headerJars, ImplementationAndResourcesJars: d.implementationAndResourceJars, ImplementationJars: d.implementationJars, @@ -188,3 +188,11 @@ func (d *DeviceHostConverter) AndroidMk() android.AndroidMkData { }, } } + +// implement the following interface for IDE completion. +var _ android.IDEInfo = (*DeviceHostConverter)(nil) + +func (d *DeviceHostConverter) IDEInfo(ideInfo *android.IdeInfo) { + ideInfo.Deps = append(ideInfo.Deps, d.properties.Libs...) + ideInfo.Libs = append(ideInfo.Libs, d.properties.Libs...) +} diff --git a/java/dex.go b/java/dex.go index 7bb69257c..d88e8f86f 100644 --- a/java/dex.go +++ b/java/dex.go @@ -291,8 +291,9 @@ func (d *dexer) r8Flags(ctx android.ModuleContext, dexParams *compileDexParams) // See b/20667396 var proguardRaiseDeps classpath ctx.VisitDirectDepsWithTag(proguardRaiseTag, func(m android.Module) { - dep, _ := android.OtherModuleProvider(ctx, m, JavaInfoProvider) - proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...) + if dep, ok := android.OtherModuleProvider(ctx, m, JavaInfoProvider); ok { + proguardRaiseDeps = append(proguardRaiseDeps, dep.RepackagedHeaderJars...) + } }) r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars")) @@ -428,7 +429,7 @@ func (d *dexer) addArtProfile(ctx android.ModuleContext, dexParams *compileDexPa } // Return the compiled dex jar and (optional) profile _after_ r8 optimization -func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.OutputPath, *android.OutputPath) { +func (d *dexer) compileDex(ctx android.ModuleContext, dexParams *compileDexParams) (android.Path, android.Path) { // Compile classes.jar into classes.dex and then javalib.jar javalibJar := android.PathForModuleOut(ctx, "dex", dexParams.jarName).OutputPath diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 794924401..a38642a50 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -359,7 +359,7 @@ func (d *Dexpreopter) DexpreoptPrebuiltApexSystemServerJars(ctx android.ModuleCo d.dexpreopt(ctx, libraryName, dexJarFile) } -func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.WritablePath) { +func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, libName string, dexJarFile android.Path) { global := dexpreopt.GetGlobalConfig(ctx) // TODO(b/148690468): The check on d.installPath is to bail out in cases where @@ -616,10 +616,8 @@ func getModuleInstallPathInfo(ctx android.ModuleContext, fullInstallPath string) return installPath, relDir, installBase } -// RuleBuilder.Install() adds output-to-install copy pairs to a list for Make. To share this -// information with PackagingSpec in soong, call PackageFile for them. -// The install path and the target install partition of the module must be the same. -func packageFile(ctx android.ModuleContext, install android.RuleBuilderInstall) { +// installFile will install the file if `install` path and the target install partition are the same. +func installFile(ctx android.ModuleContext, install android.RuleBuilderInstall) { installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To) // Empty name means the install partition is not for the target image. // For the system image, files for "apex" and "system_other" are skipped here. @@ -628,7 +626,7 @@ func packageFile(ctx android.ModuleContext, install android.RuleBuilderInstall) // TODO(b/320196894): Files for "system_other" are skipped because soong creates the system // image only for now. if name != "" { - ctx.PackageFile(installPath.Join(ctx, relDir), name, install.From) + ctx.InstallFile(installPath.Join(ctx, relDir), name, install.From) } } diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index defa82c0c..56e007bbf 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -491,6 +491,11 @@ type dexpreoptBootJars struct { // Build path to a config file that Soong writes for Make (to be used in makefiles that install // the default boot image). dexpreoptConfigForMake android.WritablePath + + // Build path to the boot framework profile. + // This is used as the `OutputFile` in `AndroidMkEntries`. + // A non-nil value ensures that this singleton module does not get skipped in AndroidMkEntries processing. + bootFrameworkProfile android.WritablePath } func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) { @@ -603,7 +608,8 @@ func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContex installs := generateBootImage(ctx, config) profileInstalls = append(profileInstalls, installs...) if config == d.defaultBootImage { - _, installs := bootFrameworkProfileRule(ctx, config) + bootProfile, installs := bootFrameworkProfileRule(ctx, config) + d.bootFrameworkProfile = bootProfile profileInstalls = append(profileInstalls, installs...) } } @@ -613,7 +619,7 @@ func (d *dexpreoptBootJars) GenerateAndroidBuildActions(ctx android.ModuleContex profileLicenseMetadataFile: android.OptionalPathForPath(ctx.LicenseMetadataFile()), }) for _, install := range profileInstalls { - packageFile(ctx, install) + installFile(ctx, install) } } } @@ -939,24 +945,21 @@ func packageFileForTargetImage(ctx android.ModuleContext, image *bootImageVarian } for _, install := range image.installs { - packageFile(ctx, install) + installFile(ctx, install) } for _, install := range image.vdexInstalls { - if image.target.Arch.ArchType.Name != ctx.DeviceConfig().DeviceArch() { - // Note that the vdex files are identical between architectures. If the target image is - // not for the primary architecture create symlinks to share the vdex of the primary - // architecture with the other architectures. - // - // Assuming that the install path has the architecture name with it, replace the - // architecture name with the primary architecture name to find the source vdex file. - installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To) - if name != "" { - srcRelDir := strings.Replace(relDir, image.target.Arch.ArchType.Name, ctx.DeviceConfig().DeviceArch(), 1) - ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, srcRelDir, name)) - } - } else { - packageFile(ctx, install) + installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To) + if name == "" { + continue + } + // Note that the vdex files are identical between architectures. Copy the vdex to a no arch directory + // and create symlinks for both the primary and secondary arches. + ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, "framework", name)) + if image.target.Arch.ArchType.Name == ctx.DeviceConfig().DeviceArch() { + // Copy the vdex from the primary arch to the no-arch directory + // e.g. /system/framework/$bootjar.vdex + ctx.InstallFile(installPath.Join(ctx, "framework"), name, install.From) } } } @@ -1234,7 +1237,7 @@ func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) (an profile := bootImageProfileRuleCommon(ctx, image.name, image.dexPathsDeps.Paths(), image.getAnyAndroidVariant().dexLocationsDeps) - if image == defaultBootImageConfig(ctx) { + if image == defaultBootImageConfig(ctx) && profile != nil { rule := android.NewRuleBuilder(pctx, ctx) rule.Install(profile, "/system/etc/boot-image.prof") return profile, rule.Installs() @@ -1380,3 +1383,12 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(getImageNames(), " ")) } } + +// Add one of the outputs in `OutputFile` +// This ensures that this singleton module does not get skipped when writing out/soong/Android-*.mk +func (d *dexpreoptBootJars) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{{ + Class: "ETC", + OutputFile: android.OptionalPathForPath(d.bootFrameworkProfile), + }} +} diff --git a/java/droiddoc.go b/java/droiddoc.go index 730f23696..f81c5bac5 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -365,10 +365,10 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { case bootClasspathTag: if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars...) - } else if sm, ok := module.(SystemModulesProvider); ok { + } else if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok { // A system modules dependency has been added to the bootclasspath // so add its libs to the bootclasspath. - deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...) + deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars...) } else { panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName())) } @@ -396,9 +396,12 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { if deps.systemModules != nil { panic("Found two system module dependencies") } - sm := module.(SystemModulesProvider) - outputDir, outputDeps := sm.OutputDirAndDeps() - deps.systemModules = &systemModules{outputDir, outputDeps} + if sm, ok := android.OtherModuleProvider(ctx, module, SystemModulesProvider); ok { + deps.systemModules = &systemModules{sm.OutputDir, sm.OutputDirDeps} + } else { + ctx.PropertyErrorf("boot classpath dependency %q does not provide SystemModulesProvider", + ctx.OtherModuleName(module)) + } case aconfigDeclarationTag: if dep, ok := android.OtherModuleProvider(ctx, module, android.AconfigDeclarationsProviderKey); ok { deps.aconfigProtoFiles = append(deps.aconfigProtoFiles, dep.IntermediateCacheOutputPath) diff --git a/java/droidstubs.go b/java/droidstubs.go index a8e0a22e5..d6229038f 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -197,6 +197,10 @@ type DroidstubsProperties struct { // a list of aconfig_declarations module names that the stubs generated in this module // depend on. Aconfig_declarations []string + + // List of hard coded filegroups containing Metalava config files that are passed to every + // Metalava invocation that this module performs. See addMetalavaConfigFilesToCmd. + ConfigFiles []string `android:"path" blueprint:"mutated"` } // Used by xsd_config @@ -259,6 +263,7 @@ func DroidstubsFactory() android.Module { module.AddProperties(&module.properties, &module.Javadoc.properties) + module.properties.ConfigFiles = getMetalavaConfigFilegroupReference() module.initModuleAndImport(module) InitDroiddocModule(module, android.HostAndDeviceSupported) @@ -279,6 +284,7 @@ func DroidstubsHostFactory() android.Module { module.AddProperties(&module.properties, &module.Javadoc.properties) + module.properties.ConfigFiles = getMetalavaConfigFilegroupReference() InitDroiddocModule(module, android.HostSupported) return module } @@ -694,7 +700,7 @@ func metalavaUseRbe(ctx android.ModuleContext) bool { } func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths, - srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams) *android.RuleBuilderCommand { + srcJarList android.Path, homeDir android.WritablePath, params stubsCommandConfigParams, configFiles android.Paths) *android.RuleBuilderCommand { rule.Command().Text("rm -rf").Flag(homeDir.String()) rule.Command().Text("mkdir -p").Flag(homeDir.String()) @@ -738,9 +744,26 @@ func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs andr cmd.Flag(config.MetalavaFlags) + addMetalavaConfigFilesToCmd(cmd, configFiles) + return cmd } +// MetalavaConfigFilegroup is the name of the filegroup in build/soong/java/metalava that lists +// the configuration files to pass to Metalava. +const MetalavaConfigFilegroup = "metalava-config-files" + +// Get a reference to the MetalavaConfigFilegroup suitable for use in a property. +func getMetalavaConfigFilegroupReference() []string { + return []string{":" + MetalavaConfigFilegroup} +} + +// addMetalavaConfigFilesToCmd adds --config-file options to use the config files list in the +// MetalavaConfigFilegroup filegroup. +func addMetalavaConfigFilesToCmd(cmd *android.RuleBuilderCommand, configFiles android.Paths) { + cmd.FlagForEachInput("--config-file ", configFiles) +} + // Pass flagged apis related flags to metalava. When aconfig_declarations property is not // defined for a module, simply revert all flagged apis annotations. If aconfig_declarations // property is defined, apply transformations and only revert the flagged apis that are not @@ -812,7 +835,10 @@ func (d *Droidstubs) commonMetalavaStubCmd(ctx android.ModuleContext, rule *andr srcJarList := zipSyncCmd(ctx, rule, params.srcJarDir, d.Javadoc.srcJars) homeDir := android.PathForModuleOut(ctx, params.stubConfig.stubsType.String(), "home") - cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig) + + configFiles := android.PathsForModuleSrc(ctx, d.properties.ConfigFiles) + + cmd := metalavaCmd(ctx, rule, d.Javadoc.srcFiles, srcJarList, homeDir, params.stubConfig, configFiles) cmd.Implicits(d.Javadoc.implicits) d.stubsFlags(ctx, cmd, params.stubsDir, params.stubConfig.stubsType, params.stubConfig.checkApi) diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go index 6a14f3645..1e8362cf2 100644 --- a/java/droidstubs_test.go +++ b/java/droidstubs_test.go @@ -421,11 +421,9 @@ func TestReleaseExportRuntimeApis(t *testing.T) { result := android.GroupFixturePreparers( prepareForJavaTest, android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } variables.ExportRuntimeApis = proptools.BoolPtr(true) }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), android.FixtureMergeMockFs(map[string][]byte{ "a/A.java": nil, "a/current.txt": nil, diff --git a/java/hiddenapi.go b/java/hiddenapi.go index 5441a3b6e..b1a9debe1 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -98,8 +98,9 @@ func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar OptionalDexJ // processing. classesJars := android.Paths{classesJar} ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) { - javaInfo, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) - classesJars = append(classesJars, javaInfo.ImplementationJars...) + if javaInfo, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { + classesJars = append(classesJars, javaInfo.ImplementationJars...) + } }) h.classesJarPaths = classesJars @@ -151,7 +152,7 @@ func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Modul // // Otherwise, it creates a copy of the supplied dex file into which it has encoded the hiddenapi // flags and returns this instead of the supplied dex jar. -func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.OutputPath) android.OutputPath { +func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.Path) android.Path { if !h.active { return dexJar diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index 62297978c..afe8b4c8e 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -203,10 +203,8 @@ func TestHiddenAPISingletonSdks(t *testing.T) { FixtureConfigureBootJars("platform:foo"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { variables.Always_use_prebuilt_sdks = proptools.BoolPtr(tc.unbundledBuild) - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` java_library { name: "foo", diff --git a/java/jarjar_test.go b/java/jarjar_test.go new file mode 100644 index 000000000..82bfa2b86 --- /dev/null +++ b/java/jarjar_test.go @@ -0,0 +1,85 @@ +// Copyright 2018 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 java + +import ( + "fmt" + "testing" + + "android/soong/android" +) + +func AssertJarJarRename(t *testing.T, result *android.TestResult, libName, original, expectedRename string) { + module := result.ModuleForTests(libName, "android_common") + + provider, found := android.OtherModuleProvider(result.OtherModuleProviderAdaptor(), module.Module(), JarJarProvider) + android.AssertBoolEquals(t, fmt.Sprintf("found provider (%s)", libName), true, found) + + renamed, found := provider.Rename[original] + android.AssertBoolEquals(t, fmt.Sprintf("found rename (%s)", libName), true, found) + android.AssertStringEquals(t, fmt.Sprintf("renamed (%s)", libName), expectedRename, renamed) +} + +func TestJarJarRenameDifferentModules(t *testing.T) { + t.Parallel() + result := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + java_library { + name: "their_lib", + jarjar_rename: ["com.example.a"], + } + + java_library { + name: "boundary_lib", + jarjar_prefix: "RENAME", + static_libs: ["their_lib"], + } + + java_library { + name: "my_lib", + static_libs: ["boundary_lib"], + } + `) + + original := "com.example.a" + renamed := "RENAME.com.example.a" + AssertJarJarRename(t, result, "their_lib", original, "") + AssertJarJarRename(t, result, "boundary_lib", original, renamed) + AssertJarJarRename(t, result, "my_lib", original, renamed) +} + +func TestJarJarRenameSameModule(t *testing.T) { + t.Parallel() + result := android.GroupFixturePreparers( + prepareForJavaTest, + ).RunTestWithBp(t, ` + java_library { + name: "their_lib", + jarjar_rename: ["com.example.a"], + jarjar_prefix: "RENAME", + } + + java_library { + name: "my_lib", + static_libs: ["their_lib"], + } + `) + + original := "com.example.a" + renamed := "RENAME.com.example.a" + AssertJarJarRename(t, result, "their_lib", original, renamed) + AssertJarJarRename(t, result, "my_lib", original, renamed) +} diff --git a/java/java.go b/java/java.go index 88b31b586..126d8f3a4 100644 --- a/java/java.go +++ b/java/java.go @@ -269,7 +269,7 @@ type JavaInfo struct { ImplementationAndResourcesJars android.Paths // ImplementationJars is a list of jars that contain the implementations of classes in the - //module. + // module. ImplementationJars android.Paths // ResourceJars is a list of jars that contain the resources included in the module. @@ -315,14 +315,14 @@ type JavaInfo struct { AconfigIntermediateCacheOutputPaths android.Paths } -var JavaInfoProvider = blueprint.NewProvider[JavaInfo]() +var JavaInfoProvider = blueprint.NewProvider[*JavaInfo]() // SyspropPublicStubInfo contains info about the sysprop public stub library that corresponds to // the sysprop implementation library. type SyspropPublicStubInfo struct { // JavaInfo is the JavaInfoProvider of the sysprop public stub library that corresponds to // the sysprop implementation library. - JavaInfo JavaInfo + JavaInfo *JavaInfo } var SyspropPublicStubInfoProvider = blueprint.NewProvider[SyspropPublicStubInfo]() @@ -432,7 +432,6 @@ var ( r8LibraryJarTag = dependencyTag{name: "r8-libraryjar", runtimeLinked: true} syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"} javaApiContributionTag = dependencyTag{name: "java-api-contribution"} - depApiSrcsTag = dependencyTag{name: "dep-api-srcs"} aconfigDeclarationTag = dependencyTag{name: "aconfig-declaration"} jniInstallTag = dependencyTag{name: "jni install", runtimeLinked: true, installable: true} binaryInstallTag = dependencyTag{name: "binary install", runtimeLinked: true, installable: true} @@ -443,6 +442,30 @@ var ( usesLibCompat30OptTag = makeUsesLibraryDependencyTag(30, true) ) +// A list of tags for deps used for compiling a module. +// Any dependency tags that modifies the following properties of `deps` in `Module.collectDeps` should be +// added to this list: +// - bootClasspath +// - classpath +// - java9Classpath +// - systemModules +// - kotlin deps... +var ( + compileDependencyTags = []blueprint.DependencyTag{ + sdkLibTag, + libTag, + staticLibTag, + bootClasspathTag, + systemModulesTag, + java9LibTag, + kotlinStdlibTag, + kotlinAnnotationsTag, + kotlinPluginTag, + syspropPublicStubDepTag, + instrumentationForTag, + } +) + func IsLibDepTag(depTag blueprint.DependencyTag) bool { return depTag == libTag || depTag == sdkLibTag } @@ -1981,12 +2004,6 @@ type JavaApiLibraryProperties struct { // merge zipped after metalava invocation Static_libs []string - // Java Api library to provide the full API surface stub jar file. - // If this property is set, the stub jar of this module is created by - // extracting the compiled class files provided by the - // full_api_surface_stub module. - Full_api_surface_stub *string - // Version of previously released API file for compatibility check. Previous_api *string `android:"path"` @@ -2015,12 +2032,26 @@ type JavaApiLibraryProperties struct { // List of aconfig_declarations module names that the stubs generated in this module // depend on. Aconfig_declarations []string + + // List of hard coded filegroups containing Metalava config files that are passed to every + // Metalava invocation that this module performs. See addMetalavaConfigFilesToCmd. + ConfigFiles []string `android:"path" blueprint:"mutated"` + + // If not blank, set to the version of the sdk to compile against. + // Defaults to an empty string, which compiles the module against the private platform APIs. + // Values are of one of the following forms: + // 1) numerical API level, "current", "none", or "core_platform" + // 2) An SDK kind with an API level: "<sdk kind>_<API level>" + // See build/soong/android/sdk_version.go for the complete and up to date list of SDK kinds. + // If the SDK kind is empty, it will be set to public. + Sdk_version *string } func ApiLibraryFactory() android.Module { module := &ApiLibrary{} - android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) module.AddProperties(&module.properties) + module.properties.ConfigFiles = getMetalavaConfigFilegroupReference() + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon) module.initModuleAndImport(module) android.InitDefaultableModule(module) return module @@ -2036,7 +2067,7 @@ func (al *ApiLibrary) StubsJar() android.Path { func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, srcs android.Paths, homeDir android.WritablePath, - classpath android.Paths) *android.RuleBuilderCommand { + classpath android.Paths, configFiles android.Paths) *android.RuleBuilderCommand { rule.Command().Text("rm -rf").Flag(homeDir.String()) rule.Command().Text("mkdir -p").Flag(homeDir.String()) @@ -2075,6 +2106,8 @@ func metalavaStubCmd(ctx android.ModuleContext, rule *android.RuleBuilder, FlagWithArg("--hide ", "InvalidNullabilityOverride"). FlagWithArg("--hide ", "ChangedDefault") + addMetalavaConfigFilesToCmd(cmd, configFiles) + if len(classpath) == 0 { // The main purpose of the `--api-class-resolution api` option is to force metalava to ignore // classes on the classpath when an API file contains missing classes. However, as this command @@ -2110,40 +2143,6 @@ func (al *ApiLibrary) addValidation(ctx android.ModuleContext, cmd *android.Rule } } -// This method extracts the stub class files from the stub jar file provided -// from full_api_surface_stub module instead of compiling the srcjar generated from invoking metalava. -// This method is used because metalava can generate compilable from-text stubs only when -// the codebase encompasses all classes listed in the input API text file, and a class can extend -// a class that is not within the same API domain. -func (al *ApiLibrary) extractApiSrcs(ctx android.ModuleContext, rule *android.RuleBuilder, stubsDir android.OptionalPath, fullApiSurfaceStubJar android.Path) { - classFilesList := android.PathForModuleOut(ctx, "metalava", "classes.txt") - unzippedSrcJarDir := android.PathForModuleOut(ctx, "metalava", "unzipDir") - - rule.Command(). - BuiltTool("list_files"). - Text(stubsDir.String()). - FlagWithOutput("--out ", classFilesList). - FlagWithArg("--extensions ", ".java"). - FlagWithArg("--root ", unzippedSrcJarDir.String()). - Flag("--classes") - - rule.Command(). - Text("unzip"). - Flag("-q"). - Input(fullApiSurfaceStubJar). - FlagWithArg("-d ", unzippedSrcJarDir.String()) - - rule.Command(). - BuiltTool("soong_zip"). - Flag("-jar"). - Flag("-write_if_changed"). - Flag("-ignore_missing_files"). - Flag("-quiet"). - FlagWithArg("-C ", unzippedSrcJarDir.String()). - FlagWithInput("-l ", classFilesList). - FlagWithOutput("-o ", al.stubsJarWithoutStaticLibs) -} - func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { apiContributions := al.properties.Api_contributions addValidations := !ctx.Config().IsEnvTrue("DISABLE_STUB_VALIDATION") && @@ -2170,14 +2169,18 @@ func (al *ApiLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { } } } + if ctx.Device() { + sdkDep := decodeSdkDep(ctx, android.SdkContext(al)) + if sdkDep.useModule { + ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules) + ctx.AddVariationDependencies(nil, libTag, sdkDep.classpath...) + ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...) + + } + } ctx.AddVariationDependencies(nil, libTag, al.properties.Libs...) ctx.AddVariationDependencies(nil, staticLibTag, al.properties.Static_libs...) - if al.properties.Full_api_surface_stub != nil { - ctx.AddVariationDependencies(nil, depApiSrcsTag, String(al.properties.Full_api_surface_stub)) - } - if al.properties.System_modules != nil { - ctx.AddVariationDependencies(nil, systemModulesTag, String(al.properties.System_modules)) - } + for _, aconfigDeclarationsName := range al.properties.Aconfig_declarations { ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfigDeclarationsName) } @@ -2233,8 +2236,8 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { var srcFilesInfo []JavaApiImportInfo var classPaths android.Paths + var bootclassPaths android.Paths var staticLibs android.Paths - var depApiSrcsStubsJar android.Path var systemModulesPaths android.Paths ctx.VisitDirectDeps(func(dep android.Module) { tag := ctx.OtherModuleDependencyTag(dep) @@ -2246,17 +2249,21 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { } srcFilesInfo = append(srcFilesInfo, provider) case libTag: - provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) - classPaths = append(classPaths, provider.HeaderJars...) + if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { + classPaths = append(classPaths, provider.HeaderJars...) + } + case bootClasspathTag: + if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { + bootclassPaths = append(bootclassPaths, provider.HeaderJars...) + } case staticLibTag: - provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) - staticLibs = append(staticLibs, provider.HeaderJars...) - case depApiSrcsTag: - provider, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) - depApiSrcsStubsJar = provider.HeaderJars[0] + if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { + staticLibs = append(staticLibs, provider.HeaderJars...) + } case systemModulesTag: - module := dep.(SystemModulesProvider) - systemModulesPaths = append(systemModulesPaths, module.HeaderJars()...) + if sm, ok := android.OtherModuleProvider(ctx, dep, SystemModulesProvider); ok { + systemModulesPaths = append(systemModulesPaths, sm.HeaderJars...) + } case metalavaCurrentApiTimestampTag: if currentApiTimestampProvider, ok := dep.(currentApiTimestampProvider); ok { al.validationPaths = append(al.validationPaths, currentApiTimestampProvider.CurrentApiTimestamp()) @@ -2286,7 +2293,12 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName()) } - cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, systemModulesPaths) + configFiles := android.PathsForModuleSrc(ctx, al.properties.ConfigFiles) + + combinedPaths := append(([]android.Path)(nil), systemModulesPaths...) + combinedPaths = append(combinedPaths, classPaths...) + combinedPaths = append(combinedPaths, bootclassPaths...) + cmd := metalavaStubCmd(ctx, rule, srcFiles, homeDir, combinedPaths, configFiles) al.stubsFlags(ctx, cmd, stubsDir) @@ -2304,9 +2316,6 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { al.stubsJarWithoutStaticLibs = android.PathForModuleOut(ctx, "metalava", "stubs.jar") al.stubsJar = android.PathForModuleOut(ctx, ctx.ModuleName(), fmt.Sprintf("%s.jar", ctx.ModuleName())) - if depApiSrcsStubsJar != nil { - al.extractApiSrcs(ctx, rule, stubsDir, depApiSrcsStubsJar) - } rule.Command(). BuiltTool("soong_zip"). Flag("-write_if_changed"). @@ -2317,18 +2326,17 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { rule.Build("metalava", "metalava merged text") - if depApiSrcsStubsJar == nil { - var flags javaBuilderFlags - flags.javaVersion = getStubsJavaVersion() - flags.javacFlags = strings.Join(al.properties.Javacflags, " ") - flags.classpath = classpath(classPaths) - flags.bootClasspath = classpath(systemModulesPaths) + javacFlags := javaBuilderFlags{ + javaVersion: getStubsJavaVersion(), + javacFlags: strings.Join(al.properties.Javacflags, " "), + classpath: classpath(classPaths), + bootClasspath: classpath(append(systemModulesPaths, bootclassPaths...)), + } - annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar") + annoSrcJar := android.PathForModuleOut(ctx, ctx.ModuleName(), "anno.srcjar") - TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{}, - android.Paths{al.stubsSrcJar}, annoSrcJar, flags, android.Paths{}) - } + TransformJavaToClasses(ctx, al.stubsJarWithoutStaticLibs, 0, android.Paths{}, + android.Paths{al.stubsSrcJar}, annoSrcJar, javacFlags, android.Paths{}) builder := android.NewRuleBuilder(pctx, ctx) builder.Command(). @@ -2340,7 +2348,7 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { // compile stubs to .dex for hiddenapi processing dexParams := &compileDexParams{ - flags: javaBuilderFlags{}, + flags: javacFlags, sdkVersion: al.SdkVersion(ctx), minSdkVersion: al.MinSdkVersion(ctx), classesJar: al.stubsJar, @@ -2354,7 +2362,7 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { ctx.Phony(ctx.ModuleName(), al.stubsJar) - android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{ HeaderJars: android.PathsIfNonNil(al.stubsJar), ImplementationAndResourcesJars: android.PathsIfNonNil(al.stubsJar), ImplementationJars: android.PathsIfNonNil(al.stubsJar), @@ -2376,19 +2384,56 @@ func (al *ApiLibrary) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap { return nil } -// java_api_library constitutes the sdk, and does not build against one +// Most java_api_library constitues the sdk, but there are some java_api_library that +// does not contribute to the api surface. Such modules are allowed to set sdk_version +// other than "none" func (al *ApiLibrary) SdkVersion(ctx android.EarlyModuleContext) android.SdkSpec { - return android.SdkSpecNone + return android.SdkSpecFrom(ctx, proptools.String(al.properties.Sdk_version)) } // java_api_library is always at "current". Return FutureApiLevel func (al *ApiLibrary) MinSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { - return android.FutureApiLevel + return al.SdkVersion(ctx).ApiLevel +} + +func (al *ApiLibrary) ReplaceMaxSdkVersionPlaceholder(ctx android.EarlyModuleContext) android.ApiLevel { + return al.SdkVersion(ctx).ApiLevel +} + +func (al *ApiLibrary) SystemModules() string { + return proptools.String(al.properties.System_modules) +} + +func (al *ApiLibrary) TargetSdkVersion(ctx android.EarlyModuleContext) android.ApiLevel { + return al.SdkVersion(ctx).ApiLevel +} + +func (al *ApiLibrary) IDEInfo(i *android.IdeInfo) { + i.Deps = append(i.Deps, al.ideDeps()...) + i.Libs = append(i.Libs, al.properties.Libs...) + i.Static_libs = append(i.Static_libs, al.properties.Static_libs...) + i.SrcJars = append(i.SrcJars, al.stubsSrcJar.String()) +} + +// deps of java_api_library for module_bp_java_deps.json +func (al *ApiLibrary) ideDeps() []string { + ret := []string{} + ret = append(ret, al.properties.Libs...) + ret = append(ret, al.properties.Static_libs...) + if al.properties.System_modules != nil { + ret = append(ret, proptools.String(al.properties.System_modules)) + } + // Other non java_library dependencies like java_api_contribution are ignored for now. + return ret } // implement the following interfaces for hiddenapi processing var _ hiddenAPIModule = (*ApiLibrary)(nil) var _ UsesLibraryDependency = (*ApiLibrary)(nil) +var _ android.SdkContext = (*ApiLibrary)(nil) + +// implement the following interface for IDE completion. +var _ android.IDEInfo = (*ApiLibrary)(nil) // // Java prebuilts @@ -2721,7 +2766,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { setUncompressDex(ctx, &j.dexpreopter, &j.dexer) j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex - var dexOutputFile android.OutputPath + var dexOutputFile android.Path dexParams := &compileDexParams{ flags: flags, sdkVersion: j.SdkVersion(ctx), @@ -2746,7 +2791,7 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } - android.SetProvider(ctx, JavaInfoProvider, JavaInfo{ + android.SetProvider(ctx, JavaInfoProvider, &JavaInfo{ HeaderJars: android.PathsIfNonNil(j.combinedHeaderFile), TransitiveLibsHeaderJars: j.transitiveLibsHeaderJars, TransitiveStaticLibsHeaderJars: j.transitiveStaticLibsHeaderJars, @@ -2866,7 +2911,7 @@ var _ android.IDECustomizedModuleName = (*Import)(nil) // Collect information for opening IDE project files in java/jdeps.go. func (j *Import) IDEInfo(dpInfo *android.IdeInfo) { - dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...) + dpInfo.Jars = append(dpInfo.Jars, j.combinedHeaderFile.String()) } func (j *Import) IDECustomizedModuleName() string { diff --git a/java/java_test.go b/java/java_test.go index 33079f381..86bfe9fe4 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -1342,12 +1342,12 @@ func TestJavaLibraryWithSystemModules(t *testing.T) { } `) - checkBootClasspathForSystemModule(t, ctx, "lib-with-source-system-modules", "/source-jar.jar") + checkBootClasspathForLibWithSystemModule(t, ctx, "lib-with-source-system-modules", "/source-jar.jar") - checkBootClasspathForSystemModule(t, ctx, "lib-with-prebuilt-system-modules", "/prebuilt-jar.jar") + checkBootClasspathForLibWithSystemModule(t, ctx, "lib-with-prebuilt-system-modules", "/prebuilt-jar.jar") } -func checkBootClasspathForSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) { +func checkBootClasspathForLibWithSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) { javacRule := ctx.ModuleForTests(moduleName, "android_common").Rule("javac") bootClasspath := javacRule.Args["bootClasspath"] if strings.HasPrefix(bootClasspath, "--system ") && strings.HasSuffix(bootClasspath, expectedSuffix) { @@ -2256,61 +2256,6 @@ func TestJavaApiLibraryStaticLibsLink(t *testing.T) { } } -func TestJavaApiLibraryFullApiSurfaceStub(t *testing.T) { - provider_bp_a := ` - java_api_contribution { - name: "foo1", - api_file: "current.txt", - api_surface: "public", - } - ` - provider_bp_b := ` - java_api_contribution { - name: "foo2", - api_file: "current.txt", - api_surface: "public", - } - ` - lib_bp_a := ` - java_api_library { - name: "lib1", - api_surface: "public", - api_contributions: ["foo1", "foo2"], - stubs_type: "everything", - } - ` - - ctx := android.GroupFixturePreparers( - prepareForJavaTest, - android.FixtureMergeMockFs( - map[string][]byte{ - "a/Android.bp": []byte(provider_bp_a), - "b/Android.bp": []byte(provider_bp_b), - "c/Android.bp": []byte(lib_bp_a), - }, - ), - android.FixtureMergeEnv( - map[string]string{ - "DISABLE_STUB_VALIDATION": "true", - }, - ), - ).RunTestWithBp(t, ` - java_api_library { - name: "bar1", - api_surface: "public", - api_contributions: ["foo1"], - full_api_surface_stub: "lib1", - stubs_type: "everything", - } - `) - - m := ctx.ModuleForTests("bar1", "android_common") - manifest := m.Output("metalava.sbox.textproto") - sboxProto := android.RuleBuilderSboxProtoForTests(t, ctx.TestContext, manifest) - manifestCommand := sboxProto.Commands[0].GetCommand() - android.AssertStringDoesContain(t, "Command expected to contain full_api_surface_stub output jar", manifestCommand, "lib1.jar") -} - func TestTransitiveSrcFiles(t *testing.T) { ctx, _ := testJava(t, ` java_library { @@ -2511,9 +2456,6 @@ func TestSdkLibraryProvidesSystemModulesToApiLibrary(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"), - android.FixtureModifyConfig(func(config android.Config) { - config.SetApiLibraries([]string{"foo"}) - }), android.FixtureMergeMockFs( map[string][]byte{ "A.java": nil, @@ -2534,12 +2476,8 @@ func TestSdkLibraryProvidesSystemModulesToApiLibrary(t *testing.T) { system_modules: "baz", } `) - m := result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common") - manifest := m.Output("metalava.sbox.textproto") - sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, manifest) - manifestCommand := sboxProto.Commands[0].GetCommand() - classPathFlag := "--classpath __SBOX_SANDBOX_DIR__/out/soong/.intermediates/bar/android_common/turbine-combined/bar.jar" - android.AssertStringDoesContain(t, "command expected to contain classpath flag", manifestCommand, classPathFlag) + + checkBootClasspathForLibWithSystemModule(t, result.TestContext, apiScopePublic.apiLibraryModuleName("foo"), "/bar.jar") } func TestApiLibraryDroidstubsDependency(t *testing.T) { @@ -2547,9 +2485,6 @@ func TestApiLibraryDroidstubsDependency(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"), - android.FixtureModifyConfig(func(config android.Config) { - config.SetApiLibraries([]string{"foo"}) - }), android.FixtureMergeMockFs( map[string][]byte{ "A.java": nil, @@ -2598,7 +2533,6 @@ func TestDisableFromTextStubForCoverageBuild(t *testing.T) { PrepareForTestWithJacocoInstrumentation, FixtureWithLastReleaseApis("foo"), android.FixtureModifyConfig(func(config android.Config) { - config.SetApiLibraries([]string{"foo"}) config.SetBuildFromTextStub(true) }), android.FixtureModifyEnv(func(env map[string]string) { @@ -2700,11 +2634,7 @@ func TestMultiplePrebuilts(t *testing.T) { for _, tc := range testCases { ctx := android.GroupFixturePreparers( prepareForJavaTest, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), ).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName)) // check that rdep gets the correct variation of dep @@ -2774,11 +2704,7 @@ func TestMultiplePlatformCompatConfigPrebuilts(t *testing.T) { ctx := android.GroupFixturePreparers( prepareForJavaTest, PrepareForTestWithPlatformCompatConfig, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), ).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName)) mergedGlobalConfig := ctx.SingletonForTests("platform_compat_config_singleton").Output("compat_config/merged_compat_config.xml") diff --git a/java/jdeps_test.go b/java/jdeps_test.go index 874d1d7c0..ff54da92a 100644 --- a/java/jdeps_test.go +++ b/java/jdeps_test.go @@ -22,28 +22,46 @@ import ( ) func TestCollectJavaLibraryPropertiesAddLibsDeps(t *testing.T) { - expected := []string{"Foo", "Bar"} - module := LibraryFactory().(*Library) - module.properties.Libs = append(module.properties.Libs, expected...) + ctx, _ := testJava(t, + ` + java_library {name: "Foo"} + java_library {name: "Bar"} + java_library { + name: "javalib", + libs: ["Foo", "Bar"], + } + `) + module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library) dpInfo := &android.IdeInfo{} module.IDEInfo(dpInfo) - if !reflect.DeepEqual(dpInfo.Deps, expected) { - t.Errorf("Library.IDEInfo() Deps = %v, want %v", dpInfo.Deps, expected) + for _, expected := range []string{"Foo", "Bar"} { + if !android.InList(expected, dpInfo.Deps) { + t.Errorf("Library.IDEInfo() Deps = %v, %v not found", dpInfo.Deps, expected) + } } } func TestCollectJavaLibraryPropertiesAddStaticLibsDeps(t *testing.T) { - expected := []string{"Foo", "Bar"} - module := LibraryFactory().(*Library) - module.properties.Static_libs = append(module.properties.Static_libs, expected...) + ctx, _ := testJava(t, + ` + java_library {name: "Foo"} + java_library {name: "Bar"} + java_library { + name: "javalib", + static_libs: ["Foo", "Bar"], + } + `) + module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library) dpInfo := &android.IdeInfo{} module.IDEInfo(dpInfo) - if !reflect.DeepEqual(dpInfo.Deps, expected) { - t.Errorf("Library.IDEInfo() Deps = %v, want %v", dpInfo.Deps, expected) + for _, expected := range []string{"Foo", "Bar"} { + if !android.InList(expected, dpInfo.Deps) { + t.Errorf("Library.IDEInfo() Deps = %v, %v not found", dpInfo.Deps, expected) + } } } @@ -73,15 +91,42 @@ func TestCollectJavaLibraryPropertiesAddAidlIncludeDirs(t *testing.T) { } } -func TestCollectJavaLibraryPropertiesAddJarjarRules(t *testing.T) { - expected := "Jarjar_rules.txt" - module := LibraryFactory().(*Library) - module.expandJarjarRules = android.PathForTesting(expected) +func TestCollectJavaLibraryWithJarJarRules(t *testing.T) { + ctx, _ := testJava(t, + ` + java_library { + name: "javalib", + srcs: ["foo.java"], + jarjar_rules: "jarjar_rules.txt", + } + `) + module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library) dpInfo := &android.IdeInfo{} module.IDEInfo(dpInfo) - - if dpInfo.Jarjar_rules[0] != expected { - t.Errorf("Library.IDEInfo() Jarjar_rules = %v, want %v", dpInfo.Jarjar_rules[0], expected) + android.AssertBoolEquals(t, "IdeInfo.Srcs of repackaged library should be empty", true, len(dpInfo.Srcs) == 0) + android.AssertStringEquals(t, "IdeInfo.Jar_rules of repackaged library should not be empty", "jarjar_rules.txt", dpInfo.Jarjar_rules[0]) + if !android.SubstringInList(dpInfo.Jars, "soong/.intermediates/javalib/android_common/jarjar/turbine/javalib.jar") { + t.Errorf("IdeInfo.Jars of repackaged library should contain the output of jarjar-ing. All outputs: %v\n", dpInfo.Jars) } } + +func TestCollectJavaLibraryLinkingAgainstVersionedSdk(t *testing.T) { + ctx := android.GroupFixturePreparers( + prepareForJavaTest, + FixtureWithPrebuiltApis(map[string][]string{ + "29": {}, + })).RunTestWithBp(t, + ` + java_library { + name: "javalib", + srcs: ["foo.java"], + sdk_version: "29", + } + `) + module := ctx.ModuleForTests("javalib", "android_common").Module().(*Library) + dpInfo := &android.IdeInfo{} + + module.IDEInfo(dpInfo) + android.AssertStringListContains(t, "IdeInfo.Deps should contain versioned sdk module", dpInfo.Deps, "sdk_public_29_android") +} diff --git a/java/kotlin.go b/java/kotlin.go index aa2db0ecd..c28bc3f54 100644 --- a/java/kotlin.go +++ b/java/kotlin.go @@ -101,6 +101,10 @@ func kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile andro commonSrcFilesArg = "--common_srcs " + commonSrcsList.String() } + classpathRspFile := android.PathForModuleOut(ctx, "kotlinc", "classpath.rsp") + android.WriteFileRule(ctx, classpathRspFile, strings.Join(flags.kotlincClasspath.Strings(), " ")) + deps = append(deps, classpathRspFile) + ctx.Build(pctx, android.BuildParams{ Rule: kotlinc, Description: "kotlinc", @@ -109,7 +113,7 @@ func kotlinCompile(ctx android.ModuleContext, outputFile, headerOutputFile andro Inputs: srcFiles, Implicits: deps, Args: map[string]string{ - "classpath": flags.kotlincClasspath.FormJavaClassPath(""), + "classpath": classpathRspFile.String(), "kotlincFlags": flags.kotlincFlags, "commonSrcFilesArg": commonSrcFilesArg, "srcJars": strings.Join(srcJars.Strings(), " "), @@ -205,6 +209,10 @@ func kotlinKapt(ctx android.ModuleContext, srcJarOutputFile, resJarOutputFile an kotlinName := filepath.Join(ctx.ModuleDir(), ctx.ModuleSubDir(), ctx.ModuleName()) kotlinName = strings.ReplaceAll(kotlinName, "/", "__") + classpathRspFile := android.PathForModuleOut(ctx, "kapt", "classpath.rsp") + android.WriteFileRule(ctx, classpathRspFile, strings.Join(flags.kotlincClasspath.Strings(), "\n")) + deps = append(deps, classpathRspFile) + // First run kapt to generate .java stubs from .kt files kaptStubsJar := android.PathForModuleOut(ctx, "kapt", "stubs.jar") ctx.Build(pctx, android.BuildParams{ @@ -214,7 +222,7 @@ func kotlinKapt(ctx android.ModuleContext, srcJarOutputFile, resJarOutputFile an Inputs: srcFiles, Implicits: deps, Args: map[string]string{ - "classpath": flags.kotlincClasspath.FormJavaClassPath(""), + "classpath": classpathRspFile.String(), "kotlincFlags": flags.kotlincFlags, "commonSrcFilesArg": commonSrcFilesArg, "srcJars": strings.Join(srcJars.Strings(), " "), diff --git a/java/metalava/Android.bp b/java/metalava/Android.bp new file mode 100644 index 000000000..ccbd191d3 --- /dev/null +++ b/java/metalava/Android.bp @@ -0,0 +1,18 @@ +// 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. + +filegroup { + name: "metalava-config-files", + srcs: ["*-config.xml"], +} diff --git a/java/metalava/OWNERS b/java/metalava/OWNERS new file mode 100644 index 000000000..e8c438e0f --- /dev/null +++ b/java/metalava/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 463936 + +file:platform/tools/metalava:/OWNERS diff --git a/java/metalava/main-config.xml b/java/metalava/main-config.xml new file mode 100644 index 000000000..c61196fc6 --- /dev/null +++ b/java/metalava/main-config.xml @@ -0,0 +1,19 @@ +<!-- + ~ 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. + --> + +<config xmlns="http://www.google.com/tools/metalava/config" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.google.com/tools/metalava/config ../../../../tools/metalava/metalava/src/main/resources/schemas/config.xsd"/> diff --git a/java/metalava/source-model-selection-config.xml b/java/metalava/source-model-selection-config.xml new file mode 100644 index 000000000..c61196fc6 --- /dev/null +++ b/java/metalava/source-model-selection-config.xml @@ -0,0 +1,19 @@ +<!-- + ~ 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. + --> + +<config xmlns="http://www.google.com/tools/metalava/config" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.google.com/tools/metalava/config ../../../../tools/metalava/metalava/src/main/resources/schemas/config.xsd"/> diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index 38553a61b..d794e511b 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -168,9 +168,10 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo var transitiveSrcFiles android.Paths for _, module := range allModules { - depInfo, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider) - if depInfo.TransitiveSrcFiles != nil { - transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...) + if depInfo, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { + if depInfo.TransitiveSrcFiles != nil { + transitiveSrcFiles = append(transitiveSrcFiles, depInfo.TransitiveSrcFiles.ToList()...) + } } } jarArgs := resourcePathsToJarArgs(transitiveSrcFiles) diff --git a/java/ravenwood.go b/java/ravenwood.go index 908619d5f..a52f4053f 100644 --- a/java/ravenwood.go +++ b/java/ravenwood.go @@ -33,6 +33,8 @@ func RegisterRavenwoodBuildComponents(ctx android.RegistrationContext) { var ravenwoodLibContentTag = dependencyTag{name: "ravenwoodlibcontent"} var ravenwoodUtilsTag = dependencyTag{name: "ravenwoodutils"} var ravenwoodRuntimeTag = dependencyTag{name: "ravenwoodruntime"} +var ravenwoodDataTag = dependencyTag{name: "ravenwooddata"} +var ravenwoodTestResourceApkTag = dependencyTag{name: "ravenwoodtestresapk"} const ravenwoodUtilsName = "ravenwood-utils" const ravenwoodRuntimeName = "ravenwood-runtime" @@ -53,6 +55,13 @@ func getLibPath(archType android.ArchType) string { type ravenwoodTestProperties struct { Jni_libs []string + + // Specify another android_app module here to copy it to the test directory, so that + // the ravenwood test can access it. + // TODO: For now, we simply refer to another android_app module and copy it to the + // test directory. Eventually, android_ravenwood_test should support all the resource + // related properties and build resources from the `res/` directory. + Resource_apk *string } type ravenwoodTest struct { @@ -114,6 +123,11 @@ func (r *ravenwoodTest) DepsMutator(ctx android.BottomUpMutatorContext) { for _, lib := range r.ravenwoodTestProperties.Jni_libs { ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib) } + + // Resources APK + if resourceApk := proptools.String(r.ravenwoodTestProperties.Resource_apk); resourceApk != "" { + ctx.AddVariationDependencies(nil, ravenwoodTestResourceApkTag, resourceApk) + } } func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -175,6 +189,14 @@ func (r *ravenwoodTest) GenerateAndroidBuildActions(ctx android.ModuleContext) { installDeps = append(installDeps, installJni) } + resApkInstallPath := installPath.Join(ctx, "ravenwood-res-apks") + if resApk := ctx.GetDirectDepsWithTag(ravenwoodTestResourceApkTag); len(resApk) > 0 { + for _, installFile := range resApk[0].FilesToInstall() { + installResApk := ctx.InstallFile(resApkInstallPath, "ravenwood-res.apk", installFile) + installDeps = append(installDeps, installResApk) + } + } + // Install our JAR with all dependencies ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.outputFile, installDeps...) } @@ -198,6 +220,9 @@ type ravenwoodLibgroupProperties struct { Libs []string Jni_libs []string + + // We use this to copy framework-res.apk to the ravenwood runtime directory. + Data []string } type ravenwoodLibgroup struct { @@ -236,6 +261,9 @@ func (r *ravenwoodLibgroup) DepsMutator(ctx android.BottomUpMutatorContext) { for _, lib := range r.ravenwoodLibgroupProperties.Jni_libs { ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), jniLibTag, lib) } + for _, data := range r.ravenwoodLibgroupProperties.Data { + ctx.AddVariationDependencies(nil, ravenwoodDataTag, data) + } } func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContext) { @@ -257,6 +285,14 @@ func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContex installPath := android.PathForModuleInstall(ctx, r.BaseModuleName()) for _, lib := range r.ravenwoodLibgroupProperties.Libs { libModule := ctx.GetDirectDepWithTag(lib, ravenwoodLibContentTag) + if libModule == nil { + if ctx.Config().AllowMissingDependencies() { + ctx.AddMissingDependencies([]string{lib}) + } else { + ctx.PropertyErrorf("lib", "missing dependency %q", lib) + } + continue + } libJar := android.OutputFileForModule(ctx, libModule, "") ctx.InstallFile(installPath, lib+".jar", libJar) } @@ -266,6 +302,13 @@ func (r *ravenwoodLibgroup) GenerateAndroidBuildActions(ctx android.ModuleContex ctx.InstallFile(soInstallPath, jniLib.path.Base(), jniLib.path) } + dataInstallPath := installPath.Join(ctx, "ravenwood-data") + for _, data := range r.ravenwoodLibgroupProperties.Data { + libModule := ctx.GetDirectDepWithTag(data, ravenwoodDataTag) + file := android.OutputFileForModule(ctx, libModule, "") + ctx.InstallFile(dataInstallPath, file.Base(), file) + } + // Normal build should perform install steps ctx.Phony(r.BaseModuleName(), android.PathForPhony(ctx, r.BaseModuleName()+"-install")) } diff --git a/java/ravenwood_test.go b/java/ravenwood_test.go index 59612645c..d26db930d 100644 --- a/java/ravenwood_test.go +++ b/java/ravenwood_test.go @@ -57,6 +57,14 @@ var prepareRavenwoodRuntime = android.GroupFixturePreparers( name: "framework-rules.ravenwood", srcs: ["Rules.java"], } + android_app { + name: "app1", + sdk_version: "current", + } + android_app { + name: "app2", + sdk_version: "current", + } android_ravenwood_libgroup { name: "ravenwood-runtime", libs: [ @@ -67,6 +75,9 @@ var prepareRavenwoodRuntime = android.GroupFixturePreparers( "ravenwood-runtime-jni1", "ravenwood-runtime-jni2", ], + data: [ + "app1", + ], } android_ravenwood_libgroup { name: "ravenwood-utils", @@ -102,6 +113,7 @@ func TestRavenwoodRuntime(t *testing.T) { runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni1.so") runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/libred.so") runtime.Output(installPathPrefix + "/ravenwood-runtime/lib64/ravenwood-runtime-jni3.so") + runtime.Output(installPathPrefix + "/ravenwood-runtime/ravenwood-data/app1.apk") utils := ctx.ModuleForTests("ravenwood-utils", "android_common") utils.Output(installPathPrefix + "/ravenwood-utils/framework-rules.ravenwood.jar") } @@ -143,6 +155,7 @@ func TestRavenwoodTest(t *testing.T) { "jni-lib2", "ravenwood-runtime-jni2", ], + resource_apk: "app2", sdk_version: "test_current", } `) @@ -169,6 +182,7 @@ func TestRavenwoodTest(t *testing.T) { module.Output(installPathPrefix + "/ravenwood-test/lib64/jni-lib1.so") module.Output(installPathPrefix + "/ravenwood-test/lib64/libblue.so") module.Output(installPathPrefix + "/ravenwood-test/lib64/libpink.so") + module.Output(installPathPrefix + "/ravenwood-test/ravenwood-res-apks/ravenwood-res.apk") // ravenwood-runtime*.so are included in the runtime, so it shouldn't be emitted. for _, o := range module.AllOutputs() { diff --git a/java/robolectric.go b/java/robolectric.go index 4cad5b153..26f4b7176 100644 --- a/java/robolectric.go +++ b/java/robolectric.go @@ -214,12 +214,13 @@ func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) } handleLibDeps := func(dep android.Module, runtimeOnly bool) { - m, _ := android.OtherModuleProvider(ctx, dep, JavaInfoProvider) if !runtimeOnly { r.libs = append(r.libs, ctx.OtherModuleName(dep)) } if !android.InList(ctx.OtherModuleName(dep), config.FrameworkLibraries) { - combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...) + if m, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { + combinedJarJars = append(combinedJarJars, m.ImplementationAndResourcesJars...) + } } } diff --git a/java/sdk_library.go b/java/sdk_library.go index 1eb7ab834..4f95a997d 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -20,7 +20,6 @@ import ( "path" "path/filepath" "reflect" - "regexp" "sort" "strings" "sync" @@ -427,22 +426,10 @@ var ( apiScopeModuleLib, apiScopeSystemServer, } - apiLibraryAdditionalProperties = map[string]struct { - FullApiSurfaceStubLib string - AdditionalApiContribution string - }{ - "legacy.i18n.module.platform.api": { - FullApiSurfaceStubLib: "legacy.core.platform.api.stubs", - AdditionalApiContribution: "i18n.module.public.api.stubs.source.api.contribution", - }, - "stable.i18n.module.platform.api": { - FullApiSurfaceStubLib: "stable.core.platform.api.stubs", - AdditionalApiContribution: "i18n.module.public.api.stubs.source.api.contribution", - }, - "conscrypt.module.platform.api": { - FullApiSurfaceStubLib: "stable.core.platform.api.stubs", - AdditionalApiContribution: "conscrypt.module.public.api.stubs.source.api.contribution", - }, + apiLibraryAdditionalProperties = map[string]string{ + "legacy.i18n.module.platform.api": "i18n.module.public.api.stubs.source.api.contribution", + "stable.i18n.module.platform.api": "i18n.module.public.api.stubs.source.api.contribution", + "conscrypt.module.platform.api": "conscrypt.module.public.api.stubs.source.api.contribution", } ) @@ -650,17 +637,14 @@ type sdkLibraryProperties struct { Legacy_errors_allowed *bool } - // Determines if the module contributes to any api surfaces. - // This property should be set to true only if the module is listed under - // frameworks-base-api.bootclasspath in frameworks/base/api/Android.bp. - // Otherwise, this property should be set to false. - // Defaults to false. - Contribute_to_android_api *bool - // a list of aconfig_declarations module names that the stubs generated in this module // depend on. Aconfig_declarations []string + // Determines if the module generates the stubs from the api signature files + // instead of the source Java files. Defaults to true. + Build_from_text_stub *bool + // TODO: determines whether to create HTML doc or not // Html_doc *bool } @@ -1061,28 +1045,6 @@ const ( annotationsComponentName = "annotations.zip" ) -// A regular expression to match tags that reference a specific stubs component. -// -// It will only match if given a valid scope and a valid component. It is verfy strict -// to ensure it does not accidentally match a similar looking tag that should be processed -// by the embedded Library. -var tagSplitter = func() *regexp.Regexp { - // Given a list of literal string items returns a regular expression that will - // match any one of the items. - choice := func(items ...string) string { - return `\Q` + strings.Join(items, `\E|\Q`) + `\E` - } - - // Regular expression to match one of the scopes. - scopesRegexp := choice(allScopeNames...) - - // Regular expression to match one of the components. - componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName, annotationsComponentName) - - // Regular expression to match any combination of one scope and one component. - return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp)) -}() - func (module *commonToSdkLibraryAndImport) setOutputFiles(ctx android.ModuleContext) { if module.doctagPaths != nil { ctx.SetOutputFiles(module.doctagPaths, ".doctags") @@ -1516,6 +1478,13 @@ func (module *SdkLibrary) ComponentDepsMutator(ctx android.BottomUpMutatorContex // Add other dependencies as normal. func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) { + // If the module does not create an implementation library or defaults to stubs, + // mark the top level sdk library as stubs module as the module will provide stubs via + // "magic" when listed as a dependency in the Android.bp files. + notCreateImplLib := proptools.Bool(module.sdkLibraryProperties.Api_only) + preferStubs := proptools.Bool(module.sdkLibraryProperties.Default_to_stubs) + module.properties.Is_stubs_module = proptools.BoolPtr(notCreateImplLib || preferStubs) + var missingApiModules []string for _, apiScope := range module.getGeneratedApiScopes(ctx) { if apiScope.unstable { @@ -1740,30 +1709,13 @@ func (module *SdkLibrary) latestIncompatibilitiesModuleName(apiScope *apiScope) return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope) } -func (module *SdkLibrary) contributesToApiSurface(c android.Config) bool { - _, exists := c.GetApiLibraries()[module.Name()] - return exists -} - -// The listed modules are the special java_sdk_libraries where apiScope.kind do not match the -// api surface that the module contribute to. For example, the public droidstubs and java_library -// do not contribute to the public api surface, but contributes to the core platform api surface. -// This method returns the full api surface stub lib that -// the generated java_api_library should depend on. -func (module *SdkLibrary) alternativeFullApiSurfaceStubLib() string { - if val, ok := apiLibraryAdditionalProperties[module.Name()]; ok { - return val.FullApiSurfaceStubLib - } - return "" -} - // The listed modules' stubs contents do not match the corresponding txt files, // but require additional api contributions to generate the full stubs. // This method returns the name of the additional api contribution module // for corresponding sdk_library modules. func (module *SdkLibrary) apiLibraryAdditionalApiContribution() string { if val, ok := apiLibraryAdditionalProperties[module.Name()]; ok { - return val.AdditionalApiContribution + return val } return "" } @@ -2058,17 +2010,18 @@ func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookC mctx.CreateModule(DroidstubsFactory, &props, module.sdkComponentPropertiesForChildLibrary()).(*Droidstubs).CallHookIfAvailable(mctx) } -func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope, alternativeFullApiSurfaceStub string) { +func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) { props := struct { - Name *string - Visibility []string - Api_contributions []string - Libs []string - Static_libs []string - Full_api_surface_stub *string - System_modules *string - Enable_validation *bool - Stubs_type *string + Name *string + Visibility []string + Api_contributions []string + Libs []string + Static_libs []string + System_modules *string + Enable_validation *bool + Stubs_type *string + Sdk_version *string + Previous_api *string }{} props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope)) @@ -2092,34 +2045,29 @@ func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, } props.Api_contributions = apiContributions - props.Libs = module.properties.Libs + + // Ensure that stub-annotations is added to the classpath before any other libs + props.Libs = []string{"stub-annotations"} + props.Libs = append(props.Libs, module.properties.Libs...) + props.Libs = append(props.Libs, module.properties.Static_libs...) props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...) props.Libs = append(props.Libs, module.scopeToProperties[apiScope].Libs...) - props.Libs = append(props.Libs, "stub-annotations") props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs - props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName()) - if alternativeFullApiSurfaceStub != "" { - props.Full_api_surface_stub = proptools.StringPtr(alternativeFullApiSurfaceStub) - } - - // android_module_lib_stubs_current.from-text only comprises api contributions from art, conscrypt and i18n. - // Thus, replace with android_module_lib_stubs_current_full.from-text, which comprises every api domains. - if apiScope.kind == android.SdkModule { - props.Full_api_surface_stub = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text") - } - - // java_sdk_library modules that set sdk_version as none does not depend on other api - // domains. Therefore, java_api_library created from such modules should not depend on - // full_api_surface_stubs but create and compile stubs by the java_api_library module - // itself. - if module.SdkVersion(mctx).Kind == android.SdkNone { - props.Full_api_surface_stub = nil - } props.System_modules = module.deviceProperties.System_modules props.Enable_validation = proptools.BoolPtr(true) props.Stubs_type = proptools.StringPtr("everything") + if module.deviceProperties.Sdk_version != nil { + props.Sdk_version = module.deviceProperties.Sdk_version + } + + if module.compareAgainstLatestApi(apiScope) { + // check against the latest released API + latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope)) + props.Previous_api = latestApiFilegroupName + } + mctx.CreateModule(ApiLibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } @@ -2150,7 +2098,7 @@ func (module *SdkLibrary) topLevelStubsLibraryProps(mctx android.DefaultableHook } func (module *SdkLibrary) createTopLevelStubsLibrary( - mctx android.DefaultableHookContext, apiScope *apiScope, contributesToApiSurface bool) { + mctx android.DefaultableHookContext, apiScope *apiScope) { // Dist the "everything" stubs when the RELEASE_HIDDEN_API_EXPORTABLE_STUBS build flag is false doDist := !mctx.Config().ReleaseHiddenApiExportableStubs() @@ -2159,7 +2107,7 @@ func (module *SdkLibrary) createTopLevelStubsLibrary( // Add the stub compiling java_library/java_api_library as static lib based on build config staticLib := module.sourceStubsLibraryModuleName(apiScope) - if mctx.Config().BuildFromTextStub() && contributesToApiSurface { + if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() { staticLib = module.apiLibraryModuleName(apiScope) } props.Static_libs = append(props.Static_libs, staticLib) @@ -2202,8 +2150,8 @@ func (module *SdkLibrary) UniqueApexVariations() bool { return module.uniqueApexVariations() } -func (module *SdkLibrary) ContributeToApi() bool { - return proptools.BoolDefault(module.sdkLibraryProperties.Contribute_to_android_api, false) +func (module *SdkLibrary) ModuleBuildFromTextStubs() bool { + return proptools.BoolDefault(module.sdkLibraryProperties.Build_from_text_stub, true) } // Creates the xml file that publicizes the runtime library @@ -2379,16 +2327,10 @@ func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookCont module.createStubsLibrary(mctx, scope) module.createExportableStubsLibrary(mctx, scope) - alternativeFullApiSurfaceStubLib := "" - if scope == apiScopePublic { - alternativeFullApiSurfaceStubLib = module.alternativeFullApiSurfaceStubLib() - } - contributesToApiSurface := module.contributesToApiSurface(mctx.Config()) || alternativeFullApiSurfaceStubLib != "" - if contributesToApiSurface { - module.createApiLibrary(mctx, scope, alternativeFullApiSurfaceStubLib) + if mctx.Config().BuildFromTextStub() && module.ModuleBuildFromTextStubs() { + module.createApiLibrary(mctx, scope) } - - module.createTopLevelStubsLibrary(mctx, scope, contributesToApiSurface) + module.createTopLevelStubsLibrary(mctx, scope) module.createTopLevelExportableStubsLibrary(mctx, scope) } @@ -3660,3 +3602,19 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo propertySet.AddProperty("doctag_files", dests) } } + +// TODO(b/358613520): This can be removed when modules are no longer allowed to depend on the top-level library. +func (s *SdkLibrary) IDEInfo(dpInfo *android.IdeInfo) { + s.Library.IDEInfo(dpInfo) + if s.implLibraryModule != nil { + dpInfo.Deps = append(dpInfo.Deps, s.implLibraryModule.Name()) + } else { + // This java_sdk_library does not have an implementation (it sets `api_only` to true). + // Examples of this are `art.module.intra.core.api` (IntraCore api surface). + // Return the "public" stubs for these. + stubPaths := s.findClosestScopePath(apiScopePublic) + if len(stubPaths.stubsHeaderPath) > 0 { + dpInfo.Jars = append(dpInfo.Jars, stubPaths.stubsHeaderPath[0].String()) + } + } +} diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go index a8a1494ee..368eca7f2 100644 --- a/java/sdk_library_test.go +++ b/java/sdk_library_test.go @@ -35,14 +35,7 @@ func TestJavaSdkLibrary(t *testing.T) { "29": {"foo"}, "30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"}, }), - android.FixtureModifyConfig(func(config android.Config) { - config.SetApiLibraries([]string{"foo"}) - }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` droiddoc_exported_dir { name: "droiddoc-templates-sdk", @@ -540,11 +533,7 @@ func TestJavaSdkLibrary_Deps(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib", @@ -923,6 +912,7 @@ func TestJavaSdkLibraryImport(t *testing.T) { } CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{ + `all_apex_contributions`, `dex2oatd`, `prebuilt_sdklib.stubs`, `prebuilt_sdklib.stubs.source.test`, @@ -936,11 +926,7 @@ func TestJavaSdkLibraryImport_WithSource(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib", @@ -989,11 +975,7 @@ func testJavaSdkLibraryImport_Preferred(t *testing.T, prefer string, preparer an PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib"), preparer, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib", @@ -1185,11 +1167,7 @@ func TestSdkLibraryImport_MetadataModuleSupersedesPreferred(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib.source_preferred_using_legacy_flags", "sdklib.prebuilt_preferred_using_legacy_flags"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"), ).RunTestWithBp(t, bp) // Make sure that rdeps get the correct source vs prebuilt based on mainline_module_contributions @@ -1371,11 +1349,7 @@ func TestJavaSdkLibraryDist(t *testing.T) { "sdklib_group_foo", "sdklib_owner_foo", "foo"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` java_sdk_library { name: "sdklib_no_group", @@ -1588,9 +1562,6 @@ func TestJavaSdkLibrary_ApiLibrary(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"), - android.FixtureModifyConfig(func(config android.Config) { - config.SetApiLibraries([]string{"foo"}) - }), ).RunTestWithBp(t, ` java_sdk_library { name: "foo", @@ -1609,36 +1580,30 @@ func TestJavaSdkLibrary_ApiLibrary(t *testing.T) { `) testCases := []struct { - scope *apiScope - apiContributions []string - fullApiSurfaceStub string + scope *apiScope + apiContributions []string }{ { - scope: apiScopePublic, - apiContributions: []string{"foo.stubs.source.api.contribution"}, - fullApiSurfaceStub: "android_stubs_current", + scope: apiScopePublic, + apiContributions: []string{"foo.stubs.source.api.contribution"}, }, { - scope: apiScopeSystem, - apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, - fullApiSurfaceStub: "android_system_stubs_current", + scope: apiScopeSystem, + apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, }, { - scope: apiScopeTest, - apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, - fullApiSurfaceStub: "android_test_stubs_current", + scope: apiScopeTest, + apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, }, { - scope: apiScopeModuleLib, - apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, - fullApiSurfaceStub: "android_module_lib_stubs_current_full.from-text", + scope: apiScopeModuleLib, + apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"}, }, } for _, c := range testCases { m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary) android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions) - android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.fullApiSurfaceStub, *m.properties.Full_api_surface_stub) } } @@ -1708,9 +1673,6 @@ func TestSdkLibraryExportableStubsLibrary(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"), - android.FixtureModifyConfig(func(config android.Config) { - config.SetApiLibraries([]string{"foo"}) - }), ).RunTestWithBp(t, ` aconfig_declarations { name: "bar", @@ -1799,12 +1761,8 @@ func TestStubResolutionOfJavaSdkLibraryInLibs(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - // We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions", - } - }), + // We can use any of the apex contribution build flags from build/soong/android/config.go#mainlineApexContributionBuildFlags here + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"), ) result := fixture.RunTestWithBp(t, bp) @@ -1887,11 +1845,7 @@ func TestMultipleSdkLibraryPrebuilts(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("sdklib", "sdklib.v1", "sdklib.v2"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "my_mainline_module_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "my_mainline_module_contributions"), ) for _, tc := range testCases { diff --git a/java/sdk_test.go b/java/sdk_test.go index 9e8ba6ed0..2dac27af1 100644 --- a/java/sdk_test.go +++ b/java/sdk_test.go @@ -388,7 +388,9 @@ func TestClasspath(t *testing.T) { }, } + t.Parallel() t.Run("basic", func(t *testing.T) { + t.Parallel() testClasspathTestCases(t, classpathTestcases, false) }) @@ -404,6 +406,7 @@ func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase } t.Run(testcase.name, func(t *testing.T) { + t.Parallel() moduleType := "java_library" if testcase.moduleType != "" { moduleType = testcase.moduleType diff --git a/java/system_modules.go b/java/system_modules.go index 8e2d5d8ff..5b00079f7 100644 --- a/java/system_modules.go +++ b/java/system_modules.go @@ -120,14 +120,16 @@ func SystemModulesFactory() android.Module { return module } -type SystemModulesProvider interface { - HeaderJars() android.Paths - OutputDirAndDeps() (android.Path, android.Paths) -} +type SystemModulesProviderInfo struct { + // The aggregated header jars from all jars specified in the libs property. + // Used when system module is added as a dependency to bootclasspath. + HeaderJars android.Paths -var _ SystemModulesProvider = (*SystemModules)(nil) + OutputDir android.Path + OutputDirDeps android.Paths +} -var _ SystemModulesProvider = (*systemModulesImport)(nil) +var SystemModulesProvider = blueprint.NewProvider[*SystemModulesProviderInfo]() type SystemModules struct { android.ModuleBase @@ -135,9 +137,6 @@ type SystemModules struct { properties SystemModulesProperties - // The aggregated header jars from all jars specified in the libs property. - // Used when system module is added as a dependency to bootclasspath. - headerJars android.Paths outputDir android.Path outputDeps android.Paths } @@ -147,28 +146,22 @@ type SystemModulesProperties struct { Libs []string } -func (system *SystemModules) HeaderJars() android.Paths { - return system.headerJars -} - -func (system *SystemModules) OutputDirAndDeps() (android.Path, android.Paths) { - if system.outputDir == nil || len(system.outputDeps) == 0 { - panic("Missing directory for system module dependency") - } - return system.outputDir, system.outputDeps -} - func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) { var jars android.Paths ctx.VisitDirectDepsWithTag(systemModulesLibsTag, func(module android.Module) { - dep, _ := android.OtherModuleProvider(ctx, module, JavaInfoProvider) - jars = append(jars, dep.HeaderJars...) + if dep, ok := android.OtherModuleProvider(ctx, module, JavaInfoProvider); ok { + jars = append(jars, dep.HeaderJars...) + } }) - system.headerJars = jars - system.outputDir, system.outputDeps = TransformJarsToSystemModules(ctx, jars) + + android.SetProvider(ctx, SystemModulesProvider, &SystemModulesProviderInfo{ + HeaderJars: jars, + OutputDir: system.outputDir, + OutputDirDeps: system.outputDeps, + }) } // ComponentDepsMutator is called before prebuilt modules without a corresponding source module are @@ -310,3 +303,11 @@ func (p *systemModulesInfoProperties) AddToPropertySet(ctx android.SdkMemberCont propertySet.AddPropertyWithTag("libs", p.Libs, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(true)) } } + +// implement the following interface for IDE completion. +var _ android.IDEInfo = (*SystemModules)(nil) + +func (s *SystemModules) IDEInfo(ideInfo *android.IdeInfo) { + ideInfo.Deps = append(ideInfo.Deps, s.properties.Libs...) + ideInfo.Libs = append(ideInfo.Libs, s.properties.Libs...) +} diff --git a/java/system_modules_test.go b/java/system_modules_test.go index 336dd2134..1a8d0b5d5 100644 --- a/java/system_modules_test.go +++ b/java/system_modules_test.go @@ -182,11 +182,7 @@ func TestMultipleSystemModulesPrebuilts(t *testing.T) { for _, tc := range testCases { res := android.GroupFixturePreparers( prepareForJavaTest, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": "myapex_contributions", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ADSERVICES", "myapex_contributions"), ).RunTestWithBp(t, fmt.Sprintf(bp, tc.selectedDependencyName)) // check that rdep gets the correct variation of system_modules diff --git a/java/testing.go b/java/testing.go index 5ae326d93..a99baf8a2 100644 --- a/java/testing.go +++ b/java/testing.go @@ -52,6 +52,8 @@ var PrepareForTestWithJavaBuildComponents = android.GroupFixturePreparers( android.MockFS{ // Needed for linter used by java_library. "build/soong/java/lint_defaults.txt": nil, + // Needed for java components that invoke Metalava. + "build/soong/java/metalava/Android.bp": []byte(`filegroup {name: "metalava-config-files"}`), // Needed for apps that do not provide their own. "build/make/target/product/security": nil, // Required to generate Java used-by API coverage @@ -484,21 +486,17 @@ func gatherRequiredDepsForTest() string { } extraApiLibraryModules := map[string]droidstubsStruct{ - "android_stubs_current.from-text": publicDroidstubs, - "android_system_stubs_current.from-text": systemDroidstubs, - "android_test_stubs_current.from-text": testDroidstubs, - "android_module_lib_stubs_current.from-text": moduleLibDroidstubs, - "android_module_lib_stubs_current_full.from-text": moduleLibDroidstubs, - "android_system_server_stubs_current.from-text": systemServerDroidstubs, - "core.current.stubs.from-text": publicDroidstubs, - "legacy.core.platform.api.stubs.from-text": publicDroidstubs, - "stable.core.platform.api.stubs.from-text": publicDroidstubs, - "core-lambda-stubs.from-text": publicDroidstubs, - "android-non-updatable.stubs.from-text": publicDroidstubs, - "android-non-updatable.stubs.system.from-text": systemDroidstubs, - "android-non-updatable.stubs.test.from-text": testDroidstubs, - "android-non-updatable.stubs.module_lib.from-text": moduleLibDroidstubs, - "android-non-updatable.stubs.test_module_lib": moduleLibDroidstubs, + "android_stubs_current.from-text": publicDroidstubs, + "android_system_stubs_current.from-text": systemDroidstubs, + "android_test_stubs_current.from-text": testDroidstubs, + "android_module_lib_stubs_current.from-text": moduleLibDroidstubs, + "android_module_lib_stubs_current_full.from-text": moduleLibDroidstubs, + "android_system_server_stubs_current.from-text": systemServerDroidstubs, + "core.current.stubs.from-text": publicDroidstubs, + "legacy.core.platform.api.stubs.from-text": publicDroidstubs, + "stable.core.platform.api.stubs.from-text": publicDroidstubs, + "core-lambda-stubs.from-text": publicDroidstubs, + "android-non-updatable.stubs.test_module_lib": moduleLibDroidstubs, } for _, droidstubs := range droidstubsStructs { @@ -527,6 +525,8 @@ func gatherRequiredDepsForTest() string { name: "%s", api_contributions: ["%s"], stubs_type: "everything", + sdk_version: "none", + system_modules: "none", } `, libName, droidstubs.name+".api.contribution") } diff --git a/phony/phony.go b/phony/phony.go index b421176be..807b95b32 100644 --- a/phony/phony.go +++ b/phony/phony.go @@ -20,6 +20,8 @@ import ( "strings" "android/soong/android" + + "github.com/google/blueprint/proptools" ) func init() { @@ -88,14 +90,15 @@ type PhonyRule struct { android.ModuleBase android.DefaultableModuleBase - properties PhonyProperties + phonyDepsModuleNames []string + properties PhonyProperties } type PhonyProperties struct { // The Phony_deps is the set of all dependencies for this target, // and it can function similarly to .PHONY in a makefile. // Additionally, dependencies within it can even include genrule. - Phony_deps []string + Phony_deps proptools.Configurable[[]string] } // The phony_rule provides functionality similar to the .PHONY in a makefile. @@ -109,13 +112,14 @@ func PhonyRuleFactory() android.Module { } func (p *PhonyRule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + p.phonyDepsModuleNames = p.properties.Phony_deps.GetOrDefault(ctx, nil) } func (p *PhonyRule) AndroidMk() android.AndroidMkData { return android.AndroidMkData{ Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) { - if len(p.properties.Phony_deps) > 0 { - depModulesStr := strings.Join(p.properties.Phony_deps, " ") + if len(p.phonyDepsModuleNames) > 0 { + depModulesStr := strings.Join(p.phonyDepsModuleNames, " ") fmt.Fprintln(w, ".PHONY:", name) fmt.Fprintln(w, name, ":", depModulesStr) } diff --git a/python/python.go b/python/python.go index 1ee533fa8..8726f02ef 100644 --- a/python/python.go +++ b/python/python.go @@ -38,7 +38,7 @@ func registerPythonMutators(ctx android.RegistrationContext) { // Exported to support other packages using Python modules in tests. func RegisterPythonPreDepsMutators(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("python_version", versionSplitMutator()).Parallel() + ctx.Transition("python_version", &versionSplitTransitionMutator{}) } // the version-specific properties that apply to python modules. @@ -245,7 +245,6 @@ var ( protoExt = ".proto" pyVersion2 = "PY2" pyVersion3 = "PY3" - pyVersion2And3 = "PY2ANDPY3" internalPath = "internal" ) @@ -253,46 +252,68 @@ type basePropertiesProvider interface { getBaseProperties() *BaseProperties } -// versionSplitMutator creates version variants for modules and appends the version-specific -// properties for a given variant to the properties in the variant module -func versionSplitMutator() func(android.BottomUpMutatorContext) { - return func(mctx android.BottomUpMutatorContext) { - if base, ok := mctx.Module().(basePropertiesProvider); ok { - props := base.getBaseProperties() - var versionNames []string - // collect version specific properties, so that we can merge version-specific properties - // into the module's overall properties - var versionProps []VersionProperties - // PY3 is first so that we alias the PY3 variant rather than PY2 if both - // are available - if proptools.BoolDefault(props.Version.Py3.Enabled, true) { - versionNames = append(versionNames, pyVersion3) - versionProps = append(versionProps, props.Version.Py3) - } - if proptools.BoolDefault(props.Version.Py2.Enabled, false) { - if !mctx.DeviceConfig().BuildBrokenUsesSoongPython2Modules() && - mctx.ModuleName() != "py2-cmd" && - mctx.ModuleName() != "py2-stdlib" { - mctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration") - } - versionNames = append(versionNames, pyVersion2) - versionProps = append(versionProps, props.Version.Py2) - } - modules := mctx.CreateLocalVariations(versionNames...) - // Alias module to the first variant - if len(versionNames) > 0 { - mctx.AliasVariation(versionNames[0]) - } - for i, v := range versionNames { - // set the actual version for Python module. - newProps := modules[i].(basePropertiesProvider).getBaseProperties() - newProps.Actual_version = v - // append versioned properties for the Python module to the overall properties - err := proptools.AppendMatchingProperties([]interface{}{newProps}, &versionProps[i], nil) - if err != nil { - panic(err) - } +type versionSplitTransitionMutator struct{} + +func (versionSplitTransitionMutator) Split(ctx android.BaseModuleContext) []string { + if base, ok := ctx.Module().(basePropertiesProvider); ok { + props := base.getBaseProperties() + var variants []string + // PY3 is first so that we alias the PY3 variant rather than PY2 if both + // are available + if proptools.BoolDefault(props.Version.Py3.Enabled, true) { + variants = append(variants, pyVersion3) + } + if proptools.BoolDefault(props.Version.Py2.Enabled, false) { + if !ctx.DeviceConfig().BuildBrokenUsesSoongPython2Modules() && + ctx.ModuleName() != "py2-cmd" && + ctx.ModuleName() != "py2-stdlib" { + ctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration") } + variants = append(variants, pyVersion2) + } + return variants + } + return []string{""} +} + +func (versionSplitTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + return "" +} + +func (versionSplitTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + if incomingVariation != "" { + return incomingVariation + } + if base, ok := ctx.Module().(basePropertiesProvider); ok { + props := base.getBaseProperties() + if proptools.BoolDefault(props.Version.Py3.Enabled, true) { + return pyVersion3 + } else { + return pyVersion2 + } + } + + return "" +} + +func (versionSplitTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { + if variation == "" { + return + } + if base, ok := ctx.Module().(basePropertiesProvider); ok { + props := base.getBaseProperties() + props.Actual_version = variation + + var versionProps *VersionProperties + if variation == pyVersion3 { + versionProps = &props.Version.Py3 + } else if variation == pyVersion2 { + versionProps = &props.Version.Py2 + } + + err := proptools.AppendMatchingProperties([]interface{}{props}, versionProps, nil) + if err != nil { + panic(err) } } } diff --git a/rust/bindgen.go b/rust/bindgen.go index dbc369762..a81024acd 100644 --- a/rust/bindgen.go +++ b/rust/bindgen.go @@ -29,7 +29,7 @@ var ( defaultBindgenFlags = []string{""} // bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures. - bindgenClangVersion = "clang-r522817" + bindgenClangVersion = "clang-r530567" _ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string { if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" { @@ -285,7 +285,7 @@ func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) andr if isCpp { cflags = append(cflags, "-x c++") // Add any C++ only flags. - cflags = append(cflags, esc(b.ClangProperties.Cppflags)...) + cflags = append(cflags, esc(b.ClangProperties.Cppflags.GetOrDefault(ctx, nil))...) } else { cflags = append(cflags, "-x c") } @@ -364,7 +364,7 @@ func NewRustBindgen(hod android.HostOrDeviceSupported) (*Module, *bindgenDecorat ClangProperties: cc.RustBindgenClangProperties{}, } - module := NewSourceProviderModule(hod, bindgen, false, true) + module := NewSourceProviderModule(hod, bindgen, false, false) android.AddLoadHook(module, func(ctx android.LoadHookContext) { type stub_props struct { diff --git a/rust/config/darwin_host.go b/rust/config/darwin_host.go index a4bc1879c..df8c6ac5a 100644 --- a/rust/config/darwin_host.go +++ b/rust/config/darwin_host.go @@ -15,6 +15,7 @@ package config import ( + "runtime" "strings" "android/soong/android" @@ -35,13 +36,15 @@ func init() { registerToolchainFactory(android.Darwin, android.Arm64, darwinArm64ToolchainFactory) registerToolchainFactory(android.Darwin, android.X86_64, darwinX8664ToolchainFactory) - pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " ")) - pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " ")) + if runtime.GOOS == "darwin" { + pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " ")) + pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " ")) - pctx.StaticVariable("DarwinToolchainArm64RustFlags", strings.Join(darwinArm64Rustflags, " ")) - pctx.StaticVariable("DarwinToolchainArm64LinkFlags", strings.Join(darwinArm64Linkflags, " ")) - pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " ")) - pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " ")) + pctx.StaticVariable("DarwinToolchainArm64RustFlags", strings.Join(darwinArm64Rustflags, " ")) + pctx.StaticVariable("DarwinToolchainArm64LinkFlags", strings.Join(darwinArm64Linkflags, " ")) + pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " ")) + pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " ")) + } } diff --git a/rust/coverage.go b/rust/coverage.go index e0e919c69..91a78060d 100644 --- a/rust/coverage.go +++ b/rust/coverage.go @@ -47,7 +47,7 @@ func (cov *coverage) deps(ctx DepsContext, deps Deps) Deps { // no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency. if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() { - ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}, rlibDepTag, ProfilerBuiltins) + ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}, rlibDepTag, ProfilerBuiltins) } } diff --git a/rust/library.go b/rust/library.go index ba73f27fd..50d5a72ae 100644 --- a/rust/library.go +++ b/rust/library.go @@ -20,6 +20,8 @@ import ( "regexp" "strings" + "github.com/google/blueprint" + "android/soong/android" "android/soong/cc" ) @@ -692,31 +694,28 @@ func validateLibraryStem(ctx BaseModuleContext, filename string, crate_name stri } } -// LibraryMutator mutates the libraries into variants according to the -// build{Rlib,Dylib} attributes. -func LibraryMutator(mctx android.BottomUpMutatorContext) { - // Only mutate on Rust libraries. - m, ok := mctx.Module().(*Module) +type libraryTransitionMutator struct{} + +func (libraryTransitionMutator) Split(ctx android.BaseModuleContext) []string { + m, ok := ctx.Module().(*Module) if !ok || m.compiler == nil { - return + return []string{""} } library, ok := m.compiler.(libraryInterface) if !ok { - return + return []string{""} } // Don't produce rlib/dylib/source variants for shared or static variants if library.shared() || library.static() { - return + return []string{""} } var variants []string // The source variant is used for SourceProvider modules. The other variants (i.e. rlib and dylib) // depend on this variant. It must be the first variant to be declared. - sourceVariant := false if m.sourceProvider != nil { - variants = append(variants, "source") - sourceVariant = true + variants = append(variants, sourceVariation) } if library.buildRlib() { variants = append(variants, rlibVariation) @@ -726,92 +725,134 @@ func LibraryMutator(mctx android.BottomUpMutatorContext) { } if len(variants) == 0 { - return + return []string{""} } - modules := mctx.CreateLocalVariations(variants...) - - // The order of the variations (modules) matches the variant names provided. Iterate - // through the new variation modules and set their mutated properties. - var emptyVariant = false - var rlibVariant = false - for i, v := range modules { - switch variants[i] { - case rlibVariation: - v.(*Module).compiler.(libraryInterface).setRlib() - rlibVariant = true - case dylibVariation: - v.(*Module).compiler.(libraryInterface).setDylib() - if v.(*Module).ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation { - // TODO(b/165791368) - // Disable dylib Vendor Ramdisk variations until we support these. - v.(*Module).Disable() - } - case "source": - v.(*Module).compiler.(libraryInterface).setSource() - // The source variant does not produce any library. - // Disable the compilation steps. - v.(*Module).compiler.SetDisabled() - case "": - emptyVariant = true + return variants +} + +func (libraryTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + return "" +} + +func (libraryTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + m, ok := ctx.Module().(*Module) + if !ok || m.compiler == nil { + return "" + } + library, ok := m.compiler.(libraryInterface) + if !ok { + return "" + } + + if incomingVariation == "" { + if m.sourceProvider != nil { + return sourceVariation + } + if library.shared() { + return "" } + if library.buildRlib() { + return rlibVariation + } + if library.buildDylib() { + return dylibVariation + } + } + return incomingVariation +} + +func (libraryTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { + m, ok := ctx.Module().(*Module) + if !ok || m.compiler == nil { + return + } + library, ok := m.compiler.(libraryInterface) + if !ok { + return } - if rlibVariant && library.isFFILibrary() { - // If an rlib variant is set and this is an FFI library, make it the - // default variant so CC can link against it appropriately. - mctx.AliasVariation(rlibVariation) - } else if emptyVariant { - // If there's an empty variant, alias it so it is the default variant - mctx.AliasVariation("") + switch variation { + case rlibVariation: + library.setRlib() + case dylibVariation: + library.setDylib() + if m.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation { + // TODO(b/165791368) + // Disable dylib Vendor Ramdisk variations until we support these. + m.Disable() + } + + case sourceVariation: + library.setSource() + // The source variant does not produce any library. + // Disable the compilation steps. + m.compiler.SetDisabled() } // If a source variant is created, add an inter-variant dependency // between the other variants and the source variant. - if sourceVariant { - sv := modules[0] - for _, v := range modules[1:] { - if !v.Enabled(mctx) { - continue - } - mctx.AddInterVariantDependency(sourceDepTag, v, sv) - } - // Alias the source variation so it can be named directly in "srcs" properties. - mctx.AliasVariation("source") + if m.sourceProvider != nil && variation != sourceVariation { + ctx.AddVariationDependencies( + []blueprint.Variation{ + {"rust_libraries", sourceVariation}, + }, + sourceDepTag, ctx.ModuleName()) } } -func LibstdMutator(mctx android.BottomUpMutatorContext) { - if m, ok := mctx.Module().(*Module); ok && m.compiler != nil && !m.compiler.Disabled() { - switch library := m.compiler.(type) { - case libraryInterface: - // Only create a variant if a library is actually being built. +type libstdTransitionMutator struct{} + +func (libstdTransitionMutator) Split(ctx android.BaseModuleContext) []string { + if m, ok := ctx.Module().(*Module); ok && m.compiler != nil && !m.compiler.Disabled() { + // Only create a variant if a library is actually being built. + if library, ok := m.compiler.(libraryInterface); ok { if library.rlib() && !library.sysroot() { - // If this is a rust_ffi variant it only needs rlib-std if library.isFFILibrary() { - variants := []string{"rlib-std"} - modules := mctx.CreateLocalVariations(variants...) - rlib := modules[0].(*Module) - rlib.compiler.(libraryInterface).setRlibStd() - rlib.Properties.RustSubName += RlibStdlibSuffix - mctx.AliasVariation("rlib-std") + return []string{"rlib-std"} } else { - variants := []string{"rlib-std", "dylib-std"} - modules := mctx.CreateLocalVariations(variants...) - - rlib := modules[0].(*Module) - dylib := modules[1].(*Module) - rlib.compiler.(libraryInterface).setRlibStd() - dylib.compiler.(libraryInterface).setDylibStd() - if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation { - // TODO(b/165791368) - // Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib - // variants are properly supported. - dylib.Disable() - } - rlib.Properties.RustSubName += RlibStdlibSuffix + return []string{"rlib-std", "dylib-std"} } } } } + return []string{""} +} + +func (libstdTransitionMutator) OutgoingTransition(ctx android.OutgoingTransitionContext, sourceVariation string) string { + return "" +} + +func (libstdTransitionMutator) IncomingTransition(ctx android.IncomingTransitionContext, incomingVariation string) string { + if m, ok := ctx.Module().(*Module); ok && m.compiler != nil && !m.compiler.Disabled() { + if library, ok := m.compiler.(libraryInterface); ok { + if library.shared() { + return "" + } + if library.rlib() && !library.sysroot() { + if incomingVariation != "" { + return incomingVariation + } + return "rlib-std" + } + } + } + return "" +} + +func (libstdTransitionMutator) Mutate(ctx android.BottomUpMutatorContext, variation string) { + if variation == "rlib-std" { + rlib := ctx.Module().(*Module) + rlib.compiler.(libraryInterface).setRlibStd() + rlib.Properties.RustSubName += RlibStdlibSuffix + } else if variation == "dylib-std" { + dylib := ctx.Module().(*Module) + dylib.compiler.(libraryInterface).setDylibStd() + if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation { + // TODO(b/165791368) + // Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib + // variants are properly supported. + dylib.Disable() + } + } } diff --git a/rust/rust.go b/rust/rust.go index 9dae75ee5..3402adcc5 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -37,20 +37,24 @@ var pctx = android.NewPackageContext("android/soong/rust") func init() { android.RegisterModuleType("rust_defaults", defaultsFactory) - android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("rust_libraries", LibraryMutator).Parallel() - ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel() - ctx.BottomUp("rust_begin", BeginMutator).Parallel() - }) - android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel() - }) + android.PreDepsMutators(registerPreDepsMutators) + android.PostDepsMutators(registerPostDepsMutators) pctx.Import("android/soong/android") pctx.Import("android/soong/rust/config") pctx.ImportAs("cc_config", "android/soong/cc/config") android.InitRegistrationContext.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory) } +func registerPreDepsMutators(ctx android.RegisterMutatorsContext) { + ctx.Transition("rust_libraries", &libraryTransitionMutator{}) + ctx.Transition("rust_stdlinkage", &libstdTransitionMutator{}) + ctx.BottomUp("rust_begin", BeginMutator).Parallel() +} + +func registerPostDepsMutators(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel() +} + type Flags struct { GlobalRustFlags []string // Flags that apply globally to rust GlobalLinkFlags []string // Flags that apply globally to linker @@ -1128,10 +1132,11 @@ type autoDep struct { } var ( - rlibVariation = "rlib" - dylibVariation = "dylib" - rlibAutoDep = autoDep{variation: rlibVariation, depTag: rlibDepTag} - dylibAutoDep = autoDep{variation: dylibVariation, depTag: dylibDepTag} + sourceVariation = "source" + rlibVariation = "rlib" + dylibVariation = "dylib" + rlibAutoDep = autoDep{variation: rlibVariation, depTag: rlibDepTag} + dylibAutoDep = autoDep{variation: dylibVariation, depTag: dylibDepTag} ) type autoDeppable interface { @@ -1613,7 +1618,6 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { } rlibDepVariations := commonDepVariations - rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""}) if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() { rlibDepVariations = append(rlibDepVariations, @@ -1629,7 +1633,6 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { // dylibs dylibDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: dylibVariation}) - dylibDepVariations = append(dylibDepVariations, blueprint.Variation{Mutator: "link", Variation: ""}) for _, lib := range deps.Dylibs { actx.AddVariationDependencies(dylibDepVariations, dylibDepTag, lib) @@ -1650,7 +1653,6 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { // otherwise select the rlib variant. autoDepVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}) - autoDepVariations = append(autoDepVariations, blueprint.Variation{Mutator: "link", Variation: ""}) if actx.OtherModuleDependencyVariantExists(autoDepVariations, lib) { actx.AddVariationDependencies(autoDepVariations, autoDep.depTag, lib) @@ -1664,8 +1666,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { } else if _, ok := mod.sourceProvider.(*protobufDecorator); ok { for _, lib := range deps.Rustlibs { srcProviderVariations := append(commonDepVariations, - blueprint.Variation{Mutator: "rust_libraries", Variation: "source"}) - srcProviderVariations = append(srcProviderVariations, blueprint.Variation{Mutator: "link", Variation: ""}) + blueprint.Variation{Mutator: "rust_libraries", Variation: sourceVariation}) // Only add rustlib dependencies if they're source providers themselves. // This is used to track which crate names need to be added to the source generated @@ -1681,7 +1682,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { if deps.Stdlibs != nil { if mod.compiler.stdLinkage(ctx) == RlibLinkage { for _, lib := range deps.Stdlibs { - actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}...), + actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...), rlibDepTag, lib) } } else { diff --git a/rust/rust_test.go b/rust/rust_test.go index 0d005d0a2..eeedf3f57 100644 --- a/rust/rust_test.go +++ b/rust/rust_test.go @@ -60,6 +60,7 @@ var rustMockedFiles = android.MockFS{ // testRust returns a TestContext in which a basic environment has been setup. // This environment contains a few mocked files. See rustMockedFiles for the list of these files. func testRust(t *testing.T, bp string) *android.TestContext { + t.Helper() skipTestIfOsNotSupported(t) result := android.GroupFixturePreparers( prepareForRustTest, diff --git a/rust/test.go b/rust/test.go index 3087d8d94..b7ddd06a3 100644 --- a/rust/test.go +++ b/rust/test.go @@ -116,7 +116,8 @@ func (test *testDecorator) compilerProps() []interface{} { } func (test *testDecorator) install(ctx ModuleContext) { - testInstallBase := "/data/local/tests/unrestricted" + // TODO: (b/167308193) Switch to /data/local/tests/unrestricted as the default install base. + testInstallBase := "/data/local/tmp" if ctx.RustModule().InVendorOrProduct() { testInstallBase = "/data/local/tests/vendor" } diff --git a/rust/testing.go b/rust/testing.go index 6ee49a971..32cc82354 100644 --- a/rust/testing.go +++ b/rust/testing.go @@ -200,15 +200,8 @@ func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) { ctx.RegisterModuleType("rust_prebuilt_library", PrebuiltLibraryFactory) ctx.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory) ctx.RegisterModuleType("rust_prebuilt_rlib", PrebuiltRlibFactory) - ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) { - // rust mutators - ctx.BottomUp("rust_libraries", LibraryMutator).Parallel() - ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel() - ctx.BottomUp("rust_begin", BeginMutator).Parallel() - }) + ctx.PreDepsMutators(registerPreDepsMutators) ctx.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton) ctx.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory) - ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel() - }) + ctx.PostDepsMutators(registerPostDepsMutators) } diff --git a/scripts/Android.bp b/scripts/Android.bp index 91aa19560..3d81b83c2 100644 --- a/scripts/Android.bp +++ b/scripts/Android.bp @@ -306,12 +306,6 @@ python_binary_host { } python_binary_host { - name: "buildinfo", - main: "buildinfo.py", - srcs: ["buildinfo.py"], -} - -python_binary_host { name: "extra_install_zips_file_list", main: "extra_install_zips_file_list.py", srcs: ["extra_install_zips_file_list.py"], diff --git a/scripts/buildinfo.py b/scripts/buildinfo.py deleted file mode 100755 index db9920918..000000000 --- a/scripts/buildinfo.py +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/env python3 -# -# 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. -# -"""A tool for generating buildinfo.prop""" - -import argparse -import contextlib -import json -import os -import subprocess - -TEST_KEY_DIR = "build/make/target/product/security" - -def get_build_variant(product_config): - if product_config["Eng"]: - return "eng" - elif product_config["Debuggable"]: - return "userdebug" - else: - return "user" - -def get_build_flavor(product_config): - build_flavor = product_config["DeviceProduct"] + "-" + get_build_variant(product_config) - if "address" in product_config.get("SanitizeDevice", []) and "_asan" not in build_flavor: - build_flavor += "_asan" - return build_flavor - -def get_build_keys(product_config): - default_cert = product_config.get("DefaultAppCertificate", "") - if default_cert == "" or default_cert == os.path.join(TEST_KEY_DIR, "testKey"): - return "test-keys" - return "dev-keys" - -def parse_args(): - """Parse commandline arguments.""" - parser = argparse.ArgumentParser() - parser.add_argument('--build-hostname-file', required=True, type=argparse.FileType('r')), - parser.add_argument('--build-number-file', required=True, type=argparse.FileType('r')) - parser.add_argument('--build-thumbprint-file', type=argparse.FileType('r')) - parser.add_argument('--build-username', required=True) - parser.add_argument('--date-file', required=True, type=argparse.FileType('r')) - parser.add_argument('--platform-preview-sdk-fingerprint-file', - required=True, - type=argparse.FileType('r')) - parser.add_argument('--product-config', required=True, type=argparse.FileType('r')) - parser.add_argument('--out', required=True, type=argparse.FileType('w')) - - option = parser.parse_args() - - product_config = json.load(option.product_config) - build_flags = product_config["BuildFlags"] - - option.build_flavor = get_build_flavor(product_config) - option.build_keys = get_build_keys(product_config) - option.build_id = product_config["BuildId"] - option.build_type = product_config["BuildType"] - option.build_variant = get_build_variant(product_config) - option.build_version_tags = product_config["BuildVersionTags"] - option.cpu_abis = product_config["DeviceAbi"] - option.default_locale = None - if len(product_config.get("ProductLocales", [])) > 0: - option.default_locale = product_config["ProductLocales"][0] - option.default_wifi_channels = product_config.get("ProductDefaultWifiChannels", []) - option.device = product_config["DeviceName"] - option.display_build_number = product_config["DisplayBuildNumber"] - option.platform_base_os = product_config["Platform_base_os"] - option.platform_display_version = product_config["Platform_display_version_name"] - option.platform_min_supported_target_sdk_version = build_flags["RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION"] - option.platform_preview_sdk_version = product_config["Platform_preview_sdk_version"] - option.platform_sdk_version = product_config["Platform_sdk_version"] - option.platform_security_patch = product_config["Platform_security_patch"] - option.platform_version = product_config["Platform_version_name"] - option.platform_version_codename = product_config["Platform_sdk_codename"] - option.platform_version_all_codenames = product_config["Platform_version_active_codenames"] - option.platform_version_known_codenames = product_config["Platform_version_known_codenames"] - option.platform_version_last_stable = product_config["Platform_version_last_stable"] - option.product = product_config["DeviceProduct"] - option.use_vbmeta_digest_in_fingerprint = product_config["BoardUseVbmetaDigestInFingerprint"] - - return option - -def main(): - option = parse_args() - - build_hostname = option.build_hostname_file.read().strip() - build_number = option.build_number_file.read().strip() - build_version_tags_list = option.build_version_tags - if option.build_type == "debug": - build_version_tags_list.append("debug") - build_version_tags_list.append(option.build_keys) - build_version_tags = ",".join(sorted(set(build_version_tags_list))) - - raw_date = option.date_file.read().strip() - date = subprocess.check_output(["date", "-d", f"@{raw_date}"], text=True).strip() - date_utc = subprocess.check_output(["date", "-d", f"@{raw_date}", "+%s"], text=True).strip() - - # build_desc is human readable strings that describe this build. This has the same info as the - # build fingerprint. - # e.g. "aosp_cf_x86_64_phone-userdebug VanillaIceCream MAIN eng.20240319.143939 test-keys" - build_desc = f"{option.product}-{option.build_variant} {option.platform_version} " \ - f"{option.build_id} {build_number} {build_version_tags}" - - platform_preview_sdk_fingerprint = option.platform_preview_sdk_fingerprint_file.read().strip() - - with contextlib.redirect_stdout(option.out): - print("# begin build properties") - print("# autogenerated by buildinfo.py") - - # The ro.build.id will be set dynamically by init, by appending the unique vbmeta digest. - if option.use_vbmeta_digest_in_fingerprint: - print(f"ro.build.legacy.id={option.build_id}") - else: - print(f"ro.build.id?={option.build_id}") - - # ro.build.display.id is shown under Settings -> About Phone - if option.build_variant == "user": - # User builds should show: - # release build number or branch.buld_number non-release builds - - # Dev. branches should have DISPLAY_BUILD_NUMBER set - if option.display_build_number: - print(f"ro.build.display.id?={option.build_id} {build_number} {option.build_keys}") - else: - print(f"ro.build.display.id?={option.build_id} {option.build_keys}") - else: - # Non-user builds should show detailed build information (See build desc above) - print(f"ro.build.display.id?={build_desc}") - print(f"ro.build.version.incremental={build_number}") - print(f"ro.build.version.sdk={option.platform_sdk_version}") - print(f"ro.build.version.preview_sdk={option.platform_preview_sdk_version}") - print(f"ro.build.version.preview_sdk_fingerprint={platform_preview_sdk_fingerprint}") - print(f"ro.build.version.codename={option.platform_version_codename}") - print(f"ro.build.version.all_codenames={','.join(option.platform_version_all_codenames)}") - print(f"ro.build.version.known_codenames={option.platform_version_known_codenames}") - print(f"ro.build.version.release={option.platform_version_last_stable}") - print(f"ro.build.version.release_or_codename={option.platform_version}") - print(f"ro.build.version.release_or_preview_display={option.platform_display_version}") - print(f"ro.build.version.security_patch={option.platform_security_patch}") - print(f"ro.build.version.base_os={option.platform_base_os}") - print(f"ro.build.version.min_supported_target_sdk={option.platform_min_supported_target_sdk_version}") - print(f"ro.build.date={date}") - print(f"ro.build.date.utc={date_utc}") - print(f"ro.build.type={option.build_variant}") - print(f"ro.build.user={option.build_username}") - print(f"ro.build.host={build_hostname}") - # TODO: Remove any tag-related optional property declarations once the goals - # from go/arc-android-sigprop-changes have been achieved. - print(f"ro.build.tags?={build_version_tags}") - # ro.build.flavor are used only by the test harness to distinguish builds. - # Only add _asan for a sanitized build if it isn't already a part of the - # flavor (via a dedicated lunch config for example). - print(f"ro.build.flavor={option.build_flavor}") - - # These values are deprecated, use "ro.product.cpu.abilist" - # instead (see below). - print(f"# ro.product.cpu.abi and ro.product.cpu.abi2 are obsolete,") - print(f"# use ro.product.cpu.abilist instead.") - print(f"ro.product.cpu.abi={option.cpu_abis[0]}") - if len(option.cpu_abis) > 1: - print(f"ro.product.cpu.abi2={option.cpu_abis[1]}") - - if option.default_locale: - print(f"ro.product.locale={option.default_locale}") - print(f"ro.wifi.channels={' '.join(option.default_wifi_channels)}") - - print(f"# ro.build.product is obsolete; use ro.product.device") - print(f"ro.build.product={option.device}") - - print(f"# Do not try to parse description or thumbprint") - print(f"ro.build.description?={build_desc}") - if option.build_thumbprint_file: - build_thumbprint = option.build_thumbprint_file.read().strip() - print(f"ro.build.thumbprint={build_thumbprint}") - - print(f"# end build properties") - -if __name__ == "__main__": - main() diff --git a/scripts/check_prebuilt_presigned_apk.py b/scripts/check_prebuilt_presigned_apk.py index abedfb770..abab2e146 100755 --- a/scripts/check_prebuilt_presigned_apk.py +++ b/scripts/check_prebuilt_presigned_apk.py @@ -37,7 +37,7 @@ def has_preprocessed_issues(args, *, fail=False): sys.exit(args.apk + ': Contains compressed JNI libraries') return True # It's ok for non-privileged apps to have compressed dex files, see go/gms-uncompressed-jni-slides - if args.privileged: + if args.privileged and args.uncompress_priv_app_dex: if info.filename.endswith('.dex') and info.compress_type != zipfile.ZIP_STORED: if fail: sys.exit(args.apk + ': Contains compressed dex files and is privileged') @@ -52,6 +52,7 @@ def main(): parser.add_argument('--skip-preprocessed-apk-checks', action = 'store_true', help = "the value of the soong property with the same name") parser.add_argument('--preprocessed', action = 'store_true', help = "the value of the soong property with the same name") parser.add_argument('--privileged', action = 'store_true', help = "the value of the soong property with the same name") + parser.add_argument('--uncompress-priv-app-dex', action = 'store_true', help = "the value of the product variable with the same name") parser.add_argument('apk', help = "the apk to check") parser.add_argument('stampfile', help = "a file to touch if successful") args = parser.parse_args() diff --git a/scripts/gen-kotlin-build-file.py b/scripts/gen-kotlin-build-file.py index 99afdca75..8b7876f64 100644 --- a/scripts/gen-kotlin-build-file.py +++ b/scripts/gen-kotlin-build-file.py @@ -37,7 +37,7 @@ def parse_args(): parser.add_argument('--out', dest='out', help='file to which the module.xml contents will be written.') parser.add_argument('--classpath', dest='classpath', action='append', default=[], - help='classpath to pass to kotlinc.') + help='file containing classpath to pass to kotlinc.') parser.add_argument('--name', dest='name', help='name of the module.') parser.add_argument('--out_dir', dest='out_dir', @@ -65,8 +65,8 @@ def main(): f.write(' <module name="%s" type="java-production" outputDir="%s">\n' % (args.name, args.out_dir or '')) # Print classpath entries - for c in args.classpath: - for entry in c.split(':'): + for classpath_rsp_file in args.classpath: + for entry in NinjaRspFileReader(classpath_rsp_file): path = os.path.abspath(entry) f.write(' <classpath path="%s"/>\n' % path) diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py index 799e00b19..c08a3fd42 100644 --- a/scripts/gen_build_prop.py +++ b/scripts/gen_build_prop.py @@ -129,16 +129,16 @@ def generate_common_build_props(args): print(f"ro.product.{partition}.name={config['DeviceProduct']}") if partition != "system": - if config["ModelForAttestation"]: - print(f"ro.product.model_for_attestation={config['ModelForAttestation']}") - if config["BrandForAttestation"]: - print(f"ro.product.brand_for_attestation={config['BrandForAttestation']}") - if config["NameForAttestation"]: - print(f"ro.product.name_for_attestation={config['NameForAttestation']}") - if config["DeviceForAttestation"]: - print(f"ro.product.device_for_attestation={config['DeviceForAttestation']}") - if config["ManufacturerForAttestation"]: - print(f"ro.product.manufacturer_for_attestation={config['ManufacturerForAttestation']}") + if config["ProductModelForAttestation"]: + print(f"ro.product.model_for_attestation={config['ProductModelForAttestation']}") + if config["ProductBrandForAttestation"]: + print(f"ro.product.brand_for_attestation={config['ProductBrandForAttestation']}") + if config["ProductNameForAttestation"]: + print(f"ro.product.name_for_attestation={config['ProductNameForAttestation']}") + if config["ProductDeviceForAttestation"]: + print(f"ro.product.device_for_attestation={config['ProductDeviceForAttestation']}") + if config["ProductManufacturerForAttestation"]: + print(f"ro.product.manufacturer_for_attestation={config['ProductManufacturerForAttestation']}") if config["ZygoteForce64"]: if partition == "vendor": @@ -188,7 +188,7 @@ def generate_build_info(args): # Dev. branches should have DISPLAY_BUILD_NUMBER set if config["DisplayBuildNumber"]: - print(f"ro.build.display.id?={config['BuildId']} {config['BuildNumber']} {config['BuildKeys']}") + print(f"ro.build.display.id?={config['BuildId']}.{config['BuildNumber']} {config['BuildKeys']}") else: print(f"ro.build.display.id?={config['BuildId']} {config['BuildKeys']}") else: @@ -237,7 +237,7 @@ def generate_build_info(args): print(f"# Do not try to parse description or thumbprint") print(f"ro.build.description?={config['BuildDesc']}") - if "build_thumbprint" in config: + if "BuildThumbprint" in config: print(f"ro.build.thumbprint={config['BuildThumbprint']}") print(f"# end build properties") @@ -279,7 +279,7 @@ def append_additional_system_props(args): config = args.config # Add the product-defined properties to the build properties. - if config["PropertySplitEnabled"] or config["VendorImageFileSystemType"]: + if not config["PropertySplitEnabled"] or not config["VendorImageFileSystemType"]: if "PRODUCT_PROPERTY_OVERRIDES" in config: props += config["PRODUCT_PROPERTY_OVERRIDES"] @@ -311,6 +311,7 @@ def append_additional_system_props(args): props.append("ro.postinstall.fstab.prefix=/system") enable_target_debugging = True + enable_dalvik_lock_contention_logging = True if config["BuildVariant"] == "user" or config["BuildVariant"] == "userdebug": # Target is secure in user builds. props.append("ro.secure=1") @@ -320,6 +321,12 @@ def append_additional_system_props(args): # Disable debugging in plain user builds. props.append("ro.adb.secure=1") enable_target_debugging = False + enable_dalvik_lock_contention_logging = False + else: + # Disable debugging in userdebug builds if PRODUCT_NOT_DEBUGGABLE_IN_USERDEBUG + # is set. + if config["ProductNotDebuggableInUserdebug"]: + enable_target_debugging = False # Disallow mock locations by default for user builds props.append("ro.allow.mock.location=0") @@ -331,10 +338,11 @@ def append_additional_system_props(args): # Allow mock locations by default for non user builds props.append("ro.allow.mock.location=1") - if enable_target_debugging: + if enable_dalvik_lock_contention_logging: # Enable Dalvik lock contention logging. props.append("dalvik.vm.lockprof.threshold=500") + if enable_target_debugging: # Target is more debuggable and adbd is on by default props.append("ro.debuggable=1") else: @@ -416,7 +424,7 @@ def append_additional_vendor_props(args): # This must not be defined for the non-GRF devices. # The values of the GRF properties will be verified by post_process_props.py if config["BoardShippingApiLevel"]: - props.append(f"ro.board.first_api_level={config['ProductShippingApiLevel']}") + props.append(f"ro.board.first_api_level={config['BoardShippingApiLevel']}") # Build system set BOARD_API_LEVEL to show the api level of the vendor API surface. # This must not be altered outside of build system. @@ -464,6 +472,8 @@ def append_additional_product_props(args): # Add the 16K developer args if it is defined for the product. props.append(f"ro.product.build.16k_page.enabled={'true' if config['Product16KDeveloperOption'] else 'false'}") + props.append(f"ro.product.page_size={16384 if config['TargetBoots16K'] else 4096}") + props.append(f"ro.build.characteristics={config['AAPTCharacteristics']}") if "AbOtaUpdater" in config and config["AbOtaUpdater"]: @@ -475,6 +485,9 @@ def append_additional_product_props(args): if config["NoBionicPageSizeMacro"]: props.append(f"ro.product.build.no_bionic_page_size_macro=true") + # This is a temporary system property that controls the ART module. The plan is + # to remove it by Aug 2025, at which time Mainline updates of the ART module + # will ignore it as well. # If the value is "default", it will be mangled by post_process_props.py. props.append(f"ro.dalvik.vm.enable_uffd_gc={config['EnableUffdGc']}") @@ -500,6 +513,15 @@ def build_system_prop(args): build_prop(args, gen_build_info=True, gen_common_build_props=True, variables=variables) +def build_system_ext_prop(args): + config = args.config + + # Order matters here. When there are duplicates, the last one wins. + # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter + variables = ["PRODUCT_SYSTEM_EXT_PROPERTIES"] + + build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables) + ''' def build_vendor_prop(args): config = args.config @@ -517,6 +539,7 @@ def build_vendor_prop(args): ] build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables) +''' def build_product_prop(args): config = args.config @@ -527,8 +550,32 @@ def build_product_prop(args): "ADDITIONAL_PRODUCT_PROPERTIES", "PRODUCT_PRODUCT_PROPERTIES", ] + + gen_common_build_props = True + + # Skip common /product properties generation if device released before R and + # has no product partition. This is the first part of the check. + if config["Shipping_api_level"] and int(config["Shipping_api_level"]) < 30: + gen_common_build_props = False + + # The second part of the check - always generate common properties for the + # devices with product partition regardless of shipping level. + if config["UsesProductImage"]: + gen_common_build_props = True + + build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables) + + if config["OemProperties"]: + print("####################################") + print("# PRODUCT_OEM_PROPERTIES") + print("####################################") + + for prop in config["OemProperties"]: + print(f"import /oem/oem.prop {prop}") + +def build_odm_prop(args): + variables = ["ADDITIONAL_ODM_PROPERTIES", "PRODUCT_ODM_PROPERTIES"] build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables) -''' def build_prop(args, gen_build_info, gen_common_build_props, variables): config = args.config @@ -550,16 +597,19 @@ def main(): args = parse_args() with contextlib.redirect_stdout(args.out): - if args.partition == "system": - build_system_prop(args) - ''' - elif args.partition == "vendor": - build_vendor_prop(args) - elif args.partition == "product": - build_product_prop(args) - ''' - else: - sys.exit(f"not supported partition {args.partition}") + match args.partition: + case "system": + build_system_prop(args) + case "system_ext": + build_system_ext_prop(args) + case "odm": + build_odm_prop(args) + case "product": + build_product_prop(args) + # case "vendor": # NOT IMPLEMENTED + # build_vendor_prop(args) + case _: + sys.exit(f"not supported partition {args.partition}") if __name__ == "__main__": main() diff --git a/scripts/run-ckati.sh b/scripts/run-ckati.sh index b670c8af8..70f5a7a81 100755 --- a/scripts/run-ckati.sh +++ b/scripts/run-ckati.sh @@ -73,12 +73,12 @@ prebuilts/build-tools/linux-x86/bin/ckati \ --writable out/ \ -f build/make/core/main.mk \ "${tracing[@]}" \ - ANDROID_JAVA_HOME=prebuilts/jdk/jdk17/linux-x86 \ + ANDROID_JAVA_HOME=prebuilts/jdk/jdk21/linux-x86 \ ASAN_SYMBOLIZER_PATH=$PWD/prebuilts/clang/host/linux-x86/llvm-binutils-stable/llvm-symbolizer \ BUILD_DATETIME_FILE="$timestamp_file" \ BUILD_HOSTNAME=$(hostname) \ BUILD_USERNAME="$USER" \ - JAVA_HOME=$PWD/prebuilts/jdk/jdk17/linux-x86 \ + JAVA_HOME=$PWD/prebuilts/jdk/jdk21/linux-x86 \ KATI_PACKAGE_MK_DIR="{$out}/target/product/${target_device}/CONFIG/kati_packaging" \ OUT_DIR="$out" \ PATH="$PWD/prebuilts/build-tools/path/linux-x86:$PWD/${out}/.path" \ diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go index 7048a15b7..7ee548f34 100644 --- a/sdk/bootclasspath_fragment_sdk_test.go +++ b/sdk/bootclasspath_fragment_sdk_test.go @@ -276,11 +276,7 @@ func testSnapshotWithBootClasspathFragment_Contents(t *testing.T, sdk string, co // Add a platform_bootclasspath that depends on the fragment. fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), // Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi // file creation. java.FixtureConfigureBootJars("platform:foo"), @@ -799,11 +795,7 @@ func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { // Add a platform_bootclasspath that depends on the fragment. fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), android.MockFS{ "my-blocked.txt": nil, @@ -1053,11 +1045,7 @@ func testSnapshotWithBootClasspathFragment_MinSdkVersion(t *testing.T, targetBui variables.Platform_version_active_codenames = []string{"VanillaIceCream"} }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), android.FixtureWithRootAndroidBp(` sdk { diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index 5d76930b4..25839b8a1 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -2205,6 +2205,7 @@ cc_prebuilt_library_shared { "3", "current", ], + symbol_file: "stubslib.map.txt", }, arch: { arm64: { @@ -2268,6 +2269,7 @@ cc_prebuilt_library_shared { "3", "current", ], + symbol_file: "stubslib.map.txt", }, target: { host: { diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index 0a5483b07..0fb5c70d2 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -45,11 +45,7 @@ var prepareForSdkTestWithJavaSdkLibrary = android.GroupFixturePreparers( java.PrepareForTestWithJavaDefaultModules, java.PrepareForTestWithJavaSdkLibraryFiles, java.FixtureWithLastReleaseApis("myjavalib"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ) // Contains tests for SDK members provided by the java package. @@ -666,11 +662,7 @@ func TestSnapshotWithJavaSystemModules(t *testing.T) { "1": {"myjavalib"}, "2": {"myjavalib"}, }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` sdk { name: "mysdk", @@ -1313,11 +1305,7 @@ java_sdk_library_import { func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) { result := android.GroupFixturePreparers( prepareForSdkTestWithJavaSdkLibrary, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTestWithBp(t, ` sdk { name: "mysdk", diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 4894210d4..057b370d3 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -457,11 +457,7 @@ java_import { android.FixtureMergeEnv(map[string]string{ "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S", }), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } - }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTest(t) CheckSnapshot(t, result, "mysdk", "", @@ -573,11 +569,9 @@ java_sdk_library_import { "SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S", }), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.BuildFlags = map[string]string{ - "RELEASE_HIDDEN_API_EXPORTABLE_STUBS": "true", - } variables.Platform_version_active_codenames = []string{"UpsideDownCake", "Tiramisu", "S-V2"} }), + android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"), ).RunTest(t) CheckSnapshot(t, result, "mysdk", "", diff --git a/snapshot/Android.bp b/snapshot/Android.bp index 6cb318e53..ae1869a26 100644 --- a/snapshot/Android.bp +++ b/snapshot/Android.bp @@ -14,13 +14,8 @@ bootstrap_go_package { // Source file name convention is to include _snapshot as a // file suffix for files that are generating snapshots. srcs: [ - "host_fake_snapshot.go", - "host_snapshot.go", "snapshot_base.go", "util.go", ], - testSrcs: [ - "host_test.go", - ], pluginFor: ["soong_build"], } diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go deleted file mode 100644 index 278247e10..000000000 --- a/snapshot/host_fake_snapshot.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2021 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 snapshot - -import ( - "encoding/json" - "path/filepath" - - "android/soong/android" -) - -// The host_snapshot module creates a snapshot of host tools to be used -// in a minimal source tree. In order to create the host_snapshot the -// user must explicitly list the modules to be included. The -// host-fake-snapshot, defined in this file, is a utility to help determine -// which host modules are being used in the minimal source tree. -// -// The host-fake-snapshot is designed to run in a full source tree and -// will result in a snapshot that contains an empty file for each host -// tool found in the tree. The fake snapshot is only used to determine -// the host modules that the minimal source tree depends on, hence the -// snapshot uses an empty file for each module and saves on having to -// actually build any tool to generate the snapshot. The fake snapshot -// is compatible with an actual host_snapshot and is installed into a -// minimal source tree via the development/vendor_snapshot/update.py -// script. -// -// After generating the fake snapshot and installing into the minimal -// source tree, the dependent modules are determined via the -// development/vendor_snapshot/update.py script (see script for more -// information). These modules are then used to define the actual -// host_snapshot to be used. This is a similar process to the other -// snapshots (vendor, recovery,...) -// -// Example -// -// Full source tree: -// 1/ Generate fake host snapshot -// -// Minimal source tree: -// 2/ Install the fake host snapshot -// 3/ List the host modules used from the snapshot -// 4/ Remove fake host snapshot -// -// Full source tree: -// 4/ Create host_snapshot with modules identified in step 3 -// -// Minimal source tree: -// 5/ Install host snapshot -// 6/ Build -// -// The host-fake-snapshot is a singleton module, that will be built -// if HOST_FAKE_SNAPSHOT_ENABLE=true. - -func init() { - registerHostSnapshotComponents(android.InitRegistrationContext) -} - -// Add prebuilt information to snapshot data -type hostSnapshotFakeJsonFlags struct { - SnapshotJsonFlags - Prebuilt bool `json:",omitempty"` -} - -func registerHostSnapshotComponents(ctx android.RegistrationContext) { - ctx.RegisterParallelSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton) -} - -type hostFakeSingleton struct { - snapshotDir string - zipFile android.OptionalPath -} - -func (c *hostFakeSingleton) init() { - c.snapshotDir = "host-fake-snapshot" - -} -func HostToolsFakeAndroidSingleton() android.Singleton { - singleton := &hostFakeSingleton{} - singleton.init() - return singleton -} - -func (c *hostFakeSingleton) GenerateBuildActions(ctx android.SingletonContext) { - if !ctx.DeviceConfig().HostFakeSnapshotEnabled() { - return - } - // Find all host binary modules add 'fake' versions to snapshot - var outputs android.Paths - seen := make(map[string]bool) - var jsonData []hostSnapshotFakeJsonFlags - prebuilts := make(map[string]bool) - - ctx.VisitAllModules(func(module android.Module) { - if module.Target().Os != ctx.Config().BuildOSTarget.Os { - return - } - if module.Target().Arch.ArchType != ctx.Config().BuildOSTarget.Arch.ArchType { - return - } - - if android.IsModulePrebuilt(module) { - // Add non-prebuilt module name to map of prebuilts - prebuilts[android.RemoveOptionalPrebuiltPrefix(module.Name())] = true - return - } - if !module.Enabled(ctx) || module.IsHideFromMake() { - return - } - apexInfo, _ := android.SingletonModuleProvider(ctx, module, android.ApexInfoProvider) - if !apexInfo.IsForPlatform() { - return - } - path := hostToolPath(module) - if path.Valid() && path.String() != "" { - outFile := filepath.Join(c.snapshotDir, path.String()) - if !seen[outFile] { - seen[outFile] = true - outputs = append(outputs, WriteStringToFileRule(ctx, "", outFile)) - jsonData = append(jsonData, hostSnapshotFakeJsonFlags{*hostJsonDesc(ctx, module), false}) - } - } - }) - // Update any module prebuilt information - for idx := range jsonData { - if _, ok := prebuilts[jsonData[idx].ModuleName]; ok { - // Prebuilt exists for this module - jsonData[idx].Prebuilt = true - } - } - marsh, err := json.Marshal(jsonData) - if err != nil { - ctx.Errorf("host fake snapshot json marshal failure: %#v", err) - return - } - outputs = append(outputs, WriteStringToFileRule(ctx, string(marsh), filepath.Join(c.snapshotDir, "host_snapshot.json"))) - c.zipFile = zipSnapshot(ctx, c.snapshotDir, c.snapshotDir, outputs) - -} -func (c *hostFakeSingleton) MakeVars(ctx android.MakeVarsContext) { - if !c.zipFile.Valid() { - return - } - ctx.Phony( - "host-fake-snapshot", - c.zipFile.Path()) - - ctx.DistForGoal( - "host-fake-snapshot", - c.zipFile.Path()) - -} diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go deleted file mode 100644 index 1ecab7ddc..000000000 --- a/snapshot/host_snapshot.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2021 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 snapshot - -import ( - "encoding/json" - "fmt" - "path/filepath" - "sort" - "strings" - - "github.com/google/blueprint" - "github.com/google/blueprint/proptools" - - "android/soong/android" -) - -// -// The host_snapshot module creates a snapshot of the modules defined in -// the deps property. The modules within the deps property (host tools) -// are ones that return a valid path via HostToolPath() of the -// HostToolProvider. The created snapshot contains the binaries and any -// transitive PackagingSpecs of the included host tools, along with a JSON -// meta file. -// -// The snapshot is installed into a source tree via -// development/vendor_snapshot/update.py, the included modules are -// provided as preferred prebuilts. -// -// To determine which tools to include in the host snapshot see -// host_fake_snapshot.go. - -func init() { - registerHostBuildComponents(android.InitRegistrationContext) -} - -func registerHostBuildComponents(ctx android.RegistrationContext) { - ctx.RegisterModuleType("host_snapshot", hostSnapshotFactory) -} - -// Relative installation path -type RelativeInstallPath interface { - RelativeInstallPath() string -} - -type hostSnapshot struct { - android.ModuleBase - android.PackagingBase - - outputFile android.OutputPath - installDir android.InstallPath -} - -type ProcMacro interface { - ProcMacro() bool - CrateName() string -} - -func hostSnapshotFactory() android.Module { - module := &hostSnapshot{} - initHostToolsModule(module) - return module -} -func initHostToolsModule(module *hostSnapshot) { - android.InitPackageModule(module) - android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon) -} - -var dependencyTag = struct { - blueprint.BaseDependencyTag - android.InstallAlwaysNeededDependencyTag - android.PackagingItemAlwaysDepTag -}{} - -func (f *hostSnapshot) DepsMutator(ctx android.BottomUpMutatorContext) { - f.AddDeps(ctx, dependencyTag) -} -func (f *hostSnapshot) installFileName() string { - return f.Name() + ".zip" -} - -// Create zipfile with JSON description, notice files... for dependent modules -func (f *hostSnapshot) CreateMetaData(ctx android.ModuleContext, fileName string) android.OutputPath { - var jsonData []SnapshotJsonFlags - var metaPaths android.Paths - - installedNotices := make(map[string]bool) - metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath - - // Create JSON file based on the direct dependencies - ctx.VisitDirectDeps(func(dep android.Module) { - desc := hostJsonDesc(ctx, dep) - if desc != nil { - jsonData = append(jsonData, *desc) - } - for _, notice := range dep.EffectiveLicenseFiles() { - if _, ok := installedNotices[notice.String()]; !ok { - installedNotices[notice.String()] = true - noticeOut := android.PathForModuleOut(ctx, "NOTICE_FILES", notice.String()).OutputPath - CopyFileToOutputPathRule(pctx, ctx, notice, noticeOut) - metaPaths = append(metaPaths, noticeOut) - } - } - }) - // Sort notice paths and json data for repeatble build - sort.Slice(jsonData, func(i, j int) bool { - return (jsonData[i].ModuleName < jsonData[j].ModuleName) - }) - sort.Slice(metaPaths, func(i, j int) bool { - return (metaPaths[i].String() < metaPaths[j].String()) - }) - - marsh, err := json.Marshal(jsonData) - if err != nil { - ctx.ModuleErrorf("host snapshot json marshal failure: %#v", err) - return android.OutputPath{} - } - - jsonZipFile := android.PathForModuleOut(ctx, "host_snapshot.json").OutputPath - metaPaths = append(metaPaths, jsonZipFile) - rspFile := android.PathForModuleOut(ctx, "host_snapshot.rsp").OutputPath - android.WriteFileRule(ctx, jsonZipFile, string(marsh)) - - builder := android.NewRuleBuilder(pctx, ctx) - - builder.Command(). - BuiltTool("soong_zip"). - FlagWithArg("-C ", android.PathForModuleOut(ctx).OutputPath.String()). - FlagWithOutput("-o ", metaZipFile). - FlagWithRspFileInputList("-r ", rspFile, metaPaths) - builder.Build("zip_meta", fmt.Sprintf("zipping meta data for %s", ctx.ModuleName())) - - return metaZipFile -} - -// Create the host tool zip file -func (f *hostSnapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Create a zip file for the binaries, and a zip of the meta data, then merge zips - depsZipFile := android.PathForModuleOut(ctx, f.Name()+"_deps.zip").OutputPath - modsZipFile := android.PathForModuleOut(ctx, f.Name()+"_mods.zip").OutputPath - f.outputFile = android.PathForModuleOut(ctx, f.installFileName()).OutputPath - - f.installDir = android.PathForModuleInstall(ctx) - - f.CopyDepsToZip(ctx, f.GatherPackagingSpecs(ctx), depsZipFile) - - builder := android.NewRuleBuilder(pctx, ctx) - builder.Command(). - BuiltTool("zip2zip"). - FlagWithInput("-i ", depsZipFile). - FlagWithOutput("-o ", modsZipFile). - Text("**/*:" + proptools.ShellEscape(f.installDir.String())) - - metaZipFile := f.CreateMetaData(ctx, f.Name()+"_meta.zip") - - builder.Command(). - BuiltTool("merge_zips"). - Output(f.outputFile). - Input(metaZipFile). - Input(modsZipFile) - - builder.Build("manifest", fmt.Sprintf("Adding manifest %s", f.installFileName())) - ctx.InstallFile(f.installDir, f.installFileName(), f.outputFile) - -} - -// Implements android.AndroidMkEntriesProvider -func (f *hostSnapshot) AndroidMkEntries() []android.AndroidMkEntries { - return []android.AndroidMkEntries{android.AndroidMkEntries{ - Class: "ETC", - OutputFile: android.OptionalPathForPath(f.outputFile), - DistFiles: android.MakeDefaultDistFiles(f.outputFile), - ExtraEntries: []android.AndroidMkExtraEntriesFunc{ - func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { - entries.SetString("LOCAL_MODULE_PATH", f.installDir.String()) - entries.SetString("LOCAL_INSTALLED_MODULE_STEM", f.installFileName()) - }, - }, - }} -} - -// Get host tools path and relative install string helpers -func hostToolPath(m android.Module) android.OptionalPath { - if provider, ok := m.(android.HostToolProvider); ok { - return provider.HostToolPath() - } - return android.OptionalPath{} - -} -func hostRelativePathString(m android.Module) string { - var outString string - if rel, ok := m.(RelativeInstallPath); ok { - outString = rel.RelativeInstallPath() - } - return outString -} - -// Create JSON description for given module, only create descriptions for binary modules -// and rust_proc_macro modules which provide a valid HostToolPath -func hostJsonDesc(ctx android.ConfigAndErrorContext, m android.Module) *SnapshotJsonFlags { - path := hostToolPath(m) - relPath := hostRelativePathString(m) - procMacro := false - moduleStem := filepath.Base(path.String()) - crateName := "" - - if pm, ok := m.(ProcMacro); ok && pm.ProcMacro() { - procMacro = pm.ProcMacro() - moduleStem = strings.TrimSuffix(moduleStem, filepath.Ext(moduleStem)) - crateName = pm.CrateName() - } - - if path.Valid() && path.String() != "" { - props := &SnapshotJsonFlags{ - ModuleStemName: moduleStem, - Filename: path.String(), - Required: append(m.HostRequiredModuleNames(), m.RequiredModuleNames(ctx)...), - RelativeInstallPath: relPath, - RustProcMacro: procMacro, - CrateName: crateName, - } - props.InitBaseSnapshotProps(m) - return props - } - return nil -} diff --git a/snapshot/host_test.go b/snapshot/host_test.go deleted file mode 100644 index ab9feddfb..000000000 --- a/snapshot/host_test.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2021 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 snapshot - -import ( - "path/filepath" - "testing" - - "android/soong/android" -) - -// host_snapshot and host-fake-snapshot test functions - -type hostTestModule struct { - android.ModuleBase - props struct { - Deps []string - } -} - -func hostTestBinOut(bin string) string { - return filepath.Join("out", "bin", bin) -} - -func (c *hostTestModule) HostToolPath() android.OptionalPath { - return (android.OptionalPathForPath(android.PathForTesting(hostTestBinOut(c.Name())))) -} - -func hostTestModuleFactory() android.Module { - m := &hostTestModule{} - m.AddProperties(&m.props) - android.InitAndroidArchModule(m, android.HostSupported, android.MultilibFirst) - return m -} -func (m *hostTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - builtFile := android.PathForModuleOut(ctx, m.Name()) - dir := ctx.Target().Arch.ArchType.Multilib - installDir := android.PathForModuleInstall(ctx, dir) - ctx.InstallFile(installDir, m.Name(), builtFile) -} - -// Common blueprint used for testing -var hostTestBp = ` - license_kind { - name: "test_notice", - conditions: ["notice"], - } - license { - name: "host_test_license", - visibility: ["//visibility:public"], - license_kinds: [ - "test_notice" - ], - license_text: [ - "NOTICE", - ], - } - component { - name: "foo", - deps: ["bar"], - } - component { - name: "bar", - licenses: ["host_test_license"], - } - ` - -var hostTestModBp = ` - host_snapshot { - name: "test-host-snapshot", - deps: [ - "foo", - ], - } - ` - -var prepareForHostTest = android.GroupFixturePreparers( - android.PrepareForTestWithAndroidBuildComponents, - android.PrepareForTestWithLicenses, - android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { - ctx.RegisterModuleType("component", hostTestModuleFactory) - }), -) - -// Prepare for host_snapshot test -var prepareForHostModTest = android.GroupFixturePreparers( - prepareForHostTest, - android.FixtureWithRootAndroidBp(hostTestBp+hostTestModBp), - android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { - registerHostBuildComponents(ctx) - }), -) - -// Prepare for fake host snapshot test disabled -var prepareForFakeHostTest = android.GroupFixturePreparers( - prepareForHostTest, - android.FixtureWithRootAndroidBp(hostTestBp), - android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { - registerHostSnapshotComponents(ctx) - }), -) - -// Prepare for fake host snapshot test enabled -var prepareForFakeHostTestEnabled = android.GroupFixturePreparers( - prepareForFakeHostTest, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.HostFakeSnapshotEnabled = true - }), -) - -// Validate that a hostSnapshot object is created containing zip files and JSON file -// content of zip file is not validated as this is done by PackagingSpecs -func TestHostSnapshot(t *testing.T) { - result := prepareForHostModTest.RunTest(t) - t.Helper() - ctx := result.TestContext.ModuleForTests("test-host-snapshot", result.Config.BuildOS.String()+"_common") - mod := ctx.Module().(*hostSnapshot) - if ctx.MaybeOutput("host_snapshot.json").Rule == nil { - t.Error("Manifest file not found") - } - zips := []string{"_deps.zip", "_mods.zip", ".zip"} - - for _, zip := range zips { - zFile := mod.Name() + zip - if ctx.MaybeOutput(zFile).Rule == nil { - t.Error("Zip file ", zFile, "not found") - } - - } -} - -// Validate fake host snapshot contains binary modules as well as the JSON meta file -func TestFakeHostSnapshotEnable(t *testing.T) { - result := prepareForFakeHostTestEnabled.RunTest(t) - t.Helper() - bins := []string{"foo", "bar"} - ctx := result.TestContext.SingletonForTests("host-fake-snapshot") - if ctx.MaybeOutput(filepath.Join("host-fake-snapshot", "host_snapshot.json")).Rule == nil { - t.Error("Manifest file not found") - } - for _, bin := range bins { - if ctx.MaybeOutput(filepath.Join("host-fake-snapshot", hostTestBinOut(bin))).Rule == nil { - t.Error("Binary file ", bin, "not found") - } - - } -} - -// Validate not fake host snapshot if HostFakeSnapshotEnabled has not been set to true -func TestFakeHostSnapshotDisable(t *testing.T) { - result := prepareForFakeHostTest.RunTest(t) - t.Helper() - ctx := result.TestContext.SingletonForTests("host-fake-snapshot") - if len(ctx.AllOutputs()) != 0 { - t.Error("Fake host snapshot not empty when disabled") - } - -} diff --git a/tests/run_tool_with_logging_test.py b/tests/run_tool_with_logging_test.py index 57a6d6296..1a946a1f8 100644 --- a/tests/run_tool_with_logging_test.py +++ b/tests/run_tool_with_logging_test.py @@ -193,7 +193,7 @@ class RunToolWithLoggingTest(unittest.TestCase): logger_path = self._import_executable("tool_event_logger") self._run_script_and_wait(f""" - TMPDIR="{self.working_dir.name}" + export TMPDIR="{self.working_dir.name}" export ANDROID_TOOL_LOGGER="{logger_path}" export ANDROID_TOOL_LOGGER_EXTRA_ARGS="--dry_run" {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} arg1 arg2 @@ -206,7 +206,7 @@ class RunToolWithLoggingTest(unittest.TestCase): logger_path = self._import_executable("tool_event_logger") self._run_script_and_wait(f""" - TMPDIR="{self.working_dir.name}" + export TMPDIR="{self.working_dir.name}" export ANDROID_TOOL_LOGGER="{logger_path}" export ANDROID_TOOL_LOGGER_EXTRA_ARGS="--dry_run" {self.logging_script_path} "FAKE_TOOL" {test_tool.executable} --tool-arg1 diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh index 8dc16301c..04718537d 100755 --- a/tests/sbom_test.sh +++ b/tests/sbom_test.sh @@ -70,13 +70,14 @@ function test_sbom_aosp_cf_x86_64_phone { # m droid, build sbom later in case additional dependencies might be built and included in partition images. run_soong "${out_dir}" "droid dump.erofs lz4" + soong_sbom_out=$out_dir/soong/sbom/$target_product product_out=$out_dir/target/product/vsoc_x86_64 sbom_test=$product_out/sbom_test mkdir -p $sbom_test cp $product_out/*.img $sbom_test # m sbom - run_soong "${out_dir}" sbom + run_soong "${out_dir}" "sbom" # Generate installed file list from .img files in PRODUCT_OUT dump_erofs=$out_dir/host/linux-x86/bin/dump.erofs @@ -117,7 +118,7 @@ function test_sbom_aosp_cf_x86_64_phone { for f in $EROFS_IMAGES; do partition_name=$(basename $f | cut -d. -f1) file_list_file="${sbom_test}/sbom-${partition_name}-files.txt" - files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt" + files_in_soong_spdx_file="${sbom_test}/soong-sbom-${partition_name}-files-in-spdx.txt" rm "$file_list_file" > /dev/null 2>&1 || true all_dirs="/" while [ ! -z "$all_dirs" ]; do @@ -145,22 +146,23 @@ function test_sbom_aosp_cf_x86_64_phone { done sort -n -o "$file_list_file" "$file_list_file" - grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_spdx_file" + # Diff the file list from image and file list in SBOM created by Soong + grep "FileName: /${partition_name}/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: //' > "$files_in_soong_spdx_file" if [ "$partition_name" = "system" ]; then # system partition is mounted to /, so include FileName starts with /root/ too. - grep "FileName: /root/" $product_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_spdx_file" + grep "FileName: /root/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: \/root//' >> "$files_in_soong_spdx_file" fi - sort -n -o "$files_in_spdx_file" "$files_in_spdx_file" + sort -n -o "$files_in_soong_spdx_file" "$files_in_soong_spdx_file" - echo ============ Diffing files in $f and SBOM - diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name" "" + echo ============ Diffing files in $f and SBOM created by Soong + diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" "" done RAMDISK_IMAGES="$product_out/ramdisk.img" for f in $RAMDISK_IMAGES; do partition_name=$(basename $f | cut -d. -f1) file_list_file="${sbom_test}/sbom-${partition_name}-files.txt" - files_in_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-spdx.txt" + files_in_soong_spdx_file="${sbom_test}/sbom-${partition_name}-files-in-soong-spdx.txt" # lz4 decompress $f to stdout # cpio list all entries like ls -l # grep filter normal files and symlinks @@ -168,13 +170,15 @@ function test_sbom_aosp_cf_x86_64_phone { # sed remove partition name from entry names $lz4 -c -d $f | cpio -tv 2>/dev/null | grep '^[-l]' | awk -F ' ' '{print $9}' | sed "s:^:/$partition_name/:" | sort -n > "$file_list_file" - grep "FileName: /${partition_name}/" $product_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_spdx_file" + grep "FileName: /${partition_name}/" $soong_sbom_out/sbom.spdx | sed 's/^FileName: //' | sort -n > "$files_in_soong_spdx_file" - echo ============ Diffing files in $f and SBOM - diff_files "$file_list_file" "$files_in_spdx_file" "$partition_name" "" + echo ============ Diffing files in $f and SBOM created by Soong + diff_files "$file_list_file" "$files_in_soong_spdx_file" "$partition_name" "" done - verify_package_verification_code "$product_out/sbom.spdx" + verify_package_verification_code "$soong_sbom_out/sbom.spdx" + + verify_packages_licenses "$soong_sbom_out/sbom.spdx" # Teardown cleanup "${out_dir}" @@ -213,6 +217,41 @@ function verify_package_verification_code { fi } +function verify_packages_licenses { + local sbom_file="$1"; shift + + num_of_packages=$(grep 'PackageName:' $sbom_file | wc -l) + num_of_declared_licenses=$(grep 'PackageLicenseDeclared:' $sbom_file | wc -l) + if [ "$num_of_packages" = "$num_of_declared_licenses" ] + then + echo "Number of packages with declared license is correct." + else + echo "Number of packages with declared license is WRONG." + exit 1 + fi + + # PRODUCT and 7 prebuilt packages have "PackageLicenseDeclared: NOASSERTION" + # All other packages have declared licenses + num_of_packages_with_noassertion_license=$(grep 'PackageLicenseDeclared: NOASSERTION' $sbom_file | wc -l) + if [ $num_of_packages_with_noassertion_license = 15 ] + then + echo "Number of packages with NOASSERTION license is correct." + else + echo "Number of packages with NOASSERTION license is WRONG." + exit 1 + fi + + num_of_files=$(grep 'FileName:' $sbom_file | wc -l) + num_of_concluded_licenses=$(grep 'LicenseConcluded:' $sbom_file | wc -l) + if [ "$num_of_files" = "$num_of_concluded_licenses" ] + then + echo "Number of files with concluded license is correct." + else + echo "Number of files with concluded license is WRONG." + exit 1 + fi +} + function test_sbom_unbundled_apex { # Setup out_dir="$(setup)" @@ -274,7 +313,7 @@ function test_sbom_unbundled_apk { target_product=aosp_cf_x86_64_phone target_release=trunk_staging -target_build_variant=userdebug +target_build_variant=eng for i in "$@"; do case $i in TARGET_PRODUCT=*) diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go index f9622d337..ef181317d 100644 --- a/tradefed_modules/test_module_config.go +++ b/tradefed_modules/test_module_config.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "slices" "strings" "github.com/google/blueprint" @@ -174,6 +175,20 @@ func (m *testModuleConfigModule) validateTestSuites(ctx android.ModuleContext) b return false } + var extra_derived_suites []string + // Ensure all suites listed are also in base. + for _, s := range m.tradefedProperties.Test_suites { + if !slices.Contains(m.provider.TestSuites, s) { + extra_derived_suites = append(extra_derived_suites, s) + } + } + if len(extra_derived_suites) != 0 { + ctx.ModuleErrorf("Suites: [%s] listed but do not exist in base module: %s", + strings.Join(extra_derived_suites, ", "), + *m.tradefedProperties.Base) + return false + } + return true } diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go index 97179f586..1510a0331 100644 --- a/tradefed_modules/test_module_config_test.go +++ b/tradefed_modules/test_module_config_test.go @@ -40,6 +40,7 @@ const bp = ` name: "base", sdk_version: "current", data: [":HelperApp", "data/testfile"], + test_suites: ["general-tests"], } test_module_config { @@ -160,7 +161,7 @@ func TestModuleConfigBadBaseShouldFailWithGeneralMessage(t *testing.T) { java.PrepareForTestWithJavaDefaultModules, android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents), ).ExtendWithErrorHandler( - android.FixtureExpectsOneErrorPattern("'base' module used as base but it is not a 'android_test' module.")). + android.FixtureExpectsAtLeastOneErrorMatchingPattern("'base' module used as base but it is not a 'android_test' module.")). RunTestWithBp(t, badBp) } @@ -193,6 +194,7 @@ func TestModuleConfigNoFiltersOrAnnotationsShouldFail(t *testing.T) { name: "base", sdk_version: "current", srcs: ["a.java"], + test_suites: ["general-tests"], } test_module_config { @@ -218,6 +220,7 @@ func TestModuleConfigMultipleDerivedTestsWriteDistinctMakeEntries(t *testing.T) sdk_version: "current", srcs: ["a.java"], data: [":HelperApp", "data/testfile"], + test_suites: ["general-tests"], } android_test_helper_app { @@ -362,6 +365,31 @@ func TestModuleConfigHostNeedsATestSuite(t *testing.T) { RunTestWithBp(t, badBp) } +func TestModuleConfigNonMatchingTestSuitesGiveErrors(t *testing.T) { + badBp := ` + java_test_host { + name: "base", + srcs: ["a.java"], + test_suites: ["general-tests", "some-compat"], + } + + test_module_config_host { + name: "derived_test", + base: "base", + exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"], + include_annotations: ["android.platform.test.annotations.LargeTest"], + test_suites: ["device-tests", "random-suite"], + }` + + android.GroupFixturePreparers( + java.PrepareForTestWithJavaDefaultModules, + android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents), + ).ExtendWithErrorHandler( + // Use \\ to escape bracket so it isn't used as [] set for regex. + android.FixtureExpectsAtLeastOneErrorMatchingPattern("Suites: \\[device-tests, random-suite] listed but do not exist in base module")). + RunTestWithBp(t, badBp) +} + func TestTestOnlyProvider(t *testing.T) { t.Parallel() ctx := android.GroupFixturePreparers( @@ -389,6 +417,7 @@ func TestTestOnlyProvider(t *testing.T) { name: "base", sdk_version: "current", data: ["data/testfile"], + test_suites: ["general-tests"], } java_test_host { diff --git a/ui/build/Android.bp b/ui/build/Android.bp index ee286f68a..fcf29c52f 100644 --- a/ui/build/Android.bp +++ b/ui/build/Android.bp @@ -36,6 +36,7 @@ bootstrap_go_package { "blueprint-bootstrap", "blueprint-microfactory", "soong-android", + "soong-elf", "soong-finder", "soong-remoteexec", "soong-shared", diff --git a/ui/build/build.go b/ui/build/build.go index 03d839237..49ac791ce 100644 --- a/ui/build/build.go +++ b/ui/build/build.go @@ -22,6 +22,7 @@ import ( "sync" "text/template" + "android/soong/elf" "android/soong/ui/metrics" ) @@ -210,9 +211,38 @@ func checkRAM(ctx Context, config Config) { } } +func abfsBuildStarted(ctx Context, config Config) { + abfsBox := config.PrebuiltBuildTool("abfsbox") + cmdArgs := []string{"build-started", "--"} + cmdArgs = append(cmdArgs, config.Arguments()...) + cmd := Command(ctx, config, "abfsbox", abfsBox, cmdArgs...) + cmd.Sandbox = noSandbox + cmd.RunAndPrintOrFatal() +} + +func abfsBuildFinished(ctx Context, config Config, finished bool) { + var errMsg string + if !finished { + errMsg = "build was interrupted" + } + abfsBox := config.PrebuiltBuildTool("abfsbox") + cmdArgs := []string{"build-finished", "-e", errMsg, "--"} + cmdArgs = append(cmdArgs, config.Arguments()...) + cmd := Command(ctx, config, "abfsbox", abfsBox, cmdArgs...) + cmd.RunAndPrintOrFatal() +} + // Build the tree. Various flags in `config` govern which components of // the build to run. func Build(ctx Context, config Config) { + done := false + if config.UseABFS() { + abfsBuildStarted(ctx, config) + defer func() { + abfsBuildFinished(ctx, config, done) + }() + } + ctx.Verboseln("Starting build with args:", config.Arguments()) ctx.Verboseln("Environment:", config.Environment().Environ()) @@ -344,11 +374,23 @@ func Build(ctx Context, config Config) { installCleanIfNecessary(ctx, config) } runNinjaForBuild(ctx, config) + updateBuildIdDir(ctx, config) } if what&RunDistActions != 0 { runDistActions(ctx, config) } + done = true +} + +func updateBuildIdDir(ctx Context, config Config) { + ctx.BeginTrace(metrics.RunShutdownTool, "update_build_id_dir") + defer ctx.EndTrace() + + symbolsDir := filepath.Join(config.ProductOut(), "symbols") + if err := elf.UpdateBuildIdDir(symbolsDir); err != nil { + ctx.Printf("failed to update %s/.build-id: %v", symbolsDir, err) + } } func evaluateWhatToRun(config Config, verboseln func(v ...interface{})) int { diff --git a/ui/build/config.go b/ui/build/config.go index 2470f843d..b8fcb6b5d 100644 --- a/ui/build/config.go +++ b/ui/build/config.go @@ -1270,9 +1270,25 @@ func (c *configImpl) canSupportRBE() bool { if !c.StubbyExists() && strings.Contains(authType, "use_google_prod_creds") { return false } + if c.UseABFS() { + return false + } return true } +func (c *configImpl) UseABFS() bool { + if v, ok := c.environ.Get("NO_ABFS"); ok { + v = strings.ToLower(strings.TrimSpace(v)) + if v == "true" || v == "1" { + return false + } + } + + abfsBox := c.PrebuiltBuildTool("abfsbox") + err := exec.Command(abfsBox, "hash", srcDirFileCheck).Run() + return err == nil +} + func (c *configImpl) UseRBE() bool { // These alternate modes of running Soong do not use RBE / reclient. if c.Queryview() || c.JsonModuleGraph() { @@ -1325,8 +1341,7 @@ func (c *configImpl) rbeDownloadTmpDir() string { } func (c *configImpl) rbeTmpDir() string { - buildTmpDir := shared.TempDirForOutDir(c.SoongOutDir()) - return filepath.Join(buildTmpDir, "rbe") + return filepath.Join(c.SoongOutDir(), "rbe") } func (c *configImpl) rbeCacheDir() string { @@ -1585,6 +1600,23 @@ func (c *configImpl) HostPrebuiltTag() string { } } +func (c *configImpl) KatiBin() string { + binName := "ckati" + if c.UseABFS() { + binName = "ckati-wrap" + } + + return c.PrebuiltBuildTool(binName) +} + +func (c *configImpl) NinjaBin() string { + binName := "ninja" + if c.UseABFS() { + binName = "ninjago" + } + return c.PrebuiltBuildTool(binName) +} + func (c *configImpl) PrebuiltBuildTool(name string) string { if v, ok := c.environ.Get("SANITIZE_HOST"); ok { if sanitize := strings.Fields(v); inList("address", sanitize) { diff --git a/ui/build/config_test.go b/ui/build/config_test.go index b1222fe93..b42edb0c6 100644 --- a/ui/build/config_test.go +++ b/ui/build/config_test.go @@ -22,6 +22,7 @@ import ( "os" "path/filepath" "reflect" + "runtime" "strings" "testing" @@ -1043,12 +1044,13 @@ func TestBuildConfig(t *testing.T) { }, }, { + // RBE is only supported on linux. name: "use rbe", environ: Environment{"USE_RBE=1"}, expectedBuildConfig: &smpb.BuildConfig{ ForceUseGoma: proto.Bool(false), UseGoma: proto.Bool(false), - UseRbe: proto.Bool(true), + UseRbe: proto.Bool(runtime.GOOS == "linux"), NinjaWeightListSource: smpb.BuildConfig_NOT_USED.Enum(), }, }, diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go index e77df44a5..5df3a959b 100644 --- a/ui/build/dumpvars.go +++ b/ui/build/dumpvars.go @@ -93,7 +93,7 @@ func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_ defer tool.Finish() cmd := Command(ctx, config, "dumpvars", - config.PrebuiltBuildTool("ckati"), + config.KatiBin(), "-f", "build/make/core/config.mk", "--color_warnings", "--kati_stats", diff --git a/ui/build/kati.go b/ui/build/kati.go index d599c99a7..a0efd2c88 100644 --- a/ui/build/kati.go +++ b/ui/build/kati.go @@ -84,7 +84,7 @@ func writeValueIfChanged(ctx Context, config Config, dir string, filename string // arguments, and a custom function closure to mutate the environment Kati runs // in. func runKati(ctx Context, config Config, extraSuffix string, args []string, envFunc func(*Environment)) { - executable := config.PrebuiltBuildTool("ckati") + executable := config.KatiBin() // cKati arguments. args = append([]string{ // Instead of executing commands directly, generate a Ninja file. diff --git a/ui/build/ninja.go b/ui/build/ninja.go index 1935e7210..b5e74b422 100644 --- a/ui/build/ninja.go +++ b/ui/build/ninja.go @@ -49,7 +49,7 @@ func runNinjaForBuild(ctx Context, config Config) { nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo) defer nr.Close() - executable := config.PrebuiltBuildTool("ninja") + executable := config.NinjaBin() args := []string{ "-d", "keepdepfile", "-d", "keeprsp", diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go index 81c678d3f..6c9a1ebb9 100644 --- a/ui/build/paths/config.go +++ b/ui/build/paths/config.go @@ -86,28 +86,28 @@ func GetConfig(name string) PathConfig { // This list specifies whether a particular binary from $PATH is allowed to be // run during the build. For more documentation, see path_interposer.go . var Configuration = map[string]PathConfig{ - "bash": Allowed, - "diff": Allowed, - "dlv": Allowed, - "expr": Allowed, - "fuser": Allowed, - "gcert": Allowed, - "gcertstatus": Allowed, - "gcloud": Allowed, - "git": Allowed, - "hexdump": Allowed, - "jar": Allowed, - "java": Allowed, - "javap": Allowed, - "lsof": Allowed, - "openssl": Allowed, - "pstree": Allowed, - "rsync": Allowed, - "sh": Allowed, - "stubby": Allowed, - "tr": Allowed, - "unzip": Allowed, - "zip": Allowed, + "bash": Allowed, + "diff": Allowed, + "dlv": Allowed, + "expr": Allowed, + "fuser": Allowed, + "gcert": Allowed, + "gcertstatus": Allowed, + "gcloud": Allowed, + "git": Allowed, + "hexdump": Allowed, + "jar": Allowed, + "java": Allowed, + "javap": Allowed, + "lsof": Allowed, + "openssl": Allowed, + "pstree": Allowed, + "rsync": Allowed, + "sh": Allowed, + "stubby": Allowed, + "tr": Allowed, + "unzip": Allowed, + "zip": Allowed, // Host toolchain is removed. In-tree toolchain should be used instead. // GCC also can't find cc1 with this implementation. diff --git a/ui/build/rbe.go b/ui/build/rbe.go index 8fa147f70..0a0f95628 100644 --- a/ui/build/rbe.go +++ b/ui/build/rbe.go @@ -65,7 +65,7 @@ func getRBEVars(ctx Context, config Config) map[string]string { "RBE_platform": "container-image=" + remoteexec.DefaultImage, } if config.StartRBE() { - name, err := config.rbeSockAddr(absPath(ctx, config.TempDir())) + name, err := config.rbeSockAddr(absPath(ctx, config.rbeTmpDir())) if err != nil { ctx.Fatalf("Error retrieving socket address: %v", err) return nil diff --git a/ui/build/rbe_test.go b/ui/build/rbe_test.go index 266f76b35..d1b8e2644 100644 --- a/ui/build/rbe_test.go +++ b/ui/build/rbe_test.go @@ -19,6 +19,7 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "strings" "testing" @@ -26,6 +27,10 @@ import ( ) func TestDumpRBEMetrics(t *testing.T) { + // RBE is only supported on linux. + if runtime.GOOS != "linux" { + t.Skip("RBE is only supported on linux") + } ctx := testContext() tests := []struct { description string @@ -82,6 +87,10 @@ func TestDumpRBEMetrics(t *testing.T) { } func TestDumpRBEMetricsErrors(t *testing.T) { + // RBE is only supported on linux. + if runtime.GOOS != "linux" { + t.Skip("RBE is only supported on linux") + } ctx := testContext() tests := []struct { description string diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go index edb3b66d4..c38174c69 100644 --- a/ui/build/sandbox_linux.go +++ b/ui/build/sandbox_linux.go @@ -48,7 +48,11 @@ var ( } ) -const nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail" +const ( + nsjailPath = "prebuilts/build-tools/linux-x86/bin/nsjail" + abfsSrcDir = "/src" + abfsOutDir = "/src/out" +) var sandboxConfig struct { once sync.Once @@ -145,6 +149,22 @@ func (c *Cmd) sandboxSupported() bool { return sandboxConfig.working } +func (c *Cmd) srcDirArg() string { + if !c.config.UseABFS() { + return sandboxConfig.srcDir + } + + return sandboxConfig.srcDir + ":" + abfsSrcDir +} + +func (c *Cmd) outDirArg() string { + if !c.config.UseABFS() { + return sandboxConfig.outDir + } + + return sandboxConfig.outDir + ":" + abfsOutDir +} + func (c *Cmd) wrapSandbox() { wd, _ := os.Getwd() @@ -188,10 +208,10 @@ func (c *Cmd) wrapSandbox() { "-B", "/tmp", // Mount source - c.config.sandboxConfig.SrcDirMountFlag(), sandboxConfig.srcDir, + c.config.sandboxConfig.SrcDirMountFlag(), c.srcDirArg(), //Mount out dir as read-write - "-B", sandboxConfig.outDir, + "-B", c.outDirArg(), // Disable newcgroup for now, since it may require newer kernels // TODO: try out cgroups @@ -200,6 +220,9 @@ func (c *Cmd) wrapSandbox() { // Only log important warnings / errors "-q", } + if c.config.UseABFS() { + sandboxArgs = append(sandboxArgs, "-B", "{ABFS_DIR}") + } // Mount srcDir RW allowlists as Read-Write if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() { diff --git a/ui/build/soong.go b/ui/build/soong.go index e18cc2579..2ccdfecd8 100644 --- a/ui/build/soong.go +++ b/ui/build/soong.go @@ -661,7 +661,7 @@ func runSoong(ctx Context, config Config) { } ninjaArgs = append(ninjaArgs, targets...) - ninjaCmd := config.PrebuiltBuildTool("ninja") + ninjaCmd := config.NinjaBin() if config.useN2 { ninjaCmd = config.PrebuiltBuildTool("n2") } diff --git a/ui/build/test_build.go b/ui/build/test_build.go index 687ad6f61..3faa94d95 100644 --- a/ui/build/test_build.go +++ b/ui/build/test_build.go @@ -15,14 +15,16 @@ package build import ( - "android/soong/ui/metrics" - "android/soong/ui/status" "bufio" "fmt" "path/filepath" + "regexp" "runtime" "sort" "strings" + + "android/soong/ui/metrics" + "android/soong/ui/status" ) // Checks for files in the out directory that have a rule that depends on them but no rule to @@ -84,6 +86,10 @@ func testForDanglingRules(ctx Context, config Config) { // before running soong and ninja. releaseConfigDir := filepath.Join(outDir, "soong", "release-config") + // out/target/product/<xxxxx>/build_fingerprint.txt is a source file created in sysprop.mk + // ^out/target/product/[^/]+/build_fingerprint.txt$ + buildFingerPrintFilePattern := regexp.MustCompile("^" + filepath.Join(outDir, "target", "product") + "/[^/]+/build_fingerprint.txt$") + danglingRules := make(map[string]bool) scanner := bufio.NewScanner(stdout) @@ -100,7 +106,8 @@ func testForDanglingRules(ctx Context, config Config) { line == dexpreoptConfigFilePath || line == buildDatetimeFilePath || line == bpglob || - strings.HasPrefix(line, releaseConfigDir) { + strings.HasPrefix(line, releaseConfigDir) || + buildFingerPrintFilePattern.MatchString(line) { // Leaf node is in one of Soong's bootstrap directories, which do not have // full build rules in the primary build.ninja file. continue diff --git a/ui/terminal/format.go b/ui/terminal/format.go index 241a1ddf7..01f8b0d13 100644 --- a/ui/terminal/format.go +++ b/ui/terminal/format.go @@ -23,26 +23,28 @@ import ( ) type formatter struct { - format string - quiet bool - start time.Time + colorize bool + format string + quiet bool + start time.Time } // newFormatter returns a formatter for formatting output to // the terminal in a format similar to Ninja. // format takes nearly all the same options as NINJA_STATUS. // %c is currently unsupported. -func newFormatter(format string, quiet bool) formatter { +func newFormatter(colorize bool, format string, quiet bool) formatter { return formatter{ - format: format, - quiet: quiet, - start: time.Now(), + colorize: colorize, + format: format, + quiet: quiet, + start: time.Now(), } } func (s formatter) message(level status.MsgLevel, message string) string { if level >= status.ErrorLvl { - return fmt.Sprintf("FAILED: %s", message) + return fmt.Sprintf("%s %s", s.failedString(), message) } else if level > status.StatusLvl { return fmt.Sprintf("%s%s", level.Prefix(), message) } else if level == status.StatusLvl { @@ -127,9 +129,9 @@ func (s formatter) result(result status.ActionResult) string { if result.Error != nil { targets := strings.Join(result.Outputs, " ") if s.quiet || result.Command == "" { - ret = fmt.Sprintf("FAILED: %s\n%s", targets, result.Output) + ret = fmt.Sprintf("%s %s\n%s", s.failedString(), targets, result.Output) } else { - ret = fmt.Sprintf("FAILED: %s\n%s\n%s", targets, result.Command, result.Output) + ret = fmt.Sprintf("%s %s\n%s\n%s", s.failedString(), targets, result.Command, result.Output) } } else if result.Output != "" { ret = result.Output @@ -141,3 +143,11 @@ func (s formatter) result(result status.ActionResult) string { return ret } + +func (s formatter) failedString() string { + failed := "FAILED:" + if s.colorize { + failed = ansi.red() + ansi.bold() + failed + ansi.regular() + } + return failed +} diff --git a/ui/terminal/status.go b/ui/terminal/status.go index 2ad174fee..92f299405 100644 --- a/ui/terminal/status.go +++ b/ui/terminal/status.go @@ -27,9 +27,10 @@ import ( // statusFormat takes nearly all the same options as NINJA_STATUS. // %c is currently unsupported. func NewStatusOutput(w io.Writer, statusFormat string, forceSimpleOutput, quietBuild, forceKeepANSI bool) status.StatusOutput { - formatter := newFormatter(statusFormat, quietBuild) + canUseSmartFormatting := !forceSimpleOutput && isSmartTerminal(w) + formatter := newFormatter(canUseSmartFormatting, statusFormat, quietBuild) - if !forceSimpleOutput && isSmartTerminal(w) { + if canUseSmartFormatting { return NewSmartStatusOutput(w, formatter) } else { return NewSimpleStatusOutput(w, formatter, forceKeepANSI) diff --git a/ui/terminal/status_test.go b/ui/terminal/status_test.go index 8dd180967..991eca04e 100644 --- a/ui/terminal/status_test.go +++ b/ui/terminal/status_test.go @@ -58,7 +58,7 @@ func TestStatusOutput(t *testing.T) { { name: "action with error", calls: actionsWithError, - smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n", + smart: "\r\x1b[1m[ 0% 0/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action1\x1b[0m\x1b[K\r\x1b[1m[ 33% 1/3] action2\x1b[0m\x1b[K\r\x1b[1m[ 66% 2/3] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m f1 f2\ntouch f1 f2\nerror1\nerror2\n\r\x1b[1m[ 66% 2/3] action3\x1b[0m\x1b[K\r\x1b[1m[100% 3/3] action3\x1b[0m\x1b[K\n", simple: "[ 33% 1/3] action1\n[ 66% 2/3] action2\nFAILED: f1 f2\ntouch f1 f2\nerror1\nerror2\n[100% 3/3] action3\n", }, { @@ -70,7 +70,7 @@ func TestStatusOutput(t *testing.T) { { name: "messages", calls: actionsWithMessages, - smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\nFAILED: error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n", + smart: "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\r\x1b[1mstatus\x1b[0m\x1b[K\r\x1b[Kprint\n\x1b[31m\x1b[1mFAILED:\x1b[0m error\n\r\x1b[1m[ 50% 1/2] action2\x1b[0m\x1b[K\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n", simple: "[ 50% 1/2] action1\nstatus\nprint\nFAILED: error\n[100% 2/2] action2\n", }, { @@ -362,7 +362,7 @@ func TestSmartStatusHideAfterFailure(t *testing.T) { stat.Flush() - w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n" + w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nThere was 1 action that completed after the action that failed. See verbose.log.gz for its output.\n" if g := smart.String(); g != w { t.Errorf("want:\n%q\ngot:\n%q", w, g) @@ -407,7 +407,7 @@ func TestSmartStatusHideAfterFailurePlural(t *testing.T) { stat.Flush() - w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\r\x1b[1m[150% 3/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n" + w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action3\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\r\x1b[1m[150% 3/2] action3\x1b[0m\x1b[K\nThere were 2 actions that completed after the action that failed. See verbose.log.gz for their output.\n" if g := smart.String(); g != w { t.Errorf("want:\n%q\ngot:\n%q", w, g) @@ -445,7 +445,7 @@ func TestSmartStatusDontHideErrorAfterFailure(t *testing.T) { stat.Flush() - w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\nFAILED: \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\nFAILED: \nOutput2\n" + w := "\r\x1b[1m[ 0% 0/2] action1\x1b[0m\x1b[K\r\x1b[1m[ 0% 0/2] action2\x1b[0m\x1b[K\r\x1b[1m[ 50% 1/2] action1\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput1\n\r\x1b[1m[100% 2/2] action2\x1b[0m\x1b[K\n\x1b[31m\x1b[1mFAILED:\x1b[0m \nOutput2\n" if g := smart.String(); g != w { t.Errorf("want:\n%q\ngot:\n%q", w, g) |