Repository: rust-lang/cargo (rust)

Issue:
Failed testing proc-macros built with `-Zbuild-std`
### Problem

When testing proc-macros with `-Zbuild-std`, the binary under test fails to load the standard library (at least on macOS):

```console
     Running unittests src/lib.rs (target/debug/deps/foo_proc_macro-0dc0d3e74fa08def)
dyld[99947]: Library not loaded: @rpath/libstd-7cbc1cc35ff1ddd0.dylib
  Referenced from: <E6DCD012-647E-354E-B422-B62E4D01C5BF> target/debug/deps/foo_proc_macro-0dc0d3e74fa08def
  Reason: tried: '$(pwd)/target/debug/deps/libstd-7cbc1cc35ff1ddd0.dylib' (no such file), '$(pwd)/target/debug/libstd-7cbc1cc35ff1ddd0.dylib' (no such file), '$HOME/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/libstd-7cbc1cc35ff1ddd0.dylib' (no such file), '$HOME/lib/libstd-7cbc1cc35ff1ddd0.dylib' (no such file), '/usr/local/lib/libstd-7cbc1cc35ff1ddd0.dylib' (no such file), '/usr/lib/libstd-7cbc1cc35ff1ddd0.dylib' (no such file, not in dyld cache)
error: test failed, to rerun pass `--lib`

Caused by:
  process didn't exit successfully: `$(pwd)/target/debug/deps/foo_proc_macro-0dc0d3e74fa08def` (signal: 6, SIGABRT: process abort signal)
```


### Steps

```sh
# Create basic proc-macro
cargo new foo-proc-macro --lib
cd foo-proc-macro/
echo "\n[lib]\nproc-macro = true" >> Cargo.toml
echo > src/lib.rs

# Test it with -Zbuild-std
cargo +nightly test --lib -Zbuild-std --target=aarch64-apple-darwin
```

### Notes

This started happening since https://github.com/rust-lang/rust/pull/131188, because now `$HOME/.rustup/toolchains/$TOOLCHAIN/lib` (which cargo is adding to the `DYLD_FALLBACK_LIBRARY_PATH`/`LD_LIBRARY_PATH`) no longer includes `libstd-*.dylib`, that is only present in `$HOME/.rustup/toolchains/$TOOLCHAIN-$HOST/lib/rustlib/$HOST/lib` (which Cargo normally adds, but doesn't add when using `-Zbuild-std`).

This was found in https://github.com/sonos/dinghy/issues/238.

### Possible Solution(s)

Not sure, perhaps the code that sets `DYLD_FALLBACK_LIBRARY_PATH`/`LD_LIBRARY_PATH` https://github.com/rust-lang/cargo/blob/80d82ca22abbee5fb7b51fa1abeb1ae34e99e88a/src/cargo/core/compiler/compilation.rs#L293-L339 needs to be adjusted? Or perhaps different handling when testing proc macros is needed altogether?

### Version

cargo 1.84.0-nightly (cf53cc54b 2024-10-18)
release: 1.84.0-nightly
commit-hash: cf53cc54bb593b5ec3dc2be4b1702f50c36d24d5
commit-date: 2024-10-18
host: aarch64-apple-darwin
libgit2: 1.8.1 (sys:0.19.0 vendored)
libcurl: 8.7.1 (sys:0.4.74+curl-8.9.0 system ssl:(SecureTransport) LibreSSL/3.3.6)
ssl: OpenSSL 1.1.1w  11 Sep 2023
os: Mac OS 14.7.0 [64-bit]


Pull request:
fix(build-std): always link to std when testing proc-macros
### What does this PR try to resolve?

Fixes #14735

https://github.com/rust-lang/rust/pull/131188 removes libstd.so from sysroot so `cargo test -Zbuild-std` no longer links to it. That results in a "Library not loaded: @rpath/libstd-[HASH].dylib" when testing a proc macro.

This is a pretty niche use case, though it can be easily reproduced by running `cargo test -Zbuild-std` in any proc-macro package. Like in [serde-rs/serde](https://github.com/serde-rs/serde/tree/b9dbfcb4ac3b7a663d9efc6eb1387c62302a6fb4) running it would fail.

This patch adds a special case that if it is `cargo run/test` against a proc-macro, fill in std dynamic library search path for it.



### How should we test and review this PR?

```
CARGO_RUN_BUILD_STD_TESTS=1 cargo +nightly t --test build-std test_proc_macro
```

or

```
git clone https://github.com/serde-rs/serde
cd serde
git switch -d b9dbfcb4ac3b7a663d9efc6eb1387c62302a6fb4
cargo +nightly t --test build-std
```

### Additional information


Patch:
diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs
index c31ee9ee4..7531f0fa6 100644
--- a/src/cargo/core/compiler/compilation.rs
+++ b/src/cargo/core/compiler/compilation.rs
@@ -315,7 +315,10 @@ impl<'gctx> Compilation<'gctx> {
             // libs from the sysroot that ships with rustc. This may not be
             // required (at least I cannot craft a situation where it
             // matters), but is here to be safe.
-            if self.gctx.cli_unstable().build_std.is_none() {
+            if self.gctx.cli_unstable().build_std.is_none() ||
+                // Proc macros dynamically link to std, so set it anyway.
+                pkg.proc_macro()
+            {
                 search_path.push(self.sysroot_target_libdir[&kind].clone());
             }
         }


Test patch:
diff --git a/tests/build-std/main.rs b/tests/build-std/main.rs
index a106b38b3..9906d0a89 100644
--- a/tests/build-std/main.rs
+++ b/tests/build-std/main.rs
@@ -360,3 +360,33 @@ fn remap_path_scope() {
         )
         .run();
 }
+
+#[cargo_test(build_std_real)]
+fn test_proc_macro() {
+    // See rust-lang/cargo#14735
+    let p = project()
+        .file(
+            "Cargo.toml",
+            r#"
+                [package]
+                name = "foo"
+                edition = "2021"
+
+                [lib]
+                proc-macro = true
+            "#,
+        )
+        .file("src/lib.rs", "")
+        .build();
+
+    p.cargo("test --lib")
+        .env_remove(cargo_util::paths::dylib_path_envvar())
+        .build_std()
+        .with_stderr_data(str![[r#"
+[COMPILING] foo v0.0.0 ([ROOT]/foo)
+[FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
+[RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH])
+
+"#]])
+        .run();
+}