1// Copyright 2020 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
24func TestRustBindgen(t *testing.T) {
25	ctx := testRust(t, `
26		rust_bindgen {
27			name: "libbindgen",
28			defaults: ["cc_defaults_flags"],
29			wrapper_src: "src/any.h",
30			crate_name: "bindgen",
31			stem: "libbindgen",
32			source_stem: "bindings",
33			bindgen_flags: ["--bindgen-flag.*"],
34			cflags: ["--clang-flag()"],
35			shared_libs: ["libfoo_shared"],
36		}
37		rust_bindgen {
38			name: "libbindgen_staticlib",
39			wrapper_src: "src/any.h",
40			crate_name: "bindgen_staticlib",
41			stem: "libbindgen_staticlib",
42			source_stem: "bindings",
43			static_libs: ["libfoo_static"],
44		}
45		rust_bindgen {
46			name: "libbindgen_headerlib",
47			wrapper_src: "src/any.h",
48			crate_name: "bindgen_headerlib",
49			stem: "libbindgen_headerlib",
50			source_stem: "bindings",
51			header_libs: ["libfoo_header"],
52		}
53		cc_library_shared {
54			name: "libfoo_shared",
55			export_include_dirs: ["shared_include"],
56		}
57		cc_library_static {
58			name: "libfoo_static",
59			export_include_dirs: ["static_include"],
60		}
61		cc_library_headers {
62			name: "libfoo_header",
63			export_include_dirs: ["header_include"],
64		}
65		cc_defaults {
66			name: "cc_defaults_flags",
67			cflags: ["--default-flag"],
68		}
69	`)
70	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
71	libbindgenStatic := ctx.ModuleForTests("libbindgen_staticlib", "android_arm64_armv8-a_source").Output("bindings.rs")
72	libbindgenHeader := ctx.ModuleForTests("libbindgen_headerlib", "android_arm64_armv8-a_source").Output("bindings.rs")
73	libbindgenHeaderModule := ctx.ModuleForTests("libbindgen_headerlib", "android_arm64_armv8-a_source").Module().(*Module)
74	// Ensure that the flags are present and escaped
75	if !strings.Contains(libbindgen.Args["flags"], "'--bindgen-flag.*'") {
76		t.Errorf("missing bindgen flags in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
77	}
78	if !strings.Contains(libbindgen.Args["cflags"], "'--clang-flag()'") {
79		t.Errorf("missing clang cflags in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
80	}
81	if !strings.Contains(libbindgen.Args["cflags"], "-Ishared_include") {
82		t.Errorf("missing shared_libs exported includes in rust_bindgen rule: cflags %#v", libbindgen.Args["cflags"])
83	}
84	if !strings.Contains(libbindgenStatic.Args["cflags"], "-Istatic_include") {
85		t.Errorf("missing static_libs exported includes in rust_bindgen rule: cflags %#v", libbindgenStatic.Args["cflags"])
86	}
87	if !strings.Contains(libbindgenHeader.Args["cflags"], "-Iheader_include") {
88		t.Errorf("missing header_libs exported includes in rust_bindgen rule: cflags %#v", libbindgenHeader.Args["cflags"])
89	}
90
91	if android.InList("libfoo_static", libbindgenHeaderModule.Properties.AndroidMkHeaderLibs) {
92		t.Errorf("Static library dependency should not be in HeaderLibs list")
93	}
94
95	if !strings.Contains(libbindgen.Args["cflags"], "--default-flag") {
96		t.Errorf("rust_bindgen missing cflags defined in cc_defaults: cflags %#v", libbindgen.Args["cflags"])
97	}
98}
99
100func TestRustBindgenCustomBindgen(t *testing.T) {
101	ctx := testRust(t, `
102		rust_bindgen {
103			name: "libbindgen",
104			wrapper_src: "src/any.h",
105			crate_name: "bindgen",
106			stem: "libbindgen",
107			source_stem: "bindings",
108			custom_bindgen: "my_bindgen"
109		}
110		rust_binary_host {
111			name: "my_bindgen",
112			srcs: ["foo.rs"],
113		}
114	`)
115
116	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
117
118	// The rule description should contain the custom binary name rather than bindgen, so checking the description
119	// should be sufficient.
120	if !strings.Contains(libbindgen.Description, "my_bindgen") {
121		t.Errorf("Custom bindgen binary %s not used for libbindgen: rule description %#v", "my_bindgen",
122			libbindgen.Description)
123	}
124}
125
126func TestRustBindgenStdVersions(t *testing.T) {
127	testRustError(t, "c_std and cpp_std cannot both be defined at the same time.", `
128		rust_bindgen {
129			name: "libbindgen",
130			wrapper_src: "src/any.h",
131			crate_name: "bindgen",
132			stem: "libbindgen",
133			source_stem: "bindings",
134			c_std: "somevalue",
135			cpp_std: "somevalue",
136		}
137	`)
138
139	ctx := testRust(t, `
140		rust_bindgen {
141			name: "libbindgen_cstd",
142			wrapper_src: "src/any.hpp",
143			crate_name: "bindgen",
144			stem: "libbindgen",
145			source_stem: "bindings",
146			c_std: "foo"
147		}
148		rust_bindgen {
149			name: "libbindgen_cppstd",
150			wrapper_src: "src/any.h",
151			crate_name: "bindgen",
152			stem: "libbindgen",
153			source_stem: "bindings",
154			cpp_std: "foo"
155		}
156	`)
157
158	libbindgen_cstd := ctx.ModuleForTests("libbindgen_cstd", "android_arm64_armv8-a_source").Output("bindings.rs")
159	libbindgen_cppstd := ctx.ModuleForTests("libbindgen_cppstd", "android_arm64_armv8-a_source").Output("bindings.rs")
160
161	if !strings.Contains(libbindgen_cstd.Args["cflags"], "-std=foo") {
162		t.Errorf("c_std value not passed in to rust_bindgen as a clang flag")
163	}
164
165	if !strings.Contains(libbindgen_cppstd.Args["cflags"], "-std=foo") {
166		t.Errorf("cpp_std value not passed in to rust_bindgen as a clang flag")
167	}
168
169	// Make sure specifying cpp_std emits the '-x c++' flag
170	if !strings.Contains(libbindgen_cppstd.Args["cflags"], "-x c++") {
171		t.Errorf("Setting cpp_std should cause the '-x c++' flag to be emitted")
172	}
173
174	// Make sure specifying c_std omits the '-x c++' flag
175	if strings.Contains(libbindgen_cstd.Args["cflags"], "-x c++") {
176		t.Errorf("Setting c_std should not cause the '-x c++' flag to be emitted")
177	}
178}
179
180func TestBindgenDisallowedFlags(t *testing.T) {
181	// Make sure passing '-x c++' to cflags generates an error
182	testRustError(t, "cflags: -x c\\+\\+ should not be specified in cflags.*", `
183		rust_bindgen {
184			name: "libbad_flag",
185			wrapper_src: "src/any.h",
186			crate_name: "bindgen",
187			stem: "libbindgen",
188			source_stem: "bindings",
189			cflags: ["-x c++"]
190		}
191	`)
192
193	// Make sure passing '-std=' to cflags generates an error
194	testRustError(t, "cflags: -std should not be specified in cflags.*", `
195		rust_bindgen {
196			name: "libbad_flag",
197			wrapper_src: "src/any.h",
198			crate_name: "bindgen",
199			stem: "libbindgen",
200			source_stem: "bindings",
201			cflags: ["-std=foo"]
202		}
203	`)
204}
205
206func TestBindgenFlagFile(t *testing.T) {
207	ctx := testRust(t, `
208		rust_bindgen {
209			name: "libbindgen",
210			wrapper_src: "src/any.h",
211			crate_name: "bindgen",
212			stem: "libbindgen",
213			source_stem: "bindings",
214			bindgen_flag_files: [
215				"flag_file.txt",
216			],
217		}
218	`)
219	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
220
221	if !strings.Contains(libbindgen.Args["flagfiles"], "/dev/null") {
222		t.Errorf("missing /dev/null in rust_bindgen rule: flags %#v", libbindgen.Args["flagfiles"])
223	}
224	if !strings.Contains(libbindgen.Args["flagfiles"], "flag_file.txt") {
225		t.Errorf("missing bindgen flags file in rust_bindgen rule: flags %#v", libbindgen.Args["flagfiles"])
226	}
227	// TODO: The best we can do right now is check $flagfiles. Once bindgen.go switches to RuleBuilder,
228	// we may be able to check libbinder.RuleParams.Command to see if it contains $(cat /dev/null flag_file.txt)
229}
230
231func TestBindgenHandleStaticInlining(t *testing.T) {
232	ctx := testRust(t, `
233		rust_bindgen {
234			name: "libbindgen",
235			wrapper_src: "src/any.h",
236			crate_name: "bindgen",
237			stem: "libbindgen",
238			source_stem: "bindings",
239			handle_static_inline: true,
240			static_inline_library: "libbindgen_staticfns"
241		}
242
243		cc_library_static {
244			name: "libbindgen_staticfns",
245			srcs: [":libbindgen"],
246			include_dirs: ["src/"],
247		}
248	`)
249	libbindgen := ctx.ModuleForTests("libbindgen", "android_arm64_armv8-a_source").Output("bindings.rs")
250	// Make sure the flag to support `static inline` functions is present
251	if !strings.Contains(libbindgen.Args["flags"], "--wrap-static-fns") {
252		t.Errorf("missing flag to handle static inlining in rust_bindgen rule: flags %#v", libbindgen.Args["flags"])
253	}
254
255	if !strings.Contains(libbindgen.Args["flags"], "--wrap-static-fns-path") {
256		t.Errorf("missing flag to define path for static inlining C source from bindgen (--wrap-static-fns-path): flags %#v", libbindgen.Args["flags"])
257	}
258
259}
260
261func TestBindgenStaticInlineProperties(t *testing.T) {
262	// Make sure handle_static_inline without static_inline_library generates an error
263	testRustError(t, "requires declaring static_inline_library to the corresponding cc_library module that includes the generated C source from bindgen", `
264		rust_bindgen {
265			name: "libbindgen",
266			wrapper_src: "src/any.h",
267			crate_name: "bindgen",
268			stem: "libbindgen",
269			source_stem: "bindings",
270			handle_static_inline: true
271		}
272	`)
273	testRustError(t, "requires declaring handle_static_inline", `
274		rust_bindgen {
275			name: "libbindgen",
276			wrapper_src: "src/any.h",
277			crate_name: "bindgen",
278			stem: "libbindgen",
279			source_stem: "bindings",
280			static_inline_library: "libbindgen_staticfns"
281		}
282
283		cc_library_static {
284			name: "libbindgen_staticfns",
285			srcs: [":libbindgen"],
286			include_dirs: ["src/"],
287		}
288	`)
289}
290