From bf2cf8cd53361826e51e01c2904f6fb4cb7502dc Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 16 Aug 2019 22:39:07 +0300 Subject: Add support for default options files --- build2/b-options.cxx | 230 +++++++++++++++++++++++++++++++++++++++++++++++ build2/b-options.hxx | 10 +++ build2/b-options.ixx | 6 ++ build2/b.cli | 5 ++ build2/b.cxx | 51 +++++++++-- build2/buildfile | 2 +- build2/types-parsers.hxx | 6 ++ libbuild2/utility.cxx | 10 ++- 8 files changed, 312 insertions(+), 8 deletions(-) diff --git a/build2/b-options.cxx b/build2/b-options.cxx index 4c8e63b..9ad7429 100644 --- a/build2/b-options.cxx +++ b/build2/b-options.cxx @@ -476,6 +476,12 @@ namespace build2 xs = true; } + + static void + merge (X& b, const X& a) + { + b = a; + } }; template <> @@ -487,6 +493,12 @@ namespace build2 s.next (); x = true; } + + static void + merge (bool& b, const bool&) + { + b = true; + } }; template <> @@ -504,6 +516,12 @@ namespace build2 xs = true; } + + static void + merge (std::string& b, const std::string& a) + { + b = a; + } }; template @@ -518,6 +536,12 @@ namespace build2 c.push_back (x); xs = true; } + + static void + merge (std::vector& b, const std::vector& a) + { + b.insert (b.end (), a.begin (), a.end ()); + } }; template @@ -532,6 +556,12 @@ namespace build2 c.insert (x); xs = true; } + + static void + merge (std::set& b, const std::set& a) + { + b.insert (a.begin (), a.end ()); + } }; template @@ -580,6 +610,15 @@ namespace build2 xs = true; } + + static void + merge (std::map& b, const std::map& a) + { + for (typename std::map::const_iterator i (a.begin ()); + i != a.end (); + ++i) + b[i->first] = i->second; + } }; template @@ -644,6 +683,7 @@ namespace build2 pager_specified_ (false), pager_option_ (), pager_option_specified_ (false), + no_default_options_ (), help_ (), version_ () { @@ -712,6 +752,191 @@ namespace build2 return r; } + void options:: + merge (const options& a) + { + CLI_POTENTIALLY_UNUSED (a); + + if (a.v_) + { + ::build2::cl::parser< bool>::merge ( + this->v_, a.v_); + } + + if (a.V_) + { + ::build2::cl::parser< bool>::merge ( + this->V_, a.V_); + } + + if (a.quiet_) + { + ::build2::cl::parser< bool>::merge ( + this->quiet_, a.quiet_); + } + + if (a.verbose_specified_) + { + ::build2::cl::parser< uint16_t>::merge ( + this->verbose_, a.verbose_); + this->verbose_specified_ = true; + } + + if (a.stat_) + { + ::build2::cl::parser< bool>::merge ( + this->stat_, a.stat_); + } + + if (a.dump_specified_) + { + ::build2::cl::parser< std::set>::merge ( + this->dump_, a.dump_); + this->dump_specified_ = true; + } + + if (a.progress_) + { + ::build2::cl::parser< bool>::merge ( + this->progress_, a.progress_); + } + + if (a.no_progress_) + { + ::build2::cl::parser< bool>::merge ( + this->no_progress_, a.no_progress_); + } + + if (a.jobs_specified_) + { + ::build2::cl::parser< size_t>::merge ( + this->jobs_, a.jobs_); + this->jobs_specified_ = true; + } + + if (a.max_jobs_specified_) + { + ::build2::cl::parser< size_t>::merge ( + this->max_jobs_, a.max_jobs_); + this->max_jobs_specified_ = true; + } + + if (a.queue_depth_specified_) + { + ::build2::cl::parser< size_t>::merge ( + this->queue_depth_, a.queue_depth_); + this->queue_depth_specified_ = true; + } + + if (a.max_stack_specified_) + { + ::build2::cl::parser< size_t>::merge ( + this->max_stack_, a.max_stack_); + this->max_stack_specified_ = true; + } + + if (a.serial_stop_) + { + ::build2::cl::parser< bool>::merge ( + this->serial_stop_, a.serial_stop_); + } + + if (a.dry_run_) + { + ::build2::cl::parser< bool>::merge ( + this->dry_run_, a.dry_run_); + } + + if (a.match_only_) + { + ::build2::cl::parser< bool>::merge ( + this->match_only_, a.match_only_); + } + + if (a.structured_result_) + { + ::build2::cl::parser< bool>::merge ( + this->structured_result_, a.structured_result_); + } + + if (a.mtime_check_) + { + ::build2::cl::parser< bool>::merge ( + this->mtime_check_, a.mtime_check_); + } + + if (a.no_mtime_check_) + { + ::build2::cl::parser< bool>::merge ( + this->no_mtime_check_, a.no_mtime_check_); + } + + if (a.no_column_) + { + ::build2::cl::parser< bool>::merge ( + this->no_column_, a.no_column_); + } + + if (a.no_line_) + { + ::build2::cl::parser< bool>::merge ( + this->no_line_, a.no_line_); + } + + if (a.buildfile_specified_) + { + ::build2::cl::parser< path>::merge ( + this->buildfile_, a.buildfile_); + this->buildfile_specified_ = true; + } + + if (a.config_guess_specified_) + { + ::build2::cl::parser< path>::merge ( + this->config_guess_, a.config_guess_); + this->config_guess_specified_ = true; + } + + if (a.config_sub_specified_) + { + ::build2::cl::parser< path>::merge ( + this->config_sub_, a.config_sub_); + this->config_sub_specified_ = true; + } + + if (a.pager_specified_) + { + ::build2::cl::parser< string>::merge ( + this->pager_, a.pager_); + this->pager_specified_ = true; + } + + if (a.pager_option_specified_) + { + ::build2::cl::parser< strings>::merge ( + this->pager_option_, a.pager_option_); + this->pager_option_specified_ = true; + } + + if (a.no_default_options_) + { + ::build2::cl::parser< bool>::merge ( + this->no_default_options_, a.no_default_options_); + } + + if (a.help_) + { + ::build2::cl::parser< bool>::merge ( + this->help_, a.help_); + } + + if (a.version_) + { + ::build2::cl::parser< bool>::merge ( + this->version_, a.version_); + } + } + ::build2::cl::usage_para options:: print_usage (::std::ostream& os, ::build2::cl::usage_para p) { @@ -899,6 +1124,9 @@ namespace build2 << " this option to specify multiple pager options." << ::std::endl; os << std::endl + << "\033[1m--no-default-options\033[0m Don't load default options files." << ::std::endl; + + os << std::endl << "\033[1m--help\033[0m Print usage information and exit." << ::std::endl; os << std::endl @@ -995,6 +1223,8 @@ namespace build2 _cli_options_map_["--pager-option"] = &::build2::cl::thunk< options, strings, &options::pager_option_, &options::pager_option_specified_ >; + _cli_options_map_["--no-default-options"] = + &::build2::cl::thunk< options, bool, &options::no_default_options_ >; _cli_options_map_["--help"] = &::build2::cl::thunk< options, bool, &options::help_ >; _cli_options_map_["--version"] = diff --git a/build2/b-options.hxx b/build2/b-options.hxx index 3e10901..297e6a4 100644 --- a/build2/b-options.hxx +++ b/build2/b-options.hxx @@ -422,6 +422,12 @@ namespace build2 ::build2::cl::unknown_mode option = ::build2::cl::unknown_mode::fail, ::build2::cl::unknown_mode argument = ::build2::cl::unknown_mode::stop); + // Merge options from the specified instance appending/overriding + // them as if they appeared after options in this instance. + // + void + merge (const options&); + // Option accessors. // const bool& @@ -533,6 +539,9 @@ namespace build2 pager_option_specified () const; const bool& + no_default_options () const; + + const bool& help () const; const bool& @@ -593,6 +602,7 @@ namespace build2 bool pager_specified_; strings pager_option_; bool pager_option_specified_; + bool no_default_options_; bool help_; bool version_; }; diff --git a/build2/b-options.ixx b/build2/b-options.ixx index a8dd01c..ab5af3f 100644 --- a/build2/b-options.ixx +++ b/build2/b-options.ixx @@ -477,6 +477,12 @@ namespace build2 } inline const bool& options:: + no_default_options () const + { + return this->no_default_options_; + } + + inline const bool& options:: help () const { return this->help_; diff --git a/build2/b.cli b/build2/b.cli index 4ab397b..f0ddb93 100644 --- a/build2/b.cli +++ b/build2/b.cli @@ -622,6 +622,11 @@ namespace build2 specify multiple pager options." } + bool --no-default-options + { + "Don't load default options files." + } + bool --help {"Print usage information and exit."} bool --version {"Print version and exit."} }; diff --git a/build2/b.cxx b/build2/b.cxx index 8965d64..15844dc 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -19,8 +19,9 @@ #include // terminate(), set_terminate(), terminate_handler #include -#include // stderr_fd(), fdterm() -#include // backtrace() +#include // stderr_fd(), fdterm() +#include // backtrace() +#include #include #include @@ -225,6 +226,18 @@ main (int argc, char* argv[]) // try { + // Note that the diagnostics verbosity level can only be calculated after + // default options are loaded and merged (see below). Thus, to trace the + // default options files search, we refer to the verbosity level specified + // on the command line. + // + auto verbosity = [] () + { + return ops.verbose_specified () + ? ops.verbose () + : ops.V () ? 3 : ops.v () ? 2 : ops.quiet () ? 0 : 1; + }; + // 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). @@ -349,6 +362,36 @@ main (int argc, char* argv[]) args += ')'; } + // Handle default options files. + // + if (!ops.no_default_options ()) // Command line option. + try + { + ops = merge_default_options ( + load_default_options ( + nullopt /* sys_dir */, + path::home_directory (), // The home variable is not assigned yet. + default_options_files {{path ("b.options")}, + nullopt /* start_dir */}, + [&trace, &verbosity] (const path& f, bool remote) + { + if (verbosity () >= 3) + trace << "loading " << (remote ? "remote " : "local ") << f; + }), + ops); + } + 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; + } + // Validate options. // if (ops.progress () && ops.no_progress ()) @@ -364,9 +407,7 @@ main (int argc, char* argv[]) // Initialize the diagnostics state. // - init_diag ((ops.verbose_specified () - ? ops.verbose () - : ops.V () ? 3 : ops.v () ? 2 : ops.quiet () ? 0 : 1), + init_diag (verbosity (), (ops.progress () ? optional (true) : ops.no_progress () ? optional (false) : nullopt), ops.no_line (), diff --git a/build2/buildfile b/build2/buildfile index d3b937f..d8b045a 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -90,7 +90,7 @@ if $cli.configured --include-prefix build2 --guard-prefix BUILD2 \ --cxx-prologue "#include " \ --cli-namespace build2::cl --generate-file-scanner --keep-separator \ ---generate-parse --generate-specifier +--generate-parse --generate-merge --generate-specifier # Usage options. # diff --git a/build2/types-parsers.hxx b/build2/types-parsers.hxx index beea977..db55c05 100644 --- a/build2/types-parsers.hxx +++ b/build2/types-parsers.hxx @@ -24,6 +24,9 @@ namespace build2 { static void parse (path&, bool&, scanner&); + + static void + merge (path& b, const path& a) {b = a;} }; template <> @@ -31,6 +34,9 @@ namespace build2 { static void parse (dir_path&, bool&, scanner&); + + static void + merge (dir_path& b, const dir_path& a) {b = a;} }; } } diff --git a/libbuild2/utility.cxx b/libbuild2/utility.cxx index 506e88e..79545cc 100644 --- a/libbuild2/utility.cxx +++ b/libbuild2/utility.cxx @@ -143,7 +143,6 @@ namespace build2 else if (rb.sub (home)) return "~/" + rb.leaf (home).representation (); } - #endif return move (rb).representation (); @@ -515,6 +514,13 @@ namespace build2 fail << "invalid current working directory: " << e; } - home = dir_path::home_directory (); + try + { + home = dir_path::home_directory (); + } + catch (const system_error& e) + { + fail << "unable to obtain home directory: " << e; + } } } -- cgit v1.1