Skip to content

Commit

Permalink
ccinfo: when providing ccinfo, optionally include libstd and alloc
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
durin42 committed Mar 4, 2021
1 parent 2de6496 commit cc40e23
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 5 deletions.
4 changes: 4 additions & 0 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,10 @@ def establish_cc_info(ctx, crate_info, toolchain, cc_toolchain, feature_configur
if CcInfo in dep:
cc_infos.append(dep[CcInfo])

if crate_info.type in ("rlib", "lib") and toolchain.libstd_and_allocator_ccinfo:
# TODO: if we already have an rlib in our deps, we could skip this
cc_infos.append(toolchain.libstd_and_allocator_ccinfo)

return [cc_common.merge_cc_infos(cc_infos = cc_infos)]

def add_edition_flags(args, crate):
Expand Down
79 changes: 79 additions & 0 deletions rust/toolchain.bzl
Original file line number Diff line number Diff line change
@@ -1,5 +1,76 @@
"""The rust_toolchain rule definition and implementation."""

load(
"//rust/private:utils.bzl",
"find_cc_toolchain",
)

def _make_dota(ctx, f):
"""Add a symlink for a file that ends in .a, so it can be used as a staticlib.
Args:
ctx (ctx): The rule's context object.
f (File): The file to symlink.
Returns:
The symlink's File.
"""
dot_a = ctx.actions.declare_file(f.basename + ".a", sibling = f)
ctx.actions.symlink(output = dot_a, target_file = f)
return dot_a

def _make_libstd_and_allocator_ccinfo(ctx, rust_lib, allocator_library):
"""Make the CcInfo (if possible) for libstd and allocator libraries.
Args:
ctx (ctx): The rule's context object.
rust_lib: The rust standard library.
allocator_library: The target to use for providing allocator functions.
Returns:
A CcInfo object for the required libraries, or None if no such libraries are available.
"""
cc_toolchain, feature_configuration = find_cc_toolchain(ctx)
link_inputs = []
std_rlibs = [f for f in rust_lib.files.to_list() if f.basename.endswith(".rlib")]
if std_rlibs:
dot_a_files = [_make_dota(ctx, f) for f in std_rlibs]
link_inputs.append(cc_common.create_linker_input(
owner = rust_lib.label,
libraries = depset(
[
cc_common.create_library_to_link(
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
static_library = f,
pic_static_library = f,
)
for f in dot_a_files
],
),
))
if allocator_library:
link_inputs.append(cc_common.create_linker_input(
owner = allocator_library.allocator_library.label,
libraries = depset([
cc_common.create_library_to_link(
# TODO(augie): wat? this needs no actions
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
static_library = f,
)
for f in allocator_library.files.to_list()
]),
))
libstd_and_allocator_ccinfo = None
if link_inputs:
return CcInfo(linking_context = cc_common.create_linking_context(linker_inputs = depset(link_inputs)))
return None


def _rust_toolchain_impl(ctx):
"""The rust_toolchain implementation
Expand Down Expand Up @@ -38,11 +109,13 @@ def _rust_toolchain_impl(ctx):
default_edition = ctx.attr.default_edition,
compilation_mode_opts = compilation_mode_opts,
crosstool_files = ctx.files._crosstool,
libstd_and_allocator_ccinfo = _make_libstd_and_allocator_ccinfo(ctx, ctx.attr.rust_lib, ctx.attr.allocator_library),
)
return [toolchain]

rust_toolchain = rule(
implementation = _rust_toolchain_impl,
fragments = ["cpp"],
attrs = {
"binary_ext": attr.string(
doc = "The extension for binaries created from rustc.",
Expand Down Expand Up @@ -131,6 +204,12 @@ rust_toolchain = rule(
"_crosstool": attr.label(
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
),
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
"allocator_library": attr.label(
doc = "Target that provides allocator functions when rust_library targets are embedded in a cc_binary.",
),
},
doc = """Declares a Rust toolchain for use.
Expand Down
10 changes: 5 additions & 5 deletions test/unit/cc_info/cc_info_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_proc_macro", "rust_
def _is_dylib_on_windows(ctx):
return ctx.target_platform_has_constraint(ctx.attr._windows[platform_common.ConstraintValueInfo])

def _assert_cc_info_has_library_to_link(env, tut, type):
def _assert_cc_info_has_library_to_link(env, tut, type, ccinfo_count):
asserts.true(env, CcInfo in tut, "rust_library should provide CcInfo")
cc_info = tut[CcInfo]
linker_inputs = cc_info.linking_context.linker_inputs.to_list()
asserts.equals(env, len(linker_inputs), 1)
asserts.equals(env, len(linker_inputs), ccinfo_count)
library_to_link = linker_inputs[0].libraries[0]
asserts.equals(env, False, library_to_link.alwayslink)

Expand Down Expand Up @@ -42,7 +42,7 @@ def _assert_cc_info_has_library_to_link(env, tut, type):
def _rlib_provides_cc_info_test_impl(ctx):
env = analysistest.begin(ctx)
tut = analysistest.target_under_test(env)
_assert_cc_info_has_library_to_link(env, tut, "rlib")
_assert_cc_info_has_library_to_link(env, tut, "rlib", 2)
return analysistest.end(env)

def _bin_does_not_provide_cc_info_test_impl(ctx):
Expand All @@ -60,13 +60,13 @@ def _proc_macro_does_not_provide_cc_info_test_impl(ctx):
def _cdylib_provides_cc_info_test_impl(ctx):
env = analysistest.begin(ctx)
tut = analysistest.target_under_test(env)
_assert_cc_info_has_library_to_link(env, tut, "cdylib")
_assert_cc_info_has_library_to_link(env, tut, "cdylib", 1)
return analysistest.end(env)

def _staticlib_provides_cc_info_test_impl(ctx):
env = analysistest.begin(ctx)
tut = analysistest.target_under_test(env)
_assert_cc_info_has_library_to_link(env, tut, "staticlib")
_assert_cc_info_has_library_to_link(env, tut, "staticlib", 1)
return analysistest.end(env)

rlib_provides_cc_info_test = analysistest.make(_rlib_provides_cc_info_test_impl)
Expand Down

0 comments on commit cc40e23

Please sign in to comment.