• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(t, "fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
188	rustc := ctx.ModuleForTests(t, "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:+whole-archive=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(t, "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(t, "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(t, "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(t, "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(t, "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(t, "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(t, "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(t, "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(t, "libfoo", "android_arm64_armv8-a_rlib_dylib-std")
389	_ = ctx.ModuleForTests(t, "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(t, "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(t, "foo", "android_arm64_armv8-a").Rule("rustc")
427	if !strings.Contains(fooRustc.Args["libFlags"], "--extern force:bar_renamed=out/soong/.intermediates/libbar/android_arm64_armv8-a_dylib/unstripped/libbar.dylib.so") {
428		t.Errorf("--extern force: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 force:baz=out/soong/.intermediates/libbaz/android_arm64_armv8-a_dylib/unstripped/libbaz.dylib.so") {
431		t.Errorf("--extern force: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 TestRustFFIRlibs(t *testing.T) {
436	ctx := testRust(t, `
437		rust_ffi_static {
438			name: "libbar",
439			crate_name: "bar",
440			srcs: ["src/lib.rs"],
441			export_include_dirs: ["bar_includes"]
442		}
443
444		rust_ffi_static {
445			name: "libfoo",
446			crate_name: "foo",
447			srcs: ["src/lib.rs"],
448			export_include_dirs: ["foo_includes"]
449		}
450
451		rust_ffi_static {
452			name: "libfoo_from_rlib",
453			crate_name: "foo_from_rlib",
454			srcs: ["src/lib.rs"],
455			export_include_dirs: ["foo_includes"]
456		}
457
458		rust_ffi_static {
459			name: "libfoo_from_rlib_whole",
460			crate_name: "foo_from_rlib_whole",
461			srcs: ["src/lib.rs"],
462			export_include_dirs: ["foo_includes"]
463		}
464
465		rust_ffi_static {
466			name: "libbuzz",
467			crate_name: "buzz",
468			srcs: ["src/lib.rs"],
469			export_include_dirs: ["buzz_includes"]
470		}
471
472		rust_ffi_static {
473			name: "libbuzz_from_rlib",
474			crate_name: "buzz_from_rlib",
475			srcs: ["src/lib.rs"],
476			export_include_dirs: ["buzz_includes"]
477		}
478
479		rust_ffi_static {
480			name: "libbuzz_from_rlib_whole",
481			crate_name: "buzz_from_rlib_whole",
482			srcs: ["src/lib.rs"],
483			export_include_dirs: ["buzz_includes"]
484		}
485
486		cc_library_shared {
487			name: "libcc_shared",
488			srcs:["foo.c"],
489			static_libs: ["libbar"],
490		}
491
492		cc_library_static {
493			name: "libcc_static",
494			srcs:["foo.c"],
495			static_libs: ["libbuzz"],
496			whole_static_libs: ["libfoo"],
497		}
498
499		cc_library_static {
500			name: "libcc_static_from_rlib",
501			srcs:["foo.c"],
502			static_libs: ["libbuzz_from_rlib"],
503			whole_static_libs: ["libfoo_from_rlib"],
504		}
505
506		cc_library_static {
507			name: "libcc_whole_static_from_rlib",
508			srcs:["foo.c"],
509			static_libs: ["libbuzz_from_rlib_whole"],
510			whole_static_libs: ["libfoo_from_rlib_whole"],
511		}
512
513		cc_binary {
514			name: "ccBin",
515			srcs:["foo.c"],
516			static_libs: ["libcc_static", "libbar"],
517		}
518
519		rust_library {
520			name: "librs",
521			srcs:["src/foo.rs"],
522			crate_name: "rs",
523			static_libs: ["libcc_static_from_rlib"],
524			whole_static_libs: ["libcc_whole_static_from_rlib"],
525		}
526
527		rust_library {
528			name: "librs2",
529			srcs:["src/foo.rs"],
530			crate_name: "rs",
531			rustlibs: ["librs"],
532		}
533
534		rust_binary {
535			name: "rsBin",
536			srcs:["src/foo.rs"],
537			crate_name: "rsBin",
538			rlibs: ["librs", "libbar"],
539			static_libs: ["libcc_static"],
540		}
541	`)
542
543	libbar := ctx.ModuleForTests(t, "libbar", "android_arm64_armv8-a_rlib_rlib-std").Rule("rustc")
544	libcc_shared_rustc := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("rustc")
545	libcc_shared_ld := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("ld")
546	libcc_shared_cc := ctx.ModuleForTests(t, "libcc_shared", "android_arm64_armv8-a_shared").Rule("cc")
547	ccbin_rustc := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("rustc")
548	ccbin_ld := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("ld")
549	ccbin_cc := ctx.ModuleForTests(t, "ccBin", "android_arm64_armv8-a").Rule("cc")
550	rustbin_genlib := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("generated_rust_staticlib/librustlibs.a")
551	rustbin := ctx.ModuleForTests(t, "rsBin", "android_arm64_armv8-a").Output("unstripped/rsBin")
552	librs_rlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
553	librs2_rlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_rlib_dylib-std").MaybeOutput("generated_rust_staticlib/librustlibs.a")
554	librs_genlib := ctx.ModuleForTests(t, "librs", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a")
555	librs2_genlib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("generated_rust_staticlib/librustlibs.a")
556	librs2_dylib := ctx.ModuleForTests(t, "librs2", "android_arm64_armv8-a_dylib").Output("unstripped/librs2.dylib.so")
557
558	if !strings.Contains(libbar.Args["rustcFlags"], "crate-type=rlib") {
559		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "rlib", libbar.Args["rustcFlags"])
560	}
561
562	// Make sure there's a rustc command, and it's producing a staticlib
563	if !strings.Contains(libcc_shared_rustc.Args["rustcFlags"], "crate-type=staticlib") {
564		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v",
565			"staticlib", libcc_shared_rustc.Args["rustcFlags"])
566	}
567
568	// Make sure the static lib is included in the ld command
569	if !strings.Contains(libcc_shared_ld.Args["libFlags"], "generated_rust_staticlib/librustlibs.a") {
570		t.Errorf("missing generated static library in linker step libFlags %#v, libFlags: %#v",
571			"libcc_shared.generated_rust_staticlib.a", libcc_shared_ld.Args["libFlags"])
572	}
573
574	// Make sure the static lib includes are in the cc command
575	if !strings.Contains(libcc_shared_cc.Args["cFlags"], "-Ibar_includes") {
576		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
577			"-Ibar_includes", libcc_shared_cc.Args["cFlags"])
578	}
579
580	// Make sure there's a rustc command, and it's producing a staticlib
581	if !strings.Contains(ccbin_rustc.Args["rustcFlags"], "crate-type=staticlib") {
582		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", "staticlib", ccbin_rustc.Args["rustcFlags"])
583	}
584
585	// Make sure the static lib is included in the cc command
586	if !strings.Contains(ccbin_ld.Args["libFlags"], "generated_rust_staticlib/librustlibs.a") {
587		t.Errorf("missing generated static library in linker step libFlags, expecting %#v, libFlags: %#v",
588			"generated_rust_staticlib/librustlibs.a", ccbin_ld.Args["libFlags"])
589	}
590
591	// Make sure the static lib includes are in the ld command
592	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ibar_includes") {
593		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
594			"-Ibar_includes", ccbin_cc.Args)
595	}
596
597	// Make sure that direct dependencies and indirect whole static dependencies are
598	// propagating correctly to the generated rlib.
599	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern foo=") {
600		t.Errorf("Missing indirect whole_static_lib dependency libfoo when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
601	}
602	if strings.Contains(ccbin_rustc.Args["libFlags"], "--extern buzz=") {
603		t.Errorf("Indirect static_lib dependency libbuzz found when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
604	}
605	if !strings.Contains(ccbin_rustc.Args["libFlags"], "--extern bar=") {
606		t.Errorf("Missing direct dependency libbar when writing generated Rust staticlib: %#v", ccbin_rustc.Args["libFlags"])
607	}
608
609	// Make sure the static lib is included in the rustc command
610	if !strings.Contains(rustbin.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") {
611		t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
612			"generated_rust_staticlib/librustlibs.a", rustbin.Args["libFlags"])
613	}
614	if !strings.Contains(librs2_dylib.Args["linkFlags"], "generated_rust_staticlib/librustlibs.a") {
615		t.Errorf("missing generated static library in linker step libFlags in Rust module, expecting %#v, libFlags: %#v",
616			"generated_rust_staticlib/librustlibs.a", librs2_dylib.Args["libFlags"])
617	}
618
619	// Make sure that direct dependencies and indirect whole static dependencies are
620	// propagating correctly for the rlib -> cc_library_static -> rust_* generated library example.
621	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo=") {
622		t.Errorf("Missing indirect whole_static_lib dependency libfoo from cc static_lib when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
623	}
624	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern buzz=") {
625		t.Errorf("Indirect rlib dependency libbuzz from cc static_lib found when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
626	}
627	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern bar=") {
628		t.Errorf("Direct rlib dependency libbar getting included in the generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
629	}
630	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib=") {
631		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib from cc static_lib when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
632	}
633	if strings.Contains(rustbin_genlib.Args["libFlags"], "--extern buzz_from_rlib=") {
634		// While static-libs propagate for rust modules, this is not the
635		// expected behavior for cc modules. Thus, libbuzz_from_rlib would
636		// be expected to have to be re-declared as a direct rlib dependency.
637		t.Errorf("Indirect rlib dependency libbuzz_from_rlib from cc static_lib found when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
638	}
639
640	// Test indirect includes propagation
641	if !strings.Contains(ccbin_cc.Args["cFlags"], "-Ifoo_includes") {
642		t.Errorf("missing rlibs includes, expecting %#v, cFlags: %#v",
643			"-Ifoo_includes", ccbin_cc.Args)
644	}
645
646	// Make sure we're not generating superfluous mto staticlibs.
647	if librs_rlib.Rule != nil {
648		t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
649	}
650	if librs2_rlib.Rule != nil {
651		t.Error("rlibs should not be generating mto staticlibs", "rlib", libbar.Args["rustcFlags"])
652	}
653
654	// Make sure that direct whole static dependencies are propagating correctly downstream
655	// foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs
656	if !strings.Contains(librs_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
657		t.Errorf("Missing direct whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs_genlib.Args["libFlags"])
658	}
659
660	// Make sure that indirect whole static dependencies are propagating correctly downstream
661	// foo_from_rlib_whole --(ws)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_*
662	if !strings.Contains(librs2_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
663		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"])
664	}
665	if !strings.Contains(rustbin_genlib.Args["libFlags"], "--extern foo_from_rlib_whole=") {
666		t.Errorf("Missing indirect whole_static_lib dependency libfoo_from_rlib_whole from rust dylib in rust binary when writing generated Rust staticlib: %#v", rustbin_genlib.Args["libFlags"])
667	}
668
669	// Make sure that normal static dependencies are not propagating through dylib dependencies
670	// buzz_from_rlib_whole --(s)--> libcc_whole_static_from_rlib --(ws)--> librs --> rust_*
671	if strings.Contains(librs2_genlib.Args["libFlags"], "--extern buzz_from_rlib_whole=") {
672		t.Errorf("dependency from indirect cc staticlib from direct dylib dep found in rust dylib when writing generated Rust staticlib: %#v", librs2_genlib.Args["libFlags"])
673	}
674
675}
676
677func assertString(t *testing.T, got, expected string) {
678	t.Helper()
679	if got != expected {
680		t.Errorf("expected %q got %q", expected, got)
681	}
682}
683
684func TestStdLinkMismatch(t *testing.T) {
685	// Test that we catch cases where the std linkage mismatches. This leads to
686	// a confusing rustc error where a crate is declared missing despite being
687	// passed in as a rustlib dependency / via the --extern flag. Thus, we want
688	// to make sure we detect it in Soong.
689
690	// libfoo depends on libbar as an rlib, but does not link libstd as an rlib.
691	// libbar only links libstd as an rlib (prefer_rlib).
692	testRustError(t, "wrong StdLinkage", `
693		rust_library {
694			name: "libfoo",
695			crate_name: "foo",
696			srcs: [
697				"foo.rs",
698			],
699			rlibs: ["libbar"],
700		}
701		rust_library {
702			name: "libbar",
703			crate_name: "bar",
704			srcs: [
705				"bar.rs",
706			],
707			prefer_rlib: true,
708		}
709	`)
710}
711
712func TestRustLinkPropagation(t *testing.T) {
713	// Test static and whole static propagation behavior
714	//
715	//  Whole static libs propagate through rlibs and through dylibs to
716	//  dependencies further down. rustc does not re-export whole-archived
717	//  static libs for dylibs, so this simulates re-exporting those symbols.
718	//
719	//  Static libs only propagate through rlibs to some final dylib. We propagate
720	//  normal static libs because we allow rustlib dependencies to represent
721	//  either rlibs or dylibs. Not propagating static libs through rlibs would
722	//  mean we'd need to always redeclare static libs throughout a dependency tree
723	//  We don't propagate past dylibs because they represent a final link.
724
725	ctx := testRust(t, `
726	rust_library_rlib {
727		name: "librlib1",
728		crate_name: "rlib1",
729		srcs: ["src/lib.rs"],
730		static_libs: ["libcc_static_rlib1"],
731		whole_static_libs: ["libcc_whole_static_rlib1"],
732	}
733
734	rust_library_dylib {
735		name: "libdylib1",
736		crate_name: "dylib1",
737		static_libs: ["libcc_static_dylib1"],
738		srcs: ["src/lib.rs"],
739		whole_static_libs: ["libcc_whole_static_dylib1"],
740	}
741
742	rust_library_rlib {
743		name: "librlib2",
744		crate_name: "rlib2",
745		srcs: ["src/lib.rs"],
746		rlibs: ["librlib1"],
747		static_libs: ["libcc_static_rlib2"],
748		whole_static_libs: ["libcc_whole_static_rlib2"],
749	}
750
751	rust_library_dylib {
752		name: "libdylib2",
753		crate_name: "dylib2",
754		srcs: ["src/lib.rs"],
755		rlibs: ["librlib1"],
756		rustlibs: ["libdylib1"],
757		static_libs: ["libcc_static_dylib2"],
758		whole_static_libs: ["libcc_whole_static_dylib2"],
759	}
760
761	cc_library_static {
762		name: "libcc_static_rlib1",
763		srcs:["foo.c"],
764	}
765
766	cc_library_static {
767		name: "libcc_static_rlib2",
768		srcs:["foo.c"],
769	}
770
771	cc_library_static {
772		name: "libcc_static_dylib1",
773		srcs:["foo.c"],
774	}
775
776	cc_library_static {
777		name: "libcc_static_dylib2",
778		srcs:["foo.c"],
779	}
780
781	cc_library_static {
782		name: "libcc_whole_static_rlib1",
783		srcs:["foo.c"],
784	}
785
786	cc_library_static {
787		name: "libcc_whole_static_rlib2",
788		srcs:["foo.c"],
789	}
790
791	cc_library_static {
792		name: "libcc_whole_static_dylib1",
793		srcs:["foo.c"],
794	}
795
796	cc_library_static {
797		name: "libcc_whole_static_dylib2",
798		srcs:["foo.c"],
799	}
800
801	rust_library_rlib {
802		name: "librlib3",
803		crate_name: "rlib3",
804		srcs: ["src/lib.rs"],
805		rlibs: ["librlib2"],
806	}
807
808	rust_library_dylib {
809		name: "libdylib3",
810		crate_name: "dylib3",
811		srcs: ["src/lib.rs"],
812		rlibs: ["librlib2"],
813		rustlibs: ["libdylib2"],
814	}
815	`)
816
817	librlib3 := ctx.ModuleForTests(t, "librlib3", "android_arm64_armv8-a_rlib_dylib-std").Rule("rustc")
818	libdylib3 := ctx.ModuleForTests(t, "libdylib3", "android_arm64_armv8-a_dylib").Rule("rustc")
819
820	// Test static lib propagation from:
821	// rlib -> rlib
822	if !strings.Contains(librlib3.Args["linkFlags"], "libcc_static_rlib2.a") {
823		t.Errorf("direct dependency static lib not propagating from rlib to rlib; linkFlags %#v",
824			librlib3.Args["linkFlags"])
825	}
826	// rlib -> rlib -> rlib
827	if !strings.Contains(librlib3.Args["linkFlags"], "libcc_static_rlib1.a") {
828		t.Errorf("indirect dependency static lib not propagating from rlib to rlib: linkFlags %#v",
829			librlib3.Args["linkFlags"])
830	}
831	// rlib -> rlib -> dylib
832	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_rlib1.a") {
833		t.Errorf("indirect dependency static lib not propagating from rlib to dylib: linkFlags %#v",
834			libdylib3.Args["linkFlags"])
835	}
836	// rlib -> dylib
837	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_rlib2.a") {
838		t.Errorf("direct dependency static lib not propagating from rlib to dylib: linkFlags: %#v",
839			libdylib3.Args["linkFlags"])
840	}
841	// dylib -> dylib (negative case, should not propagate)
842	if strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_dylib2.a") {
843		t.Errorf("direct dependency static lib propagating from dylib to dylib: linkFlags: %#v",
844			libdylib3.Args["linkFlags"])
845	}
846	// dylib -> dylib -> dylib (negative case, should not propagate)
847	if strings.Contains(libdylib3.Args["linkFlags"], "libcc_static_dylib1.a") {
848		t.Errorf("indirect dependency static lib propagating from dylib to dylib: linkFlags: %#v",
849			libdylib3.Args["linkFlags"])
850	}
851
852	// Test whole static lib propagation from:
853	// rlib -> rlib
854	if !strings.Contains(librlib3.Args["linkFlags"], "libcc_whole_static_rlib2.a") {
855		t.Errorf("direct dependency whole static lib not propagating from rlib to rlib: linkFlags %#v",
856			librlib3.Args["linkFlags"])
857	}
858	// rlib -> rlib -> rlib
859	if !strings.Contains(librlib3.Args["linkFlags"], "libcc_whole_static_rlib1.a") {
860		t.Errorf("indirect dependency whole static lib not propagating from rlib to rlib: linkFlags %#v",
861			librlib3.Args["linkFlags"])
862	}
863	// rlib -> dylib
864	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_rlib2.a") {
865		t.Errorf("direct dependency whole static lib not propagating from rlib to dylib: linkFlags %#v",
866			libdylib3.Args["linkFlags"])
867	}
868	// rlib -> rlib -> dylib
869	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_rlib1.a") {
870		t.Errorf("indirect dependency whole static lib not propagating from rlib to dylib: linkFlags %#v",
871			libdylib3.Args["linkFlags"])
872	}
873	// dylib -> dylib
874	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_dylib2.a") {
875		t.Errorf("direct dependency whole static lib not propagating from dylib to dylib: linkFlags %#v",
876			libdylib3.Args["linkFlags"])
877	}
878	// dylib -> dylib -> dylib
879	if !strings.Contains(libdylib3.Args["linkFlags"], "libcc_whole_static_dylib1.a") {
880		t.Errorf("indirect dependency whole static lib not propagating from dylib to dylib: linkFlags %#v",
881			libdylib3.Args["linkFlags"])
882	}
883}
884