mirror of
https://github.com/watchexec/watchexec.git
synced 2024-09-29 22:51:33 +02:00
Support powershell and custom shells in the backend
This commit is contained in:
parent
2a382a9486
commit
1280a15ca2
@ -5,10 +5,24 @@ use crate::pathop::PathOp;
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::path::PathBuf;
|
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(
|
pub fn spawn(
|
||||||
cmd: &[String],
|
cmd: &[String],
|
||||||
updated_paths: &[PathOp],
|
updated_paths: &[PathOp],
|
||||||
shell: bool,
|
shell: Shell,
|
||||||
environment: bool,
|
environment: bool,
|
||||||
) -> Result<Process> {
|
) -> Result<Process> {
|
||||||
self::imp::Process::new(cmd, updated_paths, shell, environment).map_err(|e| e.into())
|
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")]
|
#[cfg(target_family = "unix")]
|
||||||
mod imp {
|
mod imp {
|
||||||
|
use super::Shell;
|
||||||
use crate::pathop::PathOp;
|
use crate::pathop::PathOp;
|
||||||
use crate::signal::Signal;
|
use crate::signal::Signal;
|
||||||
use nix::libc::*;
|
use nix::libc::*;
|
||||||
@ -45,7 +60,7 @@ mod imp {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
cmd: &[String],
|
cmd: &[String],
|
||||||
updated_paths: &[PathOp],
|
updated_paths: &[PathOp],
|
||||||
shell: bool,
|
shell: Shell,
|
||||||
environment: bool,
|
environment: bool,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
use nix::unistd::*;
|
use nix::unistd::*;
|
||||||
@ -53,20 +68,27 @@ mod imp {
|
|||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
|
|
||||||
// Assemble command to run.
|
// 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,
|
// 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
|
// but is a little less performant and can cause trouble when using custom signals
|
||||||
// (e.g. --signal SIGHUP)
|
// (e.g. --signal SIGHUP)
|
||||||
let mut command = if !shell {
|
let mut command = match shell {
|
||||||
let (head, tail) = cmd.split_first().expect("cmd was empty");
|
Shell::None => {
|
||||||
let mut command = Command::new(head);
|
let (head, tail) = cmd.split_first().expect("cmd was empty");
|
||||||
command.args(tail);
|
let mut c = Command::new(head);
|
||||||
command
|
c.args(tail);
|
||||||
} else {
|
c
|
||||||
let mut command = Command::new("sh");
|
}
|
||||||
//command.arg("-c").arg(wrap_commands(cmd).join(" "));
|
Shell::Powershell => {
|
||||||
command.arg("-c").arg(cmd.join(" "));
|
let mut c = Command::new("pwsh");
|
||||||
command
|
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);
|
debug!("Assembled command {:?}", command);
|
||||||
@ -151,6 +173,7 @@ mod imp {
|
|||||||
|
|
||||||
#[cfg(target_family = "windows")]
|
#[cfg(target_family = "windows")]
|
||||||
mod imp {
|
mod imp {
|
||||||
|
use super::Shell;
|
||||||
use crate::pathop::PathOp;
|
use crate::pathop::PathOp;
|
||||||
use crate::signal::Signal;
|
use crate::signal::Signal;
|
||||||
use std::io;
|
use std::io;
|
||||||
@ -200,7 +223,7 @@ mod imp {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
cmd: &[String],
|
cmd: &[String],
|
||||||
updated_paths: &[PathOp],
|
updated_paths: &[PathOp],
|
||||||
shell: bool,
|
shell: Shell,
|
||||||
environment: bool,
|
environment: bool,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
@ -262,17 +285,30 @@ mod imp {
|
|||||||
panic!("failed to set job info: {}", last_err());
|
panic!("failed to set job info: {}", last_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut command;
|
let mut command = match shell {
|
||||||
if !shell {
|
Shell::None => {
|
||||||
let (first, rest) = cmd.split_first().expect("command is empty");
|
let (first, rest) = cmd.split_first().expect("command is empty");
|
||||||
command = Command::new(first);
|
let mut c = Command::new(first);
|
||||||
command.args(rest);
|
c.args(rest);
|
||||||
} else {
|
}
|
||||||
command = Command::new("cmd.exe");
|
Shell::Cmd => {
|
||||||
command.arg("/C");
|
let mut c = Command::new("cmd.exe");
|
||||||
//command.arg(wrap_commands(cmd).join(" "));
|
c.arg("/C").arg(cmd.join(" "));
|
||||||
command.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);
|
command.creation_flags(CREATE_SUSPENDED);
|
||||||
debug!("Assembled command {:?}", command);
|
debug!("Assembled command {:?}", command);
|
||||||
|
24
src/run.rs
24
src/run.rs
@ -5,7 +5,7 @@ use crate::gitignore;
|
|||||||
use crate::ignore;
|
use crate::ignore;
|
||||||
use crate::notification_filter::NotificationFilter;
|
use crate::notification_filter::NotificationFilter;
|
||||||
use crate::pathop::PathOp;
|
use crate::pathop::PathOp;
|
||||||
use crate::process::{self, Process};
|
use crate::process::{self, Shell, Process};
|
||||||
use crate::signal::{self, Signal};
|
use crate::signal::{self, Signal};
|
||||||
use crate::watcher::{Event, Watcher};
|
use crate::watcher::{Event, Watcher};
|
||||||
use std::{
|
use std::{
|
||||||
@ -168,13 +168,31 @@ impl ExecHandler {
|
|||||||
clear_screen();
|
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");
|
debug!("Launching child process");
|
||||||
let mut guard = self.child_process.write()?;
|
let mut guard = self.child_process.write()?;
|
||||||
*guard = Some(process::spawn(
|
*guard = Some(process::spawn(
|
||||||
&self.args.cmd,
|
&self.args.cmd,
|
||||||
ops,
|
ops,
|
||||||
self.args.no_shell,
|
get_shell(&self.args),
|
||||||
self.args.no_environment,
|
!self.args.no_environment,
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
Reference in New Issue
Block a user