diff options
Diffstat (limited to 'mod')
-rw-r--r-- | mod/build-config-module.cxx | 33 | ||||
-rw-r--r-- | mod/build-config-module.hxx | 49 | ||||
-rw-r--r-- | mod/build-config.hxx | 72 | ||||
-rw-r--r-- | mod/build-target-config.cxx (renamed from mod/build-config.cxx) | 23 | ||||
-rw-r--r-- | mod/build-target-config.hxx | 79 | ||||
-rw-r--r-- | mod/build.cxx | 29 | ||||
-rw-r--r-- | mod/buildfile | 2 | ||||
-rw-r--r-- | mod/mod-build-configs.cxx | 15 | ||||
-rw-r--r-- | mod/mod-build-force.cxx | 32 | ||||
-rw-r--r-- | mod/mod-build-log.cxx | 55 | ||||
-rw-r--r-- | mod/mod-build-result.cxx | 77 | ||||
-rw-r--r-- | mod/mod-build-task.cxx | 356 | ||||
-rw-r--r-- | mod/mod-builds.cxx | 373 | ||||
-rw-r--r-- | mod/mod-package-version-details.cxx | 112 | ||||
-rw-r--r-- | mod/mod-repository-details.cxx | 2 | ||||
-rw-r--r-- | mod/mod-repository-root.cxx | 1 | ||||
-rw-r--r-- | mod/module.cli | 39 | ||||
-rw-r--r-- | mod/page.cxx | 38 |
18 files changed, 791 insertions, 596 deletions
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<const build_configs> + static shared_ptr<const build_target_configs> shared_build_config (const path& p) { - static map<path, weak_ptr<build_configs>> configs; + static map<path, weak_ptr<build_target_configs>> configs; auto i (configs.find (p)); if (i != configs.end ()) { - if (shared_ptr<build_configs> c = i->second.lock ()) + if (shared_ptr<build_target_configs> c = i->second.lock ()) return c; } - shared_ptr<build_configs> c ( - make_shared<build_configs> (parse_buildtab (p))); + shared_ptr<build_target_configs> c ( + make_shared<build_target_configs> (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<build_config_id, const build_config*>; + using conf_map_type = map<build_target_config_id, + const build_target_config*>; + 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<conf_map_type> (move (conf_map)); + target_conf_map_ = make_shared<conf_map_type> (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<string, string>& im (build_conf_->class_inheritance_map); + const map<string, string>& 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 <libbpkg/manifest.hxx> -#include <libbbot/build-config.hxx> - #include <libbrep/types.hxx> #include <libbrep/utility.hxx> -#include <mod/build-config.hxx> #include <mod/module-options.hxx> +#include <mod/build-target-config.hxx> // 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<bpkg::build_class_expr, 1>& exprs, - const vector<bpkg::build_constraint>& 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<const bbot::build_configs> build_conf_; + shared_ptr<const build_target_configs> target_conf_; - shared_ptr<const std::map<build_config_id, const bbot::build_config*>> - build_conf_map_; + shared_ptr<const std::map<build_target_config_id, + const build_target_config*>> + target_conf_map_; // Map of build bot agent public keys fingerprints to the key file paths. // 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 <map> - -#include <libbutl/target-triplet.hxx> - -#include <libbpkg/manifest.hxx> - -#include <libbbot/build-config.hxx> - -#include <libbrep/types.hxx> -#include <libbrep/utility.hxx> - -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<bpkg::build_class_expr, 1>&, - const vector<bpkg::build_constraint>&, - const bbot::build_config&, - const std::map<string, string>& 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<const string> name; - reference_wrapper<const butl::target_triplet> 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-config.cxx b/mod/build-target-config.cxx index 8fbbf99..a30cf07 100644 --- a/mod/build-config.cxx +++ b/mod/build-target-config.cxx @@ -1,7 +1,7 @@ -// file : mod/build-config-module.cxx -*- C++ -*- +// file : mod/target-build-config.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file -#include <mod/build-config.hxx> +#include <mod/build-target-config.hxx> #include <libbutl/utility.hxx> // alpha(), etc. #include <libbutl/path-pattern.hxx> @@ -11,7 +11,6 @@ namespace brep using namespace std; using namespace butl; using namespace bpkg; - using namespace bbot; // The default underlying class set expressions (see below). // @@ -22,13 +21,17 @@ namespace brep {"all"}, '+', "All."); bool - exclude (const small_vector<build_class_expr, 1>& exprs, - const vector<build_constraint>& constrs, - const build_config& cfg, + exclude (const build_package_config& pc, + const build_class_exprs& cbs, + const build_constraints& ccs, + const build_target_config& tc, const map<string, string>& 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). @@ -74,11 +77,11 @@ namespace brep // (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] + auto match = [&tc, &m, reason, &sanitize, &class_inheritance_map] (const build_class_expr& e) { bool pm (m); - e.match (cfg.classes, class_inheritance_map, m); + e.match (tc.classes, class_inheritance_map, m); if (reason != nullptr) { @@ -168,8 +171,8 @@ namespace brep if (!constrs.empty ()) try { - path cn (dash_components_to_path (cfg.name)); - path tg (dash_components_to_path (cfg.target.string ())); + path cn (dash_components_to_path (tc.name)); + path tg (dash_components_to_path (tc.target.string ())); for (const build_constraint& c: constrs) { 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 <map> + +#include <libbutl/target-triplet.hxx> + +#include <libbpkg/manifest.hxx> + +#include <libbbot/build-target-config.hxx> + +#include <libbrep/types.hxx> +#include <libbrep/utility.hxx> + +#include <libbrep/common.hxx> + +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<string, string>& 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<const butl::target_triplet> target; + reference_wrapper<const string> 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 <mod/mod-build-configs.hxx> -#include <algorithm> // replace() - #include <libstudxml/serializer.hxx> #include <web/server/module.hxx> @@ -15,7 +13,6 @@ #include <mod/module-options.hxx> 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<string, string>& im (build_conf_->class_inheritance_map); + const strings& cls (target_conf_->classes); + const map<string, string>& 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<const build_config*> configs; + vector<const build_target_config*> 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 <mod/mod-build-force.hxx> -#include <algorithm> // replace() - #include <odb/database.hxx> #include <odb/transaction.hxx> @@ -16,7 +14,6 @@ #include <mod/module-options.hxx> 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 <mod/mod-build-log.hxx> -#include <algorithm> // find_if() - #include <odb/database.hxx> #include <odb/transaction.hxx> @@ -18,7 +16,6 @@ #include <mod/module-options.hxx> 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<build_package> p ( build_db_->load<build_package> (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 <libbutl/manifest-serializer.hxx> #include <libbbot/manifest.hxx> -#include <libbbot/build-config.hxx> #include <web/server/module.hxx> @@ -30,6 +29,8 @@ #include <libbrep/build-package.hxx> #include <libbrep/build-package-odb.hxx> +#include <mod/build-target-config.hxx> + #include <mod/module-options.hxx> 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<build_config_id, config_machine>; + using config_machines = map<build_target_config_id, config_machine>; 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<build>&& b, shared_ptr<build_package>&& p, + build_package_config&& pc, shared_ptr<build_tenant>&& t, const config_machine& cm) -> task_response_manifest { @@ -215,12 +218,13 @@ handle (request& rq, response& rs) chrono::duration_cast<std::chrono::nanoseconds> ( 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<build_package> 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<build>; using prep_bld_query = prepared_query<build>; 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<build> (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<build_package> p (build_db_->load<build_package> (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<build_package> p (build_db_->load<build_package> (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<build> b (build_db_->find<build> (bid)); - optional<string> cl (challenge ()); - - shared_ptr<build_tenant> t ( - build_db_->load<build_tenant> (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<string> 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<build> (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<build> b (build_db_->find<build> (bid)); + optional<string> cl (challenge ()); - b->controller_checksum = move (ccs); - b->machine_checksum = move (mcs); + shared_ptr<build_tenant> t ( + build_db_->load<build_tenant> (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<string> 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<build> (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<string> 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<build_tenant> (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 <mod/mod-builds.hxx> #include <set> -#include <algorithm> // find_if() #include <libstudxml/serializer.hxx> @@ -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 <typename T> static inline query<T> -build_query (const brep::vector<brep::build_config_id>* config_ids, +build_query (const brep::vector<brep::build_target_config_id>* config_ids, const brep::params::builds& params, const brep::optional<brep::string>& tenant, const brep::optional<bool>& archived) @@ -159,8 +157,8 @@ build_query (const brep::vector<brep::build_config_id>* 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<brep::build_config_id>* 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<brep::build_config_id>* 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<brep::build_config_id>* config_ids, true /* revision */); } - // Build configuration name. + // Build target. // - if (!params.configuration ().empty ()) - q = q && match<T> (qb::id.configuration, params.configuration ()); + if (!params.target ().empty ()) + q = q && match<T> (qb::id.target, params.target ()); - // Build machine name. + // Build target configuration name. // - if (!params.machine ().empty ()) - q = q && match<T> (qb::machine, params.machine ()); + if (!params.target_config ().empty ()) + q = q && match<T> (qb::id.target_config_name, params.target_config ()); - // Build target. + // Build package configuration name. // - if (!params.target ().empty ()) - q = q && match<T> (qb::id.target, params.target ()); + if (!params.package_config ().empty ()) + q = q && match<T> (qb::id.package_config_name, params.package_config ()); // Build result. // @@ -244,7 +242,7 @@ build_query (const brep::vector<brep::build_config_id>* 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<pair<string, string>> 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<const char*, butl::compare_c_string> 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<build_config_id> conf_ids; - conf_ids.reserve (build_conf_map_->size ()); + vector<build_target_config_id> 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<build>& 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<build_package> p ( build_db_->load<build_package> (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_toolchain> 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<target_config_toolchain> 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<const build_config*> configs; + vector<const build_target_config*> 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<buildable_package_count> ( - package_query<buildable_package_count> ( - 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<package_build_count> ( - build_query<package_build_count> ( - &conf_ids, bld_params, tn, false /* archived */)); + size_t ncur = build_db_->query_value<package_build_count> ( + build_query<package_build_count> ( + &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_build_count>; 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<package_build_count> (bid.package, id) && - bid.configuration == bld_query::_ref (config) && - bid.target == bld_query::_ref (target) && + equal<package_build_count> (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<package_build_count> ( "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<buildable_package> q ( - package_query<buildable_package> ( - params, tn, false /* archived */)); + package_query<buildable_package> (params, tn, false /* archived */)); for (auto& bp: build_db_->query<buildable_package> (q)) { @@ -862,22 +897,33 @@ handle (request& rq, response& rs) shared_ptr<build_package> p (build_db_->load<build_package> (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<package_build> (bld_query::build::id.package, id) && + bld_query bq (equal<package_build> (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<package_build> ( - &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<package_build> ( + &conf_ids, bld_params, tn, false /* archived */)); prep_bld_query bld_prep_query ( conn->prepare_query<package_build> ("mod-builds-build-query", bq)); @@ -986,22 +1032,35 @@ handle (request& rq, response& rs) { id = move (p.id); + shared_ptr<build_package> bp (build_db_->load<build_package> (id)); + // Copy configuration/toolchain combinations for this package, // skipping excluded configurations. // set<config_toolchain> unbuilt_configs; - { - shared_ptr<build_package> p (build_db_->load<build_package> (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<build>; query sq (false); set<config_toolchain> 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<build> ( @@ -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 <mod/mod-repository-details.hxx> -#include <algorithm> // max() - #include <libstudxml/serializer.hxx> #include <odb/database.hxx> 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 <cmark-gfm-core-extensions.h> #include <sstream> -#include <algorithm> // find() #include <web/server/module.hxx> 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 <cmark-gfm-extension_api.h> #include <set> -#include <ios> // hex, uppercase, right +#include <ios> // hex, uppercase, right #include <sstream> -#include <iomanip> // setw(), setfill() -#include <algorithm> // min(), find() +#include <iomanip> // setw(), setfill() +#include <iterator> // back_inserter() #include <libstudxml/serializer.hxx> @@ -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 |