fix(rustdoc): link doc tests against the cc_toolchain's runtime libs (#4220) `rust_doc_test` fails to link on any cc_toolchain that supplies its C++/unwind runtime via `static_runtime_lib` (i.e. enables `static_link_cpp_runtimes`). This happens when cross compiling with the hermetic-LLVM e.g.: ``` ld.lld: error: undefined symbol: _Unwind_Resume >>> referenced by alloc.rs:0 ... liballoc-*.rlib ... and the rest of the _Unwind_* family ``` `rust_library` / `rust_binary` / `rust_test` are fine. Only doc tests fail. ## Evidence The first commit adds only a test to reproduce the issue by extending the fixture from #3741 (which already declares a cc_toolchain with `static_runtime_lib = ":dummy.a"`); the second commit adds the fix: | commit | `//test/unit/cc_toolchain_runtime_lib:runtime_libs_test/doc_test` | |---|---| | `test(rustdoc): reproduce ...` | **FAILED** | | `fix(rustdoc): link doc tests ...` | **PASSED** | The failing action's argv shows the search path arriving while the library reference never does: ``` "-Lnative=test/unit/cc_toolchain_runtime_lib", <- present ... <- no -Clink-arg=-ldummy ``` ## Cause `add_native_link_flags` emits the runtime libs' `-Lnative=` unconditionally but gates the matching `-lstatic=` behind `include_link_flags`, which rustdoc sets to False (#2467). #4080 added a compensating `-Clink-arg=-l` loop for doc tests, but this does not work for a `static_runtime_lib`. ## Alternatives Injecting the flag from outside doesn't work. The archive is already in the doc test's runfiles (via #3741), so `-Clink-arg=<its short_path>` does fix doc tests. However, `toolchain.extra_rustc_flags` applies to every rustc invocation, and normal `rust_test` binaries link in the execroot, where that runfiles-relative path doesn't exist: `clang++: error: no such file or directory`. Doc tests and normal links need *different paths to the same file*, No single toolchain-level flag satisfies both. Per-target `rustdoc_flags` does work, but only by hardcoding the canonical repo name and the runtime lib's internal layout in every `rust_doc_test`. However, this is unmaintainable in any major project. ## Changes - `rustdoc.bzl` — extend the #4080 loop to the cc_toolchain runtime libs, mirroring the crate-type split in `collect_inputs`. Guarded on `cc_toolchain` being present (#3665). - `rustdoc_test.bzl` — add those libs' root to `--strip_substring`. Required: they're built in a different configuration than the crate outputs, so otherwise the `-Lnative=` path stays an execroot path that doesn't exist in the runfiles tree the doc test runs from. Only non-empty roots are added — source-file runtime libs need no stripping, and an empty root would emit `--strip_substring=/`, which `rustdoc_test_writer` applies as a plain `str::replace`. The libs already reach runfiles via `ctx.runfiles(transitive_files = action.inputs)` thanks to #3741. ## Testing `bazel test -- //... -//test/unit/remap_path_prefix:integration_test` on macOS: **569 passed / 35 skipped**, against **568 / 35** before. The new doc test is the only target that changed status; every other action was a cache hit, so this is a provable no-op where `static_runtime_lib` is empty. The patch has also been verified against a repo that builds with hermetic-LLVM and all failing doc tests passed. --------- Signed-off-by: Marvin Hansen <marvin.hansen@gmail.com>
This repository provides rules for building Rust projects with Bazel.
The fastest way to try this in an empty project is to click the green “Use this template” button on https://github.com/bazel-starters/rust.
General discussions and announcements take place in the GitHub Discussions, but there are additional places where community members gather to discuss rules_rust.
Please refer to the full documentation.