aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelody Horn <melody@boringcactus.com>2021-03-26 16:36:54 -0600
committerMelody Horn <melody@boringcactus.com>2021-03-26 16:36:54 -0600
commitabd55e36d781645566a7815c7712ded6b5cc1923 (patch)
tree44f2a9466d2886626b9ea9d844f95d22d19107c2
parent1844cb79ae82e71610573f133c5ed7aeeb0c50b6 (diff)
downloadmakers-abd55e36d781645566a7815c7712ded6b5cc1923.tar.gz
makers-abd55e36d781645566a7815c7712ded6b5cc1923.zip
tune up argument handling
-rw-r--r--src/args.rs139
1 files changed, 127 insertions, 12 deletions
diff --git a/src/args.rs b/src/args.rs
index 963c01e..babd7a0 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -1,5 +1,6 @@
use std::env;
use std::ffi::OsString;
+use std::iter;
use std::path::PathBuf;
use structopt::StructOpt;
@@ -32,7 +33,7 @@ pub struct Args {
/// Continue to update other targets that do not depend on the current target if a
/// non-ignored error occurs while executing the commands to bring a target
/// up-to-date.
- #[structopt(short, long)]
+ #[structopt(short, long, overrides_with="keep-going", overrides_with="no-keep-going")]
pub keep_going: bool,
/// Write commands that would be executed on standard output, but do not execute them
@@ -69,7 +70,8 @@ pub struct Args {
/// reason).
///
/// This shall be the default and the opposite of -k.
- #[structopt(short = "S", long, visible_alias = "stop", hidden = true, overrides_with="keep-going")]
+ #[structopt(short = "S", long, visible_alias = "stop", hidden = true,
+ overrides_with="keep-going", overrides_with="no-keep-going")]
pub no_keep_going: bool,
/// Do not write makefile command lines or touch messages to standard output before
@@ -100,25 +102,54 @@ pub struct Args {
}
impl Args {
- pub fn from_env_and_args() -> Args {
+ fn from_given_args_and_given_env(mut args: impl Iterator<Item=OsString>, env_makeflags: String) -> Args {
// POSIX spec says "Any options specified in the MAKEFLAGS environment variable
// shall be evaluated before any options specified on the make utility command
// line."
- // TODO make sure only flags show up in MAKEFLAGS (i.e. not -f or other stuff)
- // TODO accept "option letters without the leading <hyphen-minus> characters"
- let env_makeflags = env::var("MAKEFLAGS").unwrap_or_default();
+ // TODO allow macro definitions in MAKEFLAGS
+ // POSIX says we have to accept
+ // > The characters are option letters without the leading <hyphen-minus>
+ // > characters or <blank> separation used on a make utility command line.
+ let makeflags_given = !env_makeflags.is_empty();
+ let makeflags_spaces = env_makeflags.contains(' ');
+ let makeflags_leading_dash = env_makeflags.starts_with('-');
+ let makeflags_has_equals = env_makeflags.starts_with('=');
+ let makeflags_obviously_full = makeflags_spaces || makeflags_leading_dash || makeflags_has_equals;
+ let env_makeflags = if makeflags_given && !makeflags_obviously_full {
+ format!("-{}", env_makeflags)
+ } else {
+ env_makeflags
+ };
let env_makeflags = env_makeflags.split_whitespace()
.map(|x| OsString::from(x));
- let mut args = env::args_os();
// per the structopt docs, the first argument will be used as the binary name,
// so we need to make sure it goes in before MAKEFLAGS
let arg0 = args.next().unwrap_or_else(|| env!("CARGO_PKG_NAME").into());
- let args = ::std::iter::once(arg0)
+ let args = iter::once(arg0)
.chain(env_makeflags.into_iter())
.chain(args);
+
+ let args = args.collect::<Vec<_>>();
+ dbg!(&args);
+ let args = args.into_iter();
+
Args::from_iter(args)
}
+
+ pub fn from_env_and_args() -> Args {
+ let env_makeflags = env::var("MAKEFLAGS").unwrap_or_default();
+ let args = env::args_os();
+ Self::from_given_args_and_given_env(args, env_makeflags)
+ }
+
+ pub fn targets(&self) -> impl Iterator<Item=&String> {
+ self.targets_or_macros.iter().filter(|x| !x.contains('='))
+ }
+
+ pub fn macros(&self) -> impl Iterator<Item=&String> {
+ self.targets_or_macros.iter().filter(|x| x.contains('='))
+ }
}
#[cfg(test)]
@@ -128,7 +159,7 @@ mod test {
#[test]
fn no_args() {
let args: Vec<OsString> = vec!["makers".into()];
- let args = Args::from_iter(args.into_iter());
+ let args = Args::from_given_args_and_given_env(args.into_iter(), String::new());
assert_eq!(args, Args {
environment_overrides: false,
makefile: vec![],
@@ -148,7 +179,7 @@ mod test {
#[test]
fn kitchen_sink_args() {
let args = "makers -eiknpqrstf foo -f bruh bar baz=yeet";
- let args = Args::from_iter(args.split_whitespace());
+ let args = Args::from_given_args_and_given_env(args.split_whitespace().map(OsString::from), String::new());
assert_eq!(args, Args {
environment_overrides: true,
makefile: vec!["foo".into(), "bruh".into()],
@@ -168,7 +199,7 @@ mod test {
#[test]
fn keep_going_wrestling() {
let args = "makers -kSkSkSSSkSkkSk -k -S -k -k -S -S -k";
- let args = Args::from_iter(args.split_whitespace());
+ let args = Args::from_given_args_and_given_env(args.split_whitespace().map(OsString::from), String::new());
assert_eq!(args, Args {
environment_overrides: false,
makefile: vec![],
@@ -185,5 +216,89 @@ mod test {
});
}
- // TODO test MAKEFLAGS
+ #[test]
+ fn keep_going_wrestling_alt() {
+ let args = "makers -kSkSkSSSkSkkSk -k -S -k -k -S -S -kS";
+ let args = Args::from_given_args_and_given_env(args.split_whitespace().map(OsString::from), String::new());
+ assert_eq!(args, Args {
+ environment_overrides: false,
+ makefile: vec![],
+ ignore_errors: false,
+ keep_going: false,
+ dry_run: false,
+ print_everything: false,
+ question: false,
+ no_builtin_rules: false,
+ no_keep_going: true,
+ silent: false,
+ touch: false,
+ targets_or_macros: vec![],
+ });
+ }
+
+ #[test]
+ fn makeflags_lazy() {
+ let args = "makers";
+ let makeflags = "eiknp";
+ let args = Args::from_given_args_and_given_env(iter::once(args.into()), makeflags.into());
+ assert_eq!(args, Args {
+ environment_overrides: true,
+ makefile: vec![],
+ ignore_errors: true,
+ keep_going: true,
+ dry_run: true,
+ print_everything: true,
+ question: false,
+ no_builtin_rules: false,
+ no_keep_going: false,
+ silent: false,
+ touch: false,
+ targets_or_macros: vec![],
+ });
+ }
+
+ #[test]
+ fn makeflags_full() {
+ let args = "makers";
+ let makeflags = "-i -knp";
+ let args = Args::from_given_args_and_given_env(iter::once(args.into()), makeflags.into());
+ assert_eq!(args, Args {
+ environment_overrides: false,
+ makefile: vec![],
+ ignore_errors: true,
+ keep_going: true,
+ dry_run: true,
+ print_everything: true,
+ question: false,
+ no_builtin_rules: false,
+ no_keep_going: false,
+ silent: false,
+ touch: false,
+ targets_or_macros: vec![],
+ });
+ }
+
+ #[test]
+ fn nightmare() {
+ let makeflags = "-nrs -k foo=bar";
+ let args = "makers -eipqtSf foo -f bruh bar baz=yeet";
+ let args = Args::from_given_args_and_given_env(
+ args.split_whitespace().map(OsString::from),
+ makeflags.into()
+ );
+ assert_eq!(args, Args {
+ environment_overrides: true,
+ makefile: vec!["foo".into(), "bruh".into()],
+ ignore_errors: true,
+ keep_going: false,
+ dry_run: true,
+ print_everything: true,
+ question: true,
+ no_builtin_rules: true,
+ no_keep_going: true,
+ silent: true,
+ touch: true,
+ targets_or_macros: vec!["foo=bar".into(), "bar".into(), "baz=yeet".into()],
+ });
+ }
}