Skip to content

Commit

Permalink
Allow rustc_compile_action to threat all dependencies as direct dep…
Browse files Browse the repository at this point in the history
…enencies (#970)
  • Loading branch information
scentini committed Oct 14, 2021
1 parent 4efa71d commit 6bf03f2
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 10 deletions.
49 changes: 39 additions & 10 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,8 @@ def construct_arguments(
out_dir,
build_env_files,
build_flags_files,
emit = ["dep-info", "link"]):
emit = ["dep-info", "link"],
force_all_deps_direct = False):
"""Builds an Args object containing common rustc flags
Args:
Expand All @@ -468,6 +469,8 @@ def construct_arguments(
build_env_files (list): Files containing rustc environment variables, for instance from `cargo_build_script` actions.
build_flags_files (list): The output files of a `cargo_build_script` actions containing rustc build flags
emit (list): Values for the --emit flag to rustc.
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
to the commandline as opposed to -L.
Returns:
tuple: A tuple of the following items
Expand Down Expand Up @@ -598,7 +601,7 @@ def construct_arguments(
_add_native_link_flags(rustc_flags, dep_info, linkstamp_outs, crate_info.type, toolchain, cc_toolchain, feature_configuration)

# These always need to be added, even if not linking this crate.
add_crate_link_flags(rustc_flags, dep_info)
add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct)

needs_extern_proc_macro_flag = "proc-macro" in [crate_info.type, crate_info.wrapped_crate_type] and \
crate_info.edition != "2015"
Expand Down Expand Up @@ -647,7 +650,8 @@ def rustc_compile_action(
crate_info,
output_hash = None,
rust_flags = [],
environ = {}):
environ = {},
force_all_deps_direct = False):
"""Create and run a rustc compile action based on the current rule's attributes
Args:
Expand All @@ -658,6 +662,8 @@ def rustc_compile_action(
output_hash (str, optional): The hashed path of the crate root. Defaults to None.
rust_flags (list, optional): Additional flags to pass to rustc. Defaults to [].
environ (dict, optional): A set of makefile expandable environment variables for the action
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
to the commandline as opposed to -L.
Returns:
list: A list of the following providers:
Expand Down Expand Up @@ -710,6 +716,7 @@ def rustc_compile_action(
out_dir = out_dir,
build_env_files = build_env_files,
build_flags_files = build_flags_files,
force_all_deps_direct = force_all_deps_direct,
)

if hasattr(attr, "version") and attr.version != "0.0.0":
Expand Down Expand Up @@ -962,33 +969,55 @@ def _get_dir_names(files):
dirs[f.dirname] = None
return dirs.keys()

def add_crate_link_flags(args, dep_info):
def add_crate_link_flags(args, dep_info, force_all_deps_direct = False):
"""Adds link flags to an Args object reference
Args:
args (Args): An arguments object reference
dep_info (DepInfo): The current target's dependency info
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
to the commandline as opposed to -L.
"""

# nb. Crates are linked via --extern regardless of their crate_type
args.add_all(dep_info.direct_crates, map_each = _crate_to_link_flag)
if force_all_deps_direct:
args.add_all(
depset(
transitive = [
dep_info.direct_crates,
dep_info.transitive_crates,
],
),
uniquify = True,
map_each = _crate_to_link_flag,
)
else:
# nb. Direct crates are linked via --extern regardless of their crate_type
args.add_all(dep_info.direct_crates, map_each = _crate_to_link_flag)
args.add_all(
dep_info.transitive_crates,
map_each = _get_crate_dirname,
uniquify = True,
format_each = "-Ldependency=%s",
)

def _crate_to_link_flag(crate_info):
def _crate_to_link_flag(crate):
"""A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
Args:
crate_info (CrateInfo): A CrateInfo provider from the current rule
crate (CrateInfo|AliasableDepInfo): A CrateInfo or an AliasableDepInfo provider
Returns:
list: Link flags for the current crate info
list: Link flags for the given provider
"""
return ["--extern={}={}".format(crate_info.name, crate_info.dep.output.path)]

# This is AliasableDepInfo, we should use the alias as a crate name
if hasattr(crate, "dep"):
name = crate.name
crate_info = crate.dep
else:
name = crate.name
crate_info = crate
return ["--extern={}={}".format(name, crate_info.output.path)]

def _get_crate_dirname(crate):
"""A helper macro used by `add_crate_link_flags` for getting the directory name of the current crate's output path
Expand Down
4 changes: 4 additions & 0 deletions test/unit/force_all_deps_direct/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
load(":force_all_deps_direct_test.bzl", "force_all_deps_direct_test_suite")

############################ UNIT TESTS #############################
force_all_deps_direct_test_suite(name = "force_all_deps_direct_test_suite")
5 changes: 5 additions & 0 deletions test/unit/force_all_deps_direct/direct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use transitive::transitive_fn;

pub fn direct_fn() {
transitive_fn();
}
61 changes: 61 additions & 0 deletions test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Unittest to verify that we can treat all dependencies as direct dependencies"""

load("@bazel_skylib//lib:unittest.bzl", "analysistest")
load("//rust:defs.bzl", "rust_library")
load("//test/unit:common.bzl", "assert_action_mnemonic", "assert_argv_contains_prefix")
load("//test/unit/force_all_deps_direct:generator.bzl", "generator")

def _force_all_deps_direct_rustc_flags_test(ctx):
env = analysistest.begin(ctx)
tut = analysistest.target_under_test(env)
action = tut.actions[1]
argv = action.argv
assert_action_mnemonic(env, action, "Rustc")
assert_argv_contains_prefix(
env,
action,
"--extern=transitive",
)
return analysistest.end(env)

force_all_deps_direct_test = analysistest.make(_force_all_deps_direct_rustc_flags_test)

def _force_all_deps_direct_test():
rust_library(
name = "direct",
srcs = ["direct.rs"],
edition = "2018",
deps = [":transitive"],
)

rust_library(
name = "transitive",
srcs = ["transitive.rs"],
edition = "2018",
)

generator(
name = "generate",
deps = [":direct"],
tags = ["noclippy"],
)

force_all_deps_direct_test(
name = "force_all_deps_direct_rustc_flags_test",
target_under_test = ":generate",
)

def force_all_deps_direct_test_suite(name):
"""Entry-point macro called from the BUILD file.
Args:
name: Name of the macro.
"""
_force_all_deps_direct_test()

native.test_suite(
name = name,
tests = [
":force_all_deps_direct_rustc_flags_test",
],
)
92 changes: 92 additions & 0 deletions test/unit/force_all_deps_direct/generator.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""A custom rule that threats all its dependencies as direct dependencies."""

# buildifier: disable=bzl-visibility
load("//rust/private:common.bzl", "rust_common")

# buildifier: disable=bzl-visibility
load("//rust/private:providers.bzl", "BuildInfo", "CrateInfo", "DepInfo", "DepVariantInfo")

# buildifier: disable=bzl-visibility
load("//rust/private:rustc.bzl", "rustc_compile_action")

def _generator_impl(ctx):
rs_file = ctx.actions.declare_file(ctx.label.name + "_generated.rs")
ctx.actions.run_shell(
outputs = [rs_file],
command = """cat <<EOF > {}
use direct::direct_fn;
use transitive::transitive_fn;
pub fn call_both() {}
direct_fn();
transitive_fn();
{}
EOF
""".format(rs_file.path, "{", "}"),
mnemonic = "WriteRsFile",
)

toolchain = ctx.toolchains[Label("//rust:toolchain")]

# Determine unique hash for this rlib
output_hash = repr(hash(rs_file.path))
crate_name = ctx.label.name
crate_type = "rlib"

rust_lib_name = "{prefix}{name}-{lib_hash}{extension}".format(
prefix = "lib",
name = crate_name,
lib_hash = output_hash,
extension = ".rlib",
)

deps = [DepVariantInfo(
crate_info = dep[CrateInfo] if CrateInfo in dep else None,
dep_info = dep[DepInfo] if DepInfo in dep else None,
build_info = dep[BuildInfo] if BuildInfo in dep else None,
cc_info = dep[CcInfo] if CcInfo in dep else None,
) for dep in ctx.attr.deps]

rust_lib = ctx.actions.declare_file(rust_lib_name)
return rustc_compile_action(
ctx = ctx,
attr = ctx.attr,
toolchain = toolchain,
crate_info = rust_common.create_crate_info(
name = crate_name,
type = crate_type,
root = rs_file,
srcs = depset([rs_file]),
deps = depset(deps),
proc_macro_deps = depset([]),
aliases = {},
output = rust_lib,
owner = ctx.label,
edition = "2018",
compile_data = depset([]),
rustc_env = {},
is_test = False,
),
output_hash = output_hash,
force_all_deps_direct = True,
)

generator = rule(
implementation = _generator_impl,
attrs = {
"deps": attr.label_list(),
"_cc_toolchain": attr.label(
default = "@bazel_tools//tools/cpp:current_cc_toolchain",
),
"_error_format": attr.label(default = "@rules_rust//:error_format"),
"_process_wrapper": attr.label(
default = Label("@rules_rust//util/process_wrapper"),
executable = True,
allow_single_file = True,
cfg = "exec",
),
},
toolchains = ["@rules_rust//rust:toolchain", "@bazel_tools//tools/cpp:toolchain_type"],
incompatible_use_toolchain_transition = True,
fragments = ["cpp"],
)
1 change: 1 addition & 0 deletions test/unit/force_all_deps_direct/transitive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub fn transitive_fn() {}

0 comments on commit 6bf03f2

Please sign in to comment.