aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/name4
-rw-r--r--build2/parser.cxx1
-rw-r--r--build2/test/common8
-rw-r--r--build2/test/init.cxx35
-rw-r--r--build2/variable33
-rw-r--r--build2/variable.cxx78
-rw-r--r--build2/variable.ixx22
-rw-r--r--doc/testscript.cli29
8 files changed, 199 insertions, 11 deletions
diff --git a/build2/name b/build2/name
index 2833a2b..ccceebe 100644
--- a/build2/name
+++ b/build2/name
@@ -147,6 +147,10 @@ namespace build2
inline ostream&
operator<< (ostream& os, const names& ns) {return os << names_view (ns);}
+
+ // Pair of names.
+ //
+ using name_pair = pair<name, name>;
}
#include <build2/name.ixx>
diff --git a/build2/parser.cxx b/build2/parser.cxx
index 412b557..20d7bd2 100644
--- a/build2/parser.cxx
+++ b/build2/parser.cxx
@@ -1556,6 +1556,7 @@ namespace build2
n == "paths" ? ptr (value_traits<paths>::value_type) :
n == "dir_paths" ? ptr (value_traits<dir_paths>::value_type) :
n == "names" ? ptr (value_traits<vector<name>>::value_type) :
+ n == "name_pair" ? ptr (value_traits<name_pair>::value_type) :
nullptr;
}
diff --git a/build2/test/common b/build2/test/common
index 44c7bf8..8e4235b 100644
--- a/build2/test/common
+++ b/build2/test/common
@@ -14,8 +14,16 @@ namespace build2
{
namespace test
{
+ enum class output_before {fail, warn, clean};
+ enum class output_after {clean, keep};
+
struct common
{
+ // The config.test.output values.
+ //
+ output_before before;
+ output_after after;
+
// The config.test query interface.
//
const names* test_ = nullptr; // The config.test value if any.
diff --git a/build2/test/init.cxx b/build2/test/init.cxx
index fd8db99..a367206 100644
--- a/build2/test/init.cxx
+++ b/build2/test/init.cxx
@@ -48,6 +48,11 @@ namespace build2
//
vp.insert ("config.test", true);
+ // Test working directory before/after cleanup (see Testscript spec for
+ // semantics).
+ //
+ vp.insert<name_pair> ("config.test.output", true);
+
// Note: none are overridable.
//
// The test variable is a name which can be a path (with the
@@ -130,6 +135,36 @@ namespace build2
m.root_ = s;
}
+ // config.test.output
+ //
+ if (lookup l = config::omitted (rs, "config.test.output").first)
+ {
+ const name_pair& p (cast<name_pair> (l));
+
+ // If second half is empty, then first is the after value.
+ //
+ const name& a (p.second.empty () ? p.first : p.second); // after
+ const name& b (p.second.empty () ? p.second : p.first); // before
+
+ // Parse and validate.
+ //
+ if (!b.simple ())
+ fail << "invalid config.test.output before value '" << b << "'";
+
+ if (!a.simple ())
+ fail << "invalid config.test.output after value '" << a << "'";
+
+ if (a.value == "clean") m.after = output_after::clean;
+ else if (a.value == "keep") m.after = output_after::keep;
+ else fail << "invalid config.test.output after value '" << a << "'";
+
+ if (b.value == "fail") m.before = output_before::fail;
+ else if (b.value == "warn") m.before = output_before::warn;
+ else if (b.value == "clean") m.before = output_before::clean;
+ else if (b.value == "") m.before = output_before::clean;
+ else fail << "invalid config.test.output before value '" << b << "'";
+ }
+
//@@ TODO: Need ability to specify extra diff options (e.g.,
// --strip-trailing-cr, now hardcoded).
//
diff --git a/build2/variable b/build2/variable
index 690c45d..ea1912a 100644
--- a/build2/variable
+++ b/build2/variable
@@ -233,13 +233,7 @@ namespace build2
// specialization below). Types that don't fit will have to be handled
// with an extra dynamic allocation.
//
- // std::max() is not constexpr on GCC 4.9.
- //
- static constexpr size_t size_ =
- sizeof (names) > sizeof (target_triplet)
- ? sizeof (names)
- : sizeof (target_triplet);
-
+ static constexpr size_t size_ = sizeof (name_pair);
std::aligned_storage<size_>::type data_;
// Make sure we have sufficient storage for untyped values.
@@ -653,7 +647,7 @@ namespace build2
static name convert (name&&, name*);
static void assign (value&, name&&);
static name reverse (const name& x) {return x;}
- static int compare (const name&, const name&);
+ static int compare (const name& l, const name& r) {return l.compare (r);}
static bool empty (const name& x) {return x.empty ();}
static const bool empty_value = true;
@@ -661,6 +655,29 @@ namespace build2
static const build2::value_type value_type;
};
+ // name_pair
+ //
+ // An empty first or second half of a pair is treated as unspecified (this
+ // way it can be usage-specific whether a single value is first or second
+ // half of a pair). If both are empty then this is an empty value (and not a
+ // pair of two empties).
+ //
+ template <>
+ struct value_traits<name_pair>
+ {
+ static_assert (sizeof (name_pair) <= value::size_, "insufficient space");
+
+ static name_pair convert (name&&, name*);
+ static void assign (value&, name_pair&&);
+ static int compare (const name_pair&, const name_pair&);
+ static bool empty (const name_pair& x) {
+ return x.first.empty () && x.second.empty ();}
+
+ static const bool empty_value = true;
+ static const char* const type_name;
+ static const build2::value_type value_type;
+ };
+
// process_path
//
// Note that instances that we store always have non-empty recall and
diff --git a/build2/variable.cxx b/build2/variable.cxx
index 09790b4..f282dc5 100644
--- a/build2/variable.cxx
+++ b/build2/variable.cxx
@@ -723,6 +723,84 @@ namespace build2
&default_empty<name>
};
+ // name_pair
+ //
+ name_pair value_traits<name_pair>::
+ convert (name&& n, name* r)
+ {
+ n.pair = '\0'; // Keep "unpaired" in case r is empty.
+ return name_pair (move (n), r != nullptr ? move (*r) : name ());
+ }
+
+ void
+ name_pair_assign (value& v, names&& ns, const variable* var)
+ {
+ using traits = value_traits<name_pair>;
+
+ size_t n (ns.size ());
+
+ if (n <= 2)
+ {
+ try
+ {
+ traits::assign (
+ v,
+ (n == 0
+ ? name_pair ()
+ : traits::convert (move (ns[0]), n == 2 ? &ns[1] : nullptr)));
+ return;
+ }
+ catch (const invalid_argument&) {} // Fall through.
+ }
+
+ diag_record dr (fail);
+ dr << "invalid name_pair value '" << ns << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ static names_view
+ name_pair_reverse (const value& v, names& ns)
+ {
+ const name_pair& p (v.as<name_pair> ());
+ const name& f (p.first);
+ const name& s (p.second);
+
+ if (f.empty () && s.empty ())
+ return names_view (nullptr, 0);
+
+ if (f.empty ())
+ return names_view (&s, 1);
+
+ if (s.empty ())
+ return names_view (&f, 1);
+
+ ns.push_back (f);
+ ns.back ().pair = '@';
+ ns.push_back (s);
+ return ns;
+ }
+
+ const char* const value_traits<name_pair>::type_name = "name_pair";
+
+ const value_type value_traits<name_pair>::value_type
+ {
+ type_name,
+ sizeof (name_pair),
+ nullptr, // No base.
+ &default_dtor<name_pair>,
+ &default_copy_ctor<name_pair>,
+ &default_copy_assign<name_pair>,
+ &name_pair_assign,
+ nullptr, // Append not supported.
+ nullptr, // Prepend not supported.
+ &name_pair_reverse,
+ nullptr, // No cast (cast data_ directly).
+ &simple_compare<name_pair>,
+ &default_empty<name_pair>
+ };
+
// process_path value
//
process_path value_traits<process_path>::
diff --git a/build2/variable.ixx b/build2/variable.ixx
index dfe5c84..8656a04 100644
--- a/build2/variable.ixx
+++ b/build2/variable.ixx
@@ -490,10 +490,26 @@ namespace build2
new (&v.data_) name (move (x));
}
- inline int value_traits<name>::
- compare (const name& l, const name& r)
+ // name_pair value
+ //
+ inline void value_traits<name_pair>::
+ assign (value& v, name_pair&& x)
{
- return l.compare (r);
+ if (v)
+ v.as<name_pair> () = move (x);
+ else
+ new (&v.data_) name_pair (move (x));
+ }
+
+ inline int value_traits<name_pair>::
+ compare (const name_pair& x, const name_pair& y)
+ {
+ int r (x.first.compare (y.first));
+
+ if (r == 0)
+ r = x.second.compare (y.second);
+
+ return r;
}
// process_path value
diff --git a/doc/testscript.cli b/doc/testscript.cli
index 5b4e8ee..692ff57 100644
--- a/doc/testscript.cli
+++ b/doc/testscript.cli
@@ -1047,6 +1047,35 @@ $ b test config.test=basics/foo # Only foo
$ b test 'config.test=basics/foo basics/fox/bar' # Only foo and bar
\
+The script working directory may exist before the execution (for example,
+because of a failed previous run) or it may be desirable not to clean it up
+after the execution (for example, to examine test setup, output, etc). Before
+the execution the default behavior is to warn and then automatically remove
+the working directory if it exists. After the execution the default behavior
+is to perform all the cleanups and teardowns and then remove the working
+directory failing if it is not empty. This default behaviors can, however, be
+overridden with the \c{config.test.output} variable.
+
+The \c{config.test.output} variable contains a pair of values with the first
+signifying the \i{before} behavior and the second \- \i{after}. The valid
+\i{before} values are \c{fail} (fail if the directory exists), \c{warn}
+(warn if the directory exists then remove), \c{clean} (silently remove
+the existing directory). The valid \i{after} values are \c{clean} (remove
+the directory failing if it is not empty) and \c{keep} (do not run cleanups
+and teardowns and do not remove the working directory). The default behavior
+is thus equivalent to specifying the \c{warn@clean} pair.
+
+If only a single value is specified in \c{config.test.output} then it is
+assumed to be the \i{after} value and the \i{before} value is assumed to
+be \c{clean}. In other words:
+
+\
+$ b test config.test.output=clean # config.test.output=clean@clean
+$ b test config.test.output=keep # config.test.output=clean@keep
+\
+
+Note also that selecting the \c{keep} behavior may result in some test
+failures due to unexpected output to go undetected.
\h1#lexical|Lexical Structure|