Skip to content

Commit

Permalink
Closes Schniz#1054
Browse files Browse the repository at this point in the history
  • Loading branch information
wyatt-herkamp committed Oct 26, 2023
1 parent d6c132a commit 5f75240
Showing 1 changed file with 44 additions and 4 deletions.
48 changes: 44 additions & 4 deletions src/commands/exec.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;
use std::ffi::{OsStr};
use super::command::Command as Cmd;
use crate::choose_version_for_user_input::{
choose_version_for_user_input, Error as UserInputError,
Expand All @@ -20,6 +22,14 @@ pub struct Exec {
#[clap(long = "using-file", hide = true)]
using_file: bool,
/// The command to run
///
/// # Windows
/// On Windows if the extension is not provided Windows will execute it
/// if the target file ends with .exe
/// To get around for node commands like npm, npx, etc
/// fnm will append PATHTEXT to the path
/// if the file exists with the extension in the node installation folder
///
arguments: Vec<String>,
}

Expand Down Expand Up @@ -80,6 +90,36 @@ impl Cmd for Exec {
#[cfg(unix)]
let bin_path = applicable_version.path().join("bin");

// https://github.com/rust-lang/rust/issues/37519
// Basically, on Windows, if the binary doesnt end with .exe it will not be found
// So we are going to append the PATHText
// This will only append it if it exists inside the node installation folder.
// Because checking against the entire PATH will be too slow for the benefit
// The main use case of doing this is to access npm,
// npx, etc inside the node installation folder
#[cfg(windows)]
let binary ={
if let Ok(value) = std::env::var("PATHTEXT"){
let split = value.split(";");
let mut windows_binary_name: Option<Cow<'_, OsStr>> = None;
for ext in split {
let mut path = binary.ends_with(ext);
// It already has a PATHText Extension so we don't need to add it
if path {
break;
}
let binary_name= format!("{}.{}", binary, ext);
let bin_path = bin_path.join(binary_name);
if bin_path.exists() {
windows_binary_name = Some(Cow::Owned(bin_path.into_os_string()))
}
}
windows_binary_name.unwrap_or(Cow::Borrowed(binary.as_ref()))
}else{
// No PathText found. We will continue with the binary as is
Cow::Borrowed(OsStr::new(binary))
}
};
let path_env = {
let paths_env = std::env::var_os("PATH").ok_or(Error::CantReadPathVariable)?;
let mut paths: Vec<_> = std::env::split_paths(&paths_env).collect();
Expand All @@ -88,9 +128,10 @@ impl Cmd for Exec {
.map_err(|source| Error::CantAddPathToEnvironment { source })?
};

log::debug!("Running {} with PATH={:?}", binary, path_env);

let exit_status = Command::new(binary)
log::debug!("Running {} with PATH={:?}", binary.to_string_lossy(), path_env);

let exit_status = Command::new(&binary)
.args(arguments)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
Expand All @@ -99,7 +140,7 @@ impl Cmd for Exec {
.spawn()
.map_err(|source| Error::CantSpawnProgram {
source,
binary: binary.to_string(),
binary: binary.to_string_lossy().to_string(),
})?
.wait()
.expect("Failed to grab exit code");
Expand All @@ -108,7 +149,6 @@ impl Cmd for Exec {
std::process::exit(code);
}
}

#[derive(Debug, Error)]
pub enum Error {
#[error("Can't spawn program: {source}\nMaybe the program {} does not exist on not available in PATH?", binary.bold())]
Expand Down

0 comments on commit 5f75240

Please sign in to comment.