From 94c3574492b6db6ae8d5fbef717f8e6f92f1d402 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 2 Sep 2015 17:09:57 +0200 Subject: Command line options/commands/help infrastructure --- bpkg/.gitignore | 2 + bpkg/bpkg-options.cli | 41 ++++++++++++ bpkg/bpkg.cxx | 156 ++++++++++++++++++++++++++++++++++++++++---- bpkg/buildfile | 24 ++++++- bpkg/common-options.cli | 48 ++++++++++++++ bpkg/help | 22 +++++++ bpkg/help-options.cli | 16 +++++ bpkg/help.cxx | 70 ++++++++++++++++++++ bpkg/rep-create-options.cli | 23 +++++++ 9 files changed, 388 insertions(+), 14 deletions(-) create mode 100644 bpkg/bpkg-options.cli create mode 100644 bpkg/common-options.cli create mode 100644 bpkg/help create mode 100644 bpkg/help-options.cli create mode 100644 bpkg/help.cxx create mode 100644 bpkg/rep-create-options.cli (limited to 'bpkg') diff --git a/bpkg/.gitignore b/bpkg/.gitignore index 5184a34..dabfb06 100644 --- a/bpkg/.gitignore +++ b/bpkg/.gitignore @@ -1 +1,3 @@ bpkg +*-options +*-options.?xx diff --git a/bpkg/bpkg-options.cli b/bpkg/bpkg-options.cli new file mode 100644 index 0000000..46cd4eb --- /dev/null +++ b/bpkg/bpkg-options.cli @@ -0,0 +1,41 @@ +// file : bpkg/bpkg-options.cli +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +include ; + +namespace bpkg +{ + class bpkg_options: common_options + { + bool --help; + bool --version; + }; + + class bpkg_commands + { + bool help + { + "[]", + "Show detailed help for a command or help topic.", + "" + }; + + bool rep-create + { + "[-d ]", + "Create repository manifest file.", + "" + }; + }; + + // Make sure these don't conflict with command names above. + // + class bpkg_topics + { + bool options + { + "Detailed description of common options." + }; + }; +} diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx index d95d005..849a386 100644 --- a/bpkg/bpkg.cxx +++ b/bpkg/bpkg.cxx @@ -2,35 +2,165 @@ // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +#include #include #include #include #include +#include + +#include +#include + +//#include +#include + using namespace std; using namespace bpkg; +// Initialize the command option class O with the common options +// and then parse the rest of the arguments. Once this is done, +// use the "final" values of the common options to do global +// initializations (verbosity level, etc). +// +template +static O +parse (const common_options& co, cli::scanner& s) +{ + O o; + static_cast (o) = co; + o.parse (s); + + // Global initializations. + // + + // Trace verbosity. + // + verb = o.verbose () > 0 ? o.verbose () : (o.v () ? 1 : 0); + + return o; +} + int -main () +main (int argc, char* argv[]) +try { - tracer trace ("main"); + using namespace cli; + + argv_file_scanner args (argc, argv, "--options-file"); + + // First parse common options and --version/--help. + // + bpkg_options bo; + bo.parse (args, unknown_mode::stop); + + if (bo.version ()) + { + cout << "bpkg 0.0.0" << endl + << "Copyright (c) 2014-2015 Code Synthesis Ltd" << endl + << "This is free software released under the MIT license." << endl; + return 0; + } + + if (bo.help ()) + { + help (help_options (), "", nullptr); + return 0; + } + + const common_options& co (bo); + + // The next argument should be a command. + // + if (!args.more ()) + fail << "bpkg command expected" << + info << "run 'bpkg help' for more information"; - try + int cmd_argc (2); + char* cmd_argv[] {argv[0], const_cast (args.next ())}; + bpkg_commands cmd; + cmd.parse (cmd_argc, cmd_argv, true, unknown_mode::stop); + + if (cmd_argc != 1) + fail << "unknown bpkg command/option '" << cmd_argv[1] << "'" << + info << "run 'bpkg 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) { - // Trace verbosity. - // - verb = 0; + ho = parse (co, args); + + if (args.more ()) + { + cmd_argc = 2; + cmd_argv[1] = const_cast (args.next ()); + + // First see if this is a command. + // + cmd = bpkg_commands (); // Clear 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); } - catch (const failed&) + + // Handle commands. + // + + // help + // + if (cmd.help ()) { - return 1; // Diagnostics has already been issued. + assert (h); + return help (ho, "help", help_options::print_usage); } - /* - catch (const std::exception& e) + + // rep-create + // + if (cmd.rep_create ()) { - error << e.what (); - return 1; + if (h) + return help (ho, "rep-create", rep_create_options::print_usage); + + auto o (parse (co, args)); + + if (verb) + text << "rep-create"; + + return 0; } - */ + + assert (false); // Unhandled command. + return 1; +} +catch (const failed&) +{ + return 1; // Diagnostics has already been issued. +} +catch (const cli::exception& e) +{ + *diag_stream << "error: "; + e.print (*diag_stream); + *diag_stream << endl; + return 1; +} +/* +catch (const std::exception& e) +{ + error << e.what (); + return 1; } +*/ diff --git a/bpkg/buildfile b/bpkg/buildfile index 8437c86..4b1a258 100644 --- a/bpkg/buildfile +++ b/bpkg/buildfile @@ -2,7 +2,29 @@ # copyright : Copyright (c) 2014-2015 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file +using cli + import libs = libbutl%lib{butl} import libs += libbpkg%lib{bpkg} -exe{bpkg}: cxx{bpkg diagnostics} $libs +exe{bpkg}: cxx{diagnostics} cli.cxx{common-options} \ + cxx{bpkg} cli.cxx{bpkg-options} \ + cxx{help} cli.cxx{help-options} \ + cli.cxx{rep-create-options} \ + $libs + +cli.options += -I $src_root --include-with-brackets --include-prefix bpkg \ +--guard-prefix BPKG --generate-file-scanner --cli-namespace bpkg::cli \ +--generate-parse --long-usage --exclude-base + +# Option length must be the same to get commands/topics/options lists +# aligned in the general help. +# +cli.cxx{common-options}: cli{common-options} +cli.cxx{common-options}: cli.options += --option-length 22 --short-usage + +cli.cxx{bpkg-options}: cli{bpkg-options} +cli.cxx{bpkg-options}: cli.options += --option-length 22 --short-usage + +cli.cxx{help-options}: cli{help-options} +cli.cxx{rep-create-options}: cli{rep-create-options} diff --git a/bpkg/common-options.cli b/bpkg/common-options.cli new file mode 100644 index 0000000..326cf64 --- /dev/null +++ b/bpkg/common-options.cli @@ -0,0 +1,48 @@ +// file : bpkg/common-options.cli +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +include ; + +namespace bpkg +{ + class common_options = 0 + { + bool -v + { + "Print actual commands being executed. This is equivalent to + \cb{--verbose 1}." + }; + + std::uint16_t --verbose = 0 + { + "", + "Set the diagnostics verbosity to which can be between 0 + (disabled) and 5 (lots of information). @@ Need to document + further." + }; + + // The following option is "fake" in that it is actually handled by + // argv_file_scanner. We have it here for documentation. + // + std::string --options-file + { + "", + "Read additional options from with each option appearing on a + separate line optionally followed by space and an option value. Empty + lines and lines starting with \cb{#} are ignored. Option values can + be enclosed in double (\cb{\"}) or single (\cb{'}) quotes to preserve + leading and trailing whitespaces as well as to specify empty values. + If the value itself contains trailing or leading quotes, enclose it + with an extra pair of quotes, for example \cb{'\"x\"'}. Non-leading + and non-trailing quotes are interpreted as being part of the option + value. + + The semantics of providing options in a file is equivalent to providing + the same set of options in the same order on the command line at the + point where the \cb{--options-file} option is specified except that + the shell escaping and quoting is not required. You can repeat this + option to specify more than one options file." + }; + }; +} diff --git a/bpkg/help b/bpkg/help new file mode 100644 index 0000000..69e8f69 --- /dev/null +++ b/bpkg/help @@ -0,0 +1,22 @@ +// file : bpkg/help -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef BPKG_HELP +#define BPKG_HELP + +#include + +#include + +namespace bpkg +{ + class help_options; + + int + help (const help_options&, + const string& topic, + void (*usage) (std::ostream&)); +} + +#endif // BPKG_HELP diff --git a/bpkg/help-options.cli b/bpkg/help-options.cli new file mode 100644 index 0000000..4ed03cf --- /dev/null +++ b/bpkg/help-options.cli @@ -0,0 +1,16 @@ +// file : bpkg/help-options.cli +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +include ; + +namespace bpkg +{ + class help_options: common_options + { + /* + "Show detailed help for a command or help topic or the list of + available commands and topics if none was specified." + */ + }; +} diff --git a/bpkg/help.cxx b/bpkg/help.cxx new file mode 100644 index 0000000..b63adbd --- /dev/null +++ b/bpkg/help.cxx @@ -0,0 +1,70 @@ +// file : bpkg/help.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include + +#include +#include + +#include +#include + +using namespace std; + +namespace bpkg +{ + static void + help () + { + ostream& o (cout); + + o << "usage: bpkg --help" << endl + << " bpkg --version" << endl + << " bpkg [] [] " << + "[]" << endl + << endl; + + o << "The commands are:" << endl + << endl; + + bpkg_commands::print_short_usage (o); + o << endl; + + o << "The help topics are:" << endl + << endl; + + bpkg_topics::print_short_usage (o); + o << endl; + + o << "The common options are:" << endl + << endl; + + common_options::print_short_usage (o); + o << endl; + + o << "The common options can also be specified as part of the command-" << + "specific ones."<< endl; + } + + int + help (const help_options&, const string& t, void (*usage) (std::ostream&)) + { + if (usage != nullptr) // Command. + usage (cout); + else if (t.empty ()) // General help. + help (); + else if (t == "options") // Help topics. + { + common_options::print_long_usage (cout); + } + else + fail << "unknown bpkg command/help topic '" << t << "'" << + info << "run 'bpkg help' for more information"; + + return 0; + } +} diff --git a/bpkg/rep-create-options.cli b/bpkg/rep-create-options.cli new file mode 100644 index 0000000..143efe4 --- /dev/null +++ b/bpkg/rep-create-options.cli @@ -0,0 +1,23 @@ +// file : bpkg/rep-create-options.cli +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +include ; + +namespace bpkg +{ + class rep_create_options: common_options + { + /* + "Create repository manifest file (\cb{packages}) based on the + packages present in the repository directory." + */ + + std::string --directory | -d + { + "", + "Use as the repository root directory instead of the + current working directory." + }; + }; +} -- cgit v1.1