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
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
new file mode 100644
index 0000000..c5183f7
--- /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)
+ }
+ }
+}