From 48707e16dd0c8806e99387b0718a078ecf092f69 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 25 Apr 2022 13:54:33 +0200 Subject: Rename cmdline to b_cmdline, and options to b_options --- libbuild2/b-cmdline.cxx | 478 ++++++++++++++++++++++++++++++++++++++++++++++++ libbuild2/b-cmdline.hxx | 44 +++++ libbuild2/b-options.cxx | 242 ++++++++++++------------ libbuild2/b-options.hxx | 6 +- libbuild2/b-options.ixx | 102 +++++------ libbuild2/b.cli | 2 +- libbuild2/cmdline.cxx | 478 ------------------------------------------------ libbuild2/cmdline.hxx | 44 ----- 8 files changed, 698 insertions(+), 698 deletions(-) create mode 100644 libbuild2/b-cmdline.cxx create mode 100644 libbuild2/b-cmdline.hxx delete mode 100644 libbuild2/cmdline.cxx delete mode 100644 libbuild2/cmdline.hxx (limited to 'libbuild2') diff --git a/libbuild2/b-cmdline.cxx b/libbuild2/b-cmdline.cxx new file mode 100644 index 0000000..2e2deb8 --- /dev/null +++ b/libbuild2/b-cmdline.cxx @@ -0,0 +1,478 @@ +// file : libbuild2/b-cmdline.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include // strcmp(), strchr() + +#include + +#include +#include +#include + +using namespace std; +using namespace butl; + +namespace cli = build2::build::cli; + +namespace build2 +{ + b_cmdline + parse_b_cmdline (tracer& trace, + int argc, char* argv[], + b_options& ops, + uint16_t def_verb, + size_t def_jobs) + { + // Note that the diagnostics verbosity level can only be calculated after + // default options are loaded and merged (see below). Thus, until then we + // refer to the verbosity level specified on the command line. + // + auto verbosity = [&ops, def_verb] () + { + uint16_t v ( + ops.verbose_specified () + ? ops.verbose () + : (ops.V () ? 3 : + ops.v () ? 2 : + ops.quiet () || ops.silent () ? 0 : def_verb)); + return v; + }; + + b_cmdline r; + + // We want to be able to specify options, vars, and buildspecs in any + // order (it is really handy to just add -v at the end of the command + // line). + // + try + { + // Command line arguments starting position. + // + // We want the positions of the command line arguments to be after the + // default options files. Normally that would be achieved by passing the + // last position of the previous scanner to the next. The problem is + // that we parse the command line arguments first (for good reasons). + // Also the default options files parsing machinery needs the maximum + // number of arguments to be specified and assigns the positions below + // this value (see load_default_options() for details). So we are going + // to "reserve" the first half of the size_t value range for the default + // options positions and the second half for the command line arguments + // positions. + // + size_t args_pos (numeric_limits::max () / 2); + cli::argv_file_scanner scan (argc, argv, "--options-file", args_pos); + + size_t argn (0); // Argument count. + bool shortcut (false); // True if the shortcut syntax is used. + + for (bool opt (true), var (true); scan.more (); ) + { + if (opt) + { + // Parse the next chunk of options until we reach an argument (or + // eos). + // + if (ops.parse (scan) && !scan.more ()) + break; + + // If we see first "--", then we are done parsing options. + // + if (strcmp (scan.peek (), "--") == 0) + { + scan.next (); + opt = false; + continue; + } + + // Fall through. + } + + const char* s (scan.next ()); + + // See if this is a command line variable. What if someone needs to + // pass a buildspec that contains '='? One way to support this would + // be to quote such a buildspec (e.g., "'/tmp/foo=bar/'"). Or invent + // another separator. Or use a second "--". Actually, let's just do + // the second "--". + // + if (var) + { + // If we see second "--", then we are also done parsing variables. + // + if (strcmp (s, "--") == 0) + { + var = false; + continue; + } + + if (const char* p = strchr (s, '=')) // Covers =, +=, and =+. + { + // Diagnose the empty variable name situation. Note that we don't + // allow "partially broken down" assignments (as in foo =bar) + // since foo= bar would be ambigous. + // + if (p == s || (p == s + 1 && *s == '+')) + fail << "missing variable name in '" << s << "'"; + + r.cmd_vars.push_back (s); + continue; + } + + // Handle the "broken down" variable assignments (i.e., foo = bar + // instead of foo=bar). + // + if (scan.more ()) + { + const char* a (scan.peek ()); + + if (strcmp (a, "=" ) == 0 || + strcmp (a, "+=") == 0 || + strcmp (a, "=+") == 0) + { + string v (s); + v += a; + + scan.next (); + + if (scan.more ()) + v += scan.next (); + + r.cmd_vars.push_back (move (v)); + continue; + } + } + + // Fall through. + } + + // Merge all the individual buildspec arguments into a single string. + // We use newlines to separate arguments so that line numbers in + // diagnostics signify argument numbers. Clever, huh? + // + if (argn != 0) + r.buildspec += '\n'; + + r.buildspec += s; + + // See if we are using the shortcut syntax. + // + if (argn == 0 && r.buildspec.back () == ':') + { + r.buildspec.back () = '('; + shortcut = true; + } + + argn++; + } + + // Add the closing parenthesis unless there wasn't anything in between + // in which case pop the opening one. + // + if (shortcut) + { + if (argn == 1) + r.buildspec.pop_back (); + else + r.buildspec += ')'; + } + + // Get/set an environment variable tracing the operation. + // + auto get_env = [&verbosity, &trace] (const char* nm) + { + optional r (getenv (nm)); + + if (verbosity () >= 5) + { + if (r) + trace << nm << ": '" << *r << "'"; + else + trace << nm << ": "; + } + + return r; + }; + + auto set_env = [&verbosity, &trace] (const char* nm, const string& vl) + { + try + { + if (verbosity () >= 5) + trace << "setting " << nm << "='" << vl << "'"; + + setenv (nm, vl); + } + catch (const system_error& e) + { + // The variable value can potentially be long/multi-line, so let's + // print it last. + // + fail << "unable to set environment variable " << nm << ": " << e << + info << "value: '" << vl << "'"; + } + }; + + // If the BUILD2_VAR_OVR environment variable is present, then parse its + // value as a newline-separated global variable overrides and prepend + // them to the overrides specified on the command line. + // + // Note that this means global overrides may not contain a newline. + + // Verify that the string is a valid global override. Uses the file name + // and the options flag for diagnostics only. + // + auto verify_glb_ovr = [] (const string& v, const path_name& fn, bool opt) + { + size_t p (v.find ('=', 1)); + if (p == string::npos || v[0] != '!') + { + diag_record dr (fail (fn)); + dr << "expected " << (opt ? "option or " : "") << "global " + << "variable override instead of '" << v << "'"; + + if (p != string::npos) + dr << info << "prefix variable assignment with '!'"; + } + + if (p == 1 || (p == 2 && v[1] == '+')) // '!=' or '!+=' ? + fail (fn) << "missing variable name in '" << v << "'"; + }; + + optional env_ovr (get_env ("BUILD2_VAR_OVR")); + if (env_ovr) + { + path_name fn (""); + + auto i (r.cmd_vars.begin ()); + for (size_t b (0), e (0); next_word (*env_ovr, b, e, '\n', '\r'); ) + { + // Extract the override from the current line, stripping the leading + // and trailing spaces. + // + string s (*env_ovr, b, e - b); + trim (s); + + // Verify and save the override, unless the line is empty. + // + if (!s.empty ()) + { + verify_glb_ovr (s, fn, false /* opt */); + i = r.cmd_vars.insert (i, move (s)) + 1; + } + } + } + + // Load the default options files, unless --no-default-options is + // specified on the command line or the BUILD2_DEF_OPT environment + // variable is set to a value other than 'true' or '1'. + // + // If loaded, prepend the default global overrides to the variables + // specified on the command line, unless BUILD2_VAR_OVR is set in which + // case just ignore them. + // + optional env_def (get_env ("BUILD2_DEF_OPT")); + + // False if --no-default-options is specified on the command line. Note + // that we cache the flag since it can be overridden by a default + // options file. + // + bool cmd_def (!ops.no_default_options ()); + + if (cmd_def && (!env_def || *env_def == "true" || *env_def == "1")) + try + { + optional extra; + if (ops.default_options_specified ()) + extra = ops.default_options (); + + // Load default options files. + // + default_options def_ops ( + load_default_options ( + nullopt /* sys_dir */, + path::home_directory (), // The home variable is not assigned yet. + extra, + default_options_files {{path ("b.options")}, + nullopt /* start */}, + [&trace, &verbosity] (const path& f, bool r, bool o) + { + if (verbosity () >= 3) + { + if (o) + trace << "treating " << f << " as " + << (r ? "remote" : "local"); + else + trace << "loading " << (r ? "remote " : "local ") << f; + } + }, + "--options-file", + args_pos, + 1024, + true /* args */)); + + // Merge the default and command line options. + // + ops = merge_default_options (def_ops, ops); + + // Merge the default and command line global overrides, unless + // BUILD2_VAR_OVR is already set (in which case we assume this has + // already been done). + // + // Note that the "broken down" variable assignments occupying a single + // line are naturally supported. + // + if (!env_ovr) + r.cmd_vars = + merge_default_arguments ( + def_ops, + r.cmd_vars, + [&verify_glb_ovr] (const default_options_entry& e, + const strings&) + { + path_name fn (e.file); + + // Verify that all arguments are global overrides. + // + for (const string& a: e.arguments) + verify_glb_ovr (a, fn, true /* opt */); + }); + } + catch (const invalid_argument& e) + { + fail << "unable to load default options files: " << e; + } + catch (const pair& e) + { + fail << "unable to load default options files: " << e.first << ": " + << e.second; + } + catch (const system_error& e) + { + fail << "unable to obtain home directory: " << e; + } + + // Verify and save the global overrides present in cmd_vars (default, + // from the command line, etc), if any, into the BUILD2_VAR_OVR + // environment variable. + // + if (!r.cmd_vars.empty ()) + { + string ovr; + for (const string& v: r.cmd_vars) + { + if (v[0] == '!') + { + if (v.find_first_of ("\n\r") != string::npos) + fail << "newline in global variable override '" << v << "'"; + + if (!ovr.empty ()) + ovr += '\n'; + + ovr += v; + } + } + + // Optimize for the common case. + // + // Note: cmd_vars may contain non-global overrides. + // + if (!ovr.empty () && (!env_ovr || *env_ovr != ovr)) + set_env ("BUILD2_VAR_OVR", ovr); + } + + // Propagate disabling of the default options files to the potential + // nested invocations. + // + if (!cmd_def && (!env_def || *env_def != "0")) + set_env ("BUILD2_DEF_OPT", "0"); + + // Validate options. + // + if (ops.progress () && ops.no_progress ()) + fail << "both --progress and --no-progress specified"; + + if (ops.mtime_check () && ops.no_mtime_check ()) + fail << "both --mtime-check and --no-mtime-check specified"; + } + catch (const cli::exception& e) + { + fail << e; + } + + if (ops.help () || ops.version ()) + return r; + + r.verbosity = verbosity (); + + if (ops.silent () && r.verbosity != 0) + fail << "specified with -v, -V, or --verbose verbosity level " + << r.verbosity << " is incompatible with --silent"; + + r.progress = (ops.progress () ? optional (true) : + ops.no_progress () ? optional (false) : nullopt); + + r.mtime_check = (ops.mtime_check () ? optional (true) : + ops.no_mtime_check () ? optional (false) : nullopt); + + + r.config_sub = (ops.config_sub_specified () + ? optional (ops.config_sub ()) + : nullopt); + + r.config_guess = (ops.config_guess_specified () + ? optional (ops.config_guess ()) + : nullopt); + + if (ops.jobs_specified ()) + r.jobs = ops.jobs (); + else if (ops.serial_stop ()) + r.jobs = 1; + + if (def_jobs != 0) + r.jobs = def_jobs; + else + { + if (r.jobs == 0) + r.jobs = scheduler::hardware_concurrency (); + + if (r.jobs == 0) + { + warn << "unable to determine the number of hardware threads" << + info << "falling back to serial execution" << + info << "use --jobs|-j to override"; + + r.jobs = 1; + } + } + + if (ops.max_jobs_specified ()) + { + r.max_jobs = ops.max_jobs (); + + if (r.max_jobs != 0 && r.max_jobs < r.jobs) + fail << "invalid --max-jobs|-J value"; + } + + r.max_stack = (ops.max_stack_specified () + ? optional (ops.max_stack () * 1024) + : nullopt); + + if (ops.file_cache_specified ()) + { + const string& v (ops.file_cache ()); + if (v == "noop" || v == "none") + r.fcache_compress = false; + else if (v == "sync-lz4") + r.fcache_compress = true; + else + fail << "invalid --file-cache value '" << v << "'"; + } + + return r; + } +} diff --git a/libbuild2/b-cmdline.hxx b/libbuild2/b-cmdline.hxx new file mode 100644 index 0000000..c5c82fc --- /dev/null +++ b/libbuild2/b-cmdline.hxx @@ -0,0 +1,44 @@ +// file : libbuild2/b-cmdline.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUILD2_B_CMDLINE_HXX +#define LIBBUILD2_B_CMDLINE_HXX + +#include +#include +#include + +#include +#include + +#include + +namespace build2 +{ + struct b_cmdline + { + strings cmd_vars; + string buildspec; + + // Processed/meged option values (unless --help or --version specified). + // + uint16_t verbosity = 1; + optional progress; + optional mtime_check; + optional config_sub; + optional config_guess; + size_t jobs = 0; + size_t max_jobs = 0; + optional max_stack; + bool fcache_compress = true; + }; + + LIBBUILD2_SYMEXPORT b_cmdline + parse_b_cmdline (tracer&, + int argc, char* argv[], + b_options&, + uint16_t default_verbosity = 1, + size_t default_jobs = 0); +} + +#endif // LIBBUILD2_B_CMDLINE_HXX diff --git a/libbuild2/b-options.cxx b/libbuild2/b-options.cxx index b3a330c..223233a 100644 --- a/libbuild2/b-options.cxx +++ b/libbuild2/b-options.cxx @@ -233,11 +233,11 @@ namespace build2 namespace build2 { - // options + // b_options // - options:: - options () + b_options:: + b_options () : build2_metadata_ (), build2_metadata_specified_ (false), v_ (), @@ -291,7 +291,7 @@ namespace build2 { } - bool options:: + bool b_options:: parse (int& argc, char** argv, bool erase, @@ -303,7 +303,7 @@ namespace build2 return r; } - bool options:: + bool b_options:: parse (int start, int& argc, char** argv, @@ -316,7 +316,7 @@ namespace build2 return r; } - bool options:: + bool b_options:: parse (int& argc, char** argv, int& end, @@ -330,7 +330,7 @@ namespace build2 return r; } - bool options:: + bool b_options:: parse (int start, int& argc, char** argv, @@ -345,7 +345,7 @@ namespace build2 return r; } - bool options:: + bool b_options:: parse (::build2::build::cli::scanner& s, ::build2::build::cli::unknown_mode opt, ::build2::build::cli::unknown_mode arg) @@ -354,8 +354,8 @@ namespace build2 return r; } - void options:: - merge (const options& a) + void b_options:: + merge (const b_options& a) { CLI_POTENTIALLY_UNUSED (a); @@ -580,7 +580,7 @@ namespace build2 } } - ::build2::build::cli::usage_para options:: + ::build2::build::cli::usage_para b_options:: print_usage (::std::ostream& os, ::build2::build::cli::usage_para p) { CLI_POTENTIALLY_UNUSED (os); @@ -888,125 +888,125 @@ namespace build2 } typedef - std::map - _cli_options_map; + std::map + _cli_b_options_map; - static _cli_options_map _cli_options_map_; + static _cli_b_options_map _cli_b_options_map_; - struct _cli_options_map_init + struct _cli_b_options_map_init { - _cli_options_map_init () + _cli_b_options_map_init () { - _cli_options_map_["--build2-metadata"] = - &::build2::build::cli::thunk< options, uint64_t, &options::build2_metadata_, - &options::build2_metadata_specified_ >; - _cli_options_map_["-v"] = - &::build2::build::cli::thunk< options, bool, &options::v_ >; - _cli_options_map_["-V"] = - &::build2::build::cli::thunk< options, bool, &options::V_ >; - _cli_options_map_["--quiet"] = - &::build2::build::cli::thunk< options, bool, &options::quiet_ >; - _cli_options_map_["-q"] = - &::build2::build::cli::thunk< options, bool, &options::quiet_ >; - _cli_options_map_["--silent"] = - &::build2::build::cli::thunk< options, bool, &options::silent_ >; - _cli_options_map_["--verbose"] = - &::build2::build::cli::thunk< options, uint16_t, &options::verbose_, - &options::verbose_specified_ >; - _cli_options_map_["--stat"] = - &::build2::build::cli::thunk< options, bool, &options::stat_ >; - _cli_options_map_["--dump"] = - &::build2::build::cli::thunk< options, std::set, &options::dump_, - &options::dump_specified_ >; - _cli_options_map_["--progress"] = - &::build2::build::cli::thunk< options, bool, &options::progress_ >; - _cli_options_map_["--no-progress"] = - &::build2::build::cli::thunk< options, bool, &options::no_progress_ >; - _cli_options_map_["--jobs"] = - &::build2::build::cli::thunk< options, size_t, &options::jobs_, - &options::jobs_specified_ >; - _cli_options_map_["-j"] = - &::build2::build::cli::thunk< options, size_t, &options::jobs_, - &options::jobs_specified_ >; - _cli_options_map_["--max-jobs"] = - &::build2::build::cli::thunk< options, size_t, &options::max_jobs_, - &options::max_jobs_specified_ >; - _cli_options_map_["-J"] = - &::build2::build::cli::thunk< options, size_t, &options::max_jobs_, - &options::max_jobs_specified_ >; - _cli_options_map_["--queue-depth"] = - &::build2::build::cli::thunk< options, size_t, &options::queue_depth_, - &options::queue_depth_specified_ >; - _cli_options_map_["-Q"] = - &::build2::build::cli::thunk< options, size_t, &options::queue_depth_, - &options::queue_depth_specified_ >; - _cli_options_map_["--file-cache"] = - &::build2::build::cli::thunk< options, string, &options::file_cache_, - &options::file_cache_specified_ >; - _cli_options_map_["--max-stack"] = - &::build2::build::cli::thunk< options, size_t, &options::max_stack_, - &options::max_stack_specified_ >; - _cli_options_map_["--serial-stop"] = - &::build2::build::cli::thunk< options, bool, &options::serial_stop_ >; - _cli_options_map_["-s"] = - &::build2::build::cli::thunk< options, bool, &options::serial_stop_ >; - _cli_options_map_["--dry-run"] = - &::build2::build::cli::thunk< options, bool, &options::dry_run_ >; - _cli_options_map_["-n"] = - &::build2::build::cli::thunk< options, bool, &options::dry_run_ >; - _cli_options_map_["--match-only"] = - &::build2::build::cli::thunk< options, bool, &options::match_only_ >; - _cli_options_map_["--no-external-modules"] = - &::build2::build::cli::thunk< options, bool, &options::no_external_modules_ >; - _cli_options_map_["--structured-result"] = - &::build2::build::cli::thunk< options, structured_result_format, &options::structured_result_, - &options::structured_result_specified_ >; - _cli_options_map_["--mtime-check"] = - &::build2::build::cli::thunk< options, bool, &options::mtime_check_ >; - _cli_options_map_["--no-mtime-check"] = - &::build2::build::cli::thunk< options, bool, &options::no_mtime_check_ >; - _cli_options_map_["--no-column"] = - &::build2::build::cli::thunk< options, bool, &options::no_column_ >; - _cli_options_map_["--no-line"] = - &::build2::build::cli::thunk< options, bool, &options::no_line_ >; - _cli_options_map_["--buildfile"] = - &::build2::build::cli::thunk< options, path, &options::buildfile_, - &options::buildfile_specified_ >; - _cli_options_map_["--config-guess"] = - &::build2::build::cli::thunk< options, path, &options::config_guess_, - &options::config_guess_specified_ >; - _cli_options_map_["--config-sub"] = - &::build2::build::cli::thunk< options, path, &options::config_sub_, - &options::config_sub_specified_ >; - _cli_options_map_["--pager"] = - &::build2::build::cli::thunk< options, string, &options::pager_, - &options::pager_specified_ >; - _cli_options_map_["--pager-option"] = - &::build2::build::cli::thunk< options, strings, &options::pager_option_, - &options::pager_option_specified_ >; - _cli_options_map_["--options-file"] = - &::build2::build::cli::thunk< options, string, &options::options_file_, - &options::options_file_specified_ >; - _cli_options_map_["--default-options"] = - &::build2::build::cli::thunk< options, dir_path, &options::default_options_, - &options::default_options_specified_ >; - _cli_options_map_["--no-default-options"] = - &::build2::build::cli::thunk< options, bool, &options::no_default_options_ >; - _cli_options_map_["--help"] = - &::build2::build::cli::thunk< options, bool, &options::help_ >; - _cli_options_map_["--version"] = - &::build2::build::cli::thunk< options, bool, &options::version_ >; + _cli_b_options_map_["--build2-metadata"] = + &::build2::build::cli::thunk< b_options, uint64_t, &b_options::build2_metadata_, + &b_options::build2_metadata_specified_ >; + _cli_b_options_map_["-v"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::v_ >; + _cli_b_options_map_["-V"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::V_ >; + _cli_b_options_map_["--quiet"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::quiet_ >; + _cli_b_options_map_["-q"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::quiet_ >; + _cli_b_options_map_["--silent"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::silent_ >; + _cli_b_options_map_["--verbose"] = + &::build2::build::cli::thunk< b_options, uint16_t, &b_options::verbose_, + &b_options::verbose_specified_ >; + _cli_b_options_map_["--stat"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::stat_ >; + _cli_b_options_map_["--dump"] = + &::build2::build::cli::thunk< b_options, std::set, &b_options::dump_, + &b_options::dump_specified_ >; + _cli_b_options_map_["--progress"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::progress_ >; + _cli_b_options_map_["--no-progress"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::no_progress_ >; + _cli_b_options_map_["--jobs"] = + &::build2::build::cli::thunk< b_options, size_t, &b_options::jobs_, + &b_options::jobs_specified_ >; + _cli_b_options_map_["-j"] = + &::build2::build::cli::thunk< b_options, size_t, &b_options::jobs_, + &b_options::jobs_specified_ >; + _cli_b_options_map_["--max-jobs"] = + &::build2::build::cli::thunk< b_options, size_t, &b_options::max_jobs_, + &b_options::max_jobs_specified_ >; + _cli_b_options_map_["-J"] = + &::build2::build::cli::thunk< b_options, size_t, &b_options::max_jobs_, + &b_options::max_jobs_specified_ >; + _cli_b_options_map_["--queue-depth"] = + &::build2::build::cli::thunk< b_options, size_t, &b_options::queue_depth_, + &b_options::queue_depth_specified_ >; + _cli_b_options_map_["-Q"] = + &::build2::build::cli::thunk< b_options, size_t, &b_options::queue_depth_, + &b_options::queue_depth_specified_ >; + _cli_b_options_map_["--file-cache"] = + &::build2::build::cli::thunk< b_options, string, &b_options::file_cache_, + &b_options::file_cache_specified_ >; + _cli_b_options_map_["--max-stack"] = + &::build2::build::cli::thunk< b_options, size_t, &b_options::max_stack_, + &b_options::max_stack_specified_ >; + _cli_b_options_map_["--serial-stop"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::serial_stop_ >; + _cli_b_options_map_["-s"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::serial_stop_ >; + _cli_b_options_map_["--dry-run"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::dry_run_ >; + _cli_b_options_map_["-n"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::dry_run_ >; + _cli_b_options_map_["--match-only"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::match_only_ >; + _cli_b_options_map_["--no-external-modules"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::no_external_modules_ >; + _cli_b_options_map_["--structured-result"] = + &::build2::build::cli::thunk< b_options, structured_result_format, &b_options::structured_result_, + &b_options::structured_result_specified_ >; + _cli_b_options_map_["--mtime-check"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::mtime_check_ >; + _cli_b_options_map_["--no-mtime-check"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::no_mtime_check_ >; + _cli_b_options_map_["--no-column"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::no_column_ >; + _cli_b_options_map_["--no-line"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::no_line_ >; + _cli_b_options_map_["--buildfile"] = + &::build2::build::cli::thunk< b_options, path, &b_options::buildfile_, + &b_options::buildfile_specified_ >; + _cli_b_options_map_["--config-guess"] = + &::build2::build::cli::thunk< b_options, path, &b_options::config_guess_, + &b_options::config_guess_specified_ >; + _cli_b_options_map_["--config-sub"] = + &::build2::build::cli::thunk< b_options, path, &b_options::config_sub_, + &b_options::config_sub_specified_ >; + _cli_b_options_map_["--pager"] = + &::build2::build::cli::thunk< b_options, string, &b_options::pager_, + &b_options::pager_specified_ >; + _cli_b_options_map_["--pager-option"] = + &::build2::build::cli::thunk< b_options, strings, &b_options::pager_option_, + &b_options::pager_option_specified_ >; + _cli_b_options_map_["--options-file"] = + &::build2::build::cli::thunk< b_options, string, &b_options::options_file_, + &b_options::options_file_specified_ >; + _cli_b_options_map_["--default-options"] = + &::build2::build::cli::thunk< b_options, dir_path, &b_options::default_options_, + &b_options::default_options_specified_ >; + _cli_b_options_map_["--no-default-options"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::no_default_options_ >; + _cli_b_options_map_["--help"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::help_ >; + _cli_b_options_map_["--version"] = + &::build2::build::cli::thunk< b_options, bool, &b_options::version_ >; } }; - static _cli_options_map_init _cli_options_map_init_; + static _cli_b_options_map_init _cli_b_options_map_init_; - bool options:: + bool b_options:: _parse (const char* o, ::build2::build::cli::scanner& s) { - _cli_options_map::const_iterator i (_cli_options_map_.find (o)); + _cli_b_options_map::const_iterator i (_cli_b_options_map_.find (o)); - if (i != _cli_options_map_.end ()) + if (i != _cli_b_options_map_.end ()) { (*(i->second)) (*this, s); return true; @@ -1015,7 +1015,7 @@ namespace build2 return false; } - bool options:: + bool b_options:: _parse (::build2::build::cli::scanner& s, ::build2::build::cli::unknown_mode opt_mode, ::build2::build::cli::unknown_mode arg_mode) @@ -1209,7 +1209,7 @@ namespace build2 << "\033[1m'--'\033[0m separator. To avoid treating an argument that contains \033[1m'='\033[0m as a variable," << ::std::endl << "add the second \033[1m'--'\033[0m separator." << ::std::endl; - p = ::build2::options::print_usage (os, ::build2::build::cli::usage_para::text); + p = ::build2::b_options::print_usage (os, ::build2::build::cli::usage_para::text); if (p != ::build2::build::cli::usage_para::none) os << ::std::endl; diff --git a/libbuild2/b-options.hxx b/libbuild2/b-options.hxx index ffdecf5..d8d85d3 100644 --- a/libbuild2/b-options.hxx +++ b/libbuild2/b-options.hxx @@ -19,10 +19,10 @@ namespace build2 { - class LIBBUILD2_SYMEXPORT options + class LIBBUILD2_SYMEXPORT b_options { public: - options (); + b_options (); // Return true if anything has been parsed. // @@ -67,7 +67,7 @@ namespace build2 // them as if they appeared after options in this instance. // void - merge (const options&); + merge (const b_options&); // Option accessors. // diff --git a/libbuild2/b-options.ixx b/libbuild2/b-options.ixx index 8030ef3..b7944ba 100644 --- a/libbuild2/b-options.ixx +++ b/libbuild2/b-options.ixx @@ -11,304 +11,304 @@ namespace build2 { - // options + // b_options // - inline const uint64_t& options:: + inline const uint64_t& b_options:: build2_metadata () const { return this->build2_metadata_; } - inline bool options:: + inline bool b_options:: build2_metadata_specified () const { return this->build2_metadata_specified_; } - inline const bool& options:: + inline const bool& b_options:: v () const { return this->v_; } - inline const bool& options:: + inline const bool& b_options:: V () const { return this->V_; } - inline const bool& options:: + inline const bool& b_options:: quiet () const { return this->quiet_; } - inline const bool& options:: + inline const bool& b_options:: silent () const { return this->silent_; } - inline const uint16_t& options:: + inline const uint16_t& b_options:: verbose () const { return this->verbose_; } - inline bool options:: + inline bool b_options:: verbose_specified () const { return this->verbose_specified_; } - inline const bool& options:: + inline const bool& b_options:: stat () const { return this->stat_; } - inline const std::set& options:: + inline const std::set& b_options:: dump () const { return this->dump_; } - inline bool options:: + inline bool b_options:: dump_specified () const { return this->dump_specified_; } - inline const bool& options:: + inline const bool& b_options:: progress () const { return this->progress_; } - inline const bool& options:: + inline const bool& b_options:: no_progress () const { return this->no_progress_; } - inline const size_t& options:: + inline const size_t& b_options:: jobs () const { return this->jobs_; } - inline bool options:: + inline bool b_options:: jobs_specified () const { return this->jobs_specified_; } - inline const size_t& options:: + inline const size_t& b_options:: max_jobs () const { return this->max_jobs_; } - inline bool options:: + inline bool b_options:: max_jobs_specified () const { return this->max_jobs_specified_; } - inline const size_t& options:: + inline const size_t& b_options:: queue_depth () const { return this->queue_depth_; } - inline bool options:: + inline bool b_options:: queue_depth_specified () const { return this->queue_depth_specified_; } - inline const string& options:: + inline const string& b_options:: file_cache () const { return this->file_cache_; } - inline bool options:: + inline bool b_options:: file_cache_specified () const { return this->file_cache_specified_; } - inline const size_t& options:: + inline const size_t& b_options:: max_stack () const { return this->max_stack_; } - inline bool options:: + inline bool b_options:: max_stack_specified () const { return this->max_stack_specified_; } - inline const bool& options:: + inline const bool& b_options:: serial_stop () const { return this->serial_stop_; } - inline const bool& options:: + inline const bool& b_options:: dry_run () const { return this->dry_run_; } - inline const bool& options:: + inline const bool& b_options:: match_only () const { return this->match_only_; } - inline const bool& options:: + inline const bool& b_options:: no_external_modules () const { return this->no_external_modules_; } - inline const structured_result_format& options:: + inline const structured_result_format& b_options:: structured_result () const { return this->structured_result_; } - inline bool options:: + inline bool b_options:: structured_result_specified () const { return this->structured_result_specified_; } - inline const bool& options:: + inline const bool& b_options:: mtime_check () const { return this->mtime_check_; } - inline const bool& options:: + inline const bool& b_options:: no_mtime_check () const { return this->no_mtime_check_; } - inline const bool& options:: + inline const bool& b_options:: no_column () const { return this->no_column_; } - inline const bool& options:: + inline const bool& b_options:: no_line () const { return this->no_line_; } - inline const path& options:: + inline const path& b_options:: buildfile () const { return this->buildfile_; } - inline bool options:: + inline bool b_options:: buildfile_specified () const { return this->buildfile_specified_; } - inline const path& options:: + inline const path& b_options:: config_guess () const { return this->config_guess_; } - inline bool options:: + inline bool b_options:: config_guess_specified () const { return this->config_guess_specified_; } - inline const path& options:: + inline const path& b_options:: config_sub () const { return this->config_sub_; } - inline bool options:: + inline bool b_options:: config_sub_specified () const { return this->config_sub_specified_; } - inline const string& options:: + inline const string& b_options:: pager () const { return this->pager_; } - inline bool options:: + inline bool b_options:: pager_specified () const { return this->pager_specified_; } - inline const strings& options:: + inline const strings& b_options:: pager_option () const { return this->pager_option_; } - inline bool options:: + inline bool b_options:: pager_option_specified () const { return this->pager_option_specified_; } - inline const string& options:: + inline const string& b_options:: options_file () const { return this->options_file_; } - inline bool options:: + inline bool b_options:: options_file_specified () const { return this->options_file_specified_; } - inline const dir_path& options:: + inline const dir_path& b_options:: default_options () const { return this->default_options_; } - inline bool options:: + inline bool b_options:: default_options_specified () const { return this->default_options_specified_; } - inline const bool& options:: + inline const bool& b_options:: no_default_options () const { return this->no_default_options_; } - inline const bool& options:: + inline const bool& b_options:: help () const { return this->help_; } - inline const bool& options:: + inline const bool& b_options:: version () const { return this->version_; diff --git a/libbuild2/b.cli b/libbuild2/b.cli index b1f8a5f..3ae6e0b 100644 --- a/libbuild2/b.cli +++ b/libbuild2/b.cli @@ -477,7 +477,7 @@ namespace build2 " } - class options + class b_options { "\h#options|OPTIONS|" diff --git a/libbuild2/cmdline.cxx b/libbuild2/cmdline.cxx deleted file mode 100644 index e022762..0000000 --- a/libbuild2/cmdline.cxx +++ /dev/null @@ -1,478 +0,0 @@ -// file : libbuild2/cmdline.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include - -#include -#include // strcmp(), strchr() - -#include - -#include -#include -#include - -using namespace std; -using namespace butl; - -namespace cli = build2::build::cli; - -namespace build2 -{ - cmdline - parse_cmdline (tracer& trace, - int argc, char* argv[], - options& ops, - uint16_t def_verb, - size_t def_jobs) - { - // Note that the diagnostics verbosity level can only be calculated after - // default options are loaded and merged (see below). Thus, until then we - // refer to the verbosity level specified on the command line. - // - auto verbosity = [&ops, def_verb] () - { - uint16_t v ( - ops.verbose_specified () - ? ops.verbose () - : (ops.V () ? 3 : - ops.v () ? 2 : - ops.quiet () || ops.silent () ? 0 : def_verb)); - return v; - }; - - cmdline r; - - // We want to be able to specify options, vars, and buildspecs in any - // order (it is really handy to just add -v at the end of the command - // line). - // - try - { - // Command line arguments starting position. - // - // We want the positions of the command line arguments to be after the - // default options files. Normally that would be achieved by passing the - // last position of the previous scanner to the next. The problem is - // that we parse the command line arguments first (for good reasons). - // Also the default options files parsing machinery needs the maximum - // number of arguments to be specified and assigns the positions below - // this value (see load_default_options() for details). So we are going - // to "reserve" the first half of the size_t value range for the default - // options positions and the second half for the command line arguments - // positions. - // - size_t args_pos (numeric_limits::max () / 2); - cli::argv_file_scanner scan (argc, argv, "--options-file", args_pos); - - size_t argn (0); // Argument count. - bool shortcut (false); // True if the shortcut syntax is used. - - for (bool opt (true), var (true); scan.more (); ) - { - if (opt) - { - // Parse the next chunk of options until we reach an argument (or - // eos). - // - if (ops.parse (scan) && !scan.more ()) - break; - - // If we see first "--", then we are done parsing options. - // - if (strcmp (scan.peek (), "--") == 0) - { - scan.next (); - opt = false; - continue; - } - - // Fall through. - } - - const char* s (scan.next ()); - - // See if this is a command line variable. What if someone needs to - // pass a buildspec that contains '='? One way to support this would - // be to quote such a buildspec (e.g., "'/tmp/foo=bar/'"). Or invent - // another separator. Or use a second "--". Actually, let's just do - // the second "--". - // - if (var) - { - // If we see second "--", then we are also done parsing variables. - // - if (strcmp (s, "--") == 0) - { - var = false; - continue; - } - - if (const char* p = strchr (s, '=')) // Covers =, +=, and =+. - { - // Diagnose the empty variable name situation. Note that we don't - // allow "partially broken down" assignments (as in foo =bar) - // since foo= bar would be ambigous. - // - if (p == s || (p == s + 1 && *s == '+')) - fail << "missing variable name in '" << s << "'"; - - r.cmd_vars.push_back (s); - continue; - } - - // Handle the "broken down" variable assignments (i.e., foo = bar - // instead of foo=bar). - // - if (scan.more ()) - { - const char* a (scan.peek ()); - - if (strcmp (a, "=" ) == 0 || - strcmp (a, "+=") == 0 || - strcmp (a, "=+") == 0) - { - string v (s); - v += a; - - scan.next (); - - if (scan.more ()) - v += scan.next (); - - r.cmd_vars.push_back (move (v)); - continue; - } - } - - // Fall through. - } - - // Merge all the individual buildspec arguments into a single string. - // We use newlines to separate arguments so that line numbers in - // diagnostics signify argument numbers. Clever, huh? - // - if (argn != 0) - r.buildspec += '\n'; - - r.buildspec += s; - - // See if we are using the shortcut syntax. - // - if (argn == 0 && r.buildspec.back () == ':') - { - r.buildspec.back () = '('; - shortcut = true; - } - - argn++; - } - - // Add the closing parenthesis unless there wasn't anything in between - // in which case pop the opening one. - // - if (shortcut) - { - if (argn == 1) - r.buildspec.pop_back (); - else - r.buildspec += ')'; - } - - // Get/set an environment variable tracing the operation. - // - auto get_env = [&verbosity, &trace] (const char* nm) - { - optional r (getenv (nm)); - - if (verbosity () >= 5) - { - if (r) - trace << nm << ": '" << *r << "'"; - else - trace << nm << ": "; - } - - return r; - }; - - auto set_env = [&verbosity, &trace] (const char* nm, const string& vl) - { - try - { - if (verbosity () >= 5) - trace << "setting " << nm << "='" << vl << "'"; - - setenv (nm, vl); - } - catch (const system_error& e) - { - // The variable value can potentially be long/multi-line, so let's - // print it last. - // - fail << "unable to set environment variable " << nm << ": " << e << - info << "value: '" << vl << "'"; - } - }; - - // If the BUILD2_VAR_OVR environment variable is present, then parse its - // value as a newline-separated global variable overrides and prepend - // them to the overrides specified on the command line. - // - // Note that this means global overrides may not contain a newline. - - // Verify that the string is a valid global override. Uses the file name - // and the options flag for diagnostics only. - // - auto verify_glb_ovr = [] (const string& v, const path_name& fn, bool opt) - { - size_t p (v.find ('=', 1)); - if (p == string::npos || v[0] != '!') - { - diag_record dr (fail (fn)); - dr << "expected " << (opt ? "option or " : "") << "global " - << "variable override instead of '" << v << "'"; - - if (p != string::npos) - dr << info << "prefix variable assignment with '!'"; - } - - if (p == 1 || (p == 2 && v[1] == '+')) // '!=' or '!+=' ? - fail (fn) << "missing variable name in '" << v << "'"; - }; - - optional env_ovr (get_env ("BUILD2_VAR_OVR")); - if (env_ovr) - { - path_name fn (""); - - auto i (r.cmd_vars.begin ()); - for (size_t b (0), e (0); next_word (*env_ovr, b, e, '\n', '\r'); ) - { - // Extract the override from the current line, stripping the leading - // and trailing spaces. - // - string s (*env_ovr, b, e - b); - trim (s); - - // Verify and save the override, unless the line is empty. - // - if (!s.empty ()) - { - verify_glb_ovr (s, fn, false /* opt */); - i = r.cmd_vars.insert (i, move (s)) + 1; - } - } - } - - // Load the default options files, unless --no-default-options is - // specified on the command line or the BUILD2_DEF_OPT environment - // variable is set to a value other than 'true' or '1'. - // - // If loaded, prepend the default global overrides to the variables - // specified on the command line, unless BUILD2_VAR_OVR is set in which - // case just ignore them. - // - optional env_def (get_env ("BUILD2_DEF_OPT")); - - // False if --no-default-options is specified on the command line. Note - // that we cache the flag since it can be overridden by a default - // options file. - // - bool cmd_def (!ops.no_default_options ()); - - if (cmd_def && (!env_def || *env_def == "true" || *env_def == "1")) - try - { - optional extra; - if (ops.default_options_specified ()) - extra = ops.default_options (); - - // Load default options files. - // - default_options def_ops ( - load_default_options ( - nullopt /* sys_dir */, - path::home_directory (), // The home variable is not assigned yet. - extra, - default_options_files {{path ("b.options")}, - nullopt /* start */}, - [&trace, &verbosity] (const path& f, bool r, bool o) - { - if (verbosity () >= 3) - { - if (o) - trace << "treating " << f << " as " - << (r ? "remote" : "local"); - else - trace << "loading " << (r ? "remote " : "local ") << f; - } - }, - "--options-file", - args_pos, - 1024, - true /* args */)); - - // Merge the default and command line options. - // - ops = merge_default_options (def_ops, ops); - - // Merge the default and command line global overrides, unless - // BUILD2_VAR_OVR is already set (in which case we assume this has - // already been done). - // - // Note that the "broken down" variable assignments occupying a single - // line are naturally supported. - // - if (!env_ovr) - r.cmd_vars = - merge_default_arguments ( - def_ops, - r.cmd_vars, - [&verify_glb_ovr] (const default_options_entry& e, - const strings&) - { - path_name fn (e.file); - - // Verify that all arguments are global overrides. - // - for (const string& a: e.arguments) - verify_glb_ovr (a, fn, true /* opt */); - }); - } - catch (const invalid_argument& e) - { - fail << "unable to load default options files: " << e; - } - catch (const pair& e) - { - fail << "unable to load default options files: " << e.first << ": " - << e.second; - } - catch (const system_error& e) - { - fail << "unable to obtain home directory: " << e; - } - - // Verify and save the global overrides present in cmd_vars (default, - // from the command line, etc), if any, into the BUILD2_VAR_OVR - // environment variable. - // - if (!r.cmd_vars.empty ()) - { - string ovr; - for (const string& v: r.cmd_vars) - { - if (v[0] == '!') - { - if (v.find_first_of ("\n\r") != string::npos) - fail << "newline in global variable override '" << v << "'"; - - if (!ovr.empty ()) - ovr += '\n'; - - ovr += v; - } - } - - // Optimize for the common case. - // - // Note: cmd_vars may contain non-global overrides. - // - if (!ovr.empty () && (!env_ovr || *env_ovr != ovr)) - set_env ("BUILD2_VAR_OVR", ovr); - } - - // Propagate disabling of the default options files to the potential - // nested invocations. - // - if (!cmd_def && (!env_def || *env_def != "0")) - set_env ("BUILD2_DEF_OPT", "0"); - - // Validate options. - // - if (ops.progress () && ops.no_progress ()) - fail << "both --progress and --no-progress specified"; - - if (ops.mtime_check () && ops.no_mtime_check ()) - fail << "both --mtime-check and --no-mtime-check specified"; - } - catch (const cli::exception& e) - { - fail << e; - } - - if (ops.help () || ops.version ()) - return r; - - r.verbosity = verbosity (); - - if (ops.silent () && r.verbosity != 0) - fail << "specified with -v, -V, or --verbose verbosity level " - << r.verbosity << " is incompatible with --silent"; - - r.progress = (ops.progress () ? optional (true) : - ops.no_progress () ? optional (false) : nullopt); - - r.mtime_check = (ops.mtime_check () ? optional (true) : - ops.no_mtime_check () ? optional (false) : nullopt); - - - r.config_sub = (ops.config_sub_specified () - ? optional (ops.config_sub ()) - : nullopt); - - r.config_guess = (ops.config_guess_specified () - ? optional (ops.config_guess ()) - : nullopt); - - if (ops.jobs_specified ()) - r.jobs = ops.jobs (); - else if (ops.serial_stop ()) - r.jobs = 1; - - if (def_jobs != 0) - r.jobs = def_jobs; - else - { - if (r.jobs == 0) - r.jobs = scheduler::hardware_concurrency (); - - if (r.jobs == 0) - { - warn << "unable to determine the number of hardware threads" << - info << "falling back to serial execution" << - info << "use --jobs|-j to override"; - - r.jobs = 1; - } - } - - if (ops.max_jobs_specified ()) - { - r.max_jobs = ops.max_jobs (); - - if (r.max_jobs != 0 && r.max_jobs < r.jobs) - fail << "invalid --max-jobs|-J value"; - } - - r.max_stack = (ops.max_stack_specified () - ? optional (ops.max_stack () * 1024) - : nullopt); - - if (ops.file_cache_specified ()) - { - const string& v (ops.file_cache ()); - if (v == "noop" || v == "none") - r.fcache_compress = false; - else if (v == "sync-lz4") - r.fcache_compress = true; - else - fail << "invalid --file-cache value '" << v << "'"; - } - - return r; - } -} diff --git a/libbuild2/cmdline.hxx b/libbuild2/cmdline.hxx deleted file mode 100644 index 56e9510..0000000 --- a/libbuild2/cmdline.hxx +++ /dev/null @@ -1,44 +0,0 @@ -// file : libbuild2/cmdline.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef LIBBUILD2_CMDLINE_HXX -#define LIBBUILD2_CMDLINE_HXX - -#include -#include -#include - -#include -#include - -#include - -namespace build2 -{ - struct cmdline - { - strings cmd_vars; - string buildspec; - - // Processed/meged option values (unless --help or --version specified). - // - uint16_t verbosity = 1; - optional progress; - optional mtime_check; - optional config_sub; - optional config_guess; - size_t jobs = 0; - size_t max_jobs = 0; - optional max_stack; - bool fcache_compress = true; - }; - - LIBBUILD2_SYMEXPORT cmdline - parse_cmdline (tracer&, - int argc, char* argv[], - options&, - uint16_t default_verbosity = 1, - size_t default_jobs = 0); -} - -#endif // LIBBUILD2_CMDLINE_HXX -- cgit v1.1