aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2019-04-09 10:33:59 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2019-04-09 10:33:59 +0200
commitac838a3c051e8f15514f0454b060d00695b372f7 (patch)
tree368cbd8f885c25f5e5fc4c5f914434ef2839fa8e
parent79a760af92146a63e337c4399ec57134f56d8886 (diff)
Add dry-run support to test rules
-rw-r--r--build2/context.hxx10
-rw-r--r--build2/context.ixx25
-rw-r--r--build2/filesystem.hxx3
-rw-r--r--build2/test/rule.cxx158
-rw-r--r--build2/utility.cxx9
-rw-r--r--build2/utility.hxx6
6 files changed, 147 insertions, 64 deletions
diff --git a/build2/context.hxx b/build2/context.hxx
index 567786d..a8d6833 100644
--- a/build2/context.hxx
+++ b/build2/context.hxx
@@ -240,6 +240,8 @@ namespace build2
{
~wait_guard () noexcept (false);
+ wait_guard (); // Empty.
+
explicit
wait_guard (atomic_count& task_count,
bool phase = false);
@@ -251,6 +253,14 @@ namespace build2
void
wait ();
+ // Note: move-assignable to empty only.
+ //
+ wait_guard (wait_guard&&);
+ wait_guard& operator= (wait_guard&&);
+
+ wait_guard (const wait_guard&) = delete;
+ wait_guard& operator= (const wait_guard&) = delete;
+
size_t start_count;
atomic_count* task_count;
bool phase;
diff --git a/build2/context.ixx b/build2/context.ixx
index 62f3d81..1c25922 100644
--- a/build2/context.ixx
+++ b/build2/context.ixx
@@ -7,6 +7,12 @@ namespace build2
// wait_guard
//
inline wait_guard::
+ wait_guard ()
+ : start_count (0), task_count (nullptr), phase (false)
+ {
+ }
+
+ inline wait_guard::
wait_guard (atomic_count& tc, bool p)
: wait_guard (0, tc, p)
{
@@ -25,6 +31,25 @@ namespace build2
wait ();
}
+ inline wait_guard::
+ wait_guard (wait_guard&& x)
+ : start_count (x.start_count), task_count (x.task_count), phase (x.phase)
+ {
+ x.task_count = nullptr;
+ }
+
+ inline wait_guard& wait_guard::
+ operator= (wait_guard&& x)
+ {
+ if (&x != this)
+ {
+ assert (task_count == nullptr);
+ start_count = x.start_count; task_count = x.task_count; phase = x.phase;
+ x.task_count = nullptr;
+ }
+ return *this;
+ }
+
inline void wait_guard::
wait ()
{
diff --git a/build2/filesystem.hxx b/build2/filesystem.hxx
index 91ee418..2ba928c 100644
--- a/build2/filesystem.hxx
+++ b/build2/filesystem.hxx
@@ -155,7 +155,8 @@ namespace build2
// Directories containing .buildignore (or .build2ignore in the alternative
// naming scheme) file are automatically ignored by recursive name patterns.
- // For now the file is just a marker and its contents don't matter.
+ // For now the file is just a marker and its contents don't matter. Note
+ // that these functions ignore dry-run.
// Create a directory containing an empty .buildignore file.
//
diff --git a/build2/test/rule.cxx b/build2/test/rule.cxx
index 941609d..8e6c92b 100644
--- a/build2/test/rule.cxx
+++ b/build2/test/rule.cxx
@@ -360,15 +360,6 @@ namespace build2
try
{
- if (verb)
- {
- diag_record dr (text);
- dr << "test " << ts;
-
- if (!t.is_a<alias> ())
- dr << ' ' << t;
- }
-
build2::test::script::script s (t, ts, wd);
{
@@ -495,14 +486,17 @@ namespace build2
// Start asynchronous execution of the testscripts.
//
- wait_guard wg (target::count_busy (), t[a].task_count);
+ wait_guard wg;
+
+ if (!dry_run)
+ wg = wait_guard (target::count_busy (), t[a].task_count);
// Result vector.
//
using script::scope_state;
- vector<scope_state> result;
- result.reserve (pts_n - pass_n); // Make sure there are no reallocations.
+ vector<scope_state> res;
+ res.reserve (pts_n - pass_n); // Make sure there are no reallocations.
for (size_t i (pass_n); i != pts_n; ++i)
{
@@ -514,47 +508,69 @@ namespace build2
//
if (one || test (t, path (ts.name)))
{
- if (mk)
+ // Because the creation of the output directory is shared between us
+ // and the script implementation (plus the fact that we actually
+ // don't clean the existing one), we are going to ignore it for
+ // dry-run.
+ //
+ if (!dry_run)
{
- mkdir_buildignore (wd, buildignore_file, 2);
- mk = false;
+ if (mk)
+ {
+ mkdir_buildignore (wd, buildignore_file, 2);
+ mk = false;
+ }
}
- result.push_back (scope_state::unknown);
- scope_state& r (result.back ());
-
- if (!sched.async (target::count_busy (),
- t[a].task_count,
- [this] (const diag_frame* ds,
- scope_state& r,
- const target& t,
- const testscript& ts,
- const dir_path& wd)
- {
- diag_frame::stack_guard dsg (ds);
- r = perform_script_impl (t, ts, wd, *this);
- },
- diag_frame::stack,
- ref (r),
- cref (t),
- cref (ts),
- cref (wd)))
+ if (verb)
{
- // Executed synchronously. If failed and we were not asked to keep
- // going, bail out.
- //
- if (r == scope_state::failed && !keep_going)
- break;
+ diag_record dr (text);
+ dr << "test " << ts;
+
+ if (!t.is_a<alias> ())
+ dr << ' ' << t;
+ }
+
+ res.push_back (dry_run ? scope_state::passed : scope_state::unknown);
+
+ if (!dry_run)
+ {
+ scope_state& r (res.back ());
+
+ if (!sched.async (target::count_busy (),
+ t[a].task_count,
+ [this] (const diag_frame* ds,
+ scope_state& r,
+ const target& t,
+ const testscript& ts,
+ const dir_path& wd)
+ {
+ diag_frame::stack_guard dsg (ds);
+ r = perform_script_impl (t, ts, wd, *this);
+ },
+ diag_frame::stack,
+ ref (r),
+ cref (t),
+ cref (ts),
+ cref (wd)))
+ {
+ // Executed synchronously. If failed and we were not asked to
+ // keep going, bail out.
+ //
+ if (r == scope_state::failed && !keep_going)
+ break;
+ }
}
}
}
- wg.wait ();
+ if (!dry_run)
+ wg.wait ();
// Re-examine.
//
bool bad (false);
- for (scope_state r: result)
+ for (scope_state r: res)
{
switch (r)
{
@@ -569,18 +585,24 @@ namespace build2
// Cleanup.
//
- if (!bad && !one && !mk && after == output_after::clean)
+ if (!dry_run)
{
- if (!empty_buildignore (wd, buildignore_file))
- fail << "working directory " << wd << " is not empty at the "
- << "end of the test";
+ if (!bad && !one && !mk && after == output_after::clean)
+ {
+ if (!empty_buildignore (wd, buildignore_file))
+ fail << "working directory " << wd << " is not empty at the "
+ << "end of the test";
- rmdir_buildignore (wd, buildignore_file, 2);
+ rmdir_buildignore (wd, buildignore_file, 2);
+ }
}
// Backlink if the working directory exists.
//
- if (!bl.empty () && exists (wd))
+ // If we dry-run then presumably all tests passed and we shouldn't
+ // have anything left unless we are keeping the output.
+ //
+ if (!bl.empty () && (dry_run ? after == output_after::keep : exists (wd)))
update_backlink (wd, bl, true /* changed */);
if (bad)
@@ -752,13 +774,16 @@ namespace build2
const path& ip (it.path ());
assert (!ip.empty ()); // Should have been assigned by update.
- try
- {
- cat.in_ofd = fdopen (ip, fdopen_mode::in);
- }
- catch (const io_error& e)
+ if (!dry_run)
{
- fail << "unable to open " << ip << ": " << e;
+ try
+ {
+ cat.in_ofd = fdopen (ip, fdopen_mode::in);
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to open " << ip << ": " << e;
+ }
}
// Purely for diagnostics.
@@ -768,8 +793,12 @@ namespace build2
args.push_back (nullptr);
}
- process_path pp (run_search (p, true /* init */));
- args.push_back (pp.recall_string ());
+ // If dry-run, the target may not exist.
+ //
+ process_path pp (!dry_run
+ ? run_search (p, true /* init */)
+ : try_run_search (p, true));
+ args.push_back (pp.empty () ? p.string ().c_str () : pp.recall_string ());
// Do we have options and/or arguments?
//
@@ -831,15 +860,18 @@ namespace build2
else if (verb)
text << "test " << tt;
- diag_record dr;
- if (!run_test (tt,
- dr,
- args.data () + (sin ? 3 : 0), // Skip cat.
- sin ? &cat : nullptr))
+ if (!dry_run)
{
- dr << info << "test command line: ";
- print_process (dr, args);
- dr << endf; // return
+ diag_record dr;
+ if (!run_test (tt,
+ dr,
+ args.data () + (sin ? 3 : 0), // Skip cat.
+ sin ? &cat : nullptr))
+ {
+ dr << info << "test command line: ";
+ print_process (dr, args);
+ dr << endf; // return
+ }
}
return target_state::changed;
diff --git a/build2/utility.cxx b/build2/utility.cxx
index af34175..91aecdf 100644
--- a/build2/utility.cxx
+++ b/build2/utility.cxx
@@ -173,6 +173,15 @@ namespace build2
fail (l) << "unable to execute " << f << ": " << e << endf;
}
+ process_path
+ try_run_search (const path& f,
+ bool init,
+ const dir_path& fallback,
+ bool path_only)
+ {
+ return process::try_path_search (f, init, fallback, path_only);
+ }
+
process
run_start (uint16_t verbosity,
const process_env& pe,
diff --git a/build2/utility.hxx b/build2/utility.hxx
index fac1ca8..41020ac 100644
--- a/build2/utility.hxx
+++ b/build2/utility.hxx
@@ -167,6 +167,12 @@ namespace build2
bool path_only = false,
const location& = location ());
+ process_path
+ try_run_search (const path&,
+ bool init = false,
+ const dir_path& fallback = dir_path (),
+ bool path_only = false);
+
// Wait for process termination. Issue diagnostics and throw failed in case
// of abnormal termination. If the process has terminated normally but with
// a non-zero exit status, then, if error is true, assume the diagnostics