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").Output("libfoo.rlib")
41	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
42	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static").Output("libfoo.ffi.a")
43	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared").Output("libfoo.ffi.so")
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").Output("libfoo.dylib.so")
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
88func TestValidateLibraryStem(t *testing.T) {
89	testRustError(t, "crate_name must be defined.", `
90			rust_library_host {
91				name: "libfoo",
92				srcs: ["foo.rs"],
93			}`)
94
95	testRustError(t, "library crate_names must be alphanumeric with underscores allowed", `
96			rust_library_host {
97				name: "libfoo-bar",
98				srcs: ["foo.rs"],
99				crate_name: "foo-bar"
100			}`)
101
102	testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", `
103			rust_library_host {
104				name: "foobar",
105				srcs: ["foo.rs"],
106				crate_name: "foo_bar"
107			}`)
108	testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", `
109			rust_library_host {
110				name: "foobar",
111				stem: "libfoo",
112				srcs: ["foo.rs"],
113				crate_name: "foo_bar"
114			}`)
115	testRustError(t, "Invalid name or stem property; library filenames must start with lib<crate_name>", `
116			rust_library_host {
117				name: "foobar",
118				stem: "foo_bar",
119				srcs: ["foo.rs"],
120				crate_name: "foo_bar"
121			}`)
122
123}
124
125func TestSharedLibrary(t *testing.T) {
126	ctx := testRust(t, `
127		rust_ffi_shared {
128			name: "libfoo",
129			srcs: ["foo.rs"],
130			crate_name: "foo",
131		}`)
132
133	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared")
134
135	libfooOutput := libfoo.Output("libfoo.so")
136	if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
137		t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v",
138			libfooOutput.Args["linkFlags"])
139	}
140
141	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkDylibs) {
142		t.Errorf("Non-static libstd dylib expected to be a dependency of Rust shared libraries. Dylib deps are: %#v",
143			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
144	}
145}
146
147func TestStaticLibraryLinkage(t *testing.T) {
148	ctx := testRust(t, `
149		rust_ffi_static {
150			name: "libfoo",
151			srcs: ["foo.rs"],
152			crate_name: "foo",
153		}`)
154
155	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
156
157	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkRlibs) {
158		t.Errorf("Static libstd rlib expected to be a dependency of Rust static libraries. Rlib deps are: %#v",
159			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
160	}
161}
162
163// Test that variants pull in the right type of rustlib autodep
164func TestAutoDeps(t *testing.T) {
165
166	ctx := testRust(t, `
167                rust_library_host {
168                        name: "libbar",
169                        srcs: ["bar.rs"],
170                        crate_name: "bar",
171                }
172		rust_library_host {
173			name: "libfoo",
174			srcs: ["foo.rs"],
175			crate_name: "foo",
176                        rustlibs: ["libbar"],
177		}
178                rust_ffi_host {
179                        name: "libfoo.ffi",
180                        srcs: ["foo.rs"],
181                        crate_name: "foo",
182                        rustlibs: ["libbar"],
183                }`)
184
185	libfooRlib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_rlib_rlib-std")
186	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib")
187	libfooStatic := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_static")
188	libfooShared := ctx.ModuleForTests("libfoo.ffi", "linux_glibc_x86_64_shared")
189
190	for _, static := range []android.TestingModule{libfooRlib, libfooStatic} {
191		if !android.InList("libbar.rlib-std", static.Module().(*Module).Properties.AndroidMkRlibs) {
192			t.Errorf("libbar not present as rlib dependency in static lib")
193		}
194		if android.InList("libbar", static.Module().(*Module).Properties.AndroidMkDylibs) {
195			t.Errorf("libbar present as dynamic dependency in static lib")
196		}
197	}
198
199	for _, dyn := range []android.TestingModule{libfooDylib, libfooShared} {
200		if !android.InList("libbar", dyn.Module().(*Module).Properties.AndroidMkDylibs) {
201			t.Errorf("libbar not present as dynamic dependency in dynamic lib")
202		}
203		if android.InList("libbar.dylib-std", dyn.Module().(*Module).Properties.AndroidMkRlibs) {
204			t.Errorf("libbar present as rlib dependency in dynamic lib")
205		}
206
207	}
208}
209
210// Test that stripped versions are correctly generated and used.
211func TestStrippedLibrary(t *testing.T) {
212	ctx := testRust(t, `
213		rust_library_dylib {
214			name: "libfoo",
215			crate_name: "foo",
216			srcs: ["foo.rs"],
217		}
218		rust_library_dylib {
219			name: "libbar",
220			crate_name: "bar",
221			srcs: ["foo.rs"],
222			strip: {
223				none: true
224			}
225		}
226	`)
227
228	foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib")
229	foo.Output("stripped/libfoo.dylib.so")
230	// Check that the `cp` rule is using the stripped version as input.
231	cp := foo.Rule("android.Cp")
232	if !strings.HasSuffix(cp.Input.String(), "stripped/libfoo.dylib.so") {
233		t.Errorf("installed binary not based on stripped version: %v", cp.Input)
234	}
235
236	fizzBar := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeOutput("stripped/libbar.dylib.so")
237	if fizzBar.Rule != nil {
238		t.Errorf("stripped version of bar has been generated")
239	}
240}
241
242func TestLibstdLinkage(t *testing.T) {
243	ctx := testRust(t, `
244		rust_library {
245			name: "libfoo",
246			srcs: ["foo.rs"],
247			crate_name: "foo",
248		}
249		rust_ffi {
250			name: "libbar",
251			srcs: ["foo.rs"],
252			crate_name: "bar",
253			rustlibs: ["libfoo"],
254		}
255		rust_ffi {
256			name: "libbar.prefer_rlib",
257			srcs: ["foo.rs"],
258			crate_name: "bar",
259			rustlibs: ["libfoo"],
260			prefer_rlib: true,
261		}`)
262
263	libfooDylib := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").Module().(*Module)
264	libfooRlibStatic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_rlib-std").Module().(*Module)
265	libfooRlibDynamic := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_rlib_dylib-std").Module().(*Module)
266
267	libbarShared := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_shared").Module().(*Module)
268	libbarStatic := ctx.ModuleForTests("libbar", "android_arm64_armv8-a_static").Module().(*Module)
269
270	// prefer_rlib works the same for both rust_library and rust_ffi, so a single check is sufficient here.
271	libbarRlibStd := ctx.ModuleForTests("libbar.prefer_rlib", "android_arm64_armv8-a_shared").Module().(*Module)
272
273	if !android.InList("libstd", libfooRlibStatic.Properties.AndroidMkRlibs) {
274		t.Errorf("rlib-std variant for device rust_library_rlib does not link libstd as an rlib")
275	}
276	if !android.InList("libstd", libfooRlibDynamic.Properties.AndroidMkDylibs) {
277		t.Errorf("dylib-std variant for device rust_library_rlib does not link libstd as an dylib")
278	}
279	if !android.InList("libstd", libfooDylib.Properties.AndroidMkDylibs) {
280		t.Errorf("Device rust_library_dylib does not link libstd as an dylib")
281	}
282
283	if !android.InList("libstd", libbarShared.Properties.AndroidMkDylibs) {
284		t.Errorf("Device rust_ffi_shared does not link libstd as an dylib")
285	}
286	if !android.InList("libstd", libbarStatic.Properties.AndroidMkRlibs) {
287		t.Errorf("Device rust_ffi_static does not link libstd as an rlib")
288	}
289	if !android.InList("libfoo.rlib-std", libbarStatic.Properties.AndroidMkRlibs) {
290		t.Errorf("Device rust_ffi_static does not link dependent rustlib rlib-std variant")
291	}
292	if !android.InList("libstd", libbarRlibStd.Properties.AndroidMkRlibs) {
293		t.Errorf("rust_ffi with prefer_rlib does not link libstd as an rlib")
294	}
295
296}
297