diff --git a/android/apex.go b/android/apex.go index 4a71b4061..f6eca86ab 100644 --- a/android/apex.go +++ b/android/apex.go @@ -153,13 +153,12 @@ type ApexModule interface { // run. DirectlyInAnyApex() bool - // Returns true in the primary variant of a module if _any_ variant of the module is - // directly in any apex. This includes host, arch, asan, etc. variants. It is unused in any - // variant that is not the primary variant. Ideally this wouldn't be used, as it incorrectly - // mixes arch variants if only one arch is in an apex, but a few places depend on it, for - // example when an ASAN variant is created before the apexMutator. Call this after - // apex.apexMutator is run. - AnyVariantDirectlyInAnyApex() bool + // NotInPlatform tells whether or not this module is included in an APEX and therefore + // shouldn't be exposed to the platform (i.e. outside of the APEX) directly. A module is + // considered to be included in an APEX either when there actually is an APEX that + // explicitly has the module as its dependency or the module is not available to the + // platform, which indicates that the module belongs to at least one or more other APEXes. + NotInPlatform() bool // Tests if this module could have APEX variants. Even when a module type implements // ApexModule interface, APEX variants are created only for the module instances that return @@ -221,7 +220,12 @@ type ApexProperties struct { // See ApexModule.DirectlyInAnyApex() DirectlyInAnyApex bool `blueprint:"mutated"` - // See ApexModule.AnyVariantDirectlyInAnyApex() + // AnyVariantDirectlyInAnyApex is true in the primary variant of a module if _any_ variant + // of the module is directly in any apex. This includes host, arch, asan, etc. variants. It + // is unused in any variant that is not the primary variant. Ideally this wouldn't be used, + // as it incorrectly mixes arch variants if only one arch is in an apex, but a few places + // depend on it, for example when an ASAN variant is created before the apexMutator. Call + // this after apex.apexMutator is run. AnyVariantDirectlyInAnyApex bool `blueprint:"mutated"` // See ApexModule.NotAvailableForPlatform() @@ -302,8 +306,8 @@ func (m *ApexModuleBase) DirectlyInAnyApex() bool { } // Implements ApexModule -func (m *ApexModuleBase) AnyVariantDirectlyInAnyApex() bool { - return m.ApexProperties.AnyVariantDirectlyInAnyApex +func (m *ApexModuleBase) NotInPlatform() bool { + return m.ApexProperties.AnyVariantDirectlyInAnyApex || !m.AvailableFor(AvailableToPlatform) } // Implements ApexModule diff --git a/apex/apex_test.go b/apex/apex_test.go index ab8ae1616..f71e7ef16 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -15,6 +15,7 @@ package apex import ( + "fmt" "io/ioutil" "os" "path" @@ -6411,6 +6412,160 @@ func TestExcludeDependency(t *testing.T) { ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so") } +func TestPrebuiltStubLibDep(t *testing.T) { + bpBase := ` + apex { + name: "myapex", + key: "myapex.key", + native_shared_libs: ["mylib"], + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + apex_available: ["myapex"], + shared_libs: ["stublib"], + system_shared_libs: [], + } + apex { + name: "otherapex", + enabled: %s, + key: "myapex.key", + native_shared_libs: ["stublib"], + } + ` + + stublibSourceBp := ` + cc_library { + name: "stublib", + srcs: ["mylib.cpp"], + apex_available: ["otherapex"], + system_shared_libs: [], + stl: "none", + stubs: { + versions: ["1"], + }, + } + ` + + stublibPrebuiltBp := ` + cc_prebuilt_library_shared { + name: "stublib", + srcs: ["prebuilt.so"], + apex_available: ["otherapex"], + stubs: { + versions: ["1"], + }, + %s + } + ` + + tests := []struct { + name string + stublibBp string + usePrebuilt bool + modNames []string // Modules to collect AndroidMkEntries for + otherApexEnabled []string + }{ + { + name: "only_source", + stublibBp: stublibSourceBp, + usePrebuilt: false, + modNames: []string{"stublib"}, + otherApexEnabled: []string{"true", "false"}, + }, + { + name: "source_preferred", + stublibBp: stublibSourceBp + fmt.Sprintf(stublibPrebuiltBp, ""), + usePrebuilt: false, + modNames: []string{"stublib", "prebuilt_stublib"}, + otherApexEnabled: []string{"true", "false"}, + }, + { + name: "prebuilt_preferred", + stublibBp: stublibSourceBp + fmt.Sprintf(stublibPrebuiltBp, "prefer: true,"), + usePrebuilt: true, + modNames: []string{"stublib", "prebuilt_stublib"}, + otherApexEnabled: []string{"false"}, // No "true" since APEX cannot depend on prebuilt. + }, + { + name: "only_prebuilt", + stublibBp: fmt.Sprintf(stublibPrebuiltBp, ""), + usePrebuilt: true, + modNames: []string{"stublib"}, + otherApexEnabled: []string{"false"}, // No "true" since APEX cannot depend on prebuilt. + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + for _, otherApexEnabled := range test.otherApexEnabled { + t.Run("otherapex_enabled_"+otherApexEnabled, func(t *testing.T) { + ctx, config := testApex(t, fmt.Sprintf(bpBase, otherApexEnabled)+test.stublibBp) + + type modAndMkEntries struct { + mod *cc.Module + mkEntries android.AndroidMkEntries + } + entries := []*modAndMkEntries{} + + // Gather shared lib modules that are installable + for _, modName := range test.modNames { + for _, variant := range ctx.ModuleVariantsForTests(modName) { + if !strings.HasPrefix(variant, "android_arm64_armv8-a_shared") { + continue + } + mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module) + if !mod.Enabled() || mod.IsSkipInstall() { + continue + } + for _, ent := range android.AndroidMkEntriesForTest(t, config, "", mod) { + if ent.Disabled { + continue + } + entries = append(entries, &modAndMkEntries{ + mod: mod, + mkEntries: ent, + }) + } + } + } + + var entry *modAndMkEntries = nil + for _, ent := range entries { + if strings.Join(ent.mkEntries.EntryMap["LOCAL_MODULE"], ",") == "stublib" { + if entry != nil { + t.Errorf("More than one AndroidMk entry for \"stublib\": %s and %s", entry.mod, ent.mod) + } else { + entry = ent + } + } + } + + if entry == nil { + t.Errorf("AndroidMk entry for \"stublib\" missing") + } else { + isPrebuilt := entry.mod.Prebuilt() != nil + if isPrebuilt != test.usePrebuilt { + t.Errorf("Wrong module for \"stublib\" AndroidMk entry: got prebuilt %t, want prebuilt %t", isPrebuilt, test.usePrebuilt) + } + if !entry.mod.IsStubs() { + t.Errorf("Module for \"stublib\" AndroidMk entry isn't a stub: %s", entry.mod) + } + if entry.mkEntries.EntryMap["LOCAL_NOT_AVAILABLE_FOR_PLATFORM"] != nil { + t.Errorf("AndroidMk entry for \"stublib\" has LOCAL_NOT_AVAILABLE_FOR_PLATFORM set: %+v", entry.mkEntries) + } + } + }) + } + }) + } +} + func TestMain(m *testing.M) { run := func() int { setUp() diff --git a/cc/androidmk.go b/cc/androidmk.go index 4f4b047d0..187a2ff16 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -46,7 +46,7 @@ type AndroidMkContext interface { InRamdisk() bool InVendorRamdisk() bool InRecovery() bool - AnyVariantDirectlyInAnyApex() bool + NotInPlatform() bool } type subAndroidMkProvider interface { @@ -281,10 +281,15 @@ func (library *libraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries } }) } - if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.AnyVariantDirectlyInAnyApex() && + // If a library providing a stub is included in an APEX, the private APIs of the library + // is accessible only inside the APEX. From outside of the APEX, clients can only use the + // public APIs via the stub. To enforce this, the (latest version of the) stub gets the + // name of the library. The impl library instead gets the `.bootstrap` suffix to so that + // they can be exceptionally used directly when APEXes are not available (e.g. during the + // very early stage in the boot process). + if len(library.Properties.Stubs.Versions) > 0 && !ctx.Host() && ctx.NotInPlatform() && !ctx.InRamdisk() && !ctx.InVendorRamdisk() && !ctx.InRecovery() && !ctx.UseVndk() && !ctx.static() { if library.buildStubs() && library.isLatestStubVersion() { - // reference the latest version via its name without suffix when it is provided by apex entries.SubName = "" } if !library.buildStubs() { diff --git a/cc/cc.go b/cc/cc.go index d1f5c47e3..afdf85b12 100644 --- a/cc/cc.go +++ b/cc/cc.go @@ -1586,13 +1586,14 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { } c.outputFile = android.OptionalPathForPath(outputFile) - // If a lib is directly included in any of the APEXes, unhide the stubs - // variant having the latest version gets visible to make. In addition, - // the non-stubs variant is renamed to .bootstrap. This is to - // force anything in the make world to link against the stubs library. - // (unless it is explicitly referenced via .bootstrap suffix or the - // module is marked with 'bootstrap: true'). - if c.HasStubsVariants() && c.AnyVariantDirectlyInAnyApex() && !c.InRamdisk() && + // If a lib is directly included in any of the APEXes or is not available to the + // platform (which is often the case when the stub is provided as a prebuilt), + // unhide the stubs variant having the latest version gets visible to make. In + // addition, the non-stubs variant is renamed to .bootstrap. This is to + // force anything in the make world to link against the stubs library. (unless it + // is explicitly referenced via .bootstrap suffix or the module is marked with + // 'bootstrap: true'). + if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() && !c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() && c.IsStubs() && !c.InVendorRamdisk() { c.Properties.HideFromMake = false // unhide @@ -2472,7 +2473,7 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { // an APEX (and not from platform) // However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules, // always link to non-stub variant - useStubs = dep.(android.ApexModule).AnyVariantDirectlyInAnyApex() && !c.bootstrap() + useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap() // Another exception: if this module is bundled with an APEX, then // it is linked with the non-stub variant of a module in the APEX // as if this is part of the APEX.