From abd55e36d781645566a7815c7712ded6b5cc1923 Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Fri, 26 Mar 2021 16:36:54 -0600 Subject: tune up argument handling --- src/args.rs | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file 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, 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 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 + // > characters or 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::>(); + 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 { + self.targets_or_macros.iter().filter(|x| !x.contains('=')) + } + + pub fn macros(&self) -> impl Iterator { + self.targets_or_macros.iter().filter(|x| x.contains('=')) + } } #[cfg(test)] @@ -128,7 +159,7 @@ mod test { #[test] fn no_args() { let args: Vec = 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()], + }); + } } -- cgit v1.2.3