aboutsummaryrefslogtreecommitdiff
path: root/build2/b.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'build2/b.cxx')
-rw-r--r--build2/b.cxx1270
1 files changed, 585 insertions, 685 deletions
diff --git a/build2/b.cxx b/build2/b.cxx
index 9a924d6..8decadf 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -1,26 +1,18 @@
// file : build2/b.cxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef _WIN32
-# include <signal.h> // signal()
-#else
-# include <libbutl/win32-utility.hxx>
-#endif
-
-#ifdef __GLIBCXX__
-# include <locale>
-#endif
-
#include <sstream>
-#include <cstring> // strcmp(), strchr()
#include <typeinfo>
#include <iostream> // cout
#include <exception> // terminate(), set_terminate(), terminate_handler
-#include <libbutl/pager.mxx>
-#include <libbutl/fdstream.mxx> // stderr_fd(), fdterm()
-#include <libbutl/backtrace.mxx> // backtrace()
-#include <libbutl/default-options.mxx>
+#include <libbutl/pager.hxx>
+#include <libbutl/fdstream.hxx> // stderr_fd(), fdterm()
+#include <libbutl/backtrace.hxx> // backtrace()
+
+#ifndef BUILD2_BOOTSTRAP
+# include <libbutl/json/serializer.hxx>
+#endif
#include <libbuild2/types.hxx>
#include <libbuild2/utility.hxx>
@@ -37,12 +29,14 @@
#include <libbuild2/buildspec.hxx>
#include <libbuild2/operation.hxx>
#include <libbuild2/filesystem.hxx>
+#include <libbuild2/file-cache.hxx>
#include <libbuild2/diagnostics.hxx>
#include <libbuild2/prerequisite.hxx>
#include <libbuild2/parser.hxx>
-#include <build2/b-options.hxx>
+#include <libbuild2/b-options.hxx>
+#include <libbuild2/b-cmdline.hxx>
// Build system modules.
//
@@ -60,8 +54,7 @@
#ifndef BUILD2_BOOTSTRAP
# include <libbuild2/bash/init.hxx>
-
-# include <build2/cli/init.hxx>
+# include <libbuild2/cli/init.hxx>
#endif
using namespace butl;
@@ -69,75 +62,161 @@ using namespace std;
namespace build2
{
- static options ops;
-
int
main (int argc, char* argv[]);
+#ifndef BUILD2_BOOTSTRAP
// Structured result printer (--structured-result mode).
//
class result_printer
{
public:
- result_printer (const action_targets& tgs): tgs_ (tgs) {}
+ result_printer (const b_options& ops,
+ const action_targets& tgs,
+ json::stream_serializer& js)
+ : ops_ (ops), tgs_ (tgs), json_serializer_ (js) {}
+
~result_printer ();
private:
+ void
+ print_lines ();
+
+ void
+ print_json ();
+
+ private:
+ const b_options& ops_;
const action_targets& tgs_;
+ json::stream_serializer& json_serializer_;
};
+ void result_printer::
+ print_lines ()
+ {
+ for (const action_target& at: tgs_)
+ {
+ if (at.state == target_state::unknown)
+ continue; // Not a target/no 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
+ // variant, e.g., --structured-result=lines-path). Note also that the
+ // json format includes both.
+
+ // 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);
+ }
+ }
+
+ void result_printer::
+ print_json ()
+ {
+ json::stream_serializer& s (json_serializer_);
+
+ for (const action_target& at: tgs_)
+ {
+ if (at.state == target_state::unknown)
+ continue; // Not a target/no result.
+
+ const target& t (at.as<target> ());
+ context& ctx (t.ctx);
+
+ s.begin_object ();
+
+ // Quoted target.
+ //
+ s.member_name ("target");
+ dump_quoted_target_name (s, t);
+
+ // Display target.
+ //
+ s.member_name ("display_target");
+ dump_display_target_name (s, t);
+
+ s.member ("target_type", t.type ().name, false /* check */);
+
+ if (t.is_a<dir> ())
+ s.member ("target_path", t.dir.string ());
+ else if (const auto* pt = t.is_a<path_target> ())
+ s.member ("target_path", pt->path ().string ());
+
+ s.member ("meta_operation", ctx.current_mif->name, false /* check */);
+ s.member ("operation", ctx.current_inner_oif->name, false /* check */);
+
+ if (ctx.current_outer_oif != nullptr)
+ s.member ("outer_operation",
+ ctx.current_outer_oif->name,
+ false /* check */);
+
+ s.member ("state", to_string (at.state), false /* check */);
+
+ s.end_object ();
+ }
+ }
+
result_printer::
~result_printer ()
{
// Let's do some sanity checking even when we are not in the structred
// output mode.
//
+#ifndef NDEBUG
for (const action_target& at: tgs_)
{
switch (at.state)
{
- case target_state::unknown: continue; // Not a target/no result.
+ case target_state::unknown:
case target_state::unchanged:
case target_state::changed:
case target_state::failed: break; // Valid states.
default: assert (false);
}
+ }
+#endif
- if (ops.structured_result ())
+ if (ops_.structured_result_specified ())
+ {
+ switch (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);
+ case structured_result_format::lines:
+ {
+ print_lines ();
+ break;
+ }
+ case structured_result_format::json:
+ {
+ print_json ();
+ break;
+ }
}
}
}
+#endif
}
// Print backtrace if terminating due to an unhandled exception. Note that
@@ -170,425 +249,21 @@ main (int argc, char* argv[])
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
+ init_process ();
+ int r (0);
+ b_options ops;
scheduler sched;
- // Parse the command line.
+ // Statistics.
//
+ size_t phase_switch_contention (0);
+
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 = [] ()
- {
- 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;
- };
-
- // 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).
+ // Parse the command line.
//
- strings cmd_vars;
- string args;
- try
- {
- cl::argv_file_scanner scan (argc, argv, "--options-file");
-
- size_t argn (0); // Argument count.
- bool shortcut (false); // True if the shortcut syntax is used.
-
- for (bool opt (true), var (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).
- //
- if (ops.parse (scan))
- continue;
-
- // Fall through.
- }
-
- const char* s (scan.next ());
-
- // See if this is a command line variable. What if someone needs to
- // pass a buildspec that contains '='? One way to support this would
- // be to quote such a buildspec (e.g., "'/tmp/foo=bar/'"). Or invent
- // another separator. Or use a second "--". Actually, let's just do
- // the second "--".
- //
- if (var)
- {
- // If we see second "--", then we are also done parsing variables.
- //
- if (strcmp (s, "--") == 0)
- {
- var = false;
- continue;
- }
-
- if (const char* p = strchr (s, '=')) // Covers =, +=, and =+.
- {
- // Diagnose the empty variable name situation. Note that we don't
- // allow "partially broken down" assignments (as in foo =bar)
- // since foo= bar would be ambigous.
- //
- if (p == s || (p == s + 1 && *s == '+'))
- fail << "missing variable name in '" << s << "'";
-
- cmd_vars.push_back (s);
- continue;
- }
-
- // Handle the "broken down" variable assignments (i.e., foo = bar
- // instead of foo=bar).
- //
- if (scan.more ())
- {
- const char* a (scan.peek ());
-
- if (strcmp (a, "=" ) == 0 ||
- strcmp (a, "+=") == 0 ||
- strcmp (a, "=+") == 0)
- {
- string v (s);
- v += a;
-
- scan.next ();
-
- if (scan.more ())
- v += scan.next ();
-
- cmd_vars.push_back (move (v));
- continue;
- }
- }
-
- // Fall through.
- }
-
- // Merge all the individual buildspec arguments into a single string.
- // We use newlines to separate arguments so that line numbers in
- // diagnostics signify argument numbers. Clever, huh?
- //
- if (argn != 0)
- args += '\n';
-
- args += s;
-
- // See if we are using the shortcut syntax.
- //
- if (argn == 0 && args.back () == ':')
- {
- args.back () = '(';
- shortcut = true;
- }
-
- argn++;
- }
-
- // Add the closing parenthesis unless there wasn't anything in between
- // in which case pop the opening one.
- //
- if (shortcut)
- {
- if (argn == 1)
- args.pop_back ();
- else
- args += ')';
- }
-
- // Get/set an environment variable tracing the operation.
- //
- auto get_env = [&verbosity, &trace] (const char* nm)
- {
- optional<string> r (getenv (nm));
-
- if (verbosity () >= 5)
- {
- if (r)
- trace << nm << ": '" << *r << "'";
- else
- trace << nm << ": <NULL>";
- }
-
- return r;
- };
-
- auto set_env = [&verbosity, &trace] (const char* nm, const string& vl)
- {
- try
- {
- if (verbosity () >= 5)
- trace << "setting " << nm << "='" << vl << "'";
-
- setenv (nm, vl);
- }
- catch (const system_error& e)
- {
- // The variable value can potentially be long/multi-line, so let's
- // print it last.
- //
- fail << "unable to set environment variable " << nm << ": " << e <<
- info << "value: '" << vl << "'";
- }
- };
-
- // If the BUILD2_VAR_OVR environment variable is present, then parse its
- // value as a newline-separated global variable overrides and prepend
- // them to the overrides specified on the command line.
- //
- // Note that this means global overrides may not contain a newline.
-
- // Verify that the string is a valid global override. Uses the file name
- // and the options flag for diagnostics only.
- //
- auto verify_glb_ovr = [] (const string& v, const path_name& fn, bool opt)
- {
- size_t p (v.find ('=', 1));
- if (p == string::npos || v[0] != '!')
- {
- diag_record dr (fail (fn));
- dr << "expected " << (opt ? "option or " : "") << "global "
- << "variable override instead of '" << v << "'";
-
- if (p != string::npos)
- dr << info << "prefix variable assignment with '!'";
- }
-
- if (p == 1 || (p == 2 && v[1] == '+')) // '!=' or '!+=' ?
- fail (fn) << "missing variable name in '" << v << "'";
- };
-
- optional<string> env_ovr (get_env ("BUILD2_VAR_OVR"));
- if (env_ovr)
- {
- path_name fn ("<BUILD2_VAR_OVR>");
-
- auto i (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
- // and trailing spaces.
- //
- string s (*env_ovr, b, e - b);
- trim (s);
-
- // Verify and save the override, unless the line is empty.
- //
- if (!s.empty ())
- {
- verify_glb_ovr (s, fn, false /* opt */);
- i = cmd_vars.insert (i, move (s)) + 1;
- }
- }
- }
-
- // Load the default options files, unless --no-default-options is
- // specified on the command line or the BUILD2_DEF_OPT environment
- // variable is set to a value other than 'true' or '1'.
- //
- // If loaded, prepend the default global overrides to the variables
- // specified on the command line, unless BUILD2_VAR_OVR is set in which
- // case just ignore them.
- //
- optional<string> env_def (get_env ("BUILD2_DEF_OPT"));
-
- // False if --no-default-options is specified on the command line. Note
- // that we cache the flag since it can be overridden by a default
- // options file.
- //
- bool cmd_def (!ops.no_default_options ());
-
- if (cmd_def && (!env_def || *env_def == "true" || *env_def == "1"))
- try
- {
- optional<dir_path> extra;
- if (ops.default_options_specified ())
- extra = ops.default_options ();
-
- // Load default options files.
- //
- default_options<options> def_ops (
- load_default_options<options,
- cl::argv_file_scanner,
- cl::unknown_mode> (
- nullopt /* sys_dir */,
- path::home_directory (), // The home variable is not assigned yet.
- extra,
- default_options_files {{path ("b.options")},
- nullopt /* start */},
- [&trace, &verbosity] (const path& f, bool r, bool o)
- {
- if (verbosity () >= 3)
- {
- if (o)
- trace << "treating " << f << " as "
- << (r ? "remote" : "local");
- else
- trace << "loading " << (r ? "remote " : "local ") << f;
- }
- },
- "--options-file",
- true /* args */));
-
- // Merge the default and command line options.
- //
- ops = merge_default_options (def_ops, ops);
-
- // Merge the default and command line global overrides, unless
- // BUILD2_VAR_OVR is already set (in which case we assume this has
- // already been done).
- //
- // Note that the "broken down" variable assignments occupying a single
- // line are naturally supported.
- //
- if (!env_ovr)
- cmd_vars =
- merge_default_arguments (
- def_ops,
- cmd_vars,
- [&verify_glb_ovr] (const default_options_entry<options>& e,
- const strings&)
- {
- path_name fn (e.file);
-
- // Verify that all arguments are global overrides.
- //
- for (const string& a: e.arguments)
- verify_glb_ovr (a, fn, true /* opt */);
- });
- }
- catch (const pair<path, system_error>& e)
- {
- fail << "unable to load default options files: " << e.first << ": "
- << e.second;
- }
- catch (const system_error& e)
- {
- fail << "unable to obtain home directory: " << e;
- }
-
- // Verify and save the global overrides present in cmd_vars (default,
- // from the command line, etc), if any, into the BUILD2_VAR_OVR
- // environment variable.
- //
- if (!cmd_vars.empty ())
- {
- string ovr;
- for (const string& v: cmd_vars)
- {
- if (v[0] == '!')
- {
- if (v.find_first_of ("\n\r") != string::npos)
- fail << "newline in global variable override '" << v << "'";
-
- if (!ovr.empty ())
- ovr += '\n';
-
- ovr += v;
- }
- }
-
- // Optimize for the common case.
- //
- // Note: cmd_vars may contain non-global overrides.
- //
- if (!ovr.empty () && (!env_ovr || *env_ovr != ovr))
- set_env ("BUILD2_VAR_OVR", ovr);
- }
-
- // Propagate disabling of the default options files to the potential
- // nested invocations.
- //
- if (!cmd_def && (!env_def || *env_def != "0"))
- set_env ("BUILD2_DEF_OPT", "0");
-
- // Validate options.
- //
- if (ops.progress () && ops.no_progress ())
- fail << "both --progress and --no-progress specified";
-
- if (ops.mtime_check () && ops.no_mtime_check ())
- fail << "both --mtime-check and --no-mtime-check specified";
- }
- catch (const cl::exception& e)
- {
- fail << e;
- }
+ b_cmdline cmdl (parse_b_cmdline (trace, argc, argv, ops));
// Handle --build2-metadata (see also buildfile).
//
@@ -605,6 +280,7 @@ main (int argc, char* argv[])
<< "b.name = [string] b" << endl
<< "b.version = [string] '" << LIBBUILD2_VERSION_FULL << '\'' << endl
<< "b.checksum = [string] '" << LIBBUILD2_VERSION_FULL << '\'' << endl
+ << "b.environment = [strings] BUILD2_VAR_OVR BUILD2_DEF_OPT" << endl
<< "b.static = [bool] " <<
#ifdef LIBBUILD2_STATIC
"true"
@@ -637,10 +313,10 @@ main (int argc, char* argv[])
// Initialize the diagnostics state.
//
- init_diag (verbosity (),
+ init_diag (cmdl.verbosity,
ops.silent (),
- (ops.progress () ? optional<bool> (true) :
- ops.no_progress () ? optional<bool> (false) : nullopt),
+ cmdl.progress,
+ cmdl.diag_color,
ops.no_line (),
ops.no_column (),
fdterm (stderr_fd ()));
@@ -671,36 +347,14 @@ main (int argc, char* argv[])
}
}
- // Initialize time conversion data that is used by localtime_r().
- //
-#ifndef _WIN32
- tzset ();
-#else
- _tzset ();
-#endif
-
// Initialize the global state.
//
init (&::terminate,
argv[0],
- (ops.mtime_check () ? optional<bool> (true) :
- ops.no_mtime_check () ? optional<bool> (false) : nullopt),
- (ops.config_sub_specified ()
- ? optional<path> (ops.config_sub ())
- : nullopt),
- (ops.config_guess_specified ()
- ? optional<path> (ops.config_guess ())
- : nullopt));
-
-#ifdef _WIN32
- // On Windows disable displaying error reporting dialog box for the
- // current and child processes unless we are in the stop mode. Failed that
- // we may have multiple dialog boxes popping up.
- //
- if (!ops.serial_stop ())
- SetErrorMode (SetErrorMode (0) | // Returns the current mode.
- SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
-#endif
+ ops.serial_stop (),
+ cmdl.mtime_check,
+ cmdl.config_sub,
+ cmdl.config_guess);
// Load builtin modules.
//
@@ -717,50 +371,20 @@ main (int argc, char* argv[])
load_builtin_module (&in::build2_in_load);
#ifndef BUILD2_BOOTSTRAP
- load_builtin_module (&cli::build2_cli_load);
load_builtin_module (&bash::build2_bash_load);
+ load_builtin_module (&cli::build2_cli_load);
#endif
// Start up the scheduler and allocate lock shards.
//
- size_t jobs (0);
-
- if (ops.jobs_specified ())
- jobs = ops.jobs ();
- else if (ops.serial_stop ())
- jobs = 1;
-
- if (jobs == 0)
- jobs = scheduler::hardware_concurrency ();
-
- if (jobs == 0)
- {
- warn << "unable to determine the number of hardware threads" <<
- info << "falling back to serial execution" <<
- info << "use --jobs|-j to override";
-
- jobs = 1;
- }
-
- size_t max_jobs (0);
-
- if (ops.max_jobs_specified ())
- {
- max_jobs = ops.max_jobs ();
-
- if (max_jobs != 0 && max_jobs < jobs)
- fail << "invalid --max-jobs|-J value";
- }
-
- sched.startup (jobs,
- 1,
- max_jobs,
- jobs * ops.queue_depth (),
- (ops.max_stack_specified ()
- ? optional<size_t> (ops.max_stack () * 1024)
- : nullopt));
+ sched.startup (cmdl.jobs,
+ 1 /* init_active */,
+ cmdl.max_jobs,
+ cmdl.jobs * ops.queue_depth (),
+ cmdl.max_stack);
global_mutexes mutexes (sched.shard_size ());
+ file_cache fcache (cmdl.fcache_compress);
// Trace some overall environment information.
//
@@ -772,24 +396,45 @@ main (int argc, char* argv[])
trace << "home: " << home;
trace << "path: " << (p ? *p : "<NULL>");
trace << "type: " << (build_installed ? "installed" : "development");
- trace << "jobs: " << jobs;
+ trace << "jobs: " << cmdl.jobs;
}
// Set the build context before parsing the buildspec since it relies on
// the global scope being setup. We reset it for every meta-operation (see
// below).
//
- unique_ptr<context> ctx;
- auto new_context = [&ctx, &sched, &mutexes, &cmd_vars]
+ unique_ptr<context> pctx;
+ auto new_context = [&ops, &cmdl,
+ &sched, &mutexes, &fcache,
+ &phase_switch_contention,
+ &pctx]
{
- ctx = nullptr; // Free first.
- ctx.reset (new context (sched,
- mutexes,
- ops.match_only (),
- ops.no_external_modules (),
- ops.dry_run (),
- !ops.serial_stop () /* keep_going */,
- cmd_vars));
+ if (pctx != nullptr)
+ {
+ phase_switch_contention += (pctx->phase_mutex.contention +
+ pctx->phase_mutex.contention_load);
+ pctx = nullptr; // Free first to reuse memory.
+ }
+
+ optional<match_only_level> mo;
+ if (ops.load_only ()) mo = match_only_level::alias;
+ else if (ops.match_only ()) mo = match_only_level::all;
+
+ pctx.reset (new context (sched,
+ mutexes,
+ fcache,
+ mo,
+ ops.no_external_modules (),
+ ops.dry_run (),
+ ops.no_diag_buffer (),
+ !ops.serial_stop () /* keep_going */,
+ cmdl.cmd_vars));
+
+ if (ops.trace_match_specified ())
+ pctx->trace_match = &ops.trace_match ();
+
+ if (ops.trace_execute_specified ())
+ pctx->trace_execute = &ops.trace_execute ();
};
new_context ();
@@ -797,17 +442,18 @@ main (int argc, char* argv[])
// Parse the buildspec.
//
buildspec bspec;
+ path_name bspec_name ("<buildspec>");
try
{
- istringstream is (args);
+ istringstream is (cmdl.buildspec);
is.exceptions (istringstream::failbit | istringstream::badbit);
- parser p (*ctx);
- bspec = p.parse_buildspec (is, path_name ("<buildspec>"));
+ parser p (*pctx);
+ bspec = p.parse_buildspec (is, bspec_name);
}
catch (const io_error&)
{
- fail << "unable to parse buildspec '" << args << "'";
+ fail << "unable to parse buildspec '" << cmdl.buildspec << "'";
}
l5 ([&]{trace << "buildspec: " << bspec;});
@@ -815,18 +461,194 @@ main (int argc, char* argv[])
if (bspec.empty ())
bspec.push_back (metaopspec ()); // Default meta-operation.
+ // The reserve values were picked experimentally. They allow building a
+ // sample application that depends on Qt and Boost without causing a
+ // rehash.
+ //
+ // Note: omit reserving anything for the info meta-operation since it
+ // won't be loading the buildfiles and needs to be as fast as possible.
+ //
+ bool mo_info (bspec.size () == 1 &&
+ bspec.front ().size () == 1 &&
+ (bspec.front ().name == "info" ||
+ (bspec.front ().name.empty () &&
+ bspec.front ().front ().name == "info")));
+
+ if (!mo_info)
+ {
+ // Note: also adjust in bpkg if adjusting here.
+ //
+ pctx->reserve (context::reserves {
+ 30000 /* targets */,
+ 1100 /* variables */});
+ }
+
+ bool load_only (ops.load_only ());
+
const path& buildfile (ops.buildfile_specified ()
? ops.buildfile ()
: empty_path);
bool dump_load (false);
bool dump_match (false);
- if (ops.dump_specified ())
+ bool dump_match_pre (false);
+ bool dump_match_post (false);
+ for (const string& p: ops.dump ())
+ {
+ if (p == "load") dump_load = true;
+ else if (p == "match") dump_match = true;
+ else if (p == "match-pre") dump_match_pre = true;
+ else if (p == "match-post") dump_match_post = true;
+ else fail << "unknown phase '" << p << "' specified with --dump";
+ }
+
+ dump_format dump_fmt (dump_format::buildfile);
+ if (ops.dump_format_specified ())
{
- dump_load = ops.dump ().find ("load") != ops.dump ().end ();
- dump_match = ops.dump ().find ("match") != ops.dump ().end ();
+ const string& f (ops.dump_format ());
+
+ if (f == "json-v0.1")
+ {
+#ifdef BUILD2_BOOTSTRAP
+ fail << "json dump not supported in bootstrap build system";
+#endif
+ dump_fmt = dump_format::json;
+ }
+ else if (f != "buildfile")
+ {
+ diag_record dr (fail);
+
+ dr << "unsupported format '" << f << "' specified with --dump-format";
+
+ if (f.compare (0, 4, "json") == 0)
+ dr << info << "supported json format version is json-v0.1";
+ }
}
+ auto dump = [&trace, &ops, dump_fmt] (context& ctx, optional<action> a)
+ {
+ const dir_paths& scopes (ops.dump_scope ());
+ const vector<pair<name, optional<name>>>& targets (ops.dump_target ());
+
+ if (scopes.empty () && targets.empty ())
+ build2::dump (ctx, a, dump_fmt);
+ else
+ {
+ auto comp_norm = [] (dir_path& d, const char* what)
+ {
+ try
+ {
+ if (d.relative ())
+ d.complete ();
+
+ d.normalize ();
+ }
+ catch (const invalid_path& e)
+ {
+ fail << "invalid path '" << e.path << "' specified with " << what;
+ }
+ };
+
+ // If exact is false then return any outer scope that contains this
+ // directory except for the global scope.
+ //
+ auto find_scope = [&ctx, &comp_norm] (dir_path& d,
+ bool exact,
+ const char* what) -> const scope*
+ {
+ comp_norm (d, what);
+
+ // This is always the output directory (specifically, see the target
+ // case below).
+ //
+ const scope& s (ctx.scopes.find_out (d));
+
+ return ((exact ? s.out_path () == d : s != ctx.global_scope)
+ ? &s
+ : nullptr);
+ };
+
+ // Dump scopes.
+ //
+ for (dir_path d: scopes)
+ {
+ const scope* s (find_scope (d, true, "--dump-scope"));
+
+ if (s == nullptr)
+ l5 ([&]{trace << "unknown target scope " << d
+ << " specified with --dump-scope";});
+
+ build2::dump (s, a, dump_fmt);
+ }
+
+ // Dump targets.
+ //
+ for (const pair<name, optional<name>>& p: targets)
+ {
+ const target* t (nullptr);
+
+ // Find the innermost known scope that contains this target. This
+ // is where we are going to resolve its type.
+ //
+ dir_path d (p.second ? p.second->dir : p.first.dir);
+
+ if (const scope* s = find_scope (d, false, "--dump-target"))
+ {
+ // Complete relative directories in names.
+ //
+ name n (p.first), o;
+
+ if (p.second)
+ {
+ comp_norm (n.dir, "--dump-target");
+ o.dir = move (d);
+ }
+ else
+ n.dir = move (d);
+
+ // Similar logic to parser::enter_target::find_target() as used by
+ // the dump directive. Except here we treat unknown target type as
+ // unknown target.
+ //
+ auto r (s->find_target_type (n, location ()));
+
+ if (r.first != nullptr)
+ {
+ t = ctx.targets.find (*r.first, // target type
+ n.dir,
+ o.dir,
+ n.value,
+ r.second, // extension
+ trace);
+
+ if (t == nullptr)
+ l5 ([&]
+ {
+ // @@ TODO: default_extension?
+ //
+ target::combine_name (n.value, r.second, false);
+ names ns {move (n)};
+ if (p.second)
+ ns.push_back (move (o));
+
+ trace << "unknown target " << ns
+ << " specified with --dump-target";
+ });
+ }
+ else
+ l5 ([&]{trace << "unknown target type '" << n.type << "' in "
+ << *s << " specified with --dump-target";});
+
+ }
+ else
+ l5 ([&]{trace << "unknown target scope " << d
+ << " specified with --dump-target";});
+
+ build2::dump (t, a, dump_fmt);
+ }
+ }
+ };
+
// If not NULL, then lifted points to the operation that has been "lifted"
// to the meta-operaion (see the logic below for details). Skip is the
// position of the next operation.
@@ -839,6 +661,20 @@ main (int argc, char* argv[])
//
bool dirty (false); // Already (re)set for the first run.
+#ifndef BUILD2_BOOTSTRAP
+ // Note that this constructor is cheap and so we rather call it always
+ // instead of resorting to dynamic allocations.
+ //
+ // Note also that we disable pretty-printing if there is also the JSON
+ // dump and thus we need to combine the two in the JSON Lines format.
+ //
+ json::stream_serializer js (cout, dump_fmt == dump_format::json ? 0 : 2);
+
+ if (ops.structured_result_specified () &&
+ ops.structured_result () == structured_result_format::json)
+ js.begin_array ();
+#endif
+
for (auto mit (bspec.begin ()); mit != bspec.end (); )
{
vector_view<opspec> opspecs;
@@ -880,8 +716,9 @@ main (int argc, char* argv[])
dirty = false;
}
- const path p ("<buildspec>");
- const location l (p, 0, 0); //@@ TODO
+ context& ctx (*pctx);
+
+ const location l (bspec_name, 0, 0); //@@ TODO (also bpkg::pkg_configure())
meta_operation_id mid (0); // Not yet translated.
const meta_operation_info* mif (nullptr);
@@ -894,25 +731,25 @@ main (int argc, char* argv[])
values& mparams (lifted == nullptr ? mit->params : lifted->params);
string mname (lifted == nullptr ? mit->name : lifted->name);
- ctx->current_mname = mname; // Set early.
+ ctx.current_mname = mname; // Set early.
if (!mname.empty ())
{
- if (meta_operation_id m = ctx->meta_operation_table.find (mname))
+ if (meta_operation_id m = ctx.meta_operation_table.find (mname))
{
// Can modify params, opspec, change meta-operation name.
//
- if (auto f = ctx->meta_operation_table[m].process)
- mname = ctx->current_mname = f (
- *ctx, mparams, opspecs, lifted != nullptr, l);
+ if (auto f = ctx.meta_operation_table[m].process)
+ mname = ctx.current_mname = f (
+ ctx, mparams, opspecs, lifted != nullptr, l);
}
}
// Expose early so can be used during bootstrap (with the same
// limitations as for pre-processing).
//
- scope& gs (ctx->global_scope.rw ());
- gs.assign (ctx->var_build_meta_operation) = mname;
+ scope& gs (ctx.global_scope.rw ());
+ gs.assign (ctx.var_build_meta_operation) = mname;
for (auto oit (opspecs.begin ()); oit != opspecs.end (); ++oit)
{
@@ -923,7 +760,7 @@ main (int argc, char* argv[])
const values& oparams (lifted == nullptr ? os.params : values ());
const string& oname (lifted == nullptr ? os.name : empty_string);
- ctx->current_oname = oname; // Set early.
+ ctx.current_oname = oname; // Set early.
if (lifted != nullptr)
lifted = nullptr; // Clear for the next iteration.
@@ -947,7 +784,7 @@ main (int argc, char* argv[])
&oname, &mname,
&os, &mit, &lifted, &skip, &l, &trace] ()
{
- meta_operation_id m (ctx->meta_operation_table.find (oname));
+ meta_operation_id m (ctx.meta_operation_table.find (oname));
if (m != 0)
{
@@ -1019,12 +856,19 @@ main (int argc, char* argv[])
}
}
- if (out_base.relative ())
- out_base = work / out_base;
+ try
+ {
+ if (out_base.relative ())
+ out_base = work / out_base;
- // This directory came from the command line so actualize it.
- //
- out_base.normalize (true);
+ // This directory came from the command line so actualize it.
+ //
+ out_base.normalize (true);
+ }
+ catch (const invalid_path& e)
+ {
+ fail << "invalid out_base directory '" << e.path << "'";
+ }
// The order in which we determine the roots depends on whether
// src_base was specified explicitly.
@@ -1050,12 +894,19 @@ main (int argc, char* argv[])
if (!exists (src_base))
fail << "src_base directory " << src_base << " does not exist";
- if (src_base.relative ())
- src_base = work / src_base;
+ try
+ {
+ if (src_base.relative ())
+ src_base = work / src_base;
- // Also came from the command line, so actualize.
- //
- src_base.normalize (true);
+ // Also came from the command line, so actualize.
+ //
+ src_base.normalize (true);
+ }
+ catch (const invalid_path& e)
+ {
+ fail << "invalid src_base directory '" << e.path << "'";
+ }
// Make sure out_base is not a subdirectory of src_base. Who would
// want to do that, you may ask. Well, you would be surprised...
@@ -1106,7 +957,7 @@ main (int argc, char* argv[])
// Handle a forwarded configuration. Note that if we've changed
// out_root then we also have to remap out_base.
//
- out_root = bootstrap_fwd (*ctx, src_root, altn);
+ out_root = bootstrap_fwd (ctx, src_root, altn);
if (src_root != out_root)
{
out_base = out_root / out_base.leaf (src_root);
@@ -1151,7 +1002,7 @@ main (int argc, char* argv[])
// use to the bootstrap files (other than src-root.build, which,
// BTW, doesn't need to exist if src_root == out_root).
//
- scope& rs (create_root (*ctx, out_root, src_root)->second);
+ scope& rs (*create_root (ctx, out_root, src_root)->second.front ());
bool bstrapped (bootstrapped (rs));
@@ -1187,8 +1038,8 @@ main (int argc, char* argv[])
<< (forwarded ? "forwarded " : "specified ")
<< src_root;
- ctx->new_src_root = src_root;
- ctx->old_src_root = move (p);
+ ctx.new_src_root = src_root;
+ ctx.old_src_root = move (p);
p = src_root;
}
}
@@ -1210,8 +1061,13 @@ main (int argc, char* argv[])
// Now that we have src_root, load the src_root bootstrap file,
// if there is one.
//
+ // As an optimization, omit discovering subprojects for the info
+ // meta-operation if not needed.
+ //
bootstrap_pre (rs, altn);
- bootstrap_src (rs, altn);
+ bootstrap_src (rs, altn,
+ nullopt /* amalgamation */,
+ !mo_info || info_subprojects (mparams) /*subprojects*/);
// If this is a simple project, then implicitly load the test and
// install modules.
@@ -1231,7 +1087,7 @@ main (int argc, char* argv[])
// command line and import).
//
if (forwarded)
- rs.assign (ctx->var_forwarded) = true;
+ rs.assign (ctx.var_forwarded) = true;
// Sync local variable that are used below with actual values.
//
@@ -1283,8 +1139,8 @@ main (int argc, char* argv[])
// all be known. We store the combined action id in uint8_t;
// see <operation> for details.
//
- assert (ctx->operation_table.size () <= 128);
- assert (ctx->meta_operation_table.size () <= 128);
+ assert (ctx.operation_table.size () <= 128);
+ assert (ctx.meta_operation_table.size () <= 128);
// Since we now know all the names of meta-operations and
// operations, "lift" names that we assumed (from buildspec syntax)
@@ -1301,7 +1157,7 @@ main (int argc, char* argv[])
if (!mname.empty ())
{
- m = ctx->meta_operation_table.find (mname);
+ m = ctx.meta_operation_table.find (mname);
if (m == 0)
fail (l) << "unknown meta-operation " << mname;
@@ -1309,7 +1165,7 @@ main (int argc, char* argv[])
if (!oname.empty ())
{
- o = ctx->operation_table.find (oname);
+ o = ctx.operation_table.find (oname);
if (o == 0)
fail (l) << "unknown operation " << oname;
@@ -1332,7 +1188,7 @@ main (int argc, char* argv[])
if (mif == nullptr)
fail (l) << "target " << tn << " does not support meta-"
- << "operation " << ctx->meta_operation_table[m].name;
+ << "operation " << ctx.meta_operation_table[m].name;
}
//
// Otherwise, check that all the targets in a meta-operation
@@ -1345,7 +1201,7 @@ main (int argc, char* argv[])
if (mi == nullptr)
fail (l) << "target " << tn << " does not support meta-"
- << "operation " << ctx->meta_operation_table[mid].name;
+ << "operation " << ctx.meta_operation_table[mid].name;
if (mi != mif)
fail (l) << "different implementations of meta-operation "
@@ -1368,12 +1224,12 @@ main (int argc, char* argv[])
<< ", id " << static_cast<uint16_t> (mid);});
if (mif->meta_operation_pre != nullptr)
- mif->meta_operation_pre (mparams, l);
+ mif->meta_operation_pre (ctx, mparams, l);
else if (!mparams.empty ())
fail (l) << "unexpected parameters for meta-operation "
<< mif->name;
- ctx->current_meta_operation (*mif);
+ ctx.current_meta_operation (*mif);
dirty = true;
}
@@ -1389,7 +1245,7 @@ main (int argc, char* argv[])
if (r == nullptr)
fail (l) << "target " << tn << " does not support "
- << "operation " << ctx->operation_table[o];
+ << "operation " << ctx.operation_table[o];
return r;
};
@@ -1407,7 +1263,7 @@ main (int argc, char* argv[])
// Allow the meta-operation to translate the operation.
//
if (mif->operation_pre != nullptr)
- oid = mif->operation_pre (mparams, oif->id);
+ oid = mif->operation_pre (ctx, mparams, oif->id);
else // Otherwise translate default to update.
oid = (oif->id == default_id ? update_id : oif->id);
@@ -1428,24 +1284,38 @@ main (int argc, char* argv[])
if (oif->outer_id != 0)
outer_oif = lookup (oif->outer_id);
+ if (!oparams.empty ())
+ {
+ // Operation parameters belong to outer operation, if any.
+ //
+ auto* i (outer_oif != nullptr ? outer_oif : oif);
+
+ if (i->operation_pre == nullptr)
+ fail (l) << "unexpected parameters for operation " << i->name;
+ }
+
// Handle pre/post operations.
//
- if (oif->pre != nullptr)
+ if (auto po = oif->pre_operation)
{
- if ((orig_pre_oid = oif->pre (oparams, mid, l)) != 0)
+ if ((orig_pre_oid = po (
+ ctx,
+ outer_oif == nullptr ? oparams : values {},
+ mid,
+ l)) != 0)
{
assert (orig_pre_oid != default_id);
pre_oif = lookup (orig_pre_oid);
pre_oid = pre_oif->id; // De-alias.
}
}
- else if (!oparams.empty ())
- fail (l) << "unexpected parameters for operation "
- << oif->name;
- if (oif->post != nullptr)
+ if (auto po = oif->post_operation)
{
- if ((orig_post_oid = oif->post (oparams, mid)) != 0)
+ if ((orig_post_oid = po (
+ ctx,
+ outer_oif == nullptr ? oparams : values {},
+ mid)) != 0)
{
assert (orig_post_oid != default_id);
post_oif = lookup (orig_post_oid);
@@ -1466,7 +1336,7 @@ main (int argc, char* argv[])
if (r == nullptr)
fail (l) << "target " << tn << " does not support "
- << "operation " << ctx->operation_table[o];
+ << "operation " << ctx.operation_table[o];
if (r != i)
fail (l) << "different implementations of operation "
@@ -1491,6 +1361,9 @@ main (int argc, char* argv[])
// defined there (common with non-intrusive project conversions
// where everything is built from a single root buildfile).
//
+ // Note: we use find_plausible_buildfile() and not find_buildfile()
+ // to look in outer directories.
+ //
optional<path> bf (
find_buildfile (src_base, src_base, altn, buildfile));
@@ -1528,6 +1401,7 @@ main (int argc, char* argv[])
if (const dir_path* a = *rs.root_extra->amalgamation)
{
trace << " amalgamation: " << *a;
+ trace << " bundle scope: " << *rs.bundle_scope ();
trace << " strong scope: " << *rs.strong_scope ();
trace << " weak scope: " << *rs.weak_scope ();
}
@@ -1540,60 +1414,7 @@ main (int argc, char* argv[])
// boundaries (specifically, amalgamation) are only known after
// bootstrap.
//
- // The mildly tricky part here is to distinguish the situation where
- // we are bootstrapping the same project multiple times. The first
- // override that we set cannot already exist (because the override
- // variable names are unique) so if it is already set, then it can
- // only mean this project is already bootstrapped.
- //
- // This is further complicated by the project vs amalgamation logic
- // (we may have already done the amalgamation but not the project).
- // So we split it into two passes.
- //
- {
- auto& sm (ctx->scopes.rw ());
-
- for (const variable_override& o: ctx->var_overrides)
- {
- if (o.ovr.visibility != variable_visibility::global)
- continue;
-
- // If we have a directory, enter the scope, similar to how we do
- // it in the context ctor.
- //
- scope& s (o.dir
- ? sm.insert ((out_base / *o.dir).normalize ())->second
- : *rs.weak_scope ());
-
- auto p (s.vars.insert (o.ovr));
-
- if (!p.second)
- break;
-
- value& v (p.first);
- v = o.val;
- }
-
- for (const variable_override& o: ctx->var_overrides)
- {
- // Ours is either project (%foo) or scope (/foo).
- //
- if (o.ovr.visibility == variable_visibility::global)
- continue;
-
- scope& s (o.dir
- ? sm.insert ((out_base / *o.dir).normalize ())->second
- : rs);
-
- auto p (s.vars.insert (o.ovr));
-
- if (!p.second)
- break;
-
- value& v (p.first);
- v = o.val;
- }
- }
+ ctx.enter_project_overrides (rs, out_base, ctx.var_overrides);
ts.root_scope = &rs;
ts.out_base = move (out_base);
@@ -1608,6 +1429,9 @@ main (int argc, char* argv[])
break;
}
+ if (load_only && (mid != perform_id || oid != update_id))
+ fail << "--load-only requires perform(update) action";
+
// Now load the buildfiles and search the targets.
//
action_targets tgs;
@@ -1628,7 +1452,7 @@ main (int argc, char* argv[])
// building before we know how to for all the targets in this
// operation batch.
//
- const scope& bs (ctx->scopes.find (ts.out_base));
+ const scope& bs (ctx.scopes.find_out (ts.out_base));
// Find the target type and extract the extension.
//
@@ -1639,6 +1463,9 @@ main (int argc, char* argv[])
if (tt == nullptr)
fail (l) << "unknown target type " << tn.type;
+ if (load_only && !tt->is_a<alias> ())
+ fail << "--load-only requires alias target";
+
if (mif->search != nullptr)
{
// If the directory is relative, assume it is relative to work
@@ -1646,10 +1473,17 @@ main (int argc, char* argv[])
//
dir_path& d (tn.dir);
- if (d.relative ())
- d = work / d;
+ try
+ {
+ if (d.relative ())
+ d = work / d;
- d.normalize (true); // Actualize since came from command line.
+ d.normalize (true); // Actualize since came from command line.
+ }
+ catch (const invalid_path& e)
+ {
+ fail << "invalid target directory '" << e.path << "'";
+ }
if (ts.forwarded)
d = rs.out_path () / d.leaf (rs.src_path ()); // Remap.
@@ -1669,8 +1503,10 @@ main (int argc, char* argv[])
}
} // target
- if (dump_load)
- dump (*ctx);
+ // Delay until after match in the --load-only mode (see below).
+ //
+ if (dump_load && !load_only)
+ dump (ctx, nullopt /* action */);
// Finally, match the rules and perform the operation.
//
@@ -1680,28 +1516,42 @@ main (int argc, char* argv[])
<< ", id " << static_cast<uint16_t> (pre_oid);});
if (mif->operation_pre != nullptr)
- mif->operation_pre (mparams, pre_oid); // Cannot be translated.
+ mif->operation_pre (ctx, mparams, pre_oid); // Can't be translated.
+
+ ctx.current_operation (*pre_oif, oif);
+
+ if (oif->operation_pre != nullptr)
+ oif->operation_pre (ctx, oparams, false /* inner */, l);
- ctx->current_operation (*pre_oif, oif);
+ if (pre_oif->operation_pre != nullptr)
+ pre_oif->operation_pre (ctx, {}, true /* inner */, l);
action a (mid, pre_oid, oid);
{
- result_printer p (tgs);
- uint16_t diag (ops.structured_result () ? 0 : 1);
+#ifndef BUILD2_BOOTSTRAP
+ result_printer p (ops, tgs, js);
+#endif
+ uint16_t diag (ops.structured_result_specified () ? 0 : 1);
if (mif->match != nullptr)
mif->match (mparams, a, tgs, diag, true /* progress */);
- if (dump_match)
- dump (*ctx, a);
+ if (dump_match_pre)
+ dump (ctx, a);
- if (mif->execute != nullptr && !ctx->match_only)
+ if (mif->execute != nullptr && !ctx.match_only)
mif->execute (mparams, a, tgs, diag, true /* progress */);
}
+ if (pre_oif->operation_post != nullptr)
+ pre_oif->operation_post (ctx, {}, true /* inner */);
+
+ if (oif->operation_post != nullptr)
+ oif->operation_post (ctx, oparams, false /* inner */);
+
if (mif->operation_post != nullptr)
- mif->operation_post (mparams, pre_oid);
+ mif->operation_post (ctx, mparams, pre_oid);
l5 ([&]{trace << "end pre-operation batch " << pre_oif->name
<< ", id " << static_cast<uint16_t> (pre_oid);});
@@ -1709,24 +1559,43 @@ main (int argc, char* argv[])
tgs.reset ();
}
- ctx->current_operation (*oif, outer_oif);
+ ctx.current_operation (*oif, outer_oif);
+
+ if (outer_oif != nullptr && outer_oif->operation_pre != nullptr)
+ outer_oif->operation_pre (ctx, oparams, false /* inner */, l);
+
+ if (oif->operation_pre != nullptr)
+ oif->operation_pre (ctx,
+ outer_oif == nullptr ? oparams : values {},
+ true /* inner */,
+ l);
action a (mid, oid, oif->outer_id);
{
- result_printer p (tgs);
- uint16_t diag (ops.structured_result () ? 0 : 2);
+#ifndef BUILD2_BOOTSTRAP
+ result_printer p (ops, tgs, js);
+#endif
+ uint16_t diag (ops.structured_result_specified () ? 0 : 2);
if (mif->match != nullptr)
mif->match (mparams, a, tgs, diag, true /* progress */);
if (dump_match)
- dump (*ctx, a);
+ dump (ctx, a);
- if (mif->execute != nullptr && !ctx->match_only)
+ if (mif->execute != nullptr && !ctx.match_only)
mif->execute (mparams, a, tgs, diag, true /* progress */);
}
+ if (oif->operation_post != nullptr)
+ oif->operation_post (ctx,
+ outer_oif == nullptr ? oparams : values {},
+ true /* inner */);
+
+ if (outer_oif != nullptr && outer_oif->operation_post != nullptr)
+ outer_oif->operation_post (ctx, oparams, false /* inner */);
+
if (post_oid != 0)
{
tgs.reset ();
@@ -1735,35 +1604,52 @@ main (int argc, char* argv[])
<< ", id " << static_cast<uint16_t> (post_oid);});
if (mif->operation_pre != nullptr)
- mif->operation_pre (mparams, post_oid); // Cannot be translated.
+ mif->operation_pre (ctx, mparams, post_oid); // Can't be translated.
+
+ ctx.current_operation (*post_oif, oif);
+
+ if (oif->operation_pre != nullptr)
+ oif->operation_pre (ctx, oparams, false /* inner */, l);
- ctx->current_operation (*post_oif, oif);
+ if (post_oif->operation_pre != nullptr)
+ post_oif->operation_pre (ctx, {}, true /* inner */, l);
action a (mid, post_oid, oid);
{
- result_printer p (tgs);
- uint16_t diag (ops.structured_result () ? 0 : 1);
+#ifndef BUILD2_BOOTSTRAP
+ result_printer p (ops, tgs, js);
+#endif
+ uint16_t diag (ops.structured_result_specified () ? 0 : 1);
if (mif->match != nullptr)
mif->match (mparams, a, tgs, diag, true /* progress */);
- if (dump_match)
- dump (*ctx, a);
+ if (dump_match_post)
+ dump (ctx, a);
- if (mif->execute != nullptr && !ctx->match_only)
+ if (mif->execute != nullptr && !ctx.match_only)
mif->execute (mparams, a, tgs, diag, true /* progress */);
}
+ if (post_oif->operation_post != nullptr)
+ post_oif->operation_post (ctx, {}, true /* inner */);
+
+ if (oif->operation_post != nullptr)
+ oif->operation_post (ctx, oparams, false /* inner */);
+
if (mif->operation_post != nullptr)
- mif->operation_post (mparams, post_oid);
+ mif->operation_post (ctx, mparams, post_oid);
l5 ([&]{trace << "end post-operation batch " << post_oif->name
<< ", id " << static_cast<uint16_t> (post_oid);});
}
+ if (dump_load && load_only)
+ dump (ctx, nullopt /* action */);
+
if (mif->operation_post != nullptr)
- mif->operation_post (mparams, oid);
+ mif->operation_post (ctx, mparams, oid);
l5 ([&]{trace << "end operation batch " << oif->name
<< ", id " << static_cast<uint16_t> (oid);});
@@ -1772,7 +1658,7 @@ main (int argc, char* argv[])
if (mid != 0)
{
if (mif->meta_operation_post != nullptr)
- mif->meta_operation_post (mparams);
+ mif->meta_operation_post (ctx, mparams);
l5 ([&]{trace << "end meta-operation batch " << mif->name
<< ", id " << static_cast<uint16_t> (mid);});
@@ -1781,6 +1667,18 @@ main (int argc, char* argv[])
if (lifted == nullptr && skip == 0)
++mit;
} // meta-operation
+
+#ifndef BUILD2_BOOTSTRAP
+ if (ops.structured_result_specified () &&
+ ops.structured_result () == structured_result_format::json)
+ {
+ js.end_array ();
+ cout << endl;
+ }
+#endif
+
+ phase_switch_contention += (pctx->phase_mutex.contention +
+ pctx->phase_mutex.contention_load);
}
catch (const failed&)
{
@@ -1802,16 +1700,18 @@ main (int argc, char* argv[])
{
text << '\n'
<< "build statistics:" << "\n\n"
- << " thread_max_active " << st.thread_max_active << '\n'
- << " thread_max_total " << st.thread_max_total << '\n'
- << " thread_helpers " << st.thread_helpers << '\n'
- << " thread_max_waiting " << st.thread_max_waiting << '\n'
+ << " thread_max_active " << st.thread_max_active << '\n'
+ << " thread_max_total " << st.thread_max_total << '\n'
+ << " thread_helpers " << st.thread_helpers << '\n'
+ << " thread_max_waiting " << st.thread_max_waiting << '\n'
+ << '\n'
+ << " task_queue_depth " << st.task_queue_depth << '\n'
+ << " task_queue_full " << st.task_queue_full << '\n'
<< '\n'
- << " task_queue_depth " << st.task_queue_depth << '\n'
- << " task_queue_full " << st.task_queue_full << '\n'
+ << " wait_queue_slots " << st.wait_queue_slots << '\n'
+ << " wait_queue_collisions " << st.wait_queue_collisions << '\n'
<< '\n'
- << " wait_queue_slots " << st.wait_queue_slots << '\n'
- << " wait_queue_collisions " << st.wait_queue_collisions << '\n';
+ << " phase_switch_contention " << phase_switch_contention << '\n';
}
return r;