Rust cdylib/statliclib support for vendor snapshot.

Adds support for platform vendor_available Rust FFI libraries and
binaries to be included in the vendor snapshot.

Because rlib and dylibs are not yet in snapshots, libstd cannot be
included in a vendor snapshot. As a result, vendor-specific Rust code
can't be guaranteed to work with the platform-provided vendor_available
modules built with a newer toolchain. For now, a check is added
indicating vendor-specific Rust code is unsupported.

This changes the linkage for vendor variants of these modules to default
to rlib linkage since dylibs cannot be included in the snapshot yet.

Bug: 184042776
Test: m nothing # new Soong tests pass
Change-Id: I502eaa4bb962eb87ff868fcf49b435f0d2f982e6
This commit is contained in:
Ivan Lozano
2021-05-20 13:39:16 -04:00
parent d67a6b0a88
commit 1921e8003d
9 changed files with 487 additions and 36 deletions

View File

@@ -53,6 +53,7 @@ bootstrap_go_package {
"rust_test.go", "rust_test.go",
"source_provider_test.go", "source_provider_test.go",
"test_test.go", "test_test.go",
"vendor_snapshot_test.go",
], ],
pluginFor: ["soong_build"], pluginFor: ["soong_build"],
} }

View File

@@ -137,6 +137,9 @@ func (binary *binaryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoD
// Binaries default to dylib dependencies for device, rlib for host. // Binaries default to dylib dependencies for device, rlib for host.
if binary.preferRlib() { if binary.preferRlib() {
return rlibAutoDep return rlibAutoDep
} else if mod, ok := ctx.Module().(*Module); ok && mod.InVendor() {
// Vendor Rust binaries should prefer rlibs.
return rlibAutoDep
} else if ctx.Device() { } else if ctx.Device() {
return dylibAutoDep return dylibAutoDep
} else { } else {
@@ -147,6 +150,8 @@ func (binary *binaryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoD
func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage { func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
if binary.preferRlib() { if binary.preferRlib() {
return RlibLinkage return RlibLinkage
} else if ctx.RustModule().InVendor() {
return RlibLinkage
} }
return binary.baseCompiler.stdLinkage(ctx) return binary.baseCompiler.stdLinkage(ctx)
} }

View File

@@ -202,6 +202,8 @@ func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant stri
func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) { func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
// Rust does not support installing to the product image yet. // Rust does not support installing to the product image yet.
vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
if Bool(mod.VendorProperties.Product_available) { if Bool(mod.VendorProperties.Product_available) {
mctx.PropertyErrorf("product_available", mctx.PropertyErrorf("product_available",
"Rust modules do not yet support being available to the product image") "Rust modules do not yet support being available to the product image")
@@ -217,6 +219,9 @@ func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
mctx.PropertyErrorf("vendor_ramdisk_available", "cannot be set for rust_ffi or rust_ffi_shared modules.") mctx.PropertyErrorf("vendor_ramdisk_available", "cannot be set for rust_ffi or rust_ffi_shared modules.")
} }
} }
if vendorSpecific {
mctx.PropertyErrorf("vendor", "Vendor-only non-rust_ffi Rust modules are not supported.")
}
cc.MutateImage(mctx, mod) cc.MutateImage(mctx, mod)

View File

@@ -99,6 +99,8 @@ type libraryDecorator struct {
MutatedProperties LibraryMutatedProperties MutatedProperties LibraryMutatedProperties
includeDirs android.Paths includeDirs android.Paths
sourceProvider SourceProvider sourceProvider SourceProvider
collectedSnapshotHeaders android.Paths
} }
type libraryInterface interface { type libraryInterface interface {
@@ -220,7 +222,10 @@ func (library *libraryDecorator) setSource() {
} }
func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep { func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
if library.preferRlib() { if ctx.Module().(*Module).InVendor() {
// Vendor modules should statically link libstd.
return rlibAutoDep
} else if library.preferRlib() {
return rlibAutoDep return rlibAutoDep
} else if library.rlib() || library.static() { } else if library.rlib() || library.static() {
return rlibAutoDep return rlibAutoDep
@@ -236,7 +241,10 @@ func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) aut
} }
func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage { func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
if library.static() || library.MutatedProperties.VariantIsStaticStd { if ctx.RustModule().InVendor() {
// Vendor modules should statically link libstd.
return RlibLinkage
} else if library.static() || library.MutatedProperties.VariantIsStaticStd {
return RlibLinkage return RlibLinkage
} else if library.baseCompiler.preferRlib() { } else if library.baseCompiler.preferRlib() {
return RlibLinkage return RlibLinkage
@@ -623,6 +631,19 @@ func LibraryMutator(mctx android.BottomUpMutatorContext) {
// Disable dylib Vendor Ramdisk variations until we support these. // Disable dylib Vendor Ramdisk variations until we support these.
v.(*Module).Disable() v.(*Module).Disable()
} }
variation := v.(*Module).ModuleBase.ImageVariation().Variation
if strings.HasPrefix(variation, cc.VendorVariationPrefix) &&
m.HasVendorVariant() &&
!cc.IsVendorProprietaryModule(mctx) &&
strings.TrimPrefix(variation, cc.VendorVariationPrefix) == mctx.DeviceConfig().VndkVersion() {
// cc.MutateImage runs before LibraryMutator, so vendor variations which are meant for rlibs only are
// produced for Dylibs; however, dylibs should not be enabled for boardVndkVersion for
// non-vendor proprietary modules.
v.(*Module).Disable()
}
case "source": case "source":
v.(*Module).compiler.(libraryInterface).setSource() v.(*Module).compiler.(libraryInterface).setSource()
// The source variant does not produce any library. // The source variant does not produce any library.
@@ -671,3 +692,54 @@ func LibstdMutator(mctx android.BottomUpMutatorContext) {
} }
} }
} }
func (l *libraryDecorator) snapshotHeaders() android.Paths {
if l.collectedSnapshotHeaders == nil {
panic("snapshotHeaders() must be called after collectHeadersForSnapshot()")
}
return l.collectedSnapshotHeaders
}
// collectHeadersForSnapshot collects all exported headers from library.
// It globs header files in the source tree for exported include directories,
// and tracks generated header files separately.
//
// This is to be called from GenerateAndroidBuildActions, and then collected
// header files can be retrieved by snapshotHeaders().
func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext, deps PathDeps) {
ret := android.Paths{}
// Glob together the headers from the modules include_dirs property
for _, path := range android.CopyOfPaths(l.includeDirs) {
dir := path.String()
glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
if err != nil {
ctx.ModuleErrorf("glob failed: %#v", err)
return
}
for _, header := range glob {
// Filter out only the files with extensions that are headers.
found := false
for _, ext := range cc.HeaderExts {
if strings.HasSuffix(header, ext) {
found = true
break
}
}
if !found {
continue
}
ret = append(ret, android.PathForSource(ctx, header))
}
}
// Glob together the headers from C dependencies as well, starting with non-generated headers.
ret = append(ret, cc.GlobHeadersForSnapshot(ctx, append(android.CopyOfPaths(deps.depIncludePaths), deps.depSystemIncludePaths...))...)
// Collect generated headers from C dependencies.
ret = append(ret, cc.GlobGeneratedHeadersForSnapshot(ctx, deps.depGeneratedHeaders)...)
// TODO(185577950): If support for generated headers is added, they need to be collected here as well.
l.collectedSnapshotHeaders = ret
}

