diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 474217e3d5f..c39c31477b4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -115,10 +115,8 @@ may also need to install additional tools for the target. For example, on Ubuntu you should install the `gcc-multilib` package. If you can't install an alternate target, you can set the -`CFG_DISABLE_CROSS_TESTS=1` environment variable to disable these tests. -Unfortunately 32-bit support on macOS is going away, so you may not be able to -run these tests on macOS. The Windows cross tests only support the MSVC -toolchain. +`CFG_DISABLE_CROSS_TESTS=1` environment variable to disable these tests. The +Windows cross tests only support the MSVC toolchain. Some of the nightly tests require the `rustc-dev` component installed. This component includes the compiler as a library. This may already be installed diff --git a/Cargo.toml b/Cargo.toml index ba661a07770..e349009b424 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo" -version = "0.43.0" +version = "0.43.1" edition = "2018" authors = ["Yehuda Katz ", "Carl Lerche ", diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 61d5c15699f..34f7df46067 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -26,12 +26,12 @@ jobs: - job: macOS pool: - vmImage: macos-10.13 + vmImage: macos-10.15 steps: - template: ci/azure-test-all.yml variables: TOOLCHAIN: stable - OTHER_TARGET: i686-apple-darwin + OTHER_TARGET: x86_64-apple-ios - job: Windows pool: diff --git a/ci/azure-install-rust.yml b/ci/azure-install-rust.yml index 60fbd2ffe84..f37b4a6ab6f 100644 --- a/ci/azure-install-rust.yml +++ b/ci/azure-install-rust.yml @@ -1,15 +1,4 @@ steps: - - bash: | - set -e - if command -v rustup; then - echo `command -v rustup` `rustup -V` already installed - rustup self update - else - curl -sSL https://sh.rustup.rs | sh -s -- -y --default-toolchain="$TOOLCHAIN" --profile=minimal - echo "##vso[task.prependpath]$HOME/.cargo/bin" - fi - displayName: Install rustup - - bash: | set -e rustup set profile minimal diff --git a/crates/cargo-test-support/src/cross_compile.rs b/crates/cargo-test-support/src/cross_compile.rs index 67281926e43..771a5228405 100644 --- a/crates/cargo-test-support/src/cross_compile.rs +++ b/crates/cargo-test-support/src/cross_compile.rs @@ -9,22 +9,26 @@ //! //! These tests are all disabled on rust-lang/rust's CI, but run in Cargo's CI. -use crate::{basic_bin_manifest, main_file, project}; +use crate::{basic_manifest, main_file, project}; +use cargo::util::ProcessError; +use cargo::CargoResult; use std::env; -use std::process::Command; +use std::fmt::Write; +use std::process::{Command, Output}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; +/// Whether or not the resulting cross binaries can run on the host. +static CAN_RUN_ON_HOST: AtomicBool = AtomicBool::new(false); + pub fn disabled() -> bool { - // First, disable if `./configure` requested so. + // First, disable if requested. match env::var("CFG_DISABLE_CROSS_TESTS") { Ok(ref s) if *s == "1" => return true, _ => {} } - // Right now, the Windows bots cannot cross compile due to the Mingw setup, - // so we disable ourselves on all but macOS/Linux setups where the rustc - // install script ensures we have both architectures. + // Cross tests are only tested to work on macos, linux, and MSVC windows. if !(cfg!(target_os = "macos") || cfg!(target_os = "linux") || cfg!(target_env = "msvc")) { return true; } @@ -32,29 +36,43 @@ pub fn disabled() -> bool { // It's not particularly common to have a cross-compilation setup, so // try to detect that before we fail a bunch of tests through no fault // of the user. - static CAN_RUN_CROSS_TESTS: AtomicBool = AtomicBool::new(false); + static CAN_BUILD_CROSS_TESTS: AtomicBool = AtomicBool::new(false); static CHECK: Once = Once::new(); let cross_target = alternate(); - CHECK.call_once(|| { + let run_cross_test = || -> CargoResult { let p = project() .at("cross_test") - .file("Cargo.toml", &basic_bin_manifest("cross_test")) - .file("src/cross_test.rs", &main_file(r#""testing!""#, &[])) + .file("Cargo.toml", &basic_manifest("cross_test", "1.0.0")) + .file("src/main.rs", &main_file(r#""testing!""#, &[])) .build(); - let result = p + let build_result = p .cargo("build --target") .arg(&cross_target) .exec_with_output(); + if build_result.is_ok() { + CAN_BUILD_CROSS_TESTS.store(true, Ordering::SeqCst); + } + + let result = p + .cargo("run --target") + .arg(&cross_target) + .exec_with_output(); + if result.is_ok() { - CAN_RUN_CROSS_TESTS.store(true, Ordering::SeqCst); + CAN_RUN_ON_HOST.store(true, Ordering::SeqCst); } + build_result + }; + + CHECK.call_once(|| { + drop(run_cross_test()); }); - if CAN_RUN_CROSS_TESTS.load(Ordering::SeqCst) { + if CAN_BUILD_CROSS_TESTS.load(Ordering::SeqCst) { // We were able to compile a simple project, so the user has the // necessary `std::` bits installed. Therefore, tests should not // be disabled. @@ -75,74 +93,134 @@ pub fn disabled() -> bool { } // We are responsible for warning the user, which we do by panicking. - let rustup_available = Command::new("rustup").output().is_ok(); - - let linux_help = if cfg!(target_os = "linux") { + let mut message = format!( " +Cannot cross compile to {}. -You may need to install runtime libraries for your Linux distribution as well." - .to_string() - } else { - "".to_string() - }; +This failure can be safely ignored. If you would prefer to not see this +failure, you can set the environment variable CFG_DISABLE_CROSS_TESTS to \"1\". - let rustup_help = if rustup_available { - format!( +Alternatively, you can install the necessary libraries to enable cross +compilation tests. Cross compilation tests depend on your host platform. +", + cross_target + ); + + if cfg!(target_os = "linux") { + message.push_str( " +Linux cross tests target i686-unknown-linux-gnu, which requires the ability to +build and run 32-bit targets. This requires the 32-bit libraries to be +installed. For example, on Ubuntu, run `sudo apt install gcc-multilib` to +install the necessary libraries. +", + ); + } else if cfg!(target_os = "macos") { + message.push_str( + " +macOS cross tests target x86_64-apple-ios, which requires the iOS SDK to be +installed. This should be included with Xcode automatically. If you are using +the Xcode command line tools, you'll need to install the full Xcode app (from +the Apple App Store), and switch to it with this command: -Alternatively, you can install the necessary libraries for cross-compilation with + sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer - rustup target add {}{}", - cross_target, linux_help - ) +Some cross-tests want to *run* the executables on the host. These tests will +be ignored if this is not possible. On macOS, this means you need an iOS +simulator installed to run these tests. To install a simulator, open Xcode, go +to preferences > Components, and download the latest iOS simulator. +", + ); + } else if cfg!(target_os = "windows") { + message.push_str( + " +Windows cross tests target i686-pc-windows-msvc, which requires the ability +to build and run 32-bit targets. This should work automatically if you have +properly installed Visual Studio build tools. +", + ); } else { - "".to_string() - }; + // The check at the top should prevent this. + panic!("platform should have been skipped"); + } - panic!( - "Cannot cross compile to {}. + let rustup_available = Command::new("rustup").output().is_ok(); + if rustup_available { + write!( + message, + " +Make sure that the appropriate `rustc` target is installed with rustup: -This failure can be safely ignored. If you would prefer to not see this -failure, you can set the environment variable CFG_DISABLE_CROSS_TESTS to \"1\".{} + rustup target add {} ", - cross_target, rustup_help - ); + cross_target + ) + .unwrap(); + } else { + write!( + message, + " +rustup does not appear to be installed. Make sure that the appropriate +`rustc` target is installed for the target `{}`. +", + cross_target + ) + .unwrap(); + } + + // Show the actual error message. + match run_cross_test() { + Ok(_) => message.push_str("\nUh oh, second run succeeded?\n"), + Err(err) => match err.downcast_ref::() { + Some(proc_err) => write!(message, "\nTest error: {}\n", proc_err).unwrap(), + None => write!(message, "\nUnexpected non-process error: {}\n", err).unwrap(), + }, + } + + panic!(message); } -pub fn alternate() -> String { - let platform = match env::consts::OS { - "linux" => "unknown-linux-gnu", - "macos" => "apple-darwin", - "windows" => "pc-windows-msvc", - _ => unreachable!(), - }; - let arch = match env::consts::ARCH { - "x86" => "x86_64", - "x86_64" => "i686", - _ => unreachable!(), - }; - format!("{}-{}", arch, platform) +/// The alternate target-triple to build with. +/// +/// Only use this function on tests that check `cross_compile::disabled`. +pub fn alternate() -> &'static str { + if cfg!(target_os = "macos") { + "x86_64-apple-ios" + } else if cfg!(target_os = "linux") { + "i686-unknown-linux-gnu" + } else if cfg!(all(target_os = "windows", target_env = "msvc")) { + "i686-pc-windows-msvc" + } else { + panic!("This test should be gated on cross_compile::disabled."); + } } pub fn alternate_arch() -> &'static str { - match env::consts::ARCH { - "x86" => "x86_64", - "x86_64" => "x86", - _ => unreachable!(), + if cfg!(target_os = "macos") { + "x86_64" + } else { + "x86" } } -pub fn host() -> String { - let platform = match env::consts::OS { - "linux" => "unknown-linux-gnu", - "macos" => "apple-darwin", - "windows" => "pc-windows-msvc", - _ => unreachable!(), - }; - let arch = match env::consts::ARCH { - "x86" => "i686", - "x86_64" => "x86_64", - _ => unreachable!(), - }; - format!("{}-{}", arch, platform) +/// Whether or not the host can run cross-compiled executables. +pub fn can_run_on_host() -> bool { + if disabled() { + return false; + } + // macos is currently configured to cross compile to x86_64-apple-ios + // which requires a simulator to run. Azure's CI image appears to have the + // SDK installed, but are not configured to launch iOS images with a + // simulator. + if cfg!(target_os = "macos") { + if CAN_RUN_ON_HOST.load(Ordering::SeqCst) { + return true; + } else { + println!("Note: Cannot run on host, skipping."); + return false; + } + } else { + assert!(CAN_RUN_ON_HOST.load(Ordering::SeqCst)); + return true; + } } diff --git a/crates/cargo-test-support/src/lib.rs b/crates/cargo-test-support/src/lib.rs index d6d63f4e830..5b348d72f5b 100644 --- a/crates/cargo-test-support/src/lib.rs +++ b/crates/cargo-test-support/src/lib.rs @@ -1741,6 +1741,10 @@ fn _process(t: &OsStr) -> cargo::util::ProcessBuilder { .env_remove("GIT_COMMITTER_NAME") .env_remove("GIT_COMMITTER_EMAIL") .env_remove("MSYSTEM"); // assume cmd.exe everywhere on windows + if cfg!(target_os = "macos") { + // Work-around a bug in macOS 10.15, see `link_or_copy` for details. + p.env("__CARGO_COPY_DONT_LINK_DO_NOT_USE_THIS", "1"); + } p } diff --git a/src/cargo/util/paths.rs b/src/cargo/util/paths.rs index 10f4cad9c5a..2fd6592646f 100644 --- a/src/cargo/util/paths.rs +++ b/src/cargo/util/paths.rs @@ -384,7 +384,20 @@ fn _link_or_copy(src: &Path, dst: &Path) -> CargoResult<()> { }; symlink(src, dst) } else { - fs::hard_link(src, dst) + if env::var_os("__CARGO_COPY_DONT_LINK_DO_NOT_USE_THIS").is_some() { + // This is a work-around for a bug in macOS 10.15. When running on + // APFS, there seems to be a strange race condition with + // Gatekeeper where it will forcefully kill a process launched via + // `cargo run` with SIGKILL. Copying seems to avoid the problem. + // This shouldn't affect anyone except Cargo's test suite because + // it is very rare, and only seems to happen under heavy load and + // rapidly creating lots of executables and running them. + // See /~https://github.com/rust-lang/cargo/issues/7821 for the + // gory details. + fs::copy(src, dst).map(|_| ()) + } else { + fs::hard_link(src, dst) + } }; link_result .or_else(|err| { diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs index 9faeefdfccd..b9b2f427cc5 100644 --- a/tests/testsuite/build_script.rs +++ b/tests/testsuite/build_script.rs @@ -3554,6 +3554,8 @@ fn rename_with_link_search_path() { } #[cargo_test] +// Don't have a cdylib cross target on macos. +#[cfg_attr(target_os = "macos", ignore)] fn rename_with_link_search_path_cross() { if cross_compile::disabled() { return; diff --git a/tests/testsuite/check.rs b/tests/testsuite/check.rs index 1a965a54997..27f37788f5f 100644 --- a/tests/testsuite/check.rs +++ b/tests/testsuite/check.rs @@ -64,7 +64,7 @@ fn check_fail() { foo.cargo("check") .with_status(101) - .with_stderr_contains("[..]this function takes 0 parameters but 1 parameter was supplied") + .with_stderr_contains("[..]this function takes 0[..]") .run(); } diff --git a/tests/testsuite/cross_compile.rs b/tests/testsuite/cross_compile.rs index 7407344a609..818f9472a0b 100644 --- a/tests/testsuite/cross_compile.rs +++ b/tests/testsuite/cross_compile.rs @@ -51,7 +51,9 @@ fn simple_cross() { p.cargo("build -v --target").arg(&target).run(); assert!(p.target_bin(&target, "foo").is_file()); - p.process(&p.target_bin(&target, "foo")).run(); + if cross_compile::can_run_on_host() { + p.process(&p.target_bin(&target, "foo")).run(); + } } #[cargo_test] @@ -110,7 +112,9 @@ fn simple_cross_config() { p.cargo("build -v").run(); assert!(p.target_bin(&target, "foo").is_file()); - p.process(&p.target_bin(&target, "foo")).run(); + if cross_compile::can_run_on_host() { + p.process(&p.target_bin(&target, "foo")).run(); + } } #[cargo_test] @@ -144,7 +148,9 @@ fn simple_deps() { p.cargo("build --target").arg(&target).run(); assert!(p.target_bin(&target, "foo").is_file()); - p.process(&p.target_bin(&target, "foo")).run(); + if cross_compile::can_run_on_host() { + p.process(&p.target_bin(&target, "foo")).run(); + } } #[cargo_test] @@ -292,7 +298,7 @@ fn plugin_with_extra_dylib_dep() { #[cargo_test] fn cross_tests() { - if cross_compile::disabled() { + if !cross_compile::can_run_on_host() { return; } @@ -382,7 +388,7 @@ fn no_cross_doctests() { p.cargo("test").with_stderr(&host_output).run(); println!("b"); - let target = cross_compile::host(); + let target = rustc_host(); p.cargo("test --target") .arg(&target) .with_stderr(&format!( @@ -398,13 +404,33 @@ fn no_cross_doctests() { println!("c"); let target = cross_compile::alternate(); - p.cargo("test --target") + + // This will build the library, but does not build or run doc tests. + // This should probably be a warning or error. + p.cargo("test -v --doc --target") + .arg(&target) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..] +[FINISHED] test [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + if !cross_compile::can_run_on_host() { + return; + } + + // This tests the library, but does not run the doc tests. + p.cargo("test -v --target") .arg(&target) .with_stderr(&format!( "\ [COMPILING] foo v0.0.1 ([CWD]) +[RUNNING] `rustc --crate-name foo [..]--test[..] [FINISHED] test [unoptimized + debuginfo] target(s) in [..] -[RUNNING] target/{triple}/debug/deps/foo-[..][EXE] +[RUNNING] `[CWD]/target/{triple}/debug/deps/foo-[..][EXE]` ", triple = target )) @@ -413,7 +439,7 @@ fn no_cross_doctests() { #[cargo_test] fn simple_cargo_run() { - if cross_compile::disabled() { + if !cross_compile::can_run_on_host() { return; } @@ -954,6 +980,8 @@ fn platform_specific_variables_reflected_in_build_scripts() { } #[cargo_test] +// Don't have a dylib cross target on macos. +#[cfg_attr(target_os = "macos", ignore)] fn cross_test_dylib() { if cross_compile::disabled() { return; diff --git a/tests/testsuite/metadata.rs b/tests/testsuite/metadata.rs index 8d9726de6ce..51e401e1d54 100644 --- a/tests/testsuite/metadata.rs +++ b/tests/testsuite/metadata.rs @@ -1,6 +1,5 @@ //! Tests for the `cargo metadata` command. -use cargo_test_support::cross_compile::alternate; use cargo_test_support::registry::Package; use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, main_file, project, rustc_host}; @@ -1911,6 +1910,9 @@ fn filter_platform() { Package::new("host-dep", "0.0.1").publish(); Package::new("alt-dep", "0.0.1").publish(); Package::new("cfg-dep", "0.0.1").publish(); + // Just needs to be a valid target that is different from host. + // Presumably nobody runs these tests on wasm. 🙃 + let alt_target = "wasm32-unknown-unknown"; let p = project() .file( "Cargo.toml", @@ -1933,7 +1935,7 @@ fn filter_platform() { cfg-dep = "0.0.1" "#, rustc_host(), - alternate() + alt_target ), ) .file("src/lib.rs", "") @@ -2177,7 +2179,7 @@ fn filter_platform() { "links": null } "# - .replace("$ALT_TRIPLE", &alternate()) + .replace("$ALT_TRIPLE", alt_target) .replace("$HOST_TRIPLE", &rustc_host()); // Normal metadata, no filtering, returns *everything*. @@ -2281,7 +2283,7 @@ fn filter_platform() { "workspace_root": "[..]/foo" } "# - .replace("$ALT_TRIPLE", &alternate()) + .replace("$ALT_TRIPLE", alt_target) .replace("$HOST_TRIPLE", &rustc_host()) .replace("$ALT_DEP", alt_dep) .replace("$CFG_DEP", cfg_dep) @@ -2293,7 +2295,7 @@ fn filter_platform() { // Filter on alternate, removes cfg and host. p.cargo("metadata --filter-platform") - .arg(alternate()) + .arg(alt_target) .with_json( &r#" { @@ -2355,7 +2357,7 @@ fn filter_platform() { "workspace_root": "[..]foo" } "# - .replace("$ALT_TRIPLE", &alternate()) + .replace("$ALT_TRIPLE", alt_target) .replace("$ALT_DEP", alt_dep) .replace("$NORMAL_DEP", normal_dep) .replace("$FOO", &foo), diff --git a/tests/testsuite/plugins.rs b/tests/testsuite/plugins.rs index a8ec68ffa1c..7bd5b7772ab 100644 --- a/tests/testsuite/plugins.rs +++ b/tests/testsuite/plugins.rs @@ -375,7 +375,7 @@ fn panic_abort_plugins() { "bar/src/lib.rs", r#" #![feature(rustc_private)] - extern crate syntax; + extern crate rustc_ast; "#, ) .build(); @@ -427,7 +427,7 @@ fn shared_panic_abort_plugins() { "bar/src/lib.rs", r#" #![feature(rustc_private)] - extern crate syntax; + extern crate rustc_ast; extern crate baz; "#, ) diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index a092002fcc8..38b674b9695 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -3664,6 +3664,8 @@ fn cargo_test_doctest_xcompile_ignores() { // -Zdoctest-xcompile is unstable return; } + // -Zdoctest-xcompile also enables --enable-per-target-ignores which + // allows the ignore-TARGET syntax. let p = project() .file("Cargo.toml", &basic_lib_manifest("foo")) .file( @@ -3713,7 +3715,7 @@ fn cargo_test_doctest_xcompile_ignores() { #[cargo_test] fn cargo_test_doctest_xcompile() { - if cross_compile::disabled() { + if !cross_compile::can_run_on_host() { return; } if !is_nightly() { @@ -3753,7 +3755,7 @@ fn cargo_test_doctest_xcompile() { #[cargo_test] fn cargo_test_doctest_xcompile_runner() { - if cross_compile::disabled() { + if !cross_compile::can_run_on_host() { return; } if !is_nightly() { @@ -3789,9 +3791,10 @@ fn cargo_test_doctest_xcompile_runner() { config, format!( r#" -[target.'cfg(target_arch = "x86")'] +[target.'cfg(target_arch = "{}")'] runner = "{}" "#, + cross_compile::alternate_arch(), runner_str ), ) @@ -3801,14 +3804,17 @@ runner = "{}" .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/lib.rs", - r#" - ///``` - ///assert!(cfg!(target_arch = "x86")); - ///``` - pub fn foo() -> u8 { - 4 - } - "#, + &format!( + r#" + ///``` + ///assert!(cfg!(target_arch = "{}")); + ///``` + pub fn foo() -> u8 {{ + 4 + }} + "#, + cross_compile::alternate_arch() + ), ) .build(); @@ -3830,7 +3836,7 @@ runner = "{}" #[cargo_test] fn cargo_test_doctest_xcompile_no_runner() { - if cross_compile::disabled() { + if !cross_compile::can_run_on_host() { return; } if !is_nightly() { @@ -3842,15 +3848,17 @@ fn cargo_test_doctest_xcompile_no_runner() { .file("Cargo.toml", &basic_lib_manifest("foo")) .file( "src/lib.rs", - r#" - - ///``` - ///assert!(cfg!(target_arch = "x86")); - ///``` - pub fn foo() -> u8 { - 4 - } - "#, + &format!( + r#" + ///``` + ///assert!(cfg!(target_arch = "{}")); + ///``` + pub fn foo() -> u8 {{ + 4 + }} + "#, + cross_compile::alternate_arch() + ), ) .build();