diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 6ee5943ae..58f751459 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -1,5 +1,7 @@ use crate::arch::UefiArch; use anyhow::{bail, Result}; +use std::env; +use std::ffi::OsString; use std::process::Command; #[derive(Clone, Copy, Debug)] @@ -86,6 +88,32 @@ pub enum CargoAction { Test, } +/// Get a modified PATH to remove entries added by rustup. This is +/// necessary on Windows, see +/// https://github.com/rust-lang/rustup/issues/3031. +fn sanitized_path(orig_path: OsString) -> OsString { + // Modify the PATH to remove entries added by rustup. This is + // necessary on Windows, see https://github.com/rust-lang/rustup/issues/3031. + let paths = env::split_paths(&orig_path); + let sanitized_paths = paths.filter(|path| { + !path + .components() + .any(|component| component.as_os_str() == ".rustup") + }); + + env::join_paths(sanitized_paths).expect("invalid PATH") +} + +/// Cargo automatically sets some env vars that can prevent the +/// channel arg (e.g. "+nightly") from working. Unset them in the +/// child's environment. +pub fn fix_nested_cargo_env(cmd: &mut Command) { + cmd.env_remove("RUSTC"); + cmd.env_remove("RUSTDOC"); + let orig_path = env::var_os("PATH").unwrap_or_default(); + cmd.env("PATH", sanitized_path(orig_path)); +} + #[derive(Debug)] pub struct Cargo { pub action: CargoAction, @@ -101,6 +129,8 @@ impl Cargo { pub fn command(&self) -> Result { let mut cmd = Command::new("cargo"); + fix_nested_cargo_env(&mut cmd); + if let Some(toolchain) = &self.toolchain { cmd.arg(&format!("+{}", toolchain)); } @@ -193,6 +223,17 @@ mod tests { ); } + #[test] + fn test_sanitize_path() { + let (input, expected) = match env::consts::FAMILY { + "unix" => ("Abc:/path/.rustup/cargo:Xyz", "Abc:Xyz"), + "windows" => ("Abc;/path/.rustup/cargo;Xyz", "Abc;Xyz"), + _ => unimplemented!(), + }; + + assert_eq!(sanitized_path(input.into()), expected); + } + #[test] fn test_cargo_command() { let cargo = Cargo { diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 1ecfac7bc..def472122 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -7,7 +7,7 @@ mod qemu; mod util; use anyhow::Result; -use cargo::{Cargo, CargoAction, Feature, Package}; +use cargo::{fix_nested_cargo_env, Cargo, CargoAction, Feature, Package}; use cfg_if::cfg_if; use clap::Parser; use opt::{Action, BuildOpt, ClippyOpt, DocOpt, MiriOpt, Opt, QemuOpt}; @@ -172,6 +172,7 @@ fn test_latest_release() -> Result<()> { // Create cargo build command, not using the `cargo` module to make // it explicit that it matches the command in `BUILDING.md`. let mut build_cmd = Command::new("cargo"); + fix_nested_cargo_env(&mut build_cmd); build_cmd .args(&["+nightly", "build", "--target", "x86_64-unknown-uefi"]) .current_dir(tmp_dir.join("template")); diff --git a/xtask/src/util.rs b/xtask/src/util.rs index a3f3b4a33..747eee28d 100644 --- a/xtask/src/util.rs +++ b/xtask/src/util.rs @@ -6,8 +6,12 @@ use std::process::Command; /// Example: "VAR=val program --arg1 arg2". pub fn command_to_string(cmd: &Command) -> String { // Format env vars as "name=val". + let ignore_var = ["PATH", "RUSTC", "RUSTDOC"]; let mut parts = cmd .get_envs() + // Filter out some internally-set variables that would just + // clutter the output. + .filter(|(name, _)| !ignore_var.contains(&name.to_str().unwrap_or_default())) .map(|(name, val)| { format!( "{}={}", @@ -47,8 +51,13 @@ mod tests { #[test] fn test_command_to_string() { let mut cmd = Command::new("MyCommand"); - cmd.args(&["abc", "123"]) - .envs([("VAR1", "val1"), ("VAR2", "val2")]); + cmd.args(&["abc", "123"]).envs([ + ("VAR1", "val1"), + ("VAR2", "val2"), + ("PATH", "pathval"), + ("RUSTC", "rustcval"), + ("RUSTDOC", "rustdocval"), + ]); assert_eq!( command_to_string(&cmd), "VAR1=val1 VAR2=val2 MyCommand abc 123"