// Copyright 2019 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 ( "strings" "testing" "android/soong/android" ) // Test that feature flags are being correctly generated. func TestFeaturesToFlags(t *testing.T) { ctx := testRust(t, ` rust_library_host_dylib { name: "libfoo", srcs: ["foo.rs"], crate_name: "foo", features: [ "fizz", "buzz" ], }`) libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"fizz\"'") || !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"buzz\"'") { t.Fatalf("missing fizz and buzz feature flags for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"]) } } // Test that cfgs flags are being correctly generated. func TestCfgsToFlags(t *testing.T) { ctx := testRust(t, ` rust_library_host { name: "libfoo", srcs: ["foo.rs"], crate_name: "foo", cfgs: [ "std", "cfg1=\"one\"" ], }`) libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'std'") || !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'cfg1=\"one\"'") { t.Fatalf("missing std and cfg1 flags for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"]) } } func TestLtoFlag(t *testing.T) { ctx := testRust(t, ` rust_library_host { name: "libfoo", srcs: ["foo.rs"], crate_name: "foo", lto: { thin: false, } } rust_library_host { name: "libfoo_lto", srcs: ["foo.rs"], crate_name: "foo", } `) libfoo := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") libfooLto := ctx.ModuleForTests("libfoo_lto", "linux_glibc_x86_64_dylib").Rule("rustc") if strings.Contains(libfoo.Args["rustcFlags"], "-C lto=thin") { t.Fatalf("libfoo expected to disable lto -- rustcFlags: %#v", libfoo.Args["rustcFlags"]) } if !strings.Contains(libfooLto.Args["rustcFlags"], "-C lto=thin") { t.Fatalf("libfoo expected to enable lto by default -- rustcFlags: %#v", libfooLto.Args["rustcFlags"]) } } // Test that we reject multiple source files. func TestEnforceSingleSourceFile(t *testing.T) { singleSrcError := "srcs can only contain one path for a rust file and source providers prefixed by \":\"" prebuiltSingleSrcError := "prebuilt libraries can only have one entry in srcs" // Test libraries testRustError(t, singleSrcError, ` rust_library_host { name: "foo-bar-library", srcs: ["foo.rs", "src/bar.rs"], }`) // Test binaries testRustError(t, singleSrcError, ` rust_binary_host { name: "foo-bar-binary", srcs: ["foo.rs", "src/bar.rs"], }`) // Test proc_macros testRustError(t, singleSrcError, ` rust_proc_macro { name: "foo-bar-proc-macro", srcs: ["foo.rs", "src/bar.rs"], }`) // Test prebuilts testRustError(t, prebuiltSingleSrcError, ` rust_prebuilt_dylib { name: "foo-bar-prebuilt", srcs: ["liby.so", "libz.so"], host_supported: true, }`) } // Test that we reject _no_ source files. func TestEnforceMissingSourceFiles(t *testing.T) { singleSrcError := "srcs must not be empty" // Test libraries testRustError(t, singleSrcError, ` rust_library_host { name: "foo-bar-library", crate_name: "foo", }`) // Test binaries testRustError(t, singleSrcError, ` rust_binary_host { name: "foo-bar-binary", crate_name: "foo", }`) // Test proc_macros testRustError(t, singleSrcError, ` rust_proc_macro { name: "foo-bar-proc-macro", crate_name: "foo", }`) // Test prebuilts testRustError(t, singleSrcError, ` rust_prebuilt_dylib { name: "foo-bar-prebuilt", crate_name: "foo", host_supported: true, }`) } // Test environment vars for Cargo compat are set. func TestCargoCompat(t *testing.T) { ctx := testRust(t, ` rust_binary { name: "fizz", srcs: ["foo.rs"], crate_name: "foo", cargo_env_compat: true, cargo_pkg_version: "1.0.0" }`) fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc") if !strings.Contains(fizz.Args["envVars"], "CARGO_BIN_NAME=fizz") { t.Fatalf("expected 'CARGO_BIN_NAME=fizz' in envVars, actual envVars: %#v", fizz.Args["envVars"]) } if !strings.Contains(fizz.Args["envVars"], "CARGO_CRATE_NAME=foo") { t.Fatalf("expected 'CARGO_CRATE_NAME=foo' in envVars, actual envVars: %#v", fizz.Args["envVars"]) } if !strings.Contains(fizz.Args["envVars"], "CARGO_PKG_VERSION=1.0.0") { t.Fatalf("expected 'CARGO_PKG_VERSION=1.0.0' in envVars, actual envVars: %#v", fizz.Args["envVars"]) } } func TestInstallDir(t *testing.T) { ctx := testRust(t, ` rust_library_dylib { name: "libfoo", srcs: ["foo.rs"], crate_name: "foo", } rust_binary { name: "fizzbuzz", srcs: ["foo.rs"], }`) install_path_lib64 := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module).compiler.(*libraryDecorator).path.String() install_path_lib32 := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_dylib").Module().(*Module).compiler.(*libraryDecorator).path.String() install_path_bin := ctx.ModuleForTests("fizzbuzz", "android_arm64_armv8-a").Module().(*Module).compiler.(*binaryDecorator).path.String() if !strings.HasSuffix(install_path_lib64, "system/lib64/libfoo.dylib.so") { t.Fatalf("unexpected install path for 64-bit library: %#v", install_path_lib64) } if !strings.HasSuffix(install_path_lib32, "system/lib/libfoo.dylib.so") { t.Fatalf("unexpected install path for 32-bit library: %#v", install_path_lib32) } if !strings.HasSuffix(install_path_bin, "system/bin/fizzbuzz") { t.Fatalf("unexpected install path for binary: %#v", install_path_bin) } } func TestLints(t *testing.T) { bp := ` // foo uses the default value of lints rust_library { name: "libfoo", srcs: ["foo.rs"], crate_name: "foo", } // bar forces the use of the "android" lint set rust_library { name: "libbar", srcs: ["foo.rs"], crate_name: "bar", lints: "android", } // foobar explicitly disable all lints rust_library { name: "libfoobar", srcs: ["foo.rs"], crate_name: "foobar", lints: "none", }` var lintTests = []struct { modulePath string fooFlags string }{ {"", "${config.RustDefaultLints}"}, {"external/", "${config.RustAllowAllLints}"}, {"hardware/", "${config.RustVendorLints}"}, } for _, tc := range lintTests { t.Run("path="+tc.modulePath, func(t *testing.T) { result := android.GroupFixturePreparers( prepareForRustTest, // Test with the blueprint file in different directories. android.FixtureAddTextFile(tc.modulePath+"Android.bp", bp), ).RunTest(t) r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc") android.AssertStringDoesContain(t, "libfoo flags", r.Args["rustcFlags"], tc.fooFlags) r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc") android.AssertStringDoesContain(t, "libbar flags", r.Args["rustcFlags"], "${config.RustDefaultLints}") r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc") android.AssertStringDoesContain(t, "libfoobar flags", r.Args["rustcFlags"], "${config.RustAllowAllLints}") }) } } // Test that devices are linking the stdlib dynamically func TestStdDeviceLinkage(t *testing.T) { ctx := testRust(t, ` rust_binary { name: "fizz", srcs: ["foo.rs"], } rust_library { name: "libfoo", srcs: ["foo.rs"], crate_name: "foo", }`) fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module) fooRlib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module) fooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module) if !android.InList("libstd", fizz.Properties.AndroidMkDylibs) { t.Errorf("libstd is not linked dynamically for device binaries") } if !android.InList("libstd", fooRlib.Properties.AndroidMkDylibs) { t.Errorf("libstd is not linked dynamically for rlibs") } if !android.InList("libstd", fooDylib.Properties.AndroidMkDylibs) { t.Errorf("libstd is not linked dynamically for dylibs") } } // Ensure that manual link flags are disallowed. func TestManualLinkageRejection(t *testing.T) { // rustc flags testRustError(t, ".* cannot be manually specified", ` rust_binary { name: "foo", srcs: [ "foo.rs", ], flags: ["-lbar"], } `) testRustError(t, ".* cannot be manually specified", ` rust_binary { name: "foo", srcs: [ "foo.rs", ], flags: ["--extern=foo"], } `) testRustError(t, ".* cannot be manually specified", ` rust_binary { name: "foo", srcs: [ "foo.rs", ], flags: ["-Clink-args=foo"], } `) testRustError(t, ".* cannot be manually specified", ` rust_binary { name: "foo", srcs: [ "foo.rs", ], flags: ["-C link-args=foo"], } `) testRustError(t, ".* cannot be manually specified", ` rust_binary { name: "foo", srcs: [ "foo.rs", ], flags: ["-L foo/"], } `) // lld flags testRustError(t, ".* cannot be manually specified", ` rust_binary { name: "foo", srcs: [ "foo.rs", ], ld_flags: ["-Wl,-L bar/"], } `) testRustError(t, ".* cannot be manually specified", ` rust_binary { name: "foo", srcs: [ "foo.rs", ], ld_flags: ["-Wl,-lbar"], } `) }