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