View File

@@ -85,6 +85,9 @@ type BaseProperties struct {
VendorRamdiskVariantNeeded bool `blueprint:"mutated"` VendorRamdiskVariantNeeded bool `blueprint:"mutated"`
ExtraVariants []string `blueprint:"mutated"` ExtraVariants []string `blueprint:"mutated"`
// Used by vendor snapshot to record dependencies from snapshot modules.
SnapshotSharedLibs []string `blueprint:"mutated"`
// Make this module available when building for vendor ramdisk. // Make this module available when building for vendor ramdisk.
// On device without a dedicated recovery partition, the module is only // On device without a dedicated recovery partition, the module is only
// available after switching root into // available after switching root into
@@ -92,6 +95,20 @@ type BaseProperties struct {
// the recovery variant instead (TODO(b/165791368) recovery not yet supported) // the recovery variant instead (TODO(b/165791368) recovery not yet supported)
Vendor_ramdisk_available *bool Vendor_ramdisk_available *bool
// Normally Soong uses the directory structure to decide which modules
// should be included (framework) or excluded (non-framework) from the
// different snapshots (vendor, recovery, etc.), but this property
// allows a partner to exclude a module normally thought of as a
// framework module from the vendor snapshot.
Exclude_from_vendor_snapshot *bool
// Normally Soong uses the directory structure to decide which modules
// should be included (framework) or excluded (non-framework) from the
// different snapshots (vendor, recovery, etc.), but this property
// allows a partner to exclude a module normally thought of as a
// framework module from the recovery snapshot.
Exclude_from_recovery_snapshot *bool
// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX). // Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
Min_sdk_version *string Min_sdk_version *string
@@ -826,6 +843,14 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps) mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
// RECOVERY_SNAPSHOT_VERSION is current.
if lib, ok := mod.compiler.(snapshotLibraryInterface); ok {
if cc.ShouldCollectHeadersForSnapshot(ctx, mod, apexInfo) {
lib.collectHeadersForSnapshot(ctx, deps)
}
}
apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo) apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
if mod.installable(apexInfo) { if mod.installable(apexInfo) {
mod.compiler.install(ctx) mod.compiler.install(ctx)
@@ -1056,6 +1081,10 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...) depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...) depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
directSharedLibDeps = append(directSharedLibDeps, ccDep) directSharedLibDeps = append(directSharedLibDeps, ccDep)
// Record baseLibName for snapshots.
mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName) mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
exportDep = true exportDep = true
case cc.IsHeaderDepTag(depTag): case cc.IsHeaderDepTag(depTag):
@@ -1161,6 +1190,11 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
deps := mod.deps(ctx) deps := mod.deps(ctx)
var commonDepVariations []blueprint.Variation var commonDepVariations []blueprint.Variation
var snapshotInfo *cc.SnapshotInfo
if ctx.Os() == android.Android {
deps.SharedLibs, _ = cc.RewriteLibs(mod, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
}
stdLinkage := "dylib-std" stdLinkage := "dylib-std"
if mod.compiler.stdLinkage(ctx) == RlibLinkage { if mod.compiler.stdLinkage(ctx) == RlibLinkage {
@@ -1168,20 +1202,25 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
} }
rlibDepVariations := commonDepVariations rlibDepVariations := commonDepVariations
if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() { if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
rlibDepVariations = append(rlibDepVariations, rlibDepVariations = append(rlibDepVariations,
blueprint.Variation{Mutator: "rust_stdlinkage", Variation: stdLinkage}) blueprint.Variation{Mutator: "rust_stdlinkage", Variation: stdLinkage})
} }
// rlibs
actx.AddVariationDependencies( actx.AddVariationDependencies(
append(rlibDepVariations, []blueprint.Variation{ append(rlibDepVariations, []blueprint.Variation{
{Mutator: "rust_libraries", Variation: rlibVariation}}...), {Mutator: "rust_libraries", Variation: rlibVariation}}...),
rlibDepTag, deps.Rlibs...) rlibDepTag, deps.Rlibs...)
// dylibs
actx.AddVariationDependencies( actx.AddVariationDependencies(
append(commonDepVariations, []blueprint.Variation{ append(commonDepVariations, []blueprint.Variation{
{Mutator: "rust_libraries", Variation: dylibVariation}}...), {Mutator: "rust_libraries", Variation: dylibVariation}}...),
dylibDepTag, deps.Dylibs...) dylibDepTag, deps.Dylibs...)
// rustlibs
if deps.Rustlibs != nil && !mod.compiler.Disabled() { if deps.Rustlibs != nil && !mod.compiler.Disabled() {
autoDep := mod.compiler.(autoDeppable).autoDep(ctx) autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
if autoDep.depTag == rlibDepTag { if autoDep.depTag == rlibDepTag {
@@ -1194,10 +1233,12 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
autoDep.depTag, deps.Rustlibs...) autoDep.depTag, deps.Rustlibs...)
} }
} }
// stdlibs
if deps.Stdlibs != nil { if deps.Stdlibs != nil {
if mod.compiler.stdLinkage(ctx) == RlibLinkage { if mod.compiler.stdLinkage(ctx) == RlibLinkage {
actx.AddVariationDependencies( actx.AddVariationDependencies(
append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "rlib"}), append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
rlibDepTag, deps.Stdlibs...) rlibDepTag, deps.Stdlibs...)
} else { } else {
actx.AddVariationDependencies( actx.AddVariationDependencies(
@@ -1205,24 +1246,45 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
dylibDepTag, deps.Stdlibs...) dylibDepTag, deps.Stdlibs...)
} }
} }
actx.AddVariationDependencies(append(commonDepVariations,
blueprint.Variation{Mutator: "link", Variation: "shared"}), for _, lib := range deps.SharedLibs {
cc.SharedDepTag(), deps.SharedLibs...) depTag := cc.SharedDepTag()
actx.AddVariationDependencies(append(commonDepVariations, name, version := cc.StubsLibNameAndVersion(lib)
blueprint.Variation{Mutator: "link", Variation: "static"}),
cc.StaticDepTag(false), deps.StaticLibs...) variations := []blueprint.Variation{
actx.AddVariationDependencies(append(commonDepVariations, {Mutator: "link", Variation: "shared"},
blueprint.Variation{Mutator: "link", Variation: "static"}), }
cc.StaticDepTag(true), deps.WholeStaticLibs...) cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
}
for _, lib := range deps.WholeStaticLibs {
depTag := cc.StaticDepTag(true)
lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
}, depTag, lib)
}
for _, lib := range deps.StaticLibs {
depTag := cc.StaticDepTag(false)
lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
actx.AddVariationDependencies([]blueprint.Variation{
{Mutator: "link", Variation: "static"},
}, depTag, lib)
}
actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...) actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
crtVariations := cc.GetCrtVariations(ctx, mod) crtVariations := cc.GetCrtVariations(ctx, mod)
if deps.CrtBegin != "" { if deps.CrtBegin != "" {
actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag, deps.CrtBegin) actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag,
cc.RewriteSnapshotLib(deps.CrtBegin, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
} }
if deps.CrtEnd != "" { if deps.CrtEnd != "" {
actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag, deps.CrtEnd) actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag,
cc.RewriteSnapshotLib(deps.CrtEnd, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
} }
if mod.sourceProvider != nil { if mod.sourceProvider != nil {
@@ -1232,6 +1294,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) {
bindgen.Properties.Custom_bindgen) bindgen.Properties.Custom_bindgen)
} }
} }
// proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy. // proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy.
actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...) actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
} }

