rust: Emit toc files for cdylibs

Write toc files that list the exported symbols so dependents are
only rebuilt if the exported symbols change.

This exports the CC function TransformSharedObjectToToc, and also
removes an unused arg from its signature.

Bug: 178185435
Test: New Soong test passes.
Test: m <toc file path>
Change-Id: I7ab69bf7e7f32f25eb4c7ca9d18d877dac1511db
This commit is contained in:
Ivan Lozano 2021-11-03 15:30:18 -04:00
parent a3bd963988
commit 7b0781d14c
10 changed files with 53 additions and 18 deletions

View file

@ -949,8 +949,7 @@ func sourceAbiDiff(ctx android.ModuleContext, inputDump android.Path, referenceD
}
// Generate a rule for extracting a table of contents from a shared library (.so)
func transformSharedObjectToToc(ctx android.ModuleContext, inputFile android.Path,
outputFile android.WritablePath, flags builderFlags) {
func TransformSharedObjectToToc(ctx android.ModuleContext, inputFile android.Path, outputFile android.WritablePath) {
var format string
if ctx.Darwin() {

View file

@ -1377,7 +1377,7 @@ func (library *libraryDecorator) linkShared(ctx ModuleContext,
// depending on a table of contents file instead of the library itself.
tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.ShlibSuffix()[1:]+".toc")
library.tocFile = android.OptionalPathForPath(tocFile)
transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
TransformSharedObjectToToc(ctx, outputFile, tocFile)
stripFlags := flagsToStripFlags(flags)
needsStrip := library.stripper.NeedsStrip(ctx)

View file

@ -114,8 +114,6 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext,
// TODO(ccross): verify shared library dependencies
srcs := p.prebuiltSrcs(ctx)
if len(srcs) > 0 {
builderFlags := flagsToBuilderFlags(flags)
if len(srcs) > 1 {
ctx.PropertyErrorf("srcs", "multiple prebuilt source files")
return nil
@ -152,7 +150,7 @@ func (p *prebuiltLibraryLinker) link(ctx ModuleContext,
// depending on a table of contents file instead of the library itself.
tocFile := android.PathForModuleOut(ctx, libName+".toc")
p.tocFile = android.OptionalPathForPath(tocFile)
transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
TransformSharedObjectToToc(ctx, outputFile, tocFile)
if ctx.Windows() && p.properties.Windows_import_lib != nil {
// Consumers of this library actually links to the import library in build

View file

@ -476,13 +476,12 @@ func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps Pat
if p.shared() {
libName := in.Base()
builderFlags := flagsToBuilderFlags(flags)
// Optimize out relinking against shared libraries whose interface hasn't changed by
// depending on a table of contents file instead of the library itself.
tocFile := android.PathForModuleOut(ctx, libName+".toc")
p.tocFile = android.OptionalPathForPath(tocFile)
transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
TransformSharedObjectToToc(ctx, in, tocFile)
ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
SharedLibrary: in,

View file

@ -144,7 +144,6 @@ func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
// current VNDK prebuilts are only shared libs.
in := p.singleSourcePath(ctx)
builderFlags := flagsToBuilderFlags(flags)
p.unstrippedOutputFile = in
libName := in.Base()
if p.stripper.NeedsStrip(ctx) {
@ -158,7 +157,7 @@ func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
// depending on a table of contents file instead of the library itself.
tocFile := android.PathForModuleOut(ctx, libName+".toc")
p.tocFile = android.OptionalPathForPath(tocFile)
transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
TransformSharedObjectToToc(ctx, in, tocFile)
p.androidMkSuffix = p.NameSuffix()

View file

@ -137,12 +137,16 @@ func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.An
} else if library.shared() {
ret.Class = "SHARED_LIBRARIES"
}
if library.distFile.Valid() {
ret.DistFiles = android.MakeDefaultDistFiles(library.distFile.Path())
}
ret.ExtraEntries = append(ret.ExtraEntries,
func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
if library.tocFile.Valid() {
entries.SetString("LOCAL_SOONG_TOC", library.tocFile.String())
}
})
}
func (procMacro *procMacroDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkEntries) {
ctx.SubAndroidMk(ret, procMacro.baseCompiler)

View file

@ -185,7 +185,7 @@ func rustEnvVars(ctx ModuleContext, deps PathDeps) []string {
}
func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
outputFile android.WritablePath, crate_type string) buildOutput {
outputFile android.WritablePath, crateType string) buildOutput {
var inputs android.Paths
var implicits android.Paths
@ -204,7 +204,7 @@ func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, fl
// Collect rustc flags
rustcFlags = append(rustcFlags, flags.GlobalRustFlags...)
rustcFlags = append(rustcFlags, flags.RustFlags...)
rustcFlags = append(rustcFlags, "--crate-type="+crate_type)
rustcFlags = append(rustcFlags, "--crate-type="+crateType)
if crateName != "" {
rustcFlags = append(rustcFlags, "--crate-name="+crateName)
}

View file

@ -102,6 +102,9 @@ type libraryDecorator struct {
sourceProvider SourceProvider
collectedSnapshotHeaders android.Paths
// table-of-contents file for cdylib crates to optimize out relinking when possible
tocFile android.OptionalPath
}
type libraryInterface interface {
@ -137,12 +140,18 @@ type libraryInterface interface {
BuildOnlyDylib()
BuildOnlyStatic()
BuildOnlyShared()
toc() android.OptionalPath
}
func (library *libraryDecorator) nativeCoverage() bool {
return true
}
func (library *libraryDecorator) toc() android.OptionalPath {
return library.tocFile
}
func (library *libraryDecorator) rlib() bool {
return library.MutatedProperties.VariantIsRlib
}
@ -519,9 +528,16 @@ func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps Pa
}
if library.shared() {
// Optimize out relinking against shared libraries whose interface hasn't changed by
// depending on a table of contents file instead of the library itself.
tocFile := outputFile.ReplaceExtension(ctx, flags.Toolchain.SharedLibSuffix()[1:]+".toc")
library.tocFile = android.OptionalPathForPath(tocFile)
cc.TransformSharedObjectToToc(ctx, outputFile, tocFile)
ctx.SetProvider(cc.SharedLibraryInfoProvider, cc.SharedLibraryInfo{
SharedLibrary: outputFile,
Target: ctx.Target(),
TableOfContents: android.OptionalPathForPath(tocFile),
SharedLibrary: outputFile,
Target: ctx.Target(),
})
}

View file

@ -160,6 +160,26 @@ func TestSharedLibrary(t *testing.T) {
}
}
func TestSharedLibraryToc(t *testing.T) {
ctx := testRust(t, `
rust_ffi_shared {
name: "libfoo",
srcs: ["foo.rs"],
crate_name: "foo",
}
cc_binary {
name: "fizzbuzz",
shared_libs: ["libfoo"],
}`)
fizzbuzz := ctx.ModuleForTests("fizzbuzz", "android_arm64_armv8-a").Rule("ld")
if !android.SuffixInList(fizzbuzz.Implicits.Strings(), "libfoo.so.toc") {
t.Errorf("missing expected libfoo.so.toc implicit dependency, instead found: %#v",
fizzbuzz.Implicits.Strings())
}
}
func TestStaticLibraryLinkage(t *testing.T) {
ctx := testRust(t, `
rust_ffi_static {

View file

@ -281,8 +281,8 @@ func (mod *Module) Object() bool {
func (mod *Module) Toc() android.OptionalPath {
if mod.compiler != nil {
if _, ok := mod.compiler.(libraryInterface); ok {
return android.OptionalPath{}
if lib, ok := mod.compiler.(libraryInterface); ok {
return lib.toc()
}
}
panic(fmt.Errorf("Toc() called on non-library module: %q", mod.BaseModuleName()))