From ac838a3c051e8f15514f0454b060d00695b372f7 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 9 Apr 2019 10:33:59 +0200 Subject: Add dry-run support to test rules --- build2/context.hxx | 10 ++++ build2/context.ixx | 25 ++++++++ build2/filesystem.hxx | 3 +- build2/test/rule.cxx | 158 ++++++++++++++++++++++++++++++-------------------- build2/utility.cxx | 9 +++ build2/utility.hxx | 6 ++ 6 files changed, 147 insertions(+), 64 deletions(-) (limited to 'build2') 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 ()) - 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 result; - result.reserve (pts_n - pass_n); // Make sure there are no reallocations. + vector 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 ()) + 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 -- cgit v1.1