aboutsummaryrefslogtreecommitdiff
path: root/build2/b.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2022-02-17 10:43:20 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2022-02-17 10:43:20 +0200
commitc988a7459c85bc4139cc2e151ddb5758a998ab5a (patch)
tree262b0742f229d7d111839f9d7903ada2345f1914 /build2/b.cxx
parent7863221ec4af93d9c57644be170aa49ca23c8f7a (diff)
Factor command line parsing logic into separate function
Diffstat (limited to 'build2/b.cxx')
-rw-r--r--build2/b.cxx392
1 files changed, 210 insertions, 182 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index c094523..03ded28 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -71,190 +71,37 @@ using namespace std;
namespace build2
{
- static options ops;
-
- int
- main (int argc, char* argv[]);
-
- // Structured result printer (--structured-result mode).
- //
- class result_printer
+ struct cmdline
{
- public:
- result_printer (const action_targets& tgs): tgs_ (tgs) {}
- ~result_printer ();
-
- private:
- const action_targets& tgs_;
+ strings cmd_vars;
+ string buildspec;
+ uint16_t verbosity;
};
- result_printer::
- ~result_printer ()
- {
- // Let's do some sanity checking even when we are not in the structred
- // output mode.
- //
- for (const action_target& at: tgs_)
- {
- switch (at.state)
- {
- case target_state::unknown: continue; // Not a target/no result.
- case target_state::unchanged:
- case target_state::changed:
- case target_state::failed: break; // Valid states.
- default: assert (false);
- }
-
- if (ops.structured_result ())
- {
- const target& t (at.as<target> ());
- context& ctx (t.ctx);
-
- cout << at.state
- << ' ' << ctx.current_mif->name
- << ' ' << ctx.current_inner_oif->name;
-
- if (ctx.current_outer_oif != nullptr)
- cout << '(' << ctx.current_outer_oif->name << ')';
-
- // There are two ways one may wish to identify the target of the
- // operation: as something specific but inherently non-portable (say,
- // a filesystem path, for example c:\tmp\foo.exe) or as something
- // regular that can be used to refer to a target in a portable way
- // (for example, c:\tmp\exe{foo}; note that the directory part is
- // still not portable). Which one should we use is a good question.
- // Let's go with the portable one for now and see how it goes (we
- // can always add a format version, e.g., --structured-result=2).
-
- // Set the stream extension verbosity to 0 to suppress extension
- // printing by default (this can still be overriden by the target
- // type's print function as is the case for file{}, for example).
- // And set the path verbosity to 1 to always print absolute.
- //
- stream_verbosity sv (stream_verb (cout));
- stream_verb (cout, stream_verbosity (1, 0));
-
- cout << ' ' << t << endl;
-
- stream_verb (cout, sv);
- }
- }
- }
-}
-
-// Print backtrace if terminating due to an unhandled exception. Note that
-// custom_terminate is non-static and not a lambda to reduce the noise.
-//
-static terminate_handler default_terminate;
-
-void
-custom_terminate ()
-{
- *diag_stream << backtrace ();
-
- if (default_terminate != nullptr)
- default_terminate ();
-}
-
-static void
-terminate (bool trace)
-{
- if (!trace)
- set_terminate (default_terminate);
-
- std::terminate ();
-}
-
-int build2::
-main (int argc, char* argv[])
-{
- default_terminate = set_terminate (custom_terminate);
-
- tracer trace ("main");
-
- int r (0);
-
- // This is a little hack to make out 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;
- if (optional<string> p = getenv ("PATH"))
- {
- mp = move (*p);
- mp += ';';
- }
- mp += "/bin";
-
- setenv ("PATH", mp);
- }
-#endif
-
- // A data race happens in the libstdc++ (as of GCC 7.2) implementation of
- // the ctype<char>::narrow() function (bug #77704). The issue is easily
- // triggered by the testscript runner that indirectly (via regex) uses
- // ctype<char> facet of the global locale (and can potentially be triggered
- // by other locale- aware code). We work around this by pre-initializing the
- // global locale facet internal cache.
- //
-#ifdef __GLIBCXX__
+ static cmdline
+ parse_cmdline (tracer& trace, int argc, char* argv[], options& ops)
{
- const ctype<char>& ct (use_facet<ctype<char>> (locale ()));
-
- for (size_t i (0); i != 256; ++i)
- ct.narrow (static_cast<char> (i), '\0');
- }
-#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
-
- scheduler sched;
+ // @@ cl namespace
- // Parse the command line.
- //
- try
- {
// Note that the diagnostics verbosity level can only be calculated after
// default options are loaded and merged (see below). Thus, until then we
// refer to the verbosity level specified on the command line.
//
- auto verbosity = [] ()
+ auto verbosity = [&ops] ()
{
uint16_t v (
ops.verbose_specified ()
? ops.verbose ()
: ops.V () ? 3 : ops.v () ? 2 : ops.quiet () || ops.silent () ? 0 : 1);
-
- if (ops.silent () && v != 0)
- fail << "specified with -v, -V, or --verbose verbosity level " << v
- << " is incompatible with --silent";
-
return v;
};
+ cmdline r;
+
// We want to be able to specify options, vars, and buildspecs in any
// order (it is really handy to just add -v at the end of the command
// line).
//
- strings cmd_vars;
- string args;
try
{
// Command line arguments starting position.
@@ -325,7 +172,7 @@ main (int argc, char* argv[])
if (p == s || (p == s + 1 && *s == '+'))
fail << "missing variable name in '" << s << "'";
- cmd_vars.push_back (s);
+ r.cmd_vars.push_back (s);
continue;
}
@@ -348,7 +195,7 @@ main (int argc, char* argv[])
if (scan.more ())
v += scan.next ();
- cmd_vars.push_back (move (v));
+ r.cmd_vars.push_back (move (v));
continue;
}
}
@@ -361,15 +208,15 @@ main (int argc, char* argv[])
// diagnostics signify argument numbers. Clever, huh?
//
if (argn != 0)
- args += '\n';
+ r.buildspec += '\n';
- args += s;
+ r.buildspec += s;
// See if we are using the shortcut syntax.
//
- if (argn == 0 && args.back () == ':')
+ if (argn == 0 && r.buildspec.back () == ':')
{
- args.back () = '(';
+ r.buildspec.back () = '(';
shortcut = true;
}
@@ -382,9 +229,9 @@ main (int argc, char* argv[])
if (shortcut)
{
if (argn == 1)
- args.pop_back ();
+ r.buildspec.pop_back ();
else
- args += ')';
+ r.buildspec += ')';
}
// Get/set an environment variable tracing the operation.
@@ -454,7 +301,7 @@ main (int argc, char* argv[])
{
path_name fn ("<BUILD2_VAR_OVR>");
- auto i (cmd_vars.begin ());
+ auto i (r.cmd_vars.begin ());
for (size_t b (0), e (0); next_word (*env_ovr, b, e, '\n', '\r'); )
{
// Extract the override from the current line, stripping the leading
@@ -468,7 +315,7 @@ main (int argc, char* argv[])
if (!s.empty ())
{
verify_glb_ovr (s, fn, false /* opt */);
- i = cmd_vars.insert (i, move (s)) + 1;
+ i = r.cmd_vars.insert (i, move (s)) + 1;
}
}
}
@@ -535,10 +382,10 @@ main (int argc, char* argv[])
// line are naturally supported.
//
if (!env_ovr)
- cmd_vars =
+ r.cmd_vars =
merge_default_arguments (
def_ops,
- cmd_vars,
+ r.cmd_vars,
[&verify_glb_ovr] (const default_options_entry<options>& e,
const strings&)
{
@@ -568,10 +415,10 @@ main (int argc, char* argv[])
// from the command line, etc), if any, into the BUILD2_VAR_OVR
// environment variable.
//
- if (!cmd_vars.empty ())
+ if (!r.cmd_vars.empty ())
{
string ovr;
- for (const string& v: cmd_vars)
+ for (const string& v: r.cmd_vars)
{
if (v[0] == '!')
{
@@ -612,6 +459,187 @@ main (int argc, char* argv[])
fail << e;
}
+ r.verbosity = verbosity ();
+
+ if (ops.silent () && r.verbosity != 0)
+ fail << "specified with -v, -V, or --verbose verbosity level "
+ << r.verbosity << " is incompatible with --silent";
+
+ return r;
+ }
+
+ int
+ main (int argc, char* argv[]);
+
+ // Structured result printer (--structured-result mode).
+ //
+ class result_printer
+ {
+ public:
+ result_printer (const options& ops, const action_targets& tgs)
+ : ops_ (ops), tgs_ (tgs) {}
+
+ ~result_printer ();
+
+ private:
+ const options ops_;
+ const action_targets& tgs_;
+ };
+
+ result_printer::
+ ~result_printer ()
+ {
+ // Let's do some sanity checking even when we are not in the structred
+ // output mode.
+ //
+ for (const action_target& at: tgs_)
+ {
+ switch (at.state)
+ {
+ case target_state::unknown: continue; // Not a target/no result.
+ case target_state::unchanged:
+ case target_state::changed:
+ case target_state::failed: break; // Valid states.
+ default: assert (false);
+ }
+
+ if (ops_.structured_result ())
+ {
+ const target& t (at.as<target> ());
+ context& ctx (t.ctx);
+
+ cout << at.state
+ << ' ' << ctx.current_mif->name
+ << ' ' << ctx.current_inner_oif->name;
+
+ if (ctx.current_outer_oif != nullptr)
+ cout << '(' << ctx.current_outer_oif->name << ')';
+
+ // There are two ways one may wish to identify the target of the
+ // operation: as something specific but inherently non-portable (say,
+ // a filesystem path, for example c:\tmp\foo.exe) or as something
+ // regular that can be used to refer to a target in a portable way
+ // (for example, c:\tmp\exe{foo}; note that the directory part is
+ // still not portable). Which one should we use is a good question.
+ // Let's go with the portable one for now and see how it goes (we
+ // can always add a format version, e.g., --structured-result=2).
+
+ // Set the stream extension verbosity to 0 to suppress extension
+ // printing by default (this can still be overriden by the target
+ // type's print function as is the case for file{}, for example).
+ // And set the path verbosity to 1 to always print absolute.
+ //
+ stream_verbosity sv (stream_verb (cout));
+ stream_verb (cout, stream_verbosity (1, 0));
+
+ cout << ' ' << t << endl;
+
+ stream_verb (cout, sv);
+ }
+ }
+ }
+}
+
+// Print backtrace if terminating due to an unhandled exception. Note that
+// custom_terminate is non-static and not a lambda to reduce the noise.
+//
+static terminate_handler default_terminate;
+
+void
+custom_terminate ()
+{
+ *diag_stream << backtrace ();
+
+ if (default_terminate != nullptr)
+ default_terminate ();
+}
+
+static void
+terminate (bool trace)
+{
+ if (!trace)
+ set_terminate (default_terminate);
+
+ std::terminate ();
+}
+
+int build2::
+main (int argc, char* argv[])
+{
+ default_terminate = set_terminate (custom_terminate);
+
+ tracer trace ("main");
+
+ int r (0);
+
+ // This is a little hack to make out 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;
+ if (optional<string> p = getenv ("PATH"))
+ {
+ mp = move (*p);
+ mp += ';';
+ }
+ mp += "/bin";
+
+ setenv ("PATH", mp);
+ }
+#endif
+
+ // A data race happens in the libstdc++ (as of GCC 7.2) implementation of
+ // the ctype<char>::narrow() function (bug #77704). The issue is easily
+ // triggered by the testscript runner that indirectly (via regex) uses
+ // ctype<char> facet of the global locale (and can potentially be triggered
+ // by other locale-aware code). We work around this by pre-initializing the
+ // global locale facet internal cache.
+ //
+#ifdef __GLIBCXX__
+ {
+ const ctype<char>& ct (use_facet<ctype<char>> (locale ()));
+
+ for (size_t i (0); i != 256; ++i)
+ ct.narrow (static_cast<char> (i), '\0');
+ }
+#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
+
+ options ops;
+ scheduler sched;
+
+ try
+ {
+ // Parse the command line.
+ //
+ strings cmd_vars;
+ string args;
+ uint16_t verbosity;
+ {
+ cmdline r (parse_cmdline (trace, argc, argv, ops));
+ cmd_vars = move (r.cmd_vars);
+ args = move (r.buildspec);
+ verbosity = r.verbosity;
+ }
+
// Handle --build2-metadata (see also buildfile).
//
#ifndef BUILD2_BOOTSTRAP
@@ -660,7 +688,7 @@ main (int argc, char* argv[])
// Initialize the diagnostics state.
//
- init_diag (verbosity (),
+ init_diag (verbosity,
ops.silent (),
(ops.progress () ? optional<bool> (true) :
ops.no_progress () ? optional<bool> (false) : nullopt),
@@ -817,7 +845,7 @@ main (int argc, char* argv[])
// below).
//
unique_ptr<context> pctx;
- auto new_context = [&pctx, &sched, &mutexes, &fcache, &cmd_vars]
+ auto new_context = [&ops, &pctx, &sched, &mutexes, &fcache, &cmd_vars]
{
pctx = nullptr; // Free first.
pctx.reset (new context (sched,
@@ -1674,7 +1702,7 @@ main (int argc, char* argv[])
action a (mid, pre_oid, oid);
{
- result_printer p (tgs);
+ result_printer p (ops, tgs);
uint16_t diag (ops.structured_result () ? 0 : 1);
if (mif->match != nullptr)
@@ -1701,7 +1729,7 @@ main (int argc, char* argv[])
action a (mid, oid, oif->outer_id);
{
- result_printer p (tgs);
+ result_printer p (ops, tgs);
uint16_t diag (ops.structured_result () ? 0 : 2);
if (mif->match != nullptr)
@@ -1729,7 +1757,7 @@ main (int argc, char* argv[])
action a (mid, post_oid, oid);
{
- result_printer p (tgs);
+ result_printer p (ops, tgs);
uint16_t diag (ops.structured_result () ? 0 : 1);
if (mif->match != nullptr)