mirror of
https://github.com/watchexec/watchexec.git
synced 2024-09-29 22:51:33 +02:00
Decouple --restart and --signal, so they both make sense
This change takes account of the following four use cases: 1. Make sure the previous run was ended, then run the command again (default) 2. Just send a specified signal to the child, do nothing more (--signal given) 3. Send SIGTERM to the child, wait for it to exit, then run the command again (--restart given) 4. Send a specified signal to the child, wait for it to exit, then run the command again (--restart and --signal given)
This commit is contained in:
parent
56ddfcbaee
commit
c98d0e6cfd
15
src/cli.rs
15
src/cli.rs
@ -10,7 +10,7 @@ pub struct Args {
|
|||||||
pub filters: Vec<String>,
|
pub filters: Vec<String>,
|
||||||
pub ignores: Vec<String>,
|
pub ignores: Vec<String>,
|
||||||
pub clear_screen: bool,
|
pub clear_screen: bool,
|
||||||
pub signal: String,
|
pub signal: Option<String>,
|
||||||
pub restart: bool,
|
pub restart: bool,
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
pub run_initially: bool,
|
pub run_initially: bool,
|
||||||
@ -33,7 +33,8 @@ pub fn clear_screen() {
|
|||||||
#[allow(unknown_lints)]
|
#[allow(unknown_lints)]
|
||||||
#[allow(or_fun_call)]
|
#[allow(or_fun_call)]
|
||||||
pub fn get_args() -> Args {
|
pub fn get_args() -> Args {
|
||||||
let args = App::new("watchexec")
|
let args =
|
||||||
|
App::new("watchexec")
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.about("Execute commands when watched files change")
|
.about("Execute commands when watched files change")
|
||||||
.arg(Arg::with_name("command")
|
.arg(Arg::with_name("command")
|
||||||
@ -60,8 +61,8 @@ pub fn get_args() -> Args {
|
|||||||
.help("Restart the process if it's still running")
|
.help("Restart the process if it's still running")
|
||||||
.short("r")
|
.short("r")
|
||||||
.long("restart"))
|
.long("restart"))
|
||||||
.arg(Arg::with_name("signal") // TODO: --signal only makes sense when used with --restart
|
.arg(Arg::with_name("signal")
|
||||||
.help("Signal to send when --restart is used, defaults to SIGTERM")
|
.help("Send signal to process upon changes, e.g. SIGHUP")
|
||||||
.short("s")
|
.short("s")
|
||||||
.long("signal")
|
.long("signal")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
@ -98,14 +99,12 @@ pub fn get_args() -> Args {
|
|||||||
.help("Forces polling mode")
|
.help("Forces polling mode")
|
||||||
.long("force-poll")
|
.long("force-poll")
|
||||||
.value_name("interval"))
|
.value_name("interval"))
|
||||||
.arg(Arg::with_name("once")
|
.arg(Arg::with_name("once").short("1").hidden(true))
|
||||||
.short("1")
|
|
||||||
.hidden(true))
|
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let cmd = values_t!(args.values_of("command"), String).unwrap().join(" ");
|
let cmd = values_t!(args.values_of("command"), String).unwrap().join(" ");
|
||||||
let paths = values_t!(args.values_of("path"), String).unwrap_or(vec![String::from(".")]);
|
let paths = values_t!(args.values_of("path"), String).unwrap_or(vec![String::from(".")]);
|
||||||
let signal = args.value_of("signal").unwrap_or("SIGTERM").to_owned();
|
let signal = args.value_of("signal").map(str::to_string); // Convert Option<&str> to Option<String>
|
||||||
|
|
||||||
let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(vec![]);
|
let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(vec![]);
|
||||||
|
|
||||||
|
71
src/main.rs
71
src/main.rs
@ -57,7 +57,7 @@ fn main() {
|
|||||||
let weak_child = Arc::downgrade(&child_process);
|
let weak_child = Arc::downgrade(&child_process);
|
||||||
|
|
||||||
// Convert signal string to the corresponding integer
|
// Convert signal string to the corresponding integer
|
||||||
let signal = signal::new(&*args.signal);
|
let signal = signal::new(args.signal);
|
||||||
|
|
||||||
signal::install_handler(move |sig: Signal| {
|
signal::install_handler(move |sig: Signal| {
|
||||||
if let Some(lock) = weak_child.upgrade() {
|
if let Some(lock) = weak_child.upgrade() {
|
||||||
@ -117,9 +117,18 @@ fn main() {
|
|||||||
debug!("Path updated: {:?}", path);
|
debug!("Path updated: {:?}", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for current child process to exit
|
// We have three scenarios here:
|
||||||
// Note: signal is cloned here automatically
|
//
|
||||||
wait_process(&child_process, signal, args.restart);
|
// 1. Make sure the previous run was ended, then run the command again
|
||||||
|
// 2. Just send a specified signal to the child, do nothing more
|
||||||
|
// 3. Send SIGTERM to the child, wait for it to exit, then run the command again
|
||||||
|
// 4. Send a specified signal to the child, wait for it to exit, then run the command again
|
||||||
|
//
|
||||||
|
match args.restart {
|
||||||
|
// Custom restart behaviour (--restart was given, and --signal specified):
|
||||||
|
// Send specified signal to the child, wait for it to exit, then run the command again
|
||||||
|
true if signal.is_some() => {
|
||||||
|
wait_process(&child_process, signal, true);
|
||||||
|
|
||||||
// Launch child process
|
// Launch child process
|
||||||
if args.clear_screen {
|
if args.clear_screen {
|
||||||
@ -131,10 +140,53 @@ fn main() {
|
|||||||
let mut guard = child_process.write().unwrap();
|
let mut guard = child_process.write().unwrap();
|
||||||
*guard = Some(process::spawn(&args.cmd, paths));
|
*guard = Some(process::spawn(&args.cmd, paths));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default restart behaviour (--restart was given, but --signal wasn't specified):
|
||||||
|
// Send SIGTERM to the child, wait for it to exit, then run the command again
|
||||||
|
true if signal.is_none() => {
|
||||||
|
let sigterm = signal::new(Some("SIGTERM".to_owned()));
|
||||||
|
wait_process(&child_process, sigterm, true);
|
||||||
|
|
||||||
|
// Launch child process
|
||||||
|
if args.clear_screen {
|
||||||
|
cli::clear_screen();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Launching child process");
|
||||||
|
{
|
||||||
|
let mut guard = child_process.write().unwrap();
|
||||||
|
*guard = Some(process::spawn(&args.cmd, paths));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SIGHUP scenario: --signal was given, but --restart was not
|
||||||
|
// Just send a signal (e.g. SIGHUP) to the child, do nothing more
|
||||||
|
false if signal.is_some() => wait_process(&child_process, signal, false),
|
||||||
|
|
||||||
|
// Default behaviour (neither --signal nor --restart specified):
|
||||||
|
// Make sure the previous run was ended, then run the command again
|
||||||
|
false if signal.is_none() => {
|
||||||
|
wait_process(&child_process, None, true);
|
||||||
|
|
||||||
|
// Launch child process
|
||||||
|
if args.clear_screen {
|
||||||
|
cli::clear_screen();
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Launching child process");
|
||||||
|
{
|
||||||
|
let mut guard = child_process.write().unwrap();
|
||||||
|
*guard = Some(process::spawn(&args.cmd, paths));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch everything else, just to be sure.
|
||||||
|
_ => panic!("This should never be called. Please file a bug report!"),
|
||||||
|
}
|
||||||
|
|
||||||
// Handle once option for integration testing
|
// Handle once option for integration testing
|
||||||
if args.once {
|
if args.once {
|
||||||
// Note: signal is cloned here automatically
|
|
||||||
wait_process(&child_process, signal, false);
|
wait_process(&child_process, signal, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -185,15 +237,18 @@ fn wait_fs(rx: &Receiver<Event>, filter: &NotificationFilter) -> Vec<PathBuf> {
|
|||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_process(process: &RwLock<Option<Process>>, signal: Signal, restart: bool) {
|
// wait_process sends signal to process. It waits for the process to exit if wait is true
|
||||||
|
fn wait_process(process: &RwLock<Option<Process>>, signal: Option<Signal>, wait: bool) {
|
||||||
let guard = process.read().unwrap();
|
let guard = process.read().unwrap();
|
||||||
|
|
||||||
if let Some(ref child) = *guard {
|
if let Some(ref child) = *guard {
|
||||||
if restart {
|
if let Some(s) = signal {
|
||||||
child.signal(signal);
|
child.signal(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if wait {
|
||||||
debug!("Waiting for process to exit...");
|
debug!("Waiting for process to exit...");
|
||||||
child.wait();
|
child.wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -49,8 +49,9 @@ impl ConvertToLibc for Signal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(signal_name: &str) -> Signal {
|
pub fn new(signal_name: Option<String>) -> Option<Signal> {
|
||||||
match signal_name {
|
if let Some(signame) = signal_name {
|
||||||
|
let signal = match signame.as_ref() {
|
||||||
"SIGKILL" | "KILL" => Signal::SIGKILL,
|
"SIGKILL" | "KILL" => Signal::SIGKILL,
|
||||||
"SIGTERM" | "TERM" => Signal::SIGTERM,
|
"SIGTERM" | "TERM" => Signal::SIGTERM,
|
||||||
"SIGINT" | "INT" => Signal::SIGINT,
|
"SIGINT" | "INT" => Signal::SIGINT,
|
||||||
@ -60,7 +61,12 @@ pub fn new(signal_name: &str) -> Signal {
|
|||||||
"SIGCHLD" | "CHLD" => Signal::SIGCHLD,
|
"SIGCHLD" | "CHLD" => Signal::SIGCHLD,
|
||||||
"SIGUSR1" | "USR1" => Signal::SIGUSR1,
|
"SIGUSR1" | "USR1" => Signal::SIGUSR1,
|
||||||
"SIGUSR2" | "USR2" => Signal::SIGUSR2,
|
"SIGUSR2" | "USR2" => Signal::SIGUSR2,
|
||||||
_ => panic!("unsupported signal: {}", signal_name),
|
_ => panic!("unsupported signal: {}", signame),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(signal)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user