xref: /aosp_15_r20/build/bazel/rules/cc/cc_binary_test.bzl (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
1# Copyright (C) 2022 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
15load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
16load(":cc_binary.bzl", "cc_binary")
17load(":cc_library_common_test.bzl", "target_provides_androidmk_info_test")
18load(":cc_library_shared.bzl", "cc_library_shared")
19load(":cc_library_static.bzl", "cc_library_static")
20
21def strip_test_assert_flags(env, strip_action, strip_flags):
22    # Extract these flags from strip_action (for example):
23    # build/soong/scripts/strip.sh --keep-symbols --add-gnu-debuglink -i <in> -o <out> -d <out>.d
24    #                              ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
25    flag_start_idx = 1  # starts after the strip.sh executable
26    flag_end_idx = strip_action.argv.index("-i")  # end of the flags
27    asserts.equals(
28        env,
29        strip_action.argv[flag_start_idx:flag_end_idx],
30        strip_flags,
31    )
32
33def _cc_binary_strip_test(ctx):
34    env = analysistest.begin(ctx)
35    actions = analysistest.target_actions(env)
36    filtered_actions = [a for a in actions if a.mnemonic == "CcStrip"]
37    on_target = ctx.target_platform_has_constraint(
38        ctx.attr._android_constraint[platform_common.ConstraintValueInfo],
39    )
40    if ctx.attr.strip_flags or on_target:
41        # expected to find strip flags, so look for a CcStrip action.
42        asserts.true(
43            env,
44            len(filtered_actions) == 1,
45            "expected to find an action with CcStrip mnemonic in %s" % actions,
46        )
47        if ctx.attr.strip_flags or not on_target:
48            strip_test_assert_flags(env, filtered_actions[0], ctx.attr.strip_flags)
49        return analysistest.end(env)
50    else:
51        asserts.true(
52            env,
53            len(filtered_actions) == 0,
54            "expected to not find an action with CcStrip mnemonic in %s" % actions,
55        )
56        return analysistest.end(env)
57
58cc_binary_strip_test = analysistest.make(
59    _cc_binary_strip_test,
60    attrs = {
61        "strip_flags": attr.string_list(),
62        "_android_constraint": attr.label(default = Label("//build/bazel_common_rules/platforms/os:android")),
63    },
64)
65
66def _cc_binary_strip_default():
67    name = "cc_binary_strip_default"
68    test_name = name + "_test"
69
70    cc_binary(
71        name = name,
72        srcs = ["main.cc"],
73        tags = ["manual"],
74    )
75
76    cc_binary_strip_test(
77        name = test_name,
78        target_under_test = name,
79        strip_flags = [],
80    )
81
82    return test_name
83
84def _cc_binary_strip_keep_symbols():
85    name = "cc_binary_strip_keep_symbols"
86    test_name = name + "_test"
87
88    cc_binary(
89        name = name,
90        srcs = ["main.cc"],
91        tags = ["manual"],
92        strip = {"keep_symbols": True},
93    )
94
95    cc_binary_strip_test(
96        name = test_name,
97        target_under_test = name,
98        strip_flags = [
99            "--keep-symbols",
100            "--add-gnu-debuglink",
101        ],
102    )
103
104    return test_name
105
106def _cc_binary_strip_keep_symbols_and_debug_frame():
107    name = "cc_binary_strip_keep_symbols_and_debug_frame"
108    test_name = name + "_test"
109
110    cc_binary(
111        name = name,
112        srcs = ["main.cc"],
113        tags = ["manual"],
114        strip = {"keep_symbols_and_debug_frame": True},
115    )
116
117    cc_binary_strip_test(
118        name = test_name,
119        target_under_test = name,
120        strip_flags = [
121            "--keep-symbols-and-debug-frame",
122            "--add-gnu-debuglink",
123        ],
124    )
125
126    return test_name
127
128def _cc_binary_strip_keep_symbols_list():
129    name = "cc_binary_strip_keep_symbols_list"
130    test_name = name + "_test"
131
132    cc_binary(
133        name = name,
134        srcs = ["main.cc"],
135        tags = ["manual"],
136        strip = {"keep_symbols_list": ["foo", "bar"]},
137    )
138
139    cc_binary_strip_test(
140        name = test_name,
141        target_under_test = name,
142        strip_flags = [
143            "-kfoo,bar",
144            "--add-gnu-debuglink",
145        ],
146    )
147
148    return test_name
149
150def _cc_binary_strip_all():
151    name = "cc_binary_strip_all"
152    test_name = name + "_test"
153
154    cc_binary(
155        name = name,
156        srcs = ["main.cc"],
157        tags = ["manual"],
158        strip = {"all": True},
159    )
160
161    cc_binary_strip_test(
162        name = test_name,
163        target_under_test = name,
164        strip_flags = [
165            "--add-gnu-debuglink",
166        ],
167    )
168
169    return test_name
170
171def _cc_binary_suffix_test_impl(ctx):
172    env = analysistest.begin(ctx)
173    target = analysistest.target_under_test(env)
174    info = target[DefaultInfo]
175    suffix = ctx.attr.suffix
176
177    outputs = info.files.to_list()
178    asserts.true(
179        env,
180        len(outputs) == 1,
181        "Expected 1 output file; got %s" % outputs,
182    )
183    out = outputs[0]
184    asserts.true(
185        env,
186        out.path.endswith(suffix),
187        "Expected output filename to end in `%s`; it was instead %s" % (suffix, out),
188    )
189
190    if ctx.attr.stem:
191        asserts.equals(
192            env,
193            out.basename,
194            ctx.attr.stem,
195            "Expected output filename %s to be equal to `stem` attribute %s" % (out, ctx.attr.stem),
196        )
197
198    return analysistest.end(env)
199
200cc_binary_suffix_test = analysistest.make(
201    _cc_binary_suffix_test_impl,
202    attrs = {
203        "stem": attr.string(),
204        "suffix": attr.string(),
205    },
206)
207
208def _cc_binary_suffix():
209    name = "cc_binary_suffix"
210    test_name = name + "_test"
211    suffix = "-suf"
212
213    cc_binary(
214        name,
215        srcs = ["src.cc"],
216        tags = ["manual"],
217        suffix = suffix,
218    )
219    cc_binary_suffix_test(
220        name = test_name,
221        target_under_test = name,
222        suffix = suffix,
223    )
224    return test_name
225
226def _cc_binary_empty_suffix():
227    name = "cc_binary_empty_suffix"
228    test_name = name + "_test"
229
230    cc_binary(
231        name,
232        srcs = ["src.cc"],
233        tags = ["manual"],
234    )
235    cc_binary_suffix_test(
236        name = test_name,
237        target_under_test = name,
238    )
239    return test_name
240
241def _cc_binary_with_stem():
242    name = "cc_binary_with_stem"
243    test_name = name + "_test"
244
245    cc_binary(
246        name,
247        srcs = ["src.cc"],
248        stem = "bar",
249        tags = ["manual"],
250    )
251    cc_binary_suffix_test(
252        name = test_name,
253        stem = "bar",
254        target_under_test = name,
255    )
256    return test_name
257
258def _cc_binary_provides_androidmk_info():
259    name = "cc_binary_provides_androidmk_info"
260    dep_name = name + "_static_dep"
261    whole_archive_dep_name = name + "_whole_archive_dep"
262    dynamic_dep_name = name + "_dynamic_dep"
263    test_name = name + "_test"
264
265    cc_library_static(
266        name = dep_name,
267        srcs = ["foo.c"],
268        tags = ["manual"],
269    )
270    cc_library_static(
271        name = whole_archive_dep_name,
272        srcs = ["foo.c"],
273        tags = ["manual"],
274    )
275    cc_library_shared(
276        name = dynamic_dep_name,
277        srcs = ["foo.c"],
278        tags = ["manual"],
279    )
280    cc_binary(
281        name = name,
282        srcs = ["foo.cc"],
283        deps = [dep_name],
284        whole_archive_deps = [whole_archive_dep_name],
285        dynamic_deps = [dynamic_dep_name],
286        tags = ["manual"],
287    )
288    android_test_name = test_name + "_android"
289    linux_test_name = test_name + "_linux"
290    target_provides_androidmk_info_test(
291        name = android_test_name,
292        target_under_test = name,
293        expected_static_libs = [dep_name, "libc++demangle", "libunwind"],
294        expected_whole_static_libs = [whole_archive_dep_name],
295        expected_shared_libs = [dynamic_dep_name, "libc++", "libc_stub_libs-current", "libdl_stub_libs-current", "libm_stub_libs-current"],
296        target_compatible_with = ["//build/bazel_common_rules/platforms/os:android"],
297    )
298    target_provides_androidmk_info_test(
299        name = linux_test_name,
300        target_under_test = name,
301        expected_static_libs = [dep_name],
302        expected_whole_static_libs = [whole_archive_dep_name],
303        expected_shared_libs = [dynamic_dep_name, "libc++"],
304        target_compatible_with = ["//build/bazel_common_rules/platforms/os:linux"],
305    )
306    return [
307        android_test_name,
308        linux_test_name,
309    ]
310
311def _cc_bad_linkopts_test_impl(ctx):
312    env = analysistest.begin(ctx)
313    if ctx.target_platform_has_constraint(ctx.attr._android_constraint[platform_common.ConstraintValueInfo]):
314        asserts.expect_failure(env, "Library requested via -l is not supported for device builds. Use implementation_deps instead.")
315    else:
316        asserts.expect_failure(env, "Host library(s) requested via -l is not available in the toolchain.")
317    return analysistest.end(env)
318
319cc_bad_linkopts_test = analysistest.make(
320    _cc_bad_linkopts_test_impl,
321    expect_failure = True,
322    attrs = {
323        "_android_constraint": attr.label(
324            default = Label("//build/bazel_common_rules/platforms/os:android"),
325        ),
326    },
327)
328
329# Test that an error is raised if a user requests a library that is not available in the toolchain.
330def _cc_binary_bad_linkopts():
331    subject_name = "cc_binary_bad_linkopts"
332    test_name = subject_name + "_test"
333
334    cc_binary(
335        name = subject_name,
336        linkopts = ["-lunknown"],
337        tags = ["manual"],
338    )
339    cc_bad_linkopts_test(
340        name = test_name,
341        target_under_test = subject_name,
342    )
343    return test_name
344
345def cc_binary_test_suite(name):
346    native.test_suite(
347        name = name,
348        tests = [
349            _cc_binary_strip_default(),
350            _cc_binary_strip_keep_symbols(),
351            _cc_binary_strip_keep_symbols_and_debug_frame(),
352            _cc_binary_strip_keep_symbols_list(),
353            _cc_binary_strip_all(),
354            _cc_binary_suffix(),
355            _cc_binary_empty_suffix(),
356            _cc_binary_with_stem(),
357            _cc_binary_bad_linkopts(),
358        ] + _cc_binary_provides_androidmk_info(),
359    )
360