diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2018-03-07 15:22:51 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2018-03-07 15:22:51 +0200 |
commit | 1c36adab776a900adc7325f412b1c8dd61b1a346 (patch) | |
tree | 0227b8c5697d24a862aedd59b9bfe28cc84e91b4 | |
parent | 2e2c3a81b47c650334f5767ddb4ebb2746ef98f1 (diff) |
Setup compilation, command line handling
-rw-r--r-- | bdep/.gitignore | 3 | ||||
-rw-r--r-- | bdep/bdep.cxx | 310 | ||||
-rw-r--r-- | bdep/buildfile | 60 | ||||
-rw-r--r-- | bdep/config.cli | 9 | ||||
-rw-r--r-- | bdep/config.cxx | 27 | ||||
-rw-r--r-- | bdep/config.hxx | 19 | ||||
-rw-r--r-- | bdep/configuration.cli | 35 | ||||
-rw-r--r-- | bdep/diagnostics.cxx | 77 | ||||
-rw-r--r-- | bdep/diagnostics.hxx | 233 | ||||
-rw-r--r-- | bdep/help.cxx | 59 | ||||
-rw-r--r-- | bdep/help.hxx | 21 | ||||
-rw-r--r-- | bdep/init.cli | 6 | ||||
-rw-r--r-- | bdep/init.cxx | 25 | ||||
-rw-r--r-- | bdep/init.hxx | 19 | ||||
-rw-r--r-- | bdep/options-types.hxx | 12 | ||||
-rw-r--r-- | bdep/project.cli | 23 | ||||
-rw-r--r-- | bdep/types-parsers.cxx | 51 | ||||
-rw-r--r-- | bdep/types-parsers.hxx | 39 | ||||
-rw-r--r-- | bdep/types.hxx | 108 | ||||
-rw-r--r-- | bdep/utility.cxx | 96 | ||||
-rw-r--r-- | bdep/utility.hxx | 79 | ||||
-rw-r--r-- | bdep/version.hxx.in | 48 | ||||
-rw-r--r-- | build/.gitignore | 1 | ||||
-rw-r--r-- | build/bootstrap.build | 11 | ||||
-rw-r--r-- | build/export.build | 10 | ||||
-rw-r--r-- | build/root.build | 22 | ||||
-rw-r--r-- | buildfile | 18 | ||||
-rw-r--r-- | doc/buildfile | 20 | ||||
-rwxr-xr-x | doc/cli.sh | 4 | ||||
-rw-r--r-- | manifest | 20 |
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} @@ -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) |