View File

@@ -37,21 +37,28 @@ var prepareForRustTest = android.GroupFixturePreparers(
genrule.PrepareForTestWithGenRuleBuildComponents, genrule.PrepareForTestWithGenRuleBuildComponents,
PrepareForIntegrationTestWithRust, PrepareForTestWithRustIncludeVndk,
android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
variables.DeviceVndkVersion = StringPtr("current")
variables.ProductVndkVersion = StringPtr("current")
variables.Platform_vndk_version = StringPtr("29")
}),
) )
var rustMockedFiles = android.MockFS{ var rustMockedFiles = android.MockFS{
"foo.rs": nil, "foo.rs": nil,
"foo.c": nil, "foo.c": nil,
"src/bar.rs": nil, "src/bar.rs": nil,
"src/any.h": nil, "src/any.h": nil,
"proto.proto": nil, "c_includes/c_header.h": nil,
"proto/buf.proto": nil, "rust_includes/rust_headers.h": nil,
"buf.proto": nil, "proto.proto": nil,
"foo.proto": nil, "proto/buf.proto": nil,
"liby.so": nil, "buf.proto": nil,
"libz.so": nil, "foo.proto": nil,
"data.txt": nil, "liby.so": nil,
"libz.so": nil,
"data.txt": nil,
} }
// testRust returns a TestContext in which a basic environment has been setup. // testRust returns a TestContext in which a basic environment has been setup.
@@ -67,10 +74,16 @@ func testRust(t *testing.T, bp string) *android.TestContext {
} }
func testRustVndk(t *testing.T, bp string) *android.TestContext { func testRustVndk(t *testing.T, bp string) *android.TestContext {
return testRustVndkFs(t, bp, rustMockedFiles)
}
const vendorVariant = "android_vendor.29_arm64_armv8-a_shared"
func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
skipTestIfOsNotSupported(t) skipTestIfOsNotSupported(t)
result := android.GroupFixturePreparers( result := android.GroupFixturePreparers(
prepareForRustTest, prepareForRustTest,
rustMockedFiles.AddToFixture(), fs.AddToFixture(),
android.FixtureModifyProductVariables( android.FixtureModifyProductVariables(
func(variables android.FixtureProductVariables) { func(variables android.FixtureProductVariables) {
variables.DeviceVndkVersion = StringPtr("current") variables.DeviceVndkVersion = StringPtr("current")
@@ -80,6 +93,7 @@ func testRustVndk(t *testing.T, bp string) *android.TestContext {
), ),
).RunTestWithBp(t, bp) ).RunTestWithBp(t, bp)
return result.TestContext return result.TestContext
} }
// testRustCov returns a TestContext in which a basic environment has been // testRustCov returns a TestContext in which a basic environment has been
@@ -115,10 +129,14 @@ func testRustError(t *testing.T, pattern string, bp string) {
// testRustVndkError is similar to testRustError, but can be used to test VNDK-related errors. // testRustVndkError is similar to testRustError, but can be used to test VNDK-related errors.
func testRustVndkError(t *testing.T, pattern string, bp string) { func testRustVndkError(t *testing.T, pattern string, bp string) {
testRustVndkFsError(t, pattern, bp, rustMockedFiles)
}
func testRustVndkFsError(t *testing.T, pattern string, bp string, fs android.MockFS) {
skipTestIfOsNotSupported(t) skipTestIfOsNotSupported(t)
android.GroupFixturePreparers( android.GroupFixturePreparers(
prepareForRustTest, prepareForRustTest,
rustMockedFiles.AddToFixture(), fs.AddToFixture(),
android.FixtureModifyProductVariables( android.FixtureModifyProductVariables(
func(variables android.FixtureProductVariables) { func(variables android.FixtureProductVariables) {
variables.DeviceVndkVersion = StringPtr("current") variables.DeviceVndkVersion = StringPtr("current")

View File

@@ -18,18 +18,34 @@ import (
"android/soong/android" "android/soong/android"
) )
// snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
type snapshotLibraryInterface interface {
libraryInterface
// collectHeadersForSnapshot is called in GenerateAndroidBuildActions for snapshot aware
// modules (See isSnapshotAware below).
// This function should gather all headers needed for snapshot.
collectHeadersForSnapshot(ctx android.ModuleContext, deps PathDeps)
// snapshotHeaders should return collected headers by collectHeadersForSnapshot.
// Calling snapshotHeaders before collectHeadersForSnapshot is an error.
snapshotHeaders() android.Paths
}
func (mod *Module) ExcludeFromVendorSnapshot() bool { func (mod *Module) ExcludeFromVendorSnapshot() bool {
// TODO Rust does not yet support snapshotting return Bool(mod.Properties.Exclude_from_vendor_snapshot)
return false
} }
func (mod *Module) ExcludeFromRecoverySnapshot() bool { func (mod *Module) ExcludeFromRecoverySnapshot() bool {
// TODO Rust does not yet support snapshotting return Bool(mod.Properties.Exclude_from_recovery_snapshot)
return false
} }
func (mod *Module) IsSnapshotLibrary() bool { func (mod *Module) IsSnapshotLibrary() bool {
// TODO Rust does not yet support snapshotting if lib, ok := mod.compiler.(libraryInterface); ok {
// Rust-native dylibs and rlibs are not snapshot supported yet, so only
// return true if this module produces a C shared or static library.
return lib.shared() || lib.static()
}
return false return false
} }
@@ -39,8 +55,7 @@ func (mod *Module) SnapshotRuntimeLibs() []string {
} }
func (mod *Module) SnapshotSharedLibs() []string { func (mod *Module) SnapshotSharedLibs() []string {
// TODO Rust does not yet support snapshotting return mod.Properties.SnapshotSharedLibs
return []string{}
} }
func (mod *Module) Symlinks() []string { func (mod *Module) Symlinks() []string {
@@ -49,6 +64,8 @@ func (mod *Module) Symlinks() []string {
} }
func (m *Module) SnapshotHeaders() android.Paths { func (m *Module) SnapshotHeaders() android.Paths {
// TODO Rust does not yet support snapshotting if l, ok := m.compiler.(snapshotLibraryInterface); ok {
return l.snapshotHeaders()
}
return android.Paths{} return android.Paths{}
} }

View File

@@ -45,6 +45,11 @@ var PrepareForIntegrationTestWithRust = android.GroupFixturePreparers(
PrepareForTestWithRustDefaultModules, PrepareForTestWithRustDefaultModules,
) )
var PrepareForTestWithRustIncludeVndk = android.GroupFixturePreparers(
PrepareForIntegrationTestWithRust,
cc.PrepareForTestWithCcIncludeVndk,
)
func GatherRequiredDepsForTest() string { func GatherRequiredDepsForTest() string {
bp := ` bp := `
rust_prebuilt_library { rust_prebuilt_library {

View File

@@ -0,0 +1,265 @@
// Copyright 2021 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rust
import (
"fmt"
"path/filepath"
"strings"
"testing"
"android/soong/android"
"android/soong/cc"
)
func TestVendorSnapshotCapture(t *testing.T) {
bp := `
rust_ffi {
name: "librustvendor_available",
crate_name: "rustvendor_available",
srcs: ["lib.rs"],
vendor_available: true,
include_dirs: ["rust_headers/"],
}
rust_binary {
name: "vendor_available_bin",
vendor_available: true,
srcs: ["srcs/lib.rs"],
}
`
skipTestIfOsNotSupported(t)
result := android.GroupFixturePreparers(
prepareForRustTest,
rustMockedFiles.AddToFixture(),
android.FixtureModifyProductVariables(
func(variables android.FixtureProductVariables) {
variables.DeviceVndkVersion = StringPtr("current")
variables.Platform_vndk_version = StringPtr("29")
},
),
).RunTestWithBp(t, bp)
ctx := result.TestContext
// Check Vendor snapshot output.
snapshotDir := "vendor-snapshot"
snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
var jsonFiles []string
for _, arch := range [][]string{
[]string{"arm64", "armv8-a"},
[]string{"arm", "armv7-a-neon"},
} {
archType := arch[0]
archVariant := arch[1]
archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
// For shared libraries, only non-VNDK vendor_available modules are captured
sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.so", sharedDir, sharedVariant)
jsonFiles = append(jsonFiles,
filepath.Join(sharedDir, "librustvendor_available.so.json"))
// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
staticVariant := fmt.Sprintf("android_vendor.29_%s_%s_static", archType, archVariant)
staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.a", staticDir, staticVariant)
jsonFiles = append(jsonFiles,
filepath.Join(staticDir, "librustvendor_available.a.json"))
// For binary executables, all vendor_available modules are captured.
if archType == "arm64" {
binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
cc.CheckSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
jsonFiles = append(jsonFiles,
filepath.Join(binaryDir, "vendor_available_bin.json"))
}
}
for _, jsonFile := range jsonFiles {
// verify all json files exist
if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
t.Errorf("%q expected but not found; #%v", jsonFile, jsonFiles)
}
}
// fake snapshot should have all outputs in the normal snapshot.
fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
for _, output := range snapshotSingleton.AllOutputs() {
fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
t.Errorf("%q expected but not found", fakeOutput)
}
}
}
func TestVendorSnapshotDirected(t *testing.T) {
bp := `
rust_ffi_shared {
name: "librustvendor_available",
crate_name: "rustvendor_available",
srcs: ["lib.rs"],
vendor_available: true,
}
rust_ffi_shared {
name: "librustvendor_exclude",
crate_name: "rustvendor_exclude",
srcs: ["lib.rs"],
vendor_available: true,
}
`
ctx := testRustVndk(t, bp)
ctx.Config().TestProductVariables.VendorSnapshotModules = make(map[string]bool)
ctx.Config().TestProductVariables.VendorSnapshotModules["librustvendor_available"] = true
ctx.Config().TestProductVariables.DirectedVendorSnapshot = true
// Check Vendor snapshot output.
snapshotDir := "vendor-snapshot"
snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
var includeJsonFiles []string
for _, arch := range [][]string{
[]string{"arm64", "armv8-a"},
[]string{"arm", "armv7-a-neon"},
} {
archType := arch[0]
archVariant := arch[1]
archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
// Included modules
cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.so", sharedDir, sharedVariant)
includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librustvendor_available.so.json"))
// Excluded modules. Modules not included in the directed vendor snapshot
// are still include as fake modules.
cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.so", sharedDir, sharedVariant)
includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librustvendor_exclude.so.json"))
}
// Verify that each json file for an included module has a rule.
for _, jsonFile := range includeJsonFiles {
if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
t.Errorf("include json file %q not found", jsonFile)
}
}
}
func TestVendorSnapshotExclude(t *testing.T) {
// This test verifies that the exclude_from_vendor_snapshot property
// makes its way from the Android.bp source file into the module data
// structure. It also verifies that modules are correctly included or
// excluded in the vendor snapshot based on their path (framework or
// vendor) and the exclude_from_vendor_snapshot property.
// When vendor-specific Rust modules are available, make sure to test
// that they're excluded by path here. See cc.TestVendorSnapshotExclude
// for an example.
frameworkBp := `
rust_ffi_shared {
name: "libinclude",
crate_name: "include",
srcs: ["include.rs"],
vendor_available: true,
}
rust_ffi_shared {
name: "libexclude",
crate_name: "exclude",
srcs: ["exclude.rs"],
vendor_available: true,
exclude_from_vendor_snapshot: true,
}
rust_ffi_shared {
name: "libavailable_exclude",
crate_name: "available_exclude",
srcs: ["lib.rs"],
vendor_available: true,
exclude_from_vendor_snapshot: true,
}
`
mockFS := map[string][]byte{
"framework/Android.bp": []byte(frameworkBp),
"framework/include.rs": nil,
"framework/exclude.rs": nil,
}
ctx := testRustVndkFs(t, "", mockFS)
// Test an include and exclude framework module.
cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false, vendorVariant)
cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true, vendorVariant)
cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true, vendorVariant)
// Verify the content of the vendor snapshot.
snapshotDir := "vendor-snapshot"
snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
var includeJsonFiles []string
var excludeJsonFiles []string
for _, arch := range [][]string{
[]string{"arm64", "armv8-a"},
[]string{"arm", "armv7-a-neon"},
} {
archType := arch[0]
archVariant := arch[1]
archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
// Included modules
cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
// Excluded modules
cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
}
// Verify that each json file for an included module has a rule.
for _, jsonFile := range includeJsonFiles {
if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
t.Errorf("include json file %q not found", jsonFile)
}
}
// Verify that each json file for an excluded module has no rule.
for _, jsonFile := range excludeJsonFiles {
if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
t.Errorf("exclude json file %q found", jsonFile)
}
}
}