Support powershell and custom shells in the backend

This commit is contained in:
Félix Saparelli 2021-04-11 02:08:39 +12:00
parent 2a382a9486
commit 1280a15ca2
No known key found for this signature in database
GPG key ID: B948C4BAE44FC474
2 changed files with 82 additions and 28 deletions

View file

@ -5,10 +5,24 @@ use crate::pathop::PathOp;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
/// Shell to use to run commands.
///
/// `Cmd` and `Powershell` are special-cased because they have different calling
/// conventions. Also `Cmd` is only available in Windows, while `Powershell` is
/// also available on unices (provided the end-user has it installed, of course).
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Shell {
None,
Unix(String),
#[cfg(windows)]
Cmd,
Powershell,
}
pub fn spawn(
cmd: &[String],
updated_paths: &[PathOp],
shell: bool,
shell: Shell,
environment: bool,
) -> Result<Process> {
self::imp::Process::new(cmd, updated_paths, shell, environment).map_err(|e| e.into())
@ -18,6 +32,7 @@ pub use self::imp::Process;
#[cfg(target_family = "unix")]
mod imp {
use super::Shell;
use crate::pathop::PathOp;
use crate::signal::Signal;
use nix::libc::*;
@ -45,7 +60,7 @@ mod imp {
pub fn new(
cmd: &[String],
updated_paths: &[PathOp],
shell: bool,
shell: Shell,
environment: bool,
) -> Result<Self> {
use nix::unistd::*;
@ -53,20 +68,27 @@ mod imp {
use std::os::unix::process::CommandExt;
// Assemble command to run.
// This is either the first argument from cmd (if shell == false) or "sh".
// This is either the first argument from cmd (if shell == None) or "sh".
// Using "sh -c" gives us features like supporting pipes and redirects,
// but is a little less performant and can cause trouble when using custom signals
// (e.g. --signal SIGHUP)
let mut command = if !shell {
let (head, tail) = cmd.split_first().expect("cmd was empty");
let mut command = Command::new(head);
command.args(tail);
command
} else {
let mut command = Command::new("sh");
//command.arg("-c").arg(wrap_commands(cmd).join(" "));
command.arg("-c").arg(cmd.join(" "));
command
let mut command = match shell {
Shell::None => {
let (head, tail) = cmd.split_first().expect("cmd was empty");
let mut c = Command::new(head);
c.args(tail);
c
}
Shell::Powershell => {
let mut c = Command::new("pwsh");
c.arg("-Command").arg(cmd.join(" "));
c
}
Shell::Unix(name) => {
let mut c = Command::new(name);
c.arg("-c").arg(cmd.join(" "));
c
}
};
debug!("Assembled command {:?}", command);
@ -151,6 +173,7 @@ mod imp {
#[cfg(target_family = "windows")]
mod imp {
use super::Shell;
use crate::pathop::PathOp;
use crate::signal::Signal;
use std::io;
@ -200,7 +223,7 @@ mod imp {
pub fn new(
cmd: &[String],
updated_paths: &[PathOp],
shell: bool,
shell: Shell,
environment: bool,
) -> Result<Self> {
use std::convert::TryInto;
@ -262,17 +285,30 @@ mod imp {
panic!("failed to set job info: {}", last_err());
}
let mut command;
if !shell {
let (first, rest) = cmd.split_first().expect("command is empty");
command = Command::new(first);
command.args(rest);
} else {
command = Command::new("cmd.exe");
command.arg("/C");
//command.arg(wrap_commands(cmd).join(" "));
command.arg(cmd.join(" "));
}
let mut command = match shell {
Shell::None => {
let (first, rest) = cmd.split_first().expect("command is empty");
let mut c = Command::new(first);
c.args(rest);
}
Shell::Cmd => {
let mut c = Command::new("cmd.exe");
c.arg("/C").arg(cmd.join(" "));
c
}
Shell::Powershell => {
let mut c = Command::new("powershell.exe");
c.arg("-Command").arg(cmd.join(" "));
c
}
// Using unixy shells on windows could be supported...
Shell::Unix(name) => {
let mut c = Command::new(name);
c.arg("-c").arg(cmd.join(" "));
c
}
};
command.creation_flags(CREATE_SUSPENDED);
debug!("Assembled command {:?}", command);

View file

@ -5,7 +5,7 @@ use crate::gitignore;
use crate::ignore;
use crate::notification_filter::NotificationFilter;
use crate::pathop::PathOp;
use crate::process::{self, Process};
use crate::process::{self, Shell, Process};
use crate::signal::{self, Signal};
use crate::watcher::{Event, Watcher};
use std::{
@ -168,13 +168,31 @@ impl ExecHandler {
clear_screen();
}
#[cfg(windows)]
fn get_shell(config: &Config) -> Shell {
if config.no_shell {
Shell::None
} else {
Shell::Cmd
}
}
#[cfg(not(windows))]
fn get_shell(config: &Config) -> Shell {
if config.no_shell {
Shell::None
} else {
Shell::Unix("sh".into())
}
}
debug!("Launching child process");
let mut guard = self.child_process.write()?;
*guard = Some(process::spawn(
&self.args.cmd,
ops,
self.args.no_shell,
self.args.no_environment,
get_shell(&self.args),
!self.args.no_environment,
)?);
Ok(())