Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 53 additions & 9 deletions rust/private/rustc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,14 @@ def collect_inputs(
if build_env_file:
build_env_files = list(build_env_files)
build_env_files.append(build_env_file)
compile_inputs = depset(build_env_files + lint_files, transitive = [build_script_compile_inputs, compile_inputs])

# On Windows, symlink transitive deps to a single directory to avoid exceeding PATH length limits
# when loading proc macro DLLs. See https://github.com/rust-lang/rust/issues/79923
symlink_deps = []
if toolchain.target_os == "windows":
symlink_deps = windows_symlink_transitive_deps(ctx, crate_info, dep_info)

compile_inputs = depset(build_env_files + lint_files + symlink_deps, transitive = [build_script_compile_inputs, compile_inputs])
return compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs

def _will_emit_object_file(emit):
Expand Down Expand Up @@ -1156,7 +1163,7 @@ def construct_arguments(
use_metadata = _depend_on_metadata(crate_info, force_depend_on_objects)

# These always need to be added, even if not linking this crate.
add_crate_link_flags(rustc_flags, dep_info, force_all_deps_direct, use_metadata)
add_crate_link_flags(rustc_flags, dep_info, crate_info, force_all_deps_direct, use_metadata, toolchain.target_os == "windows")

needs_extern_proc_macro_flag = _is_proc_macro(crate_info) and crate_info.edition != "2015"
if needs_extern_proc_macro_flag:
Expand Down Expand Up @@ -2120,15 +2127,46 @@ def _get_dir_names(files):
dirs[f.dirname] = None
return dirs.keys()

def add_crate_link_flags(args, dep_info, force_all_deps_direct = False, use_metadata = False):
def windows_symlink_transitive_deps(ctx, crate_info, dep_info):
"""Symlinks in transitive dependencies to fix rustc bug on Windows: https://github.com/rust-lang/rust/issues/79923

Args:
ctx (ctx): The source rule's context object
crate_info (CrateInfo): A CrateInfo provider
dep_info (DepInfo): The current target's dependency info

Returns:
list: A list of all symlinked transitive dependencies
"""
symlink_deps = []
if crate_info.output == None:
return []
for dep in dep_info.transitive_crates.to_list():
if hasattr(dep, "dep"):
dep = dep.dep

if dep.output == None:
continue

symlink_dir = ctx.actions.declare_file("rlib_sym/" + dep.output.basename, sibling = crate_info.output)
ctx.actions.symlink(
output = symlink_dir,
target_file = dep.output,
)
symlink_deps.append(symlink_dir)
return symlink_deps

def add_crate_link_flags(args, dep_info, crate_info, force_all_deps_direct = False, use_metadata = False, for_windows = 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
crate_info (CrateInfo): A CrateInfo provider
force_all_deps_direct (bool, optional): Whether to pass the transitive rlibs with --extern
to the commandline as opposed to -L.
use_metadata (bool, optional): Build command line arguments using metadata for crates that provide it.
for_windows (bool, optional): Whether to add the rlib_sym directory to the linker search path.
"""

direct_crates = depset(
Expand All @@ -2141,12 +2179,18 @@ def add_crate_link_flags(args, dep_info, force_all_deps_direct = False, use_meta
crate_to_link_flags = _crate_to_link_flag_metadata if use_metadata else _crate_to_link_flag
args.add_all(direct_crates, uniquify = True, map_each = crate_to_link_flags)

args.add_all(
dep_info.transitive_crates,
map_each = _get_crate_dirname,
uniquify = True,
format_each = "-Ldependency=%s",
)
# crate_info.output is None when building rust_doc targets
if for_windows and crate_info.output != None:
args.add(
"-Ldependency={}".format(crate_info.output.dirname + "/rlib_sym"),
)
else:
args.add_all(
dep_info.transitive_crates,
map_each = _get_crate_dirname,
uniquify = True,
format_each = "-Ldependency=%s",
)

def _crate_to_link_flag_metadata(crate):
"""A helper macro used by `add_crate_link_flags` for adding crate link flags to a Arg object
Expand Down
15 changes: 15 additions & 0 deletions test/unit/common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ def assert_argv_contains_prefix_not(env, action, prefix):
),
)

def find_action_by_mnemonic(actions, mnemonic):
"""Find the first action with the given mnemonic.

Args:
actions (list): A list of actions.
mnemonic (str): The mnemonic to search for.

Returns:
Action: The first action matching the given mnemonic.
"""
for action in actions:
if action.mnemonic == mnemonic:
return action
fail("Failed to find action with mnemonic '{}' in actions: {}".format(mnemonic, actions))

def assert_action_mnemonic(env, action, mnemonic):
if not action.mnemonic == mnemonic:
unittest.fail(
Expand Down
20 changes: 15 additions & 5 deletions test/unit/exports/exports_test.bzl
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
"""Unittest to verify re-exported symbols propagate to downstream crates"""

load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load("//test/unit:common.bzl", "assert_argv_contains_prefix", "assert_argv_contains_prefix_suffix")
load("//test/unit:common.bzl", "assert_argv_contains_prefix", "assert_argv_contains_prefix_suffix", "find_action_by_mnemonic")

def _exports_test_impl(ctx, dependencies, externs):
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)

action = target.actions[0]
asserts.equals(env, action.mnemonic, "Rustc")
action = find_action_by_mnemonic(target.actions, "Rustc")

# Transitive symbols that get re-exported are expected to be located by a `-Ldependency` flag.
# The assert below ensures that each dependency flag is passed to the Rustc action. For details see
# https://doc.rust-lang.org/rustc/command-line-arguments.html#-l-add-a-directory-to-the-library-search-path
for dep in dependencies:
#
# On Windows, transitive deps are symlinked to a single rlib_sym directory
# rather than individual -Ldependency flags per dependency directory.
if any([a.startswith("-Ldependency") and a.endswith("/rlib_sym") for a in action.argv]):
assert_argv_contains_prefix_suffix(
env = env,
action = action,
prefix = "-Ldependency",
suffix = dep,
suffix = "rlib_sym",
)
else:
for dep in dependencies:
assert_argv_contains_prefix_suffix(
env = env,
action = action,
prefix = "-Ldependency",
suffix = dep,
)

for dep in externs:
assert_argv_contains_prefix(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

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:common.bzl", "assert_action_mnemonic", "assert_argv_contains_prefix", "find_action_by_mnemonic")
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]
assert_action_mnemonic(env, action, "Rustc")
action = find_action_by_mnemonic(tut.actions, "Rustc")
assert_argv_contains_prefix(
env,
action,
Expand Down
7 changes: 2 additions & 5 deletions test/unit/rustdoc/rustdoc_unit_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ load("//cargo:defs.bzl", "cargo_build_script")
load("//rust:defs.bzl", "rust_binary", "rust_doc", "rust_doc_test", "rust_library", "rust_proc_macro", "rust_test")
load(
"//test/unit:common.bzl",
"assert_action_mnemonic",
"assert_argv_contains",
"assert_argv_contains_prefix_not",
"find_action_by_mnemonic",
)

# TODO: `rust_doc_test` currently does not work on Windows.
Expand All @@ -19,10 +19,7 @@ NOT_WINDOWS = select({
})

def _get_rustdoc_action(env, tut):
actions = tut.actions
action = actions[0]
assert_action_mnemonic(env, action, "Rustdoc")

action = find_action_by_mnemonic(tut.actions, "Rustdoc")
return action

def _common_rustdoc_checks(env, tut):
Expand Down
10 changes: 4 additions & 6 deletions test/unit/stamp/stamp_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ load(
"assert_action_mnemonic",
"assert_argv_contains",
"assert_argv_contains_not",
"find_action_by_mnemonic",
)

_STAMP_ATTR_VALUES = (0, 1, -1)
Expand All @@ -30,8 +31,7 @@ def _stamp_build_flag_test_impl(ctx, flag_value):
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)

action = target.actions[0]
assert_action_mnemonic(env, action, "Rustc")
action = find_action_by_mnemonic(target.actions, "Rustc")

is_test = target[rust_common.crate_info].is_test
is_bin = target[rust_common.crate_info].type == "bin"
Expand Down Expand Up @@ -132,8 +132,7 @@ def _attribute_stamp_test_impl(ctx, attribute_value, build_flag_value):
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)

