Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow rustc_compile_action to threat all dependencies as direct depenencies #970

Merged
merged 12 commits into from
Oct 14, 2021
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"):
hlopko marked this conversation as resolved.
Show resolved Hide resolved
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() {}