diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2017-02-10 08:15:48 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2017-02-13 12:42:42 +0200 |
commit | abccaf9596461215fce0e32322133fb6c39be44f (patch) | |
tree | 3fc16a6e6142d65e6b47ae686ab845cc164478cc /build2/test/script/parser.cxx | |
parent | bcb2a89e111a918a48a132a2a29e0c26d724591d (diff) |
Implement parallel error propagation, keep_going mode
Keep going is the default but there is now the -s|--serial-stop that makes
the driver run serially and stop at first error.
Also fix some lockups, other minor improvements/features.
Diffstat (limited to 'build2/test/script/parser.cxx')
-rw-r--r-- | build2/test/script/parser.cxx | 86 |
1 files changed, 56 insertions, 30 deletions
diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx index d4e318b..9d926fa 100644 --- a/build2/test/script/parser.cxx +++ b/build2/test/script/parser.cxx @@ -6,6 +6,7 @@ #include <sstream> +#include <build2/context> // keep_going #include <build2/scheduler> #include <build2/test/script/lexer> @@ -2789,6 +2790,21 @@ namespace build2 // void parser:: + execute (script& s, runner& r) + { + assert (s.state == scope_state::unknown); + + auto g ( + make_exception_guard ( + [&s] () {s.state = scope_state::failed;})); + + if (!s.empty ()) + execute (s, s, r); + + s.state = scope_state::passed; + } + + void parser:: execute (scope& sc, script& s, runner& r) { path_ = nullptr; // Set by replays. @@ -2824,13 +2840,19 @@ namespace build2 scheduler::atomic_count task_count (0); - for (unique_ptr<scope>& chain: g->scopes) + // Start asynchronous execution of inner scopes keeping track of how + // many we have handled. + // + auto i (g->scopes.begin ()); + for (auto e (g->scopes.end ()); i != e; ++i) { + unique_ptr<scope>& chain (*i); + // Check if this scope is ignored (e.g., via config.test). // if (!runner_->test (*chain)) { - chain.reset (); + chain = nullptr; continue; } @@ -2909,43 +2931,47 @@ namespace build2 // scope_ = chain.get (); // exec_scope_body (); // scope_ = os; + + // If the scope was executed synchronously, check the status and + // bail out if we weren't asked to keep going. // - sched.async (task_count, - [] (scope& s, script& scr, runner& r) - { - try - { - parser p; - p.execute (s, scr, r); - s.state = scope_state::passed; - } - catch (const failed&) - { - s.state = scope_state::failed; - } - }, - ref (*chain), - ref (*script_), - ref (*runner_)); + if (!sched.async (task_count, + [] (scope& s, script& scr, runner& r) + { + try + { + parser p; + p.execute (s, scr, r); + s.state = scope_state::passed; + } + catch (const failed&) + { + s.state = scope_state::failed; + } + }, + ref (*chain), + ref (*script_), + ref (*runner_))) + { + if (chain->state == scope_state::failed && !keep_going) + { + ++i; + break; + } + } } } - sched.wait (task_count); - for (unique_ptr<scope>& chain: g->scopes) + // Re-examine the scopes we have executed collecting their state. + // + for (auto j (g->scopes.begin ()); j != i; ++j) { + const unique_ptr<scope>& chain (*j); + if (chain == nullptr) continue; - // @@ Currently we simply re-throw though the default mode should - // probably be "keep going". While we could already do it at - // the testscript level, there is no support for continuing - // testing targets at the build2 level. This will probably all - // fall into place when we add support for parallel builds. - // - // At that stage we should also probably think about the "stop - // on first error" mode (which is what we have now). - // switch (chain->state) { case scope_state::passed: break; |