action = target.actions[0]
assert_action_mnemonic(env, action, "Rustc")
action = find_action_by_mnemonic(target.actions, "Rustc")

if attribute_value == 1:
_assert_stamped(env, action)
Expand Down Expand Up @@ -281,8 +280,7 @@ def _process_wrapper_with_stamp_test_impl(ctx):
env = analysistest.begin(ctx)
target = analysistest.target_under_test(env)

action = target.actions[0]
assert_action_mnemonic(env, action, "Rustc")
action = find_action_by_mnemonic(target.actions, "Rustc")

_assert_not_stamped(env, action)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load("//cargo:defs.bzl", "cargo_build_script")
load("//rust:defs.bzl", "rust_binary", "rust_common", "rust_library", "rust_proc_macro")
load("//test/unit:common.bzl", "assert_action_mnemonic", "assert_list_contains")
load("//test/unit:common.bzl", "assert_action_mnemonic", "assert_list_contains", "find_action_by_mnemonic")

def _transitive_link_search_paths_test_impl(ctx):
env = analysistest.begin(ctx)
Expand All @@ -22,8 +22,7 @@ transitive_link_search_paths_test = analysistest.make(_transitive_link_search_pa
def _transitive_out_dir_test_impl(ctx):
env = analysistest.begin(ctx)
tut = analysistest.target_under_test(env)
action = tut.actions[0]
assert_action_mnemonic(env, action, "Rustc")
action = find_action_by_mnemonic(tut.actions, "Rustc")
inputs = action.inputs.to_list()
input_basenames = [f.basename for f in inputs]
assert_list_contains(env, input_basenames, "dep_build_script.out_dir")
Expand Down