xref: /aosp_15_r20/external/bazelbuild-rules_rust/test/unit/native_deps/native_deps_test.bzl (revision d4726bddaa87cc4778e7472feed243fa4b6c267f)
1"""Unittests for rust rules."""
2
3load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
4load("@rules_cc//cc:defs.bzl", "cc_library")
5load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro", "rust_shared_library", "rust_static_library")
6load(
7    "//test/unit:common.bzl",
8    "assert_argv_contains",
9    "assert_argv_contains_not",
10    "assert_argv_contains_prefix",
11    "assert_argv_contains_prefix_not",
12    "assert_argv_contains_prefix_suffix",
13    "assert_list_contains_adjacent_elements",
14)
15
16def _get_toolchain(ctx):
17    return ctx.attr._toolchain[platform_common.ToolchainInfo]
18
19def _get_darwin_component(arg):
20    # path/to/darwin_x86_64-fastbuild-fastbuild/package -> darwin_x86_64-fastbuild
21    darwin_component = [x for x in arg.split("/") if x.startswith("darwin")][0]
22
23    # darwin_x86_64-fastbuild -> darwin
24    return darwin_component.split("-")[0]
25
26def _rlib_has_no_native_libs_test_impl(ctx):
27    env = analysistest.begin(ctx)
28    tut = analysistest.target_under_test(env)
29    action = tut.actions[0]
30    assert_argv_contains(env, action, "--crate-type=rlib")
31    assert_argv_contains_not(env, action, "-lstatic=native_dep")
32    assert_argv_contains_not(env, action, "-ldylib=native_dep")
33    assert_argv_contains_prefix_not(env, action, "--codegen=linker=")
34    return analysistest.end(env)
35
36def _cdylib_has_native_libs_test_impl(ctx):
37    env = analysistest.begin(ctx)
38    tut = analysistest.target_under_test(env)
39    action = tut.actions[0]
40    toolchain = _get_toolchain(ctx)
41    compilation_mode = ctx.var["COMPILATION_MODE"]
42    pic_suffix = _get_pic_suffix(ctx, compilation_mode)
43    assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps")
44    assert_argv_contains(env, action, "--crate-type=cdylib")
45    assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix))
46    if toolchain.target_os == "windows":
47        if toolchain.target_triple.abi == "msvc":
48            native_link_arg = "-Clink-arg=native_dep.lib"
49        else:
50            native_link_arg = "-Clink-arg=-lnative_dep.lib"
51    else:
52        native_link_arg = "-Clink-arg=-lnative_dep{}".format(pic_suffix)
53    assert_argv_contains(env, action, native_link_arg)
54    assert_argv_contains_prefix(env, action, "--codegen=linker=")
55    return analysistest.end(env)
56
57def _staticlib_has_native_libs_test_impl(ctx):
58    env = analysistest.begin(ctx)
59    tut = analysistest.target_under_test(env)
60    action = tut.actions[0]
61    toolchain = _get_toolchain(ctx)
62    assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps")
63    assert_argv_contains(env, action, "--crate-type=staticlib")
64    assert_argv_contains(env, action, "-lstatic=native_dep")
65    if toolchain.target_os == "windows":
66        if toolchain.target_triple.abi == "msvc":
67            native_link_arg = "-Clink-arg=native_dep.lib"
68        else:
69            native_link_arg = "-Clink-arg=-lnative_dep.lib"
70    else:
71        native_link_arg = "-Clink-arg=-lnative_dep"
72    assert_argv_contains(env, action, native_link_arg)
73    assert_argv_contains_prefix(env, action, "--codegen=linker=")
74    return analysistest.end(env)
75
76def _proc_macro_has_native_libs_test_impl(ctx):
77    env = analysistest.begin(ctx)
78    tut = analysistest.target_under_test(env)
79    action = tut.actions[0]
80    toolchain = _get_toolchain(ctx)
81    compilation_mode = ctx.var["COMPILATION_MODE"]
82    pic_suffix = _get_pic_suffix(ctx, compilation_mode)
83    assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps")
84    assert_argv_contains(env, action, "--crate-type=proc-macro")
85    assert_argv_contains(env, action, "-lstatic=native_dep{}".format(pic_suffix))
86    if toolchain.target_os == "windows":
87        if toolchain.target_triple.abi == "msvc":
88            native_link_arg = "-Clink-arg=native_dep.lib"
89        else:
90            native_link_arg = "-Clink-arg=-lnative_dep.lib"
91    else:
92        native_link_arg = "-Clink-arg=-lnative_dep{}".format(pic_suffix)
93    assert_argv_contains(env, action, native_link_arg)
94    assert_argv_contains_prefix(env, action, "--codegen=linker=")
95    return analysistest.end(env)
96
97def _bin_has_native_libs_test_impl(ctx):
98    env = analysistest.begin(ctx)
99    tut = analysistest.target_under_test(env)
100    action = tut.actions[0]
101    toolchain = _get_toolchain(ctx)
102    assert_argv_contains_prefix_suffix(env, action, "-Lnative=", "/native_deps")
103    assert_argv_contains(env, action, "-lstatic=native_dep")
104    if toolchain.target_os == "windows":
105        if toolchain.target_triple.abi == "msvc":
106            native_link_arg = "-Clink-arg=native_dep.lib"
107        else:
108            native_link_arg = "-Clink-arg=-lnative_dep.lib"
109    else:
110        native_link_arg = "-Clink-arg=-lnative_dep"
111    assert_argv_contains(env, action, native_link_arg)
112    assert_argv_contains_prefix(env, action, "--codegen=linker=")
113    return analysistest.end(env)
114
115def _extract_linker_args(argv):
116    return [
117        a.removeprefix("--codegen=").removeprefix("-C").removeprefix("link-arg=").removeprefix("link-args=")
118        for a in argv
119        if (
120            a.startswith("--codegen=link-arg=") or
121            a.startswith("--codegen=link-args=") or
122            a.startswith("-Clink-args=") or
123            a.startswith("-Clink-arg=") or
124            a.startswith("link-args=") or
125            a.startswith("link-arg=") or
126            a.startswith("-l") or
127            a.endswith(".lo") or
128            a.endswith(".o")
129        )
130    ]
131
132def _bin_has_native_dep_and_alwayslink_test_impl(ctx):
133    env = analysistest.begin(ctx)
134    tut = analysistest.target_under_test(env)
135    action = tut.actions[0]
136
137    toolchain = _get_toolchain(ctx)
138    compilation_mode = ctx.var["COMPILATION_MODE"]
139    workspace_prefix = "" if ctx.workspace_name == "rules_rust" else "external/rules_rust/"
140    link_args = _extract_linker_args(action.argv)
141    if toolchain.target_os == "darwin":
142        darwin_component = _get_darwin_component(link_args[-1])
143        want = [
144            "-lstatic=native_dep",
145            "-lnative_dep",
146            "-Wl,-force_load,bazel-out/{}-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(darwin_component, compilation_mode, workspace_prefix),
147        ]
148        assert_list_contains_adjacent_elements(env, link_args, want)
149    elif toolchain.target_os == "windows":
150        if toolchain.target_triple.abi == "msvc":
151            want = [
152                "-lstatic=native_dep",
153                "native_dep.lib",
154                "/WHOLEARCHIVE:bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix),
155            ]
156        else:
157            want = [
158                "-lstatic=native_dep",
159                "native_dep.lib",
160                "-Wl,--whole-archive",
161                "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix),
162                "-Wl,--no-whole-archive",
163            ]
164    else:
165        want = [
166            "-lstatic=native_dep",
167            "-lnative_dep",
168            "-Wl,--whole-archive",
169            "bazel-out/k8-{}/bin/{}test/unit/native_deps/libalwayslink.lo".format(compilation_mode, workspace_prefix),
170            "-Wl,--no-whole-archive",
171        ]
172    assert_list_contains_adjacent_elements(env, link_args, want)
173    return analysistest.end(env)
174
175def _cdylib_has_native_dep_and_alwayslink_test_impl(ctx):
176    toolchain = _get_toolchain(ctx)
177
178    env = analysistest.begin(ctx)
179    tut = analysistest.target_under_test(env)
180    action = tut.actions[0]
181
182    linker_args = _extract_linker_args(action.argv)
183
184    toolchain = _get_toolchain(ctx)
185    compilation_mode = ctx.var["COMPILATION_MODE"]
186    workspace_prefix = "" if ctx.workspace_name == "rules_rust" else "external/rules_rust/"
187    pic_suffix = _get_pic_suffix(ctx, compilation_mode)
188    if toolchain.target_os == "darwin":
189        darwin_component = _get_darwin_component(linker_args[-1])
190        want = [
191            "-lstatic=native_dep{}".format(pic_suffix),
192            "-lnative_dep{}".format(pic_suffix),
193            "-Wl,-force_load,bazel-out/{}-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(darwin_component, compilation_mode, workspace_prefix, pic_suffix),
194        ]
195    elif toolchain.target_os == "windows":
196        if toolchain.target_triple.abi == "msvc":
197            want = [
198                "-lstatic=native_dep",
199                "native_dep.lib",
200                "/WHOLEARCHIVE:bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix),
201            ]
202        else:
203            want = [
204                "-lstatic=native_dep",
205                "native_dep.lib",
206                "-Wl,--whole-archive",
207                "bazel-out/x64_windows-{}/bin/{}test/unit/native_deps/alwayslink.lo.lib".format(compilation_mode, workspace_prefix),
208                "-Wl,--no-whole-archive",
209            ]
210    else:
211        want = [
212            "-lstatic=native_dep{}".format(pic_suffix),
213            "-lnative_dep{}".format(pic_suffix),
214            "-Wl,--whole-archive",
215            "bazel-out/k8-{}/bin/{}test/unit/native_deps/libalwayslink{}.lo".format(compilation_mode, workspace_prefix, pic_suffix),
216            "-Wl,--no-whole-archive",
217        ]
218    assert_list_contains_adjacent_elements(env, linker_args, want)
219    return analysistest.end(env)
220
221def _get_pic_suffix(ctx, compilation_mode):
222    toolchain = _get_toolchain(ctx)
223    if toolchain.target_os == "darwin" or toolchain.target_os == "windows":
224        return ""
225    return ".pic" if compilation_mode == "opt" else ""
226
227rlib_has_no_native_libs_test = analysistest.make(_rlib_has_no_native_libs_test_impl)
228staticlib_has_native_libs_test = analysistest.make(_staticlib_has_native_libs_test_impl, attrs = {
229    "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")),
230})
231cdylib_has_native_libs_test = analysistest.make(_cdylib_has_native_libs_test_impl, attrs = {
232    "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")),
233})
234proc_macro_has_native_libs_test = analysistest.make(_proc_macro_has_native_libs_test_impl, attrs = {
235    "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")),
236})
237bin_has_native_libs_test = analysistest.make(_bin_has_native_libs_test_impl, attrs = {
238    "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")),
239})
240bin_has_native_dep_and_alwayslink_test = analysistest.make(_bin_has_native_dep_and_alwayslink_test_impl, attrs = {
241    "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")),
242})
243cdylib_has_native_dep_and_alwayslink_test = analysistest.make(_cdylib_has_native_dep_and_alwayslink_test_impl, attrs = {
244    "_toolchain": attr.label(default = Label("//rust/toolchain:current_rust_toolchain")),
245})
246
247def _native_dep_test():
248    rust_library(
249        name = "rlib_has_no_native_dep",
250        srcs = ["lib_using_native_dep.rs"],
251        edition = "2018",
252        deps = [":native_dep"],
253    )
254
255    rust_static_library(
256        name = "staticlib_has_native_dep",
257        srcs = ["lib_using_native_dep.rs"],
258        edition = "2018",
259        deps = [":native_dep"],
260    )
261
262    rust_shared_library(
263        name = "cdylib_has_native_dep",
264        srcs = ["lib_using_native_dep.rs"],
265        edition = "2018",
266        deps = [":native_dep"],
267    )
268
269    rust_proc_macro(
270        name = "proc_macro_has_native_dep",
271        srcs = ["proc_macro_using_native_dep.rs"],
272        edition = "2018",
273        deps = [":native_dep"],
274    )
275
276    rust_binary(
277        name = "bin_has_native_dep",
278        srcs = ["bin_using_native_dep.rs"],
279        edition = "2018",
280        deps = [":native_dep"],
281    )
282
283    rust_binary(
284        name = "bin_has_native_dep_and_alwayslink",
285        srcs = ["bin_using_native_dep.rs"],
286        edition = "2018",
287        deps = [":native_dep", ":alwayslink"],
288    )
289
290    cc_library(
291        name = "native_dep",
292        srcs = ["native_dep.cc"],
293        visibility = ["//test/unit:__subpackages__"],
294    )
295
296    cc_library(
297        name = "alwayslink",
298        srcs = ["alwayslink.cc"],
299        alwayslink = 1,
300    )
301
302    rust_shared_library(
303        name = "cdylib_has_native_dep_and_alwayslink",
304        srcs = ["lib_using_native_dep.rs"],
305        edition = "2018",
306        deps = [":native_dep", ":alwayslink"],
307    )
308
309    rlib_has_no_native_libs_test(
310        name = "rlib_has_no_native_libs_test",
311        target_under_test = ":rlib_has_no_native_dep",
312    )
313    staticlib_has_native_libs_test(
314        name = "staticlib_has_native_libs_test",
315        target_under_test = ":staticlib_has_native_dep",
316    )
317    cdylib_has_native_libs_test(
318        name = "cdylib_has_native_libs_test",
319        target_under_test = ":cdylib_has_native_dep",
320    )
321    proc_macro_has_native_libs_test(
322        name = "proc_macro_has_native_libs_test",
323        target_under_test = ":proc_macro_has_native_dep",
324    )
325    bin_has_native_libs_test(
326        name = "bin_has_native_libs_test",
327        target_under_test = ":bin_has_native_dep",
328    )
329    bin_has_native_dep_and_alwayslink_test(
330        name = "bin_has_native_dep_and_alwayslink_test",
331        target_under_test = ":bin_has_native_dep_and_alwayslink",
332    )
333    cdylib_has_native_dep_and_alwayslink_test(
334        name = "cdylib_has_native_dep_and_alwayslink_test",
335        target_under_test = ":cdylib_has_native_dep_and_alwayslink",
336    )
337
338def _linkopts_propagate_test_impl(ctx):
339    env = analysistest.begin(ctx)
340    tut = analysistest.target_under_test(env)
341    action = tut.actions[0]
342
343    # Ensure linkopts from direct (-Llinkoptdep1) and transitive
344    # (-Llinkoptdep2) dependencies are propagated.
345    # Consistently with cc rules, dependency linkopts take precedence over
346    # dependent linkopts (i.e. dependency linkopts appear later in the command
347    # line).
348
349    linkopt_args = _extract_linker_args(action.argv)
350    assert_list_contains_adjacent_elements(
351        env,
352        linkopt_args,
353        ["-Llinkoptdep1", "-Llinkoptdep2"],
354    )
355    return analysistest.end(env)
356
357linkopts_propagate_test = analysistest.make(_linkopts_propagate_test_impl)
358
359def _linkopts_test():
360    rust_binary(
361        name = "linkopts_rust_bin",
362        srcs = ["bin_using_native_dep.rs"],
363        edition = "2018",
364        deps = [":linkopts_native_dep_a"],
365    )
366
367    cc_library(
368        name = "linkopts_native_dep_a",
369        srcs = ["native_dep.cc"],
370        linkopts = ["-Llinkoptdep1"],
371        deps = [":linkopts_native_dep_b"],
372    )
373
374    cc_library(
375        name = "linkopts_native_dep_b",
376        linkopts = ["-Llinkoptdep2"],
377    )
378
379    linkopts_propagate_test(
380        name = "native_linkopts_propagate_test",
381        target_under_test = ":linkopts_rust_bin",
382    )
383
384def _check_additional_deps_test_impl(ctx, expect_additional_deps):
385    env = analysistest.begin(ctx)
386    tut = analysistest.target_under_test(env)
387    action = tut.actions[0]
388    additional_inputs = [inp.basename for inp in action.inputs.to_list()]
389    asserts.equals(env, "dynamic.lds" in additional_inputs, expect_additional_deps)
390    return analysistest.end(env)
391
392def _has_additional_deps_test_impl(ctx):
393    return _check_additional_deps_test_impl(ctx, expect_additional_deps = True)
394
395def _has_no_additional_deps_test_impl(ctx):
396    return _check_additional_deps_test_impl(ctx, expect_additional_deps = False)
397
398has_additional_deps_test = analysistest.make(_has_additional_deps_test_impl)
399has_no_additional_deps_test = analysistest.make(_has_no_additional_deps_test_impl)
400
401def _additional_deps_test():
402    rust_binary(
403        name = "bin_additional_deps",
404        srcs = ["bin_using_native_dep.rs"],
405        edition = "2018",
406        deps = [":additional_deps_cc"],
407    )
408
409    rust_shared_library(
410        name = "cdylib_additional_deps",
411        srcs = ["lib_using_native_dep.rs"],
412        edition = "2018",
413        deps = [":additional_deps_cc"],
414    )
415
416    rust_library(
417        name = "lib_additional_deps",
418        srcs = ["lib_using_native_dep.rs"],
419        edition = "2018",
420        deps = ["additional_deps_cc"],
421    )
422
423    cc_library(
424        name = "additional_deps_cc",
425        srcs = ["native_dep.cc"],
426        linkopts = ["-L$(execpath :dynamic.lds)"],
427        deps = [":dynamic.lds"],
428    )
429
430    has_additional_deps_test(
431        name = "bin_has_additional_deps_test",
432        target_under_test = ":bin_additional_deps",
433    )
434
435    has_additional_deps_test(
436        name = "cdylib_has_additional_deps_test",
437        target_under_test = ":cdylib_additional_deps",
438    )
439
440    has_no_additional_deps_test(
441        name = "lib_has_no_additional_deps_test",
442        target_under_test = ":lib_additional_deps",
443    )
444
445def native_deps_test_suite(name):
446    """Entry-point macro called from the BUILD file.
447
448    Args:
449        name: Name of the macro.
450    """
451    _native_dep_test()
452    _linkopts_test()
453    _additional_deps_test()
454
455    native.test_suite(
456        name = name,
457        tests = [
458            ":bin_has_additional_deps_test",
459            ":bin_has_native_dep_and_alwayslink_test",
460            ":bin_has_native_libs_test",
461            ":cdylib_has_additional_deps_test",
462            ":cdylib_has_native_dep_and_alwayslink_test",
463            ":cdylib_has_native_libs_test",
464            ":lib_has_no_additional_deps_test",
465            ":native_linkopts_propagate_test",
466            ":proc_macro_has_native_libs_test",
467            ":rlib_has_no_native_libs_test",
468            ":staticlib_has_native_libs_test",
469        ],
470    )
471