diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index dcd2e1923bf..35dc7e26656 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -163,7 +163,7 @@ impl<'cfg> Compilation<'cfg> { self.fill_env(process(cmd), pkg, true) } - fn target_runner(&self) -> &Option<(PathBuf, Vec)> { + pub fn target_runner(&self) -> &Option<(PathBuf, Vec)> { &self.target_runner } diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 0e0ffb3fe6f..dd24951bbd1 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -336,6 +336,7 @@ pub struct CliUnstable { pub binary_dep_depinfo: bool, pub build_std: Option>, pub timings: Option>, + pub doctest_xcompile: bool, } impl CliUnstable { @@ -393,6 +394,7 @@ impl CliUnstable { self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v)) } "timings" => self.timings = Some(parse_timings(v)), + "doctest-xcompile" => self.doctest_xcompile = true, _ => failure::bail!("unknown `-Z` flag specified: {}", k), } diff --git a/src/cargo/ops/cargo_test.rs b/src/cargo/ops/cargo_test.rs index 1079df47efa..f49821cdfef 100644 --- a/src/cargo/ops/cargo_test.rs +++ b/src/cargo/ops/cargo_test.rs @@ -140,8 +140,14 @@ fn run_doc_tests( let mut errors = Vec::new(); let config = options.compile_opts.config; - // We don't build/run doc tests if `target` does not equal `host`. - if compilation.host != compilation.target { + // The unstable doctest-xcompile feature enables both per-target-ignores and + // cross-compiling doctests. As a side effect, this feature also gates running + // doctests with runtools when target == host. + let doctest_xcompile = config.cli_unstable().doctest_xcompile; + let mut runtool: &Option<(std::path::PathBuf, Vec)> = &None; + if doctest_xcompile { + runtool = compilation.target_runner(); + } else if compilation.host != compilation.target { return Ok((Test::Doc, errors)); } @@ -158,6 +164,19 @@ fn run_doc_tests( .arg("--crate-name") .arg(&target.crate_name()); + if doctest_xcompile { + p.arg("--target").arg(&compilation.target); + p.arg("-Zunstable-options"); + p.arg("--enable-per-target-ignores"); + } + + runtool.as_ref().map(|(runtool, runtool_args)| { + p.arg("--runtool").arg(runtool); + for arg in runtool_args { + p.arg("--runtool-arg").arg(arg); + } + }); + for &rust_dep in &[&compilation.deps_output] { let mut arg = OsString::from("dependency="); arg.push(rust_dep); @@ -194,7 +213,6 @@ fn run_doc_tests( if let Some(flags) = compilation.rustdocflags.get(&package.package_id()) { p.args(flags); } - config .shell() .verbose(|shell| shell.status("Running", p.to_string()))?; diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index c61924577b7..16539b86115 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -8,6 +8,7 @@ use cargo_test_support::registry::Package; use cargo_test_support::{ basic_bin_manifest, basic_lib_manifest, basic_manifest, cargo_exe, project, }; +use cargo_test_support::{cross_compile, is_nightly, paths}; use cargo_test_support::{rustc_host, sleep_ms}; #[cargo_test] @@ -3660,3 +3661,232 @@ fn test_dep_with_dev() { ) .run(); } + +#[cargo_test] +fn cargo_test_doctest_xcompile_ignores() { + if !is_nightly() { + return; + } + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + r#" + ///```ignore-x86_64 + ///assert!(cfg!(not(target_arch = "x86_64"))); + ///``` + pub fn foo() -> u8 { + 4 + } + "#, + ) + .build(); + + p.cargo("build").run(); + #[cfg(not(target_arch = "x86_64"))] + p.cargo("test") + .with_stdout_contains( + "\ + test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\ + ", + ) + .run(); + #[cfg(target_arch = "x86_64")] + p.cargo("test") + .with_status(101) + .with_stdout_contains( + "\ + test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out\ + ", + ) + .run(); + + #[cfg(not(target_arch = "x86_64"))] + p.cargo("test -Zdoctest-xcompile") + .masquerade_as_nightly_cargo() + .with_stdout_contains( + "\ + test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\ + ", + ) + .run(); + + #[cfg(target_arch = "x86_64")] + p.cargo("test -Zdoctest-xcompile") + .masquerade_as_nightly_cargo() + .with_stdout_contains( + "\ + test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out\ + ", + ) + .run(); +} + +#[cargo_test] +fn cargo_test_doctest_xcompile() { + if !is_nightly() { + return; + } + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + r#" + + ///``` + ///assert!(1 == 1); + ///``` + pub fn foo() -> u8 { + 4 + } + "#, + ) + .build(); + + p.cargo("build").run(); + p.cargo(&format!("test --target {}", cross_compile::alternate())) + .with_stdout_contains( + "\ + running 0 tests\ + ", + ) + .run(); + p.cargo(&format!( + "test --target {} -Zdoctest-xcompile", + cross_compile::alternate() + )) + .masquerade_as_nightly_cargo() + .with_stdout_contains( + "\ + test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\ + ", + ) + .run(); +} + +#[cargo_test] +fn cargo_test_doctest_xcompile_runner() { + use std::fs; + if !is_nightly() { + return; + } + + let runner = project() + .file("Cargo.toml", &basic_bin_manifest("runner")) + .file( + "src/main.rs", + r#" + pub fn main() { + eprintln!("this is a runner"); + let args: Vec = std::env::args().collect(); + std::process::Command::new(&args[1]).spawn(); + } + "#, + ) + .build(); + + runner.cargo("build").run(); + assert!(runner.bin("runner").is_file()); + let runner_path = paths::root().join("runner"); + fs::copy(&runner.bin("runner"), &runner_path).unwrap(); + + let config = paths::root().join(".cargo/config"); + + fs::create_dir_all(config.parent().unwrap()).unwrap(); + File::create(config) + .unwrap() + .write_all( + format!( + r#" +[target.'cfg(target_arch = "x86")'] +runner = "{}" +"#, + runner_path.to_str().unwrap() + ) + .as_bytes(), + ) + .unwrap(); + + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + r#" + ///``` + ///assert!(cfg!(target_arch = "x86")); + ///``` + pub fn foo() -> u8 { + 4 + } + "#, + ) + .build(); + + p.cargo("build").run(); + p.cargo(&format!("test --target {}", cross_compile::alternate())) + .with_stdout_contains( + "\ + running 0 tests\ + ", + ) + .run(); + p.cargo(&format!( + "test --target {} -Zdoctest-xcompile", + cross_compile::alternate() + )) + .masquerade_as_nightly_cargo() + .with_stdout_contains( + "\ + test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\ + ", + ) + .with_stderr_contains( + "\ + this is a runner\ + ", + ) + .run(); +} + +#[cargo_test] +fn cargo_test_doctest_xcompile_no_runner() { + if !is_nightly() { + return; + } + + let p = project() + .file("Cargo.toml", &basic_lib_manifest("foo")) + .file( + "src/lib.rs", + r#" + + ///``` + ///assert!(cfg!(target_arch = "x86")); + ///``` + pub fn foo() -> u8 { + 4 + } + "#, + ) + .build(); + + p.cargo("build").run(); + p.cargo(&format!("test --target {}", cross_compile::alternate())) + .with_stdout_contains( + "\ + running 0 tests\ + ", + ) + .run(); + p.cargo(&format!( + "test --target {} -Zdoctest-xcompile", + cross_compile::alternate() + )) + .masquerade_as_nightly_cargo() + .with_stdout_contains( + "\ + test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\ + ", + ) + .run(); +}