// Copyright 2021 Google Inc. All rights reserved. // // 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 cc import ( "android/soong/android" "fmt" "path/filepath" "reflect" "strings" "testing" ) func TestVendorSnapshotCapture(t *testing.T) { bp := ` cc_library { name: "libvndk", vendor_available: true, product_available: true, vndk: { enabled: true, }, nocrt: true, } cc_library { name: "libvendor", vendor: true, nocrt: true, } cc_library { name: "libvendor_available", vendor_available: true, nocrt: true, } cc_library_headers { name: "libvendor_headers", vendor_available: true, nocrt: true, } cc_binary { name: "vendor_bin", vendor: true, nocrt: true, } cc_binary { name: "vendor_available_bin", vendor_available: true, nocrt: true, } toolchain_library { name: "libb", vendor_available: true, src: "libb.a", } cc_object { name: "obj", vendor_available: true, } cc_library { name: "libllndk", llndk: { symbol_file: "libllndk.map.txt", }, } ` config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) config.TestProductVariables.DeviceVndkVersion = StringPtr("current") config.TestProductVariables.Platform_vndk_version = StringPtr("29") ctx := testCcWithConfig(t, config) // 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") checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant) checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant) jsonFiles = append(jsonFiles, filepath.Join(sharedDir, "libvendor.so.json"), filepath.Join(sharedDir, "libvendor_available.so.json")) // LLNDK modules are not captured checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant) // For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured. // Also cfi variants are captured, except for prebuilts like toolchain_library staticVariant := fmt.Sprintf("android_vendor.29_%s_%s_static", archType, archVariant) staticCfiVariant := fmt.Sprintf("android_vendor.29_%s_%s_static_cfi", archType, archVariant) staticDir := filepath.Join(snapshotVariantPath, archDir, "static") checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant) checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant) checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant) checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant) checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant) checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant) checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant) jsonFiles = append(jsonFiles, filepath.Join(staticDir, "libb.a.json"), filepath.Join(staticDir, "libvndk.a.json"), filepath.Join(staticDir, "libvndk.cfi.a.json"), filepath.Join(staticDir, "libvendor.a.json"), filepath.Join(staticDir, "libvendor.cfi.a.json"), filepath.Join(staticDir, "libvendor_available.a.json"), filepath.Join(staticDir, "libvendor_available.cfi.a.json")) // For binary executables, all vendor:true and vendor_available modules are captured. if archType == "arm64" { binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant) binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary") checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant) checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant) jsonFiles = append(jsonFiles, filepath.Join(binaryDir, "vendor_bin.json"), filepath.Join(binaryDir, "vendor_available_bin.json")) } // For header libraries, all vendor:true and vendor_available modules are captured. headerDir := filepath.Join(snapshotVariantPath, archDir, "header") jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json")) // For object modules, all vendor:true and vendor_available modules are captured. objectVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant) objectDir := filepath.Join(snapshotVariantPath, archDir, "object") checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant) jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json")) } for _, jsonFile := range jsonFiles { // verify all json files exist if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil { t.Errorf("%q expected but not found", jsonFile) } } // 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 := ` cc_library_shared { name: "libvendor", vendor: true, nocrt: true, } cc_library_shared { name: "libvendor_available", vendor_available: true, nocrt: true, } genrule { name: "libfoo_gen", cmd: "", out: ["libfoo.so"], } cc_prebuilt_library_shared { name: "libfoo", vendor: true, prefer: true, srcs: [":libfoo_gen"], } cc_library_shared { name: "libfoo", vendor: true, nocrt: true, } ` config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) config.TestProductVariables.DeviceVndkVersion = StringPtr("current") config.TestProductVariables.Platform_vndk_version = StringPtr("29") config.TestProductVariables.DirectedVendorSnapshot = true config.TestProductVariables.VendorSnapshotModules = make(map[string]bool) config.TestProductVariables.VendorSnapshotModules["libvendor"] = true config.TestProductVariables.VendorSnapshotModules["libfoo"] = true ctx := testCcWithConfig(t, config) // 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 checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant) includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json")) // Check that snapshot captures "prefer: true" prebuilt checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant) includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json")) // Excluded modules. Modules not included in the directed vendor snapshot // are still include as fake modules. checkSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant) includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor_available.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 TestVendorSnapshotUse(t *testing.T) { frameworkBp := ` cc_library { name: "libvndk", vendor_available: true, product_available: true, vndk: { enabled: true, }, nocrt: true, } cc_library { name: "libvendor", vendor: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], } cc_library { name: "libvendor_available", vendor_available: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], } cc_library { name: "lib32", vendor: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], compile_multilib: "32", } cc_library { name: "lib64", vendor: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], compile_multilib: "64", } cc_library { name: "libllndk", llndk: { symbol_file: "libllndk.map.txt", }, } cc_binary { name: "bin", vendor: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], } cc_binary { name: "bin32", vendor: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], compile_multilib: "32", } ` vndkBp := ` vndk_prebuilt_shared { name: "libvndk", version: "31", target_arch: "arm64", vendor_available: true, product_available: true, vndk: { enabled: true, }, arch: { arm64: { srcs: ["libvndk.so"], export_include_dirs: ["include/libvndk"], }, arm: { srcs: ["libvndk.so"], export_include_dirs: ["include/libvndk"], }, }, } // old snapshot module which has to be ignored vndk_prebuilt_shared { name: "libvndk", version: "26", target_arch: "arm64", vendor_available: true, product_available: true, vndk: { enabled: true, }, arch: { arm64: { srcs: ["libvndk.so"], export_include_dirs: ["include/libvndk"], }, arm: { srcs: ["libvndk.so"], export_include_dirs: ["include/libvndk"], }, }, } // different arch snapshot which has to be ignored vndk_prebuilt_shared { name: "libvndk", version: "31", target_arch: "arm", vendor_available: true, product_available: true, vndk: { enabled: true, }, arch: { arm: { srcs: ["libvndk.so"], export_include_dirs: ["include/libvndk"], }, }, } vndk_prebuilt_shared { name: "libllndk", version: "31", target_arch: "arm64", vendor_available: true, product_available: true, arch: { arm64: { srcs: ["libllndk.so"], }, arm: { srcs: ["libllndk.so"], }, }, } ` vendorProprietaryBp := ` cc_library { name: "libvendor_without_snapshot", vendor: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], } cc_library_shared { name: "libclient", vendor: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], shared_libs: ["libvndk", "libvendor_available", "libllndk"], static_libs: ["libvendor", "libvendor_without_snapshot"], arch: { arm64: { shared_libs: ["lib64"], }, arm: { shared_libs: ["lib32"], }, }, srcs: ["client.cpp"], } cc_library_shared { name: "libclient_cfi", vendor: true, nocrt: true, no_libcrt: true, stl: "none", system_shared_libs: [], static_libs: ["libvendor"], sanitize: { cfi: true, }, srcs: ["client.cpp"], } cc_binary { name: "bin_without_snapshot", vendor: true, nocrt: true, no_libcrt: true, stl: "libc++_static", system_shared_libs: [], static_libs: ["libvndk"], srcs: ["bin.cpp"], } vendor_snapshot { name: "vendor_snapshot", version: "31", arch: { arm64: { vndk_libs: [ "libvndk", "libllndk", ], static_libs: [ "libc++_static", "libc++demangle", "libunwind", "libvendor", "libvendor_available", "libvndk", "lib64", ], shared_libs: [ "libvendor", "libvendor_available", "lib64", ], binaries: [ "bin", ], }, arm: { vndk_libs: [ "libvndk", "libllndk", ], static_libs: [ "libvendor", "libvendor_available", "libvndk", "lib32", ], shared_libs: [ "libvendor", "libvendor_available", "lib32", ], binaries: [ "bin32", ], }, } } vendor_snapshot_static { name: "libvndk", version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, arch: { arm64: { src: "libvndk.a", }, arm: { src: "libvndk.a", }, }, shared_libs: ["libvndk"], export_shared_lib_headers: ["libvndk"], } vendor_snapshot_shared { name: "libvendor", version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, shared_libs: [ "libvendor_without_snapshot", "libvendor_available", "libvndk", ], arch: { arm64: { src: "libvendor.so", export_include_dirs: ["include/libvendor"], }, arm: { src: "libvendor.so", export_include_dirs: ["include/libvendor"], }, }, } vendor_snapshot_static { name: "lib32", version: "31", target_arch: "arm64", compile_multilib: "32", vendor: true, arch: { arm: { src: "lib32.a", }, }, } vendor_snapshot_shared { name: "lib32", version: "31", target_arch: "arm64", compile_multilib: "32", vendor: true, arch: { arm: { src: "lib32.so", }, }, } vendor_snapshot_static { name: "lib64", version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, arch: { arm64: { src: "lib64.a", }, }, } vendor_snapshot_shared { name: "lib64", version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, arch: { arm64: { src: "lib64.so", }, }, } vendor_snapshot_static { name: "libvendor", version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, arch: { arm64: { cfi: { src: "libvendor.cfi.a", export_include_dirs: ["include/libvendor_cfi"], }, src: "libvendor.a", export_include_dirs: ["include/libvendor"], }, arm: { cfi: { src: "libvendor.cfi.a", export_include_dirs: ["include/libvendor_cfi"], }, src: "libvendor.a", export_include_dirs: ["include/libvendor"], }, }, } vendor_snapshot_shared { name: "libvendor_available", version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, arch: { arm64: { src: "libvendor_available.so", export_include_dirs: ["include/libvendor"], }, arm: { src: "libvendor_available.so", export_include_dirs: ["include/libvendor"], }, }, } vendor_snapshot_static { name: "libvendor_available", version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, arch: { arm64: { src: "libvendor_available.a", export_include_dirs: ["include/libvendor"], }, arm: { src: "libvendor_available.so", export_include_dirs: ["include/libvendor"], }, }, } vendor_snapshot_static { name: "libc++_static", version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, arch: { arm64: { src: "libc++_static.a", }, }, } vendor_snapshot_static { name: "libc++demangle", version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, arch: { arm64: { src: "libc++demangle.a", }, }, } vendor_snapshot_static { name: "libunwind", version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, arch: { arm64: { src: "libunwind.a", }, }, } vendor_snapshot_binary { name: "bin", version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, arch: { arm64: { src: "bin", }, }, } vendor_snapshot_binary { name: "bin32", version: "31", target_arch: "arm64", compile_multilib: "32", vendor: true, arch: { arm: { src: "bin32", }, }, } // old snapshot module which has to be ignored vendor_snapshot_binary { name: "bin", version: "26", target_arch: "arm64", compile_multilib: "first", vendor: true, arch: { arm64: { src: "bin", }, }, } // different arch snapshot which has to be ignored vendor_snapshot_binary { name: "bin", version: "31", target_arch: "arm", compile_multilib: "first", vendor: true, arch: { arm64: { src: "bin", }, }, } ` depsBp := GatherRequiredDepsForTest(android.Android) mockFS := map[string][]byte{ "deps/Android.bp": []byte(depsBp), "framework/Android.bp": []byte(frameworkBp), "framework/symbol.txt": nil, "vendor/Android.bp": []byte(vendorProprietaryBp), "vendor/bin": nil, "vendor/bin32": nil, "vendor/bin.cpp": nil, "vendor/client.cpp": nil, "vendor/include/libvndk/a.h": nil, "vendor/include/libvendor/b.h": nil, "vendor/include/libvendor_cfi/c.h": nil, "vendor/libc++_static.a": nil, "vendor/libc++demangle.a": nil, "vendor/libunwind.a": nil, "vendor/libvndk.a": nil, "vendor/libvendor.a": nil, "vendor/libvendor.cfi.a": nil, "vendor/libvendor.so": nil, "vendor/lib32.a": nil, "vendor/lib32.so": nil, "vendor/lib64.a": nil, "vendor/lib64.so": nil, "vndk/Android.bp": []byte(vndkBp), "vndk/include/libvndk/a.h": nil, "vndk/libvndk.so": nil, "vndk/libllndk.so": nil, } config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS) config.TestProductVariables.DeviceVndkVersion = StringPtr("31") config.TestProductVariables.Platform_vndk_version = StringPtr("32") ctx := CreateTestContext(config) ctx.Register() _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "vendor/Android.bp", "vndk/Android.bp"}) android.FailIfErrored(t, errs) _, errs = ctx.PrepareBuildActions(config) android.FailIfErrored(t, errs) sharedVariant := "android_vendor.31_arm64_armv8-a_shared" staticVariant := "android_vendor.31_arm64_armv8-a_static" binaryVariant := "android_vendor.31_arm64_armv8-a" sharedCfiVariant := "android_vendor.31_arm64_armv8-a_shared_cfi" staticCfiVariant := "android_vendor.31_arm64_armv8-a_static_cfi" shared32Variant := "android_vendor.31_arm_armv7-a-neon_shared" binary32Variant := "android_vendor.31_arm_armv7-a-neon" // libclient uses libvndk.vndk.31.arm64, libvendor.vendor_static.31.arm64, libvendor_without_snapshot libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"] for _, includeFlags := range []string{ "-Ivndk/include/libvndk", // libvndk "-Ivendor/include/libvendor", // libvendor } { if !strings.Contains(libclientCcFlags, includeFlags) { t.Errorf("flags for libclient must contain %#v, but was %#v.", includeFlags, libclientCcFlags) } } libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("ld").Args["libFlags"] for _, input := range [][]string{ []string{sharedVariant, "libvndk.vndk.31.arm64"}, []string{sharedVariant, "libllndk.vndk.31.arm64"}, []string{staticVariant, "libvendor.vendor_static.31.arm64"}, []string{staticVariant, "libvendor_without_snapshot"}, } { outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */) if !strings.Contains(libclientLdFlags, outputPaths[0].String()) { t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags) } } libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "libllndk.vendor", "lib64"}; !reflect.DeepEqual(g, w) { t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g) } libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot"}; !reflect.DeepEqual(g, w) { t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g) } libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).Properties.AndroidMkSharedLibs if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "libllndk.vendor", "lib32"}; !reflect.DeepEqual(g, w) { t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g) } // libclient_cfi uses libvendor.vendor_static.31.arm64's cfi variant libclientCfiCcFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("cc").Args["cFlags"] if !strings.Contains(libclientCfiCcFlags, "-Ivendor/include/libvendor_cfi") { t.Errorf("flags for libclient_cfi must contain %#v, but was %#v.", "-Ivendor/include/libvendor_cfi", libclientCfiCcFlags) } libclientCfiLdFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("ld").Args["libFlags"] libvendorCfiOutputPaths := getOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.31.arm64"}) if !strings.Contains(libclientCfiLdFlags, libvendorCfiOutputPaths[0].String()) { t.Errorf("libflags for libclientCfi must contain %#v, but was %#v", libvendorCfiOutputPaths[0], libclientCfiLdFlags) } // bin_without_snapshot uses libvndk.vendor_static.31.arm64 (which reexports vndk's exported headers) binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"] if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivndk/include/libvndk") { t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.", "-Ivendor/include/libvndk", binWithoutSnapshotCcFlags) } binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"] libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.31.arm64"}) if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) { t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v", libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags) } // libvendor.so is installed by libvendor.vendor_shared.31.arm64 ctx.ModuleForTests("libvendor.vendor_shared.31.arm64", sharedVariant).Output("libvendor.so") // lib64.so is installed by lib64.vendor_shared.31.arm64 ctx.ModuleForTests("lib64.vendor_shared.31.arm64", sharedVariant).Output("lib64.so") // lib32.so is installed by lib32.vendor_shared.31.arm64 ctx.ModuleForTests("lib32.vendor_shared.31.arm64", shared32Variant).Output("lib32.so") // libvendor_available.so is installed by libvendor_available.vendor_shared.31.arm64 ctx.ModuleForTests("libvendor_available.vendor_shared.31.arm64", sharedVariant).Output("libvendor_available.so") // libvendor_without_snapshot.so is installed by libvendor_without_snapshot ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so") // bin is installed by bin.vendor_binary.31.arm64 ctx.ModuleForTests("bin.vendor_binary.31.arm64", binaryVariant).Output("bin") // bin32 is installed by bin32.vendor_binary.31.arm64 ctx.ModuleForTests("bin32.vendor_binary.31.arm64", binary32Variant).Output("bin32") // bin_without_snapshot is installed by bin_without_snapshot ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot") // libvendor, libvendor_available and bin don't have vendor.31 variant libvendorVariants := ctx.ModuleVariantsForTests("libvendor") if inList(sharedVariant, libvendorVariants) { t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant) } libvendorAvailableVariants := ctx.ModuleVariantsForTests("libvendor_available") if inList(sharedVariant, libvendorAvailableVariants) { t.Errorf("libvendor_available must not have variant %#v, but it does", sharedVariant) } binVariants := ctx.ModuleVariantsForTests("bin") if inList(binaryVariant, binVariants) { t.Errorf("bin must not have variant %#v, but it does", sharedVariant) } } func TestVendorSnapshotSanitizer(t *testing.T) { bp := ` vendor_snapshot { name: "vendor_snapshot", version: "28", arch: { arm64: { static_libs: [ "libsnapshot", "note_memtag_heap_sync", ], }, }, } vendor_snapshot_static { name: "libsnapshot", vendor: true, target_arch: "arm64", version: "28", arch: { arm64: { src: "libsnapshot.a", cfi: { src: "libsnapshot.cfi.a", } }, }, } vendor_snapshot_static { name: "note_memtag_heap_sync", vendor: true, target_arch: "arm64", version: "28", arch: { arm64: { src: "note_memtag_heap_sync.a", }, }, } cc_test { name: "vstest", gtest: false, vendor: true, compile_multilib: "64", nocrt: true, no_libcrt: true, stl: "none", static_libs: ["libsnapshot"], system_shared_libs: [], } ` mockFS := map[string][]byte{ "vendor/Android.bp": []byte(bp), "vendor/libc++demangle.a": nil, "vendor/libsnapshot.a": nil, "vendor/libsnapshot.cfi.a": nil, "vendor/note_memtag_heap_sync.a": nil, } config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS) config.TestProductVariables.DeviceVndkVersion = StringPtr("28") config.TestProductVariables.Platform_vndk_version = StringPtr("29") ctx := testCcWithConfig(t, config) // Check non-cfi and cfi variant. staticVariant := "android_vendor.28_arm64_armv8-a_static" staticCfiVariant := "android_vendor.28_arm64_armv8-a_static_cfi" staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticVariant).Module().(*Module) assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a") staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.28.arm64", staticCfiVariant).Module().(*Module) assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a") } func assertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) { t.Helper() m := ctx.ModuleForTests(name, vendorVariant).Module().(*Module) if m.ExcludeFromVendorSnapshot() != expected { t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected) } } func assertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) { t.Helper() m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module) if m.ExcludeFromRecoverySnapshot() != expected { t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected) } } 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. frameworkBp := ` cc_library_shared { name: "libinclude", srcs: ["src/include.cpp"], vendor_available: true, } cc_library_shared { name: "libexclude", srcs: ["src/exclude.cpp"], vendor: true, exclude_from_vendor_snapshot: true, } cc_library_shared { name: "libavailable_exclude", srcs: ["src/exclude.cpp"], vendor_available: true, exclude_from_vendor_snapshot: true, } ` vendorProprietaryBp := ` cc_library_shared { name: "libvendor", srcs: ["vendor.cpp"], vendor: true, } ` depsBp := GatherRequiredDepsForTest(android.Android) mockFS := map[string][]byte{ "deps/Android.bp": []byte(depsBp), "framework/Android.bp": []byte(frameworkBp), "framework/include.cpp": nil, "framework/exclude.cpp": nil, "device/Android.bp": []byte(vendorProprietaryBp), "device/vendor.cpp": nil, } config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS) config.TestProductVariables.DeviceVndkVersion = StringPtr("current") config.TestProductVariables.Platform_vndk_version = StringPtr("29") ctx := CreateTestContext(config) ctx.Register() _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"}) android.FailIfErrored(t, errs) _, errs = ctx.PrepareBuildActions(config) android.FailIfErrored(t, errs) // Test an include and exclude framework module. assertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false) assertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true) assertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true) // A vendor module is excluded, but by its path, not the // exclude_from_vendor_snapshot property. assertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false) // 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 checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant) includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json")) // Excluded modules checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant) excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json")) checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant) excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json")) 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) } } } func TestVendorSnapshotExcludeInVendorProprietaryPathErrors(t *testing.T) { // This test verifies that using the exclude_from_vendor_snapshot // property on a module in a vendor proprietary path generates an // error. These modules are already excluded, so we prohibit using the // property in this way, which could add to confusion. vendorProprietaryBp := ` cc_library_shared { name: "libvendor", srcs: ["vendor.cpp"], vendor: true, exclude_from_vendor_snapshot: true, } ` depsBp := GatherRequiredDepsForTest(android.Android) mockFS := map[string][]byte{ "deps/Android.bp": []byte(depsBp), "device/Android.bp": []byte(vendorProprietaryBp), "device/vendor.cpp": nil, } config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS) config.TestProductVariables.DeviceVndkVersion = StringPtr("current") config.TestProductVariables.Platform_vndk_version = StringPtr("29") ctx := CreateTestContext(config) ctx.Register() _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "device/Android.bp"}) android.FailIfErrored(t, errs) _, errs = ctx.PrepareBuildActions(config) android.CheckErrorsAgainstExpectations(t, errs, []string{ `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`, `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`, `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`, `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`, `module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`, `module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`, }) } func TestRecoverySnapshotCapture(t *testing.T) { bp := ` cc_library { name: "libvndk", vendor_available: true, recovery_available: true, product_available: true, vndk: { enabled: true, }, nocrt: true, } cc_library { name: "librecovery", recovery: true, nocrt: true, } cc_library { name: "librecovery_available", recovery_available: true, nocrt: true, } cc_library_headers { name: "librecovery_headers", recovery_available: true, nocrt: true, } cc_binary { name: "recovery_bin", recovery: true, nocrt: true, } cc_binary { name: "recovery_available_bin", recovery_available: true, nocrt: true, } toolchain_library { name: "libb", recovery_available: true, src: "libb.a", } cc_object { name: "obj", recovery_available: true, } ` config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current") config.TestProductVariables.Platform_vndk_version = StringPtr("29") ctx := testCcWithConfig(t, config) // Check Recovery snapshot output. snapshotDir := "recovery-snapshot" snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64") snapshotSingleton := ctx.SingletonForTests("recovery-snapshot") var jsonFiles []string for _, arch := range [][]string{ []string{"arm64", "armv8-a"}, } { archType := arch[0] archVariant := arch[1] archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant) // For shared libraries, only recovery_available modules are captured. sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant) sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared") checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant) checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant) checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant) jsonFiles = append(jsonFiles, filepath.Join(sharedDir, "libvndk.so.json"), filepath.Join(sharedDir, "librecovery.so.json"), filepath.Join(sharedDir, "librecovery_available.so.json")) // For static libraries, all recovery:true and recovery_available modules are captured. staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant) staticDir := filepath.Join(snapshotVariantPath, archDir, "static") checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant) checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant) checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant) jsonFiles = append(jsonFiles, filepath.Join(staticDir, "libb.a.json"), filepath.Join(staticDir, "librecovery.a.json"), filepath.Join(staticDir, "librecovery_available.a.json")) // For binary executables, all recovery:true and recovery_available modules are captured. if archType == "arm64" { binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant) binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary") checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant) checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant) jsonFiles = append(jsonFiles, filepath.Join(binaryDir, "recovery_bin.json"), filepath.Join(binaryDir, "recovery_available_bin.json")) } // For header libraries, all vendor:true and vendor_available modules are captured. headerDir := filepath.Join(snapshotVariantPath, archDir, "header") jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json")) // For object modules, all vendor:true and vendor_available modules are captured. objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant) objectDir := filepath.Join(snapshotVariantPath, archDir, "object") checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant) jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json")) } for _, jsonFile := range jsonFiles { // verify all json files exist if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil { t.Errorf("%q expected but not found", jsonFile) } } } func TestRecoverySnapshotExclude(t *testing.T) { // This test verifies that the exclude_from_recovery_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 recovery snapshot based on their path (framework or // vendor) and the exclude_from_recovery_snapshot property. frameworkBp := ` cc_library_shared { name: "libinclude", srcs: ["src/include.cpp"], recovery_available: true, } cc_library_shared { name: "libexclude", srcs: ["src/exclude.cpp"], recovery: true, exclude_from_recovery_snapshot: true, } cc_library_shared { name: "libavailable_exclude", srcs: ["src/exclude.cpp"], recovery_available: true, exclude_from_recovery_snapshot: true, } ` vendorProprietaryBp := ` cc_library_shared { name: "librecovery", srcs: ["recovery.cpp"], recovery: true, } ` depsBp := GatherRequiredDepsForTest(android.Android) mockFS := map[string][]byte{ "deps/Android.bp": []byte(depsBp), "framework/Android.bp": []byte(frameworkBp), "framework/include.cpp": nil, "framework/exclude.cpp": nil, "device/Android.bp": []byte(vendorProprietaryBp), "device/recovery.cpp": nil, } config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS) config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current") config.TestProductVariables.Platform_vndk_version = StringPtr("29") ctx := CreateTestContext(config) ctx.Register() _, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"}) android.FailIfErrored(t, errs) _, errs = ctx.PrepareBuildActions(config) android.FailIfErrored(t, errs) // Test an include and exclude framework module. assertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false) assertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true) assertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true) // A recovery module is excluded, but by its path, not the // exclude_from_recovery_snapshot property. assertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false) // Verify the content of the recovery snapshot. snapshotDir := "recovery-snapshot" snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64") snapshotSingleton := ctx.SingletonForTests("recovery-snapshot") var includeJsonFiles []string var excludeJsonFiles []string for _, arch := range [][]string{ []string{"arm64", "armv8-a"}, } { archType := arch[0] archVariant := arch[1] archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant) sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant) sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared") // Included modules checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant) includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json")) // Excluded modules checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant) excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json")) checkSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant) excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json")) 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) } } } func TestRecoverySnapshotDirected(t *testing.T) { bp := ` cc_library_shared { name: "librecovery", recovery: true, nocrt: true, } cc_library_shared { name: "librecovery_available", recovery_available: true, nocrt: true, } genrule { name: "libfoo_gen", cmd: "", out: ["libfoo.so"], } cc_prebuilt_library_shared { name: "libfoo", recovery: true, prefer: true, srcs: [":libfoo_gen"], } cc_library_shared { name: "libfoo", recovery: true, nocrt: true, } ` config := TestConfig(t.TempDir(), android.Android, nil, bp, nil) config.TestProductVariables.DeviceVndkVersion = StringPtr("current") config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current") config.TestProductVariables.Platform_vndk_version = StringPtr("29") config.TestProductVariables.DirectedRecoverySnapshot = true config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool) config.TestProductVariables.RecoverySnapshotModules["librecovery"] = true config.TestProductVariables.RecoverySnapshotModules["libfoo"] = true ctx := testCcWithConfig(t, config) // Check recovery snapshot output. snapshotDir := "recovery-snapshot" snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64") snapshotSingleton := ctx.SingletonForTests("recovery-snapshot") var includeJsonFiles []string for _, arch := range [][]string{ []string{"arm64", "armv8-a"}, } { archType := arch[0] archVariant := arch[1] archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant) sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant) sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared") // Included modules checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant) includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json")) // Check that snapshot captures "prefer: true" prebuilt checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant) includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json")) // Excluded modules. Modules not included in the directed recovery snapshot // are still include as fake modules. checkSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant) includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.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) } } }