aboutsummaryrefslogtreecommitdiff
path: root/mod
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2022-10-28 23:21:29 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2022-11-23 19:20:35 +0300
commit03c931e54e618221b69cfcd3dfb462e50ecad780 (patch)
treee9fa949151f518f0fdfb05db473fc538f20310ff /mod
parent5bf2dd09110f257acc730eab71301e1dede1c710 (diff)
Add support for package build configurations
Diffstat (limited to 'mod')
-rw-r--r--mod/build-config-module.cxx33
-rw-r--r--mod/build-config-module.hxx49
-rw-r--r--mod/build-config.hxx72
-rw-r--r--mod/build-target-config.cxx (renamed from mod/build-config.cxx)23
-rw-r--r--mod/build-target-config.hxx79
-rw-r--r--mod/build.cxx29
-rw-r--r--mod/buildfile2
-rw-r--r--mod/mod-build-configs.cxx15
-rw-r--r--mod/mod-build-force.cxx32
-rw-r--r--mod/mod-build-log.cxx55
-rw-r--r--mod/mod-build-result.cxx77
-rw-r--r--mod/mod-build-task.cxx356
-rw-r--r--mod/mod-builds.cxx373
-rw-r--r--mod/mod-package-version-details.cxx112
-rw-r--r--mod/mod-repository-details.cxx2
-rw-r--r--mod/mod-repository-root.cxx1
-rw-r--r--mod/module.cli39
-rw-r--r--mod/page.cxx38
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