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 "io/ioutil" 19 "os" 20 "runtime" 21 "strings" 22 "testing" 23 24 "github.com/google/blueprint/proptools" 25 26 "android/soong/android" 27 "android/soong/cc" 28) 29 30var ( 31 buildDir string 32) 33 34func setUp() { 35 var err error 36 buildDir, err = ioutil.TempDir("", "soong_rust_test") 37 if err != nil { 38 panic(err) 39 } 40} 41 42func tearDown() { 43 os.RemoveAll(buildDir) 44} 45 46func TestMain(m *testing.M) { 47 run := func() int { 48 setUp() 49 defer tearDown() 50 51 return m.Run() 52 } 53 54 os.Exit(run()) 55} 56 57func testConfig(bp string) android.Config { 58 bp = bp + GatherRequiredDepsForTest() 59 60 fs := map[string][]byte{ 61 "foo.rs": nil, 62 "foo.c": nil, 63 "src/bar.rs": nil, 64 "src/any.h": nil, 65 "liby.so": nil, 66 "libz.so": nil, 67 } 68 69 cc.GatherRequiredFilesForTest(fs) 70 71 return android.TestArchConfig(buildDir, nil, bp, fs) 72} 73 74func testRust(t *testing.T, bp string) *android.TestContext { 75 return testRustContext(t, bp, false) 76} 77 78func testRustCov(t *testing.T, bp string) *android.TestContext { 79 return testRustContext(t, bp, true) 80} 81 82func testRustContext(t *testing.T, bp string, coverage bool) *android.TestContext { 83 // TODO (b/140435149) 84 if runtime.GOOS != "linux" { 85 t.Skip("Only the Linux toolchain is supported for Rust") 86 } 87 88 t.Helper() 89 config := testConfig(bp) 90 91 if coverage { 92 config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true) 93 config.TestProductVariables.Native_coverage = proptools.BoolPtr(true) 94 config.TestProductVariables.NativeCoveragePaths = []string{"*"} 95 } 96 97 ctx := CreateTestContext() 98 ctx.Register(config) 99 100 _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) 101 android.FailIfErrored(t, errs) 102 _, errs = ctx.PrepareBuildActions(config) 103 android.FailIfErrored(t, errs) 104 105 return ctx 106} 107 108func testRustError(t *testing.T, pattern string, bp string) { 109 // TODO (b/140435149) 110 if runtime.GOOS != "linux" { 111 t.Skip("Only the Linux toolchain is supported for Rust") 112 } 113 114 t.Helper() 115 config := testConfig(bp) 116 117 ctx := CreateTestContext() 118 ctx.Register(config) 119 120 _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) 121 if len(errs) > 0 { 122 android.FailIfNoMatchingErrors(t, pattern, errs) 123 return 124 } 125 126 _, errs = ctx.PrepareBuildActions(config) 127 if len(errs) > 0 { 128 android.FailIfNoMatchingErrors(t, pattern, errs) 129 return 130 } 131 132 t.Fatalf("missing expected error %q (0 errors are returned)", pattern) 133} 134 135// Test that we can extract the lib name from a lib path. 136func TestLibNameFromFilePath(t *testing.T) { 137 libBarPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so.so") 138 libLibPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/liblib.dylib.so") 139 140 libBarName := libNameFromFilePath(libBarPath) 141 libLibName := libNameFromFilePath(libLibPath) 142 143 expectedResult := "bar.so" 144 if libBarName != expectedResult { 145 t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libBarName) 146 } 147 148 expectedResult = "lib.dylib" 149 if libLibName != expectedResult { 150 t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libLibPath) 151 } 152} 153 154// Test that we can extract the link path from a lib path. 155func TestLinkPathFromFilePath(t *testing.T) { 156 barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so") 157 libName := linkPathFromFilePath(barPath) 158 expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/" 159 160 if libName != expectedResult { 161 t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName) 162 } 163} 164 165// Test to make sure dependencies are being picked up correctly. 166func TestDepsTracking(t *testing.T) { 167 ctx := testRust(t, ` 168 rust_ffi_host_static { 169 name: "libstatic", 170 srcs: ["foo.rs"], 171 crate_name: "static", 172 } 173 rust_ffi_host_shared { 174 name: "libshared", 175 srcs: ["foo.rs"], 176 crate_name: "shared", 177 } 178 rust_library_host_dylib { 179 name: "libdylib", 180 srcs: ["foo.rs"], 181 crate_name: "dylib", 182 } 183 rust_library_host_rlib { 184 name: "librlib", 185 srcs: ["foo.rs"], 186 crate_name: "rlib", 187 } 188 rust_proc_macro { 189 name: "libpm", 190 srcs: ["foo.rs"], 191 crate_name: "pm", 192 } 193 rust_binary_host { 194 name: "fizz-buzz", 195 dylibs: ["libdylib"], 196 rlibs: ["librlib"], 197 proc_macros: ["libpm"], 198 static_libs: ["libstatic"], 199 shared_libs: ["libshared"], 200 srcs: ["foo.rs"], 201 } 202 `) 203 module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module) 204 205 // Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up. 206 if !android.InList("libdylib", module.Properties.AndroidMkDylibs) { 207 t.Errorf("Dylib dependency not detected (dependency missing from AndroidMkDylibs)") 208 } 209 210 if !android.InList("librlib", module.Properties.AndroidMkRlibs) { 211 t.Errorf("Rlib dependency not detected (dependency missing from AndroidMkRlibs)") 212 } 213 214 if !android.InList("libpm", module.Properties.AndroidMkProcMacroLibs) { 215 t.Errorf("Proc_macro dependency not detected (dependency missing from AndroidMkProcMacroLibs)") 216 } 217 218 if !android.InList("libshared", module.Properties.AndroidMkSharedLibs) { 219 t.Errorf("Shared library dependency not detected (dependency missing from AndroidMkSharedLibs)") 220 } 221 222 if !android.InList("libstatic", module.Properties.AndroidMkStaticLibs) { 223 t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)") 224 } 225} 226 227func TestSourceProviderDeps(t *testing.T) { 228 ctx := testRust(t, ` 229 rust_binary { 230 name: "fizz-buzz-dep", 231 srcs: [ 232 "foo.rs", 233 ":my_generator", 234 ":libbindings", 235 ], 236 } 237 rust_proc_macro { 238 name: "libprocmacro", 239 srcs: [ 240 "foo.rs", 241 ":my_generator", 242 ":libbindings", 243 ], 244 crate_name: "procmacro", 245 } 246 rust_library { 247 name: "libfoo", 248 srcs: [ 249 "foo.rs", 250 ":my_generator", 251 ":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_bindgen { 262 name: "libbindings", 263 stem: "bindings", 264 host_supported: true, 265 wrapper_src: "src/any.h", 266 } 267 `) 268 269 libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib").Rule("rustc") 270 if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/bindings.rs") { 271 t.Errorf("rust_bindgen generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings()) 272 } 273 if !android.SuffixInList(libfoo.Implicits.Strings(), "/out/any.rs") { 274 t.Errorf("genrule generated source not included as implicit input for libfoo; Implicits %#v", libfoo.Implicits.Strings()) 275 } 276 277 fizzBuzz := ctx.ModuleForTests("fizz-buzz-dep", "android_arm64_armv8-a").Rule("rustc") 278 if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/bindings.rs") { 279 t.Errorf("rust_bindgen generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings()) 280 } 281 if !android.SuffixInList(fizzBuzz.Implicits.Strings(), "/out/any.rs") { 282 t.Errorf("genrule generated source not included as implicit input for fizz-buzz-dep; Implicits %#v", libfoo.Implicits.Strings()) 283 } 284 285 libprocmacro := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Rule("rustc") 286 if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/bindings.rs") { 287 t.Errorf("rust_bindgen generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings()) 288 } 289 if !android.SuffixInList(libprocmacro.Implicits.Strings(), "/out/any.rs") { 290 t.Errorf("genrule generated source not included as implicit input for libprocmacro; Implicits %#v", libfoo.Implicits.Strings()) 291 } 292} 293 294func TestSourceProviderTargetMismatch(t *testing.T) { 295 // This might error while building the dependency tree or when calling depsToPaths() depending on the lunched 296 // target, which results in two different errors. So don't check the error, just confirm there is one. 297 testRustError(t, ".*", ` 298 rust_proc_macro { 299 name: "libprocmacro", 300 srcs: [ 301 "foo.rs", 302 ":libbindings", 303 ], 304 crate_name: "procmacro", 305 } 306 rust_bindgen { 307 name: "libbindings", 308 stem: "bindings", 309 wrapper_src: "src/any.h", 310 } 311 `) 312} 313 314// Test to make sure proc_macros use host variants when building device modules. 315func TestProcMacroDeviceDeps(t *testing.T) { 316 ctx := testRust(t, ` 317 rust_library_host_rlib { 318 name: "libbar", 319 srcs: ["foo.rs"], 320 crate_name: "bar", 321 } 322 rust_proc_macro { 323 name: "libpm", 324 rlibs: ["libbar"], 325 srcs: ["foo.rs"], 326 crate_name: "pm", 327 } 328 rust_binary { 329 name: "fizz-buzz", 330 proc_macros: ["libpm"], 331 srcs: ["foo.rs"], 332 } 333 `) 334 rustc := ctx.ModuleForTests("libpm", "linux_glibc_x86_64").Rule("rustc") 335 336 if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") { 337 t.Errorf("Proc_macro is not using host variant of dependent modules.") 338 } 339} 340 341// Test that no_stdlibs suppresses dependencies on rust standard libraries 342func TestNoStdlibs(t *testing.T) { 343 ctx := testRust(t, ` 344 rust_binary { 345 name: "fizz-buzz", 346 srcs: ["foo.rs"], 347 no_stdlibs: true, 348 }`) 349 module := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Module().(*Module) 350 351 if android.InList("libstd", module.Properties.AndroidMkDylibs) { 352 t.Errorf("no_stdlibs did not suppress dependency on libstd") 353 } 354} 355 356// Test that libraries provide both 32-bit and 64-bit variants. 357func TestMultilib(t *testing.T) { 358 ctx := testRust(t, ` 359 rust_library_rlib { 360 name: "libfoo", 361 srcs: ["foo.rs"], 362 crate_name: "foo", 363 }`) 364 365 _ = ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib") 366 _ = ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_rlib") 367} 368