aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clean/clean.cxx4
-rw-r--r--libbrep/build-extra.sql18
-rw-r--r--libbrep/build-package.hxx42
-rw-r--r--libbrep/package.cxx5
-rw-r--r--libbrep/package.hxx29
-rw-r--r--libbrep/package.xml38
-rw-r--r--load/load.cxx1
-rw-r--r--mod/build-config.cxx27
-rw-r--r--mod/build-config.hxx15
-rw-r--r--mod/mod-build-task.cxx157
-rw-r--r--mod/mod-builds.cxx190
-rw-r--r--mod/mod-package-version-details.cxx71
-rw-r--r--tests/load/1/math/libexp-1~1.2+1.tar.gzbin315 -> 360 bytes
-rw-r--r--tests/load/1/math/packages4
-rw-r--r--tests/load/driver.cxx32
15 files changed, 497 insertions, 136 deletions
diff --git a/clean/clean.cxx b/clean/clean.cxx
index f6d7eff..bec297b 100644
--- a/clean/clean.cxx
+++ b/clean/clean.cxx
@@ -200,8 +200,8 @@ try
package_name = b.package_name;
package_versions.clear ();
- for (auto& v: pkg_prep_query.execute ())
- package_versions.emplace (move (v.version));
+ for (auto& p: pkg_prep_query.execute ())
+ package_versions.emplace (move (p.version));
}
cleanup = package_versions.find (b.package_version) ==
diff --git a/libbrep/build-extra.sql b/libbrep/build-extra.sql
index 6a222a7..4da75e5 100644
--- a/libbrep/build-extra.sql
+++ b/libbrep/build-extra.sql
@@ -3,6 +3,8 @@
-- file for details.
--
+DROP FOREIGN TABLE IF EXISTS build_package_constraints;
+
DROP FOREIGN TABLE IF EXISTS build_package;
DROP FOREIGN TABLE IF EXISTS build_repository;
@@ -29,3 +31,19 @@ CREATE FOREIGN TABLE build_package (
version_release TEXT NULL,
internal_repository TEXT NULL)
SERVER package_server OPTIONS (table_name 'package');
+
+-- The foreign table for the build_package object constraints member (that is
+-- of a container type).
+--
+--
+CREATE FOREIGN TABLE build_package_constraints (
+ name TEXT NOT NULL,
+ version_epoch INTEGER NOT NULL,
+ version_canonical_upstream TEXT NOT NULL,
+ version_canonical_release TEXT NOT NULL COLLATE "C",
+ version_revision INTEGER NOT NULL,
+ index BIGINT NOT NULL,
+ exclusion BOOLEAN NOT NULL,
+ config TEXT NOT NULL,
+ target TEXT NULL)
+SERVER package_server OPTIONS (table_name 'package_build_constraints');
diff --git a/libbrep/build-package.hxx b/libbrep/build-package.hxx
index 8bc703a..324303d 100644
--- a/libbrep/build-package.hxx
+++ b/libbrep/build-package.hxx
@@ -23,7 +23,7 @@ namespace brep
// The mapping is established in build-extra.sql. We also explicitly mark
// non-primary key foreign-mapped members in the source object.
//
- // Foreign object that is mapped to the subset of repository object.
+ // Foreign object that is mapped to a subset of repository object.
//
#pragma db object table("build_repository") pointer(shared_ptr) readonly
class build_repository
@@ -46,7 +46,18 @@ namespace brep
build_repository () = default;
};
- // Foreign object that is mapped to the subset of package object.
+ // "Foreign" value type that is mapped to a subset of the build_constraint
+ // value type (see libbpkg/manifest.hxx for details).
+ //
+ #pragma db value
+ struct build_constraint_subset
+ {
+ bool exclusion;
+ string config;
+ optional<string> target;
+ };
+
+ // Foreign object that is mapped to a subset of package object.
//
#pragma db object table("build_package") pointer(shared_ptr) readonly
class build_package
@@ -56,10 +67,16 @@ namespace brep
upstream_version version;
lazy_shared_ptr<build_repository> internal_repository;
+ // Mapped to a subset of the package object build_constraints member
+ // using the PostgreSQL foreign table mechanism.
+ //
+ vector<build_constraint_subset> constraints;
+
// Database mapping.
//
#pragma db member(id) id column("")
#pragma db member(version) set(this.version.init (this.id.version, (?)))
+ #pragma db member(constraints) id_column("") value_column("")
private:
friend class odb::access;
@@ -102,6 +119,27 @@ namespace brep
//
#pragma db member(result) column("count(" + build_package::id.name + ")")
};
+
+ // Packages that have the build constraints. Note that only buildable
+ // (internal and non-stub) packages can have such constraints, so there is
+ // no need for additional checks.
+ //
+ #pragma db view \
+ table("build_package_constraints" = "c") \
+ object(build_package = package inner: \
+ "c.exclusion AND " \
+ "c.name = " + package::id.name + "AND" + \
+ "c.version_epoch = " + package::id.version.epoch + "AND" + \
+ "c.version_canonical_upstream = " + \
+ package::id.version.canonical_upstream + "AND" + \
+ "c.version_canonical_release = " + \
+ package::id.version.canonical_release + "AND" + \
+ "c.version_revision = " + package::id.version.revision) \
+ query(distinct)
+ struct build_constrained_package
+ {
+ shared_ptr<build_package> package;
+ };
}
#endif // LIBBREP_BUILD_PACKAGE_HXX
diff --git a/libbrep/package.cxx b/libbrep/package.cxx
index 20be387..e14b15a 100644
--- a/libbrep/package.cxx
+++ b/libbrep/package.cxx
@@ -62,6 +62,7 @@ namespace brep
optional<email_type> be,
dependencies_type dp,
requirements_type rq,
+ build_constraints_type bc,
optional<path> lc,
optional<string> sh,
shared_ptr<repository_type> rp)
@@ -80,6 +81,10 @@ namespace brep
build_email (move (be)),
dependencies (move (dp)),
requirements (move (rq)),
+ build_constraints (
+ version.compare (wildcard_version, true) != 0
+ ? move (bc)
+ : build_constraints_type ()),
internal_repository (move (rp)),
location (move (lc)),
sha256sum (move (sh))
diff --git a/libbrep/package.hxx b/libbrep/package.hxx
index 5a159ae..d3e3e00 100644
--- a/libbrep/package.hxx
+++ b/libbrep/package.hxx
@@ -9,6 +9,7 @@
#include <chrono>
#include <odb/core.hxx>
+#include <odb/section.hxx>
#include <odb/nested-container.hxx>
#include <libbrep/types.hxx>
@@ -18,9 +19,9 @@
// Used by the data migration entries.
//
-#define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 4
+#define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 5
-#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 4, closed)
+#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 5, open)
namespace brep
{
@@ -164,6 +165,13 @@ namespace brep
#pragma db value(requirement_alternatives) definition
+ // build_constraints
+ //
+ using bpkg::build_constraint;
+ using build_constraints = vector<build_constraint>;
+
+ #pragma db value(build_constraint) definition
+
#pragma db value
class certificate
{
@@ -285,8 +293,10 @@ namespace brep
using email_type = brep::email;
using dependencies_type = brep::dependencies;
using requirements_type = brep::requirements;
+ using build_constraints_type = brep::build_constraints;
- // Create internal package object.
+ // Create internal package object. Note that for stubs the build
+ // constraints are meaningless, and so not saved.
//
package (string name,
version_type,
@@ -303,6 +313,7 @@ namespace brep
optional<email_type> build_email,
dependencies_type,
requirements_type,
+ build_constraints_type,
optional<path> location,
optional<string> sha256sum,
shared_ptr<repository_type>);
@@ -337,6 +348,9 @@ namespace brep
dependencies_type dependencies;
requirements_type requirements;
+ build_constraints_type build_constraints; // Note: foreign-mapped in build.
+ odb::section build_section;
+
// Note that it is foreign-mapped in build.
//
lazy_shared_ptr<repository_type> internal_repository;
@@ -413,9 +427,16 @@ namespace brep
set(odb::nested_set (this.requirements, std::move (?))) \
id_column("") key_column("") value_column("id")
+ // build_constraints
+ //
+ #pragma db member(build_constraints) id_column("") value_column("") \
+ section(build_section)
+
+ #pragma db member(build_section) load(lazy) update(always)
+
// other_repositories
//
- #pragma db member(other_repositories) \
+ #pragma db member(other_repositories) \
id_column("") value_column("repository") value_not_null
// search_index
diff --git a/libbrep/package.xml b/libbrep/package.xml
index 657c2fc..0947d6f 100644
--- a/libbrep/package.xml
+++ b/libbrep/package.xml
@@ -1,5 +1,5 @@
<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="package" version="1">
- <model version="4">
+ <model version="5">
<table name="repository" kind="object">
<column name="name" type="TEXT" null="false"/>
<column name="location" type="TEXT" null="false"/>
@@ -374,6 +374,42 @@
<column name="version_revision"/>
</index>
</table>
+ <table name="package_build_constraints" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="exclusion" type="BOOLEAN" null="false"/>
+ <column name="config" type="TEXT" null="false"/>
+ <column name="target" type="TEXT" null="true"/>
+ <column name="comment" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_build_constraints_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ <index name="package_build_constraints_index_i">
+ <column name="index"/>
+ </index>
+ </table>
<table name="package_other_repositories" kind="container">
<column name="name" type="TEXT" null="false"/>
<column name="version_epoch" type="INTEGER" null="false"/>
diff --git a/load/load.cxx b/load/load.cxx
index bab35ec..860aeb0 100644
--- a/load/load.cxx
+++ b/load/load.cxx
@@ -410,6 +410,7 @@ load_packages (const shared_ptr<repository>& rp, database& db)
move (pm.build_email),
move (ds),
move (pm.requirements),
+ move (pm.build_constraints),
move (pm.location),
move (pm.sha256sum),
rp);
diff --git a/mod/build-config.cxx b/mod/build-config.cxx
index 6b59e54..fed2ee9 100644
--- a/mod/build-config.cxx
+++ b/mod/build-config.cxx
@@ -146,4 +146,31 @@ namespace brep
"&cf=" + mime_url_encode (b.configuration) +
"&tc=" + b.toolchain_version.string () + "&reason=";
}
+
+ bool
+ match (const string& config_pattern, const optional<string>& target_pattern,
+ const build_config& c)
+ {
+ return path_match (config_pattern, c.name) &&
+ (!target_pattern ||
+ (c.target && path_match (*target_pattern, c.target->string ())));
+ }
+
+ bool
+ exclude (const build_package& p, const build_config& c)
+ {
+ for (const auto& bc: p.constraints)
+ {
+ if (!bc.exclusion && match (bc.config, bc.target, c))
+ return false;
+ }
+
+ for (const auto& bc: p.constraints)
+ {
+ if (bc.exclusion && match (bc.config, bc.target, c))
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/mod/build-config.hxx b/mod/build-config.hxx
index b49819d..3c89ceb 100644
--- a/mod/build-config.hxx
+++ b/mod/build-config.hxx
@@ -13,9 +13,12 @@
#include <libbrep/utility.hxx>
#include <libbrep/build.hxx>
+#include <libbrep/build-package.hxx>
#include <mod/options.hxx>
+// Various build-related state and utilities.
+//
namespace brep
{
// Return pointer to the shared build configurations instance, creating one
@@ -48,6 +51,18 @@ namespace brep
//
string
force_rebuild_url (const string& host, const dir_path& root, const build&);
+
+ // Match a build configuration against the name and target patterns.
+ //
+ bool
+ match (const string& config_pattern,
+ const optional<string>& target_pattern,
+ const bbot::build_config&);
+
+ // Return true if a package excludes the specified build configuration.
+ //
+ bool
+ exclude (const build_package&, const bbot::build_config&);
}
#endif // MOD_BUILD_CONFIG_HXX
diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx
index 1a7b272..771b1ee 100644
--- a/mod/mod-build-task.cxx
+++ b/mod/mod-build-task.cxx
@@ -442,80 +442,94 @@ handle (request& rq, response& rs)
if (!configs.empty ())
{
- config_machine& cm (configs.begin ()->second);
- machine_header_manifest& mh (*cm.machine);
- build_id bid (move (id), cm.config->name, toolchain_version);
- shared_ptr<build> b (build_db_->find<build> (bid));
- optional<string> cl (challenge ());
-
- // 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.
+ // Find the first build configuration that is not excluded by the
+ // package.
//
- if (b == nullptr)
- {
- b = make_shared<build> (move (bid.package.name),
- move (bp.version),
- move (bid.configuration),
- move (tqm.toolchain_name),
- move (toolchain_version),
- move (agent_fp),
- move (cl),
- mh.name,
- move (mh.summary),
- cm.config->target);
-
- build_db_->persist (b);
- }
- else
+ shared_ptr<build_package> p (build_db_->load<build_package> (id));
+
+ auto i (configs.begin ());
+ auto e (configs.end ());
+ for (; i != e && exclude (*p, *i->second.config); ++i) ;
+
+ if (i != e)
{
- // The package configuration is in the building state, and there
- // are no results.
+ config_machine& cm (i->second);
+ machine_header_manifest& mh (*cm.machine);
+ build_id bid (move (id), cm.config->name, toolchain_version);
+ shared_ptr<build> b (build_db_->find<build> (bid));
+ optional<string> cl (challenge ());
+
+ // 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.
//
- // 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).
- //
- // Load the section to assert the above statement.
- //
- build_db_->load (*b, b->results_section);
+ if (b == nullptr)
+ {
+ b = make_shared<build> (move (bid.package.name),
+ move (bp.version),
+ move (bid.configuration),
+ move (tqm.toolchain_name),
+ move (toolchain_version),
+ move (agent_fp),
+ move (cl),
+ mh.name,
+ move (mh.summary),
+ cm.config->target);
+
+ build_db_->persist (b);
+ }
+ else
+ {
+ // The package configuration is in the building state, and there
+ // are no results.
+ //
+ // 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).
+ //
+ // Load the section to assert the above statement.
+ //
+ build_db_->load (*b, b->results_section);
- assert (b->state == build_state::building && b->results.empty ());
+ assert (b->state == build_state::building &&
+ b->results.empty ());
- b->state = build_state::building;
+ b->state = build_state::building;
- // 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->toolchain_name = move (tqm.toolchain_name);
- b->agent_fingerprint = move (agent_fp);
- b->agent_challenge = move (cl);
- b->machine = mh.name;
- b->machine_summary = move (mh.summary);
- b->target = cm.config->target;
- b->timestamp = timestamp::clock::now ();
-
- build_db_->update (b);
- }
+ // 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;
- // Finally, prepare the task response manifest.
- //
- shared_ptr<build_package> p (
- build_db_->load<build_package> (b->id.package));
+ b->toolchain_name = move (tqm.toolchain_name);
+ b->agent_fingerprint = move (agent_fp);
+ b->agent_challenge = move (cl);
+ b->machine = mh.name;
+ b->machine_summary = move (mh.summary);
+ b->target = cm.config->target;
+ b->timestamp = timestamp::clock::now ();
- p->internal_repository.load ();
+ build_db_->update (b);
+ }
- tsm = task (move (b), move (p), cm);
+ // Finally, prepare the task response manifest.
+ //
+ // We iterate over buildable packages.
+ //
+ assert (p->internal_repository != nullptr);
+
+ p->internal_repository.load ();
+
+ tsm = task (move (b), move (p), cm);
+ }
}
// If the task response manifest is prepared, then bail out from the
- // package loop, commit transactions and respond.
+ // package loop, commit the transaction and respond.
//
if (!tsm.session.empty ())
break;
@@ -556,10 +570,10 @@ handle (request& rq, response& rs)
// Pick the first package configuration from the ordered list.
//
- // Note that the configurations may not match the required criteria
- // anymore (as we have committed the database transactions that were
- // used to collect this data) so we recheck. If we find one that matches
- // then put it into the building state, refresh the timestamp and
+ // Note that the configurations and packages may not match the required
+ // criteria anymore (as we have committed the database transactions that
+ // were used to collect this data) so we recheck. If we find one that
+ // matches then put it into the building state, refresh the timestamp and
// update. Note that we don't amend the status and the force state to
// have them available in the result request handling (see above).
//
@@ -582,25 +596,28 @@ handle (request& rq, response& rs)
//
assert (i != cfg_machines.end ());
const config_machine& cm (i->second);
- const machine_header_manifest& mh (*cm.machine);
- // Load the package (if still present).
+ // Rebuild the package if still present, is buildable and doesn't
+ // exclude the configuration.
//
shared_ptr<build_package> p (
build_db_->find<build_package> (b->id.package));
- if (p != nullptr)
+ if (p != nullptr && p->internal_repository != nullptr &&
+ !exclude (*p, *cm.config))
{
assert (b->status);
b->state = build_state::building;
- b->machine = mh.name;
// Can't move from, as may need them on the next iteration.
//
b->agent_fingerprint = agent_fp;
b->agent_challenge = cl;
b->toolchain_name = tqm.toolchain_name;
+
+ const machine_header_manifest& mh (*cm.machine);
+ b->machine = mh.name;
b->machine_summary = mh.summary;
b->target = cm.config->target;
diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx
index 439bd05..c7a308e 100644
--- a/mod/mod-builds.cxx
+++ b/mod/mod-builds.cxx
@@ -98,7 +98,9 @@ build_query (const brep::cstrings& configs, const brep::params::builds& params)
using query = query<T>;
using qb = typename query::build;
- query q (qb::id.configuration.in_range (configs.begin (), configs.end ()));
+ query q (!configs.empty ()
+ ? qb::id.configuration.in_range (configs.begin (), configs.end ())
+ : query (true));
// Note that there is no error reported if the filter parameters parsing
// fails. Instead, it is considered that no package builds match such a
@@ -199,13 +201,12 @@ build_query (const brep::cstrings& configs, const brep::params::builds& params)
return q;
}
-template <typename T>
+template <typename T, typename P = typename query<T>::build_package>
static inline query<T>
package_query (const brep::params::builds& params)
{
using namespace brep;
using query = query<T>;
- using qp = typename query::build_package;
query q (true);
@@ -217,12 +218,12 @@ package_query (const brep::params::builds& params)
// Package name.
//
if (!params.name ().empty ())
- q = q && qp::id.name.like (transform (params.name ()));
+ q = q && P::id.name.like (transform (params.name ()));
// Package version.
//
if (!params.version ().empty () && params.version () != "*")
- q = q && compare_version_eq (qp::id.version,
+ q = q && compare_version_eq (P::id.version,
version (params.version ()), // May throw.
true);
}
@@ -234,6 +235,20 @@ package_query (const brep::params::builds& params)
return q;
}
+template <typename T, typename ID>
+static inline query<T>
+package_id_eq (const ID& x, const brep::package_id& y)
+{
+ using query = query<T>;
+ const auto& qv (x.version);
+
+ return x.name == query::_ref (y.name) &&
+ qv.epoch == query::_ref (y.version.epoch) &&
+ qv.canonical_upstream == query::_ref (y.version.canonical_upstream) &&
+ qv.canonical_release == query::_ref (y.version.canonical_release) &&
+ qv.revision == query::_ref (y.version.revision);
+}
+
static const vector<pair<string, string>> build_results ({
{"unbuilt", "<unbuilt>"},
{"*", "*"},
@@ -283,7 +298,7 @@ handle (request& rq, response& rs)
//
// This hack is required to avoid the "flash of unstyled content", which
// happens due to the presence of the autofocus attribute in the input
- // element of the search form. The problem appears in Firefox and has a
+ // element of the filter form. The problem appears in Firefox and has a
// (4-year old, at the time of this writing) bug report:
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=712130.
@@ -491,17 +506,15 @@ handle (request& rq, response& rs)
};
// Note that config_toolchains contains shallow references to the toolchain
- // names and versions, and in particular to the selected ones (tc_name and
- // tc_version).
+ // names and versions.
//
- string tc_name;
- version tc_version;
set<config_toolchain> config_toolchains;
-
{
transaction t (build_db_->begin ());
toolchains = query_toolchains ();
+ string tc_name;
+ version tc_version;
const string& tc (params.toolchain ());
if (tc != "*")
@@ -527,6 +540,7 @@ handle (request& rq, response& rs)
const string& pc (params.configuration ());
const string& tg (params.target ());
+ vector<const build_config*> configs;
for (const auto& c: *build_conf_)
{
@@ -537,11 +551,13 @@ handle (request& rq, response& rs)
: tg == "*" ||
(c.target && path_match (tg, c.target->string ()))))
{
- if (tc != "*") // Filter by toolchain.
- config_toolchains.insert ({c.name, tc_name, tc_version});
- else // Add all toolchains.
+ configs.push_back (&c);
+
+ for (const auto& t: toolchains)
{
- for (const auto& t: toolchains)
+ // Filter by toolchain.
+ //
+ if (tc == "*" || (t.first == tc_name && t.second == tc_version))
config_toolchains.insert ({c.name, t.first, t.second});
}
}
@@ -551,6 +567,15 @@ handle (request& rq, response& rs)
// 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 build-constrained
+ // packages and match their constraints against our configurations.
+ //
+ // Also note that some existing builds can now be excluded by packages
+ // due to the build configuration target 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)));
@@ -558,6 +583,63 @@ handle (request& rq, response& rs)
size_t ncur = build_db_->query_value<package_build_count> (
build_query<package_build_count> (*build_conf_names_, bld_params));
+ // From now we will be using specific package name and version for each
+ // build database query.
+ //
+ bld_params.name ().clear ();
+ bld_params.version ().clear ();
+
+ if (!config_toolchains.empty ())
+ {
+ // Prepare the build count prepared query.
+ //
+ // For each package-excluded configuration we will query the number of
+ // existing builds.
+ //
+ using bld_query = query<package_build_count>;
+ using prep_bld_query = prepared_query<package_build_count>;
+
+ package_id id;
+ string config;
+
+ bld_query bq (
+ package_id_eq<package_build_count> (
+ bld_query::build::id.package, id) &&
+ bld_query::build::id.configuration == bld_query::_ref (config) &&
+ build_query<package_build_count> (cstrings (), bld_params));
+
+ prep_bld_query bld_prep_query (
+ build_db_->prepare_query<package_build_count> (
+ "mod-builds-build-count-query", bq));
+
+ size_t nt (tc == "*" ? toolchains.size () : 1);
+
+ // The number of build-constrained 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.
+ //
+ using query = query<build_constrained_package>;
+ query q (package_query<build_constrained_package, query> (params));
+
+ for (const auto& p: build_db_->query<build_constrained_package> (q))
+ {
+ const build_package& bp (*p.package);
+ id = bp.id;
+
+ for (const auto& c: configs)
+ {
+ if (exclude (bp, *c))
+ {
+ nmax -= nt;
+
+ config = c->name;
+ ncur -= bld_prep_query.execute_value ();
+ }
+ }
+ }
+ }
+
assert (nmax >= ncur);
count = nmax - ncur;
@@ -620,36 +702,37 @@ handle (request& rq, response& rs)
using bld_query = query<package_build>;
using prep_bld_query = prepared_query<package_build>;
- // We will use specific package name and version for each database query.
- //
- bld_params.name ().clear ();
- bld_params.version ().clear ();
-
package_id id;
- const auto& qv (bld_query::build::id.package.version);
bld_query bq (
- bld_query::build::id.package.name == bld_query::_ref (id.name) &&
-
- qv.epoch == bld_query::_ref (id.version.epoch) &&
- qv.canonical_upstream ==
- bld_query::_ref (id.version.canonical_upstream) &&
- qv.canonical_release ==
- bld_query::_ref (id.version.canonical_release) &&
- qv.revision == bld_query::_ref (id.version.revision) &&
-
+ package_id_eq<package_build> (bld_query::build::id.package, id) &&
build_query<package_build> (*build_conf_names_, bld_params));
prep_bld_query bld_prep_query (
conn->prepare_query<package_build> ("mod-builds-build-query", bq));
+ // Prepare the build-constrained package prepared query.
+ //
+ // For each build-constrained package we will exclude the corresponding
+ // configurations from being printed.
+ //
+ using ctr_query = query<build_constrained_package>;
+ using prep_ctr_query = prepared_query<build_constrained_package>;
+
+ ctr_query cq (
+ package_id_eq<build_constrained_package> (ctr_query::id, id));
+
+ prep_ctr_query ctr_prep_query (
+ conn->prepare_query<build_constrained_package> (
+ "mod-builds-build-constrained-package-query", cq));
+
size_t skip (page * page_configs);
size_t print (page_configs);
// Enclose the subsequent tables to be able to use nth-child CSS selector.
//
s << DIV;
- for (bool prn (true); prn; )
+ while (print != 0)
{
transaction t (conn->begin ());
@@ -657,27 +740,47 @@ handle (request& rq, response& rs)
//
auto packages (pkg_prep_query.execute ());
- if ((prn = !packages.empty ()))
+ if (packages.empty ())
+ print = 0;
+ else
{
offset += packages.size ();
// Iterate over packages and print unbuilt configurations. Skip the
// appropriate number of them first (for page number greater than one).
//
- for (auto& pv: packages)
+ for (auto& p: packages)
{
- id = move (pv.id);
+ id = move (p.id);
- // Make a copy for this package.
+ // Copy configuration/toolchain combinations for this package,
+ // skipping explicitly excluded configurations.
//
- auto unbuilt_configs (config_toolchains);
+ set<config_toolchain> unbuilt_configs;
+ {
+ build_constrained_package p;
+ if (ctr_prep_query.execute_one (p))
+ {
+ const build_package& bp (*p.package);
+ for (const auto& ct: config_toolchains)
+ {
+ auto i (build_conf_map_->find (ct.configuration.c_str ()));
+ assert (i != build_conf_map_->end ());
+
+ if (!exclude (bp, *i->second))
+ unbuilt_configs.insert (ct);
+ }
+ }
+ else
+ unbuilt_configs = config_toolchains;
+ }
// Iterate through the package configuration builds and erase them
// from the unbuilt configurations set.
//
for (const auto& pb: bld_prep_query.execute ())
{
- build& b (*pb.build);
+ const build& b (*pb.build);
unbuilt_configs.erase ({
b.id.configuration, b.toolchain_name, b.toolchain_version});
@@ -693,12 +796,6 @@ handle (request& rq, response& rs)
continue;
}
- if (print-- == 0)
- {
- prn = false;
- break;
- }
-
auto i (build_conf_map_->find (ct.configuration.c_str ()));
assert (i != build_conf_map_->end ());
@@ -707,7 +804,7 @@ handle (request& rq, response& rs)
s << TABLE(CLASS="proplist build")
<< TBODY
<< TR_NAME (id.name, string (), root)
- << TR_VERSION (id.name, pv.version, root)
+ << TR_VERSION (id.name, p.version, root)
<< TR_VALUE ("toolchain",
string (ct.toolchain_name) + '-' +
ct.toolchain_version.string ())
@@ -715,9 +812,12 @@ handle (request& rq, response& rs)
<< TR_VALUE ("target", tg ? tg->string () : "<default>")
<< ~TBODY
<< ~TABLE;
+
+ if (--print == 0) // Bail out the configuration loop.
+ break;
}
- if (!prn)
+ if (print == 0) // Bail out the package loop.
break;
}
}
diff --git a/mod/mod-package-version-details.cxx b/mod/mod-package-version-details.cxx
index fd8d9a1..4da13ab 100644
--- a/mod/mod-package-version-details.cxx
+++ b/mod/mod-package-version-details.cxx
@@ -10,6 +10,8 @@
#include <odb/database.hxx>
#include <odb/transaction.hxx>
+#include <libbutl/utility.hxx> // alpha(), ucase(), lcase()
+
#include <web/xhtml.hxx>
#include <web/module.hxx>
#include <web/mime-url-encoding.hxx>
@@ -23,6 +25,7 @@
#include <mod/options.hxx>
using namespace std;
+using namespace butl;
using namespace odb::core;
using namespace brep::cli;
@@ -280,6 +283,14 @@ handle (request& rq, response& rs)
<< ~TABLE;
}
+ // Don't display the page builds section for stub packages.
+ //
+ bool builds (build_db_ != nullptr &&
+ ver.compare (wildcard_version, true) != 0);
+
+ if (builds)
+ package_db_->load (*pkg, pkg->build_section);
+
t.commit ();
const auto& rm (pkg->requirements);
@@ -325,9 +336,7 @@ handle (request& rq, response& rs)
<< ~TABLE;
}
- // Don't display the section for stub packages.
- //
- if (build_db_ != nullptr && ver.compare (wildcard_version, true) != 0)
+ if (builds)
{
s << H3 << "Builds" << ~H3
<< DIV(ID="builds");
@@ -335,6 +344,8 @@ handle (request& rq, response& rs)
timestamp now (timestamp::clock::now ());
transaction t (build_db_->begin ());
+ // Print built package configurations.
+ //
using query = query<build>;
for (auto& b: build_db_->query<build> (
@@ -368,6 +379,60 @@ handle (request& rq, response& rs)
<< ~TABLE;
}
+ // Print configurations that are excluded by the package.
+ //
+ auto excluded = [&pkg] (const bbot::build_config& c, string& reason)
+ {
+ for (const auto& bc: pkg->build_constraints)
+ {
+ if (!bc.exclusion && match (bc.config, bc.target, c))
+ return false;
+ }
+
+ for (const auto& bc: pkg->build_constraints)
+ {
+ if (bc.exclusion && match (bc.config, bc.target, c))
+ {
+ // Save the first sentence of the exclusion comment, lower-case the
+ // first letter if the beginning looks like a word (the second
+ // character is the lower-case letter or space).
+ //
+ reason = bc.comment.substr (0, bc.comment.find ('.'));
+
+ char c;
+ size_t n (reason.size ());
+ if (n > 0 && alpha (c = reason[0]) && c == ucase (c) &&
+ (n == 1 ||
+ (alpha (c = reason[1]) && c == lcase (c)) ||
+ c == ' '))
+ reason[0] = lcase (reason[0]);
+
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ for (const auto& c: *build_conf_)
+ {
+ string reason;
+ if (excluded (c, reason))
+ {
+ s << TABLE(CLASS="proplist build")
+ << TBODY
+ << TR_VALUE ("config",
+ c.name + " / " +
+ (c.target ? c.target->string () : "<default>"))
+ << TR_VALUE ("result",
+ !reason.empty ()
+ ? "excluded (" + reason + ')'
+ : "excluded")
+ << ~TBODY
+ << ~TABLE;
+ }
+ }
+
t.commit ();
s << ~DIV;
diff --git a/tests/load/1/math/libexp-1~1.2+1.tar.gz b/tests/load/1/math/libexp-1~1.2+1.tar.gz
index 1e1807f..1d15a59 100644
--- a/tests/load/1/math/libexp-1~1.2+1.tar.gz
+++ b/tests/load/1/math/libexp-1~1.2+1.tar.gz
Binary files differ
diff --git a/tests/load/1/math/packages b/tests/load/1/math/packages
index 22a3167..a796d31 100644
--- a/tests/load/1/math/packages
+++ b/tests/load/1/math/packages
@@ -12,8 +12,10 @@ email: users@exp.com
build-email: builds@exp.com
depends: libmisc
depends: libpq >= 9.0.0
+build-include: linux*
+build-exclude: *; Only supported on Linux.
location: libexp-1~1.2+1.tar.gz
-sha256sum: 96add9edada45f4ceee18b3ec344ca3c4fc1473d9aad22a13e97d7728a439087
+sha256sum: 0a7414d06ad26d49dad203deaf3841f3df97f1fe27c5bf190c1c20dfeb7f84e0
:
name: libfoo
version: 1.0
diff --git a/tests/load/driver.cxx b/tests/load/driver.cxx
index a907be1..2c8f4b6 100644
--- a/tests/load/driver.cxx
+++ b/tests/load/driver.cxx
@@ -46,6 +46,16 @@ check_external (const package& p)
p.requirements.empty () && !p.sha256sum;
}
+namespace bpkg
+{
+ static bool
+ operator== (const build_constraint& x, const build_constraint& y)
+ {
+ return x.exclusion == y.exclusion && x.config == y.config &&
+ x.target == y.target && x.comment == y.comment;
+ }
+}
+
int
main (int argc, char* argv[])
{
@@ -237,13 +247,11 @@ main (int argc, char* argv[])
assert (fpv2->dependencies[0].size () == 1);
assert (fpv2->dependencies[1].size () == 1);
- auto dep (
- [&db](const char* n,
- const optional<dependency_constraint>& c) -> dependency
- {
- return {
- lazy_shared_ptr<package> (db, package_id (n, version ())), c};
- });
+ auto dep = [&db] (
+ const char* n, const optional<dependency_constraint>& c) -> dependency
+ {
+ return {lazy_shared_ptr<package> (db, package_id (n, version ())), c};
+ };
assert (fpv2->dependencies[0][0] ==
dep (
@@ -635,9 +643,17 @@ main (int argc, char* argv[])
assert (epv->requirements.empty ());
+ db.load (*epv, epv->build_section);
+
+ assert (
+ epv->build_constraints ==
+ build_constraints ({
+ build_constraint (false, "linux*", nullopt, ""),
+ build_constraint (true, "*", nullopt, "Only supported on Linux.")}));
+
assert (check_location (epv));
assert (epv->sha256sum && *epv->sha256sum ==
- "96add9edada45f4ceee18b3ec344ca3c4fc1473d9aad22a13e97d7728a439087");
+ "0a7414d06ad26d49dad203deaf3841f3df97f1fe27c5bf190c1c20dfeb7f84e0");
// Verify libpq package version.
//