1// Copyright 2019 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package rust 16 17import ( 18 "os" 19 "runtime" 20 "strings" 21 "testing" 22 23 "github.com/google/blueprint/proptools" 24 25 "android/soong/android" 26 "android/soong/genrule" 27) 28 29func TestMain(m *testing.M) { 30 os.Exit(m.Run()) 31} 32 33var prepareForRustTest = android.GroupFixturePreparers( 34 android.PrepareForTestWithArchMutator, 35 android.PrepareForTestWithDefaults, 36 android.PrepareForTestWithPrebuilts, 37 38 genrule.PrepareForTestWithGenRuleBuildComponents, 39 40 PrepareForIntegrationTestWithRust, 41) 42 43var rustMockedFiles = android.MockFS{ 44 "foo.rs": nil, 45 "foo.c": nil, 46 "src/bar.rs": nil, 47 "src/any.h": nil, 48 "c_includes/c_header.h": nil, 49 "rust_includes/rust_headers.h": nil, 50 "proto.proto": nil, 51 "proto/buf.proto": nil, 52 "buf.proto": nil, 53 "foo.proto": nil, 54 "liby.so": nil, 55 "libz.so": nil, 56 "data.txt": nil, 57 "liblog.map.txt": nil, 58} 59 60// testRust returns a TestContext in which a basic environment has been setup. 61// This environment contains a few mocked files. See rustMockedFiles for the list of these files. 62func testRust(t *testing.T, bp string) *android.TestContext { 63 skipTestIfOsNotSupported(t) 64 result := android.GroupFixturePreparers( 65 prepareForRustTest, 66 rustMockedFiles.AddToFixture(), 67 ). 68 RunTestWithBp(t, bp) 69 return result.TestContext 70} 71 72const ( 73 sharedVendorVariant = "android_vendor_arm64_armv8-a_shared" 74 rlibVendorVariant = "android_vendor_arm64_armv8-a_rlib_rlib-std" 75 rlibDylibStdVendorVariant = "android_vendor_arm64_armv8-a_rlib_rlib-std" 76 dylibVendorVariant = "android_vendor_arm64_armv8-a_dylib" 77 sharedRecoveryVariant = "android_recovery_arm64_armv8-a_shared" 78 rlibRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_dylib-std" 79 rlibRlibStdRecoveryVariant = "android_recovery_arm64_armv8-a_rlib_rlib-std" 80 dylibRecoveryVariant = "android_recovery_arm64_armv8-a_dylib" 81 binaryCoreVariant = "android_arm64_armv8-a" 82 binaryVendorVariant = "android_vendor_arm64_armv8-a" 83 binaryProductVariant = "android_product_arm64_armv8-a" 84 binaryRecoveryVariant = "android_recovery_arm64_armv8-a" 85) 86 87// testRustCov returns a TestContext in which a basic environment has been 88// setup. This environment explicitly enables coverage. 89func testRustCov(t *testing.T, bp string) *android.TestContext { 90 skipTestIfOsNotSupported(t) 91 result := android.GroupFixturePreparers( 92 prepareForRustTest, 93 rustMockedFiles.AddToFixture(), 94 android.FixtureModifyProductVariables( 95 func(variables android.FixtureProductVariables) { 96 variables.ClangCoverage = proptools.BoolPtr(true) 97 variables.Native_coverage = proptools.BoolPtr(true) 98 variables.NativeCoveragePaths = []string{"*"} 99 }, 100 ), 101 ).RunTestWithBp(t, bp) 102 return result.TestContext 103} 104 105// testRustError ensures that at least one error was raised and its value 106// matches the pattern provided. The error can be either in the parsing of the 107// Blueprint or when generating the build actions. 108func testRustError(t *testing.T, pattern string, bp string) { 109 skipTestIfOsNotSupported(t) 110 android.GroupFixturePreparers( 111 prepareForRustTest, 112 rustMockedFiles.AddToFixture(), 113 ). 114 ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)). 115 RunTestWithBp(t, bp) 116} 117 118// testRustCtx is used to build a particular test environment. Unless your 119// tests requires a specific setup, prefer the wrapping functions: testRust, 120// testRustCov or testRustError. 121type testRustCtx struct { 122 bp string 123 fs map[string][]byte 124 env map[string]string 125 config *android.Config 126} 127 128func skipTestIfOsNotSupported(t *testing.T) { 129 // TODO (b/140435149) 130 if runtime.GOOS != "linux" { 131 t.Skip("Rust Soong tests can only be run on Linux hosts currently") 132 } 133} 134 135// Test that we can extract the link path from a lib path. 136func TestLinkPathFromFilePath(t *testing.T) { 137 barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so") 138 libName := linkPathFromFilePath(barPath) 139 expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/" 140 141 if libName != expectedResult { 142 t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName) 143 } 144} 145 146// Test to make sure dependencies are being picked up correctly. 147func TestDepsTracking(t *testing.T) { 148 ctx := testRust(t, ` 149 cc_library { 150 host_supported: true, 151 name: "cc_stubs_dep", 152 } 153 cc_library_host_static { 154 name: "libstatic", 155 } 156 cc_library_host_static { 157 name: "libwholestatic", 158 } 159 rust_ffi_host_shared { 160 name: "libshared", 161 srcs: ["foo.rs"], 162 crate_name: "shared", 163 } 164 rust_library_host_rlib { 165 name: "librlib", 166 srcs: ["foo.rs"], 167 crate_name: "rlib", 168 static_libs: ["libstatic"], 169 whole_static_libs: ["libwholestatic"], 170 shared_libs: ["cc_stubs_dep"], 171 } 172 rust_proc_macro { 173 name: "libpm", 174 srcs: ["foo.rs"], 175 crate_name: "pm", 176 } 177 rust_binary_host { 178 name: "fizz-buzz", 179 rlibs: ["librlib"], 180 proc_macros: ["libpm"], 181 static_libs: ["libstatic"], 182 shared_libs: ["libshared"], 183 srcs: ["foo.rs"], 184 } 185 `) 186 module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module) 187 rustc := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") 188 189 // Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up. 190 if !android.InList("librlib.rlib-std", module.Properties.AndroidMkRlibs) { 191 t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)") 192 } 193 194 if !android.InList("libpm", module.Properties.AndroidMkProcMacroLibs) { 195 t.Errorf("Proc_macro dependency not detected (dependency missing from AndroidMkProcMacroLibs)") 196 } 197 198 if !android.InList("libshared", module.transitiveAndroidMkSharedLibs.ToList()) { 199 t.Errorf("Shared library dependency not detected (dependency missing from AndroidMkSharedLibs)") 200 } 201 202 if !android.InList("libstatic", module.Properties.AndroidMkStaticLibs) { 203 t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)") 204 } 205 206 if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=wholestatic") { 207 t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"]) 208 } 209 210 if !strings.Contains(rustc.Args["linkFlags"], "cc_stubs_dep.so") { 211 t.Errorf("shared cc_library not being passed to rustc linkFlags %#v", rustc.Args["linkFlags"]) 212 } 213 214 if !android.SuffixInList(rustc.OrderOnly.Strings(), "cc_stubs_dep.so") { 215 t.Errorf("shared cc dep not being passed as order-only to rustc %#v", rustc.OrderOnly.Strings()) 216 } 217 218 if !android.SuffixInList(rustc.Implicits.Strings(), "cc_stubs_dep.so.toc") { 219 t.Errorf("shared cc dep TOC not being passed as implicit to rustc %#v", rustc.Implicits.Strings()) 220 } 221} 222 223func TestSourceProviderDeps(t *testing.T) { 224 ctx := testRust(t, ` 225 rust_binary { 226 name: "fizz-buzz-dep", 227 srcs: [ 228 "foo.rs", 229 ":my_generator", 230 ":libbindings", 231 ], 232 rlibs: ["libbindings"], 233 } 234 rust_proc_macro { 235 name: "libprocmacro", 236 srcs: [ 237 "foo.rs", 238 ":my_generator", 239 ":libbindings", 240 ], 241 rlibs: ["libbindings"], 242 crate_name: "procmacro", 243 } 244 rust_library { 245 name: "libfoo", 246 srcs: [ 247 "foo.rs", 248 ":my_generator", 249 ":libbindings", 250 ], 251 rlibs: ["libbindings"], 252 crate_name: "foo", 253 } 254 genrule { 255 name: "my_generator", 256 tools: ["any_rust_binary"], 257 cmd: "$(location) -o $(out) $(in)", 258 srcs: ["src/any.h"], 259 out: ["src/any.rs"], 260 } 261 rust_binary_host { 262 name: "any_rust_binary", 263 srcs: [ 264 "foo.rs", 265 ], 266 } 267 rust_bindgen { 268 name: "libbindings", 269 crate_name: "bindings", 270 source_stem: "bindings", 271 host_supported: true, 272 wrapper_src: "src/any.h", 273 } 274 `) 275 276 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc") 277 if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") { 278 t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings()) 279 } 280 if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/any.rs") { 281 t.Errorf("genrule generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings()) 282 } 283 284 fizzBuzz := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Rule("rustc") 285 if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/bindings.rs") { 286 t.Errorf("rust_bindgen generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings()) 287 } 288 if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/any.rs") { 289 t.Errorf("genrule generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings()) 290 } 291 292 libprocmacro := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Rule("rustc") 293 if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/bindings.rs") { 294 t.Errorf("rust_bindgen generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings()) 295 } 296 if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/any.rs") { 297 t.Errorf("genrule generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings()) 298 } 299 300 // Check that our bindings are picked up as crate dependencies as well 301 libfooMod := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module) 302 if !android.InList("libbindings", libfooMod.Properties.AndroidMkRlibs) { 303 t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)") 304 } 305 fizzBuzzMod := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Module().(*Module) 306 if !android.InList("libbindings", fizzBuzzMod.Properties.AndroidMkRlibs) { 307 t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)") 308 } 309 libprocmacroMod := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Module().(*Module) 310 if !android.InList("libbindings.rlib-std", libprocmacroMod.Properties.AndroidMkRlibs) { 311 t.Errorf("bindgen dependency not detected as a rlib dependency (dependency missing from AndroidMkRlibs)") 312 } 313} 314 315func TestSourceProviderTargetMismatch(t *testing.T) { 316 // This might error while building the dependency tree or when calling depsToPaths() depending on the lunched 317 // target, which results in two different errors. So don't check the error, just confirm there is one. 318 testRustError(t, ".*", ` 319 rust_proc_macro { 320 name: "libprocmacro", 321 srcs: [ 322 "foo.rs", 323 ":libbindings", 324 ], 325 crate_name: "procmacro", 326 } 327 rust_bindgen { 328 name: "libbindings", 329 crate_name: "bindings", 330 source_stem: "bindings", 331 wrapper_src: "src/any.h", 332 } 333 `) 334} 335 336// Test to make sure proc_macros use host variants when building device modules. 337func TestProcMacroDeviceDeps(t *testing.T) { 338 ctx := testRust(t, ` 339 rust_library_host_rlib { 340 name: "libbar", 341 srcs: ["foo.rs"], 342 crate_name: "bar", 343 } 344 rust_proc_macro { 345 name: "libpm", 346 rlibs: ["libbar"], 347 srcs: ["foo.rs"], 348 crate_name: "pm", 349 } 350 rust_binary { 351 name: "fizz-buzz", 352 proc_macros: ["libpm"], 353 srcs: ["foo.rs"], 354 } 355 `) 356 rustc := ctx.ModuleForTests("libpm", "linux_glibc_x86_64").Rule("rustc") 357 358 if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") { 359 t.Errorf("Proc_macro is not using host variant of dependent modules.") 360 } 361} 362 363// Test that no_stdlibs suppresses dependencies on rust standard libraries 364func TestNoStdlibs(t *testing.T) { 365 ctx := testRust(t, ` 366 rust_binary { 367 name: "fizz-buzz", 368 srcs: ["foo.rs"], 369 no_stdlibs: true, 370 }`) 371 module := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module) 372 373 if android.InList("libstd", module.Properties.AndroidMkDylibs) { 374 t.Errorf("no_stdlibs did not suppress dependency on libstd") 375 } 376} 377 378// Test that libraries provide both 32-bit and 64-bit variants. 379func TestMultilib(t *testing.T) { 380 ctx := testRust(t, ` 381 rust_library_rlib { 382 name: "libfoo", 383 srcs: ["foo.rs"], 384 crate_name: "foo", 385 }`) 386 387 _ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std") 388 _ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib_dylib-std") 389} 390 391// Test that library size measurements are generated. 392func TestLibrarySizes(t *testing.T) { 393 ctx := testRust(t, ` 394 rust_library_dylib { 395 name: "libwaldo", 396 srcs: ["foo.rs"], 397 crate_name: "waldo", 398 }`) 399 400 m := ctx.SingletonForTests("file_metrics") 401 m.Output("unstripped/libwaldo.dylib.so.bloaty.csv") 402 m.Output("libwaldo.dylib.so.bloaty.csv") 403} 404 405// Test that aliases are respected. 406func TestRustAliases(t *testing.T) { 407 ctx := testRust(t, ` 408 rust_library { 409 name: "libbar", 410 crate_name: "bar", 411 srcs: ["src/lib.rs"], 412 } 413 rust_library { 414 name: "libbaz", 415 crate_name: "baz", 416 srcs: ["src/lib.rs"], 417 } 418 rust_binary { 419 name: "foo", 420 srcs: ["src/main.rs"], 421 rustlibs: ["libbar", "libbaz"], 422 aliases: ["bar:bar_renamed"], 423 }`) 424 425 fooRustc := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc") 426 if !strings.Contains(fooRustc.Args["libFlags"], "--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") { 427 t.Errorf("--extern bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"]) 428 } 429 if !strings.Contains(fooRustc.Args["libFlags"], "--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") { 430 t.Errorf("--extern baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so flag not being passed to rustc for rust_binary with aliases. libFlags: %#v", fooRustc.Args["libFlags"]) 431 } 432} 433 434func TestRustRlibs(t *testing.T) { 435 ctx := testRust(t, ` 436 rust_ffi_rlib { 437 name: "libbar", 438 crate_name: "bar", 439 srcs: ["src/lib.rs"], 440 export_include_dirs: ["bar_includes"] 441 } 442 443 rust_ffi_rlib { 444 name: "libfoo", 445 crate_name: "foo", 446 srcs: ["src/lib.rs"], 447 export_include_dirs: ["foo_includes"] 448 } 449 450 cc_library_shared { 451 name: "libcc_shared", 452 srcs:["foo.c"], 453 static_rlibs: ["libbar"], 454 } 455 456 cc_library_static { 457 name: "libcc_static", 458 srcs:["foo.c"], 459 static_rlibs: ["libfoo"], 460 } 461 462 cc_binary { 463 name: "ccBin", 464 srcs:["foo.c"], 465 static_rlibs: ["libbar"], 466 static_libs: ["libcc_static"], 467 } 468 `) 469 470 libbar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc") 471 libcc_shared_rustc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc") 472 libcc_shared_ld := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("ld") 473 libcc_shared_cc := ctx.ModuleForTests("libcc_shared", "android_arm64_armv8-a_shared").Rule("cc") 474 ccbin_rustc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("rustc") 475 ccbin_ld := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("ld") 476 ccbin_cc := ctx.ModuleForTests("ccBin", "android_arm64_armv8-a").Rule("cc") 477 478 if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") { 479 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"]) 480 } 481 482 // Make sure there's a rustc command, and it's producing a staticlib 483 if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") { 484 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", 485 "staticlib", libcc_shared_rustc.Args["rustcFlags"]) 486 } 487 488 // Make sure the static lib is included in the ld command 489 if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/liblibcc_shared_rust_staticlib.a") { 490 t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v", 491 "libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"]) 492 } 493 494 // Make sure the static lib includes are in the cc command 495 if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") { 496 t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", 497 "-Ibar_includes", libcc_shared_cc.Args["cFlags"]) 498 } 499 500 // Make sure there's a rustc command, and it's producing a staticlib 501 if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") { 502 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"]) 503 } 504 505 // Make sure the static lib is included in the cc command 506 if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/libccBin_rust_staticlib.a") { 507 t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v", 508 "ccBin.generated_rust_staticlib.a", ccbin_ld.Args["libFlags"]) 509 } 510 511 // Make sure the static lib includes are in the ld command 512 if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") { 513 t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", 514 "-Ibar_includes", ccbin_cc.Args) 515 } 516 517 // Make sure that direct dependencies and indirect dependencies are 518 // propagating correctly to the generated rlib. 519 if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") { 520 t.Errorf("Missing indirect dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"]) 521 } 522 if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") { 523 t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"]) 524 } 525 526 // Test indirect includes propagation 527 if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") { 528 t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v", 529 "-Ifoo_includes", ccbin_cc.Args) 530 } 531} 532 533func assertString(t *testing.T, got, expected string) { 534 t.Helper() 535 if got != expected { 536 t.Errorf("expected %q got %q", expected, got) 537 } 538} 539