diff options
46 files changed, 1508 insertions, 558 deletions
diff --git a/android/override_module.go b/android/override_module.go index 21cf38125..55f384f1f 100644 --- a/android/override_module.go +++ b/android/override_module.go @@ -28,6 +28,7 @@ package android // module based on it. import ( + "fmt" "sort" "sync" @@ -120,7 +121,7 @@ type OverridableModule interface { addOverride(o OverrideModule) getOverrides() []OverrideModule - override(ctx BaseModuleContext, m Module, o OverrideModule) + override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule) GetOverriddenBy() string GetOverriddenByModuleDir() string @@ -191,15 +192,14 @@ func (b *OverridableModuleBase) setOverridesProperty(overridesProperty *[]string } // Overrides a base module with the given OverrideModule. -func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o OverrideModule) { - +func (b *OverridableModuleBase) override(ctx BaseModuleContext, bm OverridableModule, o OverrideModule) { for _, p := range b.overridableProperties { for _, op := range o.getOverridingProperties() { if proptools.TypeEqual(p, op) { err := proptools.ExtendProperties(p, op, nil, proptools.OrderReplace) if err != nil { if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok { - ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error()) + ctx.OtherModulePropertyErrorf(bm, propertyErr.Property, "%s", propertyErr.Err.Error()) } else { panic(err) } @@ -210,7 +210,7 @@ func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o Over // Adds the base module to the overrides property, if exists, of the overriding module. See the // comment on OverridableModuleBase.overridesProperty for details. if b.overridesProperty != nil { - *b.overridesProperty = append(*b.overridesProperty, ctx.ModuleName()) + *b.overridesProperty = append(*b.overridesProperty, ctx.OtherModuleName(bm)) } b.overridableModuleProperties.OverriddenBy = o.Name() b.overridableModuleProperties.OverriddenByModuleDir = o.ModuleDir() @@ -235,7 +235,7 @@ func (b *OverridableModuleBase) OverridablePropertiesDepsMutator(ctx BottomUpMut // to keep them in this order and not put any order mutators between them. func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) { ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel() - ctx.BottomUp("perform_override", performOverrideMutator).Parallel() + ctx.Transition("override", &overrideTransitionMutator{}) // overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can // add deps from overridable properties. ctx.BottomUp("overridable_deps", overridableModuleDepsMutator).Parallel() @@ -262,18 +262,6 @@ func overrideModuleDepsMutator(ctx BottomUpMutatorContext) { ctx.PropertyErrorf("base", "%q is not a valid module name", base) return } - // See if there's a prebuilt module that overrides this override module with prefer flag, - // in which case we call HideFromMake on the corresponding variant later. - ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) { - prebuilt := GetEmbeddedPrebuilt(dep) - if prebuilt == nil { - panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name()) - } - if prebuilt.UsePrebuilt() { - module.setOverriddenByPrebuilt(dep) - return - } - }) baseModule := ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)[0] if o, ok := baseModule.(OverridableModule); ok { overrideModule := ctx.Module().(OverrideModule) @@ -285,11 +273,13 @@ func overrideModuleDepsMutator(ctx BottomUpMutatorContext) { // Now, goes through all overridable modules, finds all modules overriding them, creates a local // variant for each of them, and performs the actual overriding operation by calling override(). -func performOverrideMutator(ctx BottomUpMutatorContext) { +type overrideTransitionMutator struct{} + +func (overrideTransitionMutator) Split(ctx BaseModuleContext) []string { if b, ok := ctx.Module().(OverridableModule); ok { overrides := b.getOverrides() if len(overrides) == 0 { - return + return []string{""} } variants := make([]string, len(overrides)+1) // The first variant is for the original, non-overridden, base module. @@ -297,27 +287,69 @@ func performOverrideMutator(ctx BottomUpMutatorContext) { for i, o := range overrides { variants[i+1] = o.(Module).Name() } - mods := ctx.CreateLocalVariations(variants...) - // Make the original variation the default one to depend on if no other override module variant - // is specified. - ctx.AliasVariation(variants[0]) - for i, o := range overrides { - mods[i+1].(OverridableModule).override(ctx, mods[i+1], o) - if prebuilt := o.getOverriddenByPrebuilt(); prebuilt != nil { - // The overriding module itself, too, is overridden by a prebuilt. - // Perform the same check for replacement - checkInvariantsForSourceAndPrebuilt(ctx, mods[i+1], prebuilt) - // Copy the flag and hide it in make - mods[i+1].ReplacedByPrebuilt() - } - } + return variants } else if o, ok := ctx.Module().(OverrideModule); ok { // Create a variant of the overriding module with its own name. This matches the above local // variant name rule for overridden modules, and thus allows ReplaceDependencies to match the // two. - ctx.CreateLocalVariations(o.Name()) - // To allow dependencies to be added without having to know the above variation. - ctx.AliasVariation(o.Name()) + return []string{o.Name()} + } + + return []string{""} +} + +func (overrideTransitionMutator) OutgoingTransition(ctx OutgoingTransitionContext, sourceVariation string) string { + if o, ok := ctx.Module().(OverrideModule); ok { + if ctx.DepTag() == overrideBaseDepTag { + return o.Name() + } + } + + // Variations are always local and shouldn't affect the variant used for dependencies + return "" +} + +func (overrideTransitionMutator) IncomingTransition(ctx IncomingTransitionContext, incomingVariation string) string { + if _, ok := ctx.Module().(OverridableModule); ok { + return incomingVariation + } else if o, ok := ctx.Module().(OverrideModule); ok { + // To allow dependencies to be added without having to know the variation. + return o.Name() + } + + return "" +} + +func (overrideTransitionMutator) Mutate(ctx BottomUpMutatorContext, variation string) { + if o, ok := ctx.Module().(OverrideModule); ok { + overridableDeps := ctx.GetDirectDepsWithTag(overrideBaseDepTag) + if len(overridableDeps) > 1 { + panic(fmt.Errorf("expected a single dependency with overrideBaseDepTag, found %q", overridableDeps)) + } else if len(overridableDeps) == 1 { + b := overridableDeps[0].(OverridableModule) + b.override(ctx, b, o) + + checkPrebuiltReplacesOverride(ctx, b) + } + } +} + +func checkPrebuiltReplacesOverride(ctx BottomUpMutatorContext, b OverridableModule) { + // See if there's a prebuilt module that overrides this override module with prefer flag, + // in which case we call HideFromMake on the corresponding variant later. + prebuiltDeps := ctx.GetDirectDepsWithTag(PrebuiltDepTag) + for _, prebuiltDep := range prebuiltDeps { + prebuilt := GetEmbeddedPrebuilt(prebuiltDep) + if prebuilt == nil { + panic("PrebuiltDepTag leads to a non-prebuilt module " + prebuiltDep.Name()) + } + if prebuilt.UsePrebuilt() { + // The overriding module itself, too, is overridden by a prebuilt. + // Perform the same check for replacement + checkInvariantsForSourceAndPrebuilt(ctx, b, prebuiltDep) + // Copy the flag and hide it in make + b.ReplacedByPrebuilt() + } } } diff --git a/android/packaging.go b/android/packaging.go index 383f828bd..080dcfea0 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -142,6 +142,10 @@ type PackagingBase struct { // for rare cases like when there's a dependency to a module which exists in certain repo // checkouts, this is needed. IgnoreMissingDependencies bool + + // If this is set to true by a module type inheriting PackagingBase, the deps property + // collects the first target only even with compile_multilib: true. + DepsCollectFirstTargetOnly bool } type depsProperty struct { @@ -154,6 +158,7 @@ type packagingMultilibProperties struct { Common depsProperty `android:"arch_variant"` Lib32 depsProperty `android:"arch_variant"` Lib64 depsProperty `android:"arch_variant"` + Both depsProperty `android:"arch_variant"` } type packagingArchProperties struct { @@ -194,11 +199,28 @@ func (p *PackagingBase) getDepsForArch(ctx BaseModuleContext, arch ArchType) []s ret = append(ret, p.properties.Multilib.Common.Deps...) } - for i, t := range ctx.MultiTargets() { - if t.Arch.ArchType == arch { - ret = append(ret, p.properties.Deps...) - if i == 0 { - ret = append(ret, p.properties.Multilib.First.Deps...) + if p.DepsCollectFirstTargetOnly { + if len(p.properties.Multilib.First.Deps) > 0 { + ctx.PropertyErrorf("multilib.first.deps", "not supported. use \"deps\" instead") + } + for i, t := range ctx.MultiTargets() { + if t.Arch.ArchType == arch { + ret = append(ret, p.properties.Multilib.Both.Deps...) + if i == 0 { + ret = append(ret, p.properties.Deps...) + } + } + } + } else { + if len(p.properties.Multilib.Both.Deps) > 0 { + ctx.PropertyErrorf("multilib.both.deps", "not supported. use \"deps\" instead") + } + for i, t := range ctx.MultiTargets() { + if t.Arch.ArchType == arch { + ret = append(ret, p.properties.Deps...) + if i == 0 { + ret = append(ret, p.properties.Multilib.First.Deps...) + } } } } diff --git a/android/packaging_test.go b/android/packaging_test.go index 383343723..f99bb91cd 100644 --- a/android/packaging_test.go +++ b/android/packaging_test.go @@ -67,18 +67,15 @@ type packageTestModule struct { entries []string } -func packageMultiTargetTestModuleFactory() Module { +func packageTestModuleFactory(multiTarget bool, depsCollectFirstTargetOnly bool) Module { module := &packageTestModule{} InitPackageModule(module) - InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon) - module.AddProperties(&module.properties) - return module -} - -func packageTestModuleFactory() Module { - module := &packageTestModule{} - InitPackageModule(module) - InitAndroidArchModule(module, DeviceSupported, MultilibBoth) + module.DepsCollectFirstTargetOnly = depsCollectFirstTargetOnly + if multiTarget { + InitAndroidMultiTargetsArchModule(module, DeviceSupported, MultilibCommon) + } else { + InitAndroidArchModule(module, DeviceSupported, MultilibBoth) + } module.AddProperties(&module.properties) return module } @@ -98,17 +95,23 @@ func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) { m.entries = m.CopyDepsToZip(ctx, m.GatherPackagingSpecs(ctx), zipFile) } -func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) { +type packageTestModuleConfig struct { + multiTarget bool + depsCollectFirstTargetOnly bool +} + +func runPackagingTest(t *testing.T, config packageTestModuleConfig, bp string, expected []string) { t.Helper() var archVariant string - var moduleFactory ModuleFactory - if multitarget { + if config.multiTarget { archVariant = "android_common" - moduleFactory = packageMultiTargetTestModuleFactory } else { archVariant = "android_arm64_armv8-a" - moduleFactory = packageTestModuleFactory + } + + moduleFactory := func() Module { + return packageTestModuleFactory(config.multiTarget, config.depsCollectFirstTargetOnly) } result := GroupFixturePreparers( @@ -128,8 +131,11 @@ func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []stri } func TestPackagingBaseMultiTarget(t *testing.T) { - multiTarget := true - runPackagingTest(t, multiTarget, + config := packageTestModuleConfig{ + multiTarget: true, + depsCollectFirstTargetOnly: false, + } + runPackagingTest(t, config, ` component { name: "foo", @@ -141,7 +147,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib64/foo"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -158,7 +164,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -176,7 +182,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib32/foo", "lib32/bar", "lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -199,7 +205,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib32/foo", "lib32/bar", "lib64/foo"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -221,7 +227,7 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } `, []string{"lib32/foo", "lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -252,8 +258,11 @@ func TestPackagingBaseMultiTarget(t *testing.T) { } func TestPackagingBaseSingleTarget(t *testing.T) { - multiTarget := false - runPackagingTest(t, multiTarget, + config := packageTestModuleConfig{ + multiTarget: false, + depsCollectFirstTargetOnly: false, + } + runPackagingTest(t, config, ` component { name: "foo", @@ -265,7 +274,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -282,7 +291,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -304,7 +313,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -325,7 +334,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -353,7 +362,7 @@ func TestPackagingBaseSingleTarget(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar"}) - runPackagingTest(t, multiTarget, + runPackagingTest(t, config, ` component { name: "foo", @@ -374,8 +383,11 @@ func TestPackagingBaseSingleTarget(t *testing.T) { func TestPackagingWithSkipInstallDeps(t *testing.T) { // package -[dep]-> foo -[dep]-> bar -[dep]-> baz // Packaging should continue transitively through modules that are not installed. - multiTarget := false - runPackagingTest(t, multiTarget, + config := packageTestModuleConfig{ + multiTarget: false, + depsCollectFirstTargetOnly: false, + } + runPackagingTest(t, config, ` component { name: "foo", @@ -398,3 +410,130 @@ func TestPackagingWithSkipInstallDeps(t *testing.T) { } `, []string{"lib64/foo", "lib64/bar", "lib64/baz"}) } + +func TestPackagingWithDepsCollectFirstTargetOnly(t *testing.T) { + config := packageTestModuleConfig{ + multiTarget: true, + depsCollectFirstTargetOnly: true, + } + runPackagingTest(t, config, + ` + component { + name: "foo", + } + + package_module { + name: "package", + deps: ["foo"], + } + `, []string{"lib64/foo"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + deps: ["bar"], + } + + component { + name: "bar", + } + + package_module { + name: "package", + deps: ["foo"], + } + `, []string{"lib64/foo", "lib64/bar"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + deps: ["bar"], + } + + component { + name: "bar", + } + + package_module { + name: "package", + deps: ["foo"], + compile_multilib: "both", + } + `, []string{"lib64/foo", "lib64/bar"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + } + + component { + name: "bar", + compile_multilib: "32", + } + + package_module { + name: "package", + deps: ["foo"], + multilib: { + lib32: { + deps: ["bar"], + }, + }, + compile_multilib: "both", + } + `, []string{"lib32/bar", "lib64/foo"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + } + + component { + name: "bar", + } + + package_module { + name: "package", + deps: ["foo"], + multilib: { + both: { + deps: ["bar"], + }, + }, + compile_multilib: "both", + } + `, []string{"lib64/foo", "lib32/bar", "lib64/bar"}) + + runPackagingTest(t, config, + ` + component { + name: "foo", + } + + component { + name: "bar", + } + + component { + name: "baz", + } + + package_module { + name: "package", + deps: ["foo"], + arch: { + arm64: { + deps: ["bar"], + }, + x86_64: { + deps: ["baz"], + }, + }, + compile_multilib: "both", + } + `, []string{"lib64/foo", "lib64/bar"}) +} diff --git a/android/util.go b/android/util.go index 698a85650..de4ca4df1 100644 --- a/android/util.go +++ b/android/util.go @@ -302,6 +302,24 @@ func RemoveFromList(s string, list []string) (bool, []string) { return removed, result } +// FirstUniqueFunc returns all unique elements of a slice, keeping the first copy of +// each. It does not modify the input slice. The eq function should return true +// if two elements can be considered equal. +func FirstUniqueFunc[SortableList ~[]Sortable, Sortable any](list SortableList, eq func(a, b Sortable) bool) SortableList { + k := 0 +outer: + for i := 0; i < len(list); i++ { + for j := 0; j < k; j++ { + if eq(list[i], list[j]) { + continue outer + } + } + list[k] = list[i] + k++ + } + return list[:k] +} + // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of // each. It does not modify the input slice. func FirstUniqueStrings(list []string) []string { diff --git a/apex/apex.go b/apex/apex.go index 802991009..bc2b032bf 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -699,7 +699,12 @@ var ( func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) { binVariations := target.Variations() libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) - rustLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"}) + rustLibVariations := append( + target.Variations(), []blueprint.Variation{ + {Mutator: "rust_libraries", Variation: "dylib"}, + {Mutator: "link", Variation: ""}, + }..., + ) // Append "image" variation binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation}) diff --git a/apex/apex_test.go b/apex/apex_test.go index 74b2eec5b..a758caf5b 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -5908,6 +5908,7 @@ func TestApexWithApps(t *testing.T) { srcs: ["foo/bar/MyClass.java"], sdk_version: "current", system_modules: "none", + use_embedded_native_libs: true, jni_libs: ["libjni"], stl: "none", apex_available: [ "myapex" ], diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index 2be9c10e1..9f1e1e16f 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -795,3 +795,127 @@ func TestNonBootJarInFragment(t *testing.T) { } `) } + +// Source and prebuilt apex provide different set of boot jars +func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) { + bp := ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: ["apex-fragment"], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "foo", + srcs: ["b.java"], + installable: true, + apex_available: ["myapex"], + permitted_packages: ["foo"], + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: ["myapex"], + permitted_packages: ["bar"], + } + + bootclasspath_fragment { + name: "apex-fragment", + contents: ["foo", "bar"], + apex_available:[ "myapex" ], + hidden_api: { + split_packages: ["*"], + }, + } + + prebuilt_apex { + name: "com.google.android.myapex", // mainline prebuilt selection logic in soong relies on the naming convention com.google.android + apex_name: "myapex", + source_apex_name: "myapex", + src: "myapex.apex", + exported_bootclasspath_fragments: ["apex-fragment"], + } + + java_import { + name: "foo", + jars: ["foo.jar"], + apex_available: ["myapex"], + permitted_packages: ["foo"], + } + + prebuilt_bootclasspath_fragment { + name: "apex-fragment", + contents: ["foo"], // Unlike the source fragment, this is missing bar + apex_available:[ "myapex" ], + hidden_api: { + annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv", + metadata: "my-bootclasspath-fragment/metadata.csv", + index: "my-bootclasspath-fragment/index.csv", + stub_flags: "my-bootclasspath-fragment/stub-flags.csv", + all_flags: "my-bootclasspath-fragment/all-flags.csv", + }, + } + + apex_contributions { + name: "my_apex_contributions", + api_domain: "myapex", + contents: [%v], + } + ` + testCases := []struct { + desc string + configuredBootJars []string + apexContributionContents string + errorExpected bool + }{ + { + desc: "Source apex is selected, and APEX_BOOT_JARS is correctly configured for source apex builds", + configuredBootJars: []string{"myapex:foo", "myapex:bar"}, + }, + { + desc: "Source apex is selected, and APEX_BOOT_JARS is missing bar", + configuredBootJars: []string{"myapex:foo"}, + errorExpected: true, + }, + { + desc: "Prebuilt apex is selected, and APEX_BOOT_JARS is correctly configured for prebuilt apex build", + configuredBootJars: []string{"myapex:foo"}, + apexContributionContents: `"prebuilt_com.google.android.myapex"`, + }, + { + desc: "Prebuilt apex is selected, and APEX_BOOT_JARS is missing foo", + configuredBootJars: []string{"myapex:bar"}, + apexContributionContents: `"prebuilt_com.google.android.myapex"`, + errorExpected: true, + }, + } + + for _, tc := range testCases { + fixture := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + PrepareForTestWithApexBuildComponents, + prepareForTestWithMyapex, + java.FixtureConfigureApexBootJars(tc.configuredBootJars...), + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.BuildFlags = map[string]string{ + "RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions", + } + }), + ) + if tc.errorExpected { + fixture = fixture.ExtendWithErrorHandler( + android.FixtureExpectsAtLeastOneErrorMatchingPattern(`in contents.*must also be declared in PRODUCT_APEX_BOOT_JARS`), + ) + } + fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.apexContributionContents)) + } +} diff --git a/apex/prebuilt.go b/apex/prebuilt.go index 72a9e5219..b2afa3933 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -835,7 +835,21 @@ func (p *prebuiltCommon) providePrebuiltInfo(ctx android.ModuleContext) { android.SetProvider(ctx, android.PrebuiltInfoProvider, info) } +// Uses an object provided by its deps to validate that the contents of bcpf have been added to the global +// PRODUCT_APEX_BOOT_JARS +// This validation will only run on the apex which is active for this product/release_config +func validateApexClasspathFragments(ctx android.ModuleContext) { + ctx.VisitDirectDeps(func(m android.Module) { + if info, exists := android.OtherModuleProvider(ctx, m, java.ClasspathFragmentValidationInfoProvider); exists { + ctx.ModuleErrorf("%s in contents of %s must also be declared in PRODUCT_APEX_BOOT_JARS", info.UnknownJars, info.ClasspathFragmentModuleName) + } + }) +} + func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Validate contents of classpath fragments + validateApexClasspathFragments(ctx) + p.apexKeysPath = writeApexKeys(ctx, p) // TODO(jungjw): Check the key validity. p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path() @@ -1059,6 +1073,9 @@ func (a *ApexSet) ApexInfoMutator(mctx android.TopDownMutatorContext) { } func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) { + // Validate contents of classpath fragments + validateApexClasspathFragments(ctx) + a.apexKeysPath = writeApexKeys(ctx, a) a.installFilename = a.InstallFilename() if !strings.HasSuffix(a.installFilename, imageApexSuffix) && !strings.HasSuffix(a.installFilename, imageCapexSuffix) { diff --git a/cc/binary.go b/cc/binary.go index 7aa8e20cc..3ff35de56 100644 --- a/cc/binary.go +++ b/cc/binary.go @@ -18,6 +18,7 @@ import ( "path/filepath" "android/soong/android" + "github.com/google/blueprint" ) @@ -425,6 +426,10 @@ func (binary *binaryDecorator) link(ctx ModuleContext, validations = append(validations, objs.tidyDepFiles...) linkerDeps = append(linkerDeps, flags.LdFlagsDeps...) + if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil { + deps.StaticLibs = append(deps.StaticLibs, generatedLib) + } + // Register link action. transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true, diff --git a/cc/builder.go b/cc/builder.go index e255cbebb..42aa4b66b 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -19,6 +19,7 @@ package cc // functions. import ( + "fmt" "path/filepath" "runtime" "strconv" @@ -330,6 +331,15 @@ var ( CommandDeps: []string{"$cxxExtractor", "$kytheVnames"}, }, "cFlags") + + // Function pointer for producting staticlibs from rlibs. Corresponds to + // rust.TransformRlibstoStaticlib(), initialized in soong-rust (rust/builder.go init()) + // + // This is required since soong-rust depends on soong-cc, so soong-cc cannot depend on soong-rust + // without resulting in a circular dependency. Setting this function pointer in soong-rust allows + // soong-cc to call into this particular function. + TransformRlibstoStaticlib (func(ctx android.ModuleContext, mainSrc android.Path, deps []RustRlibDep, + outputFile android.WritablePath) android.Path) = nil ) func PwdPrefix() string { @@ -778,6 +788,47 @@ func transformObjToStaticLib(ctx android.ModuleContext, } } +// Generate a Rust staticlib from a list of rlibDeps. Returns nil if TransformRlibstoStaticlib is nil or rlibDeps is empty. +func generateRustStaticlib(ctx android.ModuleContext, rlibDeps []RustRlibDep) android.Path { + if TransformRlibstoStaticlib == nil && len(rlibDeps) > 0 { + // This should only be reachable if a module defines static_rlibs and + // soong-rust hasn't been loaded alongside soong-cc (e.g. in soong-cc tests). + panic(fmt.Errorf("TransformRlibstoStaticlib is not set and static_rlibs is defined in %s", ctx.ModuleName())) + } else if len(rlibDeps) == 0 { + return nil + } + + output := android.PathForModuleOut(ctx, "generated_rust_staticlib", "lib"+ctx.ModuleName()+"_rust_staticlib.a") + stemFile := output.ReplaceExtension(ctx, "rs") + crateNames := []string{} + + // Collect crate names + for _, lib := range rlibDeps { + // Exclude libstd so this can support no_std builds. + if lib.CrateName != "libstd" { + crateNames = append(crateNames, lib.CrateName) + } + } + + // Deduplicate any crateNames just to be safe + crateNames = android.FirstUniqueStrings(crateNames) + + // Write the source file + android.WriteFileRule(ctx, stemFile, genRustStaticlibSrcFile(crateNames)) + + return TransformRlibstoStaticlib(ctx, stemFile, rlibDeps, output) +} + +func genRustStaticlibSrcFile(crateNames []string) string { + lines := []string{ + "// @Soong generated Source", + } + for _, crate := range crateNames { + lines = append(lines, fmt.Sprintf("extern crate %s;", crate)) + } + return strings.Join(lines, "\n") +} + // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries, // and shared libraries, to a shared library (.so) or dynamic executable func transformObjToDynamicBinary(ctx android.ModuleContext, @@ -100,6 +100,7 @@ type Deps struct { StaticLibs, LateStaticLibs, WholeStaticLibs []string HeaderLibs []string RuntimeLibs []string + Rlibs []string // UnexportedStaticLibs are static libraries that are also passed to -Wl,--exclude-libs= to // prevent automatically exporting symbols. @@ -145,6 +146,17 @@ type Deps struct { LlndkHeaderLibs []string } +// A struct which to collect flags for rlib dependencies +type RustRlibDep struct { + LibPath android.Path // path to the rlib + LinkDirs []string // flags required for dependency (e.g. -L flags) + CrateName string // crateNames associated with rlibDeps +} + +func EqRustRlibDeps(a RustRlibDep, b RustRlibDep) bool { + return a.LibPath == b.LibPath +} + // PathDeps is a struct containing file paths to dependencies of a module. // It's constructed in depsToPath() by traversing the direct dependencies of the current module. // It's used to construct flags for various build statements (such as for compiling and linking). @@ -157,6 +169,8 @@ type PathDeps struct { SharedLibsDeps, EarlySharedLibsDeps, LateSharedLibsDeps android.Paths // Paths to .a files StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths + // Paths and crateNames for RustStaticLib dependencies + RustRlibDeps []RustRlibDep // Transitive static library dependencies of static libraries for use in ordering. TranstiveStaticLibrariesForOrdering *android.DepSet[android.Path] @@ -185,6 +199,7 @@ type PathDeps struct { ReexportedFlags []string ReexportedGeneratedHeaders android.Paths ReexportedDeps android.Paths + ReexportedRustRlibDeps []RustRlibDep // Paths to crt*.o files CrtBegin, CrtEnd android.Paths @@ -298,6 +313,7 @@ type BaseProperties struct { AndroidMkSharedLibs []string `blueprint:"mutated"` AndroidMkStaticLibs []string `blueprint:"mutated"` + AndroidMkRlibs []string `blueprint:"mutated"` AndroidMkRuntimeLibs []string `blueprint:"mutated"` AndroidMkWholeStaticLibs []string `blueprint:"mutated"` AndroidMkHeaderLibs []string `blueprint:"mutated"` @@ -660,6 +676,7 @@ const ( headerLibraryDependency = iota sharedLibraryDependency staticLibraryDependency + rlibLibraryDependency ) func (k libraryDependencyKind) String() string { @@ -670,6 +687,8 @@ func (k libraryDependencyKind) String() string { return "sharedLibraryDependency" case staticLibraryDependency: return "staticLibraryDependency" + case rlibLibraryDependency: + return "rlibLibraryDependency" default: panic(fmt.Errorf("unknown libraryDependencyKind %d", k)) } @@ -747,6 +766,11 @@ func (d libraryDependencyTag) static() bool { return d.Kind == staticLibraryDependency } +// rlib returns true if the libraryDependencyTag is tagging an rlib dependency. +func (d libraryDependencyTag) rlib() bool { + return d.Kind == rlibLibraryDependency +} + func (d libraryDependencyTag) LicenseAnnotations() []android.LicenseAnnotation { if d.shared() { return []android.LicenseAnnotation{android.LicenseAnnotationSharedDependency} @@ -1114,6 +1138,14 @@ func (c *Module) RustLibraryInterface() bool { return false } +func (c *Module) CrateName() string { + panic(fmt.Errorf("CrateName called on non-Rust module: %q", c.BaseModuleName())) +} + +func (c *Module) ExportedCrateLinkDirs() []string { + panic(fmt.Errorf("ExportedCrateLinkDirs called on non-Rust module: %q", c.BaseModuleName())) +} + func (c *Module) IsFuzzModule() bool { if _, ok := c.compiler.(*fuzzBinary); ok { return true @@ -2309,6 +2341,7 @@ func (c *Module) deps(ctx DepsContext) Deps { deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs) deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs) + deps.Rlibs = android.LastUniqueStrings(deps.Rlibs) deps.LateStaticLibs = android.LastUniqueStrings(deps.LateStaticLibs) deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs) deps.LateSharedLibs = android.LastUniqueStrings(deps.LateSharedLibs) @@ -2616,6 +2649,15 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { }, depTag, lib) } + for _, lib := range deps.Rlibs { + depTag := libraryDependencyTag{Kind: rlibLibraryDependency} + actx.AddVariationDependencies([]blueprint.Variation{ + {Mutator: "link", Variation: ""}, + {Mutator: "rust_libraries", Variation: "rlib"}, + {Mutator: "rust_stdlinkage", Variation: "rlib-std"}, + }, depTag, lib) + } + // staticUnwinderDep is treated as staticDep for Q apexes // so that native libraries/binaries are linked with static unwinder // because Q libc doesn't have unwinder APIs @@ -3225,6 +3267,14 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { default: panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order)) } + + case libDepTag.rlib(): + rlibDep := RustRlibDep{LibPath: linkFile.Path(), CrateName: ccDep.CrateName(), LinkDirs: ccDep.ExportedCrateLinkDirs()} + depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, rlibDep) + depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, rlibDep) + depPaths.IncludeDirs = append(depPaths.IncludeDirs, depExporterInfo.IncludeDirs...) + depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, depExporterInfo.IncludeDirs...) + case libDepTag.static(): staticLibraryInfo, isStaticLib := android.OtherModuleProvider(ctx, dep, StaticLibraryInfoProvider) if !isStaticLib { @@ -3277,6 +3327,12 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { panic(fmt.Errorf("unexpected library dependency order %d", libDepTag.Order)) } } + + // We re-export the Rust static_rlibs so rlib dependencies don't need to be redeclared by cc_library_static dependents. + // E.g. libfoo (cc_library_static) depends on libfoo.ffi (a rust_ffi rlib), libbar depending on libfoo shouldn't have to also add libfoo.ffi to static_rlibs. + depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...) + depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...) + if libDepTag.unexportedSymbols { depPaths.LdFlags = append(depPaths.LdFlags, "-Wl,--exclude-libs="+staticLibraryInfo.StaticLibrary.Base()) @@ -3329,6 +3385,12 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depPaths.SystemIncludeDirs = append(depPaths.SystemIncludeDirs, depExporterInfo.SystemIncludeDirs...) depPaths.GeneratedDeps = append(depPaths.GeneratedDeps, depExporterInfo.Deps...) depPaths.Flags = append(depPaths.Flags, depExporterInfo.Flags...) + depPaths.RustRlibDeps = append(depPaths.RustRlibDeps, depExporterInfo.RustRlibDeps...) + + // Only re-export RustRlibDeps for cc static libs + if c.static() { + depPaths.ReexportedRustRlibDeps = append(depPaths.ReexportedRustRlibDeps, depExporterInfo.RustRlibDeps...) + } if libDepTag.reexportFlags { reexportExporter(depExporterInfo) @@ -3401,11 +3463,14 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depPaths.IncludeDirs = android.FirstUniquePaths(depPaths.IncludeDirs) depPaths.SystemIncludeDirs = android.FirstUniquePaths(depPaths.SystemIncludeDirs) depPaths.GeneratedDeps = android.FirstUniquePaths(depPaths.GeneratedDeps) + depPaths.RustRlibDeps = android.FirstUniqueFunc(depPaths.RustRlibDeps, EqRustRlibDeps) + depPaths.ReexportedDirs = android.FirstUniquePaths(depPaths.ReexportedDirs) depPaths.ReexportedSystemDirs = android.FirstUniquePaths(depPaths.ReexportedSystemDirs) depPaths.ReexportedFlags = android.FirstUniqueStrings(depPaths.ReexportedFlags) depPaths.ReexportedDeps = android.FirstUniquePaths(depPaths.ReexportedDeps) depPaths.ReexportedGeneratedHeaders = android.FirstUniquePaths(depPaths.ReexportedGeneratedHeaders) + depPaths.ReexportedRustRlibDeps = android.FirstUniqueFunc(depPaths.ReexportedRustRlibDeps, EqRustRlibDeps) if c.sabi != nil { c.sabi.Properties.ReexportedIncludes = android.FirstUniqueStrings(c.sabi.Properties.ReexportedIncludes) diff --git a/cc/compiler.go b/cc/compiler.go index 9187b9bfa..21b8f2e85 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -792,6 +792,9 @@ type RustBindgenClangProperties struct { // be added to the include path using -I Local_include_dirs []string `android:"arch_variant,variant_prepend"` + // list of Rust static libraries. + Static_rlibs []string `android:"arch_variant,variant_prepend"` + // list of static libraries that provide headers for this binding. Static_libs []string `android:"arch_variant,variant_prepend"` diff --git a/cc/fuzz.go b/cc/fuzz.go index b3e663962..164ec997b 100644 --- a/cc/fuzz.go +++ b/cc/fuzz.go @@ -597,7 +597,7 @@ func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilde ctx.WalkDeps(func(child, parent android.Module) bool { // If this is a Rust module which is not rust_ffi_shared, we still want to bundle any transitive - // shared dependencies (even for rust_ffi_static) + // shared dependencies (even for rust_ffi_rlib or rust_ffi_static) if rustmod, ok := child.(LinkableInterface); ok && rustmod.RustLibraryInterface() && !rustmod.Shared() { if recursed[ctx.OtherModuleName(child)] { return false diff --git a/cc/library.go b/cc/library.go index 895b1993c..cc8fafeee 100644 --- a/cc/library.go +++ b/cc/library.go @@ -274,11 +274,12 @@ func LibraryHostSharedFactory() android.Module { type flagExporter struct { Properties FlagExporterProperties - dirs android.Paths // Include directories to be included with -I - systemDirs android.Paths // System include directories to be included with -isystem - flags []string // Exported raw flags. - deps android.Paths - headers android.Paths + dirs android.Paths // Include directories to be included with -I + systemDirs android.Paths // System include directories to be included with -isystem + flags []string // Exported raw flags. + deps android.Paths + headers android.Paths + rustRlibDeps []RustRlibDep } // exportedIncludes returns the effective include paths for this module and @@ -339,6 +340,10 @@ func (f *flagExporter) reexportDeps(deps ...android.Path) { f.deps = append(f.deps, deps...) } +func (f *flagExporter) reexportRustStaticDeps(deps ...RustRlibDep) { + f.rustRlibDeps = append(f.rustRlibDeps, deps...) +} + // addExportedGeneratedHeaders does nothing but collects generated header files. // This can be differ to exportedDeps which may contain phony files to minimize ninja. func (f *flagExporter) addExportedGeneratedHeaders(headers ...android.Path) { @@ -356,6 +361,8 @@ func (f *flagExporter) setProvider(ctx android.ModuleContext) { // Used sparingly, for extra files that need to be explicitly exported to dependers, // or for phony files to minimize ninja. Deps: f.deps, + // Used for exporting rlib deps of static libraries to dependents. + RustRlibDeps: f.rustRlibDeps, // For exported generated headers, such as exported aidl headers, proto headers, or // sysprop headers. GeneratedHeaders: f.headers, @@ -1132,9 +1139,14 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext, linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...) linkerDeps = append(linkerDeps, deps.SharedLibsDeps...) linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...) + + if generatedLib := generateRustStaticlib(ctx, deps.RustRlibDeps); generatedLib != nil { + deps.StaticLibs = append(deps.StaticLibs, generatedLib) + } + transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, - deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, - linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles) + deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, + deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles) objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...) objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...) @@ -1598,6 +1610,10 @@ func (library *libraryDecorator) link(ctx ModuleContext, library.reexportDeps(deps.ReexportedDeps...) library.addExportedGeneratedHeaders(deps.ReexportedGeneratedHeaders...) + if library.static() && len(deps.ReexportedRustRlibDeps) > 0 { + library.reexportRustStaticDeps(deps.ReexportedRustRlibDeps...) + } + // Optionally export aidl headers. if Bool(library.Properties.Aidl.Export_aidl_headers) { if library.baseCompiler.hasAidl(deps) { @@ -2125,14 +2141,12 @@ func LinkageMutator(mctx android.BottomUpMutatorContext) { // Header only } - } else if library, ok := mctx.Module().(LinkableInterface); ok && library.CcLibraryInterface() { - + } else if library, ok := mctx.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 { isLLNDK = m.IsLlndk() diff --git a/cc/linkable.go b/cc/linkable.go index 10cc38f64..5579aaea5 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -73,6 +73,12 @@ type LinkableInterface interface { // RustLibraryInterface returns true if this is a Rust library module RustLibraryInterface() bool + // CrateName returns the crateName for a Rust library, panics if not a Rust library. + CrateName() string + + // DepFlags returns a slice of Rustc string flags, panics if not a Rust library + ExportedCrateLinkDirs() []string + // BaseModuleName returns the android.ModuleBase.BaseModuleName() value for this module. BaseModuleName() string @@ -380,6 +386,7 @@ type FlagExporterInfo struct { SystemIncludeDirs android.Paths // System include directories to be included with -isystem Flags []string // Exported raw flags. Deps android.Paths + RustRlibDeps []RustRlibDep GeneratedHeaders android.Paths } diff --git a/cc/linker.go b/cc/linker.go index 1d0f20547..f325c125d 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -39,6 +39,9 @@ type BaseLinkerProperties struct { // the dependency's .a file will be linked into this module using -Wl,--whole-archive. Whole_static_libs []string `android:"arch_variant,variant_prepend"` + // list of Rust libs that should be statically linked into this module. + Static_rlibs []string `android:"arch_variant"` + // list of modules that should be statically linked into this module. Static_libs []string `android:"arch_variant,variant_prepend"` @@ -116,10 +119,14 @@ type BaseLinkerProperties struct { // product variant of the C/C++ module. Static_libs []string - // list of ehader libs that only should be used to build vendor or product + // list of header libs that only should be used to build vendor or product // variant of the C/C++ module. Header_libs []string + // list of Rust libs that should be statically linked to build vendor or product + // variant. + Static_rlibs []string + // list of shared libs that should not be used to build vendor or // product variant of the C/C++ module. Exclude_shared_libs []string @@ -148,6 +155,10 @@ type BaseLinkerProperties struct { // variant of the C/C++ module. Static_libs []string + // list of Rust libs that should be statically linked to build the recovery + // variant. + Static_rlibs []string + // list of shared libs that should not be used to build // the recovery variant of the C/C++ module. Exclude_shared_libs []string @@ -165,10 +176,14 @@ type BaseLinkerProperties struct { Exclude_runtime_libs []string } Ramdisk struct { - // list of static libs that only should be used to build the recovery + // list of static libs that only should be used to build the ramdisk // variant of the C/C++ module. Static_libs []string + // list of Rust libs that should be statically linked to build the ramdisk + // variant. + Static_rlibs []string + // list of shared libs that should not be used to build // the ramdisk variant of the C/C++ module. Exclude_shared_libs []string @@ -183,9 +198,13 @@ type BaseLinkerProperties struct { } Vendor_ramdisk struct { // list of shared libs that should not be used to build - // the recovery variant of the C/C++ module. + // the vendor ramdisk variant of the C/C++ module. Exclude_shared_libs []string + // list of Rust libs that should be statically linked to build the vendor ramdisk + // variant. + Static_rlibs []string + // list of static libs that should not be used to build // the vendor ramdisk variant of the C/C++ module. Exclude_static_libs []string @@ -201,6 +220,10 @@ type BaseLinkerProperties struct { // variants. Shared_libs []string + // list of Rust libs that should be statically linked to build the vendor ramdisk + // variant. + Static_rlibs []string + // list of ehader libs that only should be used to build platform variant of // the C/C++ module. Header_libs []string @@ -295,6 +318,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.WholeStaticLibs = append(deps.WholeStaticLibs, linker.Properties.Whole_static_libs...) deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Header_libs...) deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Static_libs...) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Static_rlibs...) deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Shared_libs...) deps.RuntimeLibs = append(deps.RuntimeLibs, linker.Properties.Runtime_libs...) @@ -338,6 +362,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor.Static_rlibs...) } if ctx.inProduct() { @@ -351,6 +376,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Product.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Product.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Product.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Product.Static_rlibs...) } if ctx.inRecovery() { @@ -364,6 +390,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Recovery.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Recovery.Static_rlibs...) } if ctx.inRamdisk() { @@ -374,6 +401,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Ramdisk.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Ramdisk.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Ramdisk.Static_rlibs...) } if ctx.inVendorRamdisk() { @@ -383,6 +411,7 @@ func (linker *baseLinker) linkerDeps(ctx DepsContext, deps Deps) Deps { deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs) deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs) deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_runtime_libs) + deps.Rlibs = append(deps.Rlibs, linker.Properties.Target.Vendor_ramdisk.Static_rlibs...) } if !ctx.useSdk() { diff --git a/cc/testing.go b/cc/testing.go index c3a33cb91..989be0272 100644 --- a/cc/testing.go +++ b/cc/testing.go @@ -300,6 +300,7 @@ func commonDefaultModules() string { system_shared_libs: [], stl: "none", vendor_available: true, + vendor_ramdisk_available: true, product_available: true, recovery_available: true, host_supported: true, diff --git a/cmd/release_config/crunch_flags/main.go b/cmd/release_config/crunch_flags/main.go index cd39ffd11..8a80a0271 100644 --- a/cmd/release_config/crunch_flags/main.go +++ b/cmd/release_config/crunch_flags/main.go @@ -16,8 +16,8 @@ import ( ) var ( - // When a flag declaration has an initial value that is a string, the default workflow is PREBUILT. - // If the flag name starts with any of prefixes in manualFlagNamePrefixes, it is MANUAL. + // When a flag declaration has an initial value that is a string, the default workflow is WorkflowPrebuilt. + // If the flag name starts with any of prefixes in manualFlagNamePrefixes, it is WorkflowManual. manualFlagNamePrefixes []string = []string{ "RELEASE_ACONFIG_", "RELEASE_PLATFORM_", @@ -133,8 +133,8 @@ func ProcessBuildFlags(dir string, namespaceMap map[string]string) error { Containers: containers, } description = "" - // Most build flags are `workflow: PREBUILT`. - workflow := rc_proto.Workflow(rc_proto.Workflow_PREBUILT) + // Most build flags are `workflow: WorkflowPrebuilt`. + workflow := rc_proto.Workflow(rc_proto.Workflow_WorkflowPrebuilt) switch { case declName == "RELEASE_ACONFIG_VALUE_SETS": if strings.HasPrefix(declValue, "\"") { @@ -142,21 +142,21 @@ func ProcessBuildFlags(dir string, namespaceMap map[string]string) error { } continue case strings.HasPrefix(declValue, "\""): - // String values mean that the flag workflow is (most likely) either MANUAL or PREBUILT. + // String values mean that the flag workflow is (most likely) either WorkflowManual or WorkflowPrebuilt. declValue = declValue[1 : len(declValue)-1] flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{declValue}} for _, prefix := range manualFlagNamePrefixes { if strings.HasPrefix(declName, prefix) { - workflow = rc_proto.Workflow(rc_proto.Workflow_MANUAL) + workflow = rc_proto.Workflow(rc_proto.Workflow_WorkflowManual) break } } case declValue == "False" || declValue == "True": - // Boolean values are LAUNCH flags. + // Boolean values are WorkflowLaunch flags. flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{declValue == "True"}} - workflow = rc_proto.Workflow(rc_proto.Workflow_LAUNCH) + workflow = rc_proto.Workflow(rc_proto.Workflow_WorkflowLaunch) case declValue == "None": - // Use PREBUILT workflow with no initial value. + // Use WorkflowPrebuilt workflow with no initial value. default: fmt.Printf("%s: Unexpected value %s=%s\n", path, declName, declValue) } diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go index 67b57cc5b..061783828 100644 --- a/cmd/release_config/release_config/main.go +++ b/cmd/release_config/release_config/main.go @@ -77,7 +77,7 @@ func main() { panic(err) } - makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, targetRelease)) + makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, targetRelease)) useProto, ok := config.FlagArtifacts["RELEASE_BUILD_FLAGS_IN_PROTOBUF"] if guard && (!ok || rc_lib.MarshalValue(useProto.Value) == "") { // We were told to guard operation and either we have no build flag, or it is False. @@ -94,7 +94,7 @@ func main() { // Write one makefile per release config, using the canonical release name. for _, c := range configs.GetSortedReleaseConfigs() { if c.Name != targetRelease { - makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, c.Name)) + makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, c.Name)) err = configs.WriteMakefile(makefilePath, c.Name) if err != nil { panic(err) diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go index 93410a616..82adc34aa 100644 --- a/cmd/release_config/release_config_lib/release_config.go +++ b/cmd/release_config/release_config_lib/release_config.go @@ -69,6 +69,9 @@ type ReleaseConfig struct { // Unmarshalled flag artifacts FlagArtifacts FlagArtifacts + // The files used by this release config + FilesUsedMap map[string]bool + // Generated release config ReleaseConfigArtifact *rc_proto.ReleaseConfigArtifact @@ -80,10 +83,17 @@ type ReleaseConfig struct { } func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) { - return &ReleaseConfig{Name: name, DeclarationIndex: index} + return &ReleaseConfig{ + Name: name, + DeclarationIndex: index, + FilesUsedMap: make(map[string]bool), + } } func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error { + for f := range iConfig.FilesUsedMap { + config.FilesUsedMap[f] = true + } for _, fa := range iConfig.FlagArtifacts { name := *fa.FlagDeclaration.Name myFa, ok := config.FlagArtifacts[name] @@ -106,6 +116,17 @@ func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error { return nil } +func (config *ReleaseConfig) GetSortedFileList() []string { + ret := []string{} + for k := range config.FilesUsedMap { + ret = append(ret, k) + } + slices.SortFunc(ret, func(a, b string) int { + return cmp.Compare(a, b) + }) + return ret +} + func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error { if config.ReleaseConfigArtifact != nil { return nil @@ -145,9 +166,18 @@ func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) erro return err } } + + // If we inherited nothing, then we need to mark the global files as used for this + // config. If we inherited, then we already marked them as part of inheritance. + if len(config.InheritNames) == 0 { + for f := range configs.FilesUsedMap { + config.FilesUsedMap[f] = true + } + } + contributionsToApply = append(contributionsToApply, config.Contributions...) - workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL) + workflowManual := rc_proto.Workflow(rc_proto.Workflow_WorkflowManual) myDirsMap := make(map[int]bool) for _, contrib := range contributionsToApply { contribAconfigValueSets := []string{} @@ -181,8 +211,8 @@ func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) erro return fmt.Errorf("Setting value for flag %s not allowed in %s\n", name, value.path) } if isRoot && *fa.FlagDeclaration.Workflow != workflowManual { - // The "root" release config can only contain workflow: MANUAL flags. - return fmt.Errorf("Setting value for non-MANUAL flag %s is not allowed in %s", name, value.path) + // The "root" release config can only contain workflow: WorkflowManual flags. + return fmt.Errorf("Setting value for non-WorkflowManual flag %s is not allowed in %s", name, value.path) } if err := fa.UpdateValue(*value); err != nil { return err diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go index 461ee106c..65e6d90ab 100644 --- a/cmd/release_config/release_config_lib/release_configs.go +++ b/cmd/release_config/release_config_lib/release_configs.go @@ -67,6 +67,9 @@ type ReleaseConfigs struct { // Map of directory to *ReleaseConfigMap releaseConfigMapsMap map[string]*ReleaseConfigMap + // The files used by all release configs + FilesUsedMap map[string]bool + // The list of config directories used. configDirs []string @@ -102,8 +105,9 @@ func ReleaseConfigsFactory() (c *ReleaseConfigs) { releaseConfigMapsMap: make(map[string]*ReleaseConfigMap), configDirs: []string{}, configDirIndexes: make(ReleaseConfigDirMap), + FilesUsedMap: make(map[string]bool), } - workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL) + workflowManual := rc_proto.Workflow(rc_proto.Workflow_WorkflowManual) releaseAconfigValueSets := FlagArtifact{ FlagDeclaration: &rc_proto.FlagDeclaration{ Name: proto.String("RELEASE_ACONFIG_VALUE_SETS"), @@ -180,6 +184,7 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex return fmt.Errorf("Release config map %s has invalid container %s", path, container) } } + configs.FilesUsedMap[path] = true dir := filepath.Dir(path) // Record any aliases, checking for duplicates. for _, alias := range m.proto.Aliases { @@ -226,6 +231,7 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex return fmt.Errorf("Duplicate definition of %s", *flagDeclaration.Name) } // Set the initial value in the flag artifact. + configs.FilesUsedMap[path] = true configs.FlagArtifacts[name].UpdateValue( FlagValue{path: path, proto: rc_proto.FlagValue{ Name: proto.String(name), Value: flagDeclaration.Value}}) @@ -249,6 +255,7 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex configs.ReleaseConfigs[name] = ReleaseConfigFactory(name, ConfigDirIndex) } config := configs.ReleaseConfigs[name] + config.FilesUsedMap[path] = true config.InheritNames = append(config.InheritNames, releaseConfigContribution.proto.Inherits...) // Only walk flag_values/{RELEASE} for defined releases. @@ -260,6 +267,7 @@ func (configs *ReleaseConfigs) LoadReleaseConfigMap(path string, ConfigDirIndex if *flagValue.proto.Name == "RELEASE_ACONFIG_VALUE_SETS" { return fmt.Errorf("%s: %s is a reserved build flag", path, *flagValue.proto.Name) } + config.FilesUsedMap[path] = true releaseConfigContribution.FlagValues = append(releaseConfigContribution.FlagValues, flagValue) return nil }) @@ -371,6 +379,7 @@ func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) erro } // The variable _all_release_configs will get deleted during processing, so do not mark it read-only. data += fmt.Sprintf("_all_release_configs := %s\n", strings.Join(configs.GetAllReleaseNames(), " ")) + data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " ")) data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " ")) for _, pName := range pNames { data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " ")) diff --git a/cmd/release_config/release_config_proto/build_flags_out.pb.go b/cmd/release_config/release_config_proto/build_flags_out.pb.go index 483cffac1..8fa75aa8b 100644 --- a/cmd/release_config/release_config_proto/build_flags_out.pb.go +++ b/cmd/release_config/release_config_proto/build_flags_out.pb.go @@ -11,7 +11,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.33.0 // protoc v3.21.12 // source: build_flags_out.proto diff --git a/cmd/release_config/release_config_proto/build_flags_src.pb.go b/cmd/release_config/release_config_proto/build_flags_src.pb.go index dded97566..c52a238ed 100644 --- a/cmd/release_config/release_config_proto/build_flags_src.pb.go +++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go @@ -11,7 +11,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.33.0 // protoc v3.21.12 // source: build_flags_src.proto @@ -34,30 +34,30 @@ const ( type Workflow int32 const ( - Workflow_UNSPECIFIED_workflow Workflow = 0 + Workflow_WorkflowUnspecified Workflow = 0 // Boolean value flags that progress from false to true. - Workflow_LAUNCH Workflow = 1 + Workflow_WorkflowLaunch Workflow = 1 // String value flags that get updated with new version strings to control // prebuilt inclusion. - Workflow_PREBUILT Workflow = 2 + Workflow_WorkflowPrebuilt Workflow = 2 // Manually managed outside flags. These are likely to be found in a // different directory than flags with other workflows. - Workflow_MANUAL Workflow = 3 + Workflow_WorkflowManual Workflow = 3 ) // Enum value maps for Workflow. var ( Workflow_name = map[int32]string{ - 0: "UNSPECIFIED_workflow", - 1: "LAUNCH", - 2: "PREBUILT", - 3: "MANUAL", + 0: "WorkflowUnspecified", + 1: "WorkflowLaunch", + 2: "WorkflowPrebuilt", + 3: "WorkflowManual", } Workflow_value = map[string]int32{ - "UNSPECIFIED_workflow": 0, - "LAUNCH": 1, - "PREBUILT": 2, - "MANUAL": 3, + "WorkflowUnspecified": 0, + "WorkflowLaunch": 1, + "WorkflowPrebuilt": 2, + "WorkflowManual": 3, } ) @@ -295,7 +295,7 @@ func (x *FlagDeclaration) GetWorkflow() Workflow { if x != nil && x.Workflow != nil { return *x.Workflow } - return Workflow_UNSPECIFIED_workflow + return Workflow_WorkflowUnspecified } func (x *FlagDeclaration) GetContainers() []string { @@ -642,15 +642,17 @@ var file_build_flags_src_proto_rawDesc = []byte{ 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x2a, 0x4a, 0x0a, 0x08, - 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, - 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c, - 0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, - 0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, - 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, - 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x2a, 0x61, 0x0a, 0x08, + 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x17, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x10, + 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4c, 0x61, 0x75, + 0x6e, 0x63, 0x68, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, + 0x77, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x57, + 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x10, 0x03, 0x42, + 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, + 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, + 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, } var ( diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto index 0ef1a5ffa..81c6ae3b7 100644 --- a/cmd/release_config/release_config_proto/build_flags_src.proto +++ b/cmd/release_config/release_config_proto/build_flags_src.proto @@ -39,18 +39,18 @@ option go_package = "android/soong/release_config/release_config_proto"; // com.android.1mypackage are invalid enum workflow { - UNSPECIFIED_workflow = 0; + WorkflowUnspecified = 0; // Boolean value flags that progress from false to true. - LAUNCH = 1; + WorkflowLaunch = 1; // String value flags that get updated with new version strings to control // prebuilt inclusion. - PREBUILT = 2; + WorkflowPrebuilt = 2; // Manually managed outside flags. These are likely to be found in a // different directory than flags with other workflows. - MANUAL = 3; + WorkflowManual = 3; } message value { diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go index 6ab3b8890..e168edc63 100644 --- a/etc/prebuilt_etc.go +++ b/etc/prebuilt_etc.go @@ -59,6 +59,7 @@ func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("prebuilt_usr_keychars", PrebuiltUserKeyCharsFactory) ctx.RegisterModuleType("prebuilt_usr_idc", PrebuiltUserIdcFactory) ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory) + ctx.RegisterModuleType("prebuilt_overlay", PrebuiltOverlayFactory) ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory) ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory) ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory) @@ -650,6 +651,15 @@ func PrebuiltFontFactory() android.Module { return module } +// prebuilt_overlay is for a prebuilt artifact in <partition>/overlay directory. +func PrebuiltOverlayFactory() android.Module { + module := &PrebuiltEtc{} + InitPrebuiltEtcModule(module, "overlay") + // This module is device-only + android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst) + return module +} + // prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system // image. // If soc_specific property is set to true, the firmware file is installed to the diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go index 3ee234069..c44574aab 100644 --- a/etc/prebuilt_etc_test.go +++ b/etc/prebuilt_etc_test.go @@ -342,6 +342,19 @@ func TestPrebuiltFontInstallDirPath(t *testing.T) { android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) } +func TestPrebuiltOverlayInstallDirPath(t *testing.T) { + result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` + prebuilt_overlay { + name: "foo.conf", + src: "foo.conf", + } + `) + + p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) + expected := "out/soong/target/product/test_device/system/overlay" + android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) +} + func TestPrebuiltFirmwareDirPath(t *testing.T) { targetPath := "out/soong/target/product/test_device" tests := []struct { diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index d8a00e2c9..8b71e85c7 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -152,6 +152,7 @@ func filesystemFactory() android.Module { func initFilesystemModule(module *filesystem) { module.AddProperties(&module.properties) android.InitPackageModule(module) + module.PackagingBase.DepsCollectFirstTargetOnly = true android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) android.InitDefaultableModule(module) } diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go index 015d39aab..2dc8c21e0 100644 --- a/filesystem/filesystem_test.go +++ b/filesystem/filesystem_test.go @@ -508,11 +508,7 @@ func TestFilterOutUnsupportedArches(t *testing.T) { android_filesystem { name: "fs_64_32", compile_multilib: "both", - multilib: { - first: { - deps: ["foo"], - }, - }, + deps: ["foo"], } cc_binary { diff --git a/java/androidmk.go b/java/androidmk.go index 4f740b231..43160741b 100644 --- a/java/androidmk.go +++ b/java/androidmk.go @@ -17,7 +17,6 @@ package java import ( "fmt" "io" - "strings" "android/soong/android" @@ -413,23 +412,6 @@ func (app *AndroidApp) AndroidMkEntries() []android.AndroidMkEntries { if app.embeddedJniLibs { jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String()) entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String()) - } else { - for _, jniLib := range app.jniLibs { - entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name) - var partitionTag string - - // Mimic the creation of partition_tag in build/make, - // which defaults to an empty string when the partition is system. - // Otherwise, capitalize with a leading _ - if jniLib.partition == "system" { - partitionTag = "" - } else { - split := strings.Split(jniLib.partition, "/") - partitionTag = "_" + strings.ToUpper(split[len(split)-1]) - } - entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(), - jniLib.name+":"+partitionTag) - } } if len(app.jniCoverageOutputs) > 0 { diff --git a/java/androidmk_test.go b/java/androidmk_test.go index 2978a40aa..875e06f11 100644 --- a/java/androidmk_test.go +++ b/java/androidmk_test.go @@ -19,9 +19,6 @@ import ( "testing" "android/soong/android" - "android/soong/cc" - - "github.com/google/blueprint/proptools" ) func TestRequired(t *testing.T) { @@ -255,149 +252,3 @@ func TestGetOverriddenPackages(t *testing.T) { android.AssertDeepEquals(t, "overrides property", expected.overrides, actual) } } - -func TestJniPartition(t *testing.T) { - bp := ` - cc_library { - name: "libjni_system", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - } - - cc_library { - name: "libjni_system_ext", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - system_ext_specific: true, - } - - cc_library { - name: "libjni_odm", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - device_specific: true, - } - - cc_library { - name: "libjni_product", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - product_specific: true, - } - - cc_library { - name: "libjni_vendor", - system_shared_libs: [], - sdk_version: "current", - stl: "none", - soc_specific: true, - } - - android_app { - name: "test_app_system_jni_system", - privileged: true, - platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system"], - } - - android_app { - name: "test_app_system_jni_system_ext", - privileged: true, - platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system_ext"], - } - - android_app { - name: "test_app_system_ext_jni_system", - privileged: true, - platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system"], - system_ext_specific: true - } - - android_app { - name: "test_app_system_ext_jni_system_ext", - sdk_version: "core_platform", - jni_libs: ["libjni_system_ext"], - system_ext_specific: true - } - - android_app { - name: "test_app_product_jni_product", - sdk_version: "core_platform", - jni_libs: ["libjni_product"], - product_specific: true - } - - android_app { - name: "test_app_vendor_jni_odm", - sdk_version: "core_platform", - jni_libs: ["libjni_odm"], - soc_specific: true - } - - android_app { - name: "test_app_odm_jni_vendor", - sdk_version: "core_platform", - jni_libs: ["libjni_vendor"], - device_specific: true - } - android_app { - name: "test_app_system_jni_multiple", - privileged: true, - platform_apis: true, - certificate: "platform", - jni_libs: ["libjni_system", "libjni_system_ext"], - } - android_app { - name: "test_app_vendor_jni_multiple", - sdk_version: "core_platform", - jni_libs: ["libjni_odm", "libjni_vendor"], - soc_specific: true - } - ` - arch := "arm64" - ctx := android.GroupFixturePreparers( - PrepareForTestWithJavaDefaultModules, - cc.PrepareForTestWithCcDefaultModules, - android.PrepareForTestWithAndroidMk, - android.FixtureModifyConfig(func(config android.Config) { - config.TestProductVariables.DeviceArch = proptools.StringPtr(arch) - }), - ). - RunTestWithBp(t, bp) - testCases := []struct { - name string - partitionNames []string - partitionTags []string - }{ - {"test_app_system_jni_system", []string{"libjni_system"}, []string{""}}, - {"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}}, - {"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}}, - {"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}}, - {"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}}, - {"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}}, - {"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}}, - {"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}}, - {"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}}, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - mod := ctx.ModuleForTests(test.name, "android_common").Module() - entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0] - for i := range test.partitionNames { - actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i] - expected := test.partitionNames[i] + ":" + test.partitionTags[i] - android.AssertStringEquals(t, "Expected and actual differ", expected, actual) - } - }) - } -} diff --git a/java/app.go b/java/app.go index f05b8a7cd..377851e39 100644 --- a/java/app.go +++ b/java/app.go @@ -274,16 +274,37 @@ func (a *AndroidApp) DepsMutator(ctx android.BottomUpMutatorContext) { variation := append(jniTarget.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"}) - // If the app builds against an Android SDK use the SDK variant of JNI dependencies - // unless jni_uses_platform_apis is set. - // Don't require the SDK variant for apps that are shipped on vendor, etc., as they already - // have stable APIs through the VNDK. - if (usesSDK && !a.RequiresStableAPIs(ctx) && - !Bool(a.appProperties.Jni_uses_platform_apis)) || - Bool(a.appProperties.Jni_uses_sdk_apis) { + // Test whether to use the SDK variant or the non-SDK variant of JNI dependencies. + // Many factors are considered here. + // 1. Basically, the selection follows whether the app has sdk_version set or not. + jniUsesSdkVariant := usesSDK + // 2. However, jni_uses_platform_apis and jni_uses_sdk_apis can override it + if Bool(a.appProperties.Jni_uses_sdk_apis) { + jniUsesSdkVariant = true + } + if Bool(a.appProperties.Jni_uses_platform_apis) { + jniUsesSdkVariant = false + } + // 3. Then the use of SDK variant is again prohibited for the following cases: + // 3.1. the app is shipped on unbundled partitions like vendor. Since the entire + // partition (not only the app) is considered unbudled, there's no need to use the + // SDK variant. + // 3.2. the app doesn't support embedding the JNI libs + if a.RequiresStableAPIs(ctx) || !a.shouldEmbedJnis(ctx) { + jniUsesSdkVariant = false + } + if jniUsesSdkVariant { variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"}) } - ctx.AddFarVariationDependencies(variation, jniLibTag, a.appProperties.Jni_libs...) + + // Use the installable dep tag when the JNIs are not embedded + var tag dependencyTag + if a.shouldEmbedJnis(ctx) { + tag = jniLibTag + } else { + tag = jniInstallTag + } + ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...) } for _, aconfig_declaration := range a.aaptProperties.Flags_packages { ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration) @@ -334,6 +355,7 @@ func (a *AndroidTestHelperApp) GenerateAndroidBuildActions(ctx android.ModuleCon func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { a.checkAppSdkVersions(ctx) + a.checkEmbedJnis(ctx) a.generateAndroidBuildActions(ctx) a.generateJavaUsedByApex(ctx) } @@ -378,6 +400,17 @@ func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) { a.checkSdkVersions(ctx) } +// Ensures that use_embedded_native_libs are set for apk-in-apex +func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) { + apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) + apkInApex := !apexInfo.IsForPlatform() + hasJnis := len(a.appProperties.Jni_libs) > 0 + + if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) { + ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true") + } +} + // If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it. // This check is enforced for "updatable" APKs (including APK-in-APEX). func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) { @@ -433,9 +466,9 @@ func (a *AndroidApp) shouldUncompressDex(ctx android.ModuleContext) bool { } func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool { - apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider) return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) || - !apexInfo.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs + Bool(a.appProperties.Updatable) || + a.appProperties.AlwaysPackageNativeLibs } func generateAaptRenamePackageFlags(packageName string, renameResourcesPackage bool) []string { @@ -829,7 +862,9 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { dexJarFile, packageResources := a.dexBuildActions(ctx) - jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis)) + // No need to check the SDK version of the JNI deps unless we embed them + checkNativeSdkVersion := a.shouldEmbedJnis(ctx) && !Bool(a.appProperties.Jni_uses_platform_apis) + jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), checkNativeSdkVersion) jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx) if ctx.Failed() { @@ -911,6 +946,22 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { installed := ctx.InstallFile(a.installDir, extra.Base(), extra) extraInstalledPaths = append(extraInstalledPaths, installed) } + // If we don't embed jni libs, make sure that those are installed along with the + // app, and also place symlinks to the installed paths under the lib/<arch> + // directory of the app installation directory. ex: + // /system/app/MyApp/lib/arm64/libfoo.so -> /system/lib64/libfoo.so + if !a.embeddedJniLibs { + for _, jniLib := range jniLibs { + archStr := jniLib.target.Arch.ArchType.String() + symlinkDir := a.installDir.Join(ctx, "lib", archStr) + for _, installedLib := range jniLib.installPaths { + // install the symlink itself + symlinkName := installedLib.Base() + symlinkTarget := android.InstallPathToOnDevicePath(ctx, installedLib) + ctx.InstallAbsoluteSymlink(symlinkDir, symlinkName, symlinkTarget) + } + } + } ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...) } @@ -998,6 +1049,7 @@ func collectJniDeps(ctx android.ModuleContext, coverageFile: dep.CoverageOutputFile(), unstrippedFile: dep.UnstrippedOutputFile(), partition: dep.Partition(), + installPaths: dep.FilesToInstall(), }) } else if ctx.Config().AllowMissingDependencies() { ctx.AddMissingDependencies([]string{otherName}) diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index 82a34ca9d..4d3d794d8 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -590,13 +590,36 @@ func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) // So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS. // TODO(b/202896428): Add better way to handle this. _, unknown = android.RemoveFromList("android.car-module", unknown) - if isActiveModule(ctx, ctx.Module()) && len(unknown) > 0 { - ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown) + if isApexVariant(ctx) && len(unknown) > 0 { + if android.IsModulePrebuilt(ctx.Module()) { + // prebuilt bcpf. the validation of this will be done at the top-level apex + providerClasspathFragmentValidationInfoProvider(ctx, unknown) + } else if !disableSourceApexVariant(ctx) { + // source bcpf, and prebuilt apex are not selected. + ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown) + } } } return jars } +var ClasspathFragmentValidationInfoProvider = blueprint.NewProvider[ClasspathFragmentValidationInfo]() + +type ClasspathFragmentValidationInfo struct { + ClasspathFragmentModuleName string + UnknownJars []string +} + +// Set a provider with the list of jars that have not been added to PRODUCT_APEX_BOOT_JARS +// The validation will be done in the ctx of the top-level _selected_ apex +func providerClasspathFragmentValidationInfoProvider(ctx android.ModuleContext, unknown []string) { + info := ClasspathFragmentValidationInfo{ + ClasspathFragmentModuleName: ctx.ModuleName(), + UnknownJars: unknown, + } + android.SetProvider(ctx, ClasspathFragmentValidationInfoProvider, info) +} + // generateHiddenAPIBuildActions generates all the hidden API related build rules. func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput { diff --git a/java/java.go b/java/java.go index 0df96a3a5..05ef5d04f 100644 --- a/java/java.go +++ b/java/java.go @@ -366,14 +366,14 @@ type dependencyTag struct { toolchain bool static bool + + installable bool } -// installDependencyTag is a dependency tag that is annotated to cause the installed files of the -// dependency to be installed when the parent module is installed. -type installDependencyTag struct { - blueprint.BaseDependencyTag - android.InstallAlwaysNeededDependencyTag - name string +var _ android.InstallNeededDependencyTag = (*dependencyTag)(nil) + +func (d dependencyTag) InstallDepNeeded() bool { + return d.installable } func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation { @@ -405,7 +405,7 @@ func makeUsesLibraryDependencyTag(sdkVersion int, optional bool) usesLibraryDepe } func IsJniDepTag(depTag blueprint.DependencyTag) bool { - return depTag == jniLibTag + return depTag == jniLibTag || depTag == jniInstallTag } var ( @@ -434,8 +434,8 @@ var ( javaApiContributionTag = dependencyTag{name: "java-api-contribution"} depApiSrcsTag = dependencyTag{name: "dep-api-srcs"} aconfigDeclarationTag = dependencyTag{name: "aconfig-declaration"} - jniInstallTag = installDependencyTag{name: "jni install"} - binaryInstallTag = installDependencyTag{name: "binary install"} + jniInstallTag = dependencyTag{name: "jni install", runtimeLinked: true, installable: true} + binaryInstallTag = dependencyTag{name: "binary install", runtimeLinked: true, installable: true} usesLibReqTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false) usesLibOptTag = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true) usesLibCompat28OptTag = makeUsesLibraryDependencyTag(28, true) @@ -491,6 +491,7 @@ type jniLib struct { coverageFile android.OptionalPath unstrippedFile android.Path partition string + installPaths android.InstallPaths } func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) { diff --git a/rust/builder.go b/rust/builder.go index 4f45e33c1..1ce92f4ec 100644 --- a/rust/builder.go +++ b/rust/builder.go @@ -21,6 +21,7 @@ import ( "github.com/google/blueprint" "android/soong/android" + "android/soong/cc" "android/soong/rust/config" ) @@ -118,42 +119,129 @@ type buildOutput struct { func init() { pctx.HostBinToolVariable("SoongZipCmd", "soong_zip") + cc.TransformRlibstoStaticlib = TransformRlibstoStaticlib +} + +type transformProperties struct { + crateName string + targetTriple string + is64Bit bool + bootstrap bool + inRecovery bool + inRamdisk bool + inVendorRamdisk bool + cargoOutDir android.OptionalPath + synthetic bool + crateType string +} + +// Populates a standard transformProperties struct for Rust modules +func getTransformProperties(ctx ModuleContext, crateType string) transformProperties { + module := ctx.RustModule() + return transformProperties{ + crateName: module.CrateName(), + is64Bit: ctx.toolchain().Is64Bit(), + targetTriple: ctx.toolchain().RustTriple(), + bootstrap: module.Bootstrap(), + inRecovery: module.InRecovery(), + inRamdisk: module.InRamdisk(), + inVendorRamdisk: module.InVendorRamdisk(), + cargoOutDir: module.compiler.cargoOutDir(), + + // crateType indicates what type of crate to build + crateType: crateType, + + // synthetic indicates whether this is an actual Rust module or not + synthetic: false, + } } func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + if ctx.RustModule().compiler.Thinlto() { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + } - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin") + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "bin")) } func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib") + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "rlib")) +} + +func TransformRlibstoStaticlib(ctx android.ModuleContext, mainSrc android.Path, deps []cc.RustRlibDep, + outputFile android.WritablePath) android.Path { + + var rustPathDeps PathDeps + var rustFlags Flags + + for _, rlibDep := range deps { + rustPathDeps.RLibs = append(rustPathDeps.RLibs, RustLibrary{Path: rlibDep.LibPath, CrateName: rlibDep.CrateName}) + rustPathDeps.linkDirs = append(rustPathDeps.linkDirs, rlibDep.LinkDirs...) + } + + ccModule := ctx.(cc.ModuleContext).Module().(*cc.Module) + toolchain := config.FindToolchain(ctx.Os(), ctx.Arch()) + t := transformProperties{ + // Crate name can be a predefined value as this is a staticlib and + // it does not need to be unique. The crate name is used for name + // mangling, but it is mixed with the metadata for that purpose, which we + // already set to the module name. + crateName: "generated_rust_staticlib", + is64Bit: toolchain.Is64Bit(), + targetTriple: toolchain.RustTriple(), + bootstrap: ccModule.Bootstrap(), + inRecovery: ccModule.InRecovery(), + inRamdisk: ccModule.InRamdisk(), + inVendorRamdisk: ccModule.InVendorRamdisk(), + + // crateType indicates what type of crate to build + crateType: "staticlib", + + // synthetic indicates whether this is an actual Rust module or not + synthetic: true, + } + + rustFlags = CommonDefaultFlags(ctx, toolchain, rustFlags) + rustFlags = CommonLibraryCompilerFlags(ctx, rustFlags) + rustFlags.GlobalRustFlags = append(rustFlags.GlobalRustFlags, "-C lto=thin") + + rustFlags.EmitXrefs = false + + return transformSrctoCrate(ctx, mainSrc, rustPathDeps, rustFlags, outputFile, t).outputFile } func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + if ctx.RustModule().compiler.Thinlto() { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + } - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib") + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "dylib")) } func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib") + if ctx.RustModule().compiler.Thinlto() { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + } + + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "staticlib")) } func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib") + if ctx.RustModule().compiler.Thinlto() { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin") + } + + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "cdylib")) } func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags, outputFile android.WritablePath) buildOutput { - return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro") + return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, getTransformProperties(ctx, "proc-macro")) } func rustLibsToPaths(libs RustLibraries) android.Paths { @@ -185,18 +273,18 @@ func makeLibFlags(deps PathDeps) []string { return libFlags } -func rustEnvVars(ctx ModuleContext, deps PathDeps) []string { +func rustEnvVars(ctx android.ModuleContext, deps PathDeps, crateName string, cargoOutDir android.OptionalPath) []string { var envVars []string // libstd requires a specific environment variable to be set. This is // not officially documented and may be removed in the future. See // https://github.com/rust-lang/rust/blob/master/library/std/src/env.rs#L866. - if ctx.RustModule().CrateName() == "std" { - envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.RustModule().Arch().ArchType]) + if crateName == "std" { + envVars = append(envVars, "STD_ENV_ARCH="+config.StdEnvArch[ctx.Arch().ArchType]) } - if len(deps.SrcDeps) > 0 { - moduleGenDir := ctx.RustModule().compiler.cargoOutDir() + if len(deps.SrcDeps) > 0 && cargoOutDir.Valid() { + moduleGenDir := cargoOutDir // We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this) // assumes that paths are relative to the source file. var outDirPrefix string @@ -215,13 +303,15 @@ func rustEnvVars(ctx ModuleContext, deps PathDeps) []string { envVars = append(envVars, "ANDROID_RUST_VERSION="+config.GetRustVersion(ctx)) - if ctx.RustModule().compiler.cargoEnvCompat() { - if bin, ok := ctx.RustModule().compiler.(*binaryDecorator); ok { + if rustMod, ok := ctx.Module().(*Module); ok && rustMod.compiler.cargoEnvCompat() { + // We only emulate cargo environment variables for 3p code, which is only ever built + // by defining a Rust module, so we only need to set these for true Rust modules. + if bin, ok := rustMod.compiler.(*binaryDecorator); ok { envVars = append(envVars, "CARGO_BIN_NAME="+bin.getStem(ctx)) } - envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName()) - envVars = append(envVars, "CARGO_PKG_NAME="+ctx.RustModule().CrateName()) - pkgVersion := ctx.RustModule().compiler.cargoPkgVersion() + envVars = append(envVars, "CARGO_CRATE_NAME="+crateName) + envVars = append(envVars, "CARGO_PKG_NAME="+crateName) + pkgVersion := rustMod.compiler.cargoPkgVersion() if pkgVersion != "" { envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion) @@ -245,8 +335,8 @@ func rustEnvVars(ctx ModuleContext, deps PathDeps) []string { return envVars } -func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags, - outputFile android.WritablePath, crateType string) buildOutput { +func transformSrctoCrate(ctx android.ModuleContext, main android.Path, deps PathDeps, flags Flags, + outputFile android.WritablePath, t transformProperties) buildOutput { var inputs android.Paths var implicits android.Paths @@ -256,23 +346,21 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl var earlyLinkFlags string output.outputFile = outputFile - crateName := ctx.RustModule().CrateName() - targetTriple := ctx.toolchain().RustTriple() - envVars := rustEnvVars(ctx, deps) + envVars := rustEnvVars(ctx, deps, t.crateName, t.cargoOutDir) inputs = append(inputs, main) // Collect rustc flags rustcFlags = append(rustcFlags, flags.GlobalRustFlags...) rustcFlags = append(rustcFlags, flags.RustFlags...) - rustcFlags = append(rustcFlags, "--crate-type="+crateType) - if crateName != "" { - rustcFlags = append(rustcFlags, "--crate-name="+crateName) + rustcFlags = append(rustcFlags, "--crate-type="+t.crateType) + if t.crateName != "" { + rustcFlags = append(rustcFlags, "--crate-name="+t.crateName) } - if targetTriple != "" { - rustcFlags = append(rustcFlags, "--target="+targetTriple) - linkFlags = append(linkFlags, "-target "+targetTriple) + if t.targetTriple != "" { + rustcFlags = append(rustcFlags, "--target="+t.targetTriple) + linkFlags = append(linkFlags, "-target "+t.targetTriple) } // Suppress an implicit sysroot @@ -302,9 +390,9 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl linkFlags = append(linkFlags, flags.LinkFlags...) // Check if this module needs to use the bootstrap linker - if ctx.RustModule().Bootstrap() && !ctx.RustModule().InRecovery() && !ctx.RustModule().InRamdisk() && !ctx.RustModule().InVendorRamdisk() { + if t.bootstrap && !t.inRecovery && !t.inRamdisk && !t.inVendorRamdisk { dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker" - if ctx.toolchain().Is64Bit() { + if t.is64Bit { dynamicLinker += "64" } linkFlags = append(linkFlags, dynamicLinker) @@ -326,49 +414,56 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl orderOnly = append(orderOnly, deps.SharedLibs...) - if len(deps.SrcDeps) > 0 { - moduleGenDir := ctx.RustModule().compiler.cargoOutDir() - var outputs android.WritablePaths - - for _, genSrc := range deps.SrcDeps { - if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) { - ctx.PropertyErrorf("srcs", - "multiple source providers generate the same filename output: "+genSrc.Base()) + if !t.synthetic { + // Only worry about OUT_DIR for actual Rust modules. + // Libraries built from cc use generated source, and do not utilize OUT_DIR. + if len(deps.SrcDeps) > 0 { + var outputs android.WritablePaths + + for _, genSrc := range deps.SrcDeps { + if android.SuffixInList(outputs.Strings(), genSubDir+genSrc.Base()) { + ctx.PropertyErrorf("srcs", + "multiple source providers generate the same filename output: "+genSrc.Base()) + } + outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base())) } - outputs = append(outputs, android.PathForModuleOut(ctx, genSubDir+genSrc.Base())) - } - ctx.Build(pctx, android.BuildParams{ - Rule: cp, - Description: "cp " + moduleGenDir.Path().Rel(), - Outputs: outputs, - Inputs: deps.SrcDeps, - Args: map[string]string{ - "outDir": moduleGenDir.String(), - }, - }) - implicits = append(implicits, outputs.Paths()...) + ctx.Build(pctx, android.BuildParams{ + Rule: cp, + Description: "cp " + t.cargoOutDir.Path().Rel(), + Outputs: outputs, + Inputs: deps.SrcDeps, + Args: map[string]string{ + "outDir": t.cargoOutDir.String(), + }, + }) + implicits = append(implicits, outputs.Paths()...) + } } - if flags.Clippy { - clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy") - ctx.Build(pctx, android.BuildParams{ - Rule: clippyDriver, - Description: "clippy " + main.Rel(), - Output: clippyFile, - ImplicitOutputs: nil, - Inputs: inputs, - Implicits: implicits, - OrderOnly: orderOnly, - Args: map[string]string{ - "rustcFlags": strings.Join(rustcFlags, " "), - "libFlags": strings.Join(libFlags, " "), - "clippyFlags": strings.Join(flags.ClippyFlags, " "), - "envVars": strings.Join(envVars, " "), - }, - }) - // Declare the clippy build as an implicit dependency of the original crate. - implicits = append(implicits, clippyFile) + if !t.synthetic { + // Only worry about clippy for actual Rust modules. + // Libraries built from cc use generated source, and don't need to run clippy. + if flags.Clippy { + clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy") + ctx.Build(pctx, android.BuildParams{ + Rule: clippyDriver, + Description: "clippy " + main.Rel(), + Output: clippyFile, + ImplicitOutputs: nil, + Inputs: inputs, + Implicits: implicits, + OrderOnly: orderOnly, + Args: map[string]string{ + "rustcFlags": strings.Join(rustcFlags, " "), + "libFlags": strings.Join(libFlags, " "), + "clippyFlags": strings.Join(flags.ClippyFlags, " "), + "envVars": strings.Join(envVars, " "), + }, + }) + // Declare the clippy build as an implicit dependency of the original crate. + implicits = append(implicits, clippyFile) + } } ctx.Build(pctx, android.BuildParams{ @@ -389,25 +484,28 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl }, }) - if flags.EmitXrefs { - kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip") - ctx.Build(pctx, android.BuildParams{ - Rule: kytheExtract, - Description: "Xref Rust extractor " + main.Rel(), - Output: kytheFile, - Inputs: inputs, - Implicits: implicits, - OrderOnly: orderOnly, - Args: map[string]string{ - "rustcFlags": strings.Join(rustcFlags, " "), - "linkFlags": strings.Join(linkFlags, " "), - "libFlags": strings.Join(libFlags, " "), - "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "), - "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "), - "envVars": strings.Join(envVars, " "), - }, - }) - output.kytheFile = kytheFile + if !t.synthetic { + // Only emit xrefs for true Rust modules. + if flags.EmitXrefs { + kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip") + ctx.Build(pctx, android.BuildParams{ + Rule: kytheExtract, + Description: "Xref Rust extractor " + main.Rel(), + Output: kytheFile, + Inputs: inputs, + Implicits: implicits, + OrderOnly: orderOnly, + Args: map[string]string{ + "rustcFlags": strings.Join(rustcFlags, " "), + "linkFlags": strings.Join(linkFlags, " "), + "libFlags": strings.Join(libFlags, " "), + "crtBegin": strings.Join(deps.CrtBegin.Strings(), " "), + "crtEnd": strings.Join(deps.CrtEnd.Strings(), " "), + "envVars": strings.Join(envVars, " "), + }, + }) + output.kytheFile = kytheFile + } } return output } @@ -457,7 +555,7 @@ func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps, Args: map[string]string{ "rustdocFlags": strings.Join(rustdocFlags, " "), "outDir": docDir.String(), - "envVars": strings.Join(rustEnvVars(ctx, deps), " "), + "envVars": strings.Join(rustEnvVars(ctx, deps, crateName, ctx.RustModule().compiler.cargoOutDir()), " "), }, }) diff --git a/rust/builder_test.go b/rust/builder_test.go index 639f6d4d4..c093ac4df 100644 --- a/rust/builder_test.go +++ b/rust/builder_test.go @@ -46,6 +46,9 @@ func TestSourceProviderCollision(t *testing.T) { } func TestCompilationOutputFiles(t *testing.T) { + + // Note: Rustdoc output is produced for the PrimaryModule, so if the variant + // order changes, then it may be produced for a different variant. ctx := testRust(t, ` rust_library { name: "libfizz_buzz", @@ -126,6 +129,16 @@ func TestCompilationOutputFiles(t *testing.T) { }, }, { + testName: "rust_ffi rlib", + moduleName: "librust_ffi", + variant: "android_arm64_armv8-a_rlib_rlib-std", + expectedFiles: []string{ + "out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib", + "out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/librust_ffi.rlib.clippy", + "out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_rlib_rlib-std/meta_lic", + }, + }, + { testName: "rust_ffi shared", moduleName: "librust_ffi", variant: "android_arm64_armv8-a_shared", diff --git a/rust/compiler.go b/rust/compiler.go index 03fdf2b7a..efc3deef3 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -47,6 +47,7 @@ type compiler interface { edition() string features() []string rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath + Thinlto() bool // Output directory in which source-generated code from dependencies is // copied. This is equivalent to Cargo's OUT_DIR variable. @@ -231,6 +232,15 @@ type BaseCompilerProperties struct { // If cargo_env_compat is true, sets the CARGO_PKG_VERSION env var to this value. Cargo_pkg_version *string + + // Control whether LTO is used for the final (Rust) linkage. This does not impact + // cross-language LTO. + Lto struct { + // Whether thin LTO should be enabled. By default this is true. + // LTO provides such a large code size benefit for Rust, this should always + // be enabled for production builds unless there's a clear need to disable it. + Thin *bool `android:"arch_variant"` + } `android:"arch_variant"` } type baseCompiler struct { @@ -273,6 +283,11 @@ func (compiler *baseCompiler) Disabled() bool { return false } +// Thin LTO is enabled by default. +func (compiler *baseCompiler) Thinlto() bool { + return BoolDefault(compiler.Properties.Lto.Thin, true) +} + func (compiler *baseCompiler) SetDisabled() { panic("baseCompiler does not implement SetDisabled()") } @@ -322,9 +337,9 @@ func (compiler *baseCompiler) compilerProps() []interface{} { return []interface{}{&compiler.Properties} } -func (compiler *baseCompiler) cfgsToFlags() []string { +func cfgsToFlags(cfgs []string) []string { flags := []string{} - for _, cfg := range compiler.Properties.Cfgs { + for _, cfg := range cfgs { flags = append(flags, "--cfg '"+cfg+"'") } @@ -351,23 +366,61 @@ func (compiler *baseCompiler) featureFlags(ctx ModuleContext, flags Flags) Flags return flags } -func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags { - if ctx.RustModule().InVendorOrProduct() { - compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vndk") - if ctx.RustModule().InVendor() { - compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_vendor") - } else if ctx.RustModule().InProduct() { - compiler.Properties.Cfgs = append(compiler.Properties.Cfgs, "android_product") +func CommonDefaultCfgFlags(flags Flags, vendor bool, product bool) Flags { + var cfgs []string + if vendor || product { + cfgs = append(cfgs, "android_vndk") + if vendor { + cfgs = append(cfgs, "android_vendor") + } else if product { + cfgs = append(cfgs, "android_product") } } - flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...) - flags.RustdocFlags = append(flags.RustdocFlags, compiler.cfgsToFlags()...) + flags.RustFlags = append(flags.RustFlags, cfgsToFlags(cfgs)...) + flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(cfgs)...) + return flags +} + +func (compiler *baseCompiler) cfgFlags(ctx ModuleContext, flags Flags) Flags { + flags = CommonDefaultCfgFlags(flags, ctx.RustModule().InVendor(), ctx.RustModule().InProduct()) + + flags.RustFlags = append(flags.RustFlags, cfgsToFlags(compiler.Properties.Cfgs)...) + flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(compiler.Properties.Cfgs)...) + + return flags +} + +func CommonDefaultFlags(ctx android.ModuleContext, toolchain config.Toolchain, flags Flags) Flags { + flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...) + flags.GlobalRustFlags = append(flags.GlobalRustFlags, toolchain.ToolchainRustFlags()) + flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, toolchain.ToolchainLinkFlags()) + flags.EmitXrefs = ctx.Config().EmitXrefRules() + + if ctx.Host() && !ctx.Windows() { + flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...) + } + + if ctx.Os() == android.Linux { + // Add -lc, -lrt, -ldl, -lpthread, -lm and -lgcc_s to glibc builds to match + // the default behavior of device builds. + flags.LinkFlags = append(flags.LinkFlags, config.LinuxHostGlobalLinkFlags...) + } else if ctx.Os() == android.Darwin { + // Add -lc, -ldl, -lpthread and -lm to glibc darwin builds to match the default + // behavior of device builds. + flags.LinkFlags = append(flags.LinkFlags, + "-lc", + "-ldl", + "-lpthread", + "-lm", + ) + } return flags } func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flags { + flags = CommonDefaultFlags(ctx, ctx.toolchain(), flags) lintFlags, err := config.RustcLintsForDir(ctx.ModuleDir(), compiler.Properties.Lints) if err != nil { ctx.PropertyErrorf("lints", err.Error()) @@ -396,29 +449,7 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags) Flag flags.RustFlags = append(flags.RustFlags, "--edition="+compiler.edition()) flags.RustdocFlags = append(flags.RustdocFlags, "--edition="+compiler.edition()) flags.LinkFlags = append(flags.LinkFlags, compiler.Properties.Ld_flags...) - flags.GlobalRustFlags = append(flags.GlobalRustFlags, config.GlobalRustFlags...) - flags.GlobalRustFlags = append(flags.GlobalRustFlags, ctx.toolchain().ToolchainRustFlags()) - flags.GlobalLinkFlags = append(flags.GlobalLinkFlags, ctx.toolchain().ToolchainLinkFlags()) - flags.EmitXrefs = ctx.Config().EmitXrefRules() - if ctx.Host() && !ctx.Windows() { - flags.LinkFlags = append(flags.LinkFlags, cc.RpathFlags(ctx)...) - } - - if ctx.Os() == android.Linux { - // Add -lc, -lrt, -ldl, -lpthread, -lm and -lgcc_s to glibc builds to match - // the default behavior of device builds. - flags.LinkFlags = append(flags.LinkFlags, config.LinuxHostGlobalLinkFlags...) - } else if ctx.Os() == android.Darwin { - // Add -lc, -ldl, -lpthread and -lm to glibc darwin builds to match the default - // behavior of device builds. - flags.LinkFlags = append(flags.LinkFlags, - "-lc", - "-ldl", - "-lpthread", - "-lm", - ) - } return flags } @@ -568,11 +599,11 @@ func (compiler *baseCompiler) installTestData(ctx ModuleContext, data []android. compiler.installDeps = append(compiler.installDeps, installedData...) } -func (compiler *baseCompiler) getStem(ctx ModuleContext) string { +func (compiler *baseCompiler) getStem(ctx android.ModuleContext) string { return compiler.getStemWithoutSuffix(ctx) + String(compiler.Properties.Suffix) } -func (compiler *baseCompiler) getStemWithoutSuffix(ctx BaseModuleContext) string { +func (compiler *baseCompiler) getStemWithoutSuffix(ctx android.BaseModuleContext) string { stem := ctx.ModuleName() if String(compiler.Properties.Stem) != "" { stem = String(compiler.Properties.Stem) diff --git a/rust/compiler_test.go b/rust/compiler_test.go index 89f4d1abf..4caa12b3e 100644 --- a/rust/compiler_test.go +++ b/rust/compiler_test.go @@ -63,6 +63,35 @@ func TestCfgsToFlags(t *testing.T) { } } +func TestLtoFlag(t *testing.T) { + ctx := testRust(t, ` + rust_library_host { + name: "libfoo", + srcs: ["foo.rs"], + crate_name: "foo", + lto: { + thin: false, + } + } + + rust_library_host { + name: "libfoo_lto", + srcs: ["foo.rs"], + crate_name: "foo", + } + `) + + libfoo := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") + libfooLto := ctx.ModuleForTests("libfoo_lto", "linux_glibc_x86_64_dylib").Rule("rustc") + + if strings.Contains(libfoo.Args["rustcFlags"], "-C lto=thin") { + t.Fatalf("libfoo expected to disable lto -- rustcFlags: %#v", libfoo.Args["rustcFlags"]) + } + if !strings.Contains(libfooLto.Args["rustcFlags"], "-C lto=thin") { + t.Fatalf("libfoo expected to enable lto by default -- rustcFlags: %#v", libfooLto.Args["rustcFlags"]) + } +} + // Test that we reject multiple source files. func TestEnforceSingleSourceFile(t *testing.T) { diff --git a/rust/coverage.go b/rust/coverage.go index 91a78060d..e0e919c69 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"}}, rlibDepTag, ProfilerBuiltins) + ctx.AddVariationDependencies([]blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}, rlibDepTag, ProfilerBuiltins) } } diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go index ee28c6d3a..0d4622a7e 100644 --- a/rust/fuzz_test.go +++ b/rust/fuzz_test.go @@ -120,13 +120,17 @@ func TestCCFuzzDepBundling(t *testing.T) { } cc_fuzz { name: "fuzz_static_libtest", + static_rlibs: ["libtest_fuzzing"], + } + cc_fuzz { + name: "fuzz_staticffi_libtest", static_libs: ["libtest_fuzzing"], } - `) fuzz_shared_libtest := ctx.ModuleForTests("fuzz_shared_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface) fuzz_static_libtest := ctx.ModuleForTests("fuzz_static_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface) + fuzz_staticffi_libtest := ctx.ModuleForTests("fuzz_staticffi_libtest", "android_arm64_armv8-a_fuzzer").Module().(cc.LinkableInterface) if !strings.Contains(fuzz_shared_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") { t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_shared ('libcc_transitive_dep'): %#v", fuzz_shared_libtest.FuzzSharedLibraries().String()) @@ -134,4 +138,7 @@ func TestCCFuzzDepBundling(t *testing.T) { if !strings.Contains(fuzz_static_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") { t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_static ('libcc_transitive_dep'): %#v", fuzz_static_libtest.FuzzSharedLibraries().String()) } + if !strings.Contains(fuzz_staticffi_libtest.FuzzSharedLibraries().String(), ":libcc_transitive_dep.so") { + t.Errorf("cc_fuzz does not contain the expected bundled transitive shared libs from rust_ffi_rlib ('libcc_transitive_dep'): %#v", fuzz_staticffi_libtest.FuzzSharedLibraries().String()) + } } diff --git a/rust/image_test.go b/rust/image_test.go index ba94906c6..71e271c89 100644 --- a/rust/image_test.go +++ b/rust/image_test.go @@ -22,33 +22,45 @@ import ( "android/soong/cc" ) -// Test that cc modules can link against vendor_available rust_ffi_static libraries. +// Test that cc modules can link against vendor_available rust_ffi_rlib/rust_ffi_static libraries. func TestVendorLinkage(t *testing.T) { ctx := testRust(t, ` cc_binary { - name: "fizz_vendor", - static_libs: ["libfoo_vendor"], + name: "fizz_vendor_available", + static_libs: ["libfoo_vendor_static"], + static_rlibs: ["libfoo_vendor"], + vendor_available: true, + } + cc_binary { + name: "fizz_soc_specific", + static_rlibs: ["libfoo_vendor"], soc_specific: true, } - rust_ffi_static { + rust_ffi_rlib { name: "libfoo_vendor", crate_name: "foo", srcs: ["foo.rs"], vendor_available: true, } + rust_ffi_static { + name: "libfoo_vendor_static", + crate_name: "foo", + srcs: ["foo.rs"], + vendor_available: true, + } `) - vendorBinary := ctx.ModuleForTests("fizz_vendor", "android_vendor_arm64_armv8-a").Module().(*cc.Module) + vendorBinary := ctx.ModuleForTests("fizz_vendor_available", "android_vendor_arm64_armv8-a").Module().(*cc.Module) - if !android.InList("libfoo_vendor.vendor", vendorBinary.Properties.AndroidMkStaticLibs) { - t.Errorf("vendorBinary should have a dependency on libfoo_vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs) + if !android.InList("libfoo_vendor_static.vendor", vendorBinary.Properties.AndroidMkStaticLibs) { + t.Errorf("vendorBinary should have a dependency on libfoo_vendor_static.vendor: %#v", vendorBinary.Properties.AndroidMkStaticLibs) } } // Test that variants which use the vndk emit the appropriate cfg flag. func TestImageCfgFlag(t *testing.T) { ctx := testRust(t, ` - rust_ffi_static { + rust_ffi_shared { name: "libfoo", crate_name: "foo", srcs: ["foo.rs"], @@ -57,7 +69,7 @@ func TestImageCfgFlag(t *testing.T) { } `) - vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_static").Rule("rustc") + vendor := ctx.ModuleForTests("libfoo", "android_vendor_arm64_armv8-a_shared").Rule("rustc") if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") { t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"]) @@ -69,7 +81,7 @@ func TestImageCfgFlag(t *testing.T) { t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"]) } - product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_static").Rule("rustc") + product := ctx.ModuleForTests("libfoo", "android_product_arm64_armv8-a_shared").Rule("rustc") if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") { t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"]) } @@ -80,7 +92,7 @@ func TestImageCfgFlag(t *testing.T) { t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"]) } - system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Rule("rustc") + system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("rustc") if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") { t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"]) } @@ -93,27 +105,34 @@ func TestImageCfgFlag(t *testing.T) { } -// Test that cc modules can link against vendor_ramdisk_available rust_ffi_static libraries. +// Test that cc modules can link against vendor_ramdisk_available rust_ffi_rlib and rust_ffi_static libraries. func TestVendorRamdiskLinkage(t *testing.T) { ctx := testRust(t, ` - cc_library_static { + cc_library_shared { name: "libcc_vendor_ramdisk", - static_libs: ["libfoo_vendor_ramdisk"], + static_rlibs: ["libfoo_vendor_ramdisk"], + static_libs: ["libfoo_static_vendor_ramdisk"], system_shared_libs: [], vendor_ramdisk_available: true, } - rust_ffi_static { + rust_ffi_rlib { name: "libfoo_vendor_ramdisk", crate_name: "foo", srcs: ["foo.rs"], vendor_ramdisk_available: true, } + rust_ffi_static { + name: "libfoo_static_vendor_ramdisk", + crate_name: "foo", + srcs: ["foo.rs"], + vendor_ramdisk_available: true, + } `) - vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_static").Module().(*cc.Module) + vendorRamdiskLibrary := ctx.ModuleForTests("libcc_vendor_ramdisk", "android_vendor_ramdisk_arm64_armv8-a_shared").Module().(*cc.Module) - if !android.InList("libfoo_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) { - t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_vendor_ramdisk") + if !android.InList("libfoo_static_vendor_ramdisk.vendor_ramdisk", vendorRamdiskLibrary.Properties.AndroidMkStaticLibs) { + t.Errorf("libcc_vendor_ramdisk should have a dependency on libfoo_static_vendor_ramdisk") } } diff --git a/rust/library.go b/rust/library.go index f58a54fcc..1eb0c5ea2 100644 --- a/rust/library.go +++ b/rust/library.go @@ -37,10 +37,15 @@ func init() { android.RegisterModuleType("rust_library_host_rlib", RustLibraryRlibHostFactory) android.RegisterModuleType("rust_ffi", RustFFIFactory) android.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory) - android.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory) + android.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory) android.RegisterModuleType("rust_ffi_host", RustFFIHostFactory) android.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory) - android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory) + android.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory) + + // TODO: Remove when all instances of rust_ffi_static have been switched to rust_ffi_rlib + // Alias rust_ffi_static to the combined rust_ffi_rlib factory + android.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory) + android.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory) } type VariantLibraryProperties struct { @@ -104,6 +109,8 @@ type libraryDecorator struct { includeDirs android.Paths sourceProvider SourceProvider + isFFI bool + // table-of-contents file for cdylib crates to optimize out relinking when possible tocFile android.OptionalPath } @@ -143,6 +150,8 @@ type libraryInterface interface { BuildOnlyShared() toc() android.OptionalPath + + isFFILibrary() bool } func (library *libraryDecorator) nativeCoverage() bool { @@ -250,7 +259,7 @@ func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) aut } func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage { - if library.static() || library.MutatedProperties.VariantIsStaticStd { + if library.static() || library.MutatedProperties.VariantIsStaticStd || (library.rlib() && library.isFFILibrary()) { return RlibLinkage } else if library.baseCompiler.preferRlib() { return RlibLinkage @@ -270,8 +279,8 @@ func RustLibraryFactory() android.Module { return module.Init() } -// rust_ffi produces all FFI variants (rust_ffi_shared and -// rust_ffi_static). +// rust_ffi produces all FFI variants (rust_ffi_shared, rust_ffi_static, and +// rust_ffi_rlib). func RustFFIFactory() android.Module { module, library := NewRustLibrary(android.HostAndDeviceSupported) library.BuildOnlyFFI() @@ -300,14 +309,6 @@ func RustFFISharedFactory() android.Module { return module.Init() } -// rust_ffi_static produces a static library (Rust crate type -// "staticlib"). -func RustFFIStaticFactory() android.Module { - module, library := NewRustLibrary(android.HostAndDeviceSupported) - library.BuildOnlyStatic() - return module.Init() -} - // rust_library_host produces all Rust variants for the host // (rust_library_dylib_host and rust_library_rlib_host). func RustLibraryHostFactory() android.Module { @@ -317,7 +318,7 @@ func RustLibraryHostFactory() android.Module { } // rust_ffi_host produces all FFI variants for the host -// (rust_ffi_static_host and rust_ffi_shared_host). +// (rust_ffi_rlib_host, rust_ffi_static_host, and rust_ffi_shared_host). func RustFFIHostFactory() android.Module { module, library := NewRustLibrary(android.HostSupported) library.BuildOnlyFFI() @@ -340,14 +341,6 @@ func RustLibraryRlibHostFactory() android.Module { return module.Init() } -// rust_ffi_static_host produces a static library for the host (Rust -// crate type "staticlib"). -func RustFFIStaticHostFactory() android.Module { - module, library := NewRustLibrary(android.HostSupported) - library.BuildOnlyStatic() - return module.Init() -} - // rust_ffi_shared_host produces an shared library for the host (Rust // crate type "cdylib"). func RustFFISharedHostFactory() android.Module { @@ -356,11 +349,51 @@ func RustFFISharedHostFactory() android.Module { return module.Init() } +// rust_ffi_rlib_host produces an rlib for the host (Rust crate +// type "rlib"). +func RustFFIRlibHostFactory() android.Module { + module, library := NewRustLibrary(android.HostSupported) + library.BuildOnlyRlibStatic() + + library.isFFI = true + return module.Init() +} + +// rust_ffi_rlib produces an rlib (Rust crate type "rlib"). +func RustFFIRlibFactory() android.Module { + module, library := NewRustLibrary(android.HostAndDeviceSupported) + library.BuildOnlyRlib() + + library.isFFI = true + return module.Init() +} + +// rust_ffi_static produces a staticlib and an rlib variant +func RustFFIStaticRlibFactory() android.Module { + module, library := NewRustLibrary(android.HostAndDeviceSupported) + library.BuildOnlyRlibStatic() + + library.isFFI = true + return module.Init() +} + +// rust_ffi_static_host produces a staticlib and an rlib variant for the host +func RustFFIStaticRlibHostFactory() android.Module { + module, library := NewRustLibrary(android.HostSupported) + library.BuildOnlyRlibStatic() + + library.isFFI = true + return module.Init() +} + func (library *libraryDecorator) BuildOnlyFFI() { library.MutatedProperties.BuildDylib = false - library.MutatedProperties.BuildRlib = false + // we build rlibs for later static ffi linkage. + library.MutatedProperties.BuildRlib = true library.MutatedProperties.BuildShared = true library.MutatedProperties.BuildStatic = true + + library.isFFI = true } func (library *libraryDecorator) BuildOnlyRust() { @@ -384,11 +417,21 @@ func (library *libraryDecorator) BuildOnlyRlib() { library.MutatedProperties.BuildStatic = false } +func (library *libraryDecorator) BuildOnlyRlibStatic() { + library.MutatedProperties.BuildDylib = false + library.MutatedProperties.BuildRlib = true + library.MutatedProperties.BuildShared = false + library.MutatedProperties.BuildStatic = true + library.isFFI = true +} + func (library *libraryDecorator) BuildOnlyStatic() { library.MutatedProperties.BuildRlib = false library.MutatedProperties.BuildDylib = false library.MutatedProperties.BuildShared = false library.MutatedProperties.BuildStatic = true + + library.isFFI = true } func (library *libraryDecorator) BuildOnlyShared() { @@ -396,6 +439,12 @@ func (library *libraryDecorator) BuildOnlyShared() { library.MutatedProperties.BuildDylib = false library.MutatedProperties.BuildStatic = false library.MutatedProperties.BuildShared = true + + library.isFFI = true +} + +func (library *libraryDecorator) isFFILibrary() bool { + return library.isFFI } func NewRustLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) { @@ -446,8 +495,15 @@ func (library *libraryDecorator) sharedLibFilename(ctx ModuleContext) string { return library.getStem(ctx) + ctx.toolchain().SharedLibSuffix() } +// Library cfg flags common to all variants +func CommonLibraryCfgFlags(ctx android.ModuleContext, flags Flags) Flags { + return flags +} + func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags { flags = library.baseCompiler.cfgFlags(ctx, flags) + flags = CommonLibraryCfgFlags(ctx, flags) + if library.dylib() { // We need to add a dependency on std in order to link crates as dylibs. // The hack to add this dependency is guarded by the following cfg so @@ -455,8 +511,15 @@ func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags library.baseCompiler.Properties.Cfgs = append(library.baseCompiler.Properties.Cfgs, "android_dylib") } - flags.RustFlags = append(flags.RustFlags, library.baseCompiler.cfgsToFlags()...) - flags.RustdocFlags = append(flags.RustdocFlags, library.baseCompiler.cfgsToFlags()...) + flags.RustFlags = append(flags.RustFlags, cfgsToFlags(library.baseCompiler.Properties.Cfgs)...) + flags.RustdocFlags = append(flags.RustdocFlags, cfgsToFlags(library.baseCompiler.Properties.Cfgs)...) + + return flags +} + +// Common flags applied to all libraries irrespective of properties or variant should be included here +func CommonLibraryCompilerFlags(ctx android.ModuleContext, flags Flags) Flags { + flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName()) return flags } @@ -464,11 +527,13 @@ func (library *libraryDecorator) cfgFlags(ctx ModuleContext, flags Flags) Flags func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags { flags = library.baseCompiler.compilerFlags(ctx, flags) - flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName()) - if library.shared() || library.static() { + flags = CommonLibraryCompilerFlags(ctx, flags) + + if library.isFFI { library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...) library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Export_include_dirs)...) } + if library.shared() { if ctx.Darwin() { flags.LinkFlags = append( @@ -494,6 +559,9 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...) } + // Ensure link dirs are not duplicated + deps.linkDirs = android.FirstUniqueStrings(deps.linkDirs) + // Calculate output filename if library.rlib() { fileName = library.getStem(ctx) + ctx.toolchain().RlibSuffix() @@ -549,9 +617,10 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa library.flagExporter.exportLinkObjects(deps.linkObjects...) } - if library.static() || library.shared() { + // Since we have FFI rlibs, we need to collect their includes as well + if library.static() || library.shared() || library.rlib() { android.SetProvider(ctx, cc.FlagExporterInfoProvider, cc.FlagExporterInfo{ - IncludeDirs: library.includeDirs, + IncludeDirs: android.FirstUniquePaths(library.includeDirs), }) } @@ -666,6 +735,11 @@ func LibraryMutator(mctx android.BottomUpMutatorContext) { return } + // Don't produce rlib/dylib/source variants for shared or static variants + if library.shared() || library.static() { + return + } + 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. @@ -705,6 +779,9 @@ func LibraryMutator(mctx android.BottomUpMutatorContext) { // The source variant does not produce any library. // Disable the compilation steps. v.(*Module).compiler.SetDisabled() + case "": + // if there's an empty variant, alias it so it is the default variant + mctx.AliasVariation("") } } @@ -729,20 +806,29 @@ func LibstdMutator(mctx android.BottomUpMutatorContext) { case libraryInterface: // Only create a variant if a library is actually being built. if library.rlib() && !library.sysroot() { - 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() + // 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 + } 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 } - rlib.Properties.RustSubName += RlibStdlibSuffix } } } diff --git a/rust/library_test.go b/rust/library_test.go index 7275b66c1..1133c28ed 100644 --- a/rust/library_test.go +++ b/rust/library_test.go @@ -37,9 +37,10 @@ func TestLibraryVariants(t *testing.T) { }`) // Test all variants are being built. + libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc") libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") - libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc") + libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc") rlibCrateType := "rlib" @@ -62,6 +63,11 @@ func TestLibraryVariants(t *testing.T) { t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.Args["rustcFlags"]) } + // Test crate type for FFI rlibs is correct + if !strings.Contains(libfooFFIRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) { + t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooFFIRlib.Args["rustcFlags"]) + } + // Test crate type for C shared libraries is correct. if !strings.Contains(libfooShared.Args["rustcFlags"], "crate-type="+sharedCrateType) { t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.Args["rustcFlags"]) @@ -182,15 +188,20 @@ func TestSharedLibraryToc(t *testing.T) { func TestStaticLibraryLinkage(t *testing.T) { ctx := testRust(t, ` - rust_ffi_static { + rust_ffi { name: "libfoo", srcs: ["foo.rs"], crate_name: "foo", }`) - libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static") + libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std") + libfooStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static") if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) { + t.Errorf("Static libstd rlib expected to be a dependency of Rust rlib libraries. Rlib deps are: %#v", + libfoo.Module().(*Module).Properties.AndroidMkDylibs) + } + if !android.InList("libstd", libfooStatic.Module().(*Module).Properties.AndroidMkRlibs) { t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v", libfoo.Module().(*Module).Properties.AndroidMkDylibs) } @@ -198,6 +209,12 @@ func TestStaticLibraryLinkage(t *testing.T) { func TestNativeDependencyOfRlib(t *testing.T) { ctx := testRust(t, ` + rust_ffi_rlib { + name: "libffi_rlib", + crate_name: "ffi_rlib", + rlibs: ["librust_rlib"], + srcs: ["foo.rs"], + } rust_ffi_static { name: "libffi_static", crate_name: "ffi_static", @@ -224,10 +241,12 @@ func TestNativeDependencyOfRlib(t *testing.T) { rustRlibRlibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_rlib-std") rustRlibDylibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_dylib-std") ffiStatic := ctx.ModuleForTests("libffi_static", "android_arm64_armv8-a_static") + ffiRlib := ctx.ModuleForTests("libffi_rlib", "android_arm64_armv8-a_rlib_rlib-std") modules := []android.TestingModule{ rustRlibRlibStd, rustRlibDylibStd, + ffiRlib, ffiStatic, } @@ -290,27 +309,28 @@ func TestAutoDeps(t *testing.T) { libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std") libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib") + libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std") libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static") libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared") - for _, static := range []android.TestingModule{libfooRlib, libfooStatic} { + for _, static := range []android.TestingModule{libfooRlib, libfooStatic, libfooFFIRlib} { if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) { - t.Errorf("libbar not present as rlib dependency in static lib") + t.Errorf("libbar not present as rlib dependency in static lib: %s", static.Module().Name()) } if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) { - t.Errorf("libbar present as dynamic dependency in static lib") + t.Errorf("libbar present as dynamic dependency in static lib: %s", static.Module().Name()) } } for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} { if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) { - t.Errorf("libbar not present as dynamic dependency in dynamic lib") + t.Errorf("libbar not present as dynamic dependency in dynamic lib: %s", dyn.Module().Name()) } if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) { - t.Errorf("libbar present as rlib dependency in dynamic lib") + t.Errorf("libbar present as rlib dependency in dynamic lib: %s", dyn.Module().Name()) } if !android.InList("librlib_only", dyn.Module().(*Module).Properties.AndroidMkRlibs) { - t.Errorf("librlib_only should be selected by rustlibs as an rlib.") + t.Errorf("librlib_only should be selected by rustlibs as an rlib: %s.", dyn.Module().Name()) } } } @@ -375,6 +395,7 @@ func TestLibstdLinkage(t *testing.T) { libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module) libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module) + libbarFFIRlib := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module) // prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here. libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module) @@ -398,6 +419,12 @@ func TestLibstdLinkage(t *testing.T) { if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) { t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant") } + if !android.InList("libstd", libbarFFIRlib.Properties.AndroidMkRlibs) { + t.Errorf("Device rust_ffi_rlib does not link libstd as an rlib") + } + if !android.InList("libfoo.rlib-std", libbarFFIRlib.Properties.AndroidMkRlibs) { + t.Errorf("Device rust_ffi_rlib does not link dependent rustlib rlib-std variant") + } if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) { t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib") } diff --git a/rust/proc_macro.go b/rust/proc_macro.go index b491449f1..1ff6637a6 100644 --- a/rust/proc_macro.go +++ b/rust/proc_macro.go @@ -76,6 +76,7 @@ func (procMacro *procMacroDecorator) compile(ctx ModuleContext, flags Flags, dep srcPath := crateRootPath(ctx, procMacro) ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile) procMacro.baseCompiler.unstrippedOutputFile = outputFile + return ret } diff --git a/rust/rust.go b/rust/rust.go index e4bb99cf4..93853e534 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -158,6 +158,8 @@ type Module struct { sourceProvider SourceProvider subAndroidMkOnce map[SubAndroidMkProvider]bool + exportedLinkDirs []string + // Output file to be installed, may be stripped or unstripped. outputFile android.OptionalPath @@ -231,8 +233,8 @@ func (mod *Module) SelectedStl() string { func (mod *Module) NonCcVariants() bool { if mod.compiler != nil { - if _, ok := mod.compiler.(libraryInterface); ok { - return false + if library, ok := mod.compiler.(libraryInterface); ok { + return library.buildRlib() || library.buildDylib() } } panic(fmt.Errorf("NonCcVariants called on non-library module: %q", mod.BaseModuleName())) @@ -462,6 +464,11 @@ type PathDeps struct { linkDirs []string linkObjects []string + // exportedLinkDirs are exported linkDirs for direct rlib dependencies to + // cc_library_static dependants of rlibs. + // Track them separately from linkDirs so superfluous -L flags don't get emitted. + exportedLinkDirs []string + // Used by bindgen modules which call clang depClangFlags []string depIncludePaths android.Paths @@ -474,6 +481,9 @@ type PathDeps struct { // Paths to generated source files SrcDeps android.Paths srcProviderFiles android.Paths + + // Used by Generated Libraries + depExportedRlibs []cc.RustRlibDep } type RustLibraries []RustLibrary @@ -540,6 +550,10 @@ func (mod *Module) VndkVersion() string { return mod.Properties.VndkVersion } +func (mod *Module) ExportedCrateLinkDirs() []string { + return mod.exportedLinkDirs +} + func (mod *Module) PreventInstall() bool { return mod.Properties.PreventInstall } @@ -654,15 +668,6 @@ func (mod *Module) UnstrippedOutputFile() android.Path { return nil } -func (mod *Module) IncludeDirs() android.Paths { - if mod.compiler != nil { - if library, ok := mod.compiler.(*libraryDecorator); ok { - return library.includeDirs - } - } - panic(fmt.Errorf("IncludeDirs called on non-library module: %q", mod.BaseModuleName())) -} - func (mod *Module) SetStatic() { if mod.compiler != nil { if library, ok := mod.compiler.(libraryInterface); ok { @@ -911,6 +916,10 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } deps := mod.depsToPaths(ctx) + // Export linkDirs for CC rust generatedlibs + mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.exportedLinkDirs...) + mod.exportedLinkDirs = append(mod.exportedLinkDirs, deps.linkDirs...) + flags := Flags{ Toolchain: toolchain, } @@ -988,6 +997,9 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { if ctx.Failed() { return } + // Export your own directory as a linkDir + mod.exportedLinkDirs = append(mod.exportedLinkDirs, linkPathFromFilePath(mod.OutputFile().Path())) + } ctx.Phony("rust", ctx.RustModule().OutputFile().Path()) @@ -1218,7 +1230,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { return } - if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() { + if rustDep, ok := dep.(*Module); ok && !rustDep.Static() && !rustDep.Shared() { //Handle Rust Modules makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName) @@ -1244,9 +1256,16 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName) mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName)) + // rust_ffi rlibs may export include dirs, so collect those here. + exportedInfo, _ := android.OtherModuleProvider(ctx, dep, cc.FlagExporterInfoProvider) + depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...) + depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path())) + case procMacroDepTag: directProcMacroDeps = append(directProcMacroDeps, rustDep) mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName) + // proc_macro link dirs need to be exported, so collect those here. + depPaths.exportedLinkDirs = append(depPaths.exportedLinkDirs, linkPathFromFilePath(rustDep.OutputFile().Path())) case sourceDepTag: if _, ok := mod.sourceProvider.(*protobufDecorator); ok { @@ -1276,12 +1295,12 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { directSrcProvidersDeps = append(directSrcProvidersDeps, rustDep) } + exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider) //Append the dependencies exportedDirs, except for proc-macros which target a different arch/OS if depTag != procMacroDepTag { - exportedInfo, _ := android.OtherModuleProvider(ctx, dep, FlagExporterInfoProvider) - depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...) depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...) depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...) + depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...) } if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag { @@ -1291,6 +1310,7 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { lib.exportLinkDirs(linkDir) } } + if depTag == sourceDepTag { if _, ok := mod.sourceProvider.(*protobufDecorator); ok && mod.Source() { if _, ok := rustDep.sourceProvider.(*protobufDecorator); ok { @@ -1555,6 +1575,7 @@ 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, @@ -1570,6 +1591,8 @@ 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) } @@ -1589,7 +1612,7 @@ 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) @@ -1604,7 +1627,11 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { for _, lib := range deps.Rustlibs { srcProviderVariations := append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "source"}) + srcProviderVariations = append(srcProviderVariations, blueprint.Variation{Mutator: "link", Variation: ""}) + // 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 + // in the rust_protobuf mod.rs. if actx.OtherModuleDependencyVariantExists(srcProviderVariations, lib) { actx.AddVariationDependencies(srcProviderVariations, sourceDepTag, lib) } @@ -1616,7 +1643,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"}}...), + actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}, {Mutator: "link", Variation: ""}}...), rlibDepTag, lib) } } else { diff --git a/rust/rust_test.go b/rust/rust_test.go index 6d083f69f..8b96df8b3 100644 --- a/rust/rust_test.go +++ b/rust/rust_test.go @@ -150,15 +150,11 @@ func TestDepsTracking(t *testing.T) { host_supported: true, name: "cc_stubs_dep", } - rust_ffi_host_static { + cc_library_host_static { name: "libstatic", - srcs: ["foo.rs"], - crate_name: "static", } - rust_ffi_host_static { + cc_library_host_static { name: "libwholestatic", - srcs: ["foo.rs"], - crate_name: "wholestatic", } rust_ffi_host_shared { name: "libshared", @@ -435,6 +431,105 @@ func TestRustAliases(t *testing.T) { } } +func TestRustRlibs(t *testing.T) { + ctx := testRust(t, ` + rust_ffi_rlib { + name: "libbar", + crate_name: "bar", + srcs: ["src/lib.rs"], + export_include_dirs: ["bar_includes"] + } + + rust_ffi_rlib { + name: "libfoo", + crate_name: "foo", + srcs: ["src/lib.rs"], + export_include_dirs: ["foo_includes"] + } + + cc_library_shared { + name: "libcc_shared", + srcs:["foo.c"], + static_rlibs: ["libbar"], + } + + cc_library_static { + name: "libcc_static", + srcs:["foo.c"], + static_rlibs: ["libfoo"], + } + + cc_binary { + name: "ccBin", + srcs:["foo.c"], + static_rlibs: ["libbar"], + static_libs: ["libcc_static"], + } + `) + + libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc") + libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc") + libcc_shared_ld := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("ld") + libcc_shared_cc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("cc") + ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc") + ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld") + ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc") + + if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") { + t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"]) + } + + // Make sure there's a rustc command, and it's producing a staticlib + if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") { + t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", + "staticlib", libcc_shared_rustc.Args["rustcFlags"]) + } + + // Make sure the static lib is included in the ld command + if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/liblibcc_shared_rust_staticlib.a") { + t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v", + "libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"]) + } + + // Make sure the static lib includes are in the cc command + if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") { + t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", + "-Ibar_includes", libcc_shared_cc.Args["cFlags"]) + } + + // Make sure there's a rustc command, and it's producing a staticlib + if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") { + t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"]) + } + + // Make sure the static lib is included in the cc command + if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/libccBin_rust_staticlib.a") { + t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v", + "ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"]) + } + + // Make sure the static lib includes are in the ld command + if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") { + t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", + "-Ibar_includes", ccbin_cc.Args) + } + + // Make sure that direct dependencies and indirect dependencies are + // propagating correctly to the generated rlib. + if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") { + t.Errorf("Missing indirect dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"]) + } + if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") { + t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"]) + } + + // Test indirect includes propagation + if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") { + t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", + "-Ifoo_includes", ccbin_cc.Args) + } +} + func assertString(t *testing.T, got, expected string) { t.Helper() if got != expected { diff --git a/rust/testing.go b/rust/testing.go index 5837dccde..f31c59188 100644 --- a/rust/testing.go +++ b/rust/testing.go @@ -49,16 +49,28 @@ var PrepareForIntegrationTestWithRust = android.GroupFixturePreparers( func GatherRequiredDepsForTest() string { bp := ` rust_prebuilt_library { - name: "libstd", - crate_name: "std", - rlib: { - srcs: ["libstd.rlib"], - }, - dylib: { - srcs: ["libstd.so"], - }, - host_supported: true, - sysroot: true, + name: "libstd", + crate_name: "std", + rlib: { + srcs: ["libstd/libstd.rlib"], + }, + dylib: { + srcs: ["libstd/libstd.so"], + }, + host_supported: true, + sysroot: true, + } + rust_prebuilt_library { + name: "libcore.sysroot", + crate_name: "core", + rlib: { + srcs: ["libcore/libcore.rlib"], + }, + dylib: { + srcs: ["libcore/libcore.so"], + }, + host_supported: true, + sysroot: true, } ////////////////////////////// // Device module requirements @@ -176,10 +188,12 @@ func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) { ctx.RegisterModuleType("rust_fuzz_host", RustFuzzHostFactory) ctx.RegisterModuleType("rust_ffi", RustFFIFactory) ctx.RegisterModuleType("rust_ffi_shared", RustFFISharedFactory) - ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticFactory) + ctx.RegisterModuleType("rust_ffi_rlib", RustFFIRlibFactory) + ctx.RegisterModuleType("rust_ffi_static", RustFFIStaticRlibFactory) ctx.RegisterModuleType("rust_ffi_host", RustFFIHostFactory) ctx.RegisterModuleType("rust_ffi_host_shared", RustFFISharedHostFactory) - ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticHostFactory) + ctx.RegisterModuleType("rust_ffi_host_rlib", RustFFIRlibHostFactory) + ctx.RegisterModuleType("rust_ffi_host_static", RustFFIStaticRlibHostFactory) ctx.RegisterModuleType("rust_proc_macro", ProcMacroFactory) ctx.RegisterModuleType("rust_protobuf", RustProtobufFactory) ctx.RegisterModuleType("rust_protobuf_host", RustProtobufHostFactory) |