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>
3 files changed
tree: b2166b5503fe38c3f68ed130133b41bf864d144f
  1. .bazelci/
  2. .bcr/
  3. .github/
  4. cargo/
  5. crate_universe/
  6. docs/
  7. examples/
  8. extensions/
  9. ffi/
  10. nix/
  11. rust/
  12. test/
  13. tools/
  14. util/
  15. .bazelignore
  16. .bazelrc
  17. .clang-format
  18. .clippy.toml
  19. .envrc
  20. .gitattributes
  21. .gitignore
  22. .pre-commit-config.yaml
  23. .prettierrc.toml
  24. .rustfmt.toml
  25. .typos.toml
  26. ARCHITECTURE.md
  27. AUTHORS
  28. BUILD.bazel
  29. CODEOWNERS
  30. COMPATIBILITY.md
  31. CONTRIBUTING.md
  32. CONTRIBUTORS
  33. LICENSE.txt
  34. MODULE.bazel
  35. README.md
  36. version.bzl
  37. WORKSPACE.bazel
README.md

Rust Rules

  • Postsubmit Build status

Overview

This repository provides rules for building Rust projects with Bazel.

Starter repo

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.

Community

General discussions and announcements take place in the GitHub Discussions, but there are additional places where community members gather to discuss rules_rust.

Documentation

Please refer to the full documentation.