aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-03-07 15:22:51 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-03-07 15:22:51 +0200
commit1c36adab776a900adc7325f412b1c8dd61b1a346 (patch)
tree0227b8c5697d24a862aedd59b9bfe28cc84e91b4
parent2e2c3a81b47c650334f5767ddb4ebb2746ef98f1 (diff)
Setup compilation, command line handling
-rw-r--r--bdep/.gitignore3
-rw-r--r--bdep/bdep.cxx310
-rw-r--r--bdep/buildfile60
-rw-r--r--bdep/config.cli9
-rw-r--r--bdep/config.cxx27
-rw-r--r--bdep/config.hxx19
-rw-r--r--bdep/configuration.cli35
-rw-r--r--bdep/diagnostics.cxx77
-rw-r--r--bdep/diagnostics.hxx233
-rw-r--r--bdep/help.cxx59
-rw-r--r--bdep/help.hxx21
-rw-r--r--bdep/init.cli6
-rw-r--r--bdep/init.cxx25
-rw-r--r--bdep/init.hxx19
-rw-r--r--bdep/options-types.hxx12
-rw-r--r--bdep/project.cli23
-rw-r--r--bdep/types-parsers.cxx51
-rw-r--r--bdep/types-parsers.hxx39
-rw-r--r--bdep/types.hxx108
-rw-r--r--bdep/utility.cxx96
-rw-r--r--bdep/utility.hxx79
-rw-r--r--bdep/version.hxx.in48
-rw-r--r--build/.gitignore1
-rw-r--r--build/bootstrap.build11
-rw-r--r--build/export.build10
-rw-r--r--build/root.build22
-rw-r--r--buildfile18
-rw-r--r--doc/buildfile20
-rwxr-xr-xdoc/cli.sh4
-rw-r--r--manifest20
30 files changed, 1457 insertions, 8 deletions
diff --git a/bdep/.gitignore b/bdep/.gitignore
new file mode 100644
index 0000000..15d9eb3
--- /dev/null
+++ b/bdep/.gitignore
@@ -0,0 +1,3 @@
+bdep
+*-options.?xx
+version.hxx
diff --git a/bdep/bdep.cxx b/bdep/bdep.cxx
new file mode 100644
index 0000000..ca6ff64
--- /dev/null
+++ b/bdep/bdep.cxx
@@ -0,0 +1,310 @@
+// file : bdep/bdep.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef _WIN32
+# include <signal.h> // signal()
+#else
+# include <stdlib.h> // getenv(), _putenv()
+#endif
+
+#include <cstring> // strcmp()
+#include <iostream>
+
+#include <bdep/types.hxx>
+#include <bdep/utility.hxx>
+
+#include <bdep/diagnostics.hxx>
+#include <bdep/bdep-options.hxx>
+#include <bdep/config-options.hxx>
+
+// Commands.
+//
+#include <bdep/help.hxx>
+
+#include <bdep/config.hxx>
+#include <bdep/init.hxx>
+
+using namespace std;
+using namespace bdep;
+
+// Initialize the command option class O with the common options and then
+// parse the rest of the command line placing non-option arguments to args.
+// Once this is done, use the "final" values of the common options to do
+// global initializations (verbosity level, etc).
+//
+// If O is-a configuration_options, then also handle the @<cfg-name> arguments
+// and place them into configuration_options::config_name.
+//
+static inline bool
+cfg_name (configuration_options* o, const char* a)
+{
+ string n (a);
+
+ if (n.empty ())
+ fail << "empty configuration name";
+
+ o->config_name ().push_back (move (n));
+ return true;
+}
+
+static inline bool
+cfg_name (...)
+{
+ return false;
+}
+
+template <typename O>
+static O
+init (const common_options& co, cli::scanner& scan, strings& args)
+{
+ O o;
+ static_cast<common_options&> (o) = co;
+
+ // We want to be able to specify options and arguments in any order (it is
+ // really handy to just add -v at the end of the command line).
+ //
+ for (bool opt (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).
+ //
+ o.parse (scan);
+
+ if (!scan.more ())
+ break;
+
+ // @<cfg-name>
+ //
+ const char* a (scan.peek ());
+
+ if (*a == '@' && cfg_name (&o, a + 1))
+ {
+ scan.next ();
+ continue;
+ }
+
+ // Fall through.
+ }
+
+ args.push_back (scan.next ());
+ }
+
+ // Global initializations.
+ //
+
+ // Diagnostics verbosity.
+ //
+ verb = o.verbose_specified ()
+ ? o.verbose ()
+ : o.V () ? 3 : o.v () ? 2 : o.quiet () ? 0 : 1;
+
+ return o;
+}
+
+int
+main (int argc, char* argv[])
+try
+{
+ using namespace cli;
+
+ exec_dir = path (argv[0]).directory ();
+
+ // This is a little hack to make our 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 ("PATH=");
+ if (const char* p = getenv ("PATH"))
+ {
+ mp += p;
+ mp += ';';
+ }
+ mp += "/bin";
+
+ _putenv (mp.c_str ());
+ }
+#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
+
+ argv_file_scanner scan (argc, argv, "--options-file");
+
+ // First parse common options and --version/--help.
+ //
+ options o;
+ o.parse (scan, unknown_mode::stop);
+
+ if (o.version ())
+ {
+ cout << "bdep " << BDEP_VERSION_ID << endl
+ << "libbpkg " << LIBBPKG_VERSION_ID << endl
+ << "libbutl " << LIBBUTL_VERSION_ID << endl
+ << "Copyright (c) 2014-2017 Code Synthesis Ltd" << endl
+ << "This is free software released under the MIT license." << endl;
+ return 0;
+ }
+
+ strings argsv; // To be filled by parse() above.
+ vector_scanner args (argsv);
+
+ const common_options& co (o);
+
+ if (o.help ())
+ return help (init<help_options> (co, scan, argsv), "", nullptr);
+
+ // The next argument should be a command.
+ //
+ if (!scan.more ())
+ fail << "bdep command expected" <<
+ info << "run 'bdep help' for more information";
+
+ int cmd_argc (2);
+ char* cmd_argv[] {argv[0], const_cast<char*> (scan.next ())};
+ commands cmd;
+ cmd.parse (cmd_argc, cmd_argv, true, unknown_mode::stop);
+
+ if (cmd_argc != 1)
+ fail << "unknown bdep command/option '" << cmd_argv[1] << "'" <<
+ info << "run 'bdep help' for more information";
+
+ // If the command is 'help', then what's coming next is another command.
+ // Parse it into cmd so that we only need to check for each command in one
+ // place.
+ //
+ bool h (cmd.help ());
+ help_options ho;
+
+ if (h)
+ {
+ ho = init<help_options> (co, scan, argsv);
+
+ if (args.more ())
+ {
+ cmd_argc = 2;
+ cmd_argv[1] = const_cast<char*> (args.next ());
+
+ // First see if this is a command.
+ //
+ cmd = commands (); // Clear the help option.
+ cmd.parse (cmd_argc, cmd_argv, true, unknown_mode::stop);
+
+ // If not, then it got to be a help topic.
+ //
+ if (cmd_argc != 1)
+ return help (ho, cmd_argv[1], nullptr);
+ }
+ else
+ return help (ho, "", nullptr);
+ }
+
+ // Handle commands.
+ //
+ int r (1);
+ for (;;) // Breakout loop.
+ try
+ {
+ // help
+ //
+ if (cmd.help ())
+ {
+ assert (h);
+ r = help (ho, "help", print_bdep_help_usage);
+ break;
+ }
+
+ // Commands.
+ //
+ // if (cmd.new_ ())
+ // {
+ // if (h)
+ // r = help (ho, "new", print_bdep_cmd_new_usage);
+ // else
+ // r = cmd_new (init<cmd_new_options> (co, scan, argsv), args);
+ //
+ // break;
+ // }
+ //
+#define COMMAND_IMPL(ON, FN, SN) \
+ if (cmd.ON ()) \
+ { \
+ if (h) \
+ r = help (ho, SN, print_bdep_##FN##_usage); \
+ else \
+ r = cmd_##FN (init<cmd_##FN##_options> (co, scan, argsv), args); \
+ \
+ break; \
+ }
+
+ //COMMAND_IMPL (new_, new, "new");
+ COMMAND_IMPL (init, init, "init");
+ COMMAND_IMPL (config, config, "config");
+
+ assert (false);
+ fail << "unhandled command";
+ }
+ catch (const failed&)
+ {
+ r = 1;
+ break;
+ }
+
+ if (r != 0)
+ return r;
+
+ // Warn if args contain some leftover junk. We already successfully
+ // performed the command so failing would probably be misleading.
+ //
+ if (args.more ())
+ {
+ diag_record dr;
+ dr << warn << "ignoring unexpected argument(s)";
+ while (args.more ())
+ dr << " '" << args.next () << "'";
+ }
+
+ return 0;
+}
+catch (const failed&)
+{
+ return 1; // Diagnostics has already been issued.
+}
+catch (const cli::exception& e)
+{
+ error << e;
+ return 1;
+}
+/*
+catch (const std::exception& e)
+{
+ error << e;
+ return 1;
+}
+*/
diff --git a/bdep/buildfile b/bdep/buildfile
new file mode 100644
index 0000000..5ef9099
--- /dev/null
+++ b/bdep/buildfile
@@ -0,0 +1,60 @@
+# file : bdep/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs = libbpkg%lib{bpkg}
+import libs += libbutl%lib{butl}
+
+options_topics = \
+bdep-options \
+common-options \
+project-options \
+configuration-options \
+help-options \
+config-options \
+init-options
+
+exe{bdep}: {hxx ixx txx cxx}{** -{$options_topics} -version} \
+ {hxx ixx cxx}{$options_topics} {hxx}{version} $libs
+
+hxx{version}: in{version} $src_root/file{manifest}
+
+obj{utility}: cxx.poptions += -DBDEP_EXE_SUFFIX='"'$bin.exe.suffix'"'
+
+if $cli.configured
+{
+ # General topics and common options.
+ #
+ cli.cxx{common-options}: cli{common}
+ cli.cxx{project-options}: cli{project}
+ cli.cxx{configuration-options}: cli{configuration}
+ cli.cxx{bdep-options}: cli{bdep}
+
+ # Command.
+ #
+ cli.cxx{help-options}: cli{help}
+
+ cli.cxx{config-options}: cli{config}
+ cli.cxx{init-options}: cli{init}
+
+ # Option length must be the same to get commands/topics/options aligned.
+ #
+ cli.options += -I $src_root --include-with-brackets --include-prefix bdep \
+--guard-prefix BDEP --cxx-prologue "#include <bdep/types-parsers.hxx>" \
+--cli-namespace bdep::cli --generate-vector-scanner --generate-file-scanner \
+--generate-specifier --generate-modifier --generate-parse \
+--page-usage 'bdep::print_$name$_' --ansi-color --include-base-last \
+--suppress-undocumented --option-length 23
+
+ cli.cxx{common-options}: cli.options += --short-usage --long-usage # Both.
+ cli.cxx{bdep-options}: cli.options += --short-usage
+
+ cli.options += --long-usage # All other pages -- long usage.
+
+ # 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).
+ #
+ cli.cxx{*}: dist = true
+ cli.cxx{*}: clean = ($src_root != $out_root)
+}
diff --git a/bdep/config.cli b/bdep/config.cli
index 73d5444..42a0fe6 100644
--- a/bdep/config.cli
+++ b/bdep/config.cli
@@ -2,7 +2,7 @@
// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-include <bdep/common.cli>;
+include <bdep/configuration.cli>;
"\section=1"
"\name=bdep-config"
@@ -31,11 +31,14 @@ namespace bdep
The \cb{config} command..."
}
- class config_options: common_options
+ // Note that not all project/configuration options are valid for all
+ // subcommands.
+ //
+ class cmd_config_options: configuration_options
{
"\h|CONFIG OPTIONS|"
- bool --default|-d
+ bool --default
{
//@@ Need to explain what it means.
"Make the added or created configuration default."
diff --git a/bdep/config.cxx b/bdep/config.cxx
new file mode 100644
index 0000000..59dcbad
--- /dev/null
+++ b/bdep/config.cxx
@@ -0,0 +1,27 @@
+// file : bdep/config.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bdep/config.hxx>
+
+#include <bdep/diagnostics.hxx>
+
+using namespace std;
+
+namespace bdep
+{
+ int
+ cmd_config (const cmd_config_options& o, cli::scanner& args)
+ {
+ //@@ TODO: get subcommand and pass to tracer.
+
+ tracer trace ("config");
+
+ //@@ TODO: validate project/config options for subcommands.
+
+ for (const string& n: o.config_name ())
+ text << n;
+
+ return 0;
+ }
+}
diff --git a/bdep/config.hxx b/bdep/config.hxx
new file mode 100644
index 0000000..e4244e6
--- /dev/null
+++ b/bdep/config.hxx
@@ -0,0 +1,19 @@
+// file : bdep/config.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_CONFIG_HXX
+#define BDEP_CONFIG_HXX
+
+#include <bdep/types.hxx>
+#include <bdep/utility.hxx>
+
+#include <bdep/config-options.hxx>
+
+namespace bdep
+{
+ int
+ cmd_config (const cmd_config_options&, cli::scanner& args);
+}
+
+#endif // BDEP_CONFIG_HXX
diff --git a/bdep/configuration.cli b/bdep/configuration.cli
new file mode 100644
index 0000000..190845c
--- /dev/null
+++ b/bdep/configuration.cli
@@ -0,0 +1,35 @@
+// file : bdep/configuration.cli
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+include <bdep/project.cli>;
+
+"\name=configuration" // Not a man page.
+
+namespace bdep
+{
+ // Common options for commands that operate on configurations (cfg-spec).
+ // Note that all of them also operate on project/packages thus inheritance
+ // from project_options.
+ //
+ class configuration_options: project_options
+ {
+ dir_paths --config|-c
+ {
+ "<cfg-dir>",
+ "Specify the build configuration to use as a directory."
+ }
+
+ bool --all|-a
+ {
+ "Use all build configurations."
+ }
+
+ // Storage for configuration names specified as @<cfg-name>.
+ //
+ // Note that we leave it undocumented so that it's not mentioned in
+ // documentation.
+ //
+ strings --config-name;
+ };
+}
diff --git a/bdep/diagnostics.cxx b/bdep/diagnostics.cxx
new file mode 100644
index 0000000..e60ebc8
--- /dev/null
+++ b/bdep/diagnostics.cxx
@@ -0,0 +1,77 @@
+// file : bdep/diagnostics.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bdep/diagnostics.hxx>
+
+#include <libbutl/process.mxx>
+#include <libbutl/process-io.mxx> // operator<<(ostream, process_arg)
+
+using namespace std;
+using namespace butl;
+
+namespace bdep
+{
+ // print_process
+ //
+ void
+ print_process (const char* const args[], size_t n)
+ {
+ diag_record r (text);
+ print_process (r, args, n);
+ }
+
+ void
+ print_process (diag_record& r, const char* const args[], size_t n)
+ {
+ r << process_args {args, n};
+ }
+
+ // Diagnostics verbosity level.
+ //
+ uint16_t verb;
+
+ // Diagnostic facility, project specifics.
+ //
+
+ void simple_prologue_base::
+ operator() (const diag_record& r) const
+ {
+ if (type_ != nullptr)
+ r << type_ << ": ";
+
+ if (name_ != nullptr)
+ r << name_ << ": ";
+ }
+
+ void location_prologue_base::
+ operator() (const diag_record& r) const
+ {
+ r << loc_.file << ':' << loc_.line << ':' << loc_.column << ": ";
+
+ if (type_ != nullptr)
+ r << type_ << ": ";
+
+ if (name_ != nullptr)
+ r << name_ << ": ";
+ }
+
+ // tracer
+ //
+ void tracer::
+ operator() (const char* const args[], size_t n) const
+ {
+ if (verb >= 3)
+ {
+ diag_record dr (*this);
+ print_process (dr, args, n);
+ }
+ }
+
+ const basic_mark error ("error");
+ const basic_mark warn ("warning");
+ const basic_mark info ("info");
+ const basic_mark text (nullptr);
+ const fail_mark fail ("error");
+ const fail_end endf;
+}
diff --git a/bdep/diagnostics.hxx b/bdep/diagnostics.hxx
new file mode 100644
index 0000000..8c3b3d2
--- /dev/null
+++ b/bdep/diagnostics.hxx
@@ -0,0 +1,233 @@
+// file : bdep/diagnostics.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_DIAGNOSTICS_HXX
+#define BDEP_DIAGNOSTICS_HXX
+
+#include <libbutl/diagnostics.mxx>
+
+#include <bdep/types.hxx>
+#include <bdep/utility.hxx>
+
+namespace bdep
+{
+ using butl::diag_record;
+
+ // Throw this exception to terminate the process. The handler should
+ // assume that the diagnostics has already been issued.
+ //
+ class failed: public std::exception {};
+
+ // Print process commmand line. If the number of elements is specified
+ // (or the second version is used), then it will print the piped multi-
+ // process command line, if present. In this case, the expected format
+ // is as follows:
+ //
+ // name1 arg arg ... nullptr
+ // name2 arg arg ... nullptr
+ // ...
+ // nameN arg arg ... nullptr nullptr
+ //
+ void
+ print_process (diag_record&, const char* const args[], size_t n = 0);
+
+ void
+ print_process (const char* const args[], size_t n = 0);
+
+ inline void
+ print_process (diag_record& dr, const cstrings& args)
+ {
+ print_process (dr, args.data (), args.size ());
+ }
+
+ inline void
+ print_process (const cstrings& args)
+ {
+ print_process (args.data (), args.size ());
+ }
+
+ // Verbosity level. Update documentation for --verbose if changing.
+ //
+ // 0 - disabled
+ // 1 - high-level information messages
+ // 2 - essential underlying commands that are being executed
+ // 3 - all underlying commands that are being executed
+ // 4 - information that could be helpful to the user
+ // 5 - information that could be helpful to the developer
+ // 6 - even more detailed information
+ //
+ // While uint8 is more than enough, use uint16 for the ease of printing.
+ //
+ extern uint16_t verb;
+
+ template <typename F> inline void l1 (const F& f) {if (verb >= 1) f ();}
+ template <typename F> inline void l2 (const F& f) {if (verb >= 2) f ();}
+ template <typename F> inline void l3 (const F& f) {if (verb >= 3) f ();}
+ template <typename F> inline void l4 (const F& f) {if (verb >= 4) f ();}
+ template <typename F> inline void l5 (const F& f) {if (verb >= 5) f ();}
+ template <typename F> inline void l6 (const F& f) {if (verb >= 6) f ();}
+
+ // Diagnostic facility, base infrastructure.
+ //
+ using butl::diag_stream;
+ using butl::diag_epilogue;
+
+ // Diagnostic facility, project specifics.
+ //
+ struct simple_prologue_base
+ {
+ explicit
+ simple_prologue_base (const char* type, const char* name)
+ : type_ (type), name_ (name) {}
+
+ void
+ operator() (const diag_record& r) const;
+
+ private:
+ const char* type_;
+ const char* name_;
+ };
+
+ class location
+ {
+ public:
+ location () {}
+ location (string f, uint64_t l, uint64_t c)
+ : file (move (f)), line (l), column (c) {}
+
+ string file;
+ uint64_t line;
+ uint64_t column;
+ };
+
+ struct location_prologue_base
+ {
+ location_prologue_base (const char* type,
+ const char* name,
+ const location& l)
+ : type_ (type), name_ (name), loc_ (l) {}
+
+ void
+ operator() (const diag_record& r) const;
+
+ private:
+ const char* type_;
+ const char* name_;
+ const location loc_;
+ };
+
+ struct basic_mark_base
+ {
+ using simple_prologue = butl::diag_prologue<simple_prologue_base>;
+ using location_prologue = butl::diag_prologue<location_prologue_base>;
+
+ explicit
+ basic_mark_base (const char* type,
+ const char* name = nullptr,
+ const void* data = nullptr,
+ diag_epilogue* epilogue = nullptr)
+ : type_ (type), name_ (name), data_ (data), epilogue_ (epilogue) {}
+
+ simple_prologue
+ operator() () const
+ {
+ return simple_prologue (epilogue_, type_, name_);
+ }
+
+ location_prologue
+ operator() (const location& l) const
+ {
+ return location_prologue (epilogue_, type_, name_, l);
+ }
+
+ template <typename L>
+ location_prologue
+ operator() (const L& l) const
+ {
+ return location_prologue (
+ epilogue_, type_, name_, get_location (l, data_));
+ }
+
+ template <typename F, typename L, typename C>
+ location_prologue
+ operator() (F&& f, L&& l, C&& c) const
+ {
+ return location_prologue (
+ epilogue_,
+ type_,
+ name_,
+ location (forward<F> (f), forward<L> (l), forward<C> (c)));
+ }
+
+ protected:
+ const char* type_;
+ const char* name_;
+ const void* data_;
+ diag_epilogue* const epilogue_;
+ };
+ using basic_mark = butl::diag_mark<basic_mark_base>;
+
+ extern const basic_mark error;
+ extern const basic_mark warn;
+ extern const basic_mark info;
+ extern const basic_mark text;
+
+ // trace
+ //
+ struct trace_mark_base: basic_mark_base
+ {
+ explicit
+ trace_mark_base (const char* name, const void* data = nullptr)
+ : basic_mark_base ("trace", name, data) {}
+ };
+ using trace_mark = butl::diag_mark<trace_mark_base>;
+
+ class tracer: public trace_mark
+ {
+ public:
+ using trace_mark::trace_mark;
+
+ // process_run_callback() command tracer interface.
+ //
+ void
+ operator() (const char* const [], std::size_t) const;
+ };
+
+ // fail
+ //
+ struct fail_mark_base: basic_mark_base
+ {
+ explicit
+ fail_mark_base (const char* type, const void* data = nullptr)
+ : basic_mark_base (type,
+ nullptr,
+ data,
+ [](const diag_record& r)
+ {
+ r.flush ();
+ throw failed ();
+ }) {}
+ };
+
+ using fail_mark = butl::diag_mark<fail_mark_base>;
+
+ struct fail_end_base
+ {
+ [[noreturn]] void
+ operator() (const diag_record& r) const
+ {
+ // If we just throw then the record's destructor will see an active
+ // exception and will not flush the record.
+ //
+ r.flush ();
+ throw failed ();
+ }
+ };
+ using fail_end = butl::diag_noreturn_end<fail_end_base>;
+
+ extern const fail_mark fail;
+ extern const fail_end endf;
+}
+
+#endif // BDEP_DIAGNOSTICS_HXX
diff --git a/bdep/help.cxx b/bdep/help.cxx
new file mode 100644
index 0000000..ec2c4c2
--- /dev/null
+++ b/bdep/help.cxx
@@ -0,0 +1,59 @@
+// file : bdep/help.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bdep/help.hxx>
+
+#include <libbutl/pager.mxx>
+
+#include <bdep/diagnostics.hxx>
+#include <bdep/bdep-options.hxx>
+
+using namespace std;
+using namespace butl;
+
+namespace bdep
+{
+ int
+ help (const help_options& o, const string& t, usage_function* usage)
+ {
+ if (usage == nullptr) // Not a command.
+ {
+ if (t.empty ()) // General help.
+ usage = &print_bdep_usage;
+ //
+ // Help topics.
+ //
+ else if (t == "common-options")
+ usage = &print_bdep_common_options_long_usage;
+ else
+ fail << "unknown bdep command/help topic '" << t << "'" <<
+ info << "run 'bdep help' for more information";
+ }
+
+ try
+ {
+ pager p ("bdep " + (t.empty () ? "help" : t),
+ verb >= 2,
+ o.pager_specified () ? &o.pager () : nullptr,
+ &o.pager_option ());
+
+ usage (p.stream (), cli::usage_para::none);
+
+ // If the pager failed, assume it has issued some diagnostics.
+ //
+ return p.wait () ? 0 : 1;
+ }
+ // Catch io_error as std::system_error together with the pager-specific
+ // exceptions.
+ //
+ catch (const system_error& e)
+ {
+ error << "pager failed: " << e;
+
+ // Fall through.
+ }
+
+ throw failed ();
+ }
+}
diff --git a/bdep/help.hxx b/bdep/help.hxx
new file mode 100644
index 0000000..224151a
--- /dev/null
+++ b/bdep/help.hxx
@@ -0,0 +1,21 @@
+// file : bdep/help.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_HELP_HXX
+#define BDEP_HELP_HXX
+
+#include <bdep/types.hxx>
+#include <bdep/utility.hxx>
+
+#include <bdep/help-options.hxx>
+
+namespace bdep
+{
+ using usage_function = cli::usage_para (ostream&, cli::usage_para);
+
+ int
+ help (const help_options&, const string& topic, usage_function* usage);
+}
+
+#endif // BDEP_HELP_HXX
diff --git a/bdep/init.cli b/bdep/init.cli
index e293f03..3861b8b 100644
--- a/bdep/init.cli
+++ b/bdep/init.cli
@@ -2,7 +2,7 @@
// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
// license : MIT; see accompanying LICENSE file
-include <bdep/common.cli>;
+include <bdep/configuration.cli>;
"\section=1"
"\name=bdep-init"
@@ -57,8 +57,8 @@ namespace bdep
"
}
- class init_options: common_options
+ class cmd_init_options: configuration_options
{
- //"\h|INIT OPTIONS|"
+ "\h|INIT OPTIONS|"
};
}
diff --git a/bdep/init.cxx b/bdep/init.cxx
new file mode 100644
index 0000000..3e7998f
--- /dev/null
+++ b/bdep/init.cxx
@@ -0,0 +1,25 @@
+// file : bdep/init.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bdep/init.hxx>
+
+#include <bdep/diagnostics.hxx>
+
+using namespace std;
+
+namespace bdep
+{
+ int
+ cmd_init (const cmd_init_options& o, cli::scanner& args)
+ {
+ tracer trace ("init");
+
+ //@@ TODO: validate project/config options for subcommands.
+
+ for (const string& n: o.config_name ())
+ text << n;
+
+ return 0;
+ }
+}
diff --git a/bdep/init.hxx b/bdep/init.hxx
new file mode 100644
index 0000000..f9a5181
--- /dev/null
+++ b/bdep/init.hxx
@@ -0,0 +1,19 @@
+// file : bdep/init.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_INIT_HXX
+#define BDEP_INIT_HXX
+
+#include <bdep/types.hxx>
+#include <bdep/utility.hxx>
+
+#include <bdep/init-options.hxx>
+
+namespace bdep
+{
+ int
+ cmd_init (const cmd_init_options&, cli::scanner& args);
+}
+
+#endif // BDEP_INIT_HXX
diff --git a/bdep/options-types.hxx b/bdep/options-types.hxx
new file mode 100644
index 0000000..3cca75a
--- /dev/null
+++ b/bdep/options-types.hxx
@@ -0,0 +1,12 @@
+// file : bdep/options-types.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_OPTIONS_TYPES_HXX
+#define BDEP_OPTIONS_TYPES_HXX
+
+namespace bdep
+{
+}
+
+#endif // BDEP_OPTIONS_TYPES_HXX
diff --git a/bdep/project.cli b/bdep/project.cli
new file mode 100644
index 0000000..7b6af76
--- /dev/null
+++ b/bdep/project.cli
@@ -0,0 +1,23 @@
+// file : bdep/project.cli
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+include <bdep/common.cli>;
+
+"\name=project" // Not a man page.
+
+namespace bdep
+{
+ // Common options for commands that operate on project/packages (prj-spec
+ // and pkg-spec).
+ //
+ class project_options: common_options
+ {
+ dir_paths --directory|-d
+ {
+ "<dir>",
+ "Assume project/package is in the specified directory rather than in the
+ current working directory."
+ }
+ };
+}
diff --git a/bdep/types-parsers.cxx b/bdep/types-parsers.cxx
new file mode 100644
index 0000000..5d0fa34
--- /dev/null
+++ b/bdep/types-parsers.cxx
@@ -0,0 +1,51 @@
+// file : bdep/types-parsers.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bdep/types-parsers.hxx>
+
+#include <bdep/common-options.hxx> // bdep::cli namespace
+
+namespace bdep
+{
+ namespace cli
+ {
+ 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/bdep/types-parsers.hxx b/bdep/types-parsers.hxx
new file mode 100644
index 0000000..725ffa3
--- /dev/null
+++ b/bdep/types-parsers.hxx
@@ -0,0 +1,39 @@
+// file : bdep/types-parsers.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+// CLI parsers, included into the generated source files.
+//
+
+#ifndef BDEP_TYPES_PARSERS_HXX
+#define BDEP_TYPES_PARSERS_HXX
+
+#include <bdep/types.hxx>
+#include <bdep/options-types.hxx>
+
+namespace bdep
+{
+ namespace cli
+ {
+ class scanner;
+
+ template <typename T>
+ struct parser;
+
+ template <>
+ struct parser<path>
+ {
+ static void
+ parse (path&, bool&, scanner&);
+ };
+
+ template <>
+ struct parser<dir_path>
+ {
+ static void
+ parse (dir_path&, bool&, scanner&);
+ };
+ }
+}
+
+#endif // BDEP_TYPES_PARSERS_HXX
diff --git a/bdep/types.hxx b/bdep/types.hxx
new file mode 100644
index 0000000..c4aa9b5
--- /dev/null
+++ b/bdep/types.hxx
@@ -0,0 +1,108 @@
+// file : bdep/types.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_TYPES_HXX
+#define BDEP_TYPES_HXX
+
+#include <vector>
+#include <string>
+#include <memory> // unique_ptr, shared_ptr
+#include <utility> // pair
+#include <cstddef> // size_t, nullptr_t
+#include <cstdint> // uint{8,16,32,64}_t
+#include <istream>
+#include <ostream>
+#include <functional> // function, reference_wrapper
+
+#include <ios> // ios_base::failure
+#include <exception> // exception
+#include <stdexcept> // logic_error, invalid_argument, runtime_error
+#include <system_error>
+
+#include <libbutl/path.mxx>
+#include <libbutl/optional.mxx>
+#include <libbutl/fdstream.mxx>
+
+namespace bdep
+{
+ // Commonly-used types.
+ //
+ using std::uint8_t;
+ using std::uint16_t;
+ using std::uint32_t;
+ using std::uint64_t;
+
+ using std::size_t;
+ using std::nullptr_t;
+
+ using std::pair;
+ using std::string;
+ using std::function;
+ using std::reference_wrapper;
+
+ using std::unique_ptr;
+ using std::shared_ptr;
+ using std::weak_ptr;
+
+ using std::vector;
+
+ using strings = vector<string>;
+ using cstrings = vector<const char*>;
+
+ using std::istream;
+ using std::ostream;
+
+ // Exceptions. While <exception> is included, there is no using for
+ // std::exception -- use qualified.
+ //
+ using std::logic_error;
+ using std::invalid_argument;
+ using std::runtime_error;
+ using std::system_error;
+ using io_error = std::ios_base::failure;
+
+ // <libbutl/optional.mxx>
+ //
+ using butl::optional;
+ using butl::nullopt;
+
+ // <libbutl/path.mxx>
+ //
+ using butl::path;
+ using butl::dir_path;
+ using butl::basic_path;
+ using butl::invalid_path;
+
+ using butl::path_cast;
+
+ using paths = std::vector<path>;
+ using dir_paths = std::vector<dir_path>;
+
+ // <libbutl/fdstream.mxx>
+ //
+ using butl::auto_fd;
+ using butl::fdpipe;
+ using butl::ifdstream;
+ using butl::ofdstream;
+ using butl::fdstream_mode;
+}
+
+// In order to be found (via ADL) these have to be either in std:: or in
+// butl::. The latter is bad idea since libbutl includes the default
+// implementation.
+//
+namespace std
+{
+ // Custom path printing (canonicalized, with trailing slash for directories).
+ //
+ inline ostream&
+ operator<< (ostream& os, const ::butl::path& p)
+ {
+ string r (p.representation ());
+ ::butl::path::traits::canonicalize (r);
+ return os << r;
+ }
+}
+
+#endif // BDEP_TYPES_HXX
diff --git a/bdep/utility.cxx b/bdep/utility.cxx
new file mode 100644
index 0000000..cb005a0
--- /dev/null
+++ b/bdep/utility.cxx
@@ -0,0 +1,96 @@
+// file : bdep/utility.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bdep/utility.hxx>
+
+#include <iostream> // cout, cin
+
+#include <libbutl/process.mxx>
+#include <libbutl/fdstream.mxx>
+
+#include <bdep/diagnostics.hxx>
+
+using namespace std;
+using namespace butl;
+
+namespace bdep
+{
+ const string empty_string;
+ const path empty_path;
+ const dir_path empty_dir_path;
+
+ bool
+ exists (const path& f, bool ignore_error)
+ {
+ try
+ {
+ return file_exists (f, true /* follow_symlinks */, ignore_error);
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to stat path " << f << ": " << e << endf;
+ }
+ }
+
+ bool
+ exists (const dir_path& d, bool ignore_error)
+ {
+ try
+ {
+ return dir_exists (d, ignore_error);
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to stat path " << d << ": " << e << endf;
+ }
+ }
+
+ bool
+ empty (const dir_path& d)
+ {
+ try
+ {
+ return dir_empty (d);
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to scan directory " << d << ": " << e << endf;
+ }
+ }
+
+ void
+ mk (const dir_path& d)
+ {
+ if (verb >= 3)
+ text << "mkdir " << d;
+
+ try
+ {
+ try_mkdir (d);
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to create directory " << d << ": " << e;
+ }
+ }
+
+ void
+ rm (const path& f, uint16_t v)
+ {
+ if (verb >= v)
+ text << "rm " << f;
+
+ try
+ {
+ if (try_rmfile (f) == rmfile_status::not_exist)
+ fail << "unable to remove file " << f << ": file does not exist";
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to remove file " << f << ": " << e;
+ }
+ }
+
+ dir_path exec_dir;
+}
diff --git a/bdep/utility.hxx b/bdep/utility.hxx
new file mode 100644
index 0000000..48f168f
--- /dev/null
+++ b/bdep/utility.hxx
@@ -0,0 +1,79 @@
+// file : bdep/utility.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_UTILITY_HXX
+#define BDEP_UTILITY_HXX
+
+#include <memory> // make_shared()
+#include <string> // to_string()
+#include <utility> // move(), forward(), declval(), make_pair()
+#include <cassert> // assert()
+#include <iterator> // make_move_iterator()
+
+#include <libbutl/ft/lang.hxx>
+
+#include <libbutl/utility.mxx> // casecmp(), reverse_iterate(), etc
+
+#include <libbutl/filesystem.mxx>
+
+#include <bdep/types.hxx>
+#include <bdep/version.hxx>
+
+namespace bdep
+{
+ using std::move;
+ using std::forward;
+ using std::declval;
+
+ using std::make_pair;
+ using std::make_shared;
+ using std::make_move_iterator;
+ using std::to_string;
+
+ // <libbutl/utility.mxx>
+ //
+ using butl::casecmp;
+ using butl::reverse_iterate;
+
+ using butl::exception_guard;
+ using butl::make_exception_guard;
+
+ // <libbutl/filesystem.mxx>
+ //
+ using butl::auto_rmfile;
+ using butl::auto_rmdir;
+
+ // Empty string and path.
+ //
+ extern const string empty_string;
+ extern const path empty_path;
+ extern const dir_path empty_dir_path;
+
+ // Filesystem.
+ //
+ bool
+ exists (const path&, bool ignore_error = false);
+
+ bool
+ exists (const dir_path&, bool ignore_error = false);
+
+ bool
+ empty (const dir_path&);
+
+ void
+ mk (const dir_path&);
+
+ void
+ mk_p (const dir_path&);
+
+ void
+ rm (const path&, uint16_t verbosity = 3);
+
+ // Directory extracted from argv[0] (i.e., this process' recall directory)
+ // or empty if there is none. Can be used as a search fallback.
+ //
+ extern dir_path exec_dir;
+}
+
+#endif // BDEP_UTILITY_HXX
diff --git a/bdep/version.hxx.in b/bdep/version.hxx.in
new file mode 100644
index 0000000..c243114
--- /dev/null
+++ b/bdep/version.hxx.in
@@ -0,0 +1,48 @@
+// file : bdep/version.hxx.in -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_VERSION // Note: using the version macro itself.
+
+// Note: using build2 standard versioning scheme. The numeric version format
+// is AAABBBCCCDDDE where:
+//
+// AAA - major version number
+// BBB - minor version number
+// CCC - bugfix version number
+// DDD - alpha / beta (DDD + 500) version number
+// E - final (0) / snapshot (1)
+//
+// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example:
+//
+// Version AAABBBCCCDDDE
+//
+// 0.1.0 0000010000000
+// 0.1.2 0000010010000
+// 1.2.3 0010020030000
+// 2.2.0-a.1 0020019990010
+// 3.0.0-b.2 0029999995020
+// 2.2.0-a.1.z 0020019990011
+//
+#define BDEP_VERSION $bdep.version.project_number$ULL
+#define BDEP_VERSION_STR "$bdep.version.project$"
+#define BDEP_VERSION_ID "$bdep.version.project_id$"
+
+#define BDEP_VERSION_MAJOR $bdep.version.major$
+#define BDEP_VERSION_MINOR $bdep.version.minor$
+#define BDEP_VERSION_PATCH $bdep.version.patch$
+
+#define BDEP_PRE_RELEASE $bdep.version.pre_release$
+
+#define BDEP_SNAPSHOT $bdep.version.snapshot_sn$ULL
+#define BDEP_SNAPSHOT_ID "$bdep.version.snapshot_id$"
+
+#include <libbutl/version.hxx>
+
+$libbutl.check(LIBBUTL_VERSION, LIBBUTL_SNAPSHOT)$
+
+#include <libbpkg/version.hxx>
+
+$libbpkg.check(LIBBPKG_VERSION, LIBBPKG_SNAPSHOT)$
+
+#endif // BDEP_VERSION
diff --git a/build/.gitignore b/build/.gitignore
new file mode 100644
index 0000000..225c27f
--- /dev/null
+++ b/build/.gitignore
@@ -0,0 +1 @@
+config.build
diff --git a/build/bootstrap.build b/build/bootstrap.build
new file mode 100644
index 0000000..da6209e
--- /dev/null
+++ b/build/bootstrap.build
@@ -0,0 +1,11 @@
+# file : build/bootstrap.build
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+project = bdep
+
+using version
+using config
+using dist
+using test
+using install
diff --git a/build/export.build b/build/export.build
new file mode 100644
index 0000000..c1cab6d
--- /dev/null
+++ b/build/export.build
@@ -0,0 +1,10 @@
+# file : build/export.build
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+$out_root/:
+{
+ include bdep/
+}
+
+export $out_root/bdep/exe{bdep}
diff --git a/build/root.build b/build/root.build
new file mode 100644
index 0000000..a9f890d
--- /dev/null
+++ b/build/root.build
@@ -0,0 +1,22 @@
+# file : build/root.build
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+cxx.std = latest
+
+using cxx
+
+hxx{*}: extension = hxx
+ixx{*}: extension = ixx
+txx{*}: extension = txx
+cxx{*}: extension = cxx
+
+cxx.poptions =+ "-I$out_root" "-I$src_root"
+
+# Load the cli module but only if it's available. This way a distribution
+# that includes pre-generated files can be built without installing cli.
+# This is also the reason why we need to explicitly spell out individual
+# source file prerequisites instead of using the cli.cxx{} group (it won't
+# be there unless the module is configured).
+#
+using? cli
diff --git a/buildfile b/buildfile
new file mode 100644
index 0000000..2a5ec7a
--- /dev/null
+++ b/buildfile
@@ -0,0 +1,18 @@
+# file : buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+./: {*/ -build/} doc{INSTALL LICENSE NEWS README version} file{manifest}
+
+# The version file is auto-generated (by the version module) from manifest.
+# Include it in distribution and don't remove when cleaning in src (so that
+# clean results in a state identical to distributed).
+#
+doc{version}: file{manifest}
+doc{version}: dist = true
+doc{version}: clean = ($src_root != $out_root)
+
+# Don't install tests or the INSTALL file.
+#
+dir{tests/}: install = false
+doc{INSTALL}@./: install = false
diff --git a/doc/buildfile b/doc/buildfile
new file mode 100644
index 0000000..3e8a6e7
--- /dev/null
+++ b/doc/buildfile
@@ -0,0 +1,20 @@
+# file : doc/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+cmds = \
+bdep-help \
+bdep-init \
+bdep-config
+
+define css: file
+css{*}: extension = css
+
+define xhtml: doc
+xhtml{*}: extension = xhtml
+
+./: {man1 xhtml}{bdep bdep-common-options $cmds} \
+ css{common pre-box man} \
+ file{man-*}
+
+./: file{cli.sh}
diff --git a/doc/cli.sh b/doc/cli.sh
index 2913b59..0cca076 100755
--- a/doc/cli.sh
+++ b/doc/cli.sh
@@ -49,12 +49,12 @@ man-prologue.1 --man-epilogue-file man-epilogue.1 --man-suffix .1 \
../bdep/$n.cli
}
-o="--output-prefix bdep- --class-doc bdep::common_options=short"
+o="--suppress-undocumented --output-prefix bdep- --class-doc bdep::common_options=short"
# A few special cases.
#
compile "common" $o --output-suffix "-options" --class-doc bdep::common_options=long
-compile "bdep" $o --output-prefix "" --suppress-undocumented --class-doc bdep::commands=short --class-doc bdep::topics=short
+compile "bdep" $o --output-prefix "" --class-doc bdep::commands=short --class-doc bdep::topics=short
pages="config help init"
diff --git a/manifest b/manifest
new file mode 100644
index 0000000..debfdce
--- /dev/null
+++ b/manifest
@@ -0,0 +1,20 @@
+: 1
+name: bdep
+version: 0.7.0-a.0.z
+summary: build2 project dependency manager
+license: MIT
+tags: build2, c++, project, dependency, manager
+description-file: README
+changes-file: NEWS
+url: https://build2.org
+doc-url: https://build2.org/doc.xhtml
+src-url: https://git.build2.org/cgit/bdep/tree/
+email: users@build2.org
+build-email: builds@build2.org
+requires: c++14
+depends: * build2 >= 0.7.0-
+depends: * bpkg >= 0.7.0-
+# @@ Should probably become conditional dependency.
+requires: ? cli ; Only required if changing .cli files.
+depends: libbutl [0.7.0-a.0.1 0.7.0-a.1)
+depends: libbpkg [0.7.0-a.0.1 0.7.0-a.1)