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 "strings" 19 "testing" 20 21 "android/soong/android" 22) 23 24// Test that variants are being generated correctly, and that crate-types are correct. 25func TestLibraryVariants(t *testing.T) { 26 27 ctx := testRust(t, ` 28 rust_library_host { 29 name: "libfoo", 30 srcs: ["foo.rs"], 31 crate_name: "foo", 32 } 33 rust_ffi_host { 34 name: "libfoo.ffi", 35 srcs: ["foo.rs"], 36 crate_name: "foo" 37 } 38 rust_ffi_host_static { 39 name: "libfoo.ffi_static", 40 srcs: ["foo.rs"], 41 crate_name: "foo" 42 }`) 43 44 // Test all variants are being built. 45 libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") 46 libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") 47 libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") 48 libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc") 49 50 rlibCrateType := "rlib" 51 dylibCrateType := "dylib" 52 sharedCrateType := "cdylib" 53 54 // Test crate type for rlib is correct. 55 if !strings.Contains(libfooRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) { 56 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooRlib.Args["rustcFlags"]) 57 } 58 59 // Test crate type for dylib is correct. 60 if !strings.Contains(libfooDylib.Args["rustcFlags"], "crate-type="+dylibCrateType) { 61 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", dylibCrateType, libfooDylib.Args["rustcFlags"]) 62 } 63 64 // Test crate type for FFI rlibs is correct 65 if !strings.Contains(libfooFFIRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) { 66 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooFFIRlib.Args["rustcFlags"]) 67 } 68 69 // Test crate type for C shared libraries is correct. 70 if !strings.Contains(libfooShared.Args["rustcFlags"], "crate-type="+sharedCrateType) { 71 t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.Args["rustcFlags"]) 72 } 73 74} 75 76// Test that dylibs are not statically linking the standard library. 77func TestDylibPreferDynamic(t *testing.T) { 78 ctx := testRust(t, ` 79 rust_library_host_dylib { 80 name: "libfoo", 81 srcs: ["foo.rs"], 82 crate_name: "foo", 83 }`) 84 85 libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") 86 87 if !strings.Contains(libfooDylib.Args["rustcFlags"], "prefer-dynamic") { 88 t.Errorf("missing prefer-dynamic flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"]) 89 } 90} 91 92// Check that we are passing the android_dylib config flag 93func TestAndroidDylib(t *testing.T) { 94 ctx := testRust(t, ` 95 rust_library_host_dylib { 96 name: "libfoo", 97 srcs: ["foo.rs"], 98 crate_name: "foo", 99 }`) 100 101 libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") 102 103 if !strings.Contains(libfooDylib.Args["rustcFlags"], "--cfg 'android_dylib'") { 104 t.Errorf("missing android_dylib cfg flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"]) 105 } 106} 107 108func TestValidateLibraryStem(t *testing.T) { 109 testRustError(t, "crate_name must be defined.", ` 110 rust_library_host { 111 name: "libfoo", 112 srcs: ["foo.rs"], 113 }`) 114 115 testRustError(t, "library crate_names must be alphanumeric with underscores allowed", ` 116 rust_library_host { 117 name: "libfoo-bar", 118 srcs: ["foo.rs"], 119 crate_name: "foo-bar" 120 }`) 121 122 testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", ` 123 rust_library_host { 124 name: "foobar", 125 srcs: ["foo.rs"], 126 crate_name: "foo_bar" 127 }`) 128 testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", ` 129 rust_library_host { 130 name: "foobar", 131 stem: "libfoo", 132 srcs: ["foo.rs"], 133 crate_name: "foo_bar" 134 }`) 135 testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", ` 136 rust_library_host { 137 name: "foobar", 138 stem: "foo_bar", 139 srcs: ["foo.rs"], 140 crate_name: "foo_bar" 141 }`) 142 143} 144 145func TestSharedLibrary(t *testing.T) { 146 ctx := testRust(t, ` 147 rust_ffi_shared { 148 name: "libfoo", 149 srcs: ["foo.rs"], 150 crate_name: "foo", 151 }`) 152 153 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared") 154 155 libfooOutput := libfoo.Rule("rustc") 156 if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") { 157 t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v", 158 libfooOutput.Args["linkFlags"]) 159 } 160 161 if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkDylibs) { 162 t.Errorf("Non-static libstd dylib expected to be a dependency of Rust shared libraries. Dylib deps are: %#v", 163 libfoo.Module().(*Module).Properties.AndroidMkDylibs) 164 } 165} 166 167func TestSharedLibraryToc(t *testing.T) { 168 ctx := testRust(t, ` 169 rust_ffi_shared { 170 name: "libfoo", 171 srcs: ["foo.rs"], 172 crate_name: "foo", 173 } 174 cc_binary { 175 name: "fizzbuzz", 176 shared_libs: ["libfoo"], 177 }`) 178 179 fizzbuzz := ctx.ModuleForTests("fizzbuzz", "android_arm64_armv8-a").Rule("ld") 180 181 if !android.SuffixInList(fizzbuzz.Implicits.Strings(), "libfoo.so.toc") { 182 t.Errorf("missing expected libfoo.so.toc implicit dependency, instead found: %#v", 183 fizzbuzz.Implicits.Strings()) 184 } 185} 186 187func TestStaticLibraryLinkage(t *testing.T) { 188 ctx := testRust(t, ` 189 rust_ffi_static { 190 name: "libfoo", 191 srcs: ["foo.rs"], 192 crate_name: "foo", 193 }`) 194 195 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std") 196 197 if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) { 198 t.Errorf("Static libstd rlib expected to be a dependency of Rust rlib libraries. Rlib deps are: %#v", 199 libfoo.Module().(*Module).Properties.AndroidMkDylibs) 200 } 201} 202 203func TestNativeDependencyOfRlib(t *testing.T) { 204 ctx := testRust(t, ` 205 rust_ffi_rlib { 206 name: "libffi_rlib", 207 crate_name: "ffi_rlib", 208 rlibs: ["librust_rlib"], 209 srcs: ["foo.rs"], 210 } 211 rust_library_rlib { 212 name: "librust_rlib", 213 crate_name: "rust_rlib", 214 srcs: ["foo.rs"], 215 shared_libs: ["libshared_cc_dep"], 216 static_libs: ["libstatic_cc_dep"], 217 } 218 cc_library_shared { 219 name: "libshared_cc_dep", 220 srcs: ["foo.cpp"], 221 } 222 cc_library_static { 223 name: "libstatic_cc_dep", 224 srcs: ["foo.cpp"], 225 } 226 `) 227 228 rustRlibRlibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_rlib-std") 229 rustRlibDylibStd := ctx.ModuleForTests("librust_rlib", "android_arm64_armv8-a_rlib_dylib-std") 230 ffiRlib := ctx.ModuleForTests("libffi_rlib", "android_arm64_armv8-a_rlib_rlib-std") 231 232 modules := []android.TestingModule{ 233 rustRlibRlibStd, 234 rustRlibDylibStd, 235 ffiRlib, 236 } 237 238 // librust_rlib specifies -L flag to cc deps output directory on rustc command 239 // and re-export the cc deps to rdep libffi_static 240 // When building rlib crate, rustc doesn't link the native libraries 241 // The build system assumes the cc deps will be at the final linkage (either a shared library or binary) 242 // Hence, these flags are no-op 243 // TODO: We could consider removing these flags 244 for _, module := range modules { 245 if !strings.Contains(module.Rule("rustc").Args["libFlags"], 246 "-L out/soong/.intermediates/libshared_cc_dep/android_arm64_armv8-a_shared/") { 247 t.Errorf( 248 "missing -L flag for libshared_cc_dep of %s, rustcFlags: %#v", 249 module.Module().Name(), rustRlibRlibStd.Rule("rustc").Args["libFlags"], 250 ) 251 } 252 if !strings.Contains(module.Rule("rustc").Args["libFlags"], 253 "-L out/soong/.intermediates/libstatic_cc_dep/android_arm64_armv8-a_static/") { 254 t.Errorf( 255 "missing -L flag for libstatic_cc_dep of %s, rustcFlags: %#v", 256 module.Module().Name(), rustRlibRlibStd.Rule("rustc").Args["libFlags"], 257 ) 258 } 259 } 260} 261 262// Test that variants pull in the right type of rustlib autodep 263func TestAutoDeps(t *testing.T) { 264 265 ctx := testRust(t, ` 266 rust_library_host { 267 name: "libbar", 268 srcs: ["bar.rs"], 269 crate_name: "bar", 270 } 271 rust_library_host_rlib { 272 name: "librlib_only", 273 srcs: ["bar.rs"], 274 crate_name: "rlib_only", 275 } 276 rust_library_host { 277 name: "libfoo", 278 srcs: ["foo.rs"], 279 crate_name: "foo", 280 rustlibs: [ 281 "libbar", 282 "librlib_only", 283 ], 284 } 285 rust_ffi_host { 286 name: "libfoo.ffi", 287 srcs: ["foo.rs"], 288 crate_name: "foo", 289 rustlibs: [ 290 "libbar", 291 "librlib_only", 292 ], 293 } 294 rust_ffi_host_static { 295 name: "libfoo.ffi.static", 296 srcs: ["foo.rs"], 297 crate_name: "foo", 298 rustlibs: [ 299 "libbar", 300 "librlib_only", 301 ], 302 }`) 303 304 libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std") 305 libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib") 306 libfooFFIRlib := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_rlib_rlib-std") 307 libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared") 308 309 for _, static := range []android.TestingModule{libfooRlib, libfooFFIRlib} { 310 if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) { 311 t.Errorf("libbar not present as rlib dependency in static lib: %s", static.Module().Name()) 312 } 313 if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) { 314 t.Errorf("libbar present as dynamic dependency in static lib: %s", static.Module().Name()) 315 } 316 } 317 318 for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} { 319 if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) { 320 t.Errorf("libbar not present as dynamic dependency in dynamic lib: %s", dyn.Module().Name()) 321 } 322 if android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkRlibs) { 323 t.Errorf("libbar present as rlib dependency in dynamic lib: %s", dyn.Module().Name()) 324 } 325 if !android.InList("librlib_only", dyn.Module().(*Module).Properties.AndroidMkRlibs) { 326 t.Errorf("librlib_only should be selected by rustlibs as an rlib: %s.", dyn.Module().Name()) 327 } 328 } 329} 330 331// Test that stripped versions are correctly generated and used. 332func TestStrippedLibrary(t *testing.T) { 333 ctx := testRust(t, ` 334 rust_library_dylib { 335 name: "libfoo", 336 crate_name: "foo", 337 srcs: ["foo.rs"], 338 } 339 rust_library_dylib { 340 name: "libbar", 341 crate_name: "bar", 342 srcs: ["foo.rs"], 343 strip: { 344 none: true 345 } 346 } 347 `) 348 349 foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib") 350 foo.Output("libfoo.dylib.so") 351 foo.Output("unstripped/libfoo.dylib.so") 352 // Check that the `cp` rule is using the stripped version as input. 353 cp := foo.Rule("android.Cp") 354 if strings.HasSuffix(cp.Input.String(), "unstripped/libfoo.dylib.so") { 355 t.Errorf("installed library not based on stripped version: %v", cp.Input) 356 } 357 358 fizzBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeOutput("unstripped/libbar.dylib.so") 359 if fizzBar.Rule != nil { 360 t.Errorf("unstripped library exists, so stripped library has incorrectly been generated") 361 } 362} 363 364func TestLibstdLinkage(t *testing.T) { 365 ctx := testRust(t, ` 366 rust_library { 367 name: "libfoo", 368 srcs: ["foo.rs"], 369 crate_name: "foo", 370 } 371 rust_ffi { 372 name: "libbar", 373 srcs: ["foo.rs"], 374 crate_name: "bar", 375 rustlibs: ["libfoo"], 376 } 377 rust_ffi_static { 378 name: "libbar_static", 379 srcs: ["foo.rs"], 380 crate_name: "bar", 381 rustlibs: ["libfoo"], 382 } 383 rust_ffi { 384 name: "libbar.prefer_rlib", 385 srcs: ["foo.rs"], 386 crate_name: "bar", 387 rustlibs: ["libfoo"], 388 prefer_rlib: true, 389 }`) 390 391 libfooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module) 392 libfooRlibStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module) 393 libfooRlibDynamic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module) 394 395 libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module) 396 libbarFFIRlib := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module) 397 398 // prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here. 399 libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module) 400 401 if !android.InList("libstd", libfooRlibStatic.Properties.AndroidMkRlibs) { 402 t.Errorf("rlib-std variant for device rust_library_rlib does not link libstd as an rlib") 403 } 404 if !android.InList("libstd", libfooRlibDynamic.Properties.AndroidMkDylibs) { 405 t.Errorf("dylib-std variant for device rust_library_rlib does not link libstd as an dylib") 406 } 407 if !android.InList("libstd", libfooDylib.Properties.AndroidMkDylibs) { 408 t.Errorf("Device rust_library_dylib does not link libstd as an dylib") 409 } 410 411 if !android.InList("libstd", libbarShared.Properties.AndroidMkDylibs) { 412 t.Errorf("Device rust_ffi_shared does not link libstd as an dylib") 413 } 414 if !android.InList("libstd", libbarFFIRlib.Properties.AndroidMkRlibs) { 415 t.Errorf("Device rust_ffi_rlib does not link libstd as an rlib") 416 } 417 if !android.InList("libfoo.rlib-std", libbarFFIRlib.Properties.AndroidMkRlibs) { 418 t.Errorf("Device rust_ffi_rlib does not link dependent rustlib rlib-std variant") 419 } 420 if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) { 421 t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib") 422 } 423 424} 425 426func TestRustFFIExportedIncludes(t *testing.T) { 427 ctx := testRust(t, ` 428 rust_ffi { 429 name: "libbar", 430 srcs: ["foo.rs"], 431 crate_name: "bar", 432 export_include_dirs: ["rust_includes"], 433 host_supported: true, 434 } 435 cc_library_static { 436 name: "libfoo", 437 srcs: ["foo.cpp"], 438 shared_libs: ["libbar"], 439 host_supported: true, 440 }`) 441 libfooStatic := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_static").Rule("cc") 442 android.AssertStringDoesContain(t, "cFlags for lib module", libfooStatic.Args["cFlags"], " -Irust_includes ") 443} 444