xref: /aosp_15_r20/build/soong/rust/library_test.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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