From a0018a46d813f2ddd39790de3aec51f8e8cff0c6 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Wed, 4 Feb 2026 15:25:37 -0800 Subject: [PATCH 1/3] apply https://github.com/bazelbuild/rules_rust/pull/2116 --- rust/private/rustc.bzl | 62 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 14b02dcfe3..a2a116ba11 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -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): @@ -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: @@ -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.direct_crates.to_list() + 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( @@ -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 From 8c6b6d791b8c6a5a5a622d034a9f67c8002eb3ba Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Thu, 5 Feb 2026 17:27:40 -0800 Subject: [PATCH 2/3] Fix tests --- test/unit/common.bzl | 15 ++++++++++++++ test/unit/exports/exports_test.bzl | 20 ++++++++++++++----- .../force_all_deps_direct_test.bzl | 5 ++--- test/unit/stamp/stamp_test.bzl | 10 ++++------ .../transitive_link_search_paths_test.bzl | 5 ++--- 5 files changed, 38 insertions(+), 17 deletions(-) diff --git a/test/unit/common.bzl b/test/unit/common.bzl index 4099e8dd13..bddc1e0a85 100644 --- a/test/unit/common.bzl +++ b/test/unit/common.bzl @@ -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( diff --git a/test/unit/exports/exports_test.bzl b/test/unit/exports/exports_test.bzl index a2557ff12d..470b3c6995 100644 --- a/test/unit/exports/exports_test.bzl +++ b/test/unit/exports/exports_test.bzl @@ -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( diff --git a/test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl b/test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl index 39e705aaf0..0a5adae89e 100644 --- a/test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl +++ b/test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl @@ -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, diff --git a/test/unit/stamp/stamp_test.bzl b/test/unit/stamp/stamp_test.bzl index 871808d38d..8896f43d82 100644 --- a/test/unit/stamp/stamp_test.bzl +++ b/test/unit/stamp/stamp_test.bzl @@ -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) @@ -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" @@ -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) @@ -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) diff --git a/test/unit/transitive_link_search_paths/transitive_link_search_paths_test.bzl b/test/unit/transitive_link_search_paths/transitive_link_search_paths_test.bzl index e23d9d0111..8fa30d3412 100644 --- a/test/unit/transitive_link_search_paths/transitive_link_search_paths_test.bzl +++ b/test/unit/transitive_link_search_paths/transitive_link_search_paths_test.bzl @@ -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) @@ -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") From c333ff01f4eaedd75ad72df457a7fee522999e17 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Fri, 6 Feb 2026 10:23:13 -0800 Subject: [PATCH 3/3] Fix Windows CI: rustdoc test crash and conflicting symlink actions - Use find_action_by_mnemonic for Rustdoc actions instead of assuming actions[0], since symlink actions now precede Rustdoc on Windows - Deduplicate symlink deps by using only transitive_crates (which already includes direct_crates) to avoid conflicting actions Co-Authored-By: Claude Opus 4.6 --- rust/private/rustc.bzl | 2 +- test/unit/rustdoc/rustdoc_unit_test.bzl | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index a2a116ba11..2ada47abcb 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -2141,7 +2141,7 @@ def windows_symlink_transitive_deps(ctx, crate_info, dep_info): symlink_deps = [] if crate_info.output == None: return [] - for dep in (dep_info.direct_crates.to_list() + dep_info.transitive_crates.to_list()): + for dep in dep_info.transitive_crates.to_list(): if hasattr(dep, "dep"): dep = dep.dep diff --git a/test/unit/rustdoc/rustdoc_unit_test.bzl b/test/unit/rustdoc/rustdoc_unit_test.bzl index aa1f9b2f02..c870d6ae75 100644 --- a/test/unit/rustdoc/rustdoc_unit_test.bzl +++ b/test/unit/rustdoc/rustdoc_unit_test.bzl @@ -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. @@ -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):