From b740417add06a6df0dff65b60fbd92a8c8d95aab Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 7 Dec 2018 15:29:59 +0300 Subject: Add support for build configuration class inheritance --- libbbot/build-config.cxx | 109 +++++++++++++++++++++++++++++++++++++++++------ libbbot/build-config.hxx | 12 ++++-- libbbot/manifest.cxx | 4 +- libbbot/manifest.hxx | 2 +- 4 files changed, 108 insertions(+), 19 deletions(-) (limited to 'libbbot') 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 +#include #include #include // size_t -#include // move() +#include // move(), make_pair() #include // invalid_argument #include @@ -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 #include #include #include @@ -27,21 +28,26 @@ namespace bbot std::string name; // Configuration name. butl::target_triplet target; std::vector classes; - std::vector vars; // Note: quoting is preserved. + std::vector args; // Note: quoting is preserved. // Warning-detecting regular expressions. Note that quoting is preserved. // std::vector warning_regexes; }; - using build_configs = std::vector; + struct build_configs: std::vector + { + // A map of derived class names to their bases. + // + std::map 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: // - // [] [] + // []* []* // 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 -- cgit v1.1