From 626348009e74d2ec6e9f500d27f60728b4fc81df Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 2 Apr 2015 11:53:01 +0200 Subject: Add command line option to set verbosity level -v sets it to 1 (shows actual command lines) --verbose sets it to any value in the 0-5 range --- build/b.cxx | 35 +++- build/diagnostics | 5 +- build/diagnostics.cxx | 2 +- build/options | 290 ++++++++++++++++++++++++++++ build/options.cli | 24 +++ build/options.cxx | 518 ++++++++++++++++++++++++++++++++++++++++++++++++++ build/options.ixx | 145 ++++++++++++++ 7 files changed, 1014 insertions(+), 5 deletions(-) create mode 100644 build/options create mode 100644 build/options.cli create mode 100644 build/options.cxx create mode 100644 build/options.ixx diff --git a/build/b.cxx b/build/b.cxx index d3628d9..c71e6d3 100644 --- a/build/b.cxx +++ b/build/b.cxx @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,8 @@ #include #include +#include + using namespace std; namespace build @@ -141,13 +144,39 @@ main (int argc, char* argv[]) { tracer trace ("main"); - // Initialize time conversion data that is used by localtime_r(). + cli::argv_scanner scan (argc, argv, true); + options ops (scan); + + // Version. // - tzset (); + if (ops.version ()) + { + cout << "build2 0.0.0" << endl + << "Copyright (c) 2014-2015 Code Synthesis Tools CC" << endl + << "This is free software released under the MIT license. " << endl; + return 0; + } + + // Help. + // + if (ops.help ()) + { + ostream& o (cout); + + o << "Usage: " << argv[0] << " [options] [variables] [buildspec]" << endl + << "Options:" << endl; + + options::print_usage (o); + return 0; + } // Trace verbosity. // - verb = 5; + verb = ops.verbose () > 0 ? ops.verbose () : (ops.v () ? 1 : 0); + + // Initialize time conversion data that is used by localtime_r(). + // + tzset (); // Register modules. // diff --git a/build/diagnostics b/build/diagnostics index 68f449b..21e02e2 100644 --- a/build/diagnostics +++ b/build/diagnostics @@ -49,13 +49,16 @@ namespace build // Trace verbosity level. // + // 0 - tracing disabled. // 1 - command lines to update explicit targets (e.g., .o) // 2 - command lines to update implicit targets (e.g., .d) // 3 - things didn't work out (e.g., rule did not match) // 4 - additional information // 5 - more additional information // - extern std::uint8_t verb; + // While uint8 is more than enough, use uint16 for the ease of printing. + // + extern std::uint16_t verb; template inline void level1 (const F& f) {if (verb >= 1) f ();} template inline void level2 (const F& f) {if (verb >= 2) f ();} diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx index 53d1a09..43ee0e5 100644 --- a/build/diagnostics.cxx +++ b/build/diagnostics.cxx @@ -64,7 +64,7 @@ namespace build // Trace verbosity level. // - uint8_t verb; + uint16_t verb; // Diagnostic facility, base infrastructure. // diff --git a/build/options b/build/options new file mode 100644 index 0000000..053c066 --- /dev/null +++ b/build/options @@ -0,0 +1,290 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +#ifndef BUILD_OPTIONS +#define BUILD_OPTIONS + +// Begin prologue. +// +// +// End prologue. + +#include +#include +#include +#include + +namespace cli +{ + class unknown_mode + { + public: + enum value + { + skip, + stop, + fail + }; + + unknown_mode (value v); + + operator value () const + { + return v_; + } + + private: + value v_; + }; + + // Exceptions. + // + + class exception: public std::exception + { + public: + virtual void + print (::std::ostream&) const = 0; + }; + + ::std::ostream& + operator<< (::std::ostream&, const exception&); + + class unknown_option: public exception + { + public: + virtual + ~unknown_option () throw (); + + unknown_option (const std::string& option); + + const std::string& + option () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + }; + + class unknown_argument: public exception + { + public: + virtual + ~unknown_argument () throw (); + + unknown_argument (const std::string& argument); + + const std::string& + argument () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string argument_; + }; + + class missing_value: public exception + { + public: + virtual + ~missing_value () throw (); + + missing_value (const std::string& option); + + const std::string& + option () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + }; + + class invalid_value: public exception + { + public: + virtual + ~invalid_value () throw (); + + invalid_value (const std::string& option, + const std::string& value); + + const std::string& + option () const; + + const std::string& + value () const; + + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + + private: + std::string option_; + std::string value_; + }; + + class eos_reached: public exception + { + public: + virtual void + print (::std::ostream&) const; + + virtual const char* + what () const throw (); + }; + + class scanner + { + public: + virtual + ~scanner (); + + virtual bool + more () = 0; + + virtual const char* + peek () = 0; + + virtual const char* + next () = 0; + + virtual void + skip () = 0; + }; + + class argv_scanner: public scanner + { + public: + argv_scanner (int& argc, char** argv, bool erase = false); + argv_scanner (int start, int& argc, char** argv, bool erase = false); + + int + end () const; + + virtual bool + more (); + + virtual const char* + peek (); + + virtual const char* + next (); + + virtual void + skip (); + + private: + int i_; + int& argc_; + char** argv_; + bool erase_; + }; + + template + struct parser; +} + +#include + +class options +{ + public: + options (int& argc, + char** argv, + bool erase = false, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (int start, + int& argc, + char** argv, + bool erase = false, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (int& argc, + char** argv, + int& end, + bool erase = false, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (int start, + int& argc, + char** argv, + int& end, + bool erase = false, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (::cli::scanner&, + ::cli::unknown_mode option = ::cli::unknown_mode::fail, + ::cli::unknown_mode argument = ::cli::unknown_mode::stop); + + options (); + + // Option accessors. + // + const bool& + help () const; + + const bool& + version () const; + + const bool& + v () const; + + const std::uint16_t& + verbose () const; + + // Print usage information. + // + static void + print_usage (::std::ostream&); + + // Implementation details. + // + protected: + bool + _parse (const char*, ::cli::scanner&); + + private: + void + _parse (::cli::scanner&, + ::cli::unknown_mode option, + ::cli::unknown_mode argument); + + public: + bool help_; + bool version_; + bool v_; + std::uint16_t verbose_; +}; + +#include + +// Begin epilogue. +// +// +// End epilogue. + +#endif // BUILD_OPTIONS diff --git a/build/options.cli b/build/options.cli new file mode 100644 index 0000000..af28992 --- /dev/null +++ b/build/options.cli @@ -0,0 +1,24 @@ +// file : build/options.cli +// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC +// license : MIT; see accompanying LICENSE file + +include ; + +class options +{ + bool --help {"Print usage information and exit."}; + bool --version {"Print version and exit."}; + + bool -v + { + "Print actual commands being executed. This is equivalent to + \cb{--verbose 1}." + }; + + std::uint16_t --verbose = 0 + { + "", + "Set the diagnostics verbosity to a level between 0 (disabled) and + 5 (lots of information). @@ Need to document further." + }; +}; diff --git a/build/options.cxx b/build/options.cxx new file mode 100644 index 0000000..0d4248d --- /dev/null +++ b/build/options.cxx @@ -0,0 +1,518 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +// Begin prologue. +// +// +// End prologue. + +#include + +#include +#include +#include +#include +#include +#include + +namespace cli +{ + // unknown_option + // + unknown_option:: + ~unknown_option () throw () + { + } + + void unknown_option:: + print (::std::ostream& os) const + { + os << "unknown option '" << option ().c_str () << "'"; + } + + const char* unknown_option:: + what () const throw () + { + return "unknown option"; + } + + // unknown_argument + // + unknown_argument:: + ~unknown_argument () throw () + { + } + + void unknown_argument:: + print (::std::ostream& os) const + { + os << "unknown argument '" << argument ().c_str () << "'"; + } + + const char* unknown_argument:: + what () const throw () + { + return "unknown argument"; + } + + // missing_value + // + missing_value:: + ~missing_value () throw () + { + } + + void missing_value:: + print (::std::ostream& os) const + { + os << "missing value for option '" << option ().c_str () << "'"; + } + + const char* missing_value:: + what () const throw () + { + return "missing option value"; + } + + // invalid_value + // + invalid_value:: + ~invalid_value () throw () + { + } + + void invalid_value:: + print (::std::ostream& os) const + { + os << "invalid value '" << value ().c_str () << "' for option '" + << option ().c_str () << "'"; + } + + const char* invalid_value:: + what () const throw () + { + return "invalid option value"; + } + + // eos_reached + // + void eos_reached:: + print (::std::ostream& os) const + { + os << what (); + } + + const char* eos_reached:: + what () const throw () + { + return "end of argument stream reached"; + } + + // scanner + // + scanner:: + ~scanner () + { + } + + // argv_scanner + // + bool argv_scanner:: + more () + { + return i_ < argc_; + } + + const char* argv_scanner:: + peek () + { + if (i_ < argc_) + return argv_[i_]; + else + throw eos_reached (); + } + + const char* argv_scanner:: + next () + { + if (i_ < argc_) + { + const char* r (argv_[i_]); + + if (erase_) + { + for (int i (i_ + 1); i < argc_; ++i) + argv_[i - 1] = argv_[i]; + + --argc_; + argv_[argc_] = 0; + } + else + ++i_; + + return r; + } + else + throw eos_reached (); + } + + void argv_scanner:: + skip () + { + if (i_ < argc_) + ++i_; + else + throw eos_reached (); + } + + template + struct parser + { + static void + parse (X& x, scanner& s) + { + std::string o (s.next ()); + + if (s.more ()) + { + std::string v (s.next ()); + std::istringstream is (v); + if (!(is >> x && is.eof ())) + throw invalid_value (o, v); + } + else + throw missing_value (o); + } + }; + + template <> + struct parser + { + static void + parse (bool& x, scanner& s) + { + s.next (); + x = true; + } + }; + + template <> + struct parser + { + static void + parse (std::string& x, scanner& s) + { + const char* o (s.next ()); + + if (s.more ()) + x = s.next (); + else + throw missing_value (o); + } + }; + + template + struct parser > + { + static void + parse (std::vector& c, scanner& s) + { + X x; + parser::parse (x, s); + c.push_back (x); + } + }; + + template + struct parser > + { + static void + parse (std::set& c, scanner& s) + { + X x; + parser::parse (x, s); + c.insert (x); + } + }; + + template + struct parser > + { + static void + parse (std::map& m, scanner& s) + { + std::string o (s.next ()); + + if (s.more ()) + { + std::string ov (s.next ()); + std::string::size_type p = ov.find ('='); + + if (p == std::string::npos) + { + K k = K (); + + if (!ov.empty ()) + { + std::istringstream ks (ov); + + if (!(ks >> k && ks.eof ())) + throw invalid_value (o, ov); + } + + m[k] = V (); + } + else + { + K k = K (); + V v = V (); + std::string kstr (ov, 0, p); + std::string vstr (ov, p + 1); + + if (!kstr.empty ()) + { + std::istringstream ks (kstr); + + if (!(ks >> k && ks.eof ())) + throw invalid_value (o, ov); + } + + if (!vstr.empty ()) + { + std::istringstream vs (vstr); + + if (!(vs >> v && vs.eof ())) + throw invalid_value (o, ov); + } + + m[k] = v; + } + } + else + throw missing_value (o); + } + }; + + template + void + thunk (X& x, scanner& s) + { + parser::parse (x.*M, s); + } +} + +#include +#include + +// options +// + +options:: +options () +: help_ (), + version_ (), + v_ (), + verbose_ (0) +{ +} + +options:: +options (int& argc, + char** argv, + bool erase, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: help_ (), + version_ (), + v_ (), + verbose_ (0) +{ + ::cli::argv_scanner s (argc, argv, erase); + _parse (s, opt, arg); +} + +options:: +options (int start, + int& argc, + char** argv, + bool erase, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: help_ (), + version_ (), + v_ (), + verbose_ (0) +{ + ::cli::argv_scanner s (start, argc, argv, erase); + _parse (s, opt, arg); +} + +options:: +options (int& argc, + char** argv, + int& end, + bool erase, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: help_ (), + version_ (), + v_ (), + verbose_ (0) +{ + ::cli::argv_scanner s (argc, argv, erase); + _parse (s, opt, arg); + end = s.end (); +} + +options:: +options (int start, + int& argc, + char** argv, + int& end, + bool erase, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: help_ (), + version_ (), + v_ (), + verbose_ (0) +{ + ::cli::argv_scanner s (start, argc, argv, erase); + _parse (s, opt, arg); + end = s.end (); +} + +options:: +options (::cli::scanner& s, + ::cli::unknown_mode opt, + ::cli::unknown_mode arg) +: help_ (), + version_ (), + v_ (), + verbose_ (0) +{ + _parse (s, opt, arg); +} + +void options:: +print_usage (::std::ostream& os) +{ + os << "--help Print usage information and exit." << ::std::endl; + + os << "--version Print version and exit." << ::std::endl; + + os << "-v Print actual commands being executed." << ::std::endl; + + os << "--verbose Set the diagnostics verbosity to a level between 0 (disabled)" << ::std::endl + << " and 5 (lots of information)." << ::std::endl; +} + +typedef +std::map +_cli_options_map; + +static _cli_options_map _cli_options_map_; + +struct _cli_options_map_init +{ + _cli_options_map_init () + { + _cli_options_map_["--help"] = + &::cli::thunk< options, bool, &options::help_ >; + _cli_options_map_["--version"] = + &::cli::thunk< options, bool, &options::version_ >; + _cli_options_map_["-v"] = + &::cli::thunk< options, bool, &options::v_ >; + _cli_options_map_["--verbose"] = + &::cli::thunk< options, std::uint16_t, &options::verbose_ >; + } +}; + +static _cli_options_map_init _cli_options_map_init_; + +bool options:: +_parse (const char* o, ::cli::scanner& s) +{ + _cli_options_map::const_iterator i (_cli_options_map_.find (o)); + + if (i != _cli_options_map_.end ()) + { + (*(i->second)) (*this, s); + return true; + } + + return false; +} + +void options:: +_parse (::cli::scanner& s, + ::cli::unknown_mode opt_mode, + ::cli::unknown_mode arg_mode) +{ + bool opt = true; + + while (s.more ()) + { + const char* o = s.peek (); + + if (std::strcmp (o, "--") == 0) + { + s.skip (); + opt = false; + continue; + } + + if (opt && _parse (o, s)); + else if (opt && std::strncmp (o, "-", 1) == 0 && o[1] != '\0') + { + switch (opt_mode) + { + case ::cli::unknown_mode::skip: + { + s.skip (); + continue; + } + case ::cli::unknown_mode::stop: + { + break; + } + case ::cli::unknown_mode::fail: + { + throw ::cli::unknown_option (o); + } + } + + break; + } + else + { + switch (arg_mode) + { + case ::cli::unknown_mode::skip: + { + s.skip (); + continue; + } + case ::cli::unknown_mode::stop: + { + break; + } + case ::cli::unknown_mode::fail: + { + throw ::cli::unknown_argument (o); + } + } + + break; + } + } +} + +// Begin epilogue. +// +// +// End epilogue. + diff --git a/build/options.ixx b/build/options.ixx new file mode 100644 index 0000000..d242ba5 --- /dev/null +++ b/build/options.ixx @@ -0,0 +1,145 @@ +// -*- C++ -*- +// +// This file was generated by CLI, a command line interface +// compiler for C++. +// + +// Begin prologue. +// +// +// End prologue. + +namespace cli +{ + // unknown_mode + // + inline unknown_mode:: + unknown_mode (value v) + : v_ (v) + { + } + + // exception + // + inline ::std::ostream& + operator<< (::std::ostream& os, const exception& e) + { + e.print (os); + return os; + } + + // unknown_option + // + inline unknown_option:: + unknown_option (const std::string& option) + : option_ (option) + { + } + + inline const std::string& unknown_option:: + option () const + { + return option_; + } + + // unknown_argument + // + inline unknown_argument:: + unknown_argument (const std::string& argument) + : argument_ (argument) + { + } + + inline const std::string& unknown_argument:: + argument () const + { + return argument_; + } + + // missing_value + // + inline missing_value:: + missing_value (const std::string& option) + : option_ (option) + { + } + + inline const std::string& missing_value:: + option () const + { + return option_; + } + + // invalid_value + // + inline invalid_value:: + invalid_value (const std::string& option, + const std::string& value) + : option_ (option), value_ (value) + { + } + + inline const std::string& invalid_value:: + option () const + { + return option_; + } + + inline const std::string& invalid_value:: + value () const + { + return value_; + } + + // argv_scanner + // + inline argv_scanner:: + argv_scanner (int& argc, char** argv, bool erase) + : i_ (1), argc_ (argc), argv_ (argv), erase_ (erase) + { + } + + inline argv_scanner:: + argv_scanner (int start, int& argc, char** argv, bool erase) + : i_ (start), argc_ (argc), argv_ (argv), erase_ (erase) + { + } + + inline int argv_scanner:: + end () const + { + return i_; + } +} + +// options +// + +inline const bool& options:: +help () const +{ + return this->help_; +} + +inline const bool& options:: +version () const +{ + return this->version_; +} + +inline const bool& options:: +v () const +{ + return this->v_; +} + +inline const std::uint16_t& options:: +verbose () const +{ + return this->verbose_; +} + +// Begin epilogue. +// +// +// End epilogue. -- cgit v1.1