aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2021-11-11 13:20:30 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2021-11-16 14:37:44 +0200
commit546edb8e6a0b610c2db2a0bef878f28cd395bd77 (patch)
tree8d173f7e0f3971989cc3ad011aa40ceb75a25596
parent189a1c2a8fad0716e0bc4132e21f664c80d7574b (diff)
WIP: depdb dep infra
-rw-r--r--libbuild2/build/script/builtin-options.cxx697
-rw-r--r--libbuild2/build/script/builtin-options.hxx398
-rw-r--r--libbuild2/build/script/builtin-options.ixx248
-rw-r--r--libbuild2/build/script/builtin.cli25
-rw-r--r--libbuild2/build/script/parser.cxx261
-rw-r--r--libbuild2/build/script/parser.hxx15
-rw-r--r--libbuild2/buildfile76
-rw-r--r--libbuild2/script/parser.cxx2
8 files changed, 1629 insertions, 93 deletions
diff --git a/libbuild2/build/script/builtin-options.cxx b/libbuild2/build/script/builtin-options.cxx
new file mode 100644
index 0000000..dfb8e62
--- /dev/null
+++ b/libbuild2/build/script/builtin-options.cxx
@@ -0,0 +1,697 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <libbuild2/build/script/builtin-options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+
+namespace build2
+{
+ namespace build
+ {
+ namespace script
+ {
+ namespace cli
+ {
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ 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";
+ }
+
+ // 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_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // vector_scanner
+ //
+ bool vector_scanner::
+ more ()
+ {
+ return i_ < v_.size ();
+ }
+
+ const char* vector_scanner::
+ peek ()
+ {
+ if (i_ < v_.size ())
+ return v_[i_].c_str ();
+ else
+ throw eos_reached ();
+ }
+
+ const char* vector_scanner::
+ next ()
+ {
+ if (i_ < v_.size ())
+ return v_[i_++].c_str ();
+ else
+ throw eos_reached ();
+ }
+
+ void vector_scanner::
+ skip ()
+ {
+ if (i_ < v_.size ())
+ ++i_;
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t vector_scanner::
+ position ()
+ {
+ return start_position_ + i_;
+ }
+
+ 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;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, scanner& s)
+ {
+ s.next ();
+ x = 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;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ 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;
+ }
+ };
+
+ 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;
+ }
+ };
+
+ 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::size_t pos (s.position ());
+ 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, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ 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
+{
+ namespace build
+ {
+ namespace script
+ {
+ // depdb_dep_options
+ //
+
+ depdb_dep_options::
+ depdb_dep_options ()
+ : file_ (),
+ file_specified_ (false),
+ format_ (),
+ format_specified_ (false)
+ {
+ }
+
+ depdb_dep_options::
+ depdb_dep_options (int& argc,
+ char** argv,
+ bool erase,
+ ::build2::build::script::cli::unknown_mode opt,
+ ::build2::build::script::cli::unknown_mode arg)
+ : file_ (),
+ file_specified_ (false),
+ format_ (),
+ format_specified_ (false)
+ {
+ ::build2::build::script::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ depdb_dep_options::
+ depdb_dep_options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::build2::build::script::cli::unknown_mode opt,
+ ::build2::build::script::cli::unknown_mode arg)
+ : file_ (),
+ file_specified_ (false),
+ format_ (),
+ format_specified_ (false)
+ {
+ ::build2::build::script::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ }
+
+ depdb_dep_options::
+ depdb_dep_options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::build2::build::script::cli::unknown_mode opt,
+ ::build2::build::script::cli::unknown_mode arg)
+ : file_ (),
+ file_specified_ (false),
+ format_ (),
+ format_specified_ (false)
+ {
+ ::build2::build::script::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ depdb_dep_options::
+ depdb_dep_options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::build2::build::script::cli::unknown_mode opt,
+ ::build2::build::script::cli::unknown_mode arg)
+ : file_ (),
+ file_specified_ (false),
+ format_ (),
+ format_specified_ (false)
+ {
+ ::build2::build::script::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+ }
+
+ depdb_dep_options::
+ depdb_dep_options (::build2::build::script::cli::scanner& s,
+ ::build2::build::script::cli::unknown_mode opt,
+ ::build2::build::script::cli::unknown_mode arg)
+ : file_ (),
+ file_specified_ (false),
+ format_ (),
+ format_specified_ (false)
+ {
+ _parse (s, opt, arg);
+ }
+
+ typedef
+ std::map<std::string, void (*) (depdb_dep_options&, ::build2::build::script::cli::scanner&)>
+ _cli_depdb_dep_options_map;
+
+ static _cli_depdb_dep_options_map _cli_depdb_dep_options_map_;
+
+ struct _cli_depdb_dep_options_map_init
+ {
+ _cli_depdb_dep_options_map_init ()
+ {
+ _cli_depdb_dep_options_map_["--file"] =
+ &::build2::build::script::cli::thunk< depdb_dep_options, string, &depdb_dep_options::file_,
+ &depdb_dep_options::file_specified_ >;
+ _cli_depdb_dep_options_map_["--format"] =
+ &::build2::build::script::cli::thunk< depdb_dep_options, string, &depdb_dep_options::format_,
+ &depdb_dep_options::format_specified_ >;
+ }
+ };
+
+ static _cli_depdb_dep_options_map_init _cli_depdb_dep_options_map_init_;
+
+ bool depdb_dep_options::
+ _parse (const char* o, ::build2::build::script::cli::scanner& s)
+ {
+ _cli_depdb_dep_options_map::const_iterator i (_cli_depdb_dep_options_map_.find (o));
+
+ if (i != _cli_depdb_dep_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool depdb_dep_options::
+ _parse (::build2::build::script::cli::scanner& s,
+ ::build2::build::script::cli::unknown_mode opt_mode,
+ ::build2::build::script::cli::unknown_mode arg_mode)
+ {
+ // Can't skip combined flags (--no-combined-flags).
+ //
+ assert (opt_mode != ::build2::build::script::cli::unknown_mode::skip);
+
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ 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::build::script::cli::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::build::script::cli::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::build::script::cli::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::build::script::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::build2::build::script::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::build2::build::script::cli::unknown_mode::fail:
+ {
+ throw ::build2::build::script::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::build2::build::script::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::build2::build::script::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::build2::build::script::cli::unknown_mode::fail:
+ {
+ throw ::build2::build::script::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/libbuild2/build/script/builtin-options.hxx b/libbuild2/build/script/builtin-options.hxx
new file mode 100644
index 0000000..ccfd54b
--- /dev/null
+++ b/libbuild2/build/script/builtin-options.hxx
@@ -0,0 +1,398 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef LIBBUILD2_BUILD_SCRIPT_BUILTIN_OPTIONS_HXX
+#define LIBBUILD2_BUILD_SCRIPT_BUILTIN_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <vector>
+#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 build
+ {
+ namespace script
+ {
+ namespace cli
+ {
+ 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 ();
+ };
+
+ // 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().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class vector_scanner: public scanner
+ {
+ public:
+ vector_scanner (const std::vector<std::string>&,
+ std::size_t start = 0,
+ std::size_t start_position = 0);
+
+ std::size_t
+ end () const;
+
+ void
+ reset (std::size_t start = 0, std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ private:
+ std::size_t start_position_;
+ const std::vector<std::string>& v_;
+ std::size_t i_;
+ };
+
+ template <typename X>
+ struct parser;
+ }
+ }
+ }
+}
+
+#include <libbuild2/types.hxx>
+
+namespace build2
+{
+ namespace build
+ {
+ namespace script
+ {
+ class depdb_dep_options
+ {
+ public:
+ depdb_dep_options ();
+
+ depdb_dep_options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::build2::build::script::cli::unknown_mode option = ::build2::build::script::cli::unknown_mode::fail,
+ ::build2::build::script::cli::unknown_mode argument = ::build2::build::script::cli::unknown_mode::stop);
+
+ depdb_dep_options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::build2::build::script::cli::unknown_mode option = ::build2::build::script::cli::unknown_mode::fail,
+ ::build2::build::script::cli::unknown_mode argument = ::build2::build::script::cli::unknown_mode::stop);
+
+ depdb_dep_options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::build2::build::script::cli::unknown_mode option = ::build2::build::script::cli::unknown_mode::fail,
+ ::build2::build::script::cli::unknown_mode argument = ::build2::build::script::cli::unknown_mode::stop);
+
+ depdb_dep_options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::build2::build::script::cli::unknown_mode option = ::build2::build::script::cli::unknown_mode::fail,
+ ::build2::build::script::cli::unknown_mode argument = ::build2::build::script::cli::unknown_mode::stop);
+
+ depdb_dep_options (::build2::build::script::cli::scanner&,
+ ::build2::build::script::cli::unknown_mode option = ::build2::build::script::cli::unknown_mode::fail,
+ ::build2::build::script::cli::unknown_mode argument = ::build2::build::script::cli::unknown_mode::stop);
+
+ // Option accessors and modifiers.
+ //
+ const string&
+ file () const;
+
+ string&
+ file ();
+
+ void
+ file (const string&);
+
+ bool
+ file_specified () const;
+
+ void
+ file_specified (bool);
+
+ const string&
+ format () const;
+
+ string&
+ format ();
+
+ void
+ format (const string&);
+
+ bool
+ format_specified () const;
+
+ void
+ format_specified (bool);
+
+ // Implementation details.
+ //
+ protected:
+ bool
+ _parse (const char*, ::build2::build::script::cli::scanner&);
+
+ private:
+ bool
+ _parse (::build2::build::script::cli::scanner&,
+ ::build2::build::script::cli::unknown_mode option,
+ ::build2::build::script::cli::unknown_mode argument);
+
+ public:
+ string file_;
+ bool file_specified_;
+ string format_;
+ bool format_specified_;
+ };
+ }
+ }
+}
+
+#include <libbuild2/build/script/builtin-options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // LIBBUILD2_BUILD_SCRIPT_BUILTIN_OPTIONS_HXX
diff --git a/libbuild2/build/script/builtin-options.ixx b/libbuild2/build/script/builtin-options.ixx
new file mode 100644
index 0000000..3263803
--- /dev/null
+++ b/libbuild2/build/script/builtin-options.ixx
@@ -0,0 +1,248 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace build2
+{
+ namespace build
+ {
+ namespace script
+ {
+ namespace cli
+ {
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ 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_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // vector_scanner
+ //
+ inline vector_scanner::
+ vector_scanner (const std::vector<std::string>& v,
+ std::size_t i,
+ std::size_t sp)
+ : start_position_ (sp), v_ (v), i_ (i)
+ {
+ }
+
+ inline std::size_t vector_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ inline void vector_scanner::
+ reset (std::size_t i, std::size_t sp)
+ {
+ i_ = i;
+ start_position_ = sp;
+ }
+ }
+ }
+ }
+}
+
+namespace build2
+{
+ namespace build
+ {
+ namespace script
+ {
+ // depdb_dep_options
+ //
+
+ inline const string& depdb_dep_options::
+ file () const
+ {
+ return this->file_;
+ }
+
+ inline string& depdb_dep_options::
+ file ()
+ {
+ return this->file_;
+ }
+
+ inline void depdb_dep_options::
+ file (const string& x)
+ {
+ this->file_ = x;
+ }
+
+ inline bool depdb_dep_options::
+ file_specified () const
+ {
+ return this->file_specified_;
+ }
+
+ inline void depdb_dep_options::
+ file_specified (bool x)
+ {
+ this->file_specified_ = x;
+ }
+
+ inline const string& depdb_dep_options::
+ format () const
+ {
+ return this->format_;
+ }
+
+ inline string& depdb_dep_options::
+ format ()
+ {
+ return this->format_;
+ }
+
+ inline void depdb_dep_options::
+ format (const string& x)
+ {
+ this->format_ = x;
+ }
+
+ inline bool depdb_dep_options::
+ format_specified () const
+ {
+ return this->format_specified_;
+ }
+
+ inline void depdb_dep_options::
+ format_specified (bool x)
+ {
+ this->format_specified_ = x;
+ }
+ }
+ }
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/libbuild2/build/script/builtin.cli b/libbuild2/build/script/builtin.cli
new file mode 100644
index 0000000..6e66d67
--- /dev/null
+++ b/libbuild2/build/script/builtin.cli
@@ -0,0 +1,25 @@
+// file : libbuild2/build/script/builtin.cli
+// license : MIT; see accompanying LICENSE file
+
+include <libbuild2/types.hxx>;
+
+// Note that options in this file are undocumented because we generate neither
+// the usage printing code nor man pages. Instead, they are documented in the
+// manual.
+//
+namespace build2
+{
+ namespace build
+ {
+ namespace script
+ {
+ // Pseudo-builtin options.
+ //
+ class depdb_dep_options
+ {
+ string --file; // @@ TMP path
+ string --format;
+ };
+ }
+ }
+}
diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx
index 217fa11..4314b73 100644
--- a/libbuild2/build/script/parser.cxx
+++ b/libbuild2/build/script/parser.cxx
@@ -10,6 +10,7 @@
#include <libbuild2/build/script/lexer.hxx>
#include <libbuild2/build/script/runner.hxx>
+#include <libbuild2/build/script/builtin-options.hxx>
using namespace std;
using namespace butl;
@@ -487,7 +488,11 @@ namespace build2
next (t, tt);
if (tt != type::word ||
- (v != "clear" && v != "hash" && v != "string" && v != "env"))
+ (v != "clear" &&
+ v != "hash" &&
+ v != "string" &&
+ v != "env" &&
+ v != "dep"))
{
fail (get_location (t))
<< "expected 'depdb' builtin command instead of " << t;
@@ -533,6 +538,28 @@ namespace build2
}
else
{
+ // Verify depdb-dep is last.
+ //
+ if (v == "dep")
+ {
+ // Note that for now we do not allow multiple depdb-dep calls.
+ // But we may wan to relax this later (though alternating
+ // targets with prerequisites may be tricky -- maybe still
+ // only allow additional targets in the first call).
+ //
+ if (!depdb_dep_)
+ depdb_dep_ = l;
+ else
+ fail (l) << "multiple 'depdb dep' calls" <<
+ info (*depdb_dep_) << "previous call is here";
+ }
+ else
+ {
+ if (depdb_dep_)
+ fail (l) << "'depdb " << v << "' after 'depdb dep'" <<
+ info (*depdb_dep_) << "'depdb dep' call is here";
+ }
+
// Move the script body to the end of the depdb preamble.
//
// Note that at this (pre-parsing) stage we cannot evaluate if
@@ -917,75 +944,80 @@ namespace build2
{
if (tt == type::word && t.value == "depdb")
{
- names ns (exec_special (t, tt));
+ next (t, tt);
// This should have been enforced during pre-parsing.
//
- assert (!ns.empty ()); // <cmd> ... <newline>
+ assert (tt == type::word); // <cmd> ... <newline>
- const string& cmd (ns[0].value);
+ string cmd (move (t.value));
- if (cmd == "hash")
+ if (cmd == "dep")
{
- sha256 cs;
- for (auto i (ns.begin () + 1); i != ns.end (); ++i) // Skip <cmd>.
- to_checksum (cs, *i);
-
- if (ctx.dd.expect (cs.string ()) != nullptr)
- l4 ([&] {
- ctx.trace (ll)
- << "'depdb hash' argument change forcing update of "
- << ctx.env.target;});
+ exec_depdb_dep (t, tt, ll);
}
- else if (cmd == "string")
+ else
{
- string s;
- try
- {
- s = convert<string> (
- names (make_move_iterator (ns.begin () + 1),
- make_move_iterator (ns.end ())));
- }
- catch (const invalid_argument& e)
+ names ns (exec_special (t, tt, true /* skip <cmd> */));
+
+ if (cmd == "hash")
{
- fail (ll) << "invalid 'depdb string' argument: " << e;
+ sha256 cs;
+ for (const name& n: ns)
+ to_checksum (cs, n);
+
+ if (ctx.dd.expect (cs.string ()) != nullptr)
+ l4 ([&] {
+ ctx.trace (ll)
+ << "'depdb hash' argument change forcing update of "
+ << ctx.env.target;});
}
-
- if (ctx.dd.expect (s) != nullptr)
- l4 ([&] {
- ctx.trace (ll)
- << "'depdb string' argument change forcing update of "
- << ctx.env.target;});
- }
- else if (cmd == "env")
- {
- sha256 cs;
- const char* pf ("invalid 'depdb env' argument: ");
-
- try
+ else if (cmd == "string")
{
- // Skip <cmd>.
- //
- for (auto i (ns.begin () + 1); i != ns.end (); ++i)
+ string s;
+ try
{
- string vn (convert<string> (move (*i)));
- build2::script::verify_environment_var_name (vn, pf, ll);
- hash_environment (cs, vn);
+ s = convert<string> (move (ns));
}
+ catch (const invalid_argument& e)
+ {
+ fail (ll) << "invalid 'depdb string' argument: " << e;
+ }
+
+ if (ctx.dd.expect (s) != nullptr)
+ l4 ([&] {
+ ctx.trace (ll)
+ << "'depdb string' argument change forcing update of "
+ << ctx.env.target;});
}
- catch (const invalid_argument& e)
+ else if (cmd == "env")
{
- fail (ll) << pf << e;
- }
+ sha256 cs;
+ const char* pf ("invalid 'depdb env' argument: ");
- if (ctx.dd.expect (cs.string ()) != nullptr)
- l4 ([&] {
- ctx.trace (ll)
- << "'depdb env' environment change forcing update of "
- << ctx.env.target;});
+ try
+ {
+ for (name& n: ns)
+ {
+ string vn (convert<string> (move (n)));
+ build2::script::verify_environment_var_name (vn, pf, ll);
+ hash_environment (cs, vn);
+ }
+ }
+ catch (const invalid_argument& e)
+ {
+ fail (ll) << pf << e;
+ }
+
+ if (ctx.dd.expect (cs.string ()) != nullptr)
+ l4 ([&] {
+ ctx.trace (ll)
+ << "'depdb env' environment change forcing update of "
+ << ctx.env.target;});
+ }
+ else
+ assert (false);
}
- else
- assert (false);
}
else
{
@@ -1097,18 +1129,16 @@ namespace build2
}
names parser::
- exec_special (token& t, build2::script::token_type& tt,
- bool omit_builtin)
+ exec_special (token& t, build2::script::token_type& tt, bool skip_first)
{
- if (omit_builtin)
+ if (skip_first)
{
assert (tt != type::newline && tt != type::eos);
-
next (t, tt);
}
return tt != type::newline && tt != type::eos
- ? parse_names (t, tt, pattern_mode::expand)
+ ? parse_names (t, tt, pattern_mode::ignore)
: names ();
}
@@ -1134,6 +1164,121 @@ namespace build2
return r;
}
+ void parser::
+ exec_depdb_dep (token& t, build2::script::token_type& tt,
+ const location& ll)
+ {
+ // Similar approach to parse_env_builtin().
+ //
+ next (t, tt); // Skip 'dep' command.
+
+ // Note that an option name and value can belong to different name
+ // chunks. That's why we parse the arguments in the chunking mode into
+ // the list up to the `--` separator and parse this list into options
+ // afterwards. Note that the `--` separator should be omitted if there
+ // is no program (i.e., additional dependency info is being read from
+ // one of the prerequisites).
+ //
+ strings args;
+ bool prog (false);
+
+ names ns; // Reuse to reduce allocations.
+ while (tt != type::newline && tt != type::eos)
+ {
+ if (tt == type::word && t.value == "--")
+ {
+ prog = true;
+ break;
+ }
+
+ location l (get_location (t));
+
+ if (!start_names (tt))
+ fail (l) << "depdb dep: expected option or '--' separator "
+ << "instead of " << t;
+
+ parse_names (t, tt,
+ ns,
+ pattern_mode::ignore,
+ true /* chunk */,
+ "depdb dep builtin argument",
+ nullptr);
+
+ for (name& n: ns)
+ {
+ try
+ {
+ args.push_back (convert<string> (move (n)));
+ }
+ catch (const invalid_argument&)
+ {
+ diag_record dr (fail (l));
+ dr << "invalid string value ";
+ to_stream (dr.os, n, true /* quote */);
+ }
+ }
+
+ ns.clear ();
+ }
+
+ if (prog)
+ {
+ next (t, tt); // Skip '--'.
+
+ if (tt == type::newline || tt == type::eos)
+ fail (t) << "depdb dep: expected program name instead of " << t;
+ }
+
+ // Parse the options.
+ //
+ depdb_dep_options ops;
+ try
+ {
+ cli::vector_scanner scan (args);
+ ops = depdb_dep_options (scan);
+
+ if (scan.more ())
+ fail (ll) << "depdb dep: unexpected argument '" << scan.next ()
+ << "'";
+ }
+ catch (const cli::exception& e)
+ {
+ fail (ll) << "depdb dep: " << e;
+ }
+
+ optional<path> file;
+ if (ops.file_specified ())
+ {
+ file = path (ops.file ()); // @@ TMP path
+
+ // @@ TODO: file must be absolute.
+ }
+
+ if (prog)
+ {
+ // Run the remainder of the command line as a program (which can be
+ // a pipe). If file is absent, then redirect the command's stdout to
+ // a pipe. Otherwise, assume the command writes to file and add it
+ // to the clenups.
+ //
+ // Note that MSVC /showInclude sends its output to stderr (and so
+ // could do other broken tools). However, the user can always merge
+ // stderr to stdout (2>&1).
+ //
+ // @@ can we somehow get it as butl::process (but what if it's a
+ // builtin)?
+
+ // @@ TODO
+ }
+ else
+ {
+ // Assume file is one of the prerequisites.
+ //
+ if (!file)
+ fail (ll) << "depdb dep: program or --file expected";
+ }
+ }
+
// When add a special variable don't forget to update lexer::word().
//
bool parser::
diff --git a/libbuild2/build/script/parser.hxx b/libbuild2/build/script/parser.hxx
index b737a13..f3ddfba 100644
--- a/libbuild2/build/script/parser.hxx
+++ b/libbuild2/build/script/parser.hxx
@@ -119,8 +119,11 @@ namespace build2
exec_lines (const lines&, const function<exec_cmd_function>&);
names
- exec_special (token& t, build2::script::token_type& tt,
- bool omit_builtin = true);
+ exec_special (token&, build2::script::token_type&, bool skip_first);
+
+ void
+ exec_depdb_dep (token&, build2::script::token_type&,
+ const location&);
// Helpers.
//
@@ -219,8 +222,12 @@ namespace build2
// depdb env <var-names> - Track the environment variables change as a
// hash.
//
- optional<location> depdb_clear_; // 'depdb clear' location if any.
- lines depdb_preamble_; // Note: excludes 'depdb clear'.
+ // depdb dep ... - Extract additional dependency information.
+ // Can only be the last depdb builtin call.
+ //
+ optional<location> depdb_clear_; // depdb-clear location if any.
+ optional<location> depdb_dep_; // depdb-dep location if any.
+ lines depdb_preamble_; // Note: excludes depdb-clear.
// If present, the first impure function called in the body of the
// script that performs update of a file-based target.
diff --git a/libbuild2/buildfile b/libbuild2/buildfile
index 17003b5..51747fd 100644
--- a/libbuild2/buildfile
+++ b/libbuild2/buildfile
@@ -32,7 +32,8 @@ lib{build2}: libul{build2}: \
libul{build2}: script/{hxx ixx txx cxx}{** -*-options -**.test...} \
script/{hxx ixx cxx}{builtin-options}
-libul{build2}: build/{hxx ixx txx cxx}{** -**.test...}
+libul{build2}: build/script/{hxx ixx txx cxx}{** -*-options -**.test...} \
+ build/script/{hxx ixx cxx}{builtin-options}
# Note that this won't work in libul{} since it's not installed.
#
@@ -216,38 +217,53 @@ else
# Generated options parser.
#
-script/
+# @@ Consider generating common cli runtime namespace if adding more
+# option files.
+#
+if $cli.configured
{
- if $cli.configured
+ cli.options += --std c++11 -I $src_root --include-with-brackets \
+--generate-vector-scanner --generate-modifier --generate-specifier \
+--suppress-usage
+
+ cli.cxx{*}:
{
- cli.cxx{builtin-options}: cli{builtin}
-
- cli.options += --std c++11 -I $src_root --include-with-brackets \
---include-prefix libbuild2/script --guard-prefix LIBBUILD2_SCRIPT \
---cli-namespace build2::script::cli --generate-vector-scanner \
---generate-modifier --generate-specifier --suppress-usage
-
- 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). But don't install their headers since they are only
- # used internally in the testscript implementation.
- #
- dist = true
- clean = ($src_root != $out_root)
- install = false
-
- # We keep the generated code in the repository so copy it back to src in
- # case of a forwarded configuration.
- #
- backlink = overwrite
- }
- }
- else
- # No install for the pre-generated case.
+ # 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). But don't install their headers since they are only
+ # used internally in the testscript implementation.
+ #
+ dist = true
+ clean = ($src_root != $out_root)
+ install = false
+
+ # We keep the generated code in the repository so copy it back to src in
+ # case of a forwarded configuration.
#
- hxx{builtin-options}@./ ixx{builtin-options}@./: install = false
+ backlink = overwrite
+ }
+
+ script/cli.cxx{builtin-options}: script/cli{builtin}
+ {
+ cli.options += --cli-namespace build2::script::cli \
+--include-prefix libbuild2/script --guard-prefix LIBBUILD2_SCRIPT
+ }
+
+ build/script/cli.cxx{builtin-options}: build/script/cli{builtin}
+ {
+ cli.options += --cli-namespace build2::build::script::cli \
+--include-prefix libbuild2/build/script --guard-prefix LIBBUILD2_BUILD_SCRIPT
+ }
+}
+else
+{
+ # No install for the pre-generated case.
+ #
+ script/hxx{builtin-options}@./ \
+ script/ixx{builtin-options}@./: install = false
+
+ build/script/hxx{builtin-options}@./ \
+ build/script/ixx{builtin-options}@./: install = false
}
# Install into the libbuild2/ subdirectory of, say, /usr/include/
diff --git a/libbuild2/script/parser.cxx b/libbuild2/script/parser.cxx
index fffe7bb..7722002 100644
--- a/libbuild2/script/parser.cxx
+++ b/libbuild2/script/parser.cxx
@@ -1313,7 +1313,7 @@ namespace build2
// Note that an option name and value can belong to different name
// chunks. That's why we parse the env builtin arguments in the chunking
// mode into the argument/location pair list up to the '--' separator
- // and parse this list into the variable sets/unsets afterwords.
+ // and parse this list into the variable sets/unsets afterwards.
//
// Align the size with environment_vars (double because of -u <var>
// which is two arguments).