From 9835e2ae736235810b4ea1c162ca5e65c547e770 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:50 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- src/tools/cargo/tests/testsuite/rustup.rs | 262 ++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 src/tools/cargo/tests/testsuite/rustup.rs (limited to 'src/tools/cargo/tests/testsuite/rustup.rs') diff --git a/src/tools/cargo/tests/testsuite/rustup.rs b/src/tools/cargo/tests/testsuite/rustup.rs new file mode 100644 index 000000000..aa93f546c --- /dev/null +++ b/src/tools/cargo/tests/testsuite/rustup.rs @@ -0,0 +1,262 @@ +//! Tests for Cargo's behavior under Rustup. + +use cargo_test_support::paths::{home, root, CargoPathExt}; +use cargo_test_support::{cargo_process, process, project}; +use std::env; +use std::env::consts::EXE_EXTENSION; +use std::ffi::OsString; +use std::fs; +use std::path::{Path, PathBuf}; + +/// Helper to generate an executable. +fn make_exe(dest: &Path, name: &str, contents: &str, env: &[(&str, PathBuf)]) -> PathBuf { + let rs_name = format!("{name}.rs"); + fs::write( + root().join(&rs_name), + &format!("fn main() {{ {contents} }}"), + ) + .unwrap(); + let mut pb = process("rustc"); + env.iter().for_each(|(key, value)| { + pb.env(key, value); + }); + pb.arg("--edition=2021") + .arg(root().join(&rs_name)) + .exec() + .unwrap(); + let exe = Path::new(name).with_extension(EXE_EXTENSION); + let output = dest.join(&exe); + fs::rename(root().join(&exe), &output).unwrap(); + output +} + +fn prepend_path(path: &Path) -> OsString { + let mut paths = vec![path.to_path_buf()]; + paths.extend(env::split_paths(&env::var_os("PATH").unwrap_or_default())); + env::join_paths(paths).unwrap() +} + +struct RustupEnvironment { + /// Path for ~/.cargo/bin + cargo_bin: PathBuf, + /// Path for ~/.rustup + rustup_home: PathBuf, + /// Path to the cargo executable in the toolchain directory + /// (~/.rustup/toolchain/test-toolchain/bin/cargo.exe). + cargo_toolchain_exe: PathBuf, +} + +/// Creates an executable which prints a message and then runs the *real* rustc. +fn real_rustc_wrapper(bin_dir: &Path, message: &str) -> PathBuf { + let real_rustc = cargo_util::paths::resolve_executable("rustc".as_ref()).unwrap(); + // The toolchain rustc needs to call the real rustc. In order to do that, + // it needs to restore or clear the RUSTUP environment variables so that + // if rustup is installed, it will call the correct rustc. + let rustup_toolchain_setup = match std::env::var_os("RUSTUP_TOOLCHAIN") { + Some(t) => format!( + ".env(\"RUSTUP_TOOLCHAIN\", \"{}\")", + t.into_string().unwrap() + ), + None => format!(".env_remove(\"RUSTUP_TOOLCHAIN\")"), + }; + let mut env = vec![("CARGO_RUSTUP_TEST_real_rustc", real_rustc)]; + let rustup_home_setup = match std::env::var_os("RUSTUP_HOME") { + Some(h) => { + env.push(("CARGO_RUSTUP_TEST_RUSTUP_HOME", h.into())); + format!(".env(\"RUSTUP_HOME\", env!(\"CARGO_RUSTUP_TEST_RUSTUP_HOME\"))") + } + None => format!(".env_remove(\"RUSTUP_HOME\")"), + }; + make_exe( + bin_dir, + "rustc", + &format!( + r#" + eprintln!("{message}"); + let r = std::process::Command::new(env!("CARGO_RUSTUP_TEST_real_rustc")) + .args(std::env::args_os().skip(1)) + {rustup_toolchain_setup} + {rustup_home_setup} + .status(); + std::process::exit(r.unwrap().code().unwrap_or(2)); + "# + ), + &env, + ) +} + +/// Creates a simulation of a rustup environment with `~/.cargo/bin` and +/// `~/.rustup` directories populated with some executables that simulate +/// rustup. +fn simulated_rustup_environment() -> RustupEnvironment { + // Set up ~/.rustup/toolchains/test-toolchain/bin with a custom rustc and cargo. + let rustup_home = home().join(".rustup"); + let toolchain_bin = rustup_home + .join("toolchains") + .join("test-toolchain") + .join("bin"); + toolchain_bin.mkdir_p(); + let rustc_toolchain_exe = real_rustc_wrapper(&toolchain_bin, "real rustc running"); + let cargo_toolchain_exe = make_exe( + &toolchain_bin, + "cargo", + r#"panic!("cargo toolchain should not be called");"#, + &[], + ); + + // Set up ~/.cargo/bin with a typical set of rustup proxies. + let cargo_bin = home().join(".cargo").join("bin"); + cargo_bin.mkdir_p(); + + let rustc_proxy = make_exe( + &cargo_bin, + "rustc", + &format!( + r#" + match std::env::args().next().unwrap().as_ref() {{ + "rustc" => {{}} + arg => panic!("proxy only supports rustc, got {{arg:?}}"), + }} + eprintln!("rustc proxy running"); + let r = std::process::Command::new(env!("CARGO_RUSTUP_TEST_rustc_toolchain_exe")) + .args(std::env::args_os().skip(1)) + .status(); + std::process::exit(r.unwrap().code().unwrap_or(2)); + "# + ), + &[("CARGO_RUSTUP_TEST_rustc_toolchain_exe", rustc_toolchain_exe)], + ); + fs::hard_link( + &rustc_proxy, + cargo_bin.join("cargo").with_extension(EXE_EXTENSION), + ) + .unwrap(); + fs::hard_link( + &rustc_proxy, + cargo_bin.join("rustup").with_extension(EXE_EXTENSION), + ) + .unwrap(); + + RustupEnvironment { + cargo_bin, + rustup_home, + cargo_toolchain_exe, + } +} + +#[cargo_test] +fn typical_rustup() { + // Test behavior under a typical rustup setup with a normal toolchain. + let RustupEnvironment { + cargo_bin, + rustup_home, + cargo_toolchain_exe, + } = simulated_rustup_environment(); + + // Set up a project and run a normal cargo build. + let p = project().file("src/lib.rs", "").build(); + // The path is modified so that cargo will call `rustc` from + // `~/.cargo/bin/rustc to use our custom rustup proxies. + let path = prepend_path(&cargo_bin); + p.cargo("check") + .env("RUSTUP_TOOLCHAIN", "test-toolchain") + .env("RUSTUP_HOME", &rustup_home) + .env("PATH", &path) + .with_stderr( + "\ +[CHECKING] foo v0.0.1 [..] +real rustc running +[FINISHED] [..] +", + ) + .run(); + + // Do a similar test, but with a toolchain link that does not have cargo + // (which normally would do a fallback to nightly/beta/stable). + cargo_toolchain_exe.rm_rf(); + p.build_dir().rm_rf(); + + p.cargo("check") + .env("RUSTUP_TOOLCHAIN", "test-toolchain") + .env("RUSTUP_HOME", &rustup_home) + .env("PATH", &path) + .with_stderr( + "\ +[CHECKING] foo v0.0.1 [..] +real rustc running +[FINISHED] [..] +", + ) + .run(); +} + +// This doesn't work on Windows because Cargo forces the PATH to contain the +// sysroot_libdir, which is actually `bin`, preventing the test from +// overriding the bin directory. +#[cargo_test(ignore_windows = "PATH can't be overridden on Windows")] +fn custom_calls_other_cargo() { + // Test behavior when a custom subcommand tries to manipulate PATH to use + // a different toolchain. + let RustupEnvironment { + cargo_bin, + rustup_home, + cargo_toolchain_exe: _, + } = simulated_rustup_environment(); + + // Create a directory with a custom toolchain (outside of the rustup universe). + let custom_bin = root().join("custom-bin"); + custom_bin.mkdir_p(); + // `cargo` points to the real cargo. + let cargo_exe = cargo_test_support::cargo_exe(); + fs::hard_link(&cargo_exe, custom_bin.join(cargo_exe.file_name().unwrap())).unwrap(); + // `rustc` executes the real rustc. + real_rustc_wrapper(&custom_bin, "custom toolchain rustc running"); + + // A project that cargo-custom will try to build. + let p = project().file("src/lib.rs", "").build(); + + // Create a custom cargo subcommand. + // This will modify PATH to a custom toolchain and call cargo from that. + make_exe( + &cargo_bin, + "cargo-custom", + r#" + use std::env; + use std::process::Command; + + eprintln!("custom command running"); + + let mut paths = vec![std::path::PathBuf::from(env!("CARGO_RUSTUP_TEST_custom_bin"))]; + paths.extend(env::split_paths(&env::var_os("PATH").unwrap_or_default())); + let path = env::join_paths(paths).unwrap(); + + let status = Command::new("cargo") + .arg("check") + .current_dir(env!("CARGO_RUSTUP_TEST_project_dir")) + .env("PATH", path) + .status() + .unwrap(); + assert!(status.success()); + "#, + &[ + ("CARGO_RUSTUP_TEST_custom_bin", custom_bin), + ("CARGO_RUSTUP_TEST_project_dir", p.root()), + ], + ); + + cargo_process("custom") + // Set these to simulate what would happen when running under rustup. + // We want to make sure that cargo-custom does not try to use the + // rustup proxies. + .env("RUSTUP_TOOLCHAIN", "test-toolchain") + .env("RUSTUP_HOME", &rustup_home) + .with_stderr( + "\ +custom command running +[CHECKING] foo [..] +custom toolchain rustc running +[FINISHED] [..] +", + ) + .run(); +} -- cgit v1.2.3