Skip to content

Commit cc40e23

Browse files
committed
ccinfo: when providing ccinfo, optionally include libstd and alloc
The new attribute on RustToolchain is the label of a target that provides __rust_realloc et al, which allows ld(1) to use the .rlib files directly without needing to involve rustc in the linking step. This means Rust and C++ can be mixed in a cc_binary freely without needing any staticlib-type crates, which avoids problems if you have a cc_binary -> rust_library -> cc_library -> rust_library situation.
1 parent 2de6496 commit cc40e23

File tree

3 files changed

+88
-5
lines changed

3 files changed

+88
-5
lines changed

rust/private/rustc.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,10 @@ def establish_cc_info(ctx, crate_info, toolchain, cc_toolchain, feature_configur
693693
if CcInfo in dep:
694694
cc_infos.append(dep[CcInfo])
695695

696+
if crate_info.type in ("rlib", "lib") and toolchain.libstd_and_allocator_ccinfo:
697+
# TODO: if we already have an rlib in our deps, we could skip this
698+
cc_infos.append(toolchain.libstd_and_allocator_ccinfo)
699+
696700
return [cc_common.merge_cc_infos(cc_infos = cc_infos)]
697701

698702
def add_edition_flags(args, crate):

rust/toolchain.bzl

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,76 @@
11
"""The rust_toolchain rule definition and implementation."""
22

3+
load(
4+
"//rust/private:utils.bzl",
5+
"find_cc_toolchain",
6+
)
7+
8+
def _make_dota(ctx, f):
9+
"""Add a symlink for a file that ends in .a, so it can be used as a staticlib.
10+
11+
Args:
12+
ctx (ctx): The rule's context object.
13+
f (File): The file to symlink.
14+
15+
Returns:
16+
The symlink's File.
17+
"""
18+
dot_a = ctx.actions.declare_file(f.basename + ".a", sibling = f)
19+
ctx.actions.symlink(output = dot_a, target_file = f)
20+
return dot_a
21+
22+
def _make_libstd_and_allocator_ccinfo(ctx, rust_lib, allocator_library):
23+
"""Make the CcInfo (if possible) for libstd and allocator libraries.
24+
25+
Args:
26+
ctx (ctx): The rule's context object.
27+
rust_lib: The rust standard library.
28+
allocator_library: The target to use for providing allocator functions.
29+
30+
31+
Returns:
32+
A CcInfo object for the required libraries, or None if no such libraries are available.
33+
"""
34+
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
35+
link_inputs = []
36+
std_rlibs = [f for f in rust_lib.files.to_list() if f.basename.endswith(".rlib")]
37+
if std_rlibs:
38+
dot_a_files = [_make_dota(ctx, f) for f in std_rlibs]
39+
link_inputs.append(cc_common.create_linker_input(
40+
owner = rust_lib.label,
41+
libraries = depset(
42+
[
43+
cc_common.create_library_to_link(
44+
actions = ctx.actions,
45+
feature_configuration = feature_configuration,
46+
cc_toolchain = cc_toolchain,
47+
static_library = f,
48+
pic_static_library = f,
49+
)
50+
for f in dot_a_files
51+
],
52+
),
53+
))
54+
if allocator_library:
55+
link_inputs.append(cc_common.create_linker_input(
56+
owner = allocator_library.allocator_library.label,
57+
libraries = depset([
58+
cc_common.create_library_to_link(
59+
# TODO(augie): wat? this needs no actions
60+
actions = ctx.actions,
61+
feature_configuration = feature_configuration,
62+
cc_toolchain = cc_toolchain,
63+
static_library = f,
64+
)
65+
for f in allocator_library.files.to_list()
66+
]),
67+
))
68+
libstd_and_allocator_ccinfo = None
69+
if link_inputs:
70+
return CcInfo(linking_context = cc_common.create_linking_context(linker_inputs = depset(link_inputs)))
71+
return None
72+
73+
374
def _rust_toolchain_impl(ctx):
475
"""The rust_toolchain implementation
576
@@ -38,11 +109,13 @@ def _rust_toolchain_impl(ctx):
38109
default_edition = ctx.attr.default_edition,
39110
compilation_mode_opts = compilation_mode_opts,
40111
crosstool_files = ctx.files._crosstool,
112+
libstd_and_allocator_ccinfo = _make_libstd_and_allocator_ccinfo(ctx, ctx.attr.rust_lib, ctx.attr.allocator_library),
41113
)
42114
return [toolchain]
43115

44116
rust_toolchain = rule(
45117
implementation = _rust_toolchain_impl,
118+
fragments = ["cpp"],
46119
attrs = {
47120
"binary_ext": attr.string(
48121
doc = "The extension for binaries created from rustc.",
@@ -131,6 +204,12 @@ rust_toolchain = rule(
131204
"_crosstool": attr.label(
132205
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
133206
),
207+
"_cc_toolchain": attr.label(
208+
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
209+
),
210+
"allocator_library": attr.label(
211+
doc = "Target that provides allocator functions when rust_library targets are embedded in a cc_binary.",
212+
),
134213
},
135214
doc = """Declares a Rust toolchain for use.
136215

test/unit/cc_info/cc_info_test.bzl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro", "rust_
66
def _is_dylib_on_windows(ctx):
77
return ctx.target_platform_has_constraint(ctx.attr._windows[platform_common.ConstraintValueInfo])
88

9-
def _assert_cc_info_has_library_to_link(env, tut, type):
9+
def _assert_cc_info_has_library_to_link(env, tut, type, ccinfo_count):
1010
asserts.true(env, CcInfo in tut, "rust_library should provide CcInfo")
1111
cc_info = tut[CcInfo]
1212
linker_inputs = cc_info.linking_context.linker_inputs.to_list()
13-
asserts.equals(env, len(linker_inputs), 1)
13+
asserts.equals(env, len(linker_inputs), ccinfo_count)
1414
library_to_link = linker_inputs[0].libraries[0]
1515
asserts.equals(env, False, library_to_link.alwayslink)
1616

@@ -42,7 +42,7 @@ def _assert_cc_info_has_library_to_link(env, tut, type):
4242
def _rlib_provides_cc_info_test_impl(ctx):
4343
env = analysistest.begin(ctx)
4444
tut = analysistest.target_under_test(env)
45-
_assert_cc_info_has_library_to_link(env, tut, "rlib")
45+
_assert_cc_info_has_library_to_link(env, tut, "rlib", 2)
4646
return analysistest.end(env)
4747

4848
def _bin_does_not_provide_cc_info_test_impl(ctx):
@@ -60,13 +60,13 @@ def _proc_macro_does_not_provide_cc_info_test_impl(ctx):
6060
def _cdylib_provides_cc_info_test_impl(ctx):
6161
env = analysistest.begin(ctx)
6262
tut = analysistest.target_under_test(env)
63-
_assert_cc_info_has_library_to_link(env, tut, "cdylib")
63+
_assert_cc_info_has_library_to_link(env, tut, "cdylib", 1)
6464
return analysistest.end(env)
6565

6666
def _staticlib_provides_cc_info_test_impl(ctx):
6767
env = analysistest.begin(ctx)
6868
tut = analysistest.target_under_test(env)
69-
_assert_cc_info_has_library_to_link(env, tut, "staticlib")
69+
_assert_cc_info_has_library_to_link(env, tut, "staticlib", 1)
7070
return analysistest.end(env)
7171

7272
rlib_provides_cc_info_test = analysistest.make(_rlib_provides_cc_info_test_impl)

0 commit comments

Comments
 (0)