From b37f1aa6398065be806e6605a023189685669885 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 15 Feb 2017 03:55:15 +0200 Subject: Implement parallel match --- build2/test/rule | 6 +++--- build2/test/rule.cxx | 50 ++++++++++++++++++------------------------- build2/test/script/parser.cxx | 37 +++++++++++++++++--------------- build2/test/script/script.cxx | 10 ++++----- 4 files changed, 49 insertions(+), 54 deletions(-) (limited to 'build2/test') diff --git a/build2/test/rule b/build2/test/rule index da55173..7de2e24 100644 --- a/build2/test/rule +++ b/build2/test/rule @@ -21,7 +21,7 @@ namespace build2 { public: virtual match_result - match (slock&, action, target&, const string&) const override; + match (action, target&, const string&) const override; target_state perform_script (action, const target&) const; @@ -31,7 +31,7 @@ namespace build2 { public: virtual recipe - apply (slock&, action, target&) const override; + apply (action, target&) const override; static target_state perform_test (action, const target&); @@ -41,7 +41,7 @@ namespace build2 { public: virtual recipe - apply (slock&, action, target&) const override; + apply (action, target&) const override; target_state perform_test (action, const target&) const; diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx index eac9203..06ffc9f 100644 --- a/build2/test/rule.cxx +++ b/build2/test/rule.cxx @@ -35,7 +35,7 @@ namespace build2 "insufficient space"); match_result rule_common:: - match (slock& ml, action a, target& t, const string&) const + match (action a, target& t, const string&) const { // The (admittedly twisted) logic of this rule tries to achieve the // following: If the target is testable, then we want both first update @@ -63,7 +63,7 @@ namespace build2 // If we have any prerequisites of the test{} type, then this is the // testscript case. // - for (prerequisite_member p: group_prerequisite_members (ml, a, t)) + for (prerequisite_member p: group_prerequisite_members (a, t)) { if (p.is_a ()) { @@ -155,7 +155,7 @@ namespace build2 } recipe alias_rule:: - apply (slock& ml, action a, target& t) const + apply (action a, target& t) const { match_data md (move (t.data ())); t.clear_data (); // In case delegated-to rule also uses aux storage. @@ -171,7 +171,7 @@ namespace build2 // standard alias rule. // if (a.operation () == update_id) - return match_delegate (ml, a, t, *this).first; + return match_delegate (a, t, *this).first; // For the test operation we have to implement our own search and match // because we need to ignore prerequisites that are outside of our @@ -181,7 +181,7 @@ namespace build2 // not ours seems right. Note that we still want to make sure they are // up to date (via the above delegate) since our tests might use them. // - search_and_match_prerequisites (ml, a, t, t.root_scope ()); + match_prerequisites (a, t, t.root_scope ()); // If not a test then also redirect to the alias rule. // @@ -191,7 +191,7 @@ namespace build2 } recipe rule:: - apply (slock& ml, action a, target& t) const + apply (action a, target& t) const { tracer trace ("test::rule::apply"); @@ -208,11 +208,11 @@ namespace build2 if (md.script) { if (a.operation () == update_id) - return match_delegate (ml, a, t, *this).first; + return match_delegate (a, t, *this).first; // Collect all the testscript targets in prerequisite_targets. // - for (prerequisite_member p: group_prerequisite_members (ml, a, t)) + for (prerequisite_member p: group_prerequisite_members (a, t)) { if (p.is_a ()) t.prerequisite_targets.push_back (&p.search ()); @@ -277,10 +277,10 @@ namespace build2 // @@ OUT: what if this is a @-qualified pair or names? // - target* it (in != nullptr ? &search (*in, bs) : nullptr); - target* ot (on != nullptr - ? in == on ? it : &search (*on, bs) - : nullptr); + const target* it (in != nullptr ? &search (*in, bs) : nullptr); + const target* ot (on != nullptr + ? in == on ? it : &search (*on, bs) + : nullptr); if (a.operation () == update_id) { @@ -289,26 +289,16 @@ namespace build2 // if (it != nullptr) { - build2::match (ml, a, *it); - - if (it->unchanged ()) //@@ TM? - { - unmatch (a, *it); + if (build2::match (a, *it, unmatch::unchanged)) it = nullptr; - } } if (ot != nullptr) { if (in != on) { - build2::match (ml, a, *ot); - - if (ot->unchanged ()) //@@ MT? - { - unmatch (a, *ot); + if (build2::match (a, *ot, unmatch::unchanged)) ot = nullptr; - } } else ot = it; @@ -318,7 +308,7 @@ namespace build2 // been found if we signalled that we do not match from match() // above. // - recipe d (match_delegate (ml, a, t, *this).first); + recipe d (match_delegate (a, t, *this).first); // If we have no input/output that needs updating, then simply // redirect to it. @@ -636,8 +626,9 @@ namespace build2 if (pts.size () != 0 && pts[0] != nullptr) { const file& it (pts[0]->as ()); - assert (!it.path ().empty ()); // Should have been assigned by update. - args.push_back (it.path ().string ().c_str ()); + const path& ip (it.path ()); + assert (!ip.empty ()); // Should have been assigned by update. + args.push_back (ip.string ().c_str ()); } // Maybe arguments then? // @@ -656,7 +647,8 @@ namespace build2 if (pts.size () != 0 && pts[1] != nullptr) { const file& ot (pts[1]->as ()); - assert (!ot.path ().empty ()); // Should have been assigned by update. + const path& op (ot.path ()); + assert (!op.empty ()); // Should have been assigned by update. dpp = run_search (dp, true); @@ -668,7 +660,7 @@ namespace build2 if (cast (tt["test.target"]).class_ == "windows") args.push_back ("--strip-trailing-cr"); - args.push_back (ot.path ().string ().c_str ()); + args.push_back (op.string ().c_str ()); args.push_back ("-"); args.push_back (nullptr); } diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx index 2ea42b5..4e6759f 100644 --- a/build2/test/script/parser.cxx +++ b/build2/test/script/parser.cxx @@ -6,8 +6,7 @@ #include -#include // keep_going -#include +#include // sched, keep_going #include #include @@ -2838,16 +2837,14 @@ namespace build2 { exec_lines (g->setup_.begin (), g->setup_.end (), li, false); - scheduler::atomic_count task_count (0); + atomic_count task_count (0); + wait_guard wg (task_count); // 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) + for (unique_ptr& chain: g->scopes) { - unique_ptr& chain (*i); - // Check if this scope is ignored (e.g., via config.test). // if (!runner_->test (*chain)) @@ -2932,12 +2929,18 @@ namespace build2 // exec_scope_body (); // scope_ = os; + // Pass our diagnostics stack (this is safe since we are going + // to wait for completion before unwinding the diag stack). + // // If the scope was executed synchronously, check the status and // bail out if we weren't asked to keep going. // if (!sched.async (task_count, - [] (scope& s, script& scr, runner& r) + [] (scope& s, script& scr, runner& r, + const diag_frame* ds) { + diag_frame df (ds); + try { parser p; @@ -2951,24 +2954,24 @@ namespace build2 }, ref (*chain), ref (*script_), - ref (*runner_))) + ref (*runner_), + diag_frame::stack)) { + // Bail out if the scope has failed and we weren't instructed + // to keep going. + // if (chain->state == scope_state::failed && !keep_going) - { - ++i; - break; - } + throw failed (); } } } - sched.wait (task_count); + + wg.wait (); // Re-examine the scopes we have executed collecting their state. // - for (auto j (g->scopes.begin ()); j != i; ++j) + for (const unique_ptr& chain: g->scopes) { - const unique_ptr& chain (*j); - if (chain == nullptr) continue; diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx index 0586377..c2b13ca 100644 --- a/build2/test/script/script.cxx +++ b/build2/test/script/script.cxx @@ -567,17 +567,17 @@ namespace build2 // if (t != nullptr) { - if (auto* p = t->is_a ()) + if (auto* pt = t->is_a ()) { // Do some sanity checks: the target better be up-to-date with // an assigned path. // - if (p->path ().empty ()) - fail << "target " << *p << " specified in the test variable " + v = pt->path (); + + if (v.empty ()) + fail << "target " << *pt << " specified in the test variable " << "is out of date" << info << "consider specifying it as a prerequisite of " << tt; - - v = p->path (); } else if (t->is_a ()) v = path (t->dir); -- cgit v1.1