diff --git a/Cargo.lock b/Cargo.lock index 7b0d6d7..702a106 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,6 +66,7 @@ name = "batchtask" version = "0.1.0" dependencies = [ "ad-hoc-iter", + "cfg-if 1.0.0", "color-eyre", "futures", "lazy_format", @@ -73,6 +74,7 @@ dependencies = [ "log", "pretty_env_logger", "recolored", + "rustc_version", "tokio", ] @@ -561,6 +563,30 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "signal-hook-registry" version = "1.3.0" diff --git a/Cargo.toml b/Cargo.toml index 603ca80..52acaf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] ad-hoc-iter = "0.2.2" +cfg-if = "1.0.0" color-eyre = {version = "0.5.10", default-features=false} futures = "0.3.8" lazy_format = "1.8.3" @@ -16,3 +17,6 @@ log = "0.4.11" pretty_env_logger = "0.4.0" recolored = "1.9.3" tokio = {version = "0.2", features = ["full"]} + +[build-dependencies] +rustc_version = "0.2" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6399463 --- /dev/null +++ b/build.rs @@ -0,0 +1,24 @@ + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + match version_meta().unwrap().channel { + Channel::Stable => { + println!("cargo:rustc-cfg=stable"); + } + Channel::Beta => { + println!("cargo:rustc-cfg=beta"); + } + Channel::Nightly => { + println!("cargo:rustc-cfg=nightly"); + } + Channel::Dev => { + println!("cargo:rustc-cfg=dev"); + } + } +} diff --git a/src/arg/mod.rs b/src/arg/mod.rs index cfdeeee..ff4fb20 100644 --- a/src/arg/mod.rs +++ b/src/arg/mod.rs @@ -25,9 +25,13 @@ Usage: {prog} [OPTIONS] [TASKS]... Usage: {prog} --help {opts} + +Compiled with: +{features} "#, prog = prog_name(), - opts = usage::sections().map(|x| lazy_format!("{}", x)).join("\n").trim()); + opts = usage::sections().join("\n").trim(), + features = usage::features().join("\n")); } /// Print usage then exit with code 0 diff --git a/src/arg/usage.rs b/src/arg/usage.rs index b91a5ff..fe10b76 100644 --- a/src/arg/usage.rs +++ b/src/arg/usage.rs @@ -284,17 +284,18 @@ fn explicity() -> impl Iterator { iter![ opt!(in "EXPLICITY"), - opt!(where "Using the `-*x` / `--*-spec` family of arguments, the options for explicit dispatch are described here\nThe options are provided in the form `=:=...`. `value` can be optional depending on the option.\nThe opt-value pairs are described below. Some can be specified multiple times if said so."), + opt!(where "Using the `-*x` / `--*-spec` family of arguments, the options for explicit dispatch are described here\nThe options are provided in the form `=:=...`. `value`\nThe opt-value pairs are described below. Some can be specified multiple times if said so."), opt!("Common"), opt!(where "For all execution modes"), - opt!("r" => "number"; "Repeat execution `number` times."), - opt!("r"; * 2 "Repeat indefinately."), + opt!("r" => "number"; "Repeat execution `number` times. If `number` is `inf`, repeat indefinately."), + opt!(), + opt!("c" => "action"; * 3 "Action when process completes with non-zero return code.", ["COMPLETION"]), + opt!("timeout" => "[signal,]unit,value"; "Timeout before sending `signal` (default `SIGKILL`) to process. Unit can be ns, ms, s, m, or h."), + opt!("detach"; * 4 "Detach this process from the parent. This nullifies the behaviour of some other options", ["DETACHING"]), - opt!("c" => "action"; "Action when process completes with non-zero return code.", ["COMPLETION"]), - opt!(), opt!("uid" => "user id"; * 2 "Run as this uid (must be root to use this option)", ["ACTIONS, Control"]), opt!("user" => "user name"; "Run as this user (must be root to use this option)", ["ACTIONS, Control"]), @@ -305,14 +306,38 @@ fn explicity() -> impl Iterator opt!("stdin" => "action"; "How to handle process' stdin stream", ["ACTIONS, I/O"]), opt!("stdout" => "action"; "How to handle process' stdout stream", ["ACTIONS, I/O"]), opt!("stderr" => "action"; "How to handle process' stderr stream", ["ACTIONS, I/O"]), - + opt!(), opt!("Normal execute"), opt!(where "For non-shell execution"), opt!("p" => "program"; "Specify the process name or path"), opt!("a" => "args"; "Arguments for the process, comma seperated. Multiple of these options can be used to append arguments one by one"), + + opt!(), + opt!("Shell execute"), + opt!(where "For shell execution only"), + opt!("sh" => "shell location"; "Specify the shell to use for execution."), + opt!("ex" => "expression"; "The shell expression to use for execution"), + opt!(), + opt!("Stdin / trailing execution"), + opt!(where "For options that consume the rest of arguments or parent's stdin, a trailing option must be specified which determines which option the trailing arguments are to specify.\nEach argument is substituted for the trailing option, producing individual tasks with that option changed.\nThe format of trailing options is `:` at the end of the argument, with no value.") + ] +} + +fn detaching() -> impl Iterator +{ + iter![ + opt!(in "DETACHING"), + opt!(where "The behaviour of processes with the `detach` option set (or global `--detach` flag) is different than that of normal child processes.\nThe following flags / explicit options do not work"), + + opt!("--completion", ":c"; "Cannot wait on a detached process"), + opt!("--timeout", ":timeout"; "Cannot wait on a detached process"), + + opt!(), + opt!("--stdin", "--stdout", "--stderr", ":stdin", ":stdout", ":stderr"; "I/O redirection directives `inherit` and `ignore` do not work on detached processes. If `ignore` (default) is specified, the stream is closed instead."), + opt!() ] } @@ -326,5 +351,71 @@ pub fn sections() -> impl Iterator .chain(explicity()) .chain(actions()) .chain(completion()) + .chain(detaching()) .chain(environment()) } + +#[derive(Debug, Clone, Copy, Default)] +struct Feature +{ + name: &'static str, + desc: &'static str, + enabled: bool, + default: Option, // if `None`, this will only be printed at all if `enabled` is true. +} + +macro_rules! feature { + ($single:tt => $desc:literal) => { + { + Feature { + name: stringify!($single), + desc: $desc, + enabled: { + cfg_if!{ + if #[cfg($single)] { + true + } else { + false + } + } + }, + default: None, + } + } + }; + () => { + Feature::default() + }; +} + +impl fmt::Display for Feature +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + use recolored::Colorize; + if self.name == "" { + return Ok(()); + } + + match self.default { + None if self.enabled => { + write!(f, " +{}\t", self.name.bright_red().bold())?; + write!(f, "{}", self.desc)?; + Ok(()) + }, + Some(default) => { + todo!() + } + _ => Ok(()), + } + } +} + + +/// Get each enabled compilation feature +pub fn features() -> impl Iterator +{ + iter![ + feature!(nightly => "Compiled with Rust nightly features") + ] +} diff --git a/src/main.rs b/src/main.rs index 1353112..3c1baad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ #[macro_use] extern crate log; #[macro_use] extern crate lazy_static; #[macro_use] extern crate lazy_format; +#[macro_use] extern crate cfg_if; #[macro_use] extern crate ad_hoc_iter; use color_eyre::{