Add support for versioned stubs.
A cc_library or cc_library_shared can be configured to have stubs
variants of the lib.
cc_library_shared {
name: "libfoo",
srcs: ["foo.cpp"],
stubs: {
symbol_file: "foo.map.txt",
versions: ["1", "2", "3"],
},
}
then, stubs variants of libfoo for version 1, 2, and 3 are created
from foo.map.txt. Each version has the symbols from the map file where
each symbol is annotated with the version that the symbol was introduced
via the 'introduced=<ver>' syntax. The versions don't need to be in sync
with the platform versions (e.g., P for 28). The versions are local to
the library.
For another library or executable to use the versioned stubs lib, use
the new 'name#ver' syntax to specify the version:
cc_binary {
name: "test",
....
shared_libs: ["libFoo#2"],
}
Internally, a new mutator 'version' is applied to all cc.Module objects.
By default, a variant named 'impl' is created for the non-stub version.
If the versions property is set, additional variations are created per a
version with the mutable property BuildStubs set as true, which lets the
compiler and the linker to build a stubs lib from the symbol file
instead from the source files.
This feature will be used to enforce stable interfaces among APEXs. When
a lib foo in an APEX is depending on a lib bar in another APEX, then bar
should have stable interface (in C lang) and foo should be depending on
one of the stubs libs of bar. Only libraries in the same APEX as foo can
link against non-stub version of it.
Bug: 112672359
Test: m (cc_test added)
Change-Id: I2488be0b9d7b7b8d7761234dc1c9c0e3add8601c
diff --git a/cc/library.go b/cc/library.go
index 920292d..aafe365 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -22,6 +22,7 @@
"android/soong/android"
"android/soong/cc/config"
+ "android/soong/genrule"
)
type LibraryProperties struct {
@@ -65,6 +66,15 @@
}
Static_ndk_lib *bool
+
+ Stubs struct {
+ // Relative path to the symbol map. The symbol map provides the list of
+ // symbols that are exported for stubs variant of this library.
+ Symbol_file *string
+
+ // List versions to generate stubs libs for.
+ Versions []string
+ }
}
type LibraryMutatedProperties struct {
@@ -78,6 +88,11 @@
VariantIsShared bool `blueprint:"mutated"`
// This variant is static
VariantIsStatic bool `blueprint:"mutated"`
+
+ // This variant is a stubs lib
+ BuildStubs bool `blueprint:"mutated"`
+ // Version of the stubs lib
+ StubsVersion string `blueprint:"mutated"`
}
type FlagExporterProperties struct {
@@ -240,6 +255,8 @@
// Location of the linked, unstripped library for shared libraries
unstrippedOutputFile android.Path
+ versionScriptPath android.ModuleGenPath
+
// Decorated interafaces
*baseCompiler
*baseLinker
@@ -313,7 +330,11 @@
flags.YasmFlags = append(flags.YasmFlags, f)
}
- return library.baseCompiler.compilerFlags(ctx, flags, deps)
+ flags = library.baseCompiler.compilerFlags(ctx, flags, deps)
+ if library.buildStubs() {
+ flags = addStubLibraryCompilerFlags(flags)
+ }
+ return flags
}
func extractExportIncludesFromFlags(flags []string) []string {
@@ -336,6 +357,12 @@
}
func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+ if library.buildStubs() {
+ objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "")
+ library.versionScriptPath = versionScript
+ return objs
+ }
+
if !library.buildShared() && !library.buildStatic() {
if len(library.baseCompiler.Properties.Srcs) > 0 {
ctx.PropertyErrorf("srcs", "cc_library_headers must not have any srcs")
@@ -422,8 +449,10 @@
location = InstallInSanitizerDir
}
library.baseInstaller.location = location
-
library.baseLinker.linkerInit(ctx)
+ // Let baseLinker know whether this variant is for stubs or not, so that
+ // it can omit things that are not required for linking stubs.
+ library.baseLinker.dynamicProperties.BuildStubs = library.buildStubs()
}
func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
@@ -735,7 +764,8 @@
if Bool(library.Properties.Static_ndk_lib) && library.static() &&
!ctx.useVndk() && !ctx.inRecovery() && ctx.Device() &&
- library.baseLinker.sanitize.isUnsanitizedVariant() {
+ library.baseLinker.sanitize.isUnsanitizedVariant() &&
+ !library.buildStubs() {
installPath := getNdkSysrootBase(ctx).Join(
ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
@@ -785,6 +815,14 @@
library.MutatedProperties.BuildStatic = false
}
+func (library *libraryDecorator) buildStubs() bool {
+ return library.MutatedProperties.BuildStubs
+}
+
+func (library *libraryDecorator) stubsVersion() string {
+ return library.MutatedProperties.StubsVersion
+}
+
func NewLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
module := newModule(hod, android.MultilibBoth)
@@ -847,3 +885,41 @@
}
}
}
+
+// Version mutator splits a module into the mandatory non-stubs variant
+// (which is named "impl") and zero or more stubs variants.
+func versionMutator(mctx android.BottomUpMutatorContext) {
+ if mctx.Os() != android.Android {
+ return
+ }
+
+ if m, ok := mctx.Module().(*Module); ok && !m.inRecovery() && m.linker != nil {
+ if library, ok := m.linker.(*libraryDecorator); ok && library.buildShared() {
+ versions := []string{""}
+ for _, v := range library.Properties.Stubs.Versions {
+ versions = append(versions, v)
+ }
+ modules := mctx.CreateVariations(versions...)
+ for i, m := range modules {
+ l := m.(*Module).linker.(*libraryDecorator)
+ if i == 0 {
+ l.MutatedProperties.BuildStubs = false
+ continue
+ }
+ // Mark that this variant is for stubs.
+ l.MutatedProperties.BuildStubs = true
+ l.MutatedProperties.StubsVersion = versions[i]
+ m.(*Module).Properties.HideFromMake = true
+ }
+ } else {
+ mctx.CreateVariations("")
+ }
+ return
+ }
+ if genrule, ok := mctx.Module().(*genrule.Module); ok {
+ if props, ok := genrule.Extra.(*GenruleExtraProperties); ok && !props.InRecovery {
+ mctx.CreateVariations("")
+ return
+ }
+ }
+}