diff --git a/rust/Android.bp b/rust/Android.bp index b611672d4..63983904c 100644 --- a/rust/Android.bp +++ b/rust/Android.bp @@ -53,6 +53,7 @@ bootstrap_go_package { "rust_test.go", "source_provider_test.go", "test_test.go", + "vendor_snapshot_test.go", ], pluginFor: ["soong_build"], } diff --git a/rust/binary.go b/rust/binary.go index ffc0413ec..8d0a0a7a0 100644 --- a/rust/binary.go +++ b/rust/binary.go @@ -137,6 +137,9 @@ func (binary *binaryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoD // Binaries default to dylib dependencies for device, rlib for host. if binary.preferRlib() { return rlibAutoDep + } else if mod, ok := ctx.Module().(*Module); ok && mod.InVendor() { + // Vendor Rust binaries should prefer rlibs. + return rlibAutoDep } else if ctx.Device() { return dylibAutoDep } else { @@ -147,6 +150,8 @@ func (binary *binaryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoD func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage { if binary.preferRlib() { return RlibLinkage + } else if ctx.RustModule().InVendor() { + return RlibLinkage } return binary.baseCompiler.stdLinkage(ctx) } diff --git a/rust/image.go b/rust/image.go index 900842ec8..6cfb42c7d 100644 --- a/rust/image.go +++ b/rust/image.go @@ -202,6 +202,8 @@ func (mod *Module) SetImageVariation(ctx android.BaseModuleContext, variant stri func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) { // Rust does not support installing to the product image yet. + vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific() + if Bool(mod.VendorProperties.Product_available) { mctx.PropertyErrorf("product_available", "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.") } } + if vendorSpecific { + mctx.PropertyErrorf("vendor", "Vendor-only non-rust_ffi Rust modules are not supported.") + } cc.MutateImage(mctx, mod) diff --git a/rust/library.go b/rust/library.go index 1bdf83a43..7421817cc 100644 --- a/rust/library.go +++ b/rust/library.go @@ -99,6 +99,8 @@ type libraryDecorator struct { MutatedProperties LibraryMutatedProperties includeDirs android.Paths sourceProvider SourceProvider + + collectedSnapshotHeaders android.Paths } type libraryInterface interface { @@ -220,7 +222,10 @@ func (library *libraryDecorator) setSource() { } 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 } else if library.rlib() || library.static() { return rlibAutoDep @@ -236,7 +241,10 @@ func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) aut } 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 } else if library.baseCompiler.preferRlib() { return RlibLinkage @@ -623,6 +631,19 @@ func LibraryMutator(mctx android.BottomUpMutatorContext) { // Disable dylib Vendor Ramdisk variations until we support these. 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": v.(*Module).compiler.(libraryInterface).setSource() // 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 +} diff --git a/rust/rust.go b/rust/rust.go index 46c8f250c..7a8687c8b 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -85,6 +85,9 @@ type BaseProperties struct { VendorRamdiskVariantNeeded bool `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. // On device without a dedicated recovery partition, the module is only // available after switching root into @@ -92,6 +95,20 @@ type BaseProperties struct { // the recovery variant instead (TODO(b/165791368) recovery not yet supported) 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). Min_sdk_version *string @@ -826,6 +843,14 @@ func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { 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) if mod.installable(apexInfo) { mod.compiler.install(ctx) @@ -1056,6 +1081,10 @@ func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps { depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...) depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...) 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) exportDep = true case cc.IsHeaderDepTag(depTag): @@ -1161,6 +1190,11 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { deps := mod.deps(ctx) 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" if mod.compiler.stdLinkage(ctx) == RlibLinkage { @@ -1168,20 +1202,25 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { } rlibDepVariations := commonDepVariations + if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() { rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "rust_stdlinkage", Variation: stdLinkage}) } + // rlibs actx.AddVariationDependencies( append(rlibDepVariations, []blueprint.Variation{ {Mutator: "rust_libraries", Variation: rlibVariation}}...), rlibDepTag, deps.Rlibs...) + + // dylibs actx.AddVariationDependencies( append(commonDepVariations, []blueprint.Variation{ {Mutator: "rust_libraries", Variation: dylibVariation}}...), dylibDepTag, deps.Dylibs...) + // rustlibs if deps.Rustlibs != nil && !mod.compiler.Disabled() { autoDep := mod.compiler.(autoDeppable).autoDep(ctx) if autoDep.depTag == rlibDepTag { @@ -1194,10 +1233,12 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { autoDep.depTag, deps.Rustlibs...) } } + + // stdlibs if deps.Stdlibs != nil { if mod.compiler.stdLinkage(ctx) == RlibLinkage { actx.AddVariationDependencies( - append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "rlib"}), + append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...), rlibDepTag, deps.Stdlibs...) } else { actx.AddVariationDependencies( @@ -1205,24 +1246,45 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { dylibDepTag, deps.Stdlibs...) } } - actx.AddVariationDependencies(append(commonDepVariations, - blueprint.Variation{Mutator: "link", Variation: "shared"}), - cc.SharedDepTag(), deps.SharedLibs...) - actx.AddVariationDependencies(append(commonDepVariations, - blueprint.Variation{Mutator: "link", Variation: "static"}), - cc.StaticDepTag(false), deps.StaticLibs...) - actx.AddVariationDependencies(append(commonDepVariations, - blueprint.Variation{Mutator: "link", Variation: "static"}), - cc.StaticDepTag(true), deps.WholeStaticLibs...) + + for _, lib := range deps.SharedLibs { + depTag := cc.SharedDepTag() + name, version := cc.StubsLibNameAndVersion(lib) + + variations := []blueprint.Variation{ + {Mutator: "link", Variation: "shared"}, + } + 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...) crtVariations := cc.GetCrtVariations(ctx, mod) 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 != "" { - 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 { @@ -1232,6 +1294,7 @@ func (mod *Module) DepsMutator(actx android.BottomUpMutatorContext) { bindgen.Properties.Custom_bindgen) } } + // 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...) } diff --git a/rust/rust_test.go b/rust/rust_test.go index 6ae05d988..f07f86bf8 100644 --- a/rust/rust_test.go +++ b/rust/rust_test.go @@ -37,21 +37,28 @@ var prepareForRustTest = android.GroupFixturePreparers( 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{ - "foo.rs": nil, - "foo.c": nil, - "src/bar.rs": nil, - "src/any.h": nil, - "proto.proto": nil, - "proto/buf.proto": nil, - "buf.proto": nil, - "foo.proto": nil, - "liby.so": nil, - "libz.so": nil, - "data.txt": nil, + "foo.rs": nil, + "foo.c": nil, + "src/bar.rs": nil, + "src/any.h": nil, + "c_includes/c_header.h": nil, + "rust_includes/rust_headers.h": nil, + "proto.proto": nil, + "proto/buf.proto": nil, + "buf.proto": nil, + "foo.proto": nil, + "liby.so": nil, + "libz.so": nil, + "data.txt": nil, } // 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 { + 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) result := android.GroupFixturePreparers( prepareForRustTest, - rustMockedFiles.AddToFixture(), + fs.AddToFixture(), android.FixtureModifyProductVariables( func(variables android.FixtureProductVariables) { variables.DeviceVndkVersion = StringPtr("current") @@ -80,6 +93,7 @@ func testRustVndk(t *testing.T, bp string) *android.TestContext { ), ).RunTestWithBp(t, bp) return result.TestContext + } // 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. 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) android.GroupFixturePreparers( prepareForRustTest, - rustMockedFiles.AddToFixture(), + fs.AddToFixture(), android.FixtureModifyProductVariables( func(variables android.FixtureProductVariables) { variables.DeviceVndkVersion = StringPtr("current") diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go index 943c79098..9d5154ce2 100644 --- a/rust/snapshot_utils.go +++ b/rust/snapshot_utils.go @@ -18,18 +18,34 @@ import ( "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 { - // TODO Rust does not yet support snapshotting - return false + return Bool(mod.Properties.Exclude_from_vendor_snapshot) } func (mod *Module) ExcludeFromRecoverySnapshot() bool { - // TODO Rust does not yet support snapshotting - return false + return Bool(mod.Properties.Exclude_from_recovery_snapshot) } 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 } @@ -39,8 +55,7 @@ func (mod *Module) SnapshotRuntimeLibs() []string { } func (mod *Module) SnapshotSharedLibs() []string { - // TODO Rust does not yet support snapshotting - return []string{} + return mod.Properties.SnapshotSharedLibs } func (mod *Module) Symlinks() []string { @@ -49,6 +64,8 @@ func (mod *Module) Symlinks() []string { } 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{} } diff --git a/rust/testing.go b/rust/testing.go index a0f86b220..a7cbf54d8 100644 --- a/rust/testing.go +++ b/rust/testing.go @@ -45,6 +45,11 @@ var PrepareForIntegrationTestWithRust = android.GroupFixturePreparers( PrepareForTestWithRustDefaultModules, ) +var PrepareForTestWithRustIncludeVndk = android.GroupFixturePreparers( + PrepareForIntegrationTestWithRust, + cc.PrepareForTestWithCcIncludeVndk, +) + func GatherRequiredDepsForTest() string { bp := ` rust_prebuilt_library { diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go new file mode 100644 index 000000000..c5183f754 --- /dev/null +++ b/rust/vendor_snapshot_test.go @@ -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) + } + } +}