From 03c931e54e618221b69cfcd3dfb462e50ecad780 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 28 Oct 2022 23:21:29 +0300 Subject: Add support for package build configurations --- mod/build-config-module.cxx | 33 ++-- mod/build-config-module.hxx | 49 +++-- mod/build-config.cxx | 255 ------------------------ mod/build-config.hxx | 72 ------- mod/build-target-config.cxx | 258 +++++++++++++++++++++++++ mod/build-target-config.hxx | 79 ++++++++ mod/build.cxx | 29 +-- mod/buildfile | 2 +- mod/mod-build-configs.cxx | 15 +- mod/mod-build-force.cxx | 32 ++-- mod/mod-build-log.cxx | 55 +++--- mod/mod-build-result.cxx | 77 +++++--- mod/mod-build-task.cxx | 356 ++++++++++++++++++---------------- mod/mod-builds.cxx | 373 +++++++++++++++++++++--------------- mod/mod-package-version-details.cxx | 112 +++++++---- mod/mod-repository-details.cxx | 2 - mod/mod-repository-root.cxx | 1 - mod/module.cli | 39 ++-- mod/page.cxx | 38 +++- 19 files changed, 1036 insertions(+), 841 deletions(-) delete mode 100644 mod/build-config.cxx delete mode 100644 mod/build-config.hxx create mode 100644 mod/build-target-config.cxx create mode 100644 mod/build-target-config.hxx (limited to 'mod') diff --git a/mod/build-config-module.cxx b/mod/build-config-module.cxx index bf21fbb..eba753e 100644 --- a/mod/build-config-module.cxx +++ b/mod/build-config-module.cxx @@ -18,26 +18,25 @@ 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. + // Return pointer to the shared build target 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 + static shared_ptr shared_build_config (const path& p) { - static map> configs; + static map> configs; auto i (configs.find (p)); if (i != configs.end ()) { - if (shared_ptr c = i->second.lock ()) + if (shared_ptr c = i->second.lock ()) return c; } - shared_ptr c ( - make_shared (parse_buildtab (p))); + shared_ptr c ( + make_shared (bbot::parse_buildtab (p))); configs[p] = c; return c; @@ -122,7 +121,7 @@ namespace brep { try { - build_conf_ = shared_build_config (bo.build_config ()); + target_conf_ = shared_build_config (bo.build_config ()); } catch (const io_error& e) { @@ -137,19 +136,21 @@ namespace brep bot_agent_key_map_ = shared_bot_agent_keys (bo, bo.build_bot_agent_keys ()); - using conf_map_type = map; + using conf_map_type = map; + conf_map_type conf_map; - for (const auto& c: *build_conf_) - conf_map[build_config_id {c.name, c.target}] = &c; + for (const auto& c: *target_conf_) + conf_map[build_target_config_id {c.target, c.name}] = &c; - build_conf_map_ = make_shared (move (conf_map)); + target_conf_map_ = make_shared (move (conf_map)); } bool build_config_module:: - belongs (const bbot::build_config& cfg, const char* cls) const + belongs (const build_target_config& cfg, const char* cls) const { - const map& im (build_conf_->class_inheritance_map); + const map& im (target_conf_->class_inheritance_map); for (const string& c: cfg.classes) { diff --git a/mod/build-config-module.hxx b/mod/build-config-module.hxx index b276d6c..78661c3 100644 --- a/mod/build-config-module.hxx +++ b/mod/build-config-module.hxx @@ -10,13 +10,11 @@ #include -#include - #include #include -#include #include +#include // Base class for modules that utilize the build controller configuration. // @@ -39,16 +37,18 @@ namespace brep init (const options::build&); bool - exclude (const small_vector& exprs, - const vector& constrs, - const bbot::build_config& cfg, + exclude (const build_package_config& pc, + const build_class_exprs& common_builds, + const build_constraints& common_constraints, + const build_target_config& tc, string* reason = nullptr, bool default_all_ucs = false) const { - return brep::exclude (exprs, - constrs, - cfg, - build_conf_->class_inheritance_map, + return brep::exclude (pc, + common_builds, + common_constraints, + tc, + target_conf_->class_inheritance_map, reason, default_all_ucs); } @@ -56,27 +56,30 @@ namespace brep // Check if the configuration belongs to the specified class. // bool - belongs (const bbot::build_config&, const char*) const; + belongs (const build_target_config&, const char*) const; bool - belongs (const bbot::build_config& cfg, const string& cls) const + belongs (const build_target_config& cfg, const string& cls) const { return belongs (cfg, cls.c_str ()); } - // Configuration/target/toolchain combination that, in particular, can be + // Target/configuration/toolchain combination that, in particular, can be // used as a set value. // - // Note: contains shallow references to the configuration, target, - // toolchain name, and version. + // Note: all members are the shallow references. // struct config_toolchain { - const string& configuration; const butl::target_triplet& target; + const string& target_config; + const string& package_config; const string& toolchain_name; const bpkg::version& toolchain_version; + // Note: the comparison reflects the order of unbuilt configurations on + // the Builds page. + // bool operator< (const config_toolchain& ct) const { @@ -86,20 +89,24 @@ namespace brep if (toolchain_version != ct.toolchain_version) return toolchain_version > ct.toolchain_version; - if (int r = configuration.compare (ct.configuration)) + if (int r = target.compare (ct.target)) + return r < 0; + + if (int r = target_config.compare (ct.target_config)) return r < 0; - return target.compare (ct.target) < 0; + return package_config.compare (ct.package_config) < 0; } }; protected: // Build configurations. // - shared_ptr build_conf_; + shared_ptr target_conf_; - shared_ptr> - build_conf_map_; + shared_ptr> + target_conf_map_; // Map of build bot agent public keys fingerprints to the key file paths. // diff --git a/mod/build-config.cxx b/mod/build-config.cxx deleted file mode 100644 index 8fbbf99..0000000 --- a/mod/build-config.cxx +++ /dev/null @@ -1,255 +0,0 @@ -// file : mod/build-config-module.cxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#include - -#include // alpha(), etc. -#include - -namespace brep -{ - using namespace std; - using namespace butl; - using namespace bpkg; - using namespace bbot; - - // The default underlying class set expressions (see below). - // - static const build_class_expr default_ucs_expr ( - {"default"}, '+', "Default."); - - static const build_class_expr all_ucs_expr ( - {"all"}, '+', "All."); - - bool - exclude (const small_vector& exprs, - const vector& constrs, - const build_config& cfg, - const map& class_inheritance_map, - string* reason, - bool default_all_ucs) - { - // 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, &class_inheritance_map] - (const build_class_expr& e) - { - bool pm (m); - e.match (cfg.classes, 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_all_ucs ? all_ucs_expr : - 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 (cn, - dash_components_to_path (c.config), - dir_path () /* start */, - path_match_flags::match_absent) && - (!c.target || - path_match (tg, - dash_components_to_path (*c.target), - 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& pattern) - { - string r; - size_t nstar (0); - for (const path_pattern_term& pt: path_pattern_iterator (pattern)) - { - switch (pt.type) - { - case path_pattern_term_type::star: - { - // Replace ** with */**/* and skip all the remaining stars that may - // follow in this sequence. - // - if (nstar == 0) - r += "*"; - else if (nstar == 1) - r += "/**/*"; // The first star is already copied. - - break; - } - case path_pattern_term_type::literal: - { - // Replace '-' with '/' and fall through otherwise. - // - if (get_literal (pt) == '-') - { - r += '/'; - break; - } - } - // Fall through. - default: - { - r.append (pt.begin, pt.end); // Copy the pattern term as is. - } - } - - nstar = pt.star () ? nstar + 1 : 0; - } - - // 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 4ef01f6..0000000 --- a/mod/build-config.hxx +++ /dev/null @@ -1,72 +0,0 @@ -// file : mod/build-config.hxx -*- C++ -*- -// license : MIT; see accompanying LICENSE file - -#ifndef MOD_BUILD_CONFIG_HXX -#define MOD_BUILD_CONFIG_HXX - -#include - -#include - -#include - -#include - -#include -#include - -namespace brep -{ - // 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. Optionally use - // the `all` class as a default underlying build class set rather than the - // `default` class (which is, for example, the case for the external test - // packages not to reduce their build configuration set needlessly). - // - bool - exclude (const small_vector&, - const vector&, - const bbot::build_config&, - const std::map& class_inheritance_map, - string* reason = nullptr, - bool default_all_ucs = false); - - // 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&); - - // Build configuration name/target combination that, in particular, - // identifies configurations in the buildtab and thus can be used as a - // set/map key. - // - // Note: contains shallow references to the configuration name and target. - // - struct build_config_id - { - reference_wrapper name; - reference_wrapper target; - - bool - operator< (const build_config_id& x) const - { - if (int r = name.get ().compare (x.name.get ())) - return r < 0; - - return target.get ().compare (x.target.get ()) < 0; - } - }; -} - -#endif // MOD_BUILD_CONFIG diff --git a/mod/build-target-config.cxx b/mod/build-target-config.cxx new file mode 100644 index 0000000..a30cf07 --- /dev/null +++ b/mod/build-target-config.cxx @@ -0,0 +1,258 @@ +// file : mod/target-build-config.cxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include + +#include // alpha(), etc. +#include + +namespace brep +{ + using namespace std; + using namespace butl; + using namespace bpkg; + + // The default underlying class set expressions (see below). + // + static const build_class_expr default_ucs_expr ( + {"default"}, '+', "Default."); + + static const build_class_expr all_ucs_expr ( + {"all"}, '+', "All."); + + bool + exclude (const build_package_config& pc, + const build_class_exprs& cbs, + const build_constraints& ccs, + const build_target_config& tc, + const map& class_inheritance_map, + string* reason, + bool default_all_ucs) + { + const build_class_exprs& exprs (pc.effective_builds (cbs)); + const build_constraints& constrs (pc.effective_constraints (ccs)); + + // 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 = [&tc, &m, reason, &sanitize, &class_inheritance_map] + (const build_class_expr& e) + { + bool pm (m); + e.match (tc.classes, 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_all_ucs ? all_ucs_expr : + 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 (tc.name)); + path tg (dash_components_to_path (tc.target.string ())); + + for (const build_constraint& c: constrs) + { + if (path_match (cn, + dash_components_to_path (c.config), + dir_path () /* start */, + path_match_flags::match_absent) && + (!c.target || + path_match (tg, + dash_components_to_path (*c.target), + 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& pattern) + { + string r; + size_t nstar (0); + for (const path_pattern_term& pt: path_pattern_iterator (pattern)) + { + switch (pt.type) + { + case path_pattern_term_type::star: + { + // Replace ** with */**/* and skip all the remaining stars that may + // follow in this sequence. + // + if (nstar == 0) + r += "*"; + else if (nstar == 1) + r += "/**/*"; // The first star is already copied. + + break; + } + case path_pattern_term_type::literal: + { + // Replace '-' with '/' and fall through otherwise. + // + if (get_literal (pt) == '-') + { + r += '/'; + break; + } + } + // Fall through. + default: + { + r.append (pt.begin, pt.end); // Copy the pattern term as is. + } + } + + nstar = pt.star () ? nstar + 1 : 0; + } + + // 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-target-config.hxx b/mod/build-target-config.hxx new file mode 100644 index 0000000..180ca80 --- /dev/null +++ b/mod/build-target-config.hxx @@ -0,0 +1,79 @@ +// file : mod/build-target-config.hxx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#ifndef MOD_BUILD_TARGET_CONFIG_HXX +#define MOD_BUILD_TARGET_CONFIG_HXX + +#include + +#include + +#include + +#include + +#include +#include + +#include + +namespace brep +{ + using build_target_config = bbot::build_target_config; + using build_target_configs = bbot::build_target_configs; + + // Return true if the specified build target configuration is excluded by a + // package configuration 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. Optionally use the `all` class as a default underlying build + // class set rather than the `default` class (which is, for example, the + // case for the external test packages not to reduce their build target + // configuration set needlessly). + // + bool + exclude (const build_package_config&, + const build_class_exprs& common_builds, + const build_constraints& common_constraints, + const build_target_config&, + const std::map& class_inheritance_map, + string* reason = nullptr, + bool default_all_ucs = false); + + // Convert dash-separated components (target, build target 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&); + + // Build target/target configuration name combination that, in particular, + // identifies configurations in the buildtab and thus can be used as a + // set/map key. + // + // Note: contains shallow references to the target and configuration name. + // + struct build_target_config_id + { + reference_wrapper target; + reference_wrapper config; + + bool + operator< (const build_target_config_id& x) const + { + if (int r = target.get ().compare (x.target.get ())) + return r < 0; + + return config.get ().compare (x.config.get ()) < 0; + } + }; +} + +#endif // MOD_BUILD_TARGET_CONFIG diff --git a/mod/build.cxx b/mod/build.cxx index 3b82aed..4abd416 100644 --- a/mod/build.cxx +++ b/mod/build.cxx @@ -20,13 +20,15 @@ namespace brep // 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 /* query */) + '/' + - mime_url_encode (b.target.string (), false /* query */) + '/' + - mime_url_encode (b.toolchain_name, false /* query */) + '/' + - b.toolchain_version.string ()); + 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.target.string (), false /* query */) + '/' + + mime_url_encode (b.target_config_name, false /* query */) + '/' + + mime_url_encode (b.package_config_name, false /* query */) + '/' + + mime_url_encode (b.toolchain_name, false /* query */) + '/' + + b.toolchain_version.string ()); if (op != nullptr) { @@ -45,13 +47,14 @@ namespace brep // 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 () + + 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) + - "&tg=" + mime_url_encode (b.target.string ()) + - "&tn=" + mime_url_encode (b.toolchain_name) + - "&tv=" + b.toolchain_version.string () + + "&pv=" + b.package_version.string () + + "&tg=" + mime_url_encode (b.target.string ()) + + "&tc=" + mime_url_encode (b.target_config_name) + + "&pc=" + mime_url_encode (b.package_config_name) + + "&tn=" + mime_url_encode (b.toolchain_name) + + "&tv=" + b.toolchain_version.string () + "&reason="; } } diff --git a/mod/buildfile b/mod/buildfile index 58a3caf..6693a35 100644 --- a/mod/buildfile +++ b/mod/buildfile @@ -25,7 +25,7 @@ include ../web/server/ ./: mod{brep} {libue libus}{mod} -libu_src = options-types types-parsers build-config +libu_src = options-types types-parsers build-target-config mod{brep}: {hxx ixx txx cxx}{* -module-options -{$libu_src}} \ libus{mod} ../libbrep/lib{brep} ../web/server/libus{web-server} \ diff --git a/mod/mod-build-configs.cxx b/mod/mod-build-configs.cxx index 126c1d8..79c47f7 100644 --- a/mod/mod-build-configs.cxx +++ b/mod/mod-build-configs.cxx @@ -3,8 +3,6 @@ #include -#include // replace() - #include #include @@ -15,7 +13,6 @@ #include using namespace std; -using namespace bbot; using namespace brep::cli; // While currently the user-defined copy constructor is not required (we don't @@ -49,7 +46,7 @@ handle (request& rq, response& rs) HANDLER_DIAG; - if (build_conf_ == nullptr) + if (target_conf_ == nullptr) throw invalid_request (501, "not implemented"); const size_t page_configs (options_->build_config_page_entries ()); @@ -120,8 +117,8 @@ handle (request& rq, response& rs) // if (params.page () == 0) { - const strings& cls (build_conf_->classes); - const map& im (build_conf_->class_inheritance_map); + const strings& cls (target_conf_->classes); + const map& im (target_conf_->class_inheritance_map); s << DIV(ID="filter-heading") << "Build Configuration Classes" << ~DIV << P(ID="filter"); @@ -155,12 +152,12 @@ handle (request& rq, response& rs) // before printing the configurations. // size_t count (0); - vector configs; + vector configs; configs.reserve (page_configs); size_t skip (page * page_configs); size_t print (page_configs); - for (const build_config& c: *build_conf_) + for (const build_target_config& c: *target_conf_) { if (belongs (c, params.class_name ())) { @@ -185,7 +182,7 @@ handle (request& rq, response& rs) // Enclose the subsequent tables to be able to use nth-child CSS selector. // s << DIV; - for (const build_config* c: configs) + for (const build_target_config* c: configs) { s << TABLE(CLASS="proplist config") << TBODY diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx index 281c76c..af4b8e9 100644 --- a/mod/mod-build-force.cxx +++ b/mod/mod-build-force.cxx @@ -3,8 +3,6 @@ #include -#include // replace() - #include #include @@ -16,7 +14,6 @@ #include using namespace std; -using namespace bbot; using namespace brep::cli; using namespace odb::core; @@ -115,11 +112,6 @@ handle (request& rq, response& rs) version package_version (parse_version (params.version (), "package version")); - string& config (params.configuration ()); - - if (config.empty ()) - throw invalid_argument ("no configuration name"); - target_triplet target; try @@ -131,6 +123,16 @@ handle (request& rq, response& rs) throw invalid_argument (string ("invalid target: ") + e.what ()); } + string& target_config (params.target_config ()); + + if (target_config.empty ()) + throw invalid_argument ("no target configuration name"); + + string& package_config (params.package_config ()); + + if (package_config.empty ()) + throw invalid_argument ("no package configuration name"); + string& toolchain_name (params.toolchain_name ()); if (toolchain_name.empty ()) @@ -140,8 +142,9 @@ handle (request& rq, response& rs) "toolchain version")); id = build_id (package_id (move (tenant), move (p), package_version), - move (config), move (target), + move (target_config), + move (package_config), move (toolchain_name), toolchain_version); } @@ -161,9 +164,11 @@ handle (request& rq, response& rs) // Make sure the build configuration still exists. // - if (build_conf_map_->find (build_config_id {id.configuration, id.target}) == - build_conf_map_->end ()) - config_expired ("no configuration"); + if (target_conf_map_->find ( + build_target_config_id {id.target, + id.target_config_name}) == + target_conf_map_->end ()) + config_expired ("no target configuration"); // Load the package build configuration (if present), set the force flag and // update the object's persistent state. @@ -189,7 +194,8 @@ handle (request& rq, response& rs) l1 ([&]{trace << "force rebuild for " << b->tenant << ' ' << b->package_name << '/' << b->package_version << ' ' - << b->configuration << '/' << b->target << ' ' + << b->target_config_name << '/' << b->target << ' ' + << b->package_config_name << ' ' << b->toolchain_name << '-' << b->toolchain_version << ": " << reason;}); } diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx index 328d178..3841fad 100644 --- a/mod/mod-build-log.cxx +++ b/mod/mod-build-log.cxx @@ -3,8 +3,6 @@ #include -#include // find_if() - #include #include @@ -18,7 +16,6 @@ #include using namespace std; -using namespace bbot; using namespace brep::cli; using namespace odb::core; @@ -124,14 +121,6 @@ handle (request& rq, response& rs) assert (i != lpath.end () && *i == "log"); if (++i == lpath.end ()) - throw invalid_argument ("no configuration name"); - - string config (*i++); - - if (config.empty ()) - throw invalid_argument ("empty configuration name"); - - if (i == lpath.end ()) throw invalid_argument ("no target"); target_triplet target; @@ -145,6 +134,22 @@ handle (request& rq, response& rs) } if (i == lpath.end ()) + throw invalid_argument ("no target configuration name"); + + string target_config (*i++); + + if (target_config.empty ()) + throw invalid_argument ("empty target configuration name"); + + if (i == lpath.end ()) + throw invalid_argument ("no package configuration name"); + + string package_config (*i++); + + if (package_config.empty ()) + throw invalid_argument ("empty package configuration name"); + + if (i == lpath.end ()) throw invalid_argument ("no toolchain name"); string toolchain_name (*i++); @@ -158,8 +163,9 @@ handle (request& rq, response& rs) version toolchain_version (parse_version (*i++, "toolchain version")); id = build_id (package_id (tenant, move (name), package_version), - move (config), move (target), + move (target_config), + move (package_config), move (toolchain_name), toolchain_version); @@ -204,9 +210,11 @@ handle (request& rq, response& rs) // Make sure the build configuration still exists. // - if (build_conf_map_->find (build_config_id {id.configuration, id.target}) == - build_conf_map_->end ()) - config_expired ("no configuration"); + if (target_conf_map_->find ( + build_target_config_id {id.target, + id.target_config_name}) == + target_conf_map_->end ()) + config_expired ("no target configuration"); // Load the package build configuration (if present). // @@ -242,15 +250,14 @@ handle (request& rq, response& rs) if (!b->tenant.empty ()) os << options_->tenant_name () << ": " << b->tenant << endl << endl; - os << "package: " << b->package_name << endl - << "version: " << b->package_version << endl - << "toolchain: " << b->toolchain_name << '-' << b->toolchain_version - << endl - << "config: " << b->configuration << endl - << "target: " << b->target << endl - << "machine: " << b->machine << " (" << b->machine_summary << ")" - << endl - << "timestamp: "; + os << "package: " << b->package_name << endl + << "version: " << b->package_version << endl + << "toolchain: " << b->toolchain_name << '-' << b->toolchain_version << endl + << "target: " << b->target << endl + << "tgt config: " << b->target_config_name << endl + << "pkg config: " << b->package_config_name << endl + << "machine: " << b->machine << " (" << b->machine_summary << ")" << endl + << "timestamp: "; butl::to_stream (os, b->timestamp, diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx index 7eefe95..6f40a0b 100644 --- a/mod/mod-build-result.cxx +++ b/mod/mod-build-result.cxx @@ -189,22 +189,11 @@ handle (request& rq, response&) if (package_version != rqm.result.version) throw invalid_argument ("package version mismatch"); - b = p + 1; // Start of configuration name. - p = s.find ('/', b); // End of configuration name. - - if (p == string::npos) - throw invalid_argument ("no target"); - - string config (s, b, p - b); - - if (config.empty ()) - throw invalid_argument ("empty configuration name"); - b = p + 1; // Start of target. p = s.find ('/', b); // End of target. if (p == string::npos) - throw invalid_argument ("no toolchain name"); + throw invalid_argument ("no target configuration name"); target_triplet target; try @@ -216,6 +205,28 @@ handle (request& rq, response&) throw invalid_argument (string ("invalid target: ") + e.what ()); } + b = p + 1; // Start of target configuration name. + p = s.find ('/', b); // End of target configuration name. + + if (p == string::npos) + throw invalid_argument ("no package configuration name"); + + string target_config (s, b, p - b); + + if (target_config.empty ()) + throw invalid_argument ("empty target configuration name"); + + b = p + 1; // Start of package configuration name. + p = s.find ('/', b); // End of package configuration name. + + if (p == string::npos) + throw invalid_argument ("no toolchain name"); + + string package_config (s, b, p - b); + + if (package_config.empty ()) + throw invalid_argument ("empty package configuration name"); + b = p + 1; // Start of toolchain name. p = s.find ('/', b); // End of toolchain name. @@ -236,8 +247,9 @@ handle (request& rq, response&) version toolchain_version (parse_version ("toolchain version")); id = build_id (package_id (move (tenant), move (name), package_version), - move (config), move (target), + move (target_config), + move (package_config), move (toolchain_name), toolchain_version); @@ -278,18 +290,18 @@ handle (request& rq, response&) // Make sure the build configuration still exists. // - const bbot::build_config* cfg; + const build_target_config* tc; { - auto i (build_conf_map_->find (build_config_id {id.configuration, - id.target})); + auto i (target_conf_map_->find ( + build_target_config_id {id.target, id.target_config_name})); - if (i == build_conf_map_->end ()) + if (i == target_conf_map_->end ()) { warn_expired ("no build configuration"); return true; } - cfg = i->second; + tc = i->second; } // Load the built package (if present). @@ -526,13 +538,23 @@ handle (request& rq, response&) // `skip`, the configuration is hidden, or is now excluded by the // package. // - if (rqm.result.status != result_status::skip && belongs (*cfg, "all")) + if (rqm.result.status != result_status::skip && belongs (*tc, "all")) { shared_ptr p ( build_db_->load (b->id.package)); - if (!exclude (p->builds, p->constraints, *cfg)) - bld = move (b); + // The package configuration should be present (see mod-builds.cxx + // for details) but if it is not, let's log the warning. + // + if (const build_package_config* pc = find (b->package_config_name, + p->configs)) + { + if (!exclude (*pc, p->builds, p->constraints, *tc)) + bld = move (b); + } + else + warn << "cannot find configuration '" << b->package_config_name + << "' for package " << p->id.name << '/' << p->version; } } } @@ -554,12 +576,13 @@ handle (request& rq, response&) return true; } - string subj ((unforced ? "build " : "rebuild ") + - to_string (*bld->status) + ": " + - bld->package_name.string () + '/' + - bld->package_version.string () + '/' + - bld->configuration + '/' + - bld->target.string () + '/' + + string subj ((unforced ? "build " : "rebuild ") + + to_string (*bld->status) + ": " + + bld->package_name.string () + '/' + + bld->package_version.string () + ' ' + + bld->target_config_name + '/' + + bld->target.string () + ' ' + + bld->package_config_name + ' ' + bld->toolchain_name + '-' + bld->toolchain_version.string ()); // Send notification emails to the interested parties. diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx index 5ba6d06..a15ff90 100644 --- a/mod/mod-build-task.cxx +++ b/mod/mod-build-task.cxx @@ -21,7 +21,6 @@ #include #include -#include #include @@ -30,6 +29,8 @@ #include #include +#include + #include using namespace std; @@ -155,20 +156,20 @@ handle (request& rq, response& rs) task_response_manifest tsm; - // Map build configurations to machines that are capable of building them. - // The first matching machine is selected for each configuration. + // Map build target configurations to machines that are capable of building + // them. The first matching machine is selected for each configuration. // struct config_machine { - const build_config* config; + const build_target_config* config; machine_header_manifest* machine; }; - using config_machines = map; + using config_machines = map; config_machines conf_machines; - for (const auto& c: *build_conf_) + for (const auto& c: *target_conf_) { for (auto& m: tqm.machines) { @@ -180,24 +181,25 @@ handle (request& rq, response& rs) dash_components_to_path (c.machine_pattern), dir_path () /* start */, path_match_flags::match_absent)) - conf_machines.emplace (build_config_id {c.name, c.target}, + conf_machines.emplace (build_target_config_id {c.target, c.name}, config_machine {&c, &m}); } catch (const invalid_path&) {} } } - // Go through packages until we find one that has no build configuration - // present in the database, or is in the building state but expired - // (collectively called unbuilt). If such a package configuration is found - // then put it into the building state, set the current timestamp and respond - // with the task for building this package configuration. + // Go through package build configurations until we find one that has no + // build target configuration present in the database, or is in the building + // state but expired (collectively called unbuilt). If such a target + // configuration is found then put it into the building state, set the + // current timestamp and respond with the task for building this package + // configuration. // // While trying to find a non-built package configuration we will also - // collect the list of the built package configurations which it's time to - // rebuild. So if no unbuilt package is found, we will pickup one to - // rebuild. The rebuild preference is given in the following order: the - // greater force state, the greater overall status, the lower timestamp. + // collect the list of the built configurations which it's time to + // rebuild. So if no unbuilt package configuration is found, we will pickup + // one to rebuild. The rebuild preference is given in the following order: + // the greater force state, the greater overall status, the lower timestamp. // if (!conf_machines.empty ()) { @@ -208,6 +210,7 @@ handle (request& rq, response& rs) // auto task = [this] (shared_ptr&& b, shared_ptr&& p, + build_package_config&& pc, shared_ptr&& t, const config_machine& cm) -> task_response_manifest { @@ -215,12 +218,13 @@ handle (request& rq, response& rs) chrono::duration_cast ( b->timestamp.time_since_epoch ()).count ()); - string session (b->tenant + '/' + - b->package_name.string () + '/' + - b->package_version.string () + '/' + - b->configuration + '/' + - b->target.string () + '/' + - b->toolchain_name + '/' + + string session (b->tenant + '/' + + b->package_name.string () + '/' + + b->package_version.string () + '/' + + b->target.string () + '/' + + b->target_config_name + '/' + + b->package_config_name + '/' + + b->toolchain_name + '/' + b->toolchain_version.string () + '/' + to_string (ts)); @@ -255,13 +259,24 @@ handle (request& rq, response& rs) { shared_ptr p (td.package.load ()); + // Use the default test package configuration. + // + // Note that potentially the test package default configuration may + // contain some (bpkg) arguments associated, but we currently don't + // provide build bot worker with such information. This, however, is + // probably too far fetched so let's keep it simple for now. + // + const build_package_config* pc (find ("default", p->configs)); + assert (pc != nullptr); // Must always be present. + // Use the `all` class as a least restrictive default underlying // build class set. Note that we should only apply the explicit // build restrictions to the external test packages (think about // the `builds: all` and `builds: -windows` manifest values for // the primary and external test packages, respectively). // - if (exclude (p->builds, + if (exclude (*pc, + p->builds, p->constraints, *cm.config, nullptr /* reason */, @@ -290,6 +305,7 @@ handle (request& rq, response& rs) cm.config->target, cm.config->environment, cm.config->args, + move (pc.arguments), belongs (*cm.config, module_pkg ? "build2" : "host"), cm.config->warning_regexes, move (t->interactive), @@ -583,23 +599,26 @@ handle (request& rq, response& rs) // Prepare the build prepared query. // // Note that we can not query the database for configurations that a - // package was not built with, as the database contains only those package + // package was not built with, as the database contains only those build // configurations that have already been acted upon (initially empty). // - // This is why we query the database for package configurations that - // should not be built (in the built state, or in the building state and - // not expired). Having such a list we will select the first build + // This is why we query the database for configurations that should not be + // built (in the built state, or in the building state and not + // expired). Having such a list we will select the first build // configuration that is not in the list (if available) for the response. // using bld_query = query; using prep_bld_query = prepared_query; package_id id; + string pkg_config_name; bld_query sq (false); for (const auto& cm: conf_machines) - sq = sq || (bld_query::id.configuration == cm.first.name && - bld_query::id.target == cm.first.target); + sq = sq || (bld_query::id.target == cm.first.target && + bld_query::id.target_config_name == cm.first.config && + bld_query::id.package_config_name == + bld_query::_ref (pkg_config_name)); bld_query bq ( equal (bld_query::id.package, id) && @@ -658,7 +677,7 @@ handle (request& rq, response& rs) // target, environment, arguments, and warning-detecting regular // expressions. // - auto controller_checksum = [] (const build_config& c) + auto controller_checksum = [] (const build_target_config& c) { sha256 cs ("1"); // Hash the logic version. @@ -705,149 +724,157 @@ handle (request& rq, response& rs) { id = move (bp.id); - // Iterate through the package configurations and erase those that - // don't need building from the build configuration map. All those - // configurations that remained can be built. We will take the first - // one, if present. - // - // Also save the built package configurations for which it's time to - // be rebuilt. - // - config_machines configs (conf_machines); // Make a copy for this pkg. - auto pkg_builds (bld_prep_query.execute ()); + shared_ptr p (build_db_->load (id)); - for (auto i (pkg_builds.begin ()); i != pkg_builds.end (); ++i) + for (build_package_config& pc: p->configs) { - auto j (configs.find (build_config_id {i->id.configuration, - i->id.target})); + pkg_config_name = pc.name; - // Outdated configurations are already excluded with the database - // query. + // Iterate through the built configurations and erase them from the + // build configuration map. All those configurations that remained + // can be built. We will take the first one, if present. // - assert (j != configs.end ()); - configs.erase (j); - - if (i->state == build_state::built) - { - assert (i->force != force_state::forcing); - - if (needs_rebuild (*i)) - rebuilds.emplace_back (i.load ()); - } - } - - if (!configs.empty ()) - { - // Find the first build configuration that is not excluded by the - // package. + // Also save the built configurations for which it's time to be + // rebuilt. // - shared_ptr p (build_db_->load (id)); + config_machines configs (conf_machines); // Make a copy for this pkg. + auto pkg_builds (bld_prep_query.execute ()); - auto i (configs.begin ()); - auto e (configs.end ()); - - for (; - i != e && - exclude (p->builds, p->constraints, *i->second.config); - ++i) ; - - if (i != e) + for (auto i (pkg_builds.begin ()); i != pkg_builds.end (); ++i) { - config_machine& cm (i->second); - machine_header_manifest& mh (*cm.machine); - - build_id bid (move (id), - cm.config->name, - cm.config->target, - move (tqm.toolchain_name), - toolchain_version); - - shared_ptr b (build_db_->find (bid)); - optional cl (challenge ()); - - shared_ptr t ( - build_db_->load (bid.package.tenant)); + auto j ( + configs.find (build_target_config_id {i->id.target, + i->id.target_config_name})); - // Move the interactive build login information into the build - // object, if the package to be built interactively. + // Outdated configurations are already excluded with the database + // query. // - optional login (t->interactive - ? move (tqm.interactive_login) - : nullopt); + assert (j != configs.end ()); + configs.erase (j); - // If build configuration doesn't exist then create the new one - // and persist. Otherwise put it into the building state, refresh - // the timestamp and update. - // - if (b == nullptr) + if (i->state == build_state::built) { - b = make_shared (move (bid.package.tenant), - move (bid.package.name), - move (bp.version), - move (bid.configuration), - move (bid.target), - move (bid.toolchain_name), - move (toolchain_version), - move (login), - move (agent_fp), - move (cl), - mh.name, - move (mh.summary), - controller_checksum (*cm.config), - machine_checksum (*cm.machine)); - - build_db_->persist (b); + assert (i->force != force_state::forcing); + + if (needs_rebuild (*i)) + rebuilds.emplace_back (i.load ()); } - else - { - // The package configuration is in the building state. - // - // Note that in both cases we keep the status intact to be able - // to compare it with the final one in the result request - // handling in order to decide if to send the notification - // email. The same is true for the forced flag (in the sense - // that we don't set the force state to unforced). - // - assert (b->state == build_state::building); + } - b->state = build_state::building; - b->interactive = move (login); + if (!configs.empty ()) + { + // Find the first build configuration that is not excluded by the + // package configuration. + // + auto i (configs.begin ()); + auto e (configs.end ()); - // Switch the force state not to reissue the task after the - // forced rebuild timeout. Note that the result handler will - // still recognize that the rebuild was forced. - // - if (b->force == force_state::forcing) - b->force = force_state::forced; + for (; + i != e && + exclude (pc, p->builds, p->constraints, *i->second.config); + ++i) ; - b->agent_fingerprint = move (agent_fp); - b->agent_challenge = move (cl); - b->machine = mh.name; - b->machine_summary = move (mh.summary); + if (i != e) + { + config_machine& cm (i->second); + machine_header_manifest& mh (*cm.machine); - string ccs (controller_checksum (*cm.config)); - string mcs (machine_checksum (*cm.machine)); + build_id bid (move (id), + cm.config->target, + cm.config->name, + move (pkg_config_name), + move (tqm.toolchain_name), + toolchain_version); - // Issue the hard rebuild if it is forced or the configuration - // or machine has changed. - // - if (b->hard_timestamp <= hard_rebuild_expiration || - b->force == force_state::forced || - b->controller_checksum != ccs || - b->machine_checksum != mcs) - convert_to_hard (b); + shared_ptr b (build_db_->find (bid)); + optional cl (challenge ()); - b->controller_checksum = move (ccs); - b->machine_checksum = move (mcs); + shared_ptr t ( + build_db_->load (bid.package.tenant)); - b->timestamp = system_clock::now (); + // Move the interactive build login information into the build + // object, if the package to be built interactively. + // + optional login (t->interactive + ? move (tqm.interactive_login) + : nullopt); - build_db_->update (b); + // If build configuration doesn't exist then create the new one + // and persist. Otherwise put it into the building state, refresh + // the timestamp and update. + // + if (b == nullptr) + { + b = make_shared (move (bid.package.tenant), + move (bid.package.name), + move (bp.version), + move (bid.target), + move (bid.target_config_name), + move (bid.package_config_name), + move (bid.toolchain_name), + move (toolchain_version), + move (login), + move (agent_fp), + move (cl), + mh.name, + move (mh.summary), + controller_checksum (*cm.config), + machine_checksum (*cm.machine)); + + build_db_->persist (b); + } + else + { + // The build configuration is in the building state. + // + // Note that in both cases we keep the status intact to be + // able to compare it with the final one in the result request + // handling in order to decide if to send the notification + // email. The same is true for the forced flag (in the sense + // that we don't set the force state to unforced). + // + assert (b->state == build_state::building); + + b->state = build_state::building; + b->interactive = move (login); + + // Switch the force state not to reissue the task after the + // forced rebuild timeout. Note that the result handler will + // still recognize that the rebuild was forced. + // + if (b->force == force_state::forcing) + b->force = force_state::forced; + + b->agent_fingerprint = move (agent_fp); + b->agent_challenge = move (cl); + b->machine = mh.name; + b->machine_summary = move (mh.summary); + + string ccs (controller_checksum (*cm.config)); + string mcs (machine_checksum (*cm.machine)); + + // Issue the hard rebuild if it is forced or the configuration + // or machine has changed. + // + if (b->hard_timestamp <= hard_rebuild_expiration || + b->force == force_state::forced || + b->controller_checksum != ccs || + b->machine_checksum != mcs) + convert_to_hard (b); + + b->controller_checksum = move (ccs); + b->machine_checksum = move (mcs); + + b->timestamp = system_clock::now (); + + build_db_->update (b); + } + + // Finally, prepare the task response manifest. + // + tsm = task (move (b), move (p), move (pc), move (t), cm); + break; // Bail out from the package configurations loop. } - - // Finally, prepare the task response manifest. - // - tsm = task (move (b), move (p), move (t), cm); } } @@ -861,13 +888,12 @@ handle (request& rq, response& rs) t.commit (); } - // If we don't have an unbuilt package, then let's see if we have a - // package to rebuild. + // If we don't have an unbuilt package, then let's see if we have a build + // configuration to rebuild. // if (tsm.session.empty () && !rebuilds.empty ()) { - // Sort the package configuration rebuild list with the following sort - // priority: + // Sort the configuration rebuild list with the following sort priority: // // 1: force state // 2: overall status @@ -890,7 +916,7 @@ handle (request& rq, response& rs) optional cl (challenge ()); - // Pick the first package configuration from the ordered list. + // Pick the first build configuration from the ordered list. // // Note that the configurations and packages may not match the required // criteria anymore (as we have committed the database transactions that @@ -911,8 +937,9 @@ handle (request& rq, response& rs) b->state == build_state::built && needs_rebuild (*b)) { - auto i (conf_machines.find (build_config_id {b->configuration, - b->target})); + auto i (conf_machines.find ( + build_target_config_id {b->target, + b->target_config_name})); // Only actual package configurations are loaded (see above). // @@ -934,12 +961,17 @@ handle (request& rq, response& rs) ? build_db_->load (p->id.tenant) : nullptr); - if (p != nullptr && + build_package_config* pc (p != nullptr + ? find (b->package_config_name, + p->configs) + : nullptr); + + if (pc != nullptr && p->buildable && (imode == interactive_mode::both || (t->interactive.has_value () == (imode == interactive_mode::true_))) && - !exclude (p->builds, p->constraints, *cm.config)) + !exclude (*pc, p->builds, p->constraints, *cm.config)) { assert (b->status); @@ -985,7 +1017,7 @@ handle (request& rq, response& rs) build_db_->update (b); - tsm = task (move (b), move (p), move (t), cm); + tsm = task (move (b), move (p), move (*pc), move (t), cm); } } diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx index b9d841d..095dee3 100644 --- a/mod/mod-builds.cxx +++ b/mod/mod-builds.cxx @@ -4,7 +4,6 @@ #include #include -#include // find_if() #include @@ -32,7 +31,6 @@ using namespace std; using namespace butl; -using namespace bbot; using namespace web; using namespace odb::core; using namespace brep::cli; @@ -138,7 +136,7 @@ match (const C qc, const string& pattern) // template static inline query -build_query (const brep::vector* config_ids, +build_query (const brep::vector* config_ids, const brep::params::builds& params, const brep::optional& tenant, const brep::optional& archived) @@ -159,8 +157,8 @@ build_query (const brep::vector* config_ids, { query sq (false); for (const auto& id: *config_ids) - sq = sq || (qb::id.configuration == id.name && - qb::id.target == id.target); + sq = sq || (qb::id.target == id.target && + qb::id.target_config_name == id.config); q = q && sq; } @@ -191,11 +189,11 @@ build_query (const brep::vector* config_ids, // Build toolchain name/version. // - const string& tc (params.toolchain ()); + const string& th (params.toolchain ()); - if (tc != "*") + if (th != "*") { - size_t p (tc.find ('-')); + size_t p (th.find ('-')); if (p == string::npos) // Invalid format. throw invalid_argument (""); @@ -203,8 +201,8 @@ build_query (const brep::vector* config_ids, // the exact version revision, so an absent and zero revisions have the // same semantics and the zero revision is folded. // - string tn (tc, 0, p); - version tv (string (tc, p + 1)); // May throw invalid_argument. + string tn (th, 0, p); + version tv (string (th, p + 1)); // May throw invalid_argument. q = q && qb::id.toolchain_name == tn && @@ -213,20 +211,20 @@ build_query (const brep::vector* config_ids, true /* revision */); } - // Build configuration name. + // Build target. // - if (!params.configuration ().empty ()) - q = q && match (qb::id.configuration, params.configuration ()); + if (!params.target ().empty ()) + q = q && match (qb::id.target, params.target ()); - // Build machine name. + // Build target configuration name. // - if (!params.machine ().empty ()) - q = q && match (qb::machine, params.machine ()); + if (!params.target_config ().empty ()) + q = q && match (qb::id.target_config_name, params.target_config ()); - // Build target. + // Build package configuration name. // - if (!params.target ().empty ()) - q = q && match (qb::id.target, params.target ()); + if (!params.package_config ().empty ()) + q = q && match (qb::id.package_config_name, params.package_config ()); // Build result. // @@ -244,7 +242,7 @@ build_query (const brep::vector* config_ids, // May throw invalid_argument. // - result_status st (to_result_status (rs)); + result_status st (bbot::to_result_status (rs)); if (st != result_status::success) { @@ -364,11 +362,6 @@ handle (request& rq, response& rs) throw invalid_request (400, e.what ()); } - // Override the name parameter for the old URL (see options.cli for details). - // - if (params.name_legacy_specified ()) - params.name (params.name_legacy ()); - const char* title ("Builds"); xml::serializer s (rs.content (), title); @@ -430,16 +423,16 @@ handle (request& rq, response& rs) // the selected toolchain is still present in the database. Otherwise // fallback to the * wildcard selection. // - string ctc ("*"); + string cth ("*"); vector> toolchain_opts ({{"*", "*"}}); { for (const auto& t: toolchains) { - string tc (t.first + '-' + t.second.string ()); - toolchain_opts.emplace_back (tc, tc); + string th (t.first + '-' + t.second.string ()); + toolchain_opts.emplace_back (th, th); - if (tc == params.toolchain ()) - ctc = move (tc); + if (th == params.toolchain ()) + cth = move (th); } } @@ -455,34 +448,42 @@ handle (request& rq, response& rs) << TBODY << TR_INPUT ("name", "builds", params.name (), "*", true) << TR_INPUT ("version", "pv", params.version (), "*") - << TR_SELECT ("toolchain", "tc", ctc, toolchain_opts) + << TR_SELECT ("toolchain", "th", cth, toolchain_opts) + << TR_INPUT ("target", "tg", params.target (), "*") - << TR(CLASS="config") - << TH << "config" << ~TH + << TR(CLASS="tgt-config") + << TH << "tgt config" << ~TH << TD << *INPUT(TYPE="text", - NAME="cf", - VALUE=params.configuration (), + NAME="tc", + VALUE=params.target_config (), PLACEHOLDER="*", - LIST="configs") - << DATALIST(ID="configs") + LIST="target-configs") + << DATALIST(ID="target-configs") << *OPTION(VALUE="*"); - // Print unique config names from the config map. + // Print unique config names from the target config map. // set conf_names; - for (const auto& c: *build_conf_map_) + for (const auto& c: *target_conf_map_) { - if (conf_names.insert (c.first.name.get ().c_str ()).second) - s << *OPTION(VALUE=c.first.name.get ()); + if (conf_names.insert (c.first.config.get ().c_str ()).second) + s << *OPTION(VALUE=c.first.config.get ()); } s << ~DATALIST << ~TD << ~TR - << TR_INPUT ("target", "tg", params.target (), "*") - << TR_INPUT ("machine", "mn", params.machine (), "*") + << TR(CLASS="pkg-config") + << TH << "pkg config" << ~TH + << TD + << *INPUT(TYPE="text", + NAME="pc", + VALUE=params.package_config (), + PLACEHOLDER="*") + << ~TD + << ~TR << TR_SELECT ("result", "rs", params.result (), build_results) << ~TBODY << ~TABLE @@ -504,16 +505,19 @@ handle (request& rq, response& rs) s << DIV_COUNTER (build_count, "Build", "Builds"); }; + const string& tgt (params.target ()); + const string& tgt_cfg (params.target_config ()); + const string& pkg_cfg (params.package_config ()); + // We will not display hidden configurations, unless the configuration is // specified explicitly. // - bool exclude_hidden (params.configuration ().empty () || - path_pattern (params.configuration ())); + bool exclude_hidden (tgt_cfg.empty () || path_pattern (tgt_cfg)); - vector conf_ids; - conf_ids.reserve (build_conf_map_->size ()); + vector conf_ids; + conf_ids.reserve (target_conf_map_->size ()); - for (const auto& c: *build_conf_map_) + for (const auto& c: *target_conf_map_) { if (!exclude_hidden || belongs (*c.second, "all")) conf_ids.push_back (c.first); @@ -600,17 +604,36 @@ handle (request& rq, response& rs) { shared_ptr& b (pb.build); - auto i (build_conf_map_->find (build_config_id {b->configuration, - b->target})); - assert (i != build_conf_map_->end ()); + auto i (target_conf_map_->find ( + build_target_config_id {b->target, + b->target_config_name})); - // Match the configuration against the package build - // expressions/constraints. + assert (i != target_conf_map_->end ()); + + // Match the target configuration against the package build + // configuration expressions/constraints. // shared_ptr p ( build_db_->load (b->id.package)); - if (!exclude (p->builds, p->constraints, *i->second)) + const build_package_config* pc (find (b->package_config_name, + p->configs)); + + // The package configuration should be present since the + // configurations set cannot change if the package version doesn't + // change. If that's not the case, then the database has probably + // been manually amended. In this case let's just skip such a build + // as if it excluded and log the warning. + // + if (pc == nullptr) + { + warn << "cannot find configuration '" << b->package_config_name + << "' for package " << p->id.name << '/' << p->version; + + continue; + } + + if (!exclude (*pc, p->builds, p->constraints, *i->second)) { if (skip != 0) --skip; @@ -646,7 +669,7 @@ handle (request& rq, response& rs) } } } - + // // Print the filter form after the build count is calculated. Note: // query_toolchains() must be called inside the build db transaction. // @@ -680,9 +703,9 @@ handle (request& rq, response& rs) << TR_VALUE ("toolchain", b.toolchain_name + '-' + b.toolchain_version.string ()) - << TR_VALUE ("config", b.configuration) << TR_VALUE ("target", b.target.string ()) - << TR_VALUE ("machine", b.machine) + << TR_VALUE ("tgt config", b.target_config_name) + << TR_VALUE ("pkg config", b.package_config_name) << TR_VALUE ("timestamp", ts); if (b.interactive) // Note: can only be present for the building state. @@ -704,47 +727,55 @@ handle (request& rq, response& rs) else // Print unbuilt package configurations. { // Parameters to use for package build configurations queries. Note that - // we cleanup the machine and the result filter arguments, as they are - // irrelevant for unbuilt configurations. + // we cleanup the result filter argument, as it is irrelevant for unbuilt + // configurations. // params::builds bld_params (params); - bld_params.machine ().clear (); bld_params.result () = "*"; - // Query toolchains, filter build configurations and toolchains, and - // create the set of configuration/toolchain combinations, that we will - // print for packages. Also calculate the number of unbuilt package - // configurations. + // Query toolchains, filter build target configurations and toolchains, + // and create the set of target configuration/toolchain combinations, that + // we will print for package configurations. Also calculate the number of + // unbuilt package configurations. // toolchains toolchains; - // Note that config_toolchains contains shallow references to the - // toolchain names and versions. + // Target configuration/toolchain combination. + // + // Note: all members are the shallow references. // - set config_toolchains; + struct target_config_toolchain + { + const butl::target_triplet& target; + const string& target_config; + const string& toolchain_name; + const bpkg::version& toolchain_version; + }; + + vector config_toolchains; { transaction t (build_db_->begin ()); toolchains = query_toolchains (); - string tc_name; - version tc_version; - const string& tc (params.toolchain ()); + string th_name; + version th_version; + const string& th (params.toolchain ()); - if (tc != "*") + if (th != "*") try { - size_t p (tc.find ('-')); + size_t p (th.find ('-')); if (p == string::npos) // Invalid format. throw invalid_argument (""); - tc_name.assign (tc, 0, p); + th_name.assign (th, 0, p); // May throw invalid_argument. // // Note that an absent and zero revisions have the same semantics, // so the zero revision is folded (see above for details). // - tc_version = version (string (tc, p + 1)); + th_version = version (string (th, p + 1)); } catch (const invalid_argument&) { @@ -754,63 +785,64 @@ handle (request& rq, response& rs) throw invalid_request (400, "invalid toolchain"); } - const string& pc (params.configuration ()); - const string& tg (params.target ()); - vector configs; + vector target_configs; - for (const auto& c: *build_conf_) + for (const auto& c: *target_conf_) { - if ((pc.empty () || path_match (c.name, pc)) && // Filter by name. + // Filter by name. + // + if ((tgt_cfg.empty () || path_match (c.name, tgt_cfg)) && // Filter by target. // - (tg.empty () || path_match (c.target.string (), tg)) && + (tgt.empty () || path_match (c.target.string (), tgt)) && (!exclude_hidden || belongs (c, "all"))) // Filter hidden. { - configs.push_back (&c); + target_configs.push_back (&c); for (const auto& t: toolchains) { // Filter by toolchain. // - if (tc == "*" || (t.first == tc_name && t.second == tc_version)) - config_toolchains.insert ( - config_toolchain {c.name, c.target, t.first, t.second}); + if (th == "*" || (t.first == th_name && t.second == th_version)) + config_toolchains.push_back ( + target_config_toolchain {c.target, c.name, t.first, t.second}); } } } - // Calculate the number of unbuilt package configurations as a - // difference between the maximum possible number of unbuilt - // configurations and the number of existing package builds. - // - // Note that we also need to deduct the package-excluded configurations - // count from the maximum possible number of unbuilt configurations. The - // only way to achieve this is to traverse through the packages and - // match their build expressions/constraints against our configurations. - // - // Also note that some existing builds can now be excluded by packages - // due to the build configuration target or class set change. We should - // deduct such builds count from the number of existing package builds. - // - size_t nmax (config_toolchains.size () * - build_db_->query_value ( - package_query ( - params, tn, false /* archived */))); + if (!config_toolchains.empty ()) + { + // Calculate the number of unbuilt package configurations as a + // difference between the possible number of unbuilt configurations + // and the number of existing package builds. + // + // Note that some existing builds can now be excluded by package + // configurations due to the build target configuration class set + // change. We should deduct such builds count from the number of + // existing package configurations builds. + // + // The only way to calculate both numbers is to traverse through the + // package configurations and match their build + // expressions/constraints against our target configurations. + // + size_t npos (0); - size_t ncur = build_db_->query_value ( - build_query ( - &conf_ids, bld_params, tn, false /* archived */)); + size_t ncur = build_db_->query_value ( + build_query ( + &conf_ids, bld_params, tn, false /* archived */)); - // From now we will be using specific package name and version for each - // build database query. - // - bld_params.name ().clear (); - bld_params.version ().clear (); + // From now we will be using specific values for the below filters for + // each build database query. Note that the toolchain is the only + // filter left in bld_params. + // + bld_params.name ().clear (); + bld_params.version ().clear (); + bld_params.target ().clear (); + bld_params.target_config ().clear (); + bld_params.package_config ().clear (); - if (!config_toolchains.empty ()) - { // Prepare the build count prepared query. // // For each package-excluded configuration we will query the number of @@ -820,15 +852,17 @@ handle (request& rq, response& rs) using prep_bld_query = prepared_query; package_id id; - string config; target_triplet target; + string target_config_name; + string package_config_name; const auto& bid (bld_query::build::id); bld_query bq ( - equal (bid.package, id) && - bid.configuration == bld_query::_ref (config) && - bid.target == bld_query::_ref (target) && + equal (bid.package, id) && + bid.target == bld_query::_ref (target) && + bid.target_config_name == bld_query::_ref (target_config_name) && + bid.package_config_name == bld_query::_ref (package_config_name) && // Note that the query already constrains configurations via the // configuration name and target. @@ -846,15 +880,16 @@ handle (request& rq, response& rs) build_db_->prepare_query ( "mod-builds-build-count-query", bq)); - size_t nt (tc == "*" ? toolchains.size () : 1); + // Number of possible builds per package configuration. + // + size_t nt (th == "*" ? toolchains.size () : 1); // The number of packages can potentially be large, and we may // implement some caching in the future. However, the caching will not // be easy as the cached values depend on the filter form parameters. // query q ( - package_query ( - params, tn, false /* archived */)); + package_query (params, tn, false /* archived */)); for (auto& bp: build_db_->query (q)) { @@ -862,22 +897,33 @@ handle (request& rq, response& rs) shared_ptr p (build_db_->load (id)); - for (const auto& c: configs) + for (const build_package_config& c: p->configs) { - if (exclude (p->builds, p->constraints, *c)) + // Filter by package config name. + // + if (pkg_cfg.empty () || path_match (c.name, pkg_cfg)) { - nmax -= nt; - - config = c->name; - target = c->target; - ncur -= bld_prep_query.execute_value (); + for (const auto& tc: target_configs) + { + if (exclude (c, p->builds, p->constraints, *tc)) + { + target = tc->target; + target_config_name = tc->name; + package_config_name = c.name; + ncur -= bld_prep_query.execute_value (); + } + else + npos += nt; + } } } } - } - assert (nmax >= ncur); - count = nmax - ncur; + assert (npos >= ncur); + count = npos - ncur; + } + else + count = 0; t.commit (); } @@ -893,8 +939,9 @@ handle (request& rq, response& rs) // 3: package tenant // 4: toolchain name // 5: toolchain version (descending) - // 6: configuration name - // 7: configuration target + // 6: target + // 7: target configuration name + // 8: package configuration name // // Prepare the build package prepared query. // @@ -946,15 +993,14 @@ handle (request& rq, response& rs) package_id id; - bld_query bq ( - equal (bld_query::build::id.package, id) && + bld_query bq (equal (bld_query::build::id.package, id) && - // Note that while the query already constrains the tenant via the build - // package id, we still need to pass the tenant not to erroneously - // filter out the private tenants. - // - build_query ( - &conf_ids, bld_params, tn, false /* archived */)); + // Note that while the query already constrains the tenant + // via the build package id, we still need to pass the + // tenant not to erroneously filter out the private tenants. + // + build_query ( + &conf_ids, bld_params, tn, false /* archived */)); prep_bld_query bld_prep_query ( conn->prepare_query ("mod-builds-build-query", bq)); @@ -986,22 +1032,35 @@ handle (request& rq, response& rs) { id = move (p.id); + shared_ptr bp (build_db_->load (id)); + // Copy configuration/toolchain combinations for this package, // skipping excluded configurations. // set unbuilt_configs; - { - shared_ptr p (build_db_->load (id)); - for (const auto& ct: config_toolchains) + for (const build_package_config& pc: bp->configs) + { + // Filter by package config name. + // + if (pkg_cfg.empty () || path_match (pc.name, pkg_cfg)) { - auto i (build_conf_map_->find (build_config_id {ct.configuration, - ct.target})); - - assert (i != build_conf_map_->end ()); - - if (!exclude (p->builds, p->constraints, *i->second)) - unbuilt_configs.insert (ct); + for (const target_config_toolchain& ct: config_toolchains) + { + auto i ( + target_conf_map_->find ( + build_target_config_id {ct.target, ct.target_config})); + + assert (i != target_conf_map_->end ()); + + if (!exclude (pc, bp->builds, bp->constraints, *i->second)) + unbuilt_configs.insert ( + config_toolchain {ct.target, + ct.target_config, + pc.name, + ct.toolchain_name, + ct.toolchain_version}); + } } } @@ -1012,8 +1071,9 @@ handle (request& rq, response& rs) { const build& b (*pb.build); - unbuilt_configs.erase (config_toolchain {b.id.configuration, - b.id.target, + unbuilt_configs.erase (config_toolchain {b.target, + b.target_config_name, + b.package_config_name, b.toolchain_name, b.toolchain_version}); } @@ -1035,8 +1095,9 @@ handle (request& rq, response& rs) << TR_VALUE ("toolchain", string (ct.toolchain_name) + '-' + ct.toolchain_version.string ()) - << TR_VALUE ("config", ct.configuration) - << TR_VALUE ("target", ct.target.string ()); + << TR_VALUE ("target", ct.target.string ()) + << TR_VALUE ("tgt config", ct.target_config) + << TR_VALUE ("pkg config", ct.package_config); // In the global view mode add the tenant builds link. Note that // the global view (and the link) makes sense only in the @@ -1084,10 +1145,10 @@ handle (request& rq, response& rs) }; add_filter ("pv", params.version ()); - add_filter ("tc", params.toolchain (), "*"); - add_filter ("cf", params.configuration ()); - add_filter ("mn", params.machine ()); - add_filter ("tg", params.target ()); + add_filter ("th", params.toolchain (), "*"); + add_filter ("tg", tgt); + add_filter ("tc", tgt_cfg); + add_filter ("pc", pkg_cfg); add_filter ("rs", params.result (), "*"); s << DIV_PAGER (page, count, page_configs, options_->build_pages (), u) diff --git a/mod/mod-package-version-details.cxx b/mod/mod-package-version-details.cxx index 37eb3c6..b158228 100644 --- a/mod/mod-package-version-details.cxx +++ b/mod/mod-package-version-details.cxx @@ -528,15 +528,14 @@ handle (request& rq, response& rs) if (builds) { - using bbot::build_config; - s << H3 << "Builds" << ~H3 << DIV(ID="builds"); - auto exclude = [&pkg, this] (const build_config& cfg, - string* reason = nullptr) + auto exclude = [&pkg, this] (const build_package_config& pc, + const build_target_config& tc, + string* rs = nullptr) { - return this->exclude (pkg->builds, pkg->build_constraints, cfg, reason); + return this->exclude (pc, pkg->builds, pkg->build_constraints, tc, rs); }; timestamp now (system_clock::now ()); @@ -569,34 +568,46 @@ handle (request& rq, response& rs) } // Compose the configuration filtering sub-query and collect unbuilt - // configurations, skipping those that are hidden or excluded by the - // package. + // target configurations, skipping those that are hidden or excluded by + // the package configurations. // using query = query; query sq (false); set unbuilt_configs; - for (const auto& c: *build_conf_map_) + for (const build_package_config& pc: pkg->build_configs) { - const build_config& cfg (*c.second); - - if (belongs (cfg, "all") && !exclude (cfg)) + for (const auto& bc: *target_conf_map_) { - const build_config_id& id (c.first); + const build_target_config& tc (*bc.second); - sq = sq || (query::id.configuration == id.name && - query::id.target == id.target); + if (belongs (tc, "all") && !exclude (pc, tc)) + { + const build_target_config_id& id (bc.first); - // Note: we will erase built configurations from the unbuilt - // configurations set later (see below). - // - for (const auto& t: toolchains) - unbuilt_configs.insert ( - config_toolchain {cfg.name, cfg.target, t.first, t.second}); + sq = sq || (query::id.target == id.target && + query::id.target_config_name == id.config && + query::id.package_config_name == pc.name); + + // Note: we will erase built configurations from the unbuilt + // configurations set later (see below). + // + for (const auto& t: toolchains) + unbuilt_configs.insert (config_toolchain {tc.target, + tc.name, + pc.name, + t.first, + t.second}); + } } } + // Let's not print the package configuration row if the default + // configuration is the only one. + // + bool ppc (pkg->build_configs.size () != 1); // Note: can't be empty. + // Print the package built configurations in the time-descending order. // for (auto& b: build_db_->query ( @@ -617,9 +628,13 @@ handle (request& rq, response& rs) << TR_VALUE ("toolchain", b.toolchain_name + '-' + b.toolchain_version.string ()) - << TR_VALUE ("config", - b.configuration + " / " + b.target.string ()) - << TR_VALUE ("timestamp", ts); + << TR_VALUE ("target", b.target.string ()) + << TR_VALUE ("tgt config", b.target_config_name); + + if (ppc) + s << TR_VALUE ("pkg config", b.package_config_name); + + s << TR_VALUE ("timestamp", ts); if (b.interactive) // Note: can only be present for the building state. s << TR_VALUE ("login", *b.interactive); @@ -631,8 +646,9 @@ handle (request& rq, response& rs) // While at it, erase the built configuration from the unbuilt // configurations set. // - unbuilt_configs.erase (config_toolchain {b.configuration, - b.target, + unbuilt_configs.erase (config_toolchain {b.target, + b.target_config_name, + b.package_config_name, b.toolchain_name, b.toolchain_version}); } @@ -642,7 +658,9 @@ handle (request& rq, response& rs) // // 1: toolchain name // 2: toolchain version (descending) - // 3: configuration name + // 3: target + // 4: target configuration name + // 5: package configuration name // for (const auto& ct: unbuilt_configs) { @@ -651,9 +669,13 @@ handle (request& rq, response& rs) << TR_VALUE ("toolchain", ct.toolchain_name + '-' + ct.toolchain_version.string ()) - << TR_VALUE ("config", - ct.configuration + " / " + ct.target.string ()) - << TR_VALUE ("result", "unbuilt") + << TR_VALUE ("target", ct.target.string ()) + << TR_VALUE ("tgt config", ct.target_config); + + if (ppc) + s << TR_VALUE ("pkg config", ct.package_config); + + s << TR_VALUE ("result", "unbuilt") << ~TBODY << ~TABLE; } @@ -664,20 +686,28 @@ handle (request& rq, response& rs) // if (!tn->interactive) { - for (const auto& c: *build_conf_) + for (const build_package_config& pc: pkg->build_configs) { - string reason; - if (belongs (c, "default") && exclude (c, &reason)) + for (const auto& tc: *target_conf_) { - s << TABLE(CLASS="proplist build") - << TBODY - << TR_VALUE ("config", c.name + " / " + c.target.string ()) - << TR_VALUE ("result", - !reason.empty () - ? "excluded (" + reason + ')' - : "excluded") - << ~TBODY - << ~TABLE; + string reason; + if (belongs (tc, "default") && exclude (pc, tc, &reason)) + { + s << TABLE(CLASS="proplist build") + << TBODY + << TR_VALUE ("target", tc.target.string ()) + << TR_VALUE ("tgt config", tc.name); + + if (ppc) + s << TR_VALUE ("pkg config", pc.name); + + s << TR_VALUE ("result", + !reason.empty () + ? "excluded (" + reason + ')' + : "excluded") + << ~TBODY + << ~TABLE; + } } } } diff --git a/mod/mod-repository-details.cxx b/mod/mod-repository-details.cxx index 1cbb5cb..082903b 100644 --- a/mod/mod-repository-details.cxx +++ b/mod/mod-repository-details.cxx @@ -3,8 +3,6 @@ #include -#include // max() - #include #include diff --git a/mod/mod-repository-root.cxx b/mod/mod-repository-root.cxx index 02d6c93..f00e80e 100644 --- a/mod/mod-repository-root.cxx +++ b/mod/mod-repository-root.cxx @@ -8,7 +8,6 @@ #include #include -#include // find() #include diff --git a/mod/module.cli b/mod/module.cli index 5814f37..7c6e0b4 100644 --- a/mod/module.cli +++ b/mod/module.cli @@ -765,14 +765,18 @@ namespace brep // string version | pv; - // Package build configuration. - // - string configuration | cf; - // Package build target. // string target | tg; + // Target build configuration. + // + string target_config | tc; + + // Package build configuration. + // + string package_config | pc; + // Toolchain name. // string toolchain_name | tn; @@ -805,13 +809,10 @@ namespace brep // // https://cppget.org/?builds=bbot // - // To support the already distributed URLs the name_legacy (pn) parameter - // overrides the name (builds) parameter, if present. Note that the - // builds parameter is renamed to '_' by the root handler (see the - // request_proxy class for details). + // Note that the builds parameter is renamed to '_' by the root handler + // (see the request_proxy class for details). // string name | _; - string name_legacy | pn; // Package version. If empty or *, then no version constraint is applied. // Otherwise the build package version must match the value exactly. @@ -822,22 +823,22 @@ namespace brep // toolchain constraint is applied. Otherwise the build toolchain name // and version must match the value exactly. // - string toolchain | tc = "*"; + string toolchain | th = "*"; - // Package build configuration name wildcard. An empty value is treated - // the same way as *. + // Package build target wildcard. An empty value is treated the same way + // as *. // - string configuration | cf; + string target | tg; - // Package build machine name wildcard. An empty value is treated the - // same way as *. + // Package build target configuration name wildcard. An empty value is + // treated the same way as *. // - string machine | mn; + string target_config | tc; - // Package build target wildcard. An empty value is treated the same way - // as *. + // Package build package configuration name wildcard. An empty value is + // treated the same way as *. // - string target | tg; + string package_config | pc; // Package build result. If *, then no build result constraint is // applied. Otherwise the value is supposed to be the one of the diff --git a/mod/page.cxx b/mod/page.cxx index a73e336..e2a8b84 100644 --- a/mod/page.cxx +++ b/mod/page.cxx @@ -7,10 +7,10 @@ #include #include -#include // hex, uppercase, right +#include // hex, uppercase, right #include -#include // setw(), setfill() -#include // min(), find() +#include // setw(), setfill() +#include // back_inserter() #include @@ -36,6 +36,20 @@ using namespace web::xhtml; // namespace brep { + static inline string + label_to_class (const string& label) + { + if (label.find (' ') == string::npos) + return label; + + string r; + transform (label.begin (), label.end (), + back_inserter (r), + [] (char c) {return c != ' ' ? c : '-';}); + + return r; + } + // CSS_LINKS // static const dir_path css_path ("@"); @@ -134,7 +148,8 @@ namespace brep void TR_VALUE:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SPAN(CLASS="value") << value_ << ~SPAN << ~TD << ~TR; @@ -145,7 +160,8 @@ namespace brep void TR_INPUT:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << INPUT(TYPE="text", NAME=name_); @@ -169,7 +185,8 @@ namespace brep void TR_SELECT:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SELECT(NAME=name_); @@ -604,7 +621,8 @@ namespace brep void TR_URL:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SPAN(CLASS="value"); @@ -634,7 +652,8 @@ namespace brep void TR_EMAIL:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SPAN(CLASS="value") @@ -698,7 +717,8 @@ namespace brep void TR_LINK:: operator() (serializer& s) const { - s << TR(CLASS=label_) + string c (label_to_class (label_)); + s << TR(CLASS=c) << TH << label_ << ~TH << TD << SPAN(CLASS="value") << A(HREF=url_) << text_ << ~A << ~SPAN -- cgit v1.1