From 294c558d577cd4acb2ee8e94e0dfd6acdb946c6c Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 7 Dec 2018 23:12:05 +0300 Subject: Add support for build configuration class inheritance --- mod/build-config-module.cxx | 384 +++++++++++++++++++++++++++++++++ mod/build-config-module.hxx | 96 +++++++++ mod/build-config.cxx | 347 ----------------------------- mod/build-config.hxx | 99 --------- mod/build.cxx | 53 +++++ mod/build.hxx | 31 +++ mod/database-module.cxx | 66 +----- mod/database-module.hxx | 28 +-- mod/mod-build-configs.cxx | 25 +-- mod/mod-build-configs.hxx | 6 +- mod/mod-build-force.cxx | 8 +- mod/mod-build-force.hxx | 3 +- mod/mod-build-log.cxx | 8 +- mod/mod-build-log.hxx | 3 +- mod/mod-build-result.cxx | 21 +- mod/mod-build-result.hxx | 3 +- mod/mod-build-task.cxx | 21 +- mod/mod-build-task.hxx | 3 +- mod/mod-builds.cxx | 15 +- mod/mod-builds.hxx | 3 +- mod/mod-package-version-details.cxx | 16 +- mod/mod-package-version-details.hxx | 4 +- mod/page.cxx | 4 +- tests/load/1/math/libfoo-1.0.tar.gz | Bin 278 -> 327 bytes tests/load/1/math/packages.manifest | 6 +- tests/load/1/stable/libfoo-1.0.tar.gz | Bin 278 -> 327 bytes tests/load/1/stable/packages.manifest | 6 +- tests/load/1/stable/signature.manifest | 20 +- tests/load/driver.cxx | 2 +- 29 files changed, 685 insertions(+), 596 deletions(-) create mode 100644 mod/build-config-module.cxx create mode 100644 mod/build-config-module.hxx delete mode 100644 mod/build-config.cxx delete mode 100644 mod/build-config.hxx create mode 100644 mod/build.cxx create mode 100644 mod/build.hxx diff --git a/mod/build-config-module.cxx b/mod/build-config-module.cxx new file mode 100644 index 0000000..57c035f --- /dev/null +++ b/mod/build-config-module.cxx @@ -0,0 +1,384 @@ +// file : mod/build-config-module.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include // EIO + +#include +#include + +#include +#include // throw_generic_error(), alpha(), etc. +#include +#include + +namespace brep +{ + using namespace std; + using namespace butl; + using namespace bpkg; + using namespace bbot; + + // Return pointer to the shared build configurations instance, creating one + // on the first call. Throw tab_parsing on parsing error, io_error on the + // underlying OS error. Note: not thread-safe. + // + static shared_ptr + shared_build_config (const path& p) + { + static map> configs; + + auto i (configs.find (p)); + if (i != configs.end ()) + { + if (shared_ptr c = i->second.lock ()) + return c; + } + + shared_ptr c ( + make_shared (parse_buildtab (p))); + + configs[p] = c; + return c; + } + + // Return pointer to the shared build bot agent public keys map, creating + // one on the first call. Throw system_error on the underlying openssl or OS + // error. Note: not thread-safe. + // + using bot_agent_key_map = map; + + static shared_ptr + shared_bot_agent_keys (const options::openssl_options& o, const dir_path& d) + { + static map> keys; + + auto i (keys.find (d)); + if (i != keys.end ()) + { + if (shared_ptr k = i->second.lock ()) + return k; + } + + shared_ptr ak (make_shared ()); + + // Intercept exception handling to make error descriptions more + // informative. + // + // Path of the key being converted. Used for diagnostics. + // + path p; + + try + { + for (const dir_entry& de: dir_iterator (d, false /* ignore_dangling */)) + { + if (de.path ().extension () == "pem" && + de.type () == entry_type::regular) + { + p = d / de.path (); + + openssl os (p, path ("-"), 2, + process_env (o.openssl (), o.openssl_envvar ()), + "pkey", + o.openssl_option (), "-pubin", "-outform", "DER"); + + string fp (sha256 (os.in).string ()); + os.in.close (); + + if (!os.wait ()) + throw io_error (""); + + ak->emplace (move (fp), move (p)); + } + } + } + catch (const io_error&) + { + ostringstream os; + os << "unable to convert bbot agent pubkey " << p; + throw_generic_error (EIO, os.str ().c_str ()); + } + catch (const process_error& e) + { + ostringstream os; + os << "unable to convert bbot agent pubkey " << p; + throw_generic_error (e.code ().value (), os.str ().c_str ()); + } + catch (const system_error& e) + { + ostringstream os; + os<< "unable to iterate over agents keys directory '" << d << "'"; + throw_generic_error (e.code ().value (), os.str ().c_str ()); + } + + keys[d] = ak; + return ak; + } + + void build_config_module:: + init (const options::build& bo) + { + try + { + build_conf_ = shared_build_config (bo.build_config ()); + } + catch (const io_error& e) + { + ostringstream os; + os << "unable to read build configuration '" << bo.build_config () + << "': " << e; + + throw_generic_error (EIO, os.str ().c_str ()); + } + + if (bo.build_bot_agent_keys_specified ()) + bot_agent_key_map_ = + shared_bot_agent_keys (bo, bo.build_bot_agent_keys ()); + + cstrings conf_names; + + using conf_map_type = map; + + conf_map_type conf_map; + + for (const auto& c: *build_conf_) + { + const char* cn (c.name.c_str ()); + conf_map[cn] = &c; + conf_names.push_back (cn); + } + + build_conf_names_ = make_shared (move (conf_names)); + build_conf_map_ = make_shared (move (conf_map)); + } + + // The default underlying class set expression (see below). + // + static const build_class_expr default_ucs_expr ( + {"default"}, '+', "Default."); + + bool build_config_module:: + exclude (const vector& exprs, + const vector& constrs, + const build_config& cfg, + string* reason) const + { + // Save the first sentence of the reason, lower-case the first letter if + // the beginning looks like a word (all subsequent characters until a + // whitespace are lower-case letters). + // + auto sanitize = [] (const string& reason) + { + string r (reason.substr (0, reason.find ('.'))); + + char c (r[0]); // Can be '\0'. + if (alpha (c) && c == ucase (c)) + { + bool word (true); + + for (size_t i (1); + i != r.size () && (c = r[i]) != ' ' && c != '\t' && c != '\n'; + ++i) + { + // Is not a word if contains a non-letter or an upper-case letter. + // + if (!alpha (c) || c == ucase (c)) + { + word = false; + break; + } + } + + if (word) + r[0] = lcase (r[0]); + } + + return r; + }; + + // First, match the configuration against the package underlying build + // class set and expressions. + // + bool m (false); + + // Match the configuration against an expression, updating the match + // result. + // + // We will use a comment of the first encountered excluding expression + // (changing the result from true to false) or non-including one (leaving + // the false result) as an exclusion reason. + // + auto match = [&cfg, &m, reason, &sanitize, this] + (const build_class_expr& e) + { + bool pm (m); + e.match (cfg.classes, build_conf_->class_inheritance_map, m); + + if (reason != nullptr) + { + // Reset the reason which, if saved, makes no sense anymore. + // + if (m) + { + reason->clear (); + } + else if (reason->empty () && + // + // Exclusion. + // + (pm || + // + // Non-inclusion. Make sure that the build class expression + // is empty or starts with an addition (+...). + // + e.expr.empty () || + e.expr.front ().operation == '+')) + { + *reason = sanitize (e.comment); + } + } + }; + + // Determine the underlying class set. Note that in the future we can + // potentially extend the underlying set with special classes. + // + const build_class_expr* ucs ( + !exprs.empty () && !exprs.front ().underlying_classes.empty () + ? &exprs.front () + : nullptr); + + // Note that the combined package build configuration class expression can + // be represented as the underlying class set used as a starting set for + // the original expressions and a restricting set, simultaneously. For + // example, for the expression: + // + // default legacy : -msvc + // + // the resulting expression will be: + // + // +( +default +legacy ) -msvc &( +default +legacy ) + // + // Let's, however, optimize it a bit based on the following facts: + // + // - If the underlying class set expression (+default +legacy in the above + // example) evaluates to false, then the resulting expression also + // evaluates to false due to the trailing '&' operation. Thus, we don't + // need to evaluate further if that's the case. + // + // - On the other hand, if the underlying class set expression evaluates + // to true, then we don't need to apply the trailing '&' operation as it + // cannot affect the result. + // + const build_class_expr& ucs_expr ( + ucs != nullptr + ? build_class_expr (ucs->underlying_classes, '+', ucs->comment) + : default_ucs_expr); + + match (ucs_expr); + + if (m) + { + for (const build_class_expr& e: exprs) + match (e); + } + + // Exclude the configuration if it doesn't match the compound expression. + // + if (!m) + return true; + + // Now check if the configuration is excluded/included via the patterns. + // + // To implement matching of absent name components with wildcard-only + // pattern components we are going to convert names to paths (see + // dash_components_to_path() for details). + // + // And if any of the build-{include,exclude} values (which is legal) or + // the build configuration name/target (illegal) are invalid paths, then + // we assume no match. + // + if (!constrs.empty ()) + try + { + path cn (dash_components_to_path (cfg.name)); + path tg (dash_components_to_path (cfg.target.string ())); + + for (const build_constraint& c: constrs) + { + if (path_match (dash_components_to_path (c.config), + cn, + dir_path () /* start */, + path_match_flags::match_absent) && + (!c.target || + path_match (dash_components_to_path (*c.target), + tg, + dir_path () /* start */, + path_match_flags::match_absent))) + { + if (!c.exclusion) + return false; + + if (reason != nullptr) + *reason = sanitize (c.comment); + + return true; + } + } + } + catch (const invalid_path&) {} + + return false; + } + + path build_config_module:: + dash_components_to_path (const string& s) + { + string r; + for (size_t i (0); i != s.size (); ++i) + { + char c (s[i]); + + switch (c) + { + case '-': + { + r += '/'; + break; + } + case '*': + { + if (s[i + 1] == '*') // Can be '\0'. + { + r += "*/**/*"; + ++i; + break; + } + } + // Fall through. + default: + { + r += c; + break; + } + } + } + + // Append the trailing slash to match the resulting paths as directories. + // This is required for the trailing /* we could append to match absent + // directory path components (see path_match_flags::match_absent for + // details). + // + // Note that valid dash components may not contain a trailing dash. + // Anyway, any extra trailing slashes will be ignored by the path + // constructor. + // + r += '/'; + + return path (move (r)); + } +} diff --git a/mod/build-config-module.hxx b/mod/build-config-module.hxx new file mode 100644 index 0000000..bd6e0b0 --- /dev/null +++ b/mod/build-config-module.hxx @@ -0,0 +1,96 @@ +// file : mod/build-config-module.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_BUILD_CONFIG_MODULE_HXX +#define MOD_BUILD_CONFIG_MODULE_HXX + +#include +#include // find() + +#include // compare_c_string + +#include + +#include + +#include +#include + +#include +#include + +// Base class for modules that utilize the build controller configuration. +// +// Specifically, it loads build controller configuration and provides various +// build configuration-related utilities. Note that the configuration is +// shared across multiple modules once loaded. +// +// Note that the build database is in the database_module. +// +namespace brep +{ + class build_config_module + { + protected: + // Parse build configuration file and establish mapping of build bot agent + // public keys fingerprints to their paths. Throw tab_parsing on parsing + // error, system_error on the underlying OS error. + // + void + init (const options::build&); + + // Return true if the specified build configuration is excluded by a + // package based on its underlying build class set, build class + // expressions, and build constraints, potentially extending the + // underlying set with the special classes. Set the exclusion reason if + // requested. + // + bool + exclude (const vector&, + const vector&, + const bbot::build_config&, + string* reason = nullptr) const; + + // Check if the configuration belongs to the specified class. + // + // Note that the configuration base classes are not checked. + // + static bool + belongs (const bbot::build_config& cfg, const char* cls) + { + const strings& cs (cfg.classes); + return std::find (cs.begin (), cs.end (), cls) != cs.end (); + } + + // Convert dash-separated components (target, build configuration name, + // machine name) or a pattern thereof into a path, replacing dashes with + // slashes (directory separators), `**` with `*/**/*`, and appending the + // trailing slash for a subsequent match using the path_match() + // functionality (the idea here is for `linux**` to match `linux-gcc` + // which is quite natural to expect). Throw invalid_path if the resulting + // path is invalid. + // + // Note that the match_absent path match flag must be used for the above + // `**` transformation to work. + // + static path + dash_components_to_path (const string&); + + protected: + // Build configurations. + // + shared_ptr build_conf_; + shared_ptr build_conf_names_; + + shared_ptr> build_conf_map_; + + // Map of build bot agent public keys fingerprints to the key file paths. + // + shared_ptr> bot_agent_key_map_; + }; +} + +#endif // MOD_BUILD_CONFIG_MODULE_HXX diff --git a/mod/build-config.cxx b/mod/build-config.cxx deleted file mode 100644 index 7e1416d..0000000 --- a/mod/build-config.cxx +++ /dev/null @@ -1,347 +0,0 @@ -// file : mod/build-config.cxx -*- C++ -*- -// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#include - -#include -#include -#include // replace() - -#include -#include // throw_generic_error(), alpha(), etc. -#include -#include - -#include - -#include - -namespace brep -{ - using namespace std; - using namespace web; - using namespace butl; - using namespace bbot; - - shared_ptr - shared_build_config (const path& p) - { - static map> configs; - - auto i (configs.find (p)); - if (i != configs.end ()) - { - if (shared_ptr c = i->second.lock ()) - return c; - } - - shared_ptr c ( - make_shared (parse_buildtab (p))); - - configs[p] = c; - return c; - } - - shared_ptr - shared_bot_agent_keys (const options::openssl_options& o, const dir_path& d) - { - static map> keys; - - auto i (keys.find (d)); - if (i != keys.end ()) - { - if (shared_ptr k = i->second.lock ()) - return k; - } - - shared_ptr ak (make_shared ()); - - // Intercept exception handling to make error descriptions more - // informative. - // - // Path of the key being converted. Used for diagnostics. - // - path p; - - try - { - for (const dir_entry& de: dir_iterator (d, false /* ignore_dangling */)) - { - if (de.path ().extension () == "pem" && - de.type () == entry_type::regular) - { - p = d / de.path (); - - openssl os (p, path ("-"), 2, - process_env (o.openssl (), o.openssl_envvar ()), - "pkey", - o.openssl_option (), "-pubin", "-outform", "DER"); - - string fp (sha256 (os.in).string ()); - os.in.close (); - - if (!os.wait ()) - throw io_error (""); - - ak->emplace (move (fp), move (p)); - } - } - } - catch (const io_error&) - { - ostringstream os; - os << "unable to convert bbot agent pubkey " << p; - throw_generic_error (EIO, os.str ().c_str ()); - } - catch (const process_error& e) - { - ostringstream os; - os << "unable to convert bbot agent pubkey " << p; - throw_generic_error (e.code ().value (), os.str ().c_str ()); - } - catch (const system_error& e) - { - ostringstream os; - os<< "unable to iterate over agents keys directory '" << d << "'"; - throw_generic_error (e.code ().value (), os.str ().c_str ()); - } - - keys[d] = ak; - return ak; - } - - string - build_log_url (const string& host, const dir_path& root, - const build& b, - const string* op) - { - // Note that '+' is the only package version character that potentially - // needs to be url-encoded, and only in the query part of the URL. We embed - // the package version into the URL path part and so don't encode it. - // - string url (host + tenant_dir (root, b.tenant).representation () + - mime_url_encode (b.package_name.string (), false) + '/' + - b.package_version.string () + "/log/" + - mime_url_encode (b.configuration, false) + '/' + - b.toolchain_version.string ()); - - if (op != nullptr) - { - url += '/'; - url += *op; - } - - return url; - } - - string - force_rebuild_url (const string& host, const dir_path& root, const build& b) - { - // Note that '+' is the only package version character that potentially - // needs to be url-encoded, and only in the query part of the URL. However - // we embed the package version into the URL query part, where it is not - // encoded by design. - // - return host + tenant_dir (root, b.tenant).string () + - "?build-force&pn=" + mime_url_encode (b.package_name.string ()) + - "&pv=" + b.package_version.string () + - "&cf=" + mime_url_encode (b.configuration) + - "&tc=" + b.toolchain_version.string () + "&reason="; - } - - bool - exclude (const build_class_exprs& exprs, - const build_constraints& constrs, - const build_config& cfg, - string* reason) - { - // Save the first sentence of the reason, lower-case the first letter if - // the beginning looks like a word (all subsequent characters until a - // whitespace are lower-case letters). - // - auto sanitize = [] (const string& reason) - { - string r (reason.substr (0, reason.find ('.'))); - - char c (r[0]); // Can be '\0'. - if (alpha (c) && c == ucase (c)) - { - bool word (true); - - for (size_t i (1); - i != r.size () && (c = r[i]) != ' ' && c != '\t' && c != '\n'; - ++i) - { - // Is not a word if contains a non-letter or an upper-case letter. - // - if (!alpha (c) || c == ucase (c)) - { - word = false; - break; - } - } - - if (word) - r[0] = lcase (r[0]); - } - - return r; - }; - - bool r (false); - - // First, match the configuration against the package underlying build - // class set and expressions. - // - // Determine the underlying class set. Note that in the future we can - // potentially extend the underlying set with the special classes. - // - build_class_expr ucs ( - !exprs.empty () && !exprs.front ().underlying_classes.empty () - ? exprs.front () - : build_class_expr ("default", "Default")); - - // Transform the combined package build configuration class expression, - // making the underlying class set a starting set for the original - // expression and a restricting set, simultaneously. For example, for the - // expression: - // - // default legacy : -msvc - // - // the resulting expression will be: - // - // +default +legacy -msvc &( +default +legacy ) - // - // - build_class_exprs es; - es.emplace_back (ucs.underlying_classes, '+', ucs.comment); - es.insert (es.end (), exprs.begin (), exprs.end ()); - es.emplace_back (ucs.underlying_classes, '&', ucs.comment); - - // We will use a comment of the first encountered excluding expression - // (changing the result from true to false) or non-including one (leaving - // the false result) as an exclusion reason. - // - for (const build_class_expr& e: es) - { - bool pr (r); - e.match (cfg.classes, r); - - if (reason != nullptr) - { - // Reset the reason which, if saved, makes no sense anymore. - // - if (r) - { - reason->clear (); - } - else if (reason->empty () && - // - // Exclusion. - // - (pr || - // - // Non-inclusion. Make sure that the build class expression - // is empty or starts with an addition (+...). - // - e.expr.empty () || - e.expr.front ().operation == '+')) - { - *reason = sanitize (e.comment); - } - } - } - - if (!r) - return true; - - // Now check if the configuration is excluded/included via the patterns. - // - // To implement matching of absent name components with wildcard-only - // pattern components we are going to convert names to paths (see - // dash_components_to_path() for details). - // - // And if any of the build-{include,exclude} values (which is legal) or - // the build configuration name/target (illegal) are invalid paths, then - // we assume no match. - // - try - { - path cn (dash_components_to_path (cfg.name)); - path tg (dash_components_to_path (cfg.target.string ())); - - for (const build_constraint& c: constrs) - { - if (path_match (dash_components_to_path (c.config), - cn, - dir_path () /* start */, - path_match_flags::match_absent) && - (!c.target || - path_match (dash_components_to_path (*c.target), - tg, - dir_path () /* start */, - path_match_flags::match_absent))) - { - if (!c.exclusion) - return false; - - if (reason != nullptr) - *reason = sanitize (c.comment); - - return true; - } - } - } - catch (const invalid_path&) {} - - return false; - } - - path - dash_components_to_path (const string& s) - { - string r; - for (size_t i (0); i != s.size (); ++i) - { - char c (s[i]); - - switch (c) - { - case '-': - { - r += '/'; - break; - } - case '*': - { - if (s[i + 1] == '*') // Can be '\0'. - { - r += "*/**/*"; - ++i; - break; - } - } - // Fall through. - default: - { - r += c; - break; - } - } - } - - // Append the trailing slash to match the resulting paths as directories. - // This is required for the trailing /* we could append to match absent - // directory path components (see path_match_flags::match_absent for - // details). - // - // Note that valid dash components may not contain a trailing dash. - // Anyway, any extra trailing slashes will be ignored by the path - // constructor. - // - r += '/'; - - return path (move (r)); - } -} diff --git a/mod/build-config.hxx b/mod/build-config.hxx deleted file mode 100644 index 6eece1d..0000000 --- a/mod/build-config.hxx +++ /dev/null @@ -1,99 +0,0 @@ -// file : mod/build-config.hxx -*- C++ -*- -// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_BUILD_CONFIG_HXX -#define MOD_BUILD_CONFIG_HXX - -#include -#include // find() - -#include - -#include -#include - -#include -#include - -#include - -// Various build-related state and utilities. -// -namespace brep -{ - using bbot::build_config; - - // Return pointer to the shared build configurations instance, creating one - // on the first call. Throw tab_parsing on parsing error, io_error on the - // underlying OS error. Is not thread-safe. - // - shared_ptr - shared_build_config (const path&); - - // Map of build bot agent public keys fingerprints to the key file paths. - // - using bot_agent_keys = std::map; - - // Return pointer to the shared build bot agent public keys map, creating - // one on the first call. Throw system_error on the underlying openssl or OS - // error. Not thread-safe. - // - shared_ptr - shared_bot_agent_keys (const options::openssl_options&, const dir_path&); - - // Return the package configuration build log url. By default the url is to - // the operations combined log. - // - string - build_log_url (const string& host, const dir_path& root, - const build&, - const string* operation = nullptr); - - // Return the package configuration forced rebuild url. - // - string - force_rebuild_url (const string& host, const dir_path& root, const build&); - - // Check if the configuration belongs to the specified class. - // - inline bool - belongs (const build_config& cfg, const char* cls) - { - const strings& cs (cfg.classes); - return find (cs.begin (), cs.end (), cls) != cs.end (); - } - - // Return true if the specified build configuration is excluded by a package - // based on its underlying build class set, build class expressions, and - // build constraints, potentially extending the underlying set with the - // special classes. Set the exclusion reason if requested. - // - bool - exclude (const build_class_exprs&, - const build_constraints&, - const build_config&, - string* reason = nullptr); - - inline bool - exclude (const build_package& p, const build_config& c, string* r = nullptr) - { - return exclude (p.builds, p.constraints, c, r); - } - - // Convert dash-separated components (target, build configuration name, - // machine name) or a pattern thereof into a path, replacing dashes with - // slashes (directory separators), `**` with `*/**/*`, and appending the - // trailing slash for a subsequent match using the path_match() - // functionality (the idea here is for `linux**` to match `linux-gcc` which - // is quite natural to expect). Throw invalid_path if the resulting path is - // invalid. - // - // Note that the match_absent path match flag must be used for the above - // `**` transformation to work. - // - path - dash_components_to_path (const string&); -} - -#endif // MOD_BUILD_CONFIG_HXX diff --git a/mod/build.cxx b/mod/build.cxx new file mode 100644 index 0000000..6ad2b93 --- /dev/null +++ b/mod/build.cxx @@ -0,0 +1,53 @@ +// file : mod/build.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include + +#include + +namespace brep +{ + using namespace web; + + string + build_log_url (const string& host, const dir_path& root, + const build& b, + const string* op) + { + // Note that '+' is the only package version character that potentially + // needs to be url-encoded, and only in the query part of the URL. We embed + // the package version into the URL path part and so don't encode it. + // + string url (host + tenant_dir (root, b.tenant).representation () + + mime_url_encode (b.package_name.string (), false) + '/' + + b.package_version.string () + "/log/" + + mime_url_encode (b.configuration, false) + '/' + + b.toolchain_version.string ()); + + if (op != nullptr) + { + url += '/'; + url += *op; + } + + return url; + } + + string + build_force_url (const string& host, const dir_path& root, const build& b) + { + // Note that '+' is the only package version character that potentially + // needs to be url-encoded, and only in the query part of the URL. However + // we embed the package version into the URL query part, where it is not + // encoded by design. + // + return host + tenant_dir (root, b.tenant).string () + + "?build-force&pn=" + mime_url_encode (b.package_name.string ()) + + "&pv=" + b.package_version.string () + + "&cf=" + mime_url_encode (b.configuration) + + "&tc=" + b.toolchain_version.string () + "&reason="; + } +} diff --git a/mod/build.hxx b/mod/build.hxx new file mode 100644 index 0000000..94c270b --- /dev/null +++ b/mod/build.hxx @@ -0,0 +1,31 @@ +// file : mod/build.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_BUILD_HXX +#define MOD_BUILD_HXX + +#include +#include + +#include + +// Various package build-related utilities. +// +namespace brep +{ + // Return the package build log url. By default the url is to the operations + // combined log. + // + string + build_log_url (const string& host, const dir_path& root, + const build&, + const string* operation = nullptr); + + // Return the package build forced rebuild url. + // + string + build_force_url (const string& host, const dir_path& root, const build&); +} + +#endif // MOD_BUILD_HXX diff --git a/mod/database-module.cxx b/mod/database-module.cxx index 137d7ef..07037cd 100644 --- a/mod/database-module.cxx +++ b/mod/database-module.cxx @@ -4,24 +4,13 @@ #include -#include // EIO - -#include - #include -#include // throw_generic_error() - #include #include -#include namespace brep { - using namespace std; - using namespace butl; - using namespace bbot; - // While currently the user-defined copy constructor is not required (we // don't need to deep copy nullptr's), it is a good idea to keep the // placeholder ready for less trivial cases. @@ -31,11 +20,7 @@ namespace brep : handler (r), retry_ (r.retry_), package_db_ (r.initialized_ ? r.package_db_ : nullptr), - build_db_ (r.initialized_ ? r.build_db_ : nullptr), - build_conf_ (r.initialized_ ? r.build_conf_ : nullptr), - build_conf_names_ (r.initialized_ ? r.build_conf_names_ : nullptr), - build_conf_map_ (r.initialized_ ? r.build_conf_map_ : nullptr), - bot_agent_keys_ (r.initialized_ ? r.bot_agent_keys_ : nullptr) + build_db_ (r.initialized_ ? r.build_db_ : nullptr) { } @@ -54,48 +39,15 @@ namespace brep } void database_module:: - init (const options::build& bo, const options::build_db& dbo, size_t retry) + init (const options::build_db& o, size_t retry) { - try - { - build_conf_ = shared_build_config (bo.build_config ()); - } - catch (const io_error& e) - { - ostringstream os; - os << "unable to read build configuration '" << bo.build_config () - << "': " << e; - - throw_generic_error (EIO, os.str ().c_str ()); - } - - if (bo.build_bot_agent_keys_specified ()) - bot_agent_keys_ = shared_bot_agent_keys (bo, bo.build_bot_agent_keys ()); - - cstrings conf_names; - - using conf_map_type = map; - conf_map_type conf_map; - - for (const auto& c: *build_conf_) - { - const char* cn (c.name.c_str ()); - conf_map[cn] = &c; - conf_names.push_back (cn); - } - - build_conf_names_ = make_shared (move (conf_names)); - build_conf_map_ = make_shared (move (conf_map)); - - build_db_ = shared_database (dbo.build_db_user (), - dbo.build_db_role (), - dbo.build_db_password (), - dbo.build_db_name (), - dbo.build_db_host (), - dbo.build_db_port (), - dbo.build_db_max_connections ()); + build_db_ = shared_database (o.build_db_user (), + o.build_db_role (), + o.build_db_password (), + o.build_db_name (), + o.build_db_host (), + o.build_db_port (), + o.build_db_max_connections ()); retry_ = retry_ < retry ? retry : retry_; } diff --git a/mod/database-module.hxx b/mod/database-module.hxx index 70ae004..9791ece 100644 --- a/mod/database-module.hxx +++ b/mod/database-module.hxx @@ -5,20 +5,13 @@ #ifndef MOD_DATABASE_MODULE_HXX #define MOD_DATABASE_MODULE_HXX -#include - #include // database -#include // compare_c_string - #include #include -#include - #include #include -#include namespace brep { @@ -49,12 +42,11 @@ namespace brep void init (const options::package_db&, size_t retry); - // Initialize the build database instance and parse build configuration - // file. Throw odb::exception on database failure, tab_parsing on parsing - // error, system_error on the underlying OS error. + // Initialize the build database instance. Throw odb::exception on + // database failure. // void - init (const options::build&, const options::build_db&, size_t retry); + init (const options::build_db&, size_t retry); virtual bool handle (request&, response&) = 0; @@ -63,19 +55,7 @@ namespace brep size_t retry_ = 0; // Max of all retries. shared_ptr package_db_; - - // These are NULL if not building. - // - shared_ptr build_db_; - shared_ptr build_conf_; - shared_ptr build_conf_names_; - - shared_ptr> - build_conf_map_; - - shared_ptr bot_agent_keys_; + shared_ptr build_db_; // NULL if not building. private: virtual bool diff --git a/mod/mod-build-configs.cxx b/mod/mod-build-configs.cxx index 99a092d..826bb87 100644 --- a/mod/mod-build-configs.cxx +++ b/mod/mod-build-configs.cxx @@ -11,7 +11,6 @@ #include #include -#include using namespace std; using namespace bbot; @@ -24,8 +23,8 @@ using namespace brep::cli; brep::build_configs:: build_configs (const build_configs& r) : handler (r), - options_ (r.initialized_ ? r.options_ : nullptr), - build_conf_ (r.initialized_ ? r.build_conf_ : nullptr) + build_config_module (r), + options_ (r.initialized_ ? r.options_ : nullptr) { } @@ -38,15 +37,7 @@ init (scanner& s) s, unknown_mode::fail, unknown_mode::fail); if (options_->build_config_specified ()) - try - { - build_conf_ = shared_build_config (options_->build_config ()); - } - catch (const io_error& e) - { - fail << "unable to read build configuration '" - << options_->build_config () << "': " << e; - } + build_config_module::init (static_cast (*options_)); } bool brep::build_configs:: @@ -133,7 +124,17 @@ handle (request& rq, response& rs) { if (!classes.empty ()) classes += ' '; + classes += cls; + + // Append the base class, if present. + // + auto i (build_conf_->class_inheritance_map.find (cls)); + if (i != build_conf_->class_inheritance_map.end ()) + { + classes += ':'; + classes += i->second; + } } s << TABLE(CLASS="proplist config") diff --git a/mod/mod-build-configs.hxx b/mod/mod-build-configs.hxx index 0c57359..754c596 100644 --- a/mod/mod-build-configs.hxx +++ b/mod/mod-build-configs.hxx @@ -8,14 +8,13 @@ #include #include -#include - #include #include +#include namespace brep { - class build_configs: public handler + class build_configs: public handler, private build_config_module { public: build_configs () = default; @@ -41,7 +40,6 @@ namespace brep private: shared_ptr options_; - shared_ptr build_conf_; }; } diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx index ddc1301..3a92b53 100644 --- a/mod/mod-build-force.cxx +++ b/mod/mod-build-force.cxx @@ -28,6 +28,7 @@ using namespace odb::core; brep::build_force:: build_force (const build_force& r) : database_module (r), + build_config_module (r), options_ (r.initialized_ ? r.options_ : nullptr) { } @@ -41,9 +42,12 @@ init (scanner& s) s, unknown_mode::fail, unknown_mode::fail); if (options_->build_config_specified ()) - database_module::init (static_cast (*options_), - static_cast (*options_), + { + database_module::init (static_cast (*options_), options_->build_db_retry ()); + + build_config_module::init (static_cast (*options_)); + } } bool brep::build_force:: diff --git a/mod/mod-build-force.hxx b/mod/mod-build-force.hxx index 1af7723..5448026 100644 --- a/mod/mod-build-force.hxx +++ b/mod/mod-build-force.hxx @@ -10,10 +10,11 @@ #include #include +#include namespace brep { - class build_force: public database_module + class build_force: public database_module, private build_config_module { public: build_force () = default; diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx index ee5d1b2..63b2f75 100644 --- a/mod/mod-build-log.cxx +++ b/mod/mod-build-log.cxx @@ -30,6 +30,7 @@ using namespace odb::core; brep::build_log:: build_log (const build_log& r) : database_module (r), + build_config_module (r), options_ (r.initialized_ ? r.options_ : nullptr) { } @@ -43,10 +44,13 @@ init (scanner& s) s, unknown_mode::fail, unknown_mode::fail); if (options_->build_config_specified ()) - database_module::init (static_cast (*options_), - static_cast (*options_), + { + database_module::init (static_cast (*options_), options_->build_db_retry ()); + build_config_module::init (*options_); + } + if (options_->root ().empty ()) options_->root (dir_path ("/")); } diff --git a/mod/mod-build-log.hxx b/mod/mod-build-log.hxx index 154403e..9c31286 100644 --- a/mod/mod-build-log.hxx +++ b/mod/mod-build-log.hxx @@ -10,10 +10,11 @@ #include #include +#include namespace brep { - class build_log: public database_module + class build_log: public database_module, private build_config_module { public: build_log () = default; diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx index 177e67f..5f671d3 100644 --- a/mod/mod-build-result.cxx +++ b/mod/mod-build-result.cxx @@ -23,8 +23,8 @@ #include #include +#include // *_url() #include -#include // *_url() using namespace std; using namespace butl; @@ -39,6 +39,7 @@ using namespace odb::core; brep::build_result:: build_result (const build_result& r) : database_module (r), + build_config_module (r), options_ (r.initialized_ ? r.options_ : nullptr) { } @@ -55,10 +56,13 @@ init (scanner& s) options_->package_db_retry ()); if (options_->build_config_specified ()) - database_module::init (static_cast (*options_), - static_cast (*options_), + { + database_module::init (static_cast (*options_), options_->build_db_retry ()); + build_config_module::init (static_cast (*options_)); + } + if (options_->root ().empty ()) options_->root (dir_path ("/")); } @@ -312,18 +316,18 @@ handle (request& rq, response&) warn_auth (rqm.challenge ? "unexpected challenge" : "challenge is expected"); - else if (bot_agent_keys_ == nullptr) // Authentication is disabled. + else if (bot_agent_key_map_ == nullptr) // Authentication is disabled. auth = true; else if (!b->agent_challenge) // Authentication is recently enabled. warn_auth ("challenge is required now"); else { assert (b->agent_fingerprint && rqm.challenge); - auto i (bot_agent_keys_->find (*b->agent_fingerprint)); + auto i (bot_agent_key_map_->find (*b->agent_fingerprint)); // The agent's key is recently replaced. // - if (i == bot_agent_keys_->end ()) + if (i == bot_agent_key_map_->end ()) warn_auth ("agent's public key not found"); else { @@ -400,7 +404,8 @@ handle (request& rq, response&) shared_ptr p ( build_db_->load (b->id.package)); - if (belongs (*cfg, "all") && !exclude (*p, *cfg)) + if (belongs (*cfg, "all") && + !exclude (p->builds, p->constraints, *cfg)) bld = move (b); } } @@ -458,7 +463,7 @@ handle (request& rq, response&) os << "Force rebuild (enter the reason, use '+' instead of spaces):" << endl << endl - << " " << force_rebuild_url (host, root, *bld) << endl; + << " " << build_force_url (host, root, *bld) << endl; } sm.out.close (); diff --git a/mod/mod-build-result.hxx b/mod/mod-build-result.hxx index 2c24436..86542e7 100644 --- a/mod/mod-build-result.hxx +++ b/mod/mod-build-result.hxx @@ -10,10 +10,11 @@ #include #include +#include namespace brep { - class build_result: public database_module + class build_result: public database_module, private build_config_module { public: build_result () = default; diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx index 14c72ac..1736869 100644 --- a/mod/mod-build-task.cxx +++ b/mod/mod-build-task.cxx @@ -45,6 +45,7 @@ using namespace odb::core; brep::build_task:: build_task (const build_task& r) : database_module (r), + build_config_module (r), options_ (r.initialized_ ? r.options_ : nullptr) { } @@ -59,8 +60,7 @@ init (scanner& s) if (options_->build_config_specified ()) { - database_module::init (static_cast (*options_), - static_cast (*options_), + database_module::init (static_cast (*options_), options_->build_db_retry ()); // Check that the database 'build' schema matches the current one. It's @@ -72,6 +72,8 @@ init (scanner& s) build_db_->schema_version (ds)) fail << "database 'build' schema differs from the current one (module " << BREP_VERSION_ID << ")"; + + build_config_module::init (static_cast (*options_)); } if (options_->root ().empty ()) @@ -124,10 +126,11 @@ handle (request& rq, response& rs) // optional agent_fp; - if (bot_agent_keys_ != nullptr) + if (bot_agent_key_map_ != nullptr) { if (!tqm.fingerprint || - bot_agent_keys_->find (*tqm.fingerprint) == bot_agent_keys_->end ()) + bot_agent_key_map_->find (*tqm.fingerprint) == + bot_agent_key_map_->end ()) throw invalid_request (401, "unauthorized"); agent_fp = move (tqm.fingerprint); @@ -220,7 +223,7 @@ handle (request& rq, response& rs) move (fp), cm.machine->name, cm.config->target, - cm.config->vars, + cm.config->args, cm.config->warning_regexes); return task_response_manifest (move (session), @@ -476,7 +479,11 @@ handle (request& rq, response& rs) auto i (configs.begin ()); auto e (configs.end ()); - for (; i != e && exclude (*p, *i->second.config); ++i) ; + + for (; + i != e && + exclude (p->builds, p->constraints, *i->second.config); + ++i) ; if (i != e) { @@ -632,7 +639,7 @@ handle (request& rq, response& rs) if (p != nullptr && p->internal_repository != nullptr && - !exclude (*p, *cm.config)) + !exclude (p->builds, p->constraints, *cm.config)) { assert (b->status); diff --git a/mod/mod-build-task.hxx b/mod/mod-build-task.hxx index 75383f0..d907ee7 100644 --- a/mod/mod-build-task.hxx +++ b/mod/mod-build-task.hxx @@ -10,10 +10,11 @@ #include #include +#include namespace brep { - class build_task: public database_module + class build_task: public database_module, private build_config_module { public: build_task () = default; diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx index 6ad5a0e..6f027d2 100644 --- a/mod/mod-builds.cxx +++ b/mod/mod-builds.cxx @@ -28,7 +28,6 @@ #include #include -#include // *_url() using namespace std; using namespace butl; @@ -44,6 +43,7 @@ using namespace brep::cli; brep::builds:: builds (const builds& r) : database_module (r), + build_config_module (r), options_ (r.initialized_ ? r.options_ : nullptr) { } @@ -57,10 +57,13 @@ init (scanner& s) s, unknown_mode::fail, unknown_mode::fail); if (options_->build_config_specified ()) - database_module::init (static_cast (*options_), - static_cast (*options_), + { + database_module::init (static_cast (*options_), options_->build_db_retry ()); + build_config_module::init (static_cast (*options_)); + } + if (options_->root ().empty ()) options_->root (dir_path ("/")); } @@ -550,7 +553,7 @@ handle (request& rq, response& rs) // Match the configuration against the package build // expressions/constraints. // - if (!exclude (*p, *i->second)) + if (!exclude (p->builds, p->constraints, *i->second)) { if (skip != 0) --skip; @@ -813,7 +816,7 @@ handle (request& rq, response& rs) for (const auto& c: configs) { - if (exclude (*p, *c)) + if (exclude (p->builds, p->constraints, *c)) { nmax -= nt; @@ -942,7 +945,7 @@ handle (request& rq, response& rs) auto i (build_conf_map_->find (ct.configuration.c_str ())); assert (i != build_conf_map_->end ()); - if (!exclude (*p, *i->second)) + if (!exclude (p->builds, p->constraints, *i->second)) unbuilt_configs.insert (ct); } } diff --git a/mod/mod-builds.hxx b/mod/mod-builds.hxx index 4b2441a..cf4020e 100644 --- a/mod/mod-builds.hxx +++ b/mod/mod-builds.hxx @@ -10,10 +10,11 @@ #include #include +#include namespace brep { - class builds: public database_module + class builds: public database_module, private build_config_module { public: builds () = default; diff --git a/mod/mod-package-version-details.cxx b/mod/mod-package-version-details.cxx index 9566c8f..cbe338e 100644 --- a/mod/mod-package-version-details.cxx +++ b/mod/mod-package-version-details.cxx @@ -34,6 +34,7 @@ using namespace brep::cli; brep::package_version_details:: package_version_details (const package_version_details& r) : database_module (r), + build_config_module (r), options_ (r.initialized_ ? r.options_ : nullptr) { } @@ -46,13 +47,17 @@ init (scanner& s) options_ = make_shared ( s, unknown_mode::fail, unknown_mode::fail); - database_module::init (*options_, options_->package_db_retry ()); + database_module::init (static_cast (*options_), + options_->package_db_retry ()); if (options_->build_config_specified ()) - database_module::init (static_cast (*options_), - static_cast (*options_), + { + database_module::init (static_cast (*options_), options_->build_db_retry ()); + build_config_module::init (static_cast (*options_)); + } + if (options_->root ().empty ()) options_->root (dir_path ("/")); } @@ -393,9 +398,10 @@ handle (request& rq, response& rs) s << H3 << "Builds" << ~H3 << DIV(ID="builds"); - auto exclude = [&pkg] (const build_config& cfg, string* reason = nullptr) + auto exclude = [&pkg, this] (const build_config& cfg, + string* reason = nullptr) { - return brep::exclude (pkg->builds, pkg->build_constraints, cfg, reason); + return this->exclude (pkg->builds, pkg->build_constraints, cfg, reason); }; timestamp now (system_clock::now ()); diff --git a/mod/mod-package-version-details.hxx b/mod/mod-package-version-details.hxx index 0451e99..c15d746 100644 --- a/mod/mod-package-version-details.hxx +++ b/mod/mod-package-version-details.hxx @@ -10,10 +10,12 @@ #include #include +#include namespace brep { - class package_version_details: public database_module + class package_version_details: public database_module, + private build_config_module { public: package_version_details () = default; diff --git a/mod/page.cxx b/mod/page.cxx index eab28c6..7cfce62 100644 --- a/mod/page.cxx +++ b/mod/page.cxx @@ -18,8 +18,8 @@ #include #include +#include // build_log_url() #include -#include // build_log_url() using namespace std; using namespace xml; @@ -718,7 +718,7 @@ namespace brep s << SPAN(CLASS="pending") << "pending" << ~SPAN; else s << A - << HREF << force_rebuild_url (host_, root_, build_) << ~HREF + << HREF << build_force_url (host_, root_, build_) << ~HREF << "rebuild" << ~A; diff --git a/tests/load/1/math/libfoo-1.0.tar.gz b/tests/load/1/math/libfoo-1.0.tar.gz index e951386..2d445ec 100644 Binary files a/tests/load/1/math/libfoo-1.0.tar.gz and b/tests/load/1/math/libfoo-1.0.tar.gz differ diff --git a/tests/load/1/math/packages.manifest b/tests/load/1/math/packages.manifest index 0bbf6d4..d1c972d 100644 --- a/tests/load/1/math/packages.manifest +++ b/tests/load/1/math/packages.manifest @@ -32,9 +32,11 @@ version: 1.0 summary: The Foo Library license: MIT build-email: foo-builds@example.com -builds: default legacy : -32; 64-bit targets only +builds: default legacy; Stable configurations only. +builds: -32; 64-bit targets only +builds: &msvc_13_up; Not too old MSVC. location: libfoo-1.0.tar.gz -sha256sum: 3d32793e7b800837682ffa1dad794df7c9e2bb7a54504552a5bd261b5ec064e5 +sha256sum: e89c6d746f8b1ea3ec58d294946d2f683d133438d2ac8c88549ba24c19627e76 : name: libfoo version: 1.2.4+1 diff --git a/tests/load/1/stable/libfoo-1.0.tar.gz b/tests/load/1/stable/libfoo-1.0.tar.gz index e951386..2d445ec 100644 Binary files a/tests/load/1/stable/libfoo-1.0.tar.gz and b/tests/load/1/stable/libfoo-1.0.tar.gz differ diff --git a/tests/load/1/stable/packages.manifest b/tests/load/1/stable/packages.manifest index 797b214..4c83ed5 100644 --- a/tests/load/1/stable/packages.manifest +++ b/tests/load/1/stable/packages.manifest @@ -6,9 +6,11 @@ version: 1.0 summary: The Foo Library license: MIT build-email: foo-builds@example.com -builds: default legacy : -32; 64-bit targets only +builds: default legacy; Stable configurations only. +builds: -32; 64-bit targets only +builds: &msvc_13_up; Not too old MSVC. location: libfoo-1.0.tar.gz -sha256sum: 3d32793e7b800837682ffa1dad794df7c9e2bb7a54504552a5bd261b5ec064e5 +sha256sum: e89c6d746f8b1ea3ec58d294946d2f683d133438d2ac8c88549ba24c19627e76 : name: libfoo version: 1.2.2-alpha.1 diff --git a/tests/load/1/stable/signature.manifest b/tests/load/1/stable/signature.manifest index 2678657..decdf5b 100644 --- a/tests/load/1/stable/signature.manifest +++ b/tests/load/1/stable/signature.manifest @@ -1,13 +1,13 @@ : 1 -sha256sum: 4957fe28799fe3a037c3112c8122d216fb1d25b48c1028979e5a8738683cb1a5 +sha256sum: f48f71ccb83024007dedc63148d4682e7b2a478a3ca78583e3562ea4bcf396a5 signature: \ -Dxc/W3NM0ZZ/Aytrxpptgcg8pg6tteT9rVoFm3mxaXsLMoEzjEEHq1ruI3kbmIVHQIBYymvpj5FS -JUeKdmtkNOZA7DqbdT5njEarKGoLE4awqsZ4yAWMKpmHNxAl/dYNIQ0l5YdsAq7+c3/lCwha9SlB -QcZrKDxZ7DrhcdiI0eQej08n0X45OA1451VLnSkjHsLXXCO0qnj2o+WVKJE/lWz46Z6FCm9wlLQC -cxaamInFeL5s/eZmn7j5Eo+v2y1v/zufeCr5W6feKlFau156G43ZROPUoqVl+ZxuSAR1gKalVjny -jH1adjEkwqtxXN0fvT2pQ0XU/gn/s7PTiGR9PnKytrAFemO3hbd62PAcFRk17NV8qworyBdLMHgA -V4/CyDCItOgAzfenbjPHfxEkm+qKcK4Y6NlUQSqcHDW8kxMv6iuVw8s0o5ij9frY5XLg2h39O/Lw -I4tDO0lg8k2vxKJVxKk85uElJ+kuBG4353PP3G11uzZpah4X9zqh21TBP+rx+L1gefHwjew+ZwQi -qtOedbzrJlgLgTDK3Q+yMN2i9YOIe+D5vB4Gr97y1lL0qVS3zm/4CjLo6kj13ngGXxivGLbtDlX9 -v4COE/+PtwGS2ubmku1QMwZuXyJfjKG9lGgoNoARML17EzzMIFXnzII0uSP+NUKrT3jLdEbOlQs= +K+F80RQE97ZmEVAl2wQwyYaw/SzviqfuR7FBTdF3PtnIorCxeokwvDL0ip7PiEJRp7QF5V0T69F+ +35MqTrRm/cLdKsxk1vsG8KWMuoK3LSXj+jcPkxKGmVoI09MvPxIQNgHKe/gk1f7uBExnpeOdfHyV +2zP6GMOhZIY4SScNeFr5lo/4bpEEtLNH1TbiYHaaGlBQDmzGzY+kdCrzZ/nWEBaWIHdT8GcqQF1D +P+KWYFoOoT5DnZtfk3ohTI1YHdDQWyWq8vFqkfpRDwxqtHltGeNLjmlwxF6gPJq9H6UZMZfEHGvs +qAScoiA5ozdPt1UXiYtPXpDoweHKcoVPHyqMxcciMTx8BYXDqY8V9EKwd/F3rD0ITzXW3eaRNygo +Jh6w6tjhZjFQ+NJ9GgsxE7NNU1dYhXuzKyBwus0GkGHriGTU7L0t2MoY/1GXihexwz3fHAmkOYxe +Iz4Xt+SZeRSLa5VFFjyEOtbtvbVv8R2WngG4xwNH0b3nMJ8Baa87rajSIfIoXJaeNFWhpEBwCToz +59aJXl76+0kQL40+wSO7+/o8LnLS2sW6+ooMjM8RLRBLTzNqSsVH5kA+W0qF8e0j7AJ0qcqU5HKZ +MzhB7jPyQ82LBq/eU29bgM9hLkOiJpqwplmiRyby98jBz4ppnIytfQeD52PtDCf4yWT9p3PsKmg= \ diff --git a/tests/load/driver.cxx b/tests/load/driver.cxx index a36416b..17da60e 100644 --- a/tests/load/driver.cxx +++ b/tests/load/driver.cxx @@ -405,7 +405,7 @@ test_pkg_repos (const cstrings& loader_args, assert (check_location (fpv1)); assert (fpv1->sha256sum && *fpv1->sha256sum == - "3d32793e7b800837682ffa1dad794df7c9e2bb7a54504552a5bd261b5ec064e5"); + "e89c6d746f8b1ea3ec58d294946d2f683d133438d2ac8c88549ba24c19627e76"); // libfoo-1.2.2 // -- cgit v1.1