diff options
Diffstat (limited to 'build2')
-rw-r--r-- | build2/b-options.cxx | 1665 | ||||
-rw-r--r-- | build2/b-options.hxx | 690 | ||||
-rw-r--r-- | build2/b-options.ixx | 561 | ||||
-rw-r--r-- | build2/b.cli | 749 | ||||
-rw-r--r-- | build2/b.cxx | 1285 | ||||
-rw-r--r-- | build2/buildfile | 42 | ||||
-rw-r--r-- | build2/cli/init.cxx | 291 | ||||
-rw-r--r-- | build2/cli/init.hxx | 29 | ||||
-rw-r--r-- | build2/cli/module.hxx | 30 | ||||
-rw-r--r-- | build2/cli/rule.cxx | 336 | ||||
-rw-r--r-- | build2/cli/rule.hxx | 43 | ||||
-rw-r--r-- | build2/cli/target.cxx | 75 | ||||
-rw-r--r-- | build2/cli/target.hxx | 54 | ||||
-rw-r--r-- | build2/types-parsers.cxx | 50 | ||||
-rw-r--r-- | build2/types-parsers.hxx | 43 |
15 files changed, 590 insertions, 5353 deletions
diff --git a/build2/b-options.cxx b/build2/b-options.cxx deleted file mode 100644 index 096aebc..0000000 --- a/build2/b-options.cxx +++ /dev/null @@ -1,1665 +0,0 @@ -// -*- C++ -*- -// -// This file was generated by CLI, a command line interface -// compiler for C++. -// - -// Begin prologue. -// -#include <build2/types-parsers.hxx> -// -// End prologue. - -#include <build2/b-options.hxx> - -#include <map> -#include <set> -#include <string> -#include <vector> -#include <ostream> -#include <sstream> -#include <cstring> -#include <fstream> - -namespace build2 -{ - namespace cl - { - // 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 () << "'"; - - if (!message ().empty ()) - os << ": " << message ().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"; - } - - // file_io_failure - // - file_io_failure:: - ~file_io_failure () throw () - { - } - - void file_io_failure:: - print (::std::ostream& os) const - { - os << "unable to open file '" << file ().c_str () << "' or read failure"; - } - - const char* file_io_failure:: - what () const throw () - { - return "unable to open file or read failure"; - } - - // unmatched_quote - // - unmatched_quote:: - ~unmatched_quote () throw () - { - } - - void unmatched_quote:: - print (::std::ostream& os) const - { - os << "unmatched quote in argument '" << argument ().c_str () << "'"; - } - - const char* unmatched_quote:: - what () const throw () - { - return "unmatched quote"; - } - - // 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 (); - } - - // argv_file_scanner - // - int argv_file_scanner::zero_argc_ = 0; - std::string argv_file_scanner::empty_string_; - - bool argv_file_scanner:: - more () - { - if (!args_.empty ()) - return true; - - while (base::more ()) - { - // See if the next argument is the file option. - // - const char* a (base::peek ()); - const option_info* oi = 0; - const char* ov = 0; - - if (!skip_) - { - if ((oi = find (a)) != 0) - { - base::next (); - - if (!base::more ()) - throw missing_value (a); - - ov = base::next (); - } - else if (std::strncmp (a, "-", 1) == 0) - { - if ((ov = std::strchr (a, '=')) != 0) - { - std::string o (a, 0, ov - a); - if ((oi = find (o.c_str ())) != 0) - { - base::next (); - ++ov; - } - } - } - } - - if (oi != 0) - { - if (oi->search_func != 0) - { - std::string f (oi->search_func (ov, oi->arg)); - - if (!f.empty ()) - load (f); - } - else - load (ov); - - if (!args_.empty ()) - return true; - } - else - { - if (!skip_) - skip_ = (std::strcmp (a, "--") == 0); - - return true; - } - } - - return false; - } - - const char* argv_file_scanner:: - peek () - { - if (!more ()) - throw eos_reached (); - - return args_.empty () ? base::peek () : args_.front ().value.c_str (); - } - - const std::string& argv_file_scanner:: - peek_file () - { - if (!more ()) - throw eos_reached (); - - return args_.empty () ? empty_string_ : *args_.front ().file; - } - - std::size_t argv_file_scanner:: - peek_line () - { - if (!more ()) - throw eos_reached (); - - return args_.empty () ? 0 : args_.front ().line; - } - - const char* argv_file_scanner:: - next () - { - if (!more ()) - throw eos_reached (); - - if (args_.empty ()) - return base::next (); - else - { - hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value); - args_.pop_front (); - return hold_[i_].c_str (); - } - } - - void argv_file_scanner:: - skip () - { - if (!more ()) - throw eos_reached (); - - if (args_.empty ()) - return base::skip (); - else - args_.pop_front (); - } - - const argv_file_scanner::option_info* argv_file_scanner:: - find (const char* a) const - { - for (std::size_t i (0); i < options_count_; ++i) - if (std::strcmp (a, options_[i].option) == 0) - return &options_[i]; - - return 0; - } - - void argv_file_scanner:: - load (const std::string& file) - { - using namespace std; - - ifstream is (file.c_str ()); - - if (!is.is_open ()) - throw file_io_failure (file); - - files_.push_back (file); - - arg a; - a.file = &*files_.rbegin (); - - for (a.line = 1; !is.eof (); ++a.line) - { - string line; - getline (is, line); - - if (is.fail () && !is.eof ()) - throw file_io_failure (file); - - string::size_type n (line.size ()); - - // Trim the line from leading and trailing whitespaces. - // - if (n != 0) - { - const char* f (line.c_str ()); - const char* l (f + n); - - const char* of (f); - while (f < l && (*f == ' ' || *f == '\t' || *f == '\r')) - ++f; - - --l; - - const char* ol (l); - while (l > f && (*l == ' ' || *l == '\t' || *l == '\r')) - --l; - - if (f != of || l != ol) - line = f <= l ? string (f, l - f + 1) : string (); - } - - // Ignore empty lines, those that start with #. - // - if (line.empty () || line[0] == '#') - continue; - - string::size_type p (string::npos); - if (line.compare (0, 1, "-") == 0) - { - p = line.find (' '); - - string::size_type q (line.find ('=')); - if (q != string::npos && q < p) - p = q; - } - - string s1; - if (p != string::npos) - { - s1.assign (line, 0, p); - - // Skip leading whitespaces in the argument. - // - if (line[p] == '=') - ++p; - else - { - n = line.size (); - for (++p; p < n; ++p) - { - char c (line[p]); - if (c != ' ' && c != '\t' && c != '\r') - break; - } - } - } - else if (!skip_) - skip_ = (line == "--"); - - string s2 (line, p != string::npos ? p : 0); - - // If the string (which is an option value or argument) is - // wrapped in quotes, remove them. - // - n = s2.size (); - char cf (s2[0]), cl (s2[n - 1]); - - if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'') - { - if (n == 1 || cf != cl) - throw unmatched_quote (s2); - - s2 = string (s2, 1, n - 2); - } - - if (!s1.empty ()) - { - // See if this is another file option. - // - const option_info* oi; - if (!skip_ && (oi = find (s1.c_str ()))) - { - if (s2.empty ()) - throw missing_value (oi->option); - - if (oi->search_func != 0) - { - string f (oi->search_func (s2.c_str (), oi->arg)); - if (!f.empty ()) - load (f); - } - else - { - // If the path of the file being parsed is not simple and the - // path of the file that needs to be loaded is relative, then - // complete the latter using the former as a base. - // -#ifndef _WIN32 - string::size_type p (file.find_last_of ('/')); - bool c (p != string::npos && s2[0] != '/'); -#else - string::size_type p (file.find_last_of ("/\\")); - bool c (p != string::npos && s2[1] != ':'); -#endif - if (c) - s2.insert (0, file, 0, p + 1); - - load (s2); - } - - continue; - } - - a.value = s1; - args_.push_back (a); - } - - a.value = s2; - args_.push_back (a); - } - } - - template <typename X> - struct parser - { - static void - parse (X& x, bool& xs, scanner& s) - { - using namespace std; - - const char* o (s.next ()); - if (s.more ()) - { - string v (s.next ()); - istringstream is (v); - if (!(is >> x && is.peek () == istringstream::traits_type::eof ())) - throw invalid_value (o, v); - } - else - throw missing_value (o); - - xs = true; - } - - static void - merge (X& b, const X& a) - { - b = a; - } - }; - - template <> - struct parser<bool> - { - static void - parse (bool& x, scanner& s) - { - s.next (); - x = true; - } - - static void - merge (bool& b, const bool&) - { - b = true; - } - }; - - template <> - struct parser<std::string> - { - static void - parse (std::string& x, bool& xs, scanner& s) - { - const char* o (s.next ()); - - if (s.more ()) - x = s.next (); - else - throw missing_value (o); - - xs = true; - } - - static void - merge (std::string& b, const std::string& a) - { - b = a; - } - }; - - template <typename X> - struct parser<std::vector<X> > - { - static void - parse (std::vector<X>& c, bool& xs, scanner& s) - { - X x; - bool dummy; - parser<X>::parse (x, dummy, s); - c.push_back (x); - xs = true; - } - - static void - merge (std::vector<X>& b, const std::vector<X>& a) - { - b.insert (b.end (), a.begin (), a.end ()); - } - }; - - template <typename X, typename C> - struct parser<std::set<X, C> > - { - static void - parse (std::set<X, C>& c, bool& xs, scanner& s) - { - X x; - bool dummy; - parser<X>::parse (x, dummy, s); - c.insert (x); - xs = true; - } - - static void - merge (std::set<X, C>& b, const std::set<X, C>& a) - { - b.insert (a.begin (), a.end ()); - } - }; - - template <typename K, typename V, typename C> - struct parser<std::map<K, V, C> > - { - static void - parse (std::map<K, V, C>& m, bool& xs, scanner& s) - { - const char* o (s.next ()); - - if (s.more ()) - { - std::string ov (s.next ()); - std::string::size_type p = ov.find ('='); - - K k = K (); - V v = V (); - std::string kstr (ov, 0, p); - std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ())); - - int ac (2); - char* av[] = - { - const_cast<char*> (o), - 0 - }; - - bool dummy; - if (!kstr.empty ()) - { - av[1] = const_cast<char*> (kstr.c_str ()); - argv_scanner s (0, ac, av); - parser<K>::parse (k, dummy, s); - } - - if (!vstr.empty ()) - { - av[1] = const_cast<char*> (vstr.c_str ()); - argv_scanner s (0, ac, av); - parser<V>::parse (v, dummy, s); - } - - m[k] = v; - } - else - throw missing_value (o); - - xs = true; - } - - static void - merge (std::map<K, V, C>& b, const std::map<K, V, C>& a) - { - for (typename std::map<K, V, C>::const_iterator i (a.begin ()); - i != a.end (); - ++i) - b[i->first] = i->second; - } - }; - - template <typename X, typename T, T X::*M> - void - thunk (X& x, scanner& s) - { - parser<T>::parse (x.*M, s); - } - - template <typename X, typename T, T X::*M, bool X::*S> - void - thunk (X& x, scanner& s) - { - parser<T>::parse (x.*M, x.*S, s); - } - } -} - -#include <map> -#include <cstring> - -namespace build2 -{ - // options - // - - options:: - options () - : build2_metadata_ (), - build2_metadata_specified_ (false), - v_ (), - V_ (), - quiet_ (), - silent_ (), - verbose_ (1), - verbose_specified_ (false), - stat_ (), - dump_ (), - dump_specified_ (false), - progress_ (), - no_progress_ (), - jobs_ (), - jobs_specified_ (false), - max_jobs_ (), - max_jobs_specified_ (false), - queue_depth_ (4), - queue_depth_specified_ (false), - file_cache_ (), - file_cache_specified_ (false), - max_stack_ (), - max_stack_specified_ (false), - serial_stop_ (), - dry_run_ (), - match_only_ (), - no_external_modules_ (), - structured_result_ (), - mtime_check_ (), - no_mtime_check_ (), - no_column_ (), - no_line_ (), - buildfile_ (), - buildfile_specified_ (false), - config_guess_ (), - config_guess_specified_ (false), - config_sub_ (), - config_sub_specified_ (false), - pager_ (), - pager_specified_ (false), - pager_option_ (), - pager_option_specified_ (false), - options_file_ (), - options_file_specified_ (false), - default_options_ (), - default_options_specified_ (false), - no_default_options_ (), - help_ (), - version_ () - { - } - - bool options:: - parse (int& argc, - char** argv, - bool erase, - ::build2::cl::unknown_mode opt, - ::build2::cl::unknown_mode arg) - { - ::build2::cl::argv_scanner s (argc, argv, erase); - bool r = _parse (s, opt, arg); - return r; - } - - bool options:: - parse (int start, - int& argc, - char** argv, - bool erase, - ::build2::cl::unknown_mode opt, - ::build2::cl::unknown_mode arg) - { - ::build2::cl::argv_scanner s (start, argc, argv, erase); - bool r = _parse (s, opt, arg); - return r; - } - - bool options:: - parse (int& argc, - char** argv, - int& end, - bool erase, - ::build2::cl::unknown_mode opt, - ::build2::cl::unknown_mode arg) - { - ::build2::cl::argv_scanner s (argc, argv, erase); - bool r = _parse (s, opt, arg); - end = s.end (); - return r; - } - - bool options:: - parse (int start, - int& argc, - char** argv, - int& end, - bool erase, - ::build2::cl::unknown_mode opt, - ::build2::cl::unknown_mode arg) - { - ::build2::cl::argv_scanner s (start, argc, argv, erase); - bool r = _parse (s, opt, arg); - end = s.end (); - return r; - } - - bool options:: - parse (::build2::cl::scanner& s, - ::build2::cl::unknown_mode opt, - ::build2::cl::unknown_mode arg) - { - bool r = _parse (s, opt, arg); - return r; - } - - void options:: - merge (const options& a) - { - CLI_POTENTIALLY_UNUSED (a); - - if (a.build2_metadata_specified_) - { - ::build2::cl::parser< uint64_t>::merge ( - this->build2_metadata_, a.build2_metadata_); - this->build2_metadata_specified_ = true; - } - - 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.silent_) - { - ::build2::cl::parser< bool>::merge ( - this->silent_, a.silent_); - } - - 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<string>>::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.file_cache_specified_) - { - ::build2::cl::parser< string>::merge ( - this->file_cache_, a.file_cache_); - this->file_cache_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.no_external_modules_) - { - ::build2::cl::parser< bool>::merge ( - this->no_external_modules_, a.no_external_modules_); - } - - 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.options_file_specified_) - { - ::build2::cl::parser< string>::merge ( - this->options_file_, a.options_file_); - this->options_file_specified_ = true; - } - - if (a.default_options_specified_) - { - ::build2::cl::parser< dir_path>::merge ( - this->default_options_, a.default_options_); - this->default_options_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) - { - CLI_POTENTIALLY_UNUSED (os); - - if (p != ::build2::cl::usage_para::none) - os << ::std::endl; - - os << "\033[1mOPTIONS\033[0m" << ::std::endl; - - os << std::endl - << "\033[1m-v\033[0m Print actual commands being executed. This options is" << ::std::endl - << " equivalent to \033[1m--verbose 2\033[0m." << ::std::endl; - - os << std::endl - << "\033[1m-V\033[0m Print all underlying commands being executed. This" << ::std::endl - << " options is equivalent to \033[1m--verbose 3\033[0m." << ::std::endl; - - os << std::endl - << "\033[1m--quiet\033[0m|\033[1m-q\033[0m Run quietly, only printing error messages in most" << ::std::endl - << " contexts. In certain contexts (for example, while" << ::std::endl - << " updating build system modules) this verbosity level may" << ::std::endl - << " be ignored. Use \033[1m--silent\033[0m to run quietly in all contexts." << ::std::endl - << " This option is equivalent to \033[1m--verbose 0\033[0m." << ::std::endl; - - os << std::endl - << "\033[1m--silent\033[0m Run quietly, only printing error messages in all" << ::std::endl - << " contexts." << ::std::endl; - - os << std::endl - << "\033[1m--verbose\033[0m \033[4mlevel\033[0m Set the diagnostics verbosity to \033[4mlevel\033[0m between 0 and 6." << ::std::endl - << " Level 0 disables any non-error messages (but see the" << ::std::endl - << " difference between \033[1m--quiet\033[0m and \033[1m--silent\033[0m) while level 6" << ::std::endl - << " produces lots of information, with level 1 being the" << ::std::endl - << " default. The following additional types of diagnostics" << ::std::endl - << " are produced at each level:" << ::std::endl - << ::std::endl - << " 1. High-level information messages." << ::std::endl - << " 2. Essential underlying commands being executed." << ::std::endl - << " 3. All underlying commands being executed." << ::std::endl - << " 4. Information that could be helpful to the user." << ::std::endl - << " 5. Information that could be helpful to the developer." << ::std::endl - << " 6. Even more detailed information." << ::std::endl; - - os << std::endl - << "\033[1m--stat\033[0m Display build statistics." << ::std::endl; - - os << std::endl - << "\033[1m--dump\033[0m \033[4mphase\033[0m Dump the build system state after the specified phase." << ::std::endl - << " Valid \033[4mphase\033[0m values are \033[1mload\033[0m (after loading \033[1mbuildfiles\033[0m)" << ::std::endl - << " and \033[1mmatch\033[0m (after matching rules to targets). Repeat this" << ::std::endl - << " option to dump the state after multiple phases." << ::std::endl; - - os << std::endl - << "\033[1m--progress\033[0m Display build progress. If printing to a terminal the" << ::std::endl - << " progress is displayed by default for low verbosity" << ::std::endl - << " levels. Use \033[1m--no-progress\033[0m to suppress." << ::std::endl; - - os << std::endl - << "\033[1m--no-progress\033[0m Don't display build progress." << ::std::endl; - - os << std::endl - << "\033[1m--jobs\033[0m|\033[1m-j\033[0m \033[4mnum\033[0m Number of active jobs to perform in parallel. This" << ::std::endl - << " includes both the number of active threads inside the" << ::std::endl - << " build system as well as the number of external commands" << ::std::endl - << " (compilers, linkers, etc) started but not yet finished." << ::std::endl - << " If this option is not specified or specified with the \033[1m0\033[0m" << ::std::endl - << " value, then the number of available hardware threads is" << ::std::endl - << " used." << ::std::endl; - - os << std::endl - << "\033[1m--max-jobs\033[0m|\033[1m-J\033[0m \033[4mnum\033[0m Maximum number of jobs (threads) to create. The default" << ::std::endl - << " is 8x the number of active jobs (\033[1m--jobs|j\033[0m) on 32-bit" << ::std::endl - << " architectures and 32x on 64-bit. See the build system" << ::std::endl - << " scheduler implementation for details." << ::std::endl; - - os << std::endl - << "\033[1m--queue-depth\033[0m|\033[1m-Q\033[0m \033[4mnum\033[0m The queue depth as a multiplier over the number of active" << ::std::endl - << " jobs. Normally we want a deeper queue if the jobs take" << ::std::endl - << " long (for example, compilation) and shorter if they are" << ::std::endl - << " quick (for example, simple tests). The default is 4. See" << ::std::endl - << " the build system scheduler implementation for details." << ::std::endl; - - os << std::endl - << "\033[1m--file-cache\033[0m \033[4mimpl\033[0m File cache implementation to use for intermediate build" << ::std::endl - << " results. Valid values are \033[1mnoop\033[0m (no caching or" << ::std::endl - << " compression) and \033[1msync-lz4\033[0m (no caching with synchronous" << ::std::endl - << " LZ4 on-disk compression). If this option is not" << ::std::endl - << " specified, then a suitable default implementation is used" << ::std::endl - << " (currently \033[1msync-lz4\033[0m)." << ::std::endl; - - os << std::endl - << "\033[1m--max-stack\033[0m \033[4mnum\033[0m The maximum stack size in KBytes to allow for newly" << ::std::endl - << " created threads. For \033[4mpthreads\033[0m-based systems the driver" << ::std::endl - << " queries the stack size of the main thread and uses the" << ::std::endl - << " same size for creating additional threads. This allows" << ::std::endl - << " adjusting the stack size using familiar mechanisms, such" << ::std::endl - << " as \033[1mulimit\033[0m. Sometimes, however, the stack size of the main" << ::std::endl - << " thread is excessively large. As a result, the driver" << ::std::endl - << " checks if it is greater than a predefined limit (64MB on" << ::std::endl - << " 64-bit systems and 32MB on 32-bit ones) and caps it to a" << ::std::endl - << " more sensible value (8MB) if that's the case. This option" << ::std::endl - << " allows you to override this check with the special zero" << ::std::endl - << " value indicating that the main thread stack size should" << ::std::endl - << " be used as is." << ::std::endl; - - os << std::endl - << "\033[1m--serial-stop\033[0m|\033[1m-s\033[0m Run serially and stop at the first error. This mode is" << ::std::endl - << " useful to investigate build failures that are caused by" << ::std::endl - << " build system errors rather than compilation errors. Note" << ::std::endl - << " that if you don't want to keep going but still want" << ::std::endl - << " parallel execution, add \033[1m--jobs|-j\033[0m (for example \033[1m-j 0\033[0m for" << ::std::endl - << " default concurrency)." << ::std::endl; - - os << std::endl - << "\033[1m--dry-run\033[0m|\033[1m-n\033[0m Print commands without actually executing them. Note that" << ::std::endl - << " commands that are required to create an accurate build" << ::std::endl - << " state will still be executed and the extracted auxiliary" << ::std::endl - << " dependency information saved. In other words, this is not" << ::std::endl - << " the \033[4m\"don't touch the filesystem\"\033[0m mode but rather \033[4m\"do" << ::std::endl - << " minimum amount of work to show what needs to be done\"\033[0m." << ::std::endl - << " Note also that only the \033[1mperform\033[0m meta-operation supports" << ::std::endl - << " this mode." << ::std::endl; - - os << std::endl - << "\033[1m--match-only\033[0m Match the rules but do not execute the operation. This" << ::std::endl - << " mode is primarily useful for profiling." << ::std::endl; - - os << std::endl - << "\033[1m--no-external-modules\033[0m Don't load external modules during project bootstrap." << ::std::endl - << " Note that this option can only be used with" << ::std::endl - << " meta-operations that do not load the project's" << ::std::endl - << " \033[1mbuildfiles\033[0m, such as \033[1minfo\033[0m." << ::std::endl; - - os << std::endl - << "\033[1m--structured-result\033[0m Write the result of execution in a structured form. In" << ::std::endl - << " this mode, instead of printing to \033[1mSTDERR\033[0m diagnostics" << ::std::endl - << " messages about the outcome of executing actions on" << ::std::endl - << " targets, the driver writes to \033[1mSTDOUT\033[0m a structured result" << ::std::endl - << " description one line per the buildspec action/target" << ::std::endl - << " pair. Each line has the following format:" << ::std::endl - << ::std::endl - << " \033[4mstate\033[0m \033[4mmeta-operation\033[0m \033[4moperation\033[0m \033[4mtarget\033[0m\033[0m" << ::std::endl - << ::std::endl - << " Where \033[4mstate\033[0m can be one of \033[1munchanged\033[0m, \033[1mchanged\033[0m, or \033[1mfailed\033[0m." << ::std::endl - << " If the action is a pre or post operation, then the outer" << ::std::endl - << " operation is specified in parenthesis. For example:" << ::std::endl - << ::std::endl - << " unchanged perform update(test) /tmp/dir{hello/}" << ::std::endl - << " changed perform test /tmp/dir{hello/}" << ::std::endl - << ::std::endl - << " Note that only the \033[1mperform\033[0m meta-operation supports the" << ::std::endl - << " structured result output." << ::std::endl; - - os << std::endl - << "\033[1m--mtime-check\033[0m Perform file modification time sanity checks. These" << ::std::endl - << " checks can be helpful in diagnosing spurious rebuilds and" << ::std::endl - << " are enabled by default for the staged version of the" << ::std::endl - << " build system. Use \033[1m--no-mtime-check\033[0m to disable." << ::std::endl; - - os << std::endl - << "\033[1m--no-mtime-check\033[0m Don't perform file modification time sanity checks." << ::std::endl; - - os << std::endl - << "\033[1m--no-column\033[0m Don't print column numbers in diagnostics." << ::std::endl; - - os << std::endl - << "\033[1m--no-line\033[0m Don't print line and column numbers in diagnostics." << ::std::endl; - - os << std::endl - << "\033[1m--buildfile\033[0m \033[4mpath\033[0m The alternative file to read build information from. The" << ::std::endl - << " default is \033[1mbuildfile\033[0m or \033[1mbuild2file\033[0m, depending on the" << ::std::endl - << " project's build file/directory naming scheme. If \033[4mpath\033[0m is" << ::std::endl - << " '\033[1m-\033[0m', then read from \033[1mSTDIN\033[0m. Note that this option only" << ::std::endl - << " affects the files read as part of the buildspec" << ::std::endl - << " processing. Specifically, it has no effect on the \033[1msource\033[0m" << ::std::endl - << " and \033[1minclude\033[0m directives. As a result, this option is" << ::std::endl - << " primarily intended for testing rather than changing the" << ::std::endl - << " build file names in real projects." << ::std::endl; - - os << std::endl - << "\033[1m--config-guess\033[0m \033[4mpath\033[0m The path to the \033[1mconfig.guess(1)\033[0m script that should be" << ::std::endl - << " used to guess the host machine triplet. If this option is" << ::std::endl - << " not specified, then \033[1mb\033[0m will fall back on to using the" << ::std::endl - << " target it was built for as host." << ::std::endl; - - os << std::endl - << "\033[1m--config-sub\033[0m \033[4mpath\033[0m The path to the \033[1mconfig.sub(1)\033[0m script that should be used" << ::std::endl - << " to canonicalize machine triplets. If this option is not" << ::std::endl - << " specified, then \033[1mb\033[0m will use its built-in canonicalization" << ::std::endl - << " support which should be sufficient for commonly-used" << ::std::endl - << " platforms." << ::std::endl; - - os << std::endl - << "\033[1m--pager\033[0m \033[4mpath\033[0m The pager program to be used to show long text. Commonly" << ::std::endl - << " used pager programs are \033[1mless\033[0m and \033[1mmore\033[0m. You can also" << ::std::endl - << " specify additional options that should be passed to the" << ::std::endl - << " pager program with \033[1m--pager-option\033[0m. If an empty string is" << ::std::endl - << " specified as the pager program, then no pager will be" << ::std::endl - << " used. If the pager program is not explicitly specified," << ::std::endl - << " then \033[1mb\033[0m will try to use \033[1mless\033[0m. If it is not available, then" << ::std::endl - << " no pager will be used." << ::std::endl; - - os << std::endl - << "\033[1m--pager-option\033[0m \033[4mopt\033[0m Additional option to be passed to the pager program. See" << ::std::endl - << " \033[1m--pager\033[0m for more information on the pager program. Repeat" << ::std::endl - << " this option to specify multiple pager options." << ::std::endl; - - os << std::endl - << "\033[1m--options-file\033[0m \033[4mfile\033[0m Read additional options from \033[4mfile\033[0m. Each option should" << ::std::endl - << " appear on a separate line optionally followed by space or" << ::std::endl - << " equal sign (\033[1m=\033[0m) and an option value. Empty lines and lines" << ::std::endl - << " starting with \033[1m#\033[0m are ignored. Option values can be" << ::std::endl - << " enclosed in double (\033[1m\"\033[0m) or single (\033[1m'\033[0m) quotes to preserve" << ::std::endl - << " leading and trailing whitespaces as well as to specify" << ::std::endl - << " empty values. If the value itself contains trailing or" << ::std::endl - << " leading quotes, enclose it with an extra pair of quotes," << ::std::endl - << " for example \033[1m'\"x\"'\033[0m. Non-leading and non-trailing quotes" << ::std::endl - << " are interpreted as being part of the option value." << ::std::endl - << ::std::endl - << " The semantics of providing options in a file is" << ::std::endl - << " equivalent to providing the same set of options in the" << ::std::endl - << " same order on the command line at the point where the" << ::std::endl - << " \033[1m--options-file\033[0m option is specified except that the shell" << ::std::endl - << " escaping and quoting is not required. Repeat this option" << ::std::endl - << " to specify more than one options file." << ::std::endl; - - os << std::endl - << "\033[1m--default-options\033[0m \033[4mdir\033[0m The directory to load additional default options files" << ::std::endl - << " from." << ::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 - << "\033[1m--version\033[0m Print version and exit." << ::std::endl; - - p = ::build2::cl::usage_para::option; - - return p; - } - - typedef - std::map<std::string, void (*) (options&, ::build2::cl::scanner&)> - _cli_options_map; - - static _cli_options_map _cli_options_map_; - - struct _cli_options_map_init - { - _cli_options_map_init () - { - _cli_options_map_["--build2-metadata"] = - &::build2::cl::thunk< options, uint64_t, &options::build2_metadata_, - &options::build2_metadata_specified_ >; - _cli_options_map_["-v"] = - &::build2::cl::thunk< options, bool, &options::v_ >; - _cli_options_map_["-V"] = - &::build2::cl::thunk< options, bool, &options::V_ >; - _cli_options_map_["--quiet"] = - &::build2::cl::thunk< options, bool, &options::quiet_ >; - _cli_options_map_["-q"] = - &::build2::cl::thunk< options, bool, &options::quiet_ >; - _cli_options_map_["--silent"] = - &::build2::cl::thunk< options, bool, &options::silent_ >; - _cli_options_map_["--verbose"] = - &::build2::cl::thunk< options, uint16_t, &options::verbose_, - &options::verbose_specified_ >; - _cli_options_map_["--stat"] = - &::build2::cl::thunk< options, bool, &options::stat_ >; - _cli_options_map_["--dump"] = - &::build2::cl::thunk< options, std::set<string>, &options::dump_, - &options::dump_specified_ >; - _cli_options_map_["--progress"] = - &::build2::cl::thunk< options, bool, &options::progress_ >; - _cli_options_map_["--no-progress"] = - &::build2::cl::thunk< options, bool, &options::no_progress_ >; - _cli_options_map_["--jobs"] = - &::build2::cl::thunk< options, size_t, &options::jobs_, - &options::jobs_specified_ >; - _cli_options_map_["-j"] = - &::build2::cl::thunk< options, size_t, &options::jobs_, - &options::jobs_specified_ >; - _cli_options_map_["--max-jobs"] = - &::build2::cl::thunk< options, size_t, &options::max_jobs_, - &options::max_jobs_specified_ >; - _cli_options_map_["-J"] = - &::build2::cl::thunk< options, size_t, &options::max_jobs_, - &options::max_jobs_specified_ >; - _cli_options_map_["--queue-depth"] = - &::build2::cl::thunk< options, size_t, &options::queue_depth_, - &options::queue_depth_specified_ >; - _cli_options_map_["-Q"] = - &::build2::cl::thunk< options, size_t, &options::queue_depth_, - &options::queue_depth_specified_ >; - _cli_options_map_["--file-cache"] = - &::build2::cl::thunk< options, string, &options::file_cache_, - &options::file_cache_specified_ >; - _cli_options_map_["--max-stack"] = - &::build2::cl::thunk< options, size_t, &options::max_stack_, - &options::max_stack_specified_ >; - _cli_options_map_["--serial-stop"] = - &::build2::cl::thunk< options, bool, &options::serial_stop_ >; - _cli_options_map_["-s"] = - &::build2::cl::thunk< options, bool, &options::serial_stop_ >; - _cli_options_map_["--dry-run"] = - &::build2::cl::thunk< options, bool, &options::dry_run_ >; - _cli_options_map_["-n"] = - &::build2::cl::thunk< options, bool, &options::dry_run_ >; - _cli_options_map_["--match-only"] = - &::build2::cl::thunk< options, bool, &options::match_only_ >; - _cli_options_map_["--no-external-modules"] = - &::build2::cl::thunk< options, bool, &options::no_external_modules_ >; - _cli_options_map_["--structured-result"] = - &::build2::cl::thunk< options, bool, &options::structured_result_ >; - _cli_options_map_["--mtime-check"] = - &::build2::cl::thunk< options, bool, &options::mtime_check_ >; - _cli_options_map_["--no-mtime-check"] = - &::build2::cl::thunk< options, bool, &options::no_mtime_check_ >; - _cli_options_map_["--no-column"] = - &::build2::cl::thunk< options, bool, &options::no_column_ >; - _cli_options_map_["--no-line"] = - &::build2::cl::thunk< options, bool, &options::no_line_ >; - _cli_options_map_["--buildfile"] = - &::build2::cl::thunk< options, path, &options::buildfile_, - &options::buildfile_specified_ >; - _cli_options_map_["--config-guess"] = - &::build2::cl::thunk< options, path, &options::config_guess_, - &options::config_guess_specified_ >; - _cli_options_map_["--config-sub"] = - &::build2::cl::thunk< options, path, &options::config_sub_, - &options::config_sub_specified_ >; - _cli_options_map_["--pager"] = - &::build2::cl::thunk< options, string, &options::pager_, - &options::pager_specified_ >; - _cli_options_map_["--pager-option"] = - &::build2::cl::thunk< options, strings, &options::pager_option_, - &options::pager_option_specified_ >; - _cli_options_map_["--options-file"] = - &::build2::cl::thunk< options, string, &options::options_file_, - &options::options_file_specified_ >; - _cli_options_map_["--default-options"] = - &::build2::cl::thunk< options, dir_path, &options::default_options_, - &options::default_options_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"] = - &::build2::cl::thunk< options, bool, &options::version_ >; - } - }; - - static _cli_options_map_init _cli_options_map_init_; - - bool options:: - _parse (const char* o, ::build2::cl::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; - } - - bool options:: - _parse (::build2::cl::scanner& s, - ::build2::cl::unknown_mode opt_mode, - ::build2::cl::unknown_mode arg_mode) - { - // Can't skip combined flags (--no-combined-flags). - // - assert (opt_mode != ::build2::cl::unknown_mode::skip); - - bool r = false; - bool opt = true; - - while (s.more ()) - { - const char* o = s.peek (); - - if (std::strcmp (o, "--") == 0) - { - opt = false; - } - - if (opt) - { - if (_parse (o, s)) - { - r = true; - continue; - } - - if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0') - { - // Handle combined option values. - // - std::string co; - if (const char* v = std::strchr (o, '=')) - { - co.assign (o, 0, v - o); - ++v; - - int ac (2); - char* av[] = - { - const_cast<char*> (co.c_str ()), - const_cast<char*> (v) - }; - - ::build2::cl::argv_scanner ns (0, ac, av); - - if (_parse (co.c_str (), ns)) - { - // Parsed the option but not its value? - // - if (ns.end () != 2) - throw ::build2::cl::invalid_value (co, v); - - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = co.c_str (); - } - } - - // Handle combined flags. - // - char cf[3]; - { - const char* p = o + 1; - for (; *p != '\0'; ++p) - { - if (!((*p >= 'a' && *p <= 'z') || - (*p >= 'A' && *p <= 'Z') || - (*p >= '0' && *p <= '9'))) - break; - } - - if (*p == '\0') - { - for (p = o + 1; *p != '\0'; ++p) - { - std::strcpy (cf, "-"); - cf[1] = *p; - cf[2] = '\0'; - - int ac (1); - char* av[] = - { - cf - }; - - ::build2::cl::argv_scanner ns (0, ac, av); - - if (!_parse (cf, ns)) - break; - } - - if (*p == '\0') - { - // All handled. - // - s.next (); - r = true; - continue; - } - else - { - // Set the unknown option and fall through. - // - o = cf; - } - } - } - - switch (opt_mode) - { - case ::build2::cl::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::cl::unknown_mode::stop: - { - break; - } - case ::build2::cl::unknown_mode::fail: - { - throw ::build2::cl::unknown_option (o); - } - } - - break; - } - } - - switch (arg_mode) - { - case ::build2::cl::unknown_mode::skip: - { - s.skip (); - r = true; - continue; - } - case ::build2::cl::unknown_mode::stop: - { - break; - } - case ::build2::cl::unknown_mode::fail: - { - throw ::build2::cl::unknown_argument (o); - } - } - - break; - } - - return r; - } -} - -namespace build2 -{ - ::build2::cl::usage_para - print_b_usage (::std::ostream& os, ::build2::cl::usage_para p) - { - CLI_POTENTIALLY_UNUSED (os); - - if (p != ::build2::cl::usage_para::none) - os << ::std::endl; - - os << "\033[1mSYNOPSIS\033[0m" << ::std::endl - << ::std::endl - << "\033[1mb --help\033[0m" << ::std::endl - << "\033[1mb --version\033[0m" << ::std::endl - << "\033[1mb\033[0m [\033[4moptions\033[0m] [\033[4mvariables\033[0m] [\033[4mbuildspec\033[0m]\033[0m" << ::std::endl - << ::std::endl - << "\033[4mbuildspec\033[0m = \033[4mmeta-operation\033[0m\033[1m(\033[0m\033[4moperation\033[0m\033[1m(\033[0m\033[4mtarget\033[0m...[\033[1m,\033[0m\033[4mparameters\033[0m]\033[1m)\033[0m...\033[1m)\033[0m...\033[0m" << ::std::endl - << ::std::endl - << "\033[1mDESCRIPTION\033[0m" << ::std::endl - << ::std::endl - << "The \033[1mbuild2\033[0m build system driver executes a set of meta-operations on operations" << ::std::endl - << "on targets according to the build specification, or \033[4mbuildspec\033[0m for short. This" << ::std::endl - << "process can be controlled by specifying driver \033[4moptions\033[0m and build system" << ::std::endl - << "\033[4mvariables\033[0m." << ::std::endl - << ::std::endl - << "Note that \033[4moptions\033[0m, \033[4mvariables\033[0m, and \033[4mbuildspec\033[0m fragments can be specified in any" << ::std::endl - << "order. To avoid treating an argument that starts with \033[1m'-'\033[0m as an option, add the" << ::std::endl - << "\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::cl::usage_para::text); - - if (p != ::build2::cl::usage_para::none) - os << ::std::endl; - - os << "\033[1mDEFAULT OPTIONS FILES\033[0m" << ::std::endl - << ::std::endl - << "Instead of having a separate config file format for tool configuration, the" << ::std::endl - << "\033[1mbuild2\033[0m toolchain uses \033[4mdefault options files\033[0m which contain the same options as" << ::std::endl - << "what can be specified on the command line. The default options files are like" << ::std::endl - << "options files that one can specify with \033[1m--options-file\033[0m except that they are" << ::std::endl - << "loaded by default." << ::std::endl - << ::std::endl - << "The default options files for the build system driver are called \033[1mb.options\033[0m and" << ::std::endl - << "are searched for in the \033[1m.build2/\033[0m subdirectory of the home directory and in the" << ::std::endl - << "system directory (for example, \033[1m/etc/build2/\033[0m) if configured. Note that besides" << ::std::endl - << "options these files can also contain global variable overrides." << ::std::endl - << ::std::endl - << "Once the search is complete, the files are loaded in the reverse order, that" << ::std::endl - << "is, beginning from the system directory (if any), followed by the home" << ::std::endl - << "directory, and finishing off with the options specified on the command line. In" << ::std::endl - << "other words, the files are loaded from the more generic to the more specific" << ::std::endl - << "with the command line options having the ability to override any values" << ::std::endl - << "specified in the default options files." << ::std::endl - << ::std::endl - << "If a default options file contains \033[1m--no-default-options\033[0m, then the search is" << ::std::endl - << "stopped at the directory containing this file and no outer files are loaded. If" << ::std::endl - << "this option is specified on the command line, then none of the default options" << ::std::endl - << "files are searched for or loaded." << ::std::endl - << ::std::endl - << "An additional directory containing default options files can be specified with" << ::std::endl - << "\033[1m--default-options\033[0m. Its configuration files are loaded after the home directory." << ::std::endl - << ::std::endl - << "The order in which default options files are loaded is traced at the verbosity" << ::std::endl - << "level 3 (\033[1m-V\033[0m option) or higher." << ::std::endl - << ::std::endl - << "\033[1mEXIT STATUS\033[0m" << ::std::endl - << ::std::endl - << "Non-zero exit status is returned in case of an error." << ::std::endl; - - os << std::endl - << "\033[1mENVIRONMENT\033[0m" << ::std::endl - << ::std::endl - << "The \033[1mHOME\033[0m environment variable is used to determine the user's home directory." << ::std::endl - << "If it is not set, then \033[1mgetpwuid(3)\033[0m is used instead. This value is used to" << ::std::endl - << "shorten paths printed in diagnostics by replacing the home directory with \033[1m~/\033[0m." << ::std::endl - << "It is also made available to \033[1mbuildfile\033[0m's as the \033[1mbuild.home\033[0m variable." << ::std::endl - << ::std::endl - << "The \033[1mBUILD2_VAR_OVR\033[0m environment variable is used to propagate global variable" << ::std::endl - << "overrides to nested build system driver invocations. Its value is a list of" << ::std::endl - << "global variable assignments separated with newlines." << ::std::endl - << ::std::endl - << "The \033[1mBUILD2_DEF_OPT\033[0m environment variable is used to suppress loading of default" << ::std::endl - << "options files in nested build system driver invocations. Its values are \033[1mfalse\033[0m" << ::std::endl - << "or \033[1m0\033[0m to suppress and \033[1mtrue\033[0m or \033[1m1\033[0m to load." << ::std::endl; - - p = ::build2::cl::usage_para::text; - - return p; - } -} - -// Begin epilogue. -// -// -// End epilogue. - diff --git a/build2/b-options.hxx b/build2/b-options.hxx deleted file mode 100644 index 4e1b7bd..0000000 --- a/build2/b-options.hxx +++ /dev/null @@ -1,690 +0,0 @@ -// -*- C++ -*- -// -// This file was generated by CLI, a command line interface -// compiler for C++. -// - -#ifndef BUILD2_B_OPTIONS_HXX -#define BUILD2_B_OPTIONS_HXX - -// Begin prologue. -// -// -// End prologue. - -#include <list> -#include <deque> -#include <iosfwd> -#include <string> -#include <cstddef> -#include <exception> - -#ifndef CLI_POTENTIALLY_UNUSED -# if defined(_MSC_VER) || defined(__xlC__) -# define CLI_POTENTIALLY_UNUSED(x) (void*)&x -# else -# define CLI_POTENTIALLY_UNUSED(x) (void)x -# endif -#endif - -namespace build2 -{ - namespace cl - { - class usage_para - { - public: - enum value - { - none, - text, - option - }; - - usage_para (value); - - operator value () const - { - return v_; - } - - private: - value v_; - }; - - class unknown_mode - { - public: - enum value - { - skip, - stop, - fail - }; - - unknown_mode (value); - - 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& message = std::string ()); - - const std::string& - option () const; - - const std::string& - value () const; - - const std::string& - message () const; - - virtual void - print (::std::ostream&) const; - - virtual const char* - what () const throw (); - - private: - std::string option_; - std::string value_; - std::string message_; - }; - - class eos_reached: public exception - { - public: - virtual void - print (::std::ostream&) const; - - virtual const char* - what () const throw (); - }; - - class file_io_failure: public exception - { - public: - virtual - ~file_io_failure () throw (); - - file_io_failure (const std::string& file); - - const std::string& - file () const; - - virtual void - print (::std::ostream&) const; - - virtual const char* - what () const throw (); - - private: - std::string file_; - }; - - class unmatched_quote: public exception - { - public: - virtual - ~unmatched_quote () throw (); - - unmatched_quote (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_; - }; - - // Command line argument scanner interface. - // - // The values returned by next() are guaranteed to be valid - // for the two previous arguments up until a call to a third - // peek() or next(). - // - 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_; - }; - - class argv_file_scanner: public argv_scanner - { - public: - argv_file_scanner (int& argc, - char** argv, - const std::string& option, - bool erase = false); - - argv_file_scanner (int start, - int& argc, - char** argv, - const std::string& option, - bool erase = false); - - argv_file_scanner (const std::string& file, - const std::string& option); - - struct option_info - { - // If search_func is not NULL, it is called, with the arg - // value as the second argument, to locate the options file. - // If it returns an empty string, then the file is ignored. - // - const char* option; - std::string (*search_func) (const char*, void* arg); - void* arg; - }; - - argv_file_scanner (int& argc, - char** argv, - const option_info* options, - std::size_t options_count, - bool erase = false); - - argv_file_scanner (int start, - int& argc, - char** argv, - const option_info* options, - std::size_t options_count, - bool erase = false); - - argv_file_scanner (const std::string& file, - const option_info* options = 0, - std::size_t options_count = 0); - - virtual bool - more (); - - virtual const char* - peek (); - - virtual const char* - next (); - - virtual void - skip (); - - // Return the file path if the peeked at argument came from a file and - // the empty string otherwise. The reference is guaranteed to be valid - // till the end of the scanner lifetime. - // - const std::string& - peek_file (); - - // Return the 1-based line number if the peeked at argument came from - // a file and zero otherwise. - // - std::size_t - peek_line (); - - private: - const option_info* - find (const char*) const; - - void - load (const std::string& file); - - typedef argv_scanner base; - - const std::string option_; - option_info option_info_; - const option_info* options_; - std::size_t options_count_; - - struct arg - { - std::string value; - const std::string* file; - std::size_t line; - }; - - std::deque<arg> args_; - std::list<std::string> files_; - - // Circular buffer of two arguments. - // - std::string hold_[2]; - std::size_t i_; - - bool skip_; - - static int zero_argc_; - static std::string empty_string_; - }; - - template <typename X> - struct parser; - } -} - -#include <set> - -#include <libbuild2/types.hxx> - -namespace build2 -{ - class options - { - public: - options (); - - // Return true if anything has been parsed. - // - bool - parse (int& argc, - char** argv, - bool erase = false, - ::build2::cl::unknown_mode option = ::build2::cl::unknown_mode::fail, - ::build2::cl::unknown_mode argument = ::build2::cl::unknown_mode::stop); - - bool - parse (int start, - int& argc, - char** argv, - bool erase = false, - ::build2::cl::unknown_mode option = ::build2::cl::unknown_mode::fail, - ::build2::cl::unknown_mode argument = ::build2::cl::unknown_mode::stop); - - bool - parse (int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::cl::unknown_mode option = ::build2::cl::unknown_mode::fail, - ::build2::cl::unknown_mode argument = ::build2::cl::unknown_mode::stop); - - bool - parse (int start, - int& argc, - char** argv, - int& end, - bool erase = false, - ::build2::cl::unknown_mode option = ::build2::cl::unknown_mode::fail, - ::build2::cl::unknown_mode argument = ::build2::cl::unknown_mode::stop); - - bool - parse (::build2::cl::scanner&, - ::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 uint64_t& - build2_metadata () const; - - bool - build2_metadata_specified () const; - - const bool& - v () const; - - const bool& - V () const; - - const bool& - quiet () const; - - const bool& - silent () const; - - const uint16_t& - verbose () const; - - bool - verbose_specified () const; - - const bool& - stat () const; - - const std::set<string>& - dump () const; - - bool - dump_specified () const; - - const bool& - progress () const; - - const bool& - no_progress () const; - - const size_t& - jobs () const; - - bool - jobs_specified () const; - - const size_t& - max_jobs () const; - - bool - max_jobs_specified () const; - - const size_t& - queue_depth () const; - - bool - queue_depth_specified () const; - - const string& - file_cache () const; - - bool - file_cache_specified () const; - - const size_t& - max_stack () const; - - bool - max_stack_specified () const; - - const bool& - serial_stop () const; - - const bool& - dry_run () const; - - const bool& - match_only () const; - - const bool& - no_external_modules () const; - - const bool& - structured_result () const; - - const bool& - mtime_check () const; - - const bool& - no_mtime_check () const; - - const bool& - no_column () const; - - const bool& - no_line () const; - - const path& - buildfile () const; - - bool - buildfile_specified () const; - - const path& - config_guess () const; - - bool - config_guess_specified () const; - - const path& - config_sub () const; - - bool - config_sub_specified () const; - - const string& - pager () const; - - bool - pager_specified () const; - - const strings& - pager_option () const; - - bool - pager_option_specified () const; - - const string& - options_file () const; - - bool - options_file_specified () const; - - const dir_path& - default_options () const; - - bool - default_options_specified () const; - - const bool& - no_default_options () const; - - const bool& - help () const; - - const bool& - version () const; - - // Print usage information. - // - static ::build2::cl::usage_para - print_usage (::std::ostream&, - ::build2::cl::usage_para = ::build2::cl::usage_para::none); - - // Implementation details. - // - protected: - bool - _parse (const char*, ::build2::cl::scanner&); - - private: - bool - _parse (::build2::cl::scanner&, - ::build2::cl::unknown_mode option, - ::build2::cl::unknown_mode argument); - - public: - uint64_t build2_metadata_; - bool build2_metadata_specified_; - bool v_; - bool V_; - bool quiet_; - bool silent_; - uint16_t verbose_; - bool verbose_specified_; - bool stat_; - std::set<string> dump_; - bool dump_specified_; - bool progress_; - bool no_progress_; - size_t jobs_; - bool jobs_specified_; - size_t max_jobs_; - bool max_jobs_specified_; - size_t queue_depth_; - bool queue_depth_specified_; - string file_cache_; - bool file_cache_specified_; - size_t max_stack_; - bool max_stack_specified_; - bool serial_stop_; - bool dry_run_; - bool match_only_; - bool no_external_modules_; - bool structured_result_; - bool mtime_check_; - bool no_mtime_check_; - bool no_column_; - bool no_line_; - path buildfile_; - bool buildfile_specified_; - path config_guess_; - bool config_guess_specified_; - path config_sub_; - bool config_sub_specified_; - string pager_; - bool pager_specified_; - strings pager_option_; - bool pager_option_specified_; - string options_file_; - bool options_file_specified_; - dir_path default_options_; - bool default_options_specified_; - bool no_default_options_; - bool help_; - bool version_; - }; -} - -// Print page usage information. -// -namespace build2 -{ - ::build2::cl::usage_para - print_b_usage (::std::ostream&, - ::build2::cl::usage_para = ::build2::cl::usage_para::none); -} - -#include <build2/b-options.ixx> - -// Begin epilogue. -// -// -// End epilogue. - -#endif // BUILD2_B_OPTIONS_HXX diff --git a/build2/b-options.ixx b/build2/b-options.ixx deleted file mode 100644 index 0e90ba1..0000000 --- a/build2/b-options.ixx +++ /dev/null @@ -1,561 +0,0 @@ -// -*- C++ -*- -// -// This file was generated by CLI, a command line interface -// compiler for C++. -// - -// Begin prologue. -// -// -// End prologue. - -#include <cassert> - -namespace build2 -{ - namespace cl - { - // usage_para - // - inline usage_para:: - usage_para (value v) - : v_ (v) - { - } - - // 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, - const std::string& message) - : option_ (option), - value_ (value), - message_ (message) - { - } - - inline const std::string& invalid_value:: - option () const - { - return option_; - } - - inline const std::string& invalid_value:: - value () const - { - return value_; - } - - inline const std::string& invalid_value:: - message () const - { - return message_; - } - - // file_io_failure - // - inline file_io_failure:: - file_io_failure (const std::string& file) - : file_ (file) - { - } - - inline const std::string& file_io_failure:: - file () const - { - return file_; - } - - // unmatched_quote - // - inline unmatched_quote:: - unmatched_quote (const std::string& argument) - : argument_ (argument) - { - } - - inline const std::string& unmatched_quote:: - argument () const - { - return argument_; - } - - // 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_; - } - - // argv_file_scanner - // - inline argv_file_scanner:: - argv_file_scanner (int& argc, - char** argv, - const std::string& option, - bool erase) - : argv_scanner (argc, argv, erase), - option_ (option), - options_ (&option_info_), - options_count_ (1), - i_ (1), - skip_ (false) - { - option_info_.option = option_.c_str (); - option_info_.search_func = 0; - } - - inline argv_file_scanner:: - argv_file_scanner (int start, - int& argc, - char** argv, - const std::string& option, - bool erase) - : argv_scanner (start, argc, argv, erase), - option_ (option), - options_ (&option_info_), - options_count_ (1), - i_ (1), - skip_ (false) - { - option_info_.option = option_.c_str (); - option_info_.search_func = 0; - } - - inline argv_file_scanner:: - argv_file_scanner (const std::string& file, - const std::string& option) - : argv_scanner (0, zero_argc_, 0), - option_ (option), - options_ (&option_info_), - options_count_ (1), - i_ (1), - skip_ (false) - { - option_info_.option = option_.c_str (); - option_info_.search_func = 0; - - load (file); - } - - inline argv_file_scanner:: - argv_file_scanner (int& argc, - char** argv, - const option_info* options, - std::size_t options_count, - bool erase) - : argv_scanner (argc, argv, erase), - options_ (options), - options_count_ (options_count), - i_ (1), - skip_ (false) - { - } - - inline argv_file_scanner:: - argv_file_scanner (int start, - int& argc, - char** argv, - const option_info* options, - std::size_t options_count, - bool erase) - : argv_scanner (start, argc, argv, erase), - options_ (options), - options_count_ (options_count), - i_ (1), - skip_ (false) - { - } - - inline argv_file_scanner:: - argv_file_scanner (const std::string& file, - const option_info* options, - std::size_t options_count) - : argv_scanner (0, zero_argc_, 0), - options_ (options), - options_count_ (options_count), - i_ (1), - skip_ (false) - { - load (file); - } - } -} - -namespace build2 -{ - // options - // - - inline const uint64_t& options:: - build2_metadata () const - { - return this->build2_metadata_; - } - - inline bool options:: - build2_metadata_specified () const - { - return this->build2_metadata_specified_; - } - - inline const bool& options:: - v () const - { - return this->v_; - } - - inline const bool& options:: - V () const - { - return this->V_; - } - - inline const bool& options:: - quiet () const - { - return this->quiet_; - } - - inline const bool& options:: - silent () const - { - return this->silent_; - } - - inline const uint16_t& options:: - verbose () const - { - return this->verbose_; - } - - inline bool options:: - verbose_specified () const - { - return this->verbose_specified_; - } - - inline const bool& options:: - stat () const - { - return this->stat_; - } - - inline const std::set<string>& options:: - dump () const - { - return this->dump_; - } - - inline bool options:: - dump_specified () const - { - return this->dump_specified_; - } - - inline const bool& options:: - progress () const - { - return this->progress_; - } - - inline const bool& options:: - no_progress () const - { - return this->no_progress_; - } - - inline const size_t& options:: - jobs () const - { - return this->jobs_; - } - - inline bool options:: - jobs_specified () const - { - return this->jobs_specified_; - } - - inline const size_t& options:: - max_jobs () const - { - return this->max_jobs_; - } - - inline bool options:: - max_jobs_specified () const - { - return this->max_jobs_specified_; - } - - inline const size_t& options:: - queue_depth () const - { - return this->queue_depth_; - } - - inline bool options:: - queue_depth_specified () const - { - return this->queue_depth_specified_; - } - - inline const string& options:: - file_cache () const - { - return this->file_cache_; - } - - inline bool options:: - file_cache_specified () const - { - return this->file_cache_specified_; - } - - inline const size_t& options:: - max_stack () const - { - return this->max_stack_; - } - - inline bool options:: - max_stack_specified () const - { - return this->max_stack_specified_; - } - - inline const bool& options:: - serial_stop () const - { - return this->serial_stop_; - } - - inline const bool& options:: - dry_run () const - { - return this->dry_run_; - } - - inline const bool& options:: - match_only () const - { - return this->match_only_; - } - - inline const bool& options:: - no_external_modules () const - { - return this->no_external_modules_; - } - - inline const bool& options:: - structured_result () const - { - return this->structured_result_; - } - - inline const bool& options:: - mtime_check () const - { - return this->mtime_check_; - } - - inline const bool& options:: - no_mtime_check () const - { - return this->no_mtime_check_; - } - - inline const bool& options:: - no_column () const - { - return this->no_column_; - } - - inline const bool& options:: - no_line () const - { - return this->no_line_; - } - - inline const path& options:: - buildfile () const - { - return this->buildfile_; - } - - inline bool options:: - buildfile_specified () const - { - return this->buildfile_specified_; - } - - inline const path& options:: - config_guess () const - { - return this->config_guess_; - } - - inline bool options:: - config_guess_specified () const - { - return this->config_guess_specified_; - } - - inline const path& options:: - config_sub () const - { - return this->config_sub_; - } - - inline bool options:: - config_sub_specified () const - { - return this->config_sub_specified_; - } - - inline const string& options:: - pager () const - { - return this->pager_; - } - - inline bool options:: - pager_specified () const - { - return this->pager_specified_; - } - - inline const strings& options:: - pager_option () const - { - return this->pager_option_; - } - - inline bool options:: - pager_option_specified () const - { - return this->pager_option_specified_; - } - - inline const string& options:: - options_file () const - { - return this->options_file_; - } - - inline bool options:: - options_file_specified () const - { - return this->options_file_specified_; - } - - inline const dir_path& options:: - default_options () const - { - return this->default_options_; - } - - inline bool options:: - default_options_specified () const - { - return this->default_options_specified_; - } - - inline const bool& options:: - no_default_options () const - { - return this->no_default_options_; - } - - inline const bool& options:: - help () const - { - return this->help_; - } - - inline const bool& options:: - version () const - { - return this->version_; - } -} - -// Begin epilogue. -// -// -// End epilogue. diff --git a/build2/b.cli b/build2/b.cli deleted file mode 100644 index 0ce0f9e..0000000 --- a/build2/b.cli +++ /dev/null @@ -1,749 +0,0 @@ -// file : build2/b.cli -// license : MIT; see accompanying LICENSE file - -include <set>; -include <libbuild2/types.hxx>; - -"\section=1" -"\name=b" -"\summary=build system driver" - -namespace build2 -{ - { - "<options> - <variables> - <buildspec> <meta-operation> <operation> <target> <parameters>", - - "\h|SYNOPSIS| - - \c{\b{b --help}\n - \b{b --version}\n - \b{b} [<options>] [<variables>] [<buildspec>]} - - \c{<buildspec> = <meta-operation>\b{(}<operation>\b{(}<target>...[\b{,}<parameters>]\b{)}...\b{)}...} - - \h|DESCRIPTION| - - The \cb{build2} build system driver executes a set of meta-operations on - operations on targets according to the build specification, or - \i{buildspec} for short. This process can be controlled by specifying - driver <options> and build system <variables>. - - Note that <options>, <variables>, and <buildspec> fragments can be - specified in any order. To avoid treating an argument that starts with - \cb{'-'} as an option, add the \cb{'--'} separator. To avoid treating an - argument that contains \cb{'='} as a variable, add the second \cb{'--'} - separator." - } - - // For usage it's nice to see the list of options on the first page. So - // let's not put this "extended" description into usage. - // - { - "<meta-operation> <operation> <target> <parameters> <src-base>", - "", - - "All components in the buildspec can be omitted. If <meta-operation> is - omitted, then it defaults to \cb{perform}. If <operation> is omitted, - then it defaults to the default operation for this meta-operation. For - \cb{perform} it is \cb{update}. Finally, if <target> is omitted, then it - defaults to the current working directory. A meta-operation on operation - is called an \i{action}. Some operations and meta-operations may take - additional <parameters>. For example: - - \ - $ b # perform(update(./)) - $ b foo/ # perform(update(foo/)) - $ b foo/ bar/ # perform(update(foo/ bar/)) - $ b update # perform(update(./)) - $ b 'clean(../)' # perform(clean(../)) - $ b perform # perform(update(./)) - $ b configure # configure(?(./)) - $ b 'configure(../)' # configure(?(../)) - $ b clean update # perform(clean(./) update(./)) - $ b configure update # configure(?(./)) perform(update(./)) - $ b 'create(conf/, cxx)' # create(?(conf/), cxx) - \ - - Notice the question mark used to show the (imaginary) default operation - for the \cb{configure} meta-operation. For \cb{configure} the default - operation is \"all operations\". That is, it will configure all the - operations for the specified target. - - You can also \"generate\" multiple operations for the same set of targets. - Compare: - - \ - $ b 'clean(foo/ bar/)' 'update(foo/ bar/)' - $ b '{clean update}(foo/ bar/)' - \ - - Some more useful buildspec examples: - - \ - $ b '{clean update}(...)' # rebuild - $ b '{clean update clean}(...)' # make sure builds - $ b '{clean test clean}(...)' # make sure passes tests - $ b '{clean disfigure}(...)' # similar to distclean - \ - - In POSIX shells parenthesis are special characters and must be quoted - when used in a buildspec. Besides being an inconvenience in itself, - quoting also inhibits path auto-completion. To help with this situation a - shortcut syntax is available for executing a single operation or - meta-operation, for example: - - \ - $ b clean: foo/ bar/ # clean(foo/ bar/) - $ b configure: src/@out/ # configure(src/@out/) - $ b create: conf/, cxx # create(conf/, cxx) - $ b configure: config.cxx=g++ src/ # configure(src/) config.cxx=g++ - \ - - To activate the shortcut syntax the first buildspec argument must start - with an operation or meta-operation name and end with a colon (\cb{:}). - To transform the shortcut syntax to the normal buildspec syntax the colon - is replaced with the opening parenthesis ('\cb{(}'), the rest of the - buildspec arguments are treated as is, and the final closing parenthesis - ('\cb{)}') is added. - - For each <target> the driver expects to find \cb{buildfile} either in the - target's directory or, if the directory is part of the \cb{out} tree - (\cb{out_base}), in the corresponding \cb{src} directory (\cb{src_base}). - - For example, assuming \cb{foo/} is the source directory of a project: - - \ - $ b foo/ # out_base=src_base=foo/ - $ b foo-out/ # out_base=foo-out/ src_base=foo/ - $ b foo-out/exe{foo} # out_base=foo-out/ src_base=foo/ - \ - - An exception to this requirement is a directory target in which case, - provided the directory has subdirectories, an \i{implied} \cb{buildfile} - with the following content is assumed: - - \ - # Implied directory buildfile: build all subdirectories. - # - ./: */ - \ - - In the above example, we assumed that the build system driver was able to - determine the association between \cb{out_base} and \cb{src_base}. In - case \cb{src_base} and \cb{out_base} are not the same directory, this is - achieved in one of two ways: the \cb{config} module (which implements the - \cb{configure}, \cb{disfigure}, and \cb{create} meta-operations) saves - this association as part of the configuration process. If, however, the - association hasn't been saved, then we have to specify \cb{src_base} - explicitly using the following extended <target> syntax: - - \c{<src-base>/@<target>} - - Continuing with the previous example: - - \ - $ b foo/@foo-out/exe{foo} # out_base=foo-out/ src_base=foo/ - \ - - Normally, you would need to specify \cb{src_base} explicitly only once, - during configuration. For example, a typical usage would be: - - \ - $ b configure: foo/@foo-out/ # src_base is saved - $ b foo-out/ # no need to specify src_base - $ b clean: foo-out/exe{foo} # no need to specify src_base - \ - - Besides in and out of source builds, \cb{build2} also supports - configuring a project's source directory as \i{forwarded} to an out of - source build. With such a forwarded configuration in place, if we run the - build system driver from the source directory, it will automatically - build in the output directory and \i{backlink} (using symlinks or another - suitable mechanism) certain \"interesting\" targets (executables, - documentation, etc) to the source directory for easy access. Continuing - with the previous example: - - \ - $ b configure: foo/@foo-out/,forward # foo/ forwarded to foo-out/ - $ cd foo/ - $ b # build in foo-out/ - $ ./foo # symlink to foo-out/foo - \ - - The ability to specify \cb{build2} variables as part of the command line - is normally used to pass configuration values, for example: - - \ - $ b config.cxx=clang++ config.cxx.coptions=-O3 - \ - - Similar to buildspec, POSIX shells often inhibit path auto-completion on - the right hand side of a variable assignment. To help with this situation - the assignment can be broken down into three separate command line - arguments, for example: - - \ - $ b config.import.libhello = ../libhello/ - \ - - The build system has the following built-in and pre-defined - meta-operations: - - \dl| - - \li|\cb{perform} - - Perform an operation.| - - \li|\cb{configure} - - Configure all operations supported by a project and save the result - in the project's \cb{build/config.build} file. Implemented by the - \cb{config} module. For example: - - \ - $ b configure \ - config.cxx=clang++ \ - config.cxx.coptions=-O3 \ - config.install.root=/usr/local \ - config.install.root.sudo=sudo - \ - - Use the \cb{forward} parameter to instead configure a source - directory as forwarded to an out of source build. For example: - - \ - $ b configure: src/@out/,forward - \ - - | - - \li|\cb{disfigure} - - Disfigure all operations supported by a project and remove the - project's \cb{build/config.build} file. Implemented by the - \cb{config} module. - - Use the \cb{forward} parameter to instead disfigure forwarding of a - source directory to an out of source build. For example: - - \ - $ b disfigure: src/,forward - \ - - | - - \li|\cb{create} - - Create and configure a \i{configuration} project. Implemented by the - \cb{config} module. - - Normally a \cb{build2} project is created manually by writing the - \cb{bootstrap.build} and \cb{config.build} files, adding source - files, and so on. However, a special kind of project, which we call - \i{configuration}, is often useful. Such a project doesn't have any - source files of its own. Instead, it serves as an amalgamation for - building other projects as part of it. Doing it this way has two - major benefits: sub-projects automatically resolve their imports - to other projects in the amalgamation and sub-projects inherits their - configuration from the amalgamation (which means if we want to change - something, we only need to do it in one place). - - As an example, let's assume we have two C++ projects: the - \cb{libhello} library in \cb{libhello/} and the \cb{hello} executable - that imports it in \cb{hello/}. And we want to build \cb{hello} with - \cb{clang++}. - - One way to do it would be to configure and build each project in its - own directory, for example: - - \ - $ b configure: libhello/@libhello-clang/ config.cxx=clang++ - $ b configure: hello/@hello-clang/ config.cxx=clang++ \ - config.import.libhello=libhello-clang/ - \ - - The two drawbacks, as mentioned above, are the need to explicitly - resolve the import and having to make changes in multiple places - should, for example, we want to switch from \cb{clang++} to \cb{g++}. - - We can, however, achieve the same end result but without any of the - drawbacks using the configuration project: - - \ - $ b create: clang/,cxx config.cxx=clang++ # Creates clang/. - $ b configure: libhello/@clang/libhello/ - $ b configure: hello/@clang/hello/ - \ - - The targets passed to the \cb{create} meta-operation must be - directories which should either not exist or be empty. For each - such directory \cb{create} first initializes a project as described - below and then configures it by executing the \cb{configure} - meta-operation. - - The first optional parameter to \cb{create} is the list of modules to - load in \cb{root.build}. By default, \cb{create} appends \cb{.config} - to the names of these modules so that only their configurations are - loaded. You can override this behavior by specifying the period - (\cb{.}) after the module name. You can also instruct \cb{create} to - use the optional module load by prefixing the module name with the - question mark (\cb{?}). - - The second optional parameter is the list of modules to load in - \cb{bootstrap.build}. If not specified, then the \cb{test}, - \cb{dist}, and \cb{install} modules are loaded by default. The - \cb{config} module is always loaded first. - - Besides creating project's \cb{bootstrap.build} and \cb{root.build}, - \cb{create} also writes the root \cb{buildfile} with the following - contents: - - \ - ./: {*/ -build/} - \ - - If used, this \cb{buildfile} will build all the sub-projects - currently present in the configuration.| - - \li|\cb{dist} - - Prepare a distribution containing all files necessary to perform all - operations in a project. Implemented by the \cb{dist} module.| - - \li|\cb{info} - - Print basic information (name, version, source and output - directories, etc) about one or more projects to \cb{STDOUT}, - separating multiple projects with a blank line. Each project is - identified by its root directory target. For example: - - \ - $ b info: libfoo/ libbar/ - \ - - || - - The build system has the following built-in and pre-defined operations: - - \dl| - - \li|\cb{update} - - Update a target.| - - \li|\cb{clean} - - Clean a target.| - - \li|\cb{test} - - Test a target. Performs \cb{update} as a pre-operation. Implemented by - the \cb{test} module.| - - \li|\cb{update-for-test} - - Update a target for testing. This operation is equivalent to the - \cb{update} pre-operation as executed by the \cb{test} operation and - can be used to only update what is necessary for testing. Implemented - by the \cb{test} module.| - - \li|\cb{install} - - Install a target. Performs \cb{update} as a pre-operation. Implemented - by the \cb{install} module.| - - - \li|\cb{uninstall} - - Uninstall a target. Performs \cb{update} as a pre-operation. - Implemented by the \cb{install} module.| - - \li|\cb{update-for-install} - - Update a target for installation. This operation is equivalent to the - \cb{update} pre-operation as executed by the \cb{install} operation - and can be used to only update what is necessary for - installation. Implemented by the \cb{install} module.|| - - Note that buildspec and command line variable values are treated as - \cb{buildfile} fragments and so can use quoting and escaping as well as - contain variable expansions and evaluation contexts. However, to be more - usable on various platforms, escaping in these two situations is limited - to the \i{effective sequences} of \cb{\\'}, \cb{\\\"}, \cb{\\\\}, - \cb{\\$}, and \cb{\\(} with all other sequences interpreted as is. - Together with double-quoting this is sufficient to represent any value. - For example: - - \ - $ b config.install.root=c:\projects\install - $ b \"config.install.root='c:\Program Files (x86)\test\'\" - $ b 'config.cxx.poptions=-DFOO_STR=\"foo\"' - \ - " - } - - class options - { - "\h#options|OPTIONS|" - - uint64_t --build2-metadata; // Leave undocumented/hidden. - - bool -v - { - "Print actual commands being executed. This options is equivalent to - \cb{--verbose 2}." - } - - bool -V - { - "Print all underlying commands being executed. This options is - equivalent to \cb{--verbose 3}." - } - - bool --quiet|-q - { - "Run quietly, only printing error messages in most contexts. In certain - contexts (for example, while updating build system modules) this - verbosity level may be ignored. Use \cb{--silent} to run quietly in all - contexts. This option is equivalent to \cb{--verbose 0}." - } - - bool --silent - { - "Run quietly, only printing error messages in all contexts." - } - - uint16_t --verbose = 1 - { - "<level>", - "Set the diagnostics verbosity to <level> between 0 and 6. Level 0 - disables any non-error messages (but see the difference between - \cb{--quiet} and \cb{--silent}) while level 6 produces lots of - information, with level 1 being the default. The following additional - types of diagnostics are produced at each level: - - \ol| - - \li|High-level information messages.| - - \li|Essential underlying commands being executed.| - - \li|All underlying commands being executed.| - - \li|Information that could be helpful to the user.| - - \li|Information that could be helpful to the developer.| - - \li|Even more detailed information.||" - } - - bool --stat - { - "Display build statistics." - } - - std::set<string> --dump - { - "<phase>", - "Dump the build system state after the specified phase. Valid <phase> - values are \cb{load} (after loading \cb{buildfiles}) and \cb{match} - (after matching rules to targets). Repeat this option to dump the - state after multiple phases." - } - - bool --progress - { - "Display build progress. If printing to a terminal the progress is - displayed by default for low verbosity levels. Use \cb{--no-progress} - to suppress." - } - - bool --no-progress - { - "Don't display build progress." - } - - size_t --jobs|-j - { - "<num>", - "Number of active jobs to perform in parallel. This includes both the - number of active threads inside the build system as well as the number - of external commands (compilers, linkers, etc) started but not yet - finished. If this option is not specified or specified with the \cb{0} - value, then the number of available hardware threads is used." - } - - size_t --max-jobs|-J - { - "<num>", - "Maximum number of jobs (threads) to create. The default is 8x the - number of active jobs (\cb{--jobs|j}) on 32-bit architectures and 32x - on 64-bit. See the build system scheduler implementation for details." - } - - size_t --queue-depth|-Q = 4 - { - "<num>", - "The queue depth as a multiplier over the number of active jobs. - Normally we want a deeper queue if the jobs take long (for example, - compilation) and shorter if they are quick (for example, simple tests). - The default is 4. See the build system scheduler implementation for - details." - } - - string --file-cache - { - "<impl>", - "File cache implementation to use for intermediate build results. Valid - values are \cb{noop} (no caching or compression) and \cb{sync-lz4} (no - caching with synchronous LZ4 on-disk compression). If this option is - not specified, then a suitable default implementation is used - (currently \cb{sync-lz4})." - } - - size_t --max-stack - { - "<num>", - "The maximum stack size in KBytes to allow for newly created threads. - For \i{pthreads}-based systems the driver queries the stack size of - the main thread and uses the same size for creating additional threads. - This allows adjusting the stack size using familiar mechanisms, such - as \cb{ulimit}. Sometimes, however, the stack size of the main thread - is excessively large. As a result, the driver checks if it is greater - than a predefined limit (64MB on 64-bit systems and 32MB on 32-bit - ones) and caps it to a more sensible value (8MB) if that's the case. - This option allows you to override this check with the special zero - value indicating that the main thread stack size should be used as is." - } - - bool --serial-stop|-s - { - "Run serially and stop at the first error. This mode is useful to - investigate build failures that are caused by build system errors - rather than compilation errors. Note that if you don't want to keep - going but still want parallel execution, add \cb{--jobs|-j} (for - example \cb{-j\ 0} for default concurrency)." - } - - bool --dry-run|-n - { - "Print commands without actually executing them. Note that commands - that are required to create an accurate build state will still be - executed and the extracted auxiliary dependency information saved. In - other words, this is not the \i{\"don't touch the filesystem\"} mode - but rather \i{\"do minimum amount of work to show what needs to be - done\"}. Note also that only the \cb{perform} meta-operation supports - this mode." - } - - bool --match-only - { - "Match the rules but do not execute the operation. This mode is primarily - useful for profiling." - } - - bool --no-external-modules - { - "Don't load external modules during project bootstrap. Note that this - option can only be used with meta-operations that do not load the - project's \cb{buildfiles}, such as \cb{info}." - } - - bool --structured-result - { - "Write the result of execution in a structured form. In this mode, - instead of printing to \cb{STDERR} diagnostics messages about the - outcome of executing actions on targets, the driver writes to - \cb{STDOUT} a structured result description one line per the - buildspec action/target pair. Each line has the following format: - - \c{\i{state} \i{meta-operation} \i{operation} \i{target}} - - Where \ci{state} can be one of \cb{unchanged}, \cb{changed}, or - \cb{failed}. If the action is a pre or post operation, then the - outer operation is specified in parenthesis. For example: - - \ - unchanged perform update(test) /tmp/dir{hello/} - changed perform test /tmp/dir{hello/} - \ - - Note that only the \cb{perform} meta-operation supports the structured - result output. - " - } - - bool --mtime-check - { - "Perform file modification time sanity checks. These checks can be - helpful in diagnosing spurious rebuilds and are enabled by default - for the staged version of the build system. Use \cb{--no-mtime-check} - to disable." - } - - bool --no-mtime-check - { - "Don't perform file modification time sanity checks." - } - - bool --no-column - { - "Don't print column numbers in diagnostics." - } - - bool --no-line - { - "Don't print line and column numbers in diagnostics." - } - - path --buildfile - { - "<path>", - "The alternative file to read build information from. The default is - \cb{buildfile} or \cb{build2file}, depending on the project's build - file/directory naming scheme. If <path> is '\cb{-}', then read from - \cb{STDIN}. Note that this option only affects the files read as part - of the buildspec processing. Specifically, it has no effect on the - \cb{source} and \cb{include} directives. As a result, this option is - primarily intended for testing rather than changing the build file - names in real projects." - } - - path --config-guess - { - "<path>", - "The path to the \cb{config.guess(1)} script that should be used to - guess the host machine triplet. If this option is not specified, then - \cb{b} will fall back on to using the target it was built for as host." - } - - path --config-sub - { - "<path>", - "The path to the \cb{config.sub(1)} script that should be used to - canonicalize machine triplets. If this option is not specified, then - \cb{b} will use its built-in canonicalization support which should - be sufficient for commonly-used platforms." - } - - string --pager // String to allow empty value. - { - "<path>", - "The pager program to be used to show long text. Commonly used pager - programs are \cb{less} and \cb{more}. You can also specify additional - options that should be passed to the pager program with - \cb{--pager-option}. If an empty string is specified as the pager - program, then no pager will be used. If the pager program is not - explicitly specified, then \cb{b} will try to use \cb{less}. If it - is not available, then no pager will be used." - } - - strings --pager-option - { - "<opt>", - "Additional option to be passed to the pager program. See \cb{--pager} - for more information on the pager program. Repeat this option to - specify multiple pager options." - } - - // The following option is "fake" in that it is actually handled by - // argv_file_scanner. We have it here for documentation. - // - string --options-file - { - "<file>", - "Read additional options from <file>. Each option should appear on a - separate line optionally followed by space or equal sign (\cb{=}) and - an option value. Empty lines and lines starting with \cb{#} are - ignored. Option values can be enclosed in double (\cb{\"}) or single - (\cb{'}) quotes to preserve leading and trailing whitespaces as well as - to specify empty values. If the value itself contains trailing or - leading quotes, enclose it with an extra pair of quotes, for example - \cb{'\"x\"'}. Non-leading and non-trailing quotes are interpreted as - being part of the option value. - - The semantics of providing options in a file is equivalent to providing - the same set of options in the same order on the command line at the - point where the \cb{--options-file} option is specified except that - the shell escaping and quoting is not required. Repeat this option - to specify more than one options file." - } - - dir_path --default-options - { - "<dir>", - "The directory to load additional default options files from." - } - - bool --no-default-options - { - "Don't load default options files." - } - - bool --help {"Print usage information and exit."} - bool --version {"Print version and exit."} - }; - - " - \h|DEFAULT OPTIONS FILES| - - Instead of having a separate config file format for tool configuration, the - \cb{build2} toolchain uses \i{default options files} which contain the same - options as what can be specified on the command line. The default options - files are like options files that one can specify with \cb{--options-file} - except that they are loaded by default. - - The default options files for the build system driver are called - \cb{b.options} and are searched for in the \cb{.build2/} subdirectory of the - home directory and in the system directory (for example, \cb{/etc/build2/}) - if configured. Note that besides options these files can also contain global - variable overrides. - - Once the search is complete, the files are loaded in the reverse order, that - is, beginning from the system directory (if any), followed by the home - directory, and finishing off with the options specified on the command line. - In other words, the files are loaded from the more generic to the more - specific with the command line options having the ability to override any - values specified in the default options files. - - If a default options file contains \cb{--no-default-options}, then the - search is stopped at the directory containing this file and no outer files - are loaded. If this option is specified on the command line, then none of - the default options files are searched for or loaded. - - An additional directory containing default options files can be specified - with \cb{--default-options}. Its configuration files are loaded after the - home directory. - - The order in which default options files are loaded is traced at the - verbosity level 3 (\cb{-V} option) or higher. - - \h|EXIT STATUS| - - Non-zero exit status is returned in case of an error. - " - - // NOTE: remember to update --build2-metadata output if adding any relevant - // new environment variables. - // - " - \h|ENVIRONMENT| - - The \cb{HOME} environment variable is used to determine the user's home - directory. If it is not set, then \cb{getpwuid(3)} is used instead. This - value is used to shorten paths printed in diagnostics by replacing the home - directory with \cb{~/}. It is also made available to \cb{buildfile}'s as the - \cb{build.home} variable. - - The \cb{BUILD2_VAR_OVR} environment variable is used to propagate global - variable overrides to nested build system driver invocations. Its value is a - list of global variable assignments separated with newlines. - - The \cb{BUILD2_DEF_OPT} environment variable is used to suppress loading of - default options files in nested build system driver invocations. Its values - are \cb{false} or \cb{0} to suppress and \cb{true} or \cb{1} to load. - " -} diff --git a/build2/b.cxx b/build2/b.cxx index b07dd0e..8decadf 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -1,26 +1,18 @@ // file : build2/b.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file -#ifndef _WIN32 -# include <signal.h> // signal() -#else -# include <libbutl/win32-utility.hxx> -#endif - -#ifdef __GLIBCXX__ -# include <locale> -#endif - #include <sstream> -#include <cstring> // strcmp(), strchr() #include <typeinfo> #include <iostream> // cout #include <exception> // terminate(), set_terminate(), terminate_handler -#include <libbutl/pager.mxx> -#include <libbutl/fdstream.mxx> // stderr_fd(), fdterm() -#include <libbutl/backtrace.mxx> // backtrace() -#include <libbutl/default-options.mxx> +#include <libbutl/pager.hxx> +#include <libbutl/fdstream.hxx> // stderr_fd(), fdterm() +#include <libbutl/backtrace.hxx> // backtrace() + +#ifndef BUILD2_BOOTSTRAP +# include <libbutl/json/serializer.hxx> +#endif #include <libbuild2/types.hxx> #include <libbuild2/utility.hxx> @@ -43,7 +35,8 @@ #include <libbuild2/parser.hxx> -#include <build2/b-options.hxx> +#include <libbuild2/b-options.hxx> +#include <libbuild2/b-cmdline.hxx> // Build system modules. // @@ -61,8 +54,7 @@ #ifndef BUILD2_BOOTSTRAP # include <libbuild2/bash/init.hxx> - -# include <build2/cli/init.hxx> +# include <libbuild2/cli/init.hxx> #endif using namespace butl; @@ -70,75 +62,161 @@ using namespace std; namespace build2 { - static options ops; - int main (int argc, char* argv[]); +#ifndef BUILD2_BOOTSTRAP // Structured result printer (--structured-result mode). // class result_printer { public: - result_printer (const action_targets& tgs): tgs_ (tgs) {} + result_printer (const b_options& ops, + const action_targets& tgs, + json::stream_serializer& js) + : ops_ (ops), tgs_ (tgs), json_serializer_ (js) {} + ~result_printer (); private: + void + print_lines (); + + void + print_json (); + + private: + const b_options& ops_; const action_targets& tgs_; + json::stream_serializer& json_serializer_; }; + void result_printer:: + print_lines () + { + for (const action_target& at: tgs_) + { + if (at.state == target_state::unknown) + continue; // Not a target/no result. + + const target& t (at.as<target> ()); + context& ctx (t.ctx); + + cout << at.state + << ' ' << ctx.current_mif->name + << ' ' << ctx.current_inner_oif->name; + + if (ctx.current_outer_oif != nullptr) + cout << '(' << ctx.current_outer_oif->name << ')'; + + // There are two ways one may wish to identify the target of the + // operation: as something specific but inherently non-portable (say, a + // filesystem path, for example c:\tmp\foo.exe) or as something regular + // that can be used to refer to a target in a portable way (for example, + // c:\tmp\exe{foo}; note that the directory part is still not portable). + // Which one should we use is a good question. Let's go with the + // portable one for now and see how it goes (we can always add a format + // variant, e.g., --structured-result=lines-path). Note also that the + // json format includes both. + + // Set the stream extension verbosity to 0 to suppress extension + // printing by default (this can still be overriden by the target type's + // print function as is the case for file{}, for example). And set the + // path verbosity to 1 to always print absolute. + // + stream_verbosity sv (stream_verb (cout)); + stream_verb (cout, stream_verbosity (1, 0)); + + cout << ' ' << t << endl; + + stream_verb (cout, sv); + } + } + + void result_printer:: + print_json () + { + json::stream_serializer& s (json_serializer_); + + for (const action_target& at: tgs_) + { + if (at.state == target_state::unknown) + continue; // Not a target/no result. + + const target& t (at.as<target> ()); + context& ctx (t.ctx); + + s.begin_object (); + + // Quoted target. + // + s.member_name ("target"); + dump_quoted_target_name (s, t); + + // Display target. + // + s.member_name ("display_target"); + dump_display_target_name (s, t); + + s.member ("target_type", t.type ().name, false /* check */); + + if (t.is_a<dir> ()) + s.member ("target_path", t.dir.string ()); + else if (const auto* pt = t.is_a<path_target> ()) + s.member ("target_path", pt->path ().string ()); + + s.member ("meta_operation", ctx.current_mif->name, false /* check */); + s.member ("operation", ctx.current_inner_oif->name, false /* check */); + + if (ctx.current_outer_oif != nullptr) + s.member ("outer_operation", + ctx.current_outer_oif->name, + false /* check */); + + s.member ("state", to_string (at.state), false /* check */); + + s.end_object (); + } + } + result_printer:: ~result_printer () { // Let's do some sanity checking even when we are not in the structred // output mode. // +#ifndef NDEBUG for (const action_target& at: tgs_) { switch (at.state) { - case target_state::unknown: continue; // Not a target/no result. + case target_state::unknown: case target_state::unchanged: case target_state::changed: case target_state::failed: break; // Valid states. default: assert (false); } + } +#endif - if (ops.structured_result ()) + if (ops_.structured_result_specified ()) + { + switch (ops_.structured_result ()) { - const target& t (at.as<target> ()); - context& ctx (t.ctx); - - cout << at.state - << ' ' << ctx.current_mif->name - << ' ' << ctx.current_inner_oif->name; - - if (ctx.current_outer_oif != nullptr) - cout << '(' << ctx.current_outer_oif->name << ')'; - - // There are two ways one may wish to identify the target of the - // operation: as something specific but inherently non-portable (say, - // a filesystem path, for example c:\tmp\foo.exe) or as something - // regular that can be used to refer to a target in a portable way - // (for example, c:\tmp\exe{foo}; note that the directory part is - // still not portable). Which one should we use is a good question. - // Let's go with the portable one for now and see how it goes (we - // can always add a format version, e.g., --structured-result=2). - - // Set the stream extension verbosity to 0 to suppress extension - // printing by default (this can still be overriden by the target - // type's print function as is the case for file{}, for example). - // And set the path verbosity to 1 to always print absolute. - // - stream_verbosity sv (stream_verb (cout)); - stream_verb (cout, stream_verbosity (1, 0)); - - cout << ' ' << t << endl; - - stream_verb (cout, sv); + case structured_result_format::lines: + { + print_lines (); + break; + } + case structured_result_format::json: + { + print_json (); + break; + } } } } +#endif } // Print backtrace if terminating due to an unhandled exception. Note that @@ -171,425 +249,21 @@ main (int argc, char* argv[]) tracer trace ("main"); - int r (0); - - // This is a little hack to make out baseutils for Windows work when called - // with absolute path. In a nutshell, MSYS2's exec*p() doesn't search in the - // parent's executable directory, only in PATH. And since we are running - // without a shell (that would read /etc/profile which sets PATH to some - // sensible values), we are only getting Win32 PATH values. And MSYS2 /bin - // is not one of them. So what we are going to do is add /bin at the end of - // PATH (which will be passed as is by the MSYS2 machinery). This will make - // MSYS2 search in /bin (where our baseutils live). And for everyone else - // this should be harmless since it is not a valid Win32 path. - // -#ifdef _WIN32 - { - string mp; - if (optional<string> p = getenv ("PATH")) - { - mp = move (*p); - mp += ';'; - } - mp += "/bin"; - - setenv ("PATH", mp); - } -#endif - - // A data race happens in the libstdc++ (as of GCC 7.2) implementation of - // the ctype<char>::narrow() function (bug #77704). The issue is easily - // triggered by the testscript runner that indirectly (via regex) uses - // ctype<char> facet of the global locale (and can potentially be triggered - // by other locale- aware code). We work around this by pre-initializing the - // global locale facet internal cache. - // -#ifdef __GLIBCXX__ - { - const ctype<char>& ct (use_facet<ctype<char>> (locale ())); - - for (size_t i (0); i != 256; ++i) - ct.narrow (static_cast<char> (i), '\0'); - } -#endif - - // On POSIX ignore SIGPIPE which is signaled to a pipe-writing process if - // the pipe reading end is closed. Note that by default this signal - // terminates a process. Also note that there is no way to disable this - // behavior on a file descriptor basis or for the write() function call. - // -#ifndef _WIN32 - if (signal (SIGPIPE, SIG_IGN) == SIG_ERR) - fail << "unable to ignore broken pipe (SIGPIPE) signal: " - << system_error (errno, generic_category ()); // Sanitize. -#endif + init_process (); + int r (0); + b_options ops; scheduler sched; - // Parse the command line. + // Statistics. // + size_t phase_switch_contention (0); + try { - // 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 = [] () - { - uint16_t v ( - ops.verbose_specified () - ? ops.verbose () - : ops.V () ? 3 : ops.v () ? 2 : ops.quiet () || ops.silent () ? 0 : 1); - - if (ops.silent () && v != 0) - fail << "specified with -v, -V, or --verbose verbosity level " << v - << " is incompatible with --silent"; - - return v; - }; - - // 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). + // Parse the command line. // - strings cmd_vars; - string args; - try - { - cl::argv_file_scanner scan (argc, argv, "--options-file"); - - 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) - { - // If we see first "--", then we are done parsing options. - // - if (strcmp (scan.peek (), "--") == 0) - { - scan.next (); - opt = false; - continue; - } - - // Parse the next chunk of options until we reach an argument (or - // eos). - // - if (ops.parse (scan)) - 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 << "'"; - - 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 (); - - 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) - args += '\n'; - - args += s; - - // See if we are using the shortcut syntax. - // - if (argn == 0 && args.back () == ':') - { - args.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) - args.pop_back (); - else - args += ')'; - } - - // Get/set an environment variable tracing the operation. - // - auto get_env = [&verbosity, &trace] (const char* nm) - { - optional<string> r (getenv (nm)); - - if (verbosity () >= 5) - { - if (r) - trace << nm << ": '" << *r << "'"; - else - trace << nm << ": <NULL>"; - } - - 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<string> env_ovr (get_env ("BUILD2_VAR_OVR")); - if (env_ovr) - { - path_name fn ("<BUILD2_VAR_OVR>"); - - auto i (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 = 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<string> 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<dir_path> extra; - if (ops.default_options_specified ()) - extra = ops.default_options (); - - // Load default options files. - // - default_options<options> def_ops ( - load_default_options<options, - cl::argv_file_scanner, - cl::unknown_mode> ( - 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", - 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) - cmd_vars = - merge_default_arguments ( - def_ops, - cmd_vars, - [&verify_glb_ovr] (const default_options_entry<options>& 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 pair<path, system_error>& 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 (!cmd_vars.empty ()) - { - string ovr; - for (const string& v: 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 cl::exception& e) - { - fail << e; - } + b_cmdline cmdl (parse_b_cmdline (trace, argc, argv, ops)); // Handle --build2-metadata (see also buildfile). // @@ -639,10 +313,10 @@ main (int argc, char* argv[]) // Initialize the diagnostics state. // - init_diag (verbosity (), + init_diag (cmdl.verbosity, ops.silent (), - (ops.progress () ? optional<bool> (true) : - ops.no_progress () ? optional<bool> (false) : nullopt), + cmdl.progress, + cmdl.diag_color, ops.no_line (), ops.no_column (), fdterm (stderr_fd ())); @@ -673,36 +347,14 @@ main (int argc, char* argv[]) } } - // Initialize time conversion data that is used by localtime_r(). - // -#ifndef _WIN32 - tzset (); -#else - _tzset (); -#endif - // Initialize the global state. // init (&::terminate, argv[0], - (ops.mtime_check () ? optional<bool> (true) : - ops.no_mtime_check () ? optional<bool> (false) : nullopt), - (ops.config_sub_specified () - ? optional<path> (ops.config_sub ()) - : nullopt), - (ops.config_guess_specified () - ? optional<path> (ops.config_guess ()) - : nullopt)); - -#ifdef _WIN32 - // On Windows disable displaying error reporting dialog box for the - // current and child processes unless we are in the stop mode. Failed that - // we may have multiple dialog boxes popping up. - // - if (!ops.serial_stop ()) - SetErrorMode (SetErrorMode (0) | // Returns the current mode. - SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); -#endif + ops.serial_stop (), + cmdl.mtime_check, + cmdl.config_sub, + cmdl.config_guess); // Load builtin modules. // @@ -719,64 +371,20 @@ main (int argc, char* argv[]) load_builtin_module (&in::build2_in_load); #ifndef BUILD2_BOOTSTRAP - load_builtin_module (&cli::build2_cli_load); load_builtin_module (&bash::build2_bash_load); + load_builtin_module (&cli::build2_cli_load); #endif // Start up the scheduler and allocate lock shards. // - size_t jobs (0); - - if (ops.jobs_specified ()) - jobs = ops.jobs (); - else if (ops.serial_stop ()) - jobs = 1; - - if (jobs == 0) - jobs = scheduler::hardware_concurrency (); - - if (jobs == 0) - { - warn << "unable to determine the number of hardware threads" << - info << "falling back to serial execution" << - info << "use --jobs|-j to override"; - - jobs = 1; - } - - size_t max_jobs (0); - - if (ops.max_jobs_specified ()) - { - max_jobs = ops.max_jobs (); - - if (max_jobs != 0 && max_jobs < jobs) - fail << "invalid --max-jobs|-J value"; - } - - sched.startup (jobs, - 1, - max_jobs, - jobs * ops.queue_depth (), - (ops.max_stack_specified () - ? optional<size_t> (ops.max_stack () * 1024) - : nullopt)); + sched.startup (cmdl.jobs, + 1 /* init_active */, + cmdl.max_jobs, + cmdl.jobs * ops.queue_depth (), + cmdl.max_stack); global_mutexes mutexes (sched.shard_size ()); - - bool fcache_comp (true); - if (ops.file_cache_specified ()) - { - const string& v (ops.file_cache ()); - if (v == "noop" || v == "none") - fcache_comp = false; - else if (v == "sync-lz4") - fcache_comp = true; - else - fail << "invalid --file-cache value '" << v << "'"; - } - - file_cache fcache (fcache_comp); + file_cache fcache (cmdl.fcache_compress); // Trace some overall environment information. // @@ -788,25 +396,45 @@ main (int argc, char* argv[]) trace << "home: " << home; trace << "path: " << (p ? *p : "<NULL>"); trace << "type: " << (build_installed ? "installed" : "development"); - trace << "jobs: " << jobs; + trace << "jobs: " << cmdl.jobs; } // Set the build context before parsing the buildspec since it relies on // the global scope being setup. We reset it for every meta-operation (see // below). // - unique_ptr<context> ctx; - auto new_context = [&ctx, &sched, &mutexes, &fcache, &cmd_vars] + unique_ptr<context> pctx; + auto new_context = [&ops, &cmdl, + &sched, &mutexes, &fcache, + &phase_switch_contention, + &pctx] { - ctx = nullptr; // Free first. - ctx.reset (new context (sched, - mutexes, - fcache, - ops.match_only (), - ops.no_external_modules (), - ops.dry_run (), - !ops.serial_stop () /* keep_going */, - cmd_vars)); + if (pctx != nullptr) + { + phase_switch_contention += (pctx->phase_mutex.contention + + pctx->phase_mutex.contention_load); + pctx = nullptr; // Free first to reuse memory. + } + + optional<match_only_level> mo; + if (ops.load_only ()) mo = match_only_level::alias; + else if (ops.match_only ()) mo = match_only_level::all; + + pctx.reset (new context (sched, + mutexes, + fcache, + mo, + ops.no_external_modules (), + ops.dry_run (), + ops.no_diag_buffer (), + !ops.serial_stop () /* keep_going */, + cmdl.cmd_vars)); + + if (ops.trace_match_specified ()) + pctx->trace_match = &ops.trace_match (); + + if (ops.trace_execute_specified ()) + pctx->trace_execute = &ops.trace_execute (); }; new_context (); @@ -814,17 +442,18 @@ main (int argc, char* argv[]) // Parse the buildspec. // buildspec bspec; + path_name bspec_name ("<buildspec>"); try { - istringstream is (args); + istringstream is (cmdl.buildspec); is.exceptions (istringstream::failbit | istringstream::badbit); - parser p (*ctx); - bspec = p.parse_buildspec (is, path_name ("<buildspec>")); + parser p (*pctx); + bspec = p.parse_buildspec (is, bspec_name); } catch (const io_error&) { - fail << "unable to parse buildspec '" << args << "'"; + fail << "unable to parse buildspec '" << cmdl.buildspec << "'"; } l5 ([&]{trace << "buildspec: " << bspec;}); @@ -832,18 +461,194 @@ main (int argc, char* argv[]) if (bspec.empty ()) bspec.push_back (metaopspec ()); // Default meta-operation. + // The reserve values were picked experimentally. They allow building a + // sample application that depends on Qt and Boost without causing a + // rehash. + // + // Note: omit reserving anything for the info meta-operation since it + // won't be loading the buildfiles and needs to be as fast as possible. + // + bool mo_info (bspec.size () == 1 && + bspec.front ().size () == 1 && + (bspec.front ().name == "info" || + (bspec.front ().name.empty () && + bspec.front ().front ().name == "info"))); + + if (!mo_info) + { + // Note: also adjust in bpkg if adjusting here. + // + pctx->reserve (context::reserves { + 30000 /* targets */, + 1100 /* variables */}); + } + + bool load_only (ops.load_only ()); + const path& buildfile (ops.buildfile_specified () ? ops.buildfile () : empty_path); bool dump_load (false); bool dump_match (false); - if (ops.dump_specified ()) + bool dump_match_pre (false); + bool dump_match_post (false); + for (const string& p: ops.dump ()) { - dump_load = ops.dump ().find ("load") != ops.dump ().end (); - dump_match = ops.dump ().find ("match") != ops.dump ().end (); + if (p == "load") dump_load = true; + else if (p == "match") dump_match = true; + else if (p == "match-pre") dump_match_pre = true; + else if (p == "match-post") dump_match_post = true; + else fail << "unknown phase '" << p << "' specified with --dump"; } + dump_format dump_fmt (dump_format::buildfile); + if (ops.dump_format_specified ()) + { + const string& f (ops.dump_format ()); + + if (f == "json-v0.1") + { +#ifdef BUILD2_BOOTSTRAP + fail << "json dump not supported in bootstrap build system"; +#endif + dump_fmt = dump_format::json; + } + else if (f != "buildfile") + { + diag_record dr (fail); + + dr << "unsupported format '" << f << "' specified with --dump-format"; + + if (f.compare (0, 4, "json") == 0) + dr << info << "supported json format version is json-v0.1"; + } + } + + auto dump = [&trace, &ops, dump_fmt] (context& ctx, optional<action> a) + { + const dir_paths& scopes (ops.dump_scope ()); + const vector<pair<name, optional<name>>>& targets (ops.dump_target ()); + + if (scopes.empty () && targets.empty ()) + build2::dump (ctx, a, dump_fmt); + else + { + auto comp_norm = [] (dir_path& d, const char* what) + { + try + { + if (d.relative ()) + d.complete (); + + d.normalize (); + } + catch (const invalid_path& e) + { + fail << "invalid path '" << e.path << "' specified with " << what; + } + }; + + // If exact is false then return any outer scope that contains this + // directory except for the global scope. + // + auto find_scope = [&ctx, &comp_norm] (dir_path& d, + bool exact, + const char* what) -> const scope* + { + comp_norm (d, what); + + // This is always the output directory (specifically, see the target + // case below). + // + const scope& s (ctx.scopes.find_out (d)); + + return ((exact ? s.out_path () == d : s != ctx.global_scope) + ? &s + : nullptr); + }; + + // Dump scopes. + // + for (dir_path d: scopes) + { + const scope* s (find_scope (d, true, "--dump-scope")); + + if (s == nullptr) + l5 ([&]{trace << "unknown target scope " << d + << " specified with --dump-scope";}); + + build2::dump (s, a, dump_fmt); + } + + // Dump targets. + // + for (const pair<name, optional<name>>& p: targets) + { + const target* t (nullptr); + + // Find the innermost known scope that contains this target. This + // is where we are going to resolve its type. + // + dir_path d (p.second ? p.second->dir : p.first.dir); + + if (const scope* s = find_scope (d, false, "--dump-target")) + { + // Complete relative directories in names. + // + name n (p.first), o; + + if (p.second) + { + comp_norm (n.dir, "--dump-target"); + o.dir = move (d); + } + else + n.dir = move (d); + + // Similar logic to parser::enter_target::find_target() as used by + // the dump directive. Except here we treat unknown target type as + // unknown target. + // + auto r (s->find_target_type (n, location ())); + + if (r.first != nullptr) + { + t = ctx.targets.find (*r.first, // target type + n.dir, + o.dir, + n.value, + r.second, // extension + trace); + + if (t == nullptr) + l5 ([&] + { + // @@ TODO: default_extension? + // + target::combine_name (n.value, r.second, false); + names ns {move (n)}; + if (p.second) + ns.push_back (move (o)); + + trace << "unknown target " << ns + << " specified with --dump-target"; + }); + } + else + l5 ([&]{trace << "unknown target type '" << n.type << "' in " + << *s << " specified with --dump-target";}); + + } + else + l5 ([&]{trace << "unknown target scope " << d + << " specified with --dump-target";}); + + build2::dump (t, a, dump_fmt); + } + } + }; + // If not NULL, then lifted points to the operation that has been "lifted" // to the meta-operaion (see the logic below for details). Skip is the // position of the next operation. @@ -856,6 +661,20 @@ main (int argc, char* argv[]) // bool dirty (false); // Already (re)set for the first run. +#ifndef BUILD2_BOOTSTRAP + // Note that this constructor is cheap and so we rather call it always + // instead of resorting to dynamic allocations. + // + // Note also that we disable pretty-printing if there is also the JSON + // dump and thus we need to combine the two in the JSON Lines format. + // + json::stream_serializer js (cout, dump_fmt == dump_format::json ? 0 : 2); + + if (ops.structured_result_specified () && + ops.structured_result () == structured_result_format::json) + js.begin_array (); +#endif + for (auto mit (bspec.begin ()); mit != bspec.end (); ) { vector_view<opspec> opspecs; @@ -897,8 +716,9 @@ main (int argc, char* argv[]) dirty = false; } - const path p ("<buildspec>"); - const location l (p, 0, 0); //@@ TODO + context& ctx (*pctx); + + const location l (bspec_name, 0, 0); //@@ TODO (also bpkg::pkg_configure()) meta_operation_id mid (0); // Not yet translated. const meta_operation_info* mif (nullptr); @@ -911,25 +731,25 @@ main (int argc, char* argv[]) values& mparams (lifted == nullptr ? mit->params : lifted->params); string mname (lifted == nullptr ? mit->name : lifted->name); - ctx->current_mname = mname; // Set early. + ctx.current_mname = mname; // Set early. if (!mname.empty ()) { - if (meta_operation_id m = ctx->meta_operation_table.find (mname)) + if (meta_operation_id m = ctx.meta_operation_table.find (mname)) { // Can modify params, opspec, change meta-operation name. // - if (auto f = ctx->meta_operation_table[m].process) - mname = ctx->current_mname = f ( - *ctx, mparams, opspecs, lifted != nullptr, l); + if (auto f = ctx.meta_operation_table[m].process) + mname = ctx.current_mname = f ( + ctx, mparams, opspecs, lifted != nullptr, l); } } // Expose early so can be used during bootstrap (with the same // limitations as for pre-processing). // - scope& gs (ctx->global_scope.rw ()); - gs.assign (ctx->var_build_meta_operation) = mname; + scope& gs (ctx.global_scope.rw ()); + gs.assign (ctx.var_build_meta_operation) = mname; for (auto oit (opspecs.begin ()); oit != opspecs.end (); ++oit) { @@ -940,7 +760,7 @@ main (int argc, char* argv[]) const values& oparams (lifted == nullptr ? os.params : values ()); const string& oname (lifted == nullptr ? os.name : empty_string); - ctx->current_oname = oname; // Set early. + ctx.current_oname = oname; // Set early. if (lifted != nullptr) lifted = nullptr; // Clear for the next iteration. @@ -964,7 +784,7 @@ main (int argc, char* argv[]) &oname, &mname, &os, &mit, &lifted, &skip, &l, &trace] () { - meta_operation_id m (ctx->meta_operation_table.find (oname)); + meta_operation_id m (ctx.meta_operation_table.find (oname)); if (m != 0) { @@ -1036,12 +856,19 @@ main (int argc, char* argv[]) } } - if (out_base.relative ()) - out_base = work / out_base; + try + { + if (out_base.relative ()) + out_base = work / out_base; - // This directory came from the command line so actualize it. - // - out_base.normalize (true); + // This directory came from the command line so actualize it. + // + out_base.normalize (true); + } + catch (const invalid_path& e) + { + fail << "invalid out_base directory '" << e.path << "'"; + } // The order in which we determine the roots depends on whether // src_base was specified explicitly. @@ -1067,12 +894,19 @@ main (int argc, char* argv[]) if (!exists (src_base)) fail << "src_base directory " << src_base << " does not exist"; - if (src_base.relative ()) - src_base = work / src_base; + try + { + if (src_base.relative ()) + src_base = work / src_base; - // Also came from the command line, so actualize. - // - src_base.normalize (true); + // Also came from the command line, so actualize. + // + src_base.normalize (true); + } + catch (const invalid_path& e) + { + fail << "invalid src_base directory '" << e.path << "'"; + } // Make sure out_base is not a subdirectory of src_base. Who would // want to do that, you may ask. Well, you would be surprised... @@ -1123,7 +957,7 @@ main (int argc, char* argv[]) // Handle a forwarded configuration. Note that if we've changed // out_root then we also have to remap out_base. // - out_root = bootstrap_fwd (*ctx, src_root, altn); + out_root = bootstrap_fwd (ctx, src_root, altn); if (src_root != out_root) { out_base = out_root / out_base.leaf (src_root); @@ -1168,7 +1002,7 @@ main (int argc, char* argv[]) // use to the bootstrap files (other than src-root.build, which, // BTW, doesn't need to exist if src_root == out_root). // - scope& rs (*create_root (*ctx, out_root, src_root)->second.front ()); + scope& rs (*create_root (ctx, out_root, src_root)->second.front ()); bool bstrapped (bootstrapped (rs)); @@ -1204,8 +1038,8 @@ main (int argc, char* argv[]) << (forwarded ? "forwarded " : "specified ") << src_root; - ctx->new_src_root = src_root; - ctx->old_src_root = move (p); + ctx.new_src_root = src_root; + ctx.old_src_root = move (p); p = src_root; } } @@ -1227,8 +1061,13 @@ main (int argc, char* argv[]) // Now that we have src_root, load the src_root bootstrap file, // if there is one. // + // As an optimization, omit discovering subprojects for the info + // meta-operation if not needed. + // bootstrap_pre (rs, altn); - bootstrap_src (rs, altn); + bootstrap_src (rs, altn, + nullopt /* amalgamation */, + !mo_info || info_subprojects (mparams) /*subprojects*/); // If this is a simple project, then implicitly load the test and // install modules. @@ -1248,7 +1087,7 @@ main (int argc, char* argv[]) // command line and import). // if (forwarded) - rs.assign (ctx->var_forwarded) = true; + rs.assign (ctx.var_forwarded) = true; // Sync local variable that are used below with actual values. // @@ -1300,8 +1139,8 @@ main (int argc, char* argv[]) // all be known. We store the combined action id in uint8_t; // see <operation> for details. // - assert (ctx->operation_table.size () <= 128); - assert (ctx->meta_operation_table.size () <= 128); + assert (ctx.operation_table.size () <= 128); + assert (ctx.meta_operation_table.size () <= 128); // Since we now know all the names of meta-operations and // operations, "lift" names that we assumed (from buildspec syntax) @@ -1318,7 +1157,7 @@ main (int argc, char* argv[]) if (!mname.empty ()) { - m = ctx->meta_operation_table.find (mname); + m = ctx.meta_operation_table.find (mname); if (m == 0) fail (l) << "unknown meta-operation " << mname; @@ -1326,7 +1165,7 @@ main (int argc, char* argv[]) if (!oname.empty ()) { - o = ctx->operation_table.find (oname); + o = ctx.operation_table.find (oname); if (o == 0) fail (l) << "unknown operation " << oname; @@ -1349,7 +1188,7 @@ main (int argc, char* argv[]) if (mif == nullptr) fail (l) << "target " << tn << " does not support meta-" - << "operation " << ctx->meta_operation_table[m].name; + << "operation " << ctx.meta_operation_table[m].name; } // // Otherwise, check that all the targets in a meta-operation @@ -1362,7 +1201,7 @@ main (int argc, char* argv[]) if (mi == nullptr) fail (l) << "target " << tn << " does not support meta-" - << "operation " << ctx->meta_operation_table[mid].name; + << "operation " << ctx.meta_operation_table[mid].name; if (mi != mif) fail (l) << "different implementations of meta-operation " @@ -1385,12 +1224,12 @@ main (int argc, char* argv[]) << ", id " << static_cast<uint16_t> (mid);}); if (mif->meta_operation_pre != nullptr) - mif->meta_operation_pre (mparams, l); + mif->meta_operation_pre (ctx, mparams, l); else if (!mparams.empty ()) fail (l) << "unexpected parameters for meta-operation " << mif->name; - ctx->current_meta_operation (*mif); + ctx.current_meta_operation (*mif); dirty = true; } @@ -1406,7 +1245,7 @@ main (int argc, char* argv[]) if (r == nullptr) fail (l) << "target " << tn << " does not support " - << "operation " << ctx->operation_table[o]; + << "operation " << ctx.operation_table[o]; return r; }; @@ -1424,7 +1263,7 @@ main (int argc, char* argv[]) // Allow the meta-operation to translate the operation. // if (mif->operation_pre != nullptr) - oid = mif->operation_pre (mparams, oif->id); + oid = mif->operation_pre (ctx, mparams, oif->id); else // Otherwise translate default to update. oid = (oif->id == default_id ? update_id : oif->id); @@ -1445,24 +1284,38 @@ main (int argc, char* argv[]) if (oif->outer_id != 0) outer_oif = lookup (oif->outer_id); + if (!oparams.empty ()) + { + // Operation parameters belong to outer operation, if any. + // + auto* i (outer_oif != nullptr ? outer_oif : oif); + + if (i->operation_pre == nullptr) + fail (l) << "unexpected parameters for operation " << i->name; + } + // Handle pre/post operations. // - if (oif->pre != nullptr) + if (auto po = oif->pre_operation) { - if ((orig_pre_oid = oif->pre (oparams, mid, l)) != 0) + if ((orig_pre_oid = po ( + ctx, + outer_oif == nullptr ? oparams : values {}, + mid, + l)) != 0) { assert (orig_pre_oid != default_id); pre_oif = lookup (orig_pre_oid); pre_oid = pre_oif->id; // De-alias. } } - else if (!oparams.empty ()) - fail (l) << "unexpected parameters for operation " - << oif->name; - if (oif->post != nullptr) + if (auto po = oif->post_operation) { - if ((orig_post_oid = oif->post (oparams, mid)) != 0) + if ((orig_post_oid = po ( + ctx, + outer_oif == nullptr ? oparams : values {}, + mid)) != 0) { assert (orig_post_oid != default_id); post_oif = lookup (orig_post_oid); @@ -1483,7 +1336,7 @@ main (int argc, char* argv[]) if (r == nullptr) fail (l) << "target " << tn << " does not support " - << "operation " << ctx->operation_table[o]; + << "operation " << ctx.operation_table[o]; if (r != i) fail (l) << "different implementations of operation " @@ -1508,6 +1361,9 @@ main (int argc, char* argv[]) // defined there (common with non-intrusive project conversions // where everything is built from a single root buildfile). // + // Note: we use find_plausible_buildfile() and not find_buildfile() + // to look in outer directories. + // optional<path> bf ( find_buildfile (src_base, src_base, altn, buildfile)); @@ -1545,6 +1401,7 @@ main (int argc, char* argv[]) if (const dir_path* a = *rs.root_extra->amalgamation) { trace << " amalgamation: " << *a; + trace << " bundle scope: " << *rs.bundle_scope (); trace << " strong scope: " << *rs.strong_scope (); trace << " weak scope: " << *rs.weak_scope (); } @@ -1557,62 +1414,7 @@ main (int argc, char* argv[]) // boundaries (specifically, amalgamation) are only known after // bootstrap. // - // The mildly tricky part here is to distinguish the situation where - // we are bootstrapping the same project multiple times. The first - // override that we set cannot already exist (because the override - // variable names are unique) so if it is already set, then it can - // only mean this project is already bootstrapped. - // - // This is further complicated by the project vs amalgamation logic - // (we may have already done the amalgamation but not the project). - // So we split it into two passes. - // - { - auto& sm (ctx->scopes.rw ()); - - for (const variable_override& o: ctx->var_overrides) - { - if (o.ovr.visibility != variable_visibility::global) - continue; - - // If we have a directory, enter the scope, similar to how we do - // it in the context ctor. - // - scope& s ( - o.dir - ? *sm.insert_out ((out_base / *o.dir).normalize ())->second.front () - : *rs.weak_scope ()); - - auto p (s.vars.insert (o.ovr)); - - if (!p.second) - break; - - value& v (p.first); - v = o.val; - } - - for (const variable_override& o: ctx->var_overrides) - { - // Ours is either project (%foo) or scope (/foo). - // - if (o.ovr.visibility == variable_visibility::global) - continue; - - scope& s ( - o.dir - ? *sm.insert_out ((out_base / *o.dir).normalize ())->second.front () - : rs); - - auto p (s.vars.insert (o.ovr)); - - if (!p.second) - break; - - value& v (p.first); - v = o.val; - } - } + ctx.enter_project_overrides (rs, out_base, ctx.var_overrides); ts.root_scope = &rs; ts.out_base = move (out_base); @@ -1627,6 +1429,9 @@ main (int argc, char* argv[]) break; } + if (load_only && (mid != perform_id || oid != update_id)) + fail << "--load-only requires perform(update) action"; + // Now load the buildfiles and search the targets. // action_targets tgs; @@ -1647,7 +1452,7 @@ main (int argc, char* argv[]) // building before we know how to for all the targets in this // operation batch. // - const scope& bs (ctx->scopes.find_out (ts.out_base)); + const scope& bs (ctx.scopes.find_out (ts.out_base)); // Find the target type and extract the extension. // @@ -1658,6 +1463,9 @@ main (int argc, char* argv[]) if (tt == nullptr) fail (l) << "unknown target type " << tn.type; + if (load_only && !tt->is_a<alias> ()) + fail << "--load-only requires alias target"; + if (mif->search != nullptr) { // If the directory is relative, assume it is relative to work @@ -1665,10 +1473,17 @@ main (int argc, char* argv[]) // dir_path& d (tn.dir); - if (d.relative ()) - d = work / d; + try + { + if (d.relative ()) + d = work / d; - d.normalize (true); // Actualize since came from command line. + d.normalize (true); // Actualize since came from command line. + } + catch (const invalid_path& e) + { + fail << "invalid target directory '" << e.path << "'"; + } if (ts.forwarded) d = rs.out_path () / d.leaf (rs.src_path ()); // Remap. @@ -1688,8 +1503,10 @@ main (int argc, char* argv[]) } } // target - if (dump_load) - dump (*ctx); + // Delay until after match in the --load-only mode (see below). + // + if (dump_load && !load_only) + dump (ctx, nullopt /* action */); // Finally, match the rules and perform the operation. // @@ -1699,28 +1516,42 @@ main (int argc, char* argv[]) << ", id " << static_cast<uint16_t> (pre_oid);}); if (mif->operation_pre != nullptr) - mif->operation_pre (mparams, pre_oid); // Cannot be translated. + mif->operation_pre (ctx, mparams, pre_oid); // Can't be translated. + + ctx.current_operation (*pre_oif, oif); + + if (oif->operation_pre != nullptr) + oif->operation_pre (ctx, oparams, false /* inner */, l); - ctx->current_operation (*pre_oif, oif); + if (pre_oif->operation_pre != nullptr) + pre_oif->operation_pre (ctx, {}, true /* inner */, l); action a (mid, pre_oid, oid); { - result_printer p (tgs); - uint16_t diag (ops.structured_result () ? 0 : 1); +#ifndef BUILD2_BOOTSTRAP + result_printer p (ops, tgs, js); +#endif + uint16_t diag (ops.structured_result_specified () ? 0 : 1); if (mif->match != nullptr) mif->match (mparams, a, tgs, diag, true /* progress */); - if (dump_match) - dump (*ctx, a); + if (dump_match_pre) + dump (ctx, a); - if (mif->execute != nullptr && !ctx->match_only) + if (mif->execute != nullptr && !ctx.match_only) mif->execute (mparams, a, tgs, diag, true /* progress */); } + if (pre_oif->operation_post != nullptr) + pre_oif->operation_post (ctx, {}, true /* inner */); + + if (oif->operation_post != nullptr) + oif->operation_post (ctx, oparams, false /* inner */); + if (mif->operation_post != nullptr) - mif->operation_post (mparams, pre_oid); + mif->operation_post (ctx, mparams, pre_oid); l5 ([&]{trace << "end pre-operation batch " << pre_oif->name << ", id " << static_cast<uint16_t> (pre_oid);}); @@ -1728,24 +1559,43 @@ main (int argc, char* argv[]) tgs.reset (); } - ctx->current_operation (*oif, outer_oif); + ctx.current_operation (*oif, outer_oif); + + if (outer_oif != nullptr && outer_oif->operation_pre != nullptr) + outer_oif->operation_pre (ctx, oparams, false /* inner */, l); + + if (oif->operation_pre != nullptr) + oif->operation_pre (ctx, + outer_oif == nullptr ? oparams : values {}, + true /* inner */, + l); action a (mid, oid, oif->outer_id); { - result_printer p (tgs); - uint16_t diag (ops.structured_result () ? 0 : 2); +#ifndef BUILD2_BOOTSTRAP + result_printer p (ops, tgs, js); +#endif + uint16_t diag (ops.structured_result_specified () ? 0 : 2); if (mif->match != nullptr) mif->match (mparams, a, tgs, diag, true /* progress */); if (dump_match) - dump (*ctx, a); + dump (ctx, a); - if (mif->execute != nullptr && !ctx->match_only) + if (mif->execute != nullptr && !ctx.match_only) mif->execute (mparams, a, tgs, diag, true /* progress */); } + if (oif->operation_post != nullptr) + oif->operation_post (ctx, + outer_oif == nullptr ? oparams : values {}, + true /* inner */); + + if (outer_oif != nullptr && outer_oif->operation_post != nullptr) + outer_oif->operation_post (ctx, oparams, false /* inner */); + if (post_oid != 0) { tgs.reset (); @@ -1754,35 +1604,52 @@ main (int argc, char* argv[]) << ", id " << static_cast<uint16_t> (post_oid);}); if (mif->operation_pre != nullptr) - mif->operation_pre (mparams, post_oid); // Cannot be translated. + mif->operation_pre (ctx, mparams, post_oid); // Can't be translated. + + ctx.current_operation (*post_oif, oif); + + if (oif->operation_pre != nullptr) + oif->operation_pre (ctx, oparams, false /* inner */, l); - ctx->current_operation (*post_oif, oif); + if (post_oif->operation_pre != nullptr) + post_oif->operation_pre (ctx, {}, true /* inner */, l); action a (mid, post_oid, oid); { - result_printer p (tgs); - uint16_t diag (ops.structured_result () ? 0 : 1); +#ifndef BUILD2_BOOTSTRAP + result_printer p (ops, tgs, js); +#endif + uint16_t diag (ops.structured_result_specified () ? 0 : 1); if (mif->match != nullptr) mif->match (mparams, a, tgs, diag, true /* progress */); - if (dump_match) - dump (*ctx, a); + if (dump_match_post) + dump (ctx, a); - if (mif->execute != nullptr && !ctx->match_only) + if (mif->execute != nullptr && !ctx.match_only) mif->execute (mparams, a, tgs, diag, true /* progress */); } + if (post_oif->operation_post != nullptr) + post_oif->operation_post (ctx, {}, true /* inner */); + + if (oif->operation_post != nullptr) + oif->operation_post (ctx, oparams, false /* inner */); + if (mif->operation_post != nullptr) - mif->operation_post (mparams, post_oid); + mif->operation_post (ctx, mparams, post_oid); l5 ([&]{trace << "end post-operation batch " << post_oif->name << ", id " << static_cast<uint16_t> (post_oid);}); } + if (dump_load && load_only) + dump (ctx, nullopt /* action */); + if (mif->operation_post != nullptr) - mif->operation_post (mparams, oid); + mif->operation_post (ctx, mparams, oid); l5 ([&]{trace << "end operation batch " << oif->name << ", id " << static_cast<uint16_t> (oid);}); @@ -1791,7 +1658,7 @@ main (int argc, char* argv[]) if (mid != 0) { if (mif->meta_operation_post != nullptr) - mif->meta_operation_post (mparams); + mif->meta_operation_post (ctx, mparams); l5 ([&]{trace << "end meta-operation batch " << mif->name << ", id " << static_cast<uint16_t> (mid);}); @@ -1800,6 +1667,18 @@ main (int argc, char* argv[]) if (lifted == nullptr && skip == 0) ++mit; } // meta-operation + +#ifndef BUILD2_BOOTSTRAP + if (ops.structured_result_specified () && + ops.structured_result () == structured_result_format::json) + { + js.end_array (); + cout << endl; + } +#endif + + phase_switch_contention += (pctx->phase_mutex.contention + + pctx->phase_mutex.contention_load); } catch (const failed&) { @@ -1821,16 +1700,18 @@ main (int argc, char* argv[]) { text << '\n' << "build statistics:" << "\n\n" - << " thread_max_active " << st.thread_max_active << '\n' - << " thread_max_total " << st.thread_max_total << '\n' - << " thread_helpers " << st.thread_helpers << '\n' - << " thread_max_waiting " << st.thread_max_waiting << '\n' + << " thread_max_active " << st.thread_max_active << '\n' + << " thread_max_total " << st.thread_max_total << '\n' + << " thread_helpers " << st.thread_helpers << '\n' + << " thread_max_waiting " << st.thread_max_waiting << '\n' + << '\n' + << " task_queue_depth " << st.task_queue_depth << '\n' + << " task_queue_full " << st.task_queue_full << '\n' << '\n' - << " task_queue_depth " << st.task_queue_depth << '\n' - << " task_queue_full " << st.task_queue_full << '\n' + << " wait_queue_slots " << st.wait_queue_slots << '\n' + << " wait_queue_collisions " << st.wait_queue_collisions << '\n' << '\n' - << " wait_queue_slots " << st.wait_queue_slots << '\n' - << " wait_queue_collisions " << st.wait_queue_collisions << '\n'; + << " phase_switch_contention " << phase_switch_contention << '\n'; } return r; diff --git a/build2/buildfile b/build2/buildfile index 4d62fb5..0111ed2 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -5,16 +5,19 @@ # libs = $libbutl +# NOTE: don't forget to also update bpkg's buildfile if changing anything +# here. +# include ../libbuild2/ libs += ../libbuild2/lib{build2} -for m: bash bin c cc cxx in version +for m: bash bin c cc cli cxx in version { include ../libbuild2/$m/ libs += ../libbuild2/$m/lib{build2-$m} } -exe{b}: {hxx ixx txx cxx}{** -b-options} {hxx ixx cxx}{b-options} $libs +exe{b}: {hxx ixx txx cxx}{**} $libs # Target metadata, see also --build2-metadata in b.cxx. # @@ -42,6 +45,8 @@ copyright = $process.run_regex( \ obj{b}: cxx.poptions += -DBUILD2_COPYRIGHT=\"$copyright\" +# NOTE: remember to update bpkg buildfile if changing anything here. +# switch $cxx.target.class { case 'linux' @@ -68,36 +73,3 @@ switch $cxx.target.class : "-Wl,--stack,$stack_size") } } - -# Generated options parser. -# -if $cli.configured -{ - cli.cxx{b-options}: cli{b} - - cli.options += --std c++11 -I $src_root --include-with-brackets \ ---include-prefix build2 --guard-prefix BUILD2 \ ---cxx-prologue "#include <build2/types-parsers.hxx>" \ ---cli-namespace build2::cl --generate-file-scanner --keep-separator \ ---generate-parse --generate-merge --generate-specifier - - # Usage options. - # - cli.options += --suppress-undocumented --long-usage --ansi-color \ ---page-usage 'build2::print_$name$_' --option-length 21 - - cli.cxx{*}: - { - # Include the generated cli files into the distribution and don't remove - # them when cleaning in src (so that clean results in a state identical to - # distributed). - # - dist = true - clean = ($src_root != $out_root) - - # We keep the generated code in the repository so copy it back to src in - # case of a forwarded configuration. - # - backlink = overwrite - } -} diff --git a/build2/cli/init.cxx b/build2/cli/init.cxx deleted file mode 100644 index eadf32c..0000000 --- a/build2/cli/init.cxx +++ /dev/null @@ -1,291 +0,0 @@ -// file : build2/cli/init.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include <build2/cli/init.hxx> - -#include <libbuild2/file.hxx> -#include <libbuild2/scope.hxx> -#include <libbuild2/target.hxx> -#include <libbuild2/variable.hxx> -#include <libbuild2/diagnostics.hxx> - -#include <libbuild2/config/utility.hxx> - -#include <libbuild2/cxx/target.hxx> - -#include <build2/cli/rule.hxx> -#include <build2/cli/module.hxx> -#include <build2/cli/target.hxx> - -namespace build2 -{ - namespace cli - { - // Remaining issues/semantics change: - // - // @@ Unconfigured caching. - // - // @@ Default-found cli used to result in config.cli=cli and now it's just - // omitted (and default-not-found -- in config.cli.configured=false). - // - // - Writing any default will take precedence over config.import.cli. - // In fact, this duality is a bigger problem: if we have a config - // that uses config.cli there is no way to reconfigure it to use - // config.import.cli. - // - // - We could have saved it commented. - // - // - We could do this at the module level only since we also have - // config.cli.options? - // - // - Note that in the CLI compiler itself we now rely on default cli - // being NULL/undefined. So if faving, should probably be commented - // out. BUT: it will still be defined, so will need to be defined - // NULL. Note also that long term the CLI compiler will not use the - // module relying on an ad hoc recipe instead. - // - // ! Maybe reserving NULL (instead of making it the same as NULL) for - // this "configured to default" state and saving commented is not a - // bad idea. Feels right to have some marker in config.build that - // things are in effect. And I believe if config.import.cli is - // specified, it will just be dropped. - - bool - guess_init (scope& rs, - scope& bs, - const location& loc, - bool, - bool opt, - module_init_extra& extra) - { - tracer trace ("cli::guess_init"); - l5 ([&]{trace << "for " << rs;}); - - // We only support root loading (which means there can only be one). - // - if (rs != bs) - fail (loc) << "cli.guess module must be loaded in project root"; - - // Adjust module config.build save priority (code generator). - // - config::save_module (rs, "cli", 150); - - // Enter metadata variables. - // - auto& vp (rs.var_pool ()); - - auto& v_ver (vp.insert<string> ("cli.version")); - auto& v_sum (vp.insert<string> ("cli.checksum")); - - // Import the CLI compiler target. - // - // Note that the special config.cli=false value (recognized by the - // import machinery) is treated as an explicit request to leave the - // module unconfigured. - // - bool new_cfg (false); - pair<const exe*, import_kind> ir ( - import_direct<exe> ( - new_cfg, - rs, - name ("cli", dir_path (), "exe", "cli"), // cli%exe{cli} - true /* phase2 */, - opt, - true /* metadata */, - loc, - "module load")); - - const exe* tgt (ir.first); - - // Extract metadata. - // - auto* ver (tgt != nullptr ? &cast<string> (tgt->vars[v_ver]) : nullptr); - auto* sum (tgt != nullptr ? &cast<string> (tgt->vars[v_sum]) : nullptr); - - // Print the report. - // - // If this is a configuration with new values, then print the report - // at verbosity level 2 and up (-v). - // - if (verb >= (new_cfg ? 2 : 3)) - { - diag_record dr (text); - dr << "cli " << project (rs) << '@' << rs << '\n'; - - if (tgt != nullptr) - dr << " cli " << ir << '\n' - << " version " << *ver << '\n' - << " checksum " << *sum; - else - dr << " cli " << "not found, leaving unconfigured"; - } - - if (tgt == nullptr) - return false; - - // The cli variable (untyped) is an imported compiler target name. - // - rs.assign ("cli") = tgt->as_name (); - rs.assign (v_sum) = *sum; - rs.assign (v_ver) = *ver; - - { - standard_version v (*ver); - - rs.assign<uint64_t> ("cli.version.number") = v.version; - rs.assign<uint64_t> ("cli.version.major") = v.major (); - rs.assign<uint64_t> ("cli.version.minor") = v.minor (); - rs.assign<uint64_t> ("cli.version.patch") = v.patch (); - } - - // Cache some values in the module for easier access in the rule. - // - extra.set_module (new module (data {*tgt, *sum})); - - return true; - } - - bool - config_init (scope& rs, - scope& bs, - const location& loc, - bool, - bool opt, - module_init_extra& extra) - { - tracer trace ("cli::config_init"); - l5 ([&]{trace << "for " << rs;}); - - // We only support root loading (which means there can only be one). - // - if (rs != bs) - fail (loc) << "cli.config module must be loaded in project root"; - - // Load cli.guess and share its module instance as ours. - // - if (optional<shared_ptr<build2::module>> r = load_module ( - rs, rs, "cli.guess", loc, opt, extra.hints)) - { - extra.module = *r; - } - else - { - // This can happen if someone already optionally loaded cli.guess - // and it has failed to configure. - // - if (!opt) - fail (loc) << "cli could not be configured" << - info << "re-run with -V for more information"; - - return false; - } - - // Configuration. - // - using config::append_config; - - // config.cli.options - // - // Note that we merge it into the corresponding cli.* variable. - // - append_config<strings> (rs, rs, "cli.options", nullptr); - - return true; - } - - bool - init (scope& rs, - scope& bs, - const location& loc, - bool, - bool opt, - module_init_extra& extra) - { - tracer trace ("cli::init"); - l5 ([&]{trace << "for " << rs;}); - - // We only support root loading (which means there can only be one). - // - if (rs != bs) - fail (loc) << "cli module must be loaded in project root"; - - // Make sure the cxx module has been loaded since we need its targets - // types (?xx{}). Note that we don't try to load it ourselves because of - // the non-trivial variable merging semantics. So it is better to let - // the user load cxx explicitly. @@ Not sure the reason still holds - // though it might still make sense to expect the user to load cxx. - // - if (!cast_false<bool> (rs["cxx.loaded"])) - fail (loc) << "cxx module must be loaded before cli"; - - // Load cli.config and get its module instance. - // - if (optional<shared_ptr<build2::module>> r = load_module ( - rs, rs, "cli.config", loc, opt, extra.hints)) - { - extra.module = *r; - } - else - { - // This can happen if someone already optionally loaded cli.config - // and it has failed to configure. - // - if (!opt) - fail (loc) << "cli could not be configured" << - info << "re-run with -V for more information"; - - return false; - } - - auto& m (extra.module_as<module> ()); - - // Register target types. - // - rs.insert_target_type<cli> (); - rs.insert_target_type<cli_cxx> (); - - // Register our rules. - // - { - auto reg = [&rs, &m] (meta_operation_id mid, operation_id oid) - { - rs.insert_rule<cli_cxx> (mid, oid, "cli.compile", m); - rs.insert_rule<cxx::hxx> (mid, oid, "cli.compile", m); - rs.insert_rule<cxx::cxx> (mid, oid, "cli.compile", m); - rs.insert_rule<cxx::ixx> (mid, oid, "cli.compile", m); - }; - - reg (perform_id, update_id); - reg (perform_id, clean_id); - - // Other rules (e.g., cc::compile) may need to have the group members - // resolved/linked up. Looks like a general pattern: groups should - // resolve on *(update). - // - // @@ meta-op wildcard? - // - reg (configure_id, update_id); - reg (dist_id, update_id); - } - - return true; - } - - static const module_functions mod_functions[] = - { - // NOTE: don't forget to also update the documentation in init.hxx if - // changing anything here. - - {"cli.guess", nullptr, guess_init}, - {"cli.config", nullptr, config_init}, - {"cli", nullptr, init}, - {nullptr, nullptr, nullptr} - }; - - const module_functions* - build2_cli_load () - { - return mod_functions; - } - } -} diff --git a/build2/cli/init.hxx b/build2/cli/init.hxx deleted file mode 100644 index 1c54316..0000000 --- a/build2/cli/init.hxx +++ /dev/null @@ -1,29 +0,0 @@ -// file : build2/cli/init.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CLI_INIT_HXX -#define BUILD2_CLI_INIT_HXX - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -#include <libbuild2/module.hxx> - -namespace build2 -{ - namespace cli - { - // Module `cli` does not require bootstrapping. - // - // Submodules: - // - // `cli.guess` -- set variables describing the compiler. - // `cli.config` -- load `cli.guess` and set the rest of the variables. - // `cli` -- load `cli.config` and register targets and rules. - // - extern "C" const module_functions* - build2_cli_load (); - } -} - -#endif // BUILD2_CLI_INIT_HXX diff --git a/build2/cli/module.hxx b/build2/cli/module.hxx deleted file mode 100644 index 70f6ba8..0000000 --- a/build2/cli/module.hxx +++ /dev/null @@ -1,30 +0,0 @@ -// file : build2/cli/module.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CLI_MODULE_HXX -#define BUILD2_CLI_MODULE_HXX - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -#include <libbuild2/module.hxx> - -#include <build2/cli/rule.hxx> - -namespace build2 -{ - namespace cli - { - class module: public build2::module, - public virtual data, - public compile_rule - { - public: - explicit - module (data&& d) - : data (move (d)), compile_rule (move (d)) {} - }; - } -} - -#endif // BUILD2_CLI_MODULE_HXX diff --git a/build2/cli/rule.cxx b/build2/cli/rule.cxx deleted file mode 100644 index 99b6bee..0000000 --- a/build2/cli/rule.cxx +++ /dev/null @@ -1,336 +0,0 @@ -// file : build2/cli/rule.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include <build2/cli/rule.hxx> - -#include <libbuild2/depdb.hxx> -#include <libbuild2/scope.hxx> -#include <libbuild2/target.hxx> -#include <libbuild2/context.hxx> -#include <libbuild2/algorithm.hxx> -#include <libbuild2/filesystem.hxx> -#include <libbuild2/diagnostics.hxx> - -#include <build2/cli/target.hxx> - -namespace build2 -{ - namespace cli - { - // Figure out if name contains stem and, optionally, calculate prefix and - // suffix. - // - static bool - match_stem (const string& name, const string& stem, - string* prefix = nullptr, string* suffix = nullptr) - { - size_t p (name.find (stem)); - - if (p != string::npos) - { - if (prefix != nullptr) - prefix->assign (name, 0, p); - - if (suffix != nullptr) - suffix->assign (name, p + stem.size (), string::npos); - - return true; - } - - return false; - } - - bool compile_rule:: - match (action a, target& t, const string&) const - { - tracer trace ("cli::compile_rule::match"); - - // Find the .cli source file. - // - auto find = [&trace, a, &t] (auto&& r) -> optional<prerequisite_member> - { - for (prerequisite_member p: r) - { - // If excluded or ad hoc, then don't factor it into our tests. - // - if (include (a, t, p) != include_type::normal) - continue; - - if (p.is_a<cli> ()) - { - // Check that the stem match. - // - if (match_stem (t.name, p.name ())) - return p; - - l4 ([&]{trace << ".cli file stem '" << p.name () << "' " - << "doesn't match target " << t;}); - } - } - - return nullopt; - }; - - if (cli_cxx* pt = t.is_a<cli_cxx> ()) - { - // The cli.cxx{} group. - // - cli_cxx& t (*pt); - - // See if we have a .cli source file. - // - if (!find (group_prerequisite_members (a, t))) - { - l4 ([&]{trace << "no .cli source file for target " << t;}); - return false; - } - - // Figure out the member list. - // - // At this stage, no further changes to cli.options are possible and - // we can determine whether the --suppress-inline option is present. - // - // Passing the group as a "reference target" is a bit iffy, - // conceptually. - // - t.h = &search<cxx::hxx> (t, t.dir, t.out, t.name); - t.c = &search<cxx::cxx> (t, t.dir, t.out, t.name); - t.i = find_option ("--suppress-inline", t, "cli.options") - ? nullptr - : &search<cxx::ixx> (t, t.dir, t.out, t.name); - - return true; - } - else - { - // One of the ?xx{} members. - // - - // Check if there is a corresponding cli.cxx{} group. - // - const cli_cxx* g (t.ctx.targets.find<cli_cxx> (t.dir, t.out, t.name)); - - // If not or if it has no prerequisites (happens when we use it to - // set cli.options) and this target has a cli{} prerequisite, then - // synthesize the dependency. - // - if (g == nullptr || !g->has_prerequisites ()) - { - if (optional<prerequisite_member> p = find ( - prerequisite_members (a, t))) - { - if (g == nullptr) - g = &t.ctx.targets.insert<cli_cxx> (t.dir, t.out, t.name, trace); - - g->prerequisites (prerequisites {p->as_prerequisite ()}); - } - } - - if (g == nullptr) - return false; - - // For ixx{}, verify it is part of the group (i.e., not disabled - // via --suppress-inline). - // - if (t.is_a<cxx::ixx> () && - find_option ("--suppress-inline", *g, "cli.options")) - return false; - - t.group = g; - return true; - } - } - - recipe compile_rule:: - apply (action a, target& xt) const - { - if (cli_cxx* pt = xt.is_a<cli_cxx> ()) - { - cli_cxx& t (*pt); - - // Derive file names for the members. - // - t.h->derive_path (); - t.c->derive_path (); - if (t.i != nullptr) - t.i->derive_path (); - - // Inject dependency on the output directory. - // - inject_fsdir (a, t); - - // Match prerequisites. - // - match_prerequisite_members (a, t); - - // For update inject dependency on the CLI compiler target. - // - if (a == perform_update_id) - inject (a, t, ctgt); - - switch (a) - { - case perform_update_id: return [this] (action a, const target& t) - { - return perform_update (a, t); - }; - case perform_clean_id: return &perform_clean_group_depdb; - default: return noop_recipe; // Configure/dist update. - } - } - else - { - const cli_cxx& g (xt.group->as<cli_cxx> ()); - build2::match (a, g); - return group_recipe; // Execute the group's recipe. - } - } - - static void - append_extension (cstrings& args, - const path_target& t, - const char* option, - const char* default_extension) - { - const string* e (t.ext ()); - assert (e != nullptr); // Should have been figured out in apply(). - - if (*e != default_extension) - { - // CLI needs the extension with the leading dot (unless it is empty) - // while we store the extension without. But if there is an extension, - // then we can get it (with the dot) from the file name. - // - args.push_back (option); - args.push_back (e->empty () - ? e->c_str () - : t.path ().extension_cstring () - 1); - } - } - - target_state compile_rule:: - perform_update (action a, const target& xt) const - { - tracer trace ("cli::compile_rule::perform_update"); - - // The rule has been matched which means the members should be resolved - // and paths assigned. We use the header file as our "target path" for - // timestamp, depdb, etc. - // - const cli_cxx& t (xt.as<cli_cxx> ()); - const path& tp (t.h->path ()); - - // Update prerequisites and determine if any relevant ones render us - // out-of-date. Note that currently we treat all the prerequisites as - // potentially affecting the result (think prologues/epilogues, CLI - // compiler target itself, etc). - // - timestamp mt (t.load_mtime (tp)); - auto pr (execute_prerequisites<cli> (a, t, mt)); - - bool update (!pr.first); - target_state ts (update ? target_state::changed : *pr.first); - - const cli& s (pr.second); - - // We use depdb to track changes to the .cli file name, options, - // compiler, etc. - // - depdb dd (tp + ".d"); - { - // First should come the rule name/version. - // - if (dd.expect ("cli.compile 1") != nullptr) - l4 ([&]{trace << "rule mismatch forcing update of " << t;}); - - // Then the compiler checksum. - // - if (dd.expect (csum) != nullptr) - l4 ([&]{trace << "compiler mismatch forcing update of " << t;}); - - // Then the options checksum. - // - sha256 cs; - append_options (cs, t, "cli.options"); - - if (dd.expect (cs.string ()) != nullptr) - l4 ([&]{trace << "options mismatch forcing update of " << t;}); - - // Finally the .cli input file. - // - if (dd.expect (s.path ()) != nullptr) - l4 ([&]{trace << "input file mismatch forcing update of " << t;}); - } - - // Update if depdb mismatch. - // - if (dd.writing () || dd.mtime > mt) - update = true; - - dd.close (); - - // If nothing changed, then we are done. - // - if (!update) - return ts; - - // Translate paths to relative (to working directory). This results in - // easier to read diagnostics. - // - path relo (relative (t.dir)); - path rels (relative (s.path ())); - - const process_path& pp (ctgt.process_path ()); - cstrings args {pp.recall_string ()}; - - // See if we need to pass --output-{prefix,suffix} - // - string prefix, suffix; - match_stem (t.name, s.name, &prefix, &suffix); - - if (!prefix.empty ()) - { - args.push_back ("--output-prefix"); - args.push_back (prefix.c_str ()); - } - - if (!suffix.empty ()) - { - args.push_back ("--output-suffix"); - args.push_back (suffix.c_str ()); - } - - // See if we need to pass any --?xx-suffix options. - // - append_extension (args, *t.h, "--hxx-suffix", "hxx"); - append_extension (args, *t.c, "--cxx-suffix", "cxx"); - if (t.i != nullptr) - append_extension (args, *t.i, "--ixx-suffix", "ixx"); - - append_options (args, t, "cli.options"); - - if (!relo.empty ()) - { - args.push_back ("-o"); - args.push_back (relo.string ().c_str ()); - } - - args.push_back (rels.string ().c_str ()); - args.push_back (nullptr); - - if (verb >= 2) - print_process (args); - else if (verb) - text << "cli " << s; - - if (!t.ctx.dry_run) - { - run (pp, args); - dd.check_mtime (tp); - } - - t.mtime (system_clock::now ()); - return target_state::changed; - } - } -} diff --git a/build2/cli/rule.hxx b/build2/cli/rule.hxx deleted file mode 100644 index b3ecc2c..0000000 --- a/build2/cli/rule.hxx +++ /dev/null @@ -1,43 +0,0 @@ -// file : build2/cli/rule.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CLI_RULE_HXX -#define BUILD2_CLI_RULE_HXX - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -#include <libbuild2/rule.hxx> - -namespace build2 -{ - namespace cli - { - // Cached data shared between rules and the module. - // - struct data - { - const exe& ctgt; // CLI compiler target. - const string& csum; // CLI compiler checksum. - }; - - // @@ Redo as two separate rules? - // - class compile_rule: public simple_rule, virtual data - { - public: - compile_rule (data&& d): data (move (d)) {} - - virtual bool - match (action, target&, const string&) const override; - - virtual recipe - apply (action, target&) const override; - - target_state - perform_update (action, const target&) const; - }; - } -} - -#endif // BUILD2_CLI_RULE_HXX diff --git a/build2/cli/target.cxx b/build2/cli/target.cxx deleted file mode 100644 index ca16044..0000000 --- a/build2/cli/target.cxx +++ /dev/null @@ -1,75 +0,0 @@ -// file : build2/cli/target.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include <build2/cli/target.hxx> - -#include <libbuild2/context.hxx> - -namespace build2 -{ - namespace cli - { - // cli - // - extern const char cli_ext_def[] = "cli"; - - const target_type cli::static_type - { - "cli", - &file::static_type, - &target_factory<cli>, - nullptr, /* fixed_extension */ - &target_extension_var<cli_ext_def>, - &target_pattern_var<cli_ext_def>, - nullptr, - &file_search, - false - }; - - // cli.cxx - // - group_view cli_cxx:: - group_members (action) const - { - static_assert (sizeof (cli_cxx_members) == sizeof (const target*) * 3, - "member layout incompatible with array"); - - return h != nullptr - ? group_view {reinterpret_cast<const target* const*> (&h), - (i != nullptr ? 3U : 2U)} - : group_view {nullptr, 0}; - } - - static target* - cli_cxx_factory (context& ctx, - const target_type&, dir_path d, dir_path o, string n) - { - tracer trace ("cli::cli_cxx_factory"); - - // Pre-enter (potential) members as targets. The main purpose of doing - // this is to avoid searching for existing files in src_base if the - // buildfile mentions some of them explicitly as prerequisites. - // - // Also required for the src-out remapping logic. - // - ctx.targets.insert<cxx::hxx> (d, o, n, trace); - ctx.targets.insert<cxx::cxx> (d, o, n, trace); - ctx.targets.insert<cxx::ixx> (d, o, n, trace); - - return new cli_cxx (ctx, move (d), move (o), move (n)); - } - - const target_type cli_cxx::static_type - { - "cli.cxx", - &mtime_target::static_type, - &cli_cxx_factory, - nullptr, - nullptr, - nullptr, - nullptr, - &target_search, - true // "See through" default iteration mode. - }; - } -} diff --git a/build2/cli/target.hxx b/build2/cli/target.hxx deleted file mode 100644 index 722bb5f..0000000 --- a/build2/cli/target.hxx +++ /dev/null @@ -1,54 +0,0 @@ -// file : build2/cli/target.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_CLI_TARGET_HXX -#define BUILD2_CLI_TARGET_HXX - -#include <libbuild2/types.hxx> -#include <libbuild2/utility.hxx> - -#include <libbuild2/target.hxx> - -#include <libbuild2/cxx/target.hxx> - -namespace build2 -{ - namespace cli - { - class cli: public file - { - public: - using file::file; - - public: - static const target_type static_type; - virtual const target_type& dynamic_type () const {return static_type;} - }; - - // Standard layout type compatible with group_view's const target*[3]. - // - struct cli_cxx_members - { - const cxx::hxx* h = nullptr; - const cxx::cxx* c = nullptr; - const cxx::ixx* i = nullptr; - }; - - class cli_cxx: public mtime_target, public cli_cxx_members - { - public: - using mtime_target::mtime_target; - - virtual group_view - group_members (action) const override; - - public: - static const target_type static_type; - - virtual const target_type& - dynamic_type () const override {return static_type;} - }; - } -} - -#endif // BUILD2_CLI_TARGET_HXX diff --git a/build2/types-parsers.cxx b/build2/types-parsers.cxx deleted file mode 100644 index 3593143..0000000 --- a/build2/types-parsers.cxx +++ /dev/null @@ -1,50 +0,0 @@ -// file : build2/types-parsers.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include <build2/types-parsers.hxx> - -#include <build2/b-options.hxx> // build2::cl namespace - -namespace build2 -{ - namespace cl - { - template <typename T> - static void - parse_path (T& x, scanner& s) - { - const char* o (s.next ()); - - if (!s.more ()) - throw missing_value (o); - - const char* v (s.next ()); - - try - { - x = T (v); - - if (x.empty ()) - throw invalid_value (o, v); - } - catch (const invalid_path&) - { - throw invalid_value (o, v); - } - } - - void parser<path>:: - parse (path& x, bool& xs, scanner& s) - { - xs = true; - parse_path (x, s); - } - - void parser<dir_path>:: - parse (dir_path& x, bool& xs, scanner& s) - { - xs = true; - parse_path (x, s); - } - } -} diff --git a/build2/types-parsers.hxx b/build2/types-parsers.hxx deleted file mode 100644 index d39a096..0000000 --- a/build2/types-parsers.hxx +++ /dev/null @@ -1,43 +0,0 @@ -// file : build2/types-parsers.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -// CLI parsers, included into the generated source files. -// - -#ifndef BUILD2_TYPES_PARSERS_HXX -#define BUILD2_TYPES_PARSERS_HXX - -#include <libbuild2/types.hxx> - -namespace build2 -{ - namespace cl - { - class scanner; - - template <typename T> - struct parser; - - template <> - struct parser<path> - { - static void - parse (path&, bool&, scanner&); - - static void - merge (path& b, const path& a) {b = a;} - }; - - template <> - struct parser<dir_path> - { - static void - parse (dir_path&, bool&, scanner&); - - static void - merge (dir_path& b, const dir_path& a) {b = a;} - }; - } -} - -#endif // BUILD2_TYPES_PARSERS_HXX |