aboutsummaryrefslogtreecommitdiff
path: root/build2/operation.cxx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2017-02-10 08:15:48 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2017-02-13 12:42:42 +0200
commitabccaf9596461215fce0e32322133fb6c39be44f (patch)
tree3fc16a6e6142d65e6b47ae686ab845cc164478cc /build2/operation.cxx
parentbcb2a89e111a918a48a132a2a29e0c26d724591d (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/operation.cxx')
-rw-r--r--build2/operation.cxx79
1 files changed, 65 insertions, 14 deletions
diff --git a/build2/operation.cxx b/build2/operation.cxx
index ea9a462..c4366f7 100644
--- a/build2/operation.cxx
+++ b/build2/operation.cxx
@@ -135,47 +135,90 @@ namespace build2
phase_guard pg (run_phase::execute);
+ // Tune the scheduler.
+ //
+ switch (current_inner_oif->concurrency)
+ {
+ case 0: sched.tune (1); break; // Run serially.
+ case 1: break; // Run as is.
+ default: assert (false); // Not yet supported.
+ }
+
// Similar logic to execute_members(): first start asynchronous execution
// of all the top-level targets.
//
atomic_count task_count (0);
- for (const void* v: ts)
+
+ size_t n (ts.size ());
+ for (size_t i (0); i != n; ++i)
{
- const target& t (*static_cast<const target*> (v));
+ const target& t (*static_cast<const target*> (ts[i]));
l5 ([&]{trace << diag_doing (a, t);});
- execute_async (a, t, 0, task_count);
+ target_state s (execute_async (a, t, 0, task_count));
+
+ if (s == target_state::failed && !keep_going)
+ break;
}
sched.wait (task_count);
+ sched.tune (0); // Restore original scheduler settings.
- // We are now running serially and we should have executed every target
- // that we matched.
+ // We are now running serially. Re-examine them all.
//
- assert (dependency_count == 0);
-
- for (const void* v: ts)
+ bool fail (false);
+ for (size_t i (0); i != n; ++i)
{
- const target& t (*static_cast<const target*> (v));
+ const target& t (*static_cast<const target*> (ts[i]));
- switch (t.synchronized_state ())
+ switch (t.synchronized_state (false))
{
+ case target_state::unknown:
+ {
+ // We bailed before executing it.
+ //
+ if (!quiet)
+ info << "not " << diag_did (a, t);
+
+ break;
+ }
case target_state::unchanged:
{
+ // Nothing had to be done.
+ //
if (!quiet)
info << diag_done (a, t);
+
break;
}
case target_state::changed:
- break;
+ {
+ // Something has been done.
+ //
+ break;
+ }
case target_state::failed:
- //@@ MT: This could probably happen in a parallel build.
- // Or does state() throw? Do we want to print?
- break;
+ {
+ // Things didn't go well for this target.
+ //
+ if (!quiet)
+ info << "failed to " << diag_do (a, t);
+
+ fail = true;
+ break;
+ }
default:
assert (false);
}
}
+
+ if (fail)
+ throw failed ();
+
+ // We should have executed every target that we matched, provided we
+ // haven't failed (in which case we could have bailed out early).
+ //
+ assert (dependency_count == 0);
}
const meta_operation_info noop {
@@ -184,6 +227,7 @@ namespace build2
"", // Presumably we will never need these since we are not going
"", // to do anything.
"",
+ "",
nullptr, // meta-operation pre
nullptr, // operation pre
&load,
@@ -200,6 +244,7 @@ namespace build2
"",
"",
"",
+ "",
nullptr, // meta-operation pre
nullptr, // operation pre
&load,
@@ -218,7 +263,9 @@ namespace build2
"",
"",
"",
+ "",
execution_mode::first,
+ 1,
nullptr,
nullptr
};
@@ -228,8 +275,10 @@ namespace build2
"update",
"update",
"updating",
+ "updated",
"is up to date",
execution_mode::first,
+ 1,
nullptr,
nullptr
};
@@ -239,8 +288,10 @@ namespace build2
"clean",
"clean",
"cleaning",
+ "cleaned",
"is clean",
execution_mode::last,
+ 1,
nullptr,
nullptr
};