aboutsummaryrefslogtreecommitdiff
path: root/libbbot
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-12-07 15:29:59 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-12-11 14:03:09 +0300
commitb740417add06a6df0dff65b60fbd92a8c8d95aab (patch)
treee07e9a6e7c2f39164ff8bba7b6ee933679bf99aa /libbbot
parent1d977345398584ff774990cf436289ddd64d2b42 (diff)
Add support for build configuration class inheritance
Diffstat (limited to 'libbbot')
-rw-r--r--libbbot/build-config.cxx109
-rw-r--r--libbbot/build-config.hxx12
-rw-r--r--libbbot/manifest.cxx4
-rw-r--r--libbbot/manifest.hxx2
4 files changed, 108 insertions, 19 deletions
diff --git a/libbbot/build-config.cxx b/libbbot/build-config.cxx
index de9a71d..2e6dfbc 100644
--- a/libbbot/build-config.cxx
+++ b/libbbot/build-config.cxx
@@ -4,9 +4,10 @@
#include <libbbot/build-config.hxx>
+#include <map>
#include <string>
#include <cstddef> // size_t
-#include <utility> // move()
+#include <utility> // move(), make_pair()
#include <stdexcept> // invalid_argument
#include <libbutl/path.mxx>
@@ -56,6 +57,14 @@ namespace bbot
build_config config;
config.machine_pattern = move (tl[i++].value);
+ // If the machine pattern is a single dash character, then this is a
+ // placeholder entry. The only thing we are interested about it is the
+ // class inheritance information. Note that while all other information
+ // is discarded, the configuration name and target must be present (can
+ // also be dashes), so the classes field can be determined and parsed.
+ //
+ bool placeholder (config.machine_pattern == "-");
+
// Configuration name, target and classes fields are the required ones.
//
if (i == n)
@@ -74,6 +83,7 @@ namespace bbot
if (++i == n)
bad_line ("no target found");
+ if (!placeholder)
try
{
config.target = target_triplet (tl[i].value);
@@ -90,21 +100,75 @@ namespace bbot
//
try
{
- // We don't expect the class names be quotes as they cannot contain
- // spaces.
- //
using namespace string_parser;
- config.classes = parse_quoted (unquote (tl[i].value),
- false /* unquote */);
- // Validate the class names.
- //
- for (const string& c: config.classes)
+ auto validate = [] (const string& c)
{
+ bpkg::build_class_term::validate_name (c);
+
if (c == "none")
throw invalid_argument ("class 'none' is reserved");
+ };
- bpkg::build_class_term::validate_name (c);
+ // We don't expect the class names be quotes as they cannot contain
+ // spaces.
+ //
+ for (string& c: parse_quoted (unquote (tl[i].value),
+ false /* unquote */))
+ {
+ string base;
+ size_t p (c.find (':'));
+
+ if (p != string::npos)
+ {
+ base = string (c, p + 1);
+ validate (base);
+
+ c.resize (p);
+ }
+
+ validate (c);
+
+ // Add the mapping of the derived class to its base.
+ //
+ // Note that it's not required for a base to also be registered in
+ // the map.
+ //
+ auto i (r.class_inheritance_map.insert (make_pair (c, base)));
+
+ // If the derived-to-base mapping is added, then verify that there
+ // is no inheritance cycle. Otherwise, verify that the base class is
+ // the same as for the existing mapping. Note that once the base
+ // class is specified it can be omitted for subsequent mentions of
+ // the derived class.
+ //
+ auto j (i.first);
+
+ if (i.second) // Added?
+ {
+ // Traverse through the class hierarchy up until a non-registered
+ // base (in particular an empty one) is encountered.
+ //
+ // Note: here we also handle the 'base of itself' case.
+ //
+ while (j != r.class_inheritance_map.end ())
+ {
+ const string& base (j->second);
+
+ if (base == c)
+ throw invalid_argument (
+ "inheritance cycle in '" + c + "' class inheritance");
+
+ j = r.class_inheritance_map.find (base);
+ }
+ }
+ else if (j->second != base && !base.empty ())
+ throw invalid_argument ("'" + c + "' new base '" + base +
+ "' does not match existing '" +
+ j->second + "'");
+
+ if (!placeholder)
+ config.classes.emplace_back (move (c));
}
}
catch (const invalid_argument& e)
@@ -112,6 +176,13 @@ namespace bbot
bad_line (e.what ());
}
+ // We are done if this is a placeholder.
+ //
+ if (placeholder)
+ continue;
+
+ // Parse options, variables, and regexes.
+ //
try
{
for (++i; i < n; ++i)
@@ -121,11 +192,11 @@ namespace bbot
if (v[0] == '~') // Regular expression.
{
string re (v, 1);
- task_manifest::check_regex (re);
+ task_manifest::validate_regex (re);
config.warning_regexes.emplace_back (move (re));
}
- else // Configuration variable.
- config.vars.emplace_back (move (v));
+ else // Configuration option or variable.
+ config.args.emplace_back (move (v));
}
}
catch (const invalid_argument& e)
@@ -138,6 +209,18 @@ namespace bbot
r.emplace_back (move (config));
}
+ // Erase entries for baseless classes (we were collecting them to make
+ // sure that the class inheritance is consistent across configurations).
+ //
+ for (auto i (r.class_inheritance_map.begin ());
+ i != r.class_inheritance_map.end (); )
+ {
+ if (i->second.empty ())
+ i = r.class_inheritance_map.erase (i);
+ else
+ ++i;
+ }
+
return r;
}
diff --git a/libbbot/build-config.hxx b/libbbot/build-config.hxx
index 3f1c0cc..9396782 100644
--- a/libbbot/build-config.hxx
+++ b/libbbot/build-config.hxx
@@ -5,6 +5,7 @@
#ifndef LIBBBOT_BUILD_CONFIG_HXX
#define LIBBBOT_BUILD_CONFIG_HXX
+#include <map>
#include <string>
#include <vector>
#include <iosfwd>
@@ -27,21 +28,26 @@ namespace bbot
std::string name; // Configuration name.
butl::target_triplet target;
std::vector<std::string> classes;
- std::vector<std::string> vars; // Note: quoting is preserved.
+ std::vector<std::string> args; // Note: quoting is preserved.
// Warning-detecting regular expressions. Note that quoting is preserved.
//
std::vector<std::string> warning_regexes;
};
- using build_configs = std::vector<build_config>;
+ struct build_configs: std::vector<build_config>
+ {
+ // A map of derived class names to their bases.
+ //
+ std::map<std::string, std::string> class_inheritance_map;
+ };
// Parse buildtab stream or file. Throw tab_parsing on parsing error,
// ios::failure on the underlying OS error.
//
// buildtab consists of lines in the following format:
//
- // <machine-pattern> <config> <target> [<config-vars>] [<warning-regex>]
+ // <machine-pattern> <config> <target> <classes> [<config-arg>]* [<warning-regex>]*
//
using butl::tab_parsing;
diff --git a/libbbot/manifest.cxx b/libbbot/manifest.cxx
index 2f70b75..f412538 100644
--- a/libbbot/manifest.cxx
+++ b/libbbot/manifest.cxx
@@ -561,7 +561,7 @@ namespace bbot
if (!warning_regex.empty ())
bad_name ("task warning regex redefinition");
- warning_regex = parse_tab (v, check_regex, "warning regex");
+ warning_regex = parse_tab (v, validate_regex, "warning regex");
if (warning_regex.empty ())
bad_value ("empty task warning regex");
@@ -673,7 +673,7 @@ namespace bbot
}
void task_manifest::
- check_regex (const string& s)
+ validate_regex (const string& s)
{
try
{
diff --git a/libbbot/manifest.hxx b/libbbot/manifest.hxx
index 163c75e..de93def 100644
--- a/libbbot/manifest.hxx
+++ b/libbbot/manifest.hxx
@@ -171,7 +171,7 @@ namespace bbot
// invalid_argument if that's not the case.
//
static void
- check_regex (const std::string&);
+ validate_regex (const std::string&);
};
class LIBBBOT_EXPORT task_response_manifest