aboutsummaryrefslogtreecommitdiff
path: root/bpkg
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-09-02 17:09:57 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-09-02 17:09:57 +0200
commit94c3574492b6db6ae8d5fbef717f8e6f92f1d402 (patch)
treed5cee1ec9d601fbf9da02f82b9ca8431880aea0b /bpkg
parent317cf86dedbd72aacc673c6257cc178e76ee77e3 (diff)
Command line options/commands/help infrastructure
Diffstat (limited to 'bpkg')
-rw-r--r--bpkg/.gitignore2
-rw-r--r--bpkg/bpkg-options.cli41
-rw-r--r--bpkg/bpkg.cxx156
-rw-r--r--bpkg/buildfile24
-rw-r--r--bpkg/common-options.cli48
-rw-r--r--bpkg/help22
-rw-r--r--bpkg/help-options.cli16
-rw-r--r--bpkg/help.cxx70
-rw-r--r--bpkg/rep-create-options.cli23
9 files changed, 388 insertions, 14 deletions
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 <bpkg/common-options.cli>;
+
+namespace bpkg
+{
+ class bpkg_options: common_options
+ {
+ bool --help;
+ bool --version;
+ };
+
+ class bpkg_commands
+ {
+ bool help
+ {
+ "[<topic>]",
+ "Show detailed help for a command or help topic.",
+ ""
+ };
+
+ bool rep-create
+ {
+ "[-d <dir>]",
+ "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 <cassert>
#include <iostream>
#include <exception>
#include <bpkg/types>
#include <bpkg/diagnostics>
+#include <bpkg/bpkg-options>
+
+#include <bpkg/help>
+#include <bpkg/help-options>
+
+//#include <bpkg/rep-create>
+#include <bpkg/rep-create-options>
+
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 <typename O>
+static O
+parse (const common_options& co, cli::scanner& s)
+{
+ O o;
+ static_cast<common_options&> (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<char*> (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<help_options> (co, args);
+
+ if (args.more ())
+ {
+ cmd_argc = 2;
+ cmd_argv[1] = const_cast<char*> (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<rep_create_options> (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 <cstdint>;
+
+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
+ {
+ "<level>",
+ "Set the diagnostics verbosity to <level> 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
+ {
+ "<file>",
+ "Read additional options from <file> 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 <iosfwd>
+
+#include <bpkg/types>
+
+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 <bpkg/common-options.cli>;
+
+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 <bpkg/help>
+
+#include <cassert>
+#include <iostream>
+
+#include <bpkg/types>
+#include <bpkg/diagnostics>
+
+#include <bpkg/bpkg-options>
+#include <bpkg/help-options>
+
+using namespace std;
+
+namespace bpkg
+{
+ static void
+ help ()
+ {
+ ostream& o (cout);
+
+ o << "usage: bpkg --help" << endl
+ << " bpkg --version" << endl
+ << " bpkg [<common-options>] <command> [<command-options>] " <<
+ "[<command-args>]" << 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 <bpkg/common-options.cli>;
+
+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
+ {
+ "<dir>",
+ "Use <dir> as the repository root directory instead of the
+ current working directory."
+ };
+ };
+}