diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 018792d2b13..017c41b8afd 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -13,6 +13,25 @@ use crate::core::compiler::{CompileKind, Metadata, Unit}; use crate::core::Package; use crate::util::{config, CargoResult, GlobalContext}; +/// Represents the kind of process we are creating. +#[derive(Debug)] +enum ToolKind { + /// See [`Compilation::rustc_process`]. + Rustc, + /// See [`Compilation::rustdoc_process`]. + Rustdoc, + /// See [`Compilation::host_process`]. + HostProcess, + /// See [`Compilation::target_process`]. + TargetProcess, +} + +impl ToolKind { + fn is_rustc_tool(&self) -> bool { + matches!(self, ToolKind::Rustc | ToolKind::Rustdoc) + } +} + /// Structure with enough information to run `rustdoc --test`. pub struct Doctest { /// What's being doctested @@ -176,7 +195,7 @@ impl<'gctx> Compilation<'gctx> { }; let cmd = fill_rustc_tool_env(rustc, unit); - self.fill_env(cmd, &unit.pkg, None, unit.kind, true) + self.fill_env(cmd, &unit.pkg, None, unit.kind, ToolKind::Rustc) } /// Returns a [`ProcessBuilder`] for running `rustdoc`. @@ -187,7 +206,7 @@ impl<'gctx> Compilation<'gctx> { ) -> CargoResult { let rustdoc = ProcessBuilder::new(&*self.gctx.rustdoc()?); let cmd = fill_rustc_tool_env(rustdoc, unit); - let mut cmd = self.fill_env(cmd, &unit.pkg, script_meta, unit.kind, true)?; + let mut cmd = self.fill_env(cmd, &unit.pkg, script_meta, unit.kind, ToolKind::Rustdoc)?; cmd.retry_with_argfile(true); unit.target.edition().cmd_edition_arg(&mut cmd); @@ -214,7 +233,7 @@ impl<'gctx> Compilation<'gctx> { pkg, None, CompileKind::Host, - false, + ToolKind::HostProcess, ) } @@ -249,7 +268,8 @@ impl<'gctx> Compilation<'gctx> { } else { ProcessBuilder::new(cmd) }; - let mut builder = self.fill_env(builder, pkg, script_meta, kind, false)?; + let tool_kind = ToolKind::TargetProcess; + let mut builder = self.fill_env(builder, pkg, script_meta, kind, tool_kind)?; if let Some(client) = self.gctx.jobserver_from_env() { builder.inherit_jobserver(client); @@ -269,10 +289,22 @@ impl<'gctx> Compilation<'gctx> { pkg: &Package, script_meta: Option, kind: CompileKind, - is_rustc_tool: bool, + tool_kind: ToolKind, ) -> CargoResult { let mut search_path = Vec::new(); - if is_rustc_tool { + if tool_kind.is_rustc_tool() { + if matches!(tool_kind, ToolKind::Rustdoc) { + // HACK: `rustdoc --test` not only compiles but executes doctests. + // Ideally only execution phase should have search paths appended, + // so the executions can find native libs just like other tests. + // However, there is no way to separate these two phase, so this + // hack is added for both phases. + // TODO: handle doctest-xcompile + search_path.extend(super::filter_dynamic_search_path( + self.native_dirs.iter(), + &self.root_output[&CompileKind::Host], + )); + } search_path.push(self.deps_output[&CompileKind::Host].clone()); } else { search_path.extend(super::filter_dynamic_search_path( diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index ae79e8df688..44a830134f5 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -7,6 +7,7 @@ use cargo_test_support::{ }; use cargo_test_support::{cross_compile, paths}; use cargo_test_support::{rustc_host, rustc_host_env, sleep_ms}; +use cargo_util::paths::dylib_path_envvar; use std::fs; #[cargo_test] @@ -2767,6 +2768,53 @@ fn only_test_docs() { .run(); } +#[cargo_test] +fn doctest_with_library_paths() { + let p = project(); + // Only link search directories within the target output directory are + // propagated through to dylib_path_envvar() (see #3366). + let dir1 = p.target_debug_dir().join("foo\\backslash"); + let dir2 = p.target_debug_dir().join("dir=containing=equal=signs"); + + let p = p + .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file( + "build.rs", + &format!( + r##" + fn main() {{ + println!(r#"cargo::rustc-link-search=native={}"#); + println!(r#"cargo::rustc-link-search={}"#); + }} + "##, + dir1.display(), + dir2.display() + ), + ) + .file( + "src/lib.rs", + &format!( + r##" + /// ``` + /// foo::assert_search_path(); + /// ``` + pub fn assert_search_path() {{ + let search_path = std::env::var_os("{}").unwrap(); + let paths = std::env::split_paths(&search_path).collect::>(); + assert!(paths.contains(&r#"{}"#.into())); + assert!(paths.contains(&r#"{}"#.into())); + }} + "##, + dylib_path_envvar(), + dir1.display(), + dir2.display() + ), + ) + .build(); + + p.cargo("test --doc").run(); +} + #[cargo_test] fn test_panic_abort_with_dep() { let p = project()