diff --git a/Makefile b/Makefile index ad9b2d4..191b450 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VER=$(shell grep version Cargo.toml | head -n1 | grep -Eow '".+"' | sed 's/"//g') -.PHONY: doc +.PHONY: doc test debug: src/* Cargo.toml @cargo build diff --git a/README.md b/README.md index 6fb9581..ba6ae72 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,10 @@ Call/restart `python server.py` when any Python file in the current directory (a $ watchexec -e py -r python server.py +Call/restart `my_server` when any file in the current directory (and all subdirectories) changes, sending `SIGKILL` to stop the child process: + + $ watchexec -r -k my_server + Run `make` when any file changes, using the `.gitignore` file in the current directory to filter: $ watchexec make diff --git a/doc/watchexec.1 b/doc/watchexec.1 index 0c89eda..1450ca1 100644 --- a/doc/watchexec.1 +++ b/doc/watchexec.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "WATCHEXEC" "1" "November 2016" "" "" +.TH "WATCHEXEC" "1" "December 2016" "" "" . .SH "NAME" \fBwatchexec\fR \- execute commands when watched files change @@ -30,12 +30,16 @@ Comma\-separated list of file extensions to filter by\. Leading dots are allowed Ignores modifications from paths that do not match \fIpattern\fR\. This option can be specified multiple times, where a match on any given pattern causes the path to trigger \fIcommand\fR\. . .TP +\fB\-k\fR, \fB\-\-kill\fR +Send \fBSIGKILL\fR to the child process group instead of \fBSIGTERM\fR\. +. +.TP \fB\-i\fR, \fB\-\-ignore\fR \fIpattern\fR Ignores modifications from paths that match \fIpattern\fR\. This option can be specified multiple times, and a match on any pattern causes the path to be ignored\. . .TP \fB\-r\fR, \fB\-\-restart\fR -Sends \fBSIGTERM\fR to the child process if it is still running when subsequent file modifications are detected, then waits for the child to exit\. +Terminates the child process group if it is still running when subsequent file modifications are detected\. By default, sends \fBSIGTERM\fR; use \fB\-\-kill\fR to send \fBSIGKILL\fR\. . .TP \fB\-c\fR, \fB\-\-clear\fR diff --git a/doc/watchexec.1.html b/doc/watchexec.1.html index 5594776..e077623 100644 --- a/doc/watchexec.1.html +++ b/doc/watchexec.1.html @@ -88,8 +88,9 @@
cmd

Command to run when watched files are modified, and at startup, unless --postpone is specified. All arguments are passed to cmd.

-e, --exts extensions

Comma-separated list of file extensions to filter by. Leading dots are allowed (.rs) are allowed. (This is a shorthand for -f).

-f, --filter pattern

Ignores modifications from paths that do not match pattern. This option can be specified multiple times, where a match on any given pattern causes the path to trigger command.

+
-k, --kill

Send SIGKILL to the child process group instead of SIGTERM.

-i, --ignore pattern

Ignores modifications from paths that match pattern. This option can be specified multiple times, and a match on any pattern causes the path to be ignored.

-
-r, --restart

Sends SIGTERM to the child process if it is still running when subsequent file modifications are detected, then waits for the child to exit.

+
-r, --restart

Terminates the child process group if it is still running when subsequent file modifications are detected. By default, sends SIGTERM; use --kill to send SIGKILL.

-c, --clear

Clears the screen before executing command.

-p, --postpone

Postpone execution of command until the first file modification is detected.

-d, --debug

Prints diagnostic messages to STDERR

@@ -127,7 +128,7 @@
  1. -
  2. November 2016
  3. +
  4. December 2016
  5. watchexec(1)
diff --git a/doc/watchexec.1.ronn b/doc/watchexec.1.ronn index f25a05b..48dc309 100644 --- a/doc/watchexec.1.ronn +++ b/doc/watchexec.1.ronn @@ -22,11 +22,14 @@ Comma-separated list of file extensions to filter by. Leading dots are allowed ( * `-f`, `--filter` : Ignores modifications from paths that do not match . This option can be specified multiple times, where a match on any given pattern causes the path to trigger *command*. +* `-k`, `--kill`: +Send `SIGKILL` to the child process group instead of `SIGTERM`. + * `-i`, `--ignore` *pattern*: Ignores modifications from paths that match . This option can be specified multiple times, and a match on any pattern causes the path to be ignored. * `-r`, `--restart`: -Sends `SIGTERM` to the child process if it is still running when subsequent file modifications are detected, then waits for the child to exit. +Terminates the child process group if it is still running when subsequent file modifications are detected. By default, sends `SIGTERM`; use `--kill` to send `SIGKILL`. * `-c`, `--clear`: Clears the screen before executing . diff --git a/src/cli.rs b/src/cli.rs index c8afc77..5ff75ec 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,6 +9,7 @@ pub struct Args { pub filters: Vec, pub ignores: Vec, pub clear_screen: bool, + pub kill: bool, pub restart: bool, pub debug: bool, pub run_initially: bool, @@ -81,6 +82,9 @@ pub fn get_args() -> Args { .help("Forces polling mode") .long("force-poll") .value_name("interval")) + .arg(Arg::with_name("kill") + .help("Send SIGKILL to child processes") + .long("kill")) .get_matches(); let cmd = values_t!(args.values_of("command"), String).unwrap().join(" "); @@ -115,6 +119,7 @@ pub fn get_args() -> Args { filters: filters, ignores: ignores, clear_screen: args.is_present("clear"), + kill: args.is_present("kill"), restart: args.is_present("restart"), debug: args.is_present("debug"), run_initially: !args.is_present("postpone"), diff --git a/src/main.rs b/src/main.rs index d12b66b..d44e8b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -79,8 +79,10 @@ fn init_logger(debug: bool) { } fn main() { + let args = cli::get_args(); let child_process: Arc>> = Arc::new(RwLock::new(None)); let weak_child = Arc::downgrade(&child_process); + let kill = args.kill; signal::install_handler(move |sig: Signal| { if let Some(lock) = weak_child.upgrade() { @@ -88,7 +90,12 @@ fn main() { if let Some(ref child) = *strong { match sig { Signal::Terminate => { - child.kill(); + if kill { + child.kill(); + } else { + child.terminate(); + } + child.wait(); }, Signal::Stop => { @@ -102,8 +109,6 @@ fn main() { } }); - let args = cli::get_args(); - init_logger(args.debug); let cwd = env::current_dir() diff --git a/src/process.rs b/src/process.rs index ea23875..a147e66 100644 --- a/src/process.rs +++ b/src/process.rs @@ -8,8 +8,7 @@ pub use self::imp::Process; #[cfg(target_family = "unix")] mod imp { - use libc::c_int; - use libc::pid_t; + use libc::*; use std::io::Result; use std::path::PathBuf; use std::process::Command; @@ -20,7 +19,6 @@ mod imp { impl Process { pub fn new(cmd: &str, updated_paths: Vec) -> Result { - use libc::exit; use nix::unistd::*; use std::fs::File; use std::io; @@ -80,27 +78,19 @@ mod imp { } pub fn kill(&self) { - use libc::SIGTERM; - - self.signal(SIGTERM); + self.signal(SIGKILL); } pub fn pause(&self) { - use libc::SIGTSTP; - self.signal(SIGTSTP); } pub fn resume(&self) { - use libc::SIGCONT; - self.signal(SIGCONT); } fn signal(&self, sig: c_int) { - use libc::*; - extern "C" { fn killpg(pgrp: pid_t, sig: c_int) -> c_int; } @@ -111,6 +101,10 @@ mod imp { } + pub fn terminate(&self) { + self.signal(SIGTERM); + } + pub fn wait(&self) { use nix::sys::wait::waitpid; @@ -181,9 +175,7 @@ mod imp { } pub fn kill(&self) { - unsafe { - let _ = TerminateJobObject(self.job, 1); - } + self.terminate(); } pub fn pause(&self) { @@ -192,6 +184,13 @@ mod imp { pub fn resume(&self) { } + pub fn terminate(&self) { + unsafe { + let _ = TerminateJobObject(self.job, 1); + } + } + + pub fn wait(&self) { unsafe { let _ = WaitForSingleObject(self.job, INFINITE);