From 40e4d161fa319a443c2598ddbc74b8ad31220c68 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 16 Nov 2022 22:14:53 +0300 Subject: Add support for package-config task manifest value --- libbbot/build-config.cxx | 262 --------------------------------------- libbbot/build-config.hxx | 70 ----------- libbbot/build-target-config.cxx | 264 ++++++++++++++++++++++++++++++++++++++++ libbbot/build-target-config.hxx | 70 +++++++++++ libbbot/manifest.cxx | 35 ++++-- libbbot/manifest.hxx | 15 ++- tests/buildtab/driver.cxx | 6 +- tests/buildtab/testscript | 4 +- tests/manifest/task.testscript | 31 ++++- 9 files changed, 404 insertions(+), 353 deletions(-) delete mode 100644 libbbot/build-config.cxx delete mode 100644 libbbot/build-config.hxx create mode 100644 libbbot/build-target-config.cxx create mode 100644 libbbot/build-target-config.hxx diff --git a/libbbot/build-config.cxx b/libbbot/build-config.cxx deleted file mode 100644 index 15061fc..0000000 --- a/libbbot/build-config.cxx +++ /dev/null @@ -1,262 +0,0 @@ -// file : libbbot/build-config.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include - -#include -#include -#include // size_t -#include // move(), make_pair() -#include // invalid_argument - -#include -#include -#include -#include - -#include // build_class_term::validate_name() - -#include // task_manifest::check_config() - -using namespace std; -using namespace butl; - -namespace bbot -{ - LIBBBOT_EXPORT build_configs - parse_buildtab (istream& is, const string& name) - { - build_configs r; - tab_parser parser (is, name); - - r.classes.push_back ("all"); - r.classes.push_back ("default"); - - tab_fields tl; - while (!(tl = parser.next ()).empty ()) - { - size_t n (tl.size ()); // Fields count. - size_t i (0); // The field currently being processed. - - // Throw tab_parsing for the field currently being processed. If i == n - // then we refer to the end-of-line column (presumably reporting a missed - // field). - // - auto bad_line = [&name, &tl, &i, n] (const string& d) - { - // Offset beyond the end-of-line is meaningless. - // - assert (i <= n); - - throw tab_parsing (name, - tl.line, - i == n - ? tl.end_column - : tl[i].column, - d); - }; - - 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[/environment] and classes fields are the - // required ones. - // - if (i == n) - bad_line ("no configuration name found"); - - config.name = move (tl[i].value); - - if (++i == n) - bad_line ("no target found"); - - if (!placeholder) - try - { - const string& v (tl[i].value); - - // Extract the environment name, if present. - // - size_t p (v.find ('/')); - - if (p != string::npos) - { - string env (v, p + 1); - - if (env.empty ()) - bad_line ("empty environment"); - - config.environment = move (env); - } - - // Parse the target triplet. - // - config.target = target_triplet (p != string::npos - ? string (v, 0, p) - : v); - } - catch (const invalid_argument& e) - { - bad_line (e.what ()); - } - - // Make sure the name/target combination is unique. - // - for (const auto& c: r) - { - if (c.name == config.name && c.target == config.target) - bad_line ("duplicate configuration name/target"); - } - - if (++i == n) - bad_line ("no classes found"); - - // Parse a potentially quoted class list. - // - try - { - using namespace string_parser; - - auto validate = [] (const string& c) - { - bpkg::build_class_term::validate_name (c); - - if (c == "none") - throw invalid_argument ("class 'none' is reserved"); - }; - - // 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); - } - - if (c != "all" && c != "default") - r.classes.push_back (c); - } - 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) - { - 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) - { - string& v (tl[i].value); - - if (v[0] == '~') // Regular expression. - { - string re (v, 1); - task_manifest::validate_regex (re); - config.warning_regexes.emplace_back (move (re)); - } - else // Configuration option or variable. - config.args.emplace_back (move (v)); - } - } - catch (const invalid_argument& e) - { - bad_line (e.what ()); - } - - // Save the configuration. - // - 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; - } - - build_configs - parse_buildtab (const path& p) - { - ifdstream ifs (p); - build_configs r (parse_buildtab (ifs, p.string ())); - - ifs.close (); // Throws on failure. - return r; - } -} diff --git a/libbbot/build-config.hxx b/libbbot/build-config.hxx deleted file mode 100644 index 473e5d8..0000000 --- a/libbbot/build-config.hxx +++ /dev/null @@ -1,70 +0,0 @@ -// file : libbbot/build-config.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef LIBBBOT_BUILD_CONFIG_HXX -#define LIBBBOT_BUILD_CONFIG_HXX - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -namespace bbot -{ - // Build configuration matching specific machine names. Used by bbot - // controllers. - // - struct build_config - { - std::string machine_pattern; // Machine name pattern. - std::string name; // Configuration name. - butl::target_triplet target; // Build target. - butl::optional environment; // Build environment name. - std::vector classes; - std::vector args; // Note: quoting is preserved. - - // Warning-detecting regular expressions. Note that quoting is preserved. - // - std::vector warning_regexes; - }; - - struct build_configs: std::vector - { - // List of all configuration class names. Starts with the all and default - // classes. The rest follows in the same order as in the buildtab. - // - std::vector classes; - - // 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: - // - // [/] []* []* - // - // Note that each / pair is expected to be unique in the - // buildtab. - // - using butl::tab_parsing; - - LIBBBOT_EXPORT build_configs - parse_buildtab (std::istream&, const std::string& name); - - LIBBBOT_EXPORT build_configs - parse_buildtab (const butl::path&); -} - -#endif // LIBBBOT_BUILD_CONFIG_HXX diff --git a/libbbot/build-target-config.cxx b/libbbot/build-target-config.cxx new file mode 100644 index 0000000..9e04a17 --- /dev/null +++ b/libbbot/build-target-config.cxx @@ -0,0 +1,264 @@ +// file : libbbot/build-target-config.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +#include +#include +#include // size_t +#include // move(), make_pair() +#include // invalid_argument + +#include +#include +#include +#include + +#include // build_class_term::validate_name() + +#include // task_manifest::check_config() + +using namespace std; +using namespace butl; + +namespace bbot +{ + LIBBBOT_EXPORT build_target_configs + parse_buildtab (istream& is, const string& name) + { + build_target_configs r; + tab_parser parser (is, name); + + r.classes.push_back ("all"); + r.classes.push_back ("default"); + + tab_fields tl; + while (!(tl = parser.next ()).empty ()) + { + size_t n (tl.size ()); // Fields count. + size_t i (0); // The field currently being processed. + + // Throw tab_parsing for the field currently being processed. If i == n + // then we refer to the end-of-line column (presumably reporting a missed + // field). + // + auto bad_line = [&name, &tl, &i, n] (const string& d) + { + // Offset beyond the end-of-line is meaningless. + // + assert (i <= n); + + throw tab_parsing (name, + tl.line, + i == n + ? tl.end_column + : tl[i].column, + d); + }; + + build_target_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 target 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 == "-"); + + // Target configuration name, target[/environment] and classes fields + // are the required ones. + // + if (i == n) + bad_line ("no target configuration name found"); + + config.name = move (tl[i].value); + + if (++i == n) + bad_line ("no target found"); + + if (!placeholder) + try + { + const string& v (tl[i].value); + + // Extract the environment name, if present. + // + size_t p (v.find ('/')); + + if (p != string::npos) + { + string env (v, p + 1); + + if (env.empty ()) + bad_line ("empty environment"); + + config.environment = move (env); + } + + // Parse the target triplet. + // + config.target = target_triplet (p != string::npos + ? string (v, 0, p) + : v); + } + catch (const invalid_argument& e) + { + bad_line (e.what ()); + } + + // Make sure the name/target combination is unique. + // + for (const auto& c: r) + { + if (c.name == config.name && c.target == config.target) + bad_line ("duplicate target configuration name/target"); + } + + if (++i == n) + bad_line ("no classes found"); + + // Parse a potentially quoted class list. + // + try + { + using namespace string_parser; + + auto validate = [] (const string& c) + { + bpkg::build_class_term::validate_name (c); + + if (c == "none") + throw invalid_argument ("class 'none' is reserved"); + }; + + // 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); + } + + if (c != "all" && c != "default") + r.classes.push_back (c); + } + 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) + { + 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) + { + string& v (tl[i].value); + + if (v[0] == '~') // Regular expression. + { + string re (v, 1); + task_manifest::validate_regex (re); + config.warning_regexes.emplace_back (move (re)); + } + else // Target configuration option or variable. + config.args.emplace_back (move (v)); + } + } + catch (const invalid_argument& e) + { + bad_line (e.what ()); + } + + // Save the target configuration. + // + r.emplace_back (move (config)); + } + + // Erase entries for baseless classes (we were collecting them to make + // sure that the class inheritance is consistent across target + // 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; + } + + build_target_configs + parse_buildtab (const path& p) + { + ifdstream ifs (p); + build_target_configs r (parse_buildtab (ifs, p.string ())); + + ifs.close (); // Throws on failure. + return r; + } +} diff --git a/libbbot/build-target-config.hxx b/libbbot/build-target-config.hxx new file mode 100644 index 0000000..27b555c --- /dev/null +++ b/libbbot/build-target-config.hxx @@ -0,0 +1,70 @@ +// file : libbbot/build-target-config.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBBOT_BUILD_CONFIG_HXX +#define LIBBBOT_BUILD_CONFIG_HXX + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace bbot +{ + // Build target configuration matching specific machine names. Used by bbot + // controllers. + // + struct build_target_config + { + std::string machine_pattern; // Machine name pattern. + std::string name; // Configuration name. + butl::target_triplet target; // Build target. + butl::optional environment; // Build environment name. + std::vector classes; + std::vector args; // Note: quoting is preserved. + + // Warning-detecting regular expressions. Note that quoting is preserved. + // + std::vector warning_regexes; + }; + + struct build_target_configs: std::vector + { + // List of all target configuration class names. Starts with the all and + // default classes. The rest follows in the same order as in the buildtab. + // + std::vector classes; + + // 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: + // + // [/] []* []* + // + // Note that each / pair is expected to be unique in + // the buildtab. + // + using butl::tab_parsing; + + LIBBBOT_EXPORT build_target_configs + parse_buildtab (std::istream&, const std::string& name); + + LIBBBOT_EXPORT build_target_configs + parse_buildtab (const butl::path&); +} + +#endif // LIBBBOT_BUILD_CONFIG_HXX diff --git a/libbbot/manifest.cxx b/libbbot/manifest.cxx index 5b9de22..e2534c2 100644 --- a/libbbot/manifest.cxx +++ b/libbbot/manifest.cxx @@ -665,15 +665,23 @@ namespace bbot environment = move (v); } - else if (n == "config") + else if (n == "config" || // @@ TMP Until toolchain 0.16.0 is released. + n == "target-config") { - if (!config.empty ()) - bad_name ("task configuration redefinition"); + if (!target_config.empty ()) + bad_name ("task target configuration redefinition"); - config = parse_tab (v, [](const string&){}, "configuration"); + target_config = parse_tab (v, [](const string&){}, "configuration"); - if (config.empty ()) - bad_value ("empty task configuration"); + if (target_config.empty ()) + bad_value ("empty task target configuration"); + } + else if (n == "package-config") + { + if (!package_config.empty ()) + bad_name ("task package configuration redefinition"); + + package_config = move (v); } else if (n == "host") { @@ -829,7 +837,16 @@ namespace bbot } }; - serialize_list ("config", config); + // @@ TMP Always use 'target-config' name and always serialize + // package_config after toolchain 0.16.0 is released. + // + if (!package_config.empty ()) + { + serialize_list ("target-config", target_config); + s.next ("package-config", package_config); + } + else + serialize_list ("config", target_config); if (host) s.next ("host", *host ? "true" : "false"); @@ -846,9 +863,9 @@ namespace bbot } strings task_manifest:: - unquoted_config () const + unquoted_target_config () const { - return string_parser::unquote (config); + return string_parser::unquote (target_config); } strings task_manifest:: diff --git a/libbbot/manifest.hxx b/libbbot/manifest.hxx index 7ba53cb..13b1138 100644 --- a/libbbot/manifest.hxx +++ b/libbbot/manifest.hxx @@ -183,7 +183,12 @@ namespace bbot // // Note: could be quoted. // - strings config; + strings target_config; + + // Whitespace separated list of potentially double/single-quoted package + // configuration arguments for bpkg-pkg-build command. + // + std::string package_config; // If true, then this configuration is self-hosted. // @@ -200,7 +205,7 @@ namespace bbot butl::optional worker_checksum; strings - unquoted_config () const; + unquoted_target_config () const; strings unquoted_warning_regex () const; @@ -215,7 +220,8 @@ namespace bbot std::string mn, butl::target_triplet tg, butl::optional en, - strings cf, + strings tc, + std::string pc, butl::optional ht, strings wr, butl::optional ir, @@ -230,7 +236,8 @@ namespace bbot machine (std::move (mn)), target (std::move (tg)), environment (std::move (en)), - config (std::move (cf)), + target_config (std::move (tc)), + package_config (std::move (pc)), host (std::move (ht)), warning_regex (std::move (wr)), interactive (std::move (ir)), diff --git a/tests/buildtab/driver.cxx b/tests/buildtab/driver.cxx index df23dbc..1ea4331 100644 --- a/tests/buildtab/driver.cxx +++ b/tests/buildtab/driver.cxx @@ -6,7 +6,7 @@ #include // operator<<(ostream,exception) -#include +#include #undef NDEBUG #include @@ -27,9 +27,9 @@ try cin.exceptions (ios::failbit | ios::badbit); cout.exceptions (ios::failbit | ios::badbit); - const build_configs& configs (parse_buildtab (cin, "cin")); + const build_target_configs& configs (parse_buildtab (cin, "cin")); - for (const build_config& c: configs) + for (const build_target_config& c: configs) { cout << c.machine_pattern << ' ' << c.name << ' ' << c.target; diff --git a/tests/buildtab/testscript b/tests/buildtab/testscript index 5631527..6c8b054 100644 --- a/tests/buildtab/testscript +++ b/tests/buildtab/testscript @@ -55,12 +55,12 @@ $* <>EOE == 1 windows*-vc_14* EOI - cin:1:16: error: no configuration name found + cin:1:16: error: no target configuration name found EOE : dup : - $* <'cin:3:31: error: duplicate configuration name/target' == 1 + $* <'cin:3:31: error: duplicate target configuration name/target' == 1 windows*-vc_14* windows-vc_14 i686-microsoft-win32-msvc14.0 default windows*-vc_14* windows-vc_14 x86_64-microsoft-win32-msvc14.0 default windows*-vc_14* windows-vc_14 i686-microsoft-win32-msvc14.0 default diff --git a/tests/manifest/task.testscript b/tests/manifest/task.testscript index bd1a365..d224124 100644 --- a/tests/manifest/task.testscript +++ b/tests/manifest/task.testscript @@ -24,6 +24,31 @@ test.options += -t machine: windows_10-msvc_14 target: x86_64-microsoft-win32-msvc14.0 environment: lld + target-config: config.cc.coptions=/Z7 config.cc.loptions=/DEBUG + package-config: config.foo.network=true + warning-regex: '^warning: ' '^.+: warning: ' + interactive: error + worker-checksum: 1 + EOF + + # @@ TMP Remove when toolchain 0.16.0 is released. + # + : no-package-config + : + $* <>EOF + : 1 + name: foo + version: 1.0 + repository-url: http://pkg.example.org/1/math + trust: AB:0D:3F:C1:B0:13:E4:0E:AD:4A:08:06:AE:F3:85:DB:E2:27:5F:83:11:47:A2:7\ + 8:64:3C:73:60:F8:66:3A:A4 + requires: host + tests: foo-tests + examples: foo-examples + dependency-checksum: 12345 + machine: windows_10-msvc_14 + target: x86_64-microsoft-win32-msvc14.0 + environment: lld config: config.cc.coptions=/Z7 config.cc.loptions=/DEBUG warning-regex: '^warning: ' '^.+: warning: ' interactive: error @@ -233,7 +258,7 @@ test.options += -t : config : - $* <'stdin:3:1: error: task configuration redefinition' == 1 + $* <'stdin:3:1: error: task target configuration redefinition' == 1 : 1 config: config.cc.coptions=/Z7 config: config.cc.loptions=/DEBUG @@ -341,12 +366,12 @@ test.options += -t target: EOI - : config + : target-config : { : empty : - $* <'stdin:2:8: error: empty task configuration' == 1 + $* <'stdin:2:8: error: empty task target configuration' == 1 : 1 config: EOI -- cgit v1.1