diff options
Diffstat (limited to 'build/test')
-rw-r--r-- | build/test/module | 24 | ||||
-rw-r--r-- | build/test/module.cxx | 88 | ||||
-rw-r--r-- | build/test/operation | 18 | ||||
-rw-r--r-- | build/test/operation.cxx | 32 | ||||
-rw-r--r-- | build/test/rule | 30 | ||||
-rw-r--r-- | build/test/rule.cxx | 439 |
6 files changed, 0 insertions, 631 deletions
diff --git a/build/test/module b/build/test/module deleted file mode 100644 index 83c6d60..0000000 --- a/build/test/module +++ /dev/null @@ -1,24 +0,0 @@ -// file : build/test/module -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD_TEST_MODULE -#define BUILD_TEST_MODULE - -#include <build/types> -#include <build/module> - -namespace build -{ - namespace test - { - extern "C" void - test_boot (scope&, const location&, unique_ptr<module>&); - - extern "C" bool - test_init ( - scope&, scope&, const location&, unique_ptr<module>&, bool, bool); - } -} - -#endif // BUILD_TEST_MODULE diff --git a/build/test/module.cxx b/build/test/module.cxx deleted file mode 100644 index 9372103..0000000 --- a/build/test/module.cxx +++ /dev/null @@ -1,88 +0,0 @@ -// file : build/test/module.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <build/test/module> - -#include <build/scope> -#include <build/target> -#include <build/rule> -#include <build/diagnostics> - -#include <build/test/operation> -#include <build/test/rule> - -using namespace std; -using namespace butl; - -namespace build -{ - namespace test - { - static rule rule_; - - extern "C" void - test_boot (scope& root, const location&, unique_ptr<module>&) - { - tracer trace ("test::boot"); - - level5 ([&]{trace << "for " << root.out_path ();}); - - // Register the test operation. - // - root.operations.insert (test_id, test); - } - - extern "C" bool - test_init (scope& root, - scope&, - const location& l, - unique_ptr<module>&, - bool first, - bool) - { - tracer trace ("test::init"); - - if (!first) - { - warn (l) << "multiple test module initializations"; - return true; - } - - const dir_path& out_root (root.out_path ()); - level5 ([&]{trace << "for " << out_root;}); - - // Enter module variables. - // - { - auto& v (var_pool); - - v.find ("test", bool_type); - v.find ("test.input", name_type); - v.find ("test.output", name_type); - v.find ("test.roundtrip", name_type); - v.find ("test.options", strings_type); - v.find ("test.arguments", strings_type); - } - - // Register rules. - // - { - auto& r (root.rules); - - // Register our test running rule. - // - r.insert<target> (perform_test_id, "test", rule_); - - // Register our rule for the dist meta-operation. We need - // to do this because we have "ad-hoc prerequisites" (test - // input/output files) that need to be entered into the - // target list. - // - r.insert<target> (dist_id, test_id, "test", rule_); - } - - return true; - } - } -} diff --git a/build/test/operation b/build/test/operation deleted file mode 100644 index 1942936..0000000 --- a/build/test/operation +++ /dev/null @@ -1,18 +0,0 @@ -// file : build/test/operation -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD_TEST_OPERATION -#define BUILD_TEST_OPERATION - -#include <build/operation> - -namespace build -{ - namespace test - { - extern operation_info test; - } -} - -#endif // BUILD_TEST_OPERATION diff --git a/build/test/operation.cxx b/build/test/operation.cxx deleted file mode 100644 index 40c5b86..0000000 --- a/build/test/operation.cxx +++ /dev/null @@ -1,32 +0,0 @@ -// file : build/test/operation.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <build/test/operation> - -using namespace std; -using namespace butl; - -namespace build -{ - namespace test - { - static operation_id - test_pre (meta_operation_id mo) - { - // Run update as a pre-operation, unless we are disfiguring. - // - return mo != disfigure_id ? update_id : 0; - } - - operation_info test { - "test", - "test", - "testing", - "has nothing to test", // We cannot "be tested". - execution_mode::first, - &test_pre, - nullptr - }; - } -} diff --git a/build/test/rule b/build/test/rule deleted file mode 100644 index 17f32ce..0000000 --- a/build/test/rule +++ /dev/null @@ -1,30 +0,0 @@ -// file : build/test/rule -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD_TEST_RULE -#define BUILD_TEST_RULE - -#include <build/rule> -#include <build/operation> - -namespace build -{ - namespace test - { - class rule: public build::rule - { - public: - virtual match_result - match (action, target&, const std::string&) const; - - virtual recipe - apply (action, target&, const match_result&) const; - - static target_state - perform_test (action, target&); - }; - } -} - -#endif // BUILD_TEST_RULE diff --git a/build/test/rule.cxx b/build/test/rule.cxx deleted file mode 100644 index 3a4c91d..0000000 --- a/build/test/rule.cxx +++ /dev/null @@ -1,439 +0,0 @@ -// file : build/test/rule.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include <build/test/rule> - -#include <butl/process> - -#include <build/scope> -#include <build/target> -#include <build/algorithm> -#include <build/diagnostics> - -#include <build/config/utility> // add_options() - -using namespace std; -using namespace butl; - -namespace build -{ - namespace test - { - match_result rule:: - match (action a, target& t, const std::string&) const - { - // First determine if this is a test. This is controlled by - // the test target variable and text.<tt> scope variables. - // Also, it feels redundant to specify, say, "test = true" - // and "test.output = test.out" -- the latter already says - // this is a test. So take care of that as well. - // - bool r (false); - lookup<const value> l; - - // @@ This logic doesn't take into account target type/pattern- - // specific variables. - // - // @@ Perhaps a find_any(<list-of-vars>)? - // - for (auto p (t.vars.find_namespace ("test")); - p.first != p.second; - ++p.first) - { - const variable& var (p.first->first); - const value& val (p.first->second); - - // If we have test, then always use that. - // - if (var.name == "test") - { - l = lookup<const value> (val, t); - break; - } - - // Otherwise check for variables that would indicate this - // is a test. - // - if (var.name == "test.input" || - var.name == "test.output" || - var.name == "test.roundtrip" || - var.name == "test.options" || - var.name == "test.arguments") - { - r = true; - break; - } - } - - if (!r) - { - // See if there is a scope variable. - // - if (!l.defined ()) - l = t.base_scope ()[ - var_pool.find (string("test.") + t.type ().name, bool_type)]; - - r = l && as<bool> (*l); - } - - // If this is the update pre-operation, then all we really need to - // do is say we are not a match and the standard matching machinery - // will (hopefully) find the rule to update this target. - // - // There is one thing that compilates this simple approach: test - // input/output. While normally they will be existing (in src_base) - // files, they could also be auto-generated. In fact, they could - // only be needed for testing, which means the normall update won't - // even know about them (nor clean, for that matter; this is why we - // need cleantest). - // - // To make generated input/output work we will have to cause their - // update ourselves. I other words, we may have to do some actual - // work for (update, test), and not simply "guide" (update, 0) as - // to which targets need updating. For how exactly we are going to - // do it, see apply() below. - // - match_result mr (t, r); - - // If this is the update pre-operation, change the recipe action - // to (update, 0) (i.e., "unconditional update"). - // - if (r && a.operation () == update_id) - mr.recipe_action = action (a.meta_operation (), update_id); - - return mr; - } - - recipe rule:: - apply (action a, target& t, const match_result& mr) const - { - tracer trace ("test::rule::apply"); - - if (!mr.bvalue) // Not a test. - return noop_recipe; - - // Ok, if we are here, then this means: - // - // 1. This target is a test. - // 2. The action is either - // a. (perform, test, 0) or - // b. (*, update, install) - // - // In both cases, the next step is to see if we have test.{input, - // output,roundtrip}. - // - - // First check the target-specific vars since they override any - // scope ones. - // - auto il (t.vars["test.input"]); - auto ol (t.vars["test.output"]); - auto rl (t.vars["test.roundtrip"]); - auto al (t.vars["test.arguments"]); // Should be input or arguments. - - if (al) - { - if (il) - fail << "both test.input and test.arguments specified for " - << "target " << t; - - if (rl) - fail << "both test.roundtrip and test.arguments specified for " - << "target " << t; - } - - scope& bs (t.base_scope ()); - - if (!il && !ol && !rl) - { - string n ("test."); - n += t.type ().name; - - const variable& in (var_pool.find (n + ".input", name_type)); - const variable& on (var_pool.find (n + ".output", name_type)); - const variable& rn (var_pool.find (n + ".roundtrip", name_type)); - - // We should only keep value(s) that were specified together - // in the innermost scope. - // - // @@ Shouldn't we stop at project root? - // - for (scope* s (&bs); s != nullptr; s = s->parent_scope ()) - { - ol = s->vars[on]; - - if (!al) // Not overriden at target level by test.arguments? - { - il = s->vars[in]; - rl = s->vars[rn]; - } - - if (il || ol || rl) - break; - } - } - - const name* in; - const name* on; - - // Reduce the roundtrip case to input/output. - // - if (rl) - { - if (il || ol) - fail << "both test.roundtrip and test.input/output specified " - << "for target " << t; - - in = on = &as<name> (*rl); - } - else - { - in = il ? &as<name> (*il) : nullptr; - on = ol ? &as<name> (*ol) : nullptr; - } - - // Resolve them to targets, which normally would be existing files - // but could also be targets that need updating. - // - target* it (in != nullptr ? &search (*in, bs) : nullptr); - target* ot (on != nullptr ? in == on ? it : &search (*on, bs) : nullptr); - - if (a.operation () == update_id) - { - // First see if input/output are existing, up-to-date files. This - // is a common case optimization. - // - if (it != nullptr) - { - build::match (a, *it); - - if (it->state () == target_state::unchanged) - { - unmatch (a, *it); - it = nullptr; - } - } - - if (ot != nullptr) - { - if (in != on) - { - build::match (a, *ot); - - if (ot->state () == target_state::unchanged) - { - unmatch (a, *ot); - ot = nullptr; - } - } - else - ot = it; - } - - - // Find the "real" update rule, that is, the rule that would - // have been found if we signalled that we do not match from - // match() above. - // - recipe d (match_delegate (a, t).first); - - // If we have no input/output that needs updating, then simply - // redirect to it. - // - if (it == nullptr && ot == nullptr) - return d; - - // Ok, time to handle the worst case scenario: we need to - // cause update of input/output targets and also delegate - // to the real update. - // - return [it, ot, dr = move (d)] (action a, target& t) -> target_state - { - // Do the general update first. - // - target_state r (execute_delegate (dr, a, t)); - - if (it != nullptr) - r |= execute (a, *it); - - if (ot != nullptr) - r |= execute (a, *ot); - - return r; - }; - } - else - { - // Cache the targets in our prerequsite targets lists where they - // can be found by perform_test(). If we have either or both, - // then the first entry is input and the second -- output (either - // can be NULL). - // - if (it != nullptr || ot != nullptr) - { - auto& pts (t.prerequisite_targets); - pts.resize (2, nullptr); - pts[0] = it; - pts[1] = ot; - } - - return &perform_test; - } - } - - static void - add_arguments (cstrings& args, const target& t, const char* n) - { - string var ("test."); - var += n; - - auto l (t.vars[var]); - - if (!l) - { - var.resize (5); - var += t.type ().name; - var += '.'; - var += n; - l = t.base_scope ()[var_pool.find (var, strings_type)]; - } - - if (l) - config::append_options (args, as<strings> (*l)); - } - - // The format of args shall be: - // - // name1 arg arg ... nullptr - // name2 arg arg ... nullptr - // ... - // nameN arg arg ... nullptr nullptr - // - static bool - run_test (target& t, - diag_record& dr, - char const** args, - process* prev = nullptr) - { - // Find the next process, if any. - // - char const** next (args); - for (next++; *next != nullptr; next++) ; - next++; - - // Redirect stdout to a pipe unless we are last, in which - // case redirect it to stderr. - // - int out (*next == nullptr ? 2 : -1); - bool pr, wr; - - try - { - if (prev == nullptr) - { - // First process. - // - process p (args, 0, out); - pr = *next == nullptr || run_test (t, dr, next, &p); - wr = p.wait (); - } - else - { - // Next process. - // - process p (args, *prev, out); - pr = *next == nullptr || run_test (t, dr, next, &p); - wr = p.wait (); - } - } - catch (const process_error& e) - { - error << "unable to execute " << args[0] << ": " << e.what (); - - if (e.child ()) - exit (1); - - throw failed (); - } - - if (!wr) - { - if (pr) // First failure? - dr << fail << "test " << t << " failed"; // Multi test: test 1. - - dr << error << "non-zero exit status: "; - print_process (dr, args); - } - - return pr && wr; - } - - target_state rule:: - perform_test (action, target& t) - { - // @@ Would be nice to print what signal/core was dumped. - // - // @@ Doesn't have to be a file target if we have test.cmd. - // - - file& ft (static_cast<file&> (t)); - assert (!ft.path ().empty ()); // Should have been assigned by update. - - cstrings args {ft.path ().string ().c_str ()}; - - // Do we have options? - // - add_arguments (args, t, "options"); - - // Do we have input? - // - auto& pts (t.prerequisite_targets); - if (pts.size () != 0 && pts[0] != nullptr) - { - file& it (static_cast<file&> (*pts[0])); - assert (!it.path ().empty ()); // Should have been assigned by update. - args.push_back (it.path ().string ().c_str ()); - } - // Maybe arguments then? - // - else - add_arguments (args, t, "arguments"); - - args.push_back (nullptr); - - // Do we have output? - // - if (pts.size () != 0 && pts[1] != nullptr) - { - file& ot (static_cast<file&> (*pts[1])); - assert (!ot.path ().empty ()); // Should have been assigned by update. - - args.push_back ("diff"); - args.push_back ("-u"); - args.push_back (ot.path ().string ().c_str ()); - args.push_back ("-"); - args.push_back (nullptr); - } - - args.push_back (nullptr); // Second. - - if (verb >= 2) - print_process (args); - else if (verb) - text << "test " << t; - - { - diag_record dr; - - if (!run_test (t, dr, args.data ())) - { - dr << info << "test command line: "; - print_process (dr, args); - } - } - - return target_state::changed; - } - } -} |