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 39 // Test all variants are being built. 40 libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc") 41 libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") 42 libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Rule("rustc") 43 libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Rule("rustc") 44 45 rlibCrateType := "rlib" 46 dylibCrateType := "dylib" 47 sharedCrateType := "cdylib" 48 staticCrateType := "static" 49 50 // Test crate type for rlib is correct. 51 if !strings.Contains(libfooRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) { 52 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooRlib.Args["rustcFlags"]) 53 } 54 55 // Test crate type for dylib is correct. 56 if !strings.Contains(libfooDylib.Args["rustcFlags"], "crate-type="+dylibCrateType) { 57 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", dylibCrateType, libfooDylib.Args["rustcFlags"]) 58 } 59 60 // Test crate type for C static libraries is correct. 61 if !strings.Contains(libfooStatic.Args["rustcFlags"], "crate-type="+staticCrateType) { 62 t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.Args["rustcFlags"]) 63 } 64 65 // Test crate type for C shared libraries is correct. 66 if !strings.Contains(libfooShared.Args["rustcFlags"], "crate-type="+sharedCrateType) { 67 t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.Args["rustcFlags"]) 68 } 69 70} 71 72// Test that dylibs are not statically linking the standard library. 73func TestDylibPreferDynamic(t *testing.T) { 74 ctx := testRust(t, ` 75 rust_library_host_dylib { 76 name: "libfoo", 77 srcs: ["foo.rs"], 78 crate_name: "foo", 79 }`) 80 81 libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") 82 83 if !strings.Contains(libfooDylib.Args["rustcFlags"], "prefer-dynamic") { 84 t.Errorf("missing prefer-dynamic flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"]) 85 } 86} 87 88// Check that we are passing the android_dylib config flag 89func TestAndroidDylib(t *testing.T) { 90 ctx := testRust(t, ` 91 rust_library_host_dylib { 92 name: "libfoo", 93 srcs: ["foo.rs"], 94 crate_name: "foo", 95 }`) 96 97 libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc") 98 99 if !strings.Contains(libfooDylib.Args["rustcFlags"], "--cfg 'android_dylib'") { 100 t.Errorf("missing android_dylib cfg flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"]) 101 } 102} 103 104func TestValidateLibraryStem(t *testing.T) { 105 testRustError(t, "crate_name must be defined.", ` 106 rust_library_host { 107 name: "libfoo", 108 srcs: ["foo.rs"], 109 }`) 110 111 testRustError(t, "library crate_names must be alphanumeric with underscores allowed", ` 112 rust_library_host { 113 name: "libfoo-bar", 114 srcs: ["foo.rs"], 115 crate_name: "foo-bar" 116 }`) 117 118 testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", ` 119 rust_library_host { 120 name: "foobar", 121 srcs: ["foo.rs"], 122 crate_name: "foo_bar" 123 }`) 124 testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", ` 125 rust_library_host { 126 name: "foobar", 127 stem: "libfoo", 128 srcs: ["foo.rs"], 129 crate_name: "foo_bar" 130 }`) 131 testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", ` 132 rust_library_host { 133 name: "foobar", 134 stem: "foo_bar", 135 srcs: ["foo.rs"], 136 crate_name: "foo_bar" 137 }`) 138 139} 140 141func TestSharedLibrary(t *testing.T) { 142 ctx := testRust(t, ` 143 rust_ffi_shared { 144 name: "libfoo", 145 srcs: ["foo.rs"], 146 crate_name: "foo", 147 }`) 148 149 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared") 150 151 libfooOutput := libfoo.Rule("rustc") 152 if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") { 153 t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v", 154 libfooOutput.Args["linkFlags"]) 155 } 156 157 if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkDylibs) { 158 t.Errorf("Non-static libstd dylib expected to be a dependency of Rust shared libraries. Dylib deps are: %#v", 159 libfoo.Module().(*Module).Properties.AndroidMkDylibs) 160 } 161} 162 163func TestSharedLibraryToc(t *testing.T) { 164 ctx := testRust(t, ` 165 rust_ffi_shared { 166 name: "libfoo", 167 srcs: ["foo.rs"], 168 crate_name: "foo", 169 } 170 cc_binary { 171 name: "fizzbuzz", 172 shared_libs: ["libfoo"], 173 }`) 174 175 fizzbuzz := ctx.ModuleForTests("fizzbuzz", "android_arm64_armv8-a").Rule("ld") 176 177 if !android.SuffixInList(fizzbuzz.Implicits.Strings(), "libfoo.so.toc") { 178 t.Errorf("missing expected libfoo.so.toc implicit dependency, instead found: %#v", 179 fizzbuzz.Implicits.Strings()) 180 } 181} 182 183func TestStaticLibraryLinkage(t *testing.T) { 184 ctx := testRust(t, ` 185 rust_ffi_static { 186 name: "libfoo", 187 srcs: ["foo.rs"], 188 crate_name: "foo", 189 }`) 190 191 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static") 192 193 if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) { 194 t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v", 195 libfoo.Module().(*Module).Properties.AndroidMkDylibs) 196 } 197} 198 199// Test that variants pull in the right type of rustlib autodep 200func TestAutoDeps(t *testing.T) { 201 202 ctx := testRust(t, ` 203 rust_library_host { 204 name: "libbar", 205 srcs: ["bar.rs"], 206 crate_name: "bar", 207 } 208 rust_library_host_rlib { 209 name: "librlib_only", 210 srcs: ["bar.rs"], 211 crate_name: "rlib_only", 212 } 213 rust_library_host { 214 name: "libfoo", 215 srcs: ["foo.rs"], 216 crate_name: "foo", 217 rustlibs: [ 218 "libbar", 219 "librlib_only", 220 ], 221 } 222 rust_ffi_host { 223 name: "libfoo.ffi", 224 srcs: ["foo.rs"], 225 crate_name: "foo", 226 rustlibs: [ 227 "libbar", 228 "librlib_only", 229 ], 230 }`) 231 232 libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std") 233 libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib") 234 libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static") 235 libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared") 236 237 for _, static := range []android.TestingModule{libfooRlib, libfooStatic} { 238 if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) { 239 t.Errorf("libbar not present as rlib dependency in static lib") 240 } 241 if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) { 242 t.Errorf("libbar present as dynamic dependency in static lib") 243 } 244 } 245 246 for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} { 247 if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) { 248 t.Errorf("libbar not present as dynamic dependency in dynamic lib") 249 } 250 if android.InList("libbar.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) { 251 t.Errorf("libbar present as rlib dependency in dynamic lib") 252 } 253 if !android.InList("librlib_only.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) { 254 t.Errorf("librlib_only should be selected by rustlibs as an rlib.") 255 } 256 } 257} 258 259// Test that stripped versions are correctly generated and used. 260func TestStrippedLibrary(t *testing.T) { 261 ctx := testRust(t, ` 262 rust_library_dylib { 263 name: "libfoo", 264 crate_name: "foo", 265 srcs: ["foo.rs"], 266 } 267 rust_library_dylib { 268 name: "libbar", 269 crate_name: "bar", 270 srcs: ["foo.rs"], 271 strip: { 272 none: true 273 } 274 } 275 `) 276 277 foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib") 278 foo.Output("libfoo.dylib.so") 279 foo.Output("unstripped/libfoo.dylib.so") 280 // Check that the `cp` rule is using the stripped version as input. 281 cp := foo.Rule("android.Cp") 282 if strings.HasSuffix(cp.Input.String(), "unstripped/libfoo.dylib.so") { 283 t.Errorf("installed library not based on stripped version: %v", cp.Input) 284 } 285 286 fizzBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeOutput("unstripped/libbar.dylib.so") 287 if fizzBar.Rule != nil { 288 t.Errorf("unstripped library exists, so stripped library has incorrectly been generated") 289 } 290} 291 292func TestLibstdLinkage(t *testing.T) { 293 ctx := testRust(t, ` 294 rust_library { 295 name: "libfoo", 296 srcs: ["foo.rs"], 297 crate_name: "foo", 298 } 299 rust_ffi { 300 name: "libbar", 301 srcs: ["foo.rs"], 302 crate_name: "bar", 303 rustlibs: ["libfoo"], 304 } 305 rust_ffi { 306 name: "libbar.prefer_rlib", 307 srcs: ["foo.rs"], 308 crate_name: "bar", 309 rustlibs: ["libfoo"], 310 prefer_rlib: true, 311 }`) 312 313 libfooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module) 314 libfooRlibStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module) 315 libfooRlibDynamic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module) 316 317 libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module) 318 libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module) 319 320 // prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here. 321 libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module) 322 323 if !android.InList("libstd", libfooRlibStatic.Properties.AndroidMkRlibs) { 324 t.Errorf("rlib-std variant for device rust_library_rlib does not link libstd as an rlib") 325 } 326 if !android.InList("libstd", libfooRlibDynamic.Properties.AndroidMkDylibs) { 327 t.Errorf("dylib-std variant for device rust_library_rlib does not link libstd as an dylib") 328 } 329 if !android.InList("libstd", libfooDylib.Properties.AndroidMkDylibs) { 330 t.Errorf("Device rust_library_dylib does not link libstd as an dylib") 331 } 332 333 if !android.InList("libstd", libbarShared.Properties.AndroidMkDylibs) { 334 t.Errorf("Device rust_ffi_shared does not link libstd as an dylib") 335 } 336 if !android.InList("libstd", libbarStatic.Properties.AndroidMkRlibs) { 337 t.Errorf("Device rust_ffi_static does not link libstd as an rlib") 338 } 339 if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) { 340 t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant") 341 } 342 if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) { 343 t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib") 344 } 345 346} 347