aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-07-23 09:59:52 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-07-23 09:59:52 +0200
commit3c57a25a4d6a80301ece82ab33f1394e34f8b873 (patch)
tree2ff64ebcfab74f90e81fdd2963fb653630d6d17b /build
parent34e5a2da18f76c7d7de79a5c12b0e85ee89c4095 (diff)
Basic test support
Diffstat (limited to 'build')
-rw-r--r--build/cli/module.cxx2
-rw-r--r--build/config/operation.cxx6
-rw-r--r--build/cxx/compile.cxx2
-rw-r--r--build/cxx/link.cxx2
-rw-r--r--build/cxx/module.cxx2
-rw-r--r--build/diagnostics19
-rw-r--r--build/diagnostics.cxx41
-rw-r--r--build/operation15
-rw-r--r--build/operation.cxx8
-rw-r--r--build/test/operation.cxx2
-rw-r--r--build/test/rule3
-rw-r--r--build/test/rule.cxx111
12 files changed, 172 insertions, 41 deletions
diff --git a/build/cli/module.cxx b/build/cli/module.cxx
index c1d0aab..f112cd4 100644
--- a/build/cli/module.cxx
+++ b/build/cli/module.cxx
@@ -102,7 +102,7 @@ namespace build
string ver;
try
{
- process pr (args, false, false, true);
+ process pr (args, 0, -1); // Open pipe to stdout.
ifdstream is (pr.in_ofd);
// The version should be the last word on the first line.
diff --git a/build/config/operation.cxx b/build/config/operation.cxx
index a79ddbf..a602325 100644
--- a/build/config/operation.cxx
+++ b/build/config/operation.cxx
@@ -211,7 +211,7 @@ namespace build
"configure",
"configure",
"configuring",
- "configured",
+ "is configured",
nullptr, // meta-operation pre
&configure_operation_pre,
&load, // normal load
@@ -381,7 +381,7 @@ namespace build
targets.insert (
dir::static_type, root.path (), "", nullptr, trace).first);
- info << diag_already_done (a, t);
+ info << diag_done (a, t);
}
}
}
@@ -402,7 +402,7 @@ namespace build
"disfigure",
"disfigure",
"disfiguring",
- "disfigured",
+ "is disfigured",
nullptr, // meta-operation pre
&disfigure_operation_pre,
&disfigure_load,
diff --git a/build/cxx/compile.cxx b/build/cxx/compile.cxx
index d0e6526..a12650f 100644
--- a/build/cxx/compile.cxx
+++ b/build/cxx/compile.cxx
@@ -459,7 +459,7 @@ namespace build
try
{
- process pr (args.data (), false, false, true);
+ process pr (args.data (), 0, -1); // Open pipe to stdout.
ifdstream is (pr.in_ofd);
size_t skip (skip_count);
diff --git a/build/cxx/link.cxx b/build/cxx/link.cxx
index b35a596..145a085 100644
--- a/build/cxx/link.cxx
+++ b/build/cxx/link.cxx
@@ -127,7 +127,7 @@ namespace build
string l;
try
{
- process pr (args.data (), false, false, true);
+ process pr (args.data (), 0, -1); // Open pipe to stdout.
ifdstream is (pr.in_ofd);
while (!is.eof ())
diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx
index 3975ac5..882d4b0 100644
--- a/build/cxx/module.cxx
+++ b/build/cxx/module.cxx
@@ -110,7 +110,7 @@ namespace build
string ver;
try
{
- process pr (args, false, false, true);
+ process pr (args, 0, -1); // Open pipe to stdout.
ifdstream is (pr.in_ofd);
bool r (getline (is, ver));
diff --git a/build/diagnostics b/build/diagnostics
index 3f05688..2eaaa84 100644
--- a/build/diagnostics
+++ b/build/diagnostics
@@ -5,6 +5,7 @@
#ifndef BUILD_DIAGNOSTICS
#define BUILD_DIAGNOSTICS
+#include <cstddef> // size_t
#include <cstdint>
#include <utility>
#include <cassert>
@@ -52,7 +53,7 @@ namespace build
// Action phrases, e.g., "configure update exe{foo}", "updating exe{foo}",
- // and "updating exe{foo} already configured".
+ // and "updating exe{foo} is configured".
//
struct action;
class target;
@@ -64,17 +65,25 @@ namespace build
diag_doing (const action&, const target&);
std::string
- diag_already_done (const action&, const target&);
+ diag_done (const action&, const target&);
- // Print process commmand line.
+ // Print process commmand line. If the number of elements is specified
+ // (or the second version is used), then it will print the piped multi-
+ // process command line, if present. In this case, the expected format
+ // is as follows:
+ //
+ // name1 arg arg ... nullptr
+ // name2 arg arg ... nullptr
+ // ...
+ // nameN arg arg ... nullptr nullptr
//
void
- print_process (const char* const* args);
+ print_process (const char* const* args, std::size_t n = 0);
inline void
print_process (const cstrings& args)
{
- print_process (args.data ());
+ print_process (args.data (), args.size ());
}
// Trace verbosity level.
diff --git a/build/diagnostics.cxx b/build/diagnostics.cxx
index f6d81bf..f12119c 100644
--- a/build/diagnostics.cxx
+++ b/build/diagnostics.cxx
@@ -128,44 +128,59 @@ namespace build
}
string
- diag_already_done (const action&, const target& t)
+ diag_done (const action&, const target& t)
{
const meta_operation_info& mi (*current_mif);
const operation_info& oi (*current_oif);
ostringstream os;
- // perform(update(x)) -> "x is already up to date"
- // configure(update(x)) -> "updating x is already configured"
+ // perform(update(x)) -> "x is up to date"
+ // configure(update(x)) -> "updating x is configured"
//
- if (mi.name_already_done.empty ())
+ if (mi.name_done.empty ())
{
os << t;
- if (!oi.name_already_done.empty ())
- os << " is already " << oi.name_already_done;
+ if (!oi.name_done.empty ())
+ os << " " << oi.name_done;
}
else
{
if (!oi.name_doing.empty ())
os << oi.name_doing << ' ';
- os << t << " is already " << mi.name_already_done;
+ os << t << " " << mi.name_done;
}
return os.str ();
}
void
- print_process (const char* const* args)
+ print_process (const char* const* args, size_t n)
{
diag_record r (text);
- for (const char* const* p (args); *p != nullptr; p++)
- r << (p != args ? " " : "")
- << (**p == '\0' ? "\"" : "") // Quote empty arguments.
- << *p
- << (**p == '\0' ? "\"" : "");
+ size_t m (0);
+ const char* const* p (args);
+ do
+ {
+ if (m != 0)
+ r << " |"; // Trailing space will be added inside the loop.
+
+ for (m++; *p != nullptr; p++, m++)
+ r << (p != args ? " " : "")
+ << (**p == '\0' ? "\"" : "") // Quote empty arguments.
+ << *p
+ << (**p == '\0' ? "\"" : "");
+
+ if (m < n) // Can we examine the next element?
+ {
+ p++;
+ m++;
+ }
+
+ } while (*p != nullptr);
}
// Trace verbosity level.
diff --git a/build/operation b/build/operation
index 6fde9fd..55f901d 100644
--- a/build/operation
+++ b/build/operation
@@ -56,7 +56,7 @@ namespace build
// If this is not a nested operation, then outer should be 0.
//
- action (meta_operation_id m, operation_id inner, operation_id outer)
+ action (meta_operation_id m, operation_id inner, operation_id outer = 0)
: inner_id ((m << 4) | inner),
outer_id (outer == 0 ? 0 : (m << 4) | outer) {}
@@ -91,16 +91,13 @@ namespace build
(x.outer_id != y.outer_id && y.outer_id != 0);
}
- /*
+ // Note that these ignore the outer operation.
+ //
inline bool
- operator== (action x, action y)
- {
- return x.inner_id == y.inner_id && x.outer_id == y.outer_id;
- }
+ operator== (action x, action y) {return x.inner_id == y.inner_id;}
inline bool
operator!= (action x, action y) {return !(x == y);}
- */
std::ostream&
operator<< (std::ostream&, action);
@@ -179,7 +176,7 @@ namespace build
//
const std::string name_do; // E.g., [to] 'configure'.
const std::string name_doing; // E.g., [while] 'configuring'.
- const std::string name_already_done; // E.g., [already] 'configured'.
+ const std::string name_done; // E.g., 'is configured'.
// If operation_pre() is not NULL, then it may translate default_id
// (and only default_id) to some other operation. If not translated,
@@ -261,7 +258,7 @@ namespace build
//
const std::string name_do; // E.g., [to] 'update'.
const std::string name_doing; // E.g., [while] 'updating'.
- const std::string name_already_done; // E.g., [already] 'up to date'.
+ const std::string name_done; // E.g., 'is up to date'.
const execution_mode mode;
diff --git a/build/operation.cxx b/build/operation.cxx
index 61111fe..9bfa8d2 100644
--- a/build/operation.cxx
+++ b/build/operation.cxx
@@ -139,7 +139,7 @@ namespace build
// Be quiet in pre/post operations.
//
if (a.outer_operation () == 0)
- info << diag_already_done (a, t);
+ info << diag_done (a, t);
break;
}
case target_state::changed:
@@ -176,7 +176,7 @@ namespace build
// Be quiet in pre/post operations.
//
if (a.outer_operation () == 0)
- info << diag_already_done (a, t);
+ info << diag_done (a, t);
break;
}
case target_state::unknown: // Assume something was done to it.
@@ -221,7 +221,7 @@ namespace build
"update",
"update",
"updating",
- "up to date",
+ "is up to date",
execution_mode::first,
nullptr,
nullptr
@@ -231,7 +231,7 @@ namespace build
"clean",
"clean",
"cleaning",
- "clean",
+ "is clean",
execution_mode::last,
nullptr,
nullptr
diff --git a/build/test/operation.cxx b/build/test/operation.cxx
index 75e3e80..da98e7d 100644
--- a/build/test/operation.cxx
+++ b/build/test/operation.cxx
@@ -25,7 +25,7 @@ namespace build
"test",
"test",
"testing",
- "tested",
+ "has nothing to test", // We cannot "be tested".
execution_mode::first,
&test_pre,
nullptr
diff --git a/build/test/rule b/build/test/rule
index db10606..259b72b 100644
--- a/build/test/rule
+++ b/build/test/rule
@@ -23,6 +23,9 @@ namespace build
virtual recipe
apply (action, target&, const match_result&) const;
+ static target_state
+ perform_test (action, target&);
+
private:
operation_id test_id;
};
diff --git a/build/test/rule.cxx b/build/test/rule.cxx
index 3fd3a16..91ddb4f 100644
--- a/build/test/rule.cxx
+++ b/build/test/rule.cxx
@@ -4,12 +4,16 @@
#include <build/test/rule>
+#include <butl/process>
+#include <butl/fdstream>
+
#include <build/scope>
#include <build/target>
#include <build/algorithm>
#include <build/diagnostics>
using namespace std;
+using namespace butl;
namespace build
{
@@ -38,12 +42,115 @@ namespace build
}
recipe rule::
- apply (action, target&, const match_result& mr) const
+ apply (action a, target&, const match_result& mr) const
{
if (!mr.value) // Not a test.
return noop_recipe;
- return noop_recipe; //@@ TMP
+ return a == action (perform_id, test_id)
+ ? &perform_test
+ : noop_recipe; // Don't do anything for other meta-operations.
+ }
+
+ // The format of args shall be:
+ //
+ // name1 arg arg ... nullptr
+ // name2 arg arg ... nullptr
+ // ...
+ // nameN arg arg ... nullptr nullptr
+ //
+ static bool
+ pipe_process (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;
+
+ if (prev == nullptr)
+ {
+ // First process.
+ //
+ process p (args, 0, out);
+ pr = *next == nullptr || pipe_process (next, &p);
+ wr = p.wait ();
+ }
+ else
+ {
+ // Next process.
+ //
+ process p (args, *prev, out);
+ pr = *next == nullptr || pipe_process (next, &p);
+ wr = p.wait ();
+ }
+
+ if (!wr)
+ {
+ // @@ Needs to go into the same diag record.
+ //
+ error << "non-zero exit status from:";
+ print_process (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 (), nullptr};
+
+ args.push_back ("diff");
+ args.push_back ("-u");
+ args.push_back ("test.std");
+ args.push_back ("-");
+ args.push_back (nullptr);
+
+ args.push_back (nullptr); // Second.
+
+ if (verb)
+ print_process (args);
+ else
+ text << "test " << t;
+
+ try
+ {
+ if (!pipe_process (args.data ()))
+ {
+ //@@ Need to use the same diag record.
+ //
+ error << "failed test:";
+ print_process (args);
+ throw failed ();
+ }
+
+ return target_state::changed;
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << args[0] << ": " << e.what ();
+
+ if (e.child ())
+ exit (1);
+
+ throw failed ();
+ }
}
}
}