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