aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2024-03-29 21:13:23 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2024-04-01 17:45:55 +0300
commit6cb77d97d659441036fac18af6479eeac4127dcb (patch)
tree47e43d9919a0b90b45e76da1f46e0b736e1a3633
parent2b5bea3871d0ccc1b7da32929f195dc161bcc1ec (diff)
Stash build toolchain in tenant object
-rw-r--r--libbrep/build-extra.sql9
-rw-r--r--libbrep/build-package.hxx1
-rw-r--r--libbrep/build.hxx2
-rw-r--r--libbrep/build.xml2
-rw-r--r--libbrep/common.hxx9
-rw-r--r--libbrep/package.hxx23
-rw-r--r--libbrep/package.xml12
-rw-r--r--mod/mod-build-task.cxx93
-rw-r--r--mod/mod-builds.cxx29
-rw-r--r--mod/page.cxx14
-rw-r--r--mod/page.hxx13
11 files changed, 167 insertions, 40 deletions
diff --git a/libbrep/build-extra.sql b/libbrep/build-extra.sql
index 374cf73..a931f31 100644
--- a/libbrep/build-extra.sql
+++ b/libbrep/build-extra.sql
@@ -44,7 +44,14 @@ CREATE FOREIGN TABLE build_tenant (
service_id TEXT NULL,
service_type TEXT NULL,
service_data TEXT NULL,
- queued_timestamp BIGINT NULL)
+ queued_timestamp BIGINT NULL,
+ toolchain_name TEXT OPTIONS (column_name 'build_toolchain_name') NULL,
+ toolchain_version_epoch INTEGER OPTIONS (column_name 'build_toolchain_version_epoch') NULL,
+ toolchain_version_canonical_upstream TEXT OPTIONS (column_name 'build_toolchain_version_canonical_upstream') NULL,
+ toolchain_version_canonical_release TEXT OPTIONS (column_name 'build_toolchain_version_canonical_release') NULL,
+ toolchain_version_revision INTEGER OPTIONS (column_name 'build_toolchain_version_revision') NULL,
+ toolchain_version_upstream TEXT OPTIONS (column_name 'build_toolchain_version_upstream') NULL,
+ toolchain_version_release TEXT OPTIONS (column_name 'build_toolchain_version_release') NULL)
SERVER package_server OPTIONS (table_name 'tenant');
-- The foreign table for build_repository object.
diff --git a/libbrep/build-package.hxx b/libbrep/build-package.hxx
index 8377158..08fb781 100644
--- a/libbrep/build-package.hxx
+++ b/libbrep/build-package.hxx
@@ -37,6 +37,7 @@ namespace brep
bool archived;
optional<tenant_service> service;
optional<timestamp> queued_timestamp;
+ optional<build_toolchain> toolchain;
// Database mapping.
//
diff --git a/libbrep/build.hxx b/libbrep/build.hxx
index be7cdf5..4c470cd 100644
--- a/libbrep/build.hxx
+++ b/libbrep/build.hxx
@@ -28,7 +28,7 @@
//
#define LIBBREP_BUILD_SCHEMA_VERSION_BASE 20
-#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 25, closed)
+#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 26, closed)
// We have to keep these mappings at the global scope instead of inside the
// brep namespace because they need to be also effective in the bbot namespace
diff --git a/libbrep/build.xml b/libbrep/build.xml
index 0dc37ee..815c915 100644
--- a/libbrep/build.xml
+++ b/libbrep/build.xml
@@ -1,4 +1,6 @@
<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="build" version="1">
+ <changeset version="26"/>
+
<changeset version="25">
<add-table name="build_auxiliary_machines" kind="container">
<column name="package_tenant" type="TEXT" null="false"/>
diff --git a/libbrep/common.hxx b/libbrep/common.hxx
index 6220149..ea18fd4 100644
--- a/libbrep/common.hxx
+++ b/libbrep/common.hxx
@@ -354,6 +354,15 @@ namespace brep
#pragma db value(build_auxiliary) definition
+ // build_toolchain
+ //
+ #pragma db value
+ struct build_toolchain
+ {
+ string name;
+ brep::version version;
+ };
+
// email
//
using bpkg::email;
diff --git a/libbrep/package.hxx b/libbrep/package.hxx
index 96e51a3..b8c3c33 100644
--- a/libbrep/package.hxx
+++ b/libbrep/package.hxx
@@ -20,7 +20,7 @@
//
#define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 27
-#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 31, closed)
+#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 32, closed)
namespace brep
{
@@ -286,6 +286,27 @@ namespace brep
//
optional<timestamp> queued_timestamp; // Note: foreign-mapped in build.
+ // Note that after the package tenant is created but before the first
+ // build object is created, there is no easy way to produce a list of
+ // unbuilt package configurations. That would require to know the build
+ // toolchain(s), which are normally extracted from the build objects.
+ // Thus, the empty unbuilt package configurations list is ambiguous and
+ // can either mean that no more package configurations can be built or
+ // that we have not enough information to produce the list. To
+ // disambiguate the empty list in the interface, in the latter case we
+ // want to display the question mark instead of 0 as an unbuilt package
+ // configurations count. To achieve this we will stash the build toolchain
+ // in the tenant when a package from this tenant is considered for a build
+ // for the first time but no configuration is picked for the build (the
+ // target configurations are excluded, an auxiliary machine is not
+ // available, etc). We will also use the stashed toolchain as a fallback
+ // until we are able to retrieve the toolchain(s) from the tenant builds
+ // to produce the unbuilt package configurations list.
+ //
+ // Note: foreign-mapped in build.
+ //
+ optional<brep::build_toolchain> build_toolchain;
+
// Database mapping.
//
#pragma db member(id) id
diff --git a/libbrep/package.xml b/libbrep/package.xml
index 966861b..b66a66d 100644
--- a/libbrep/package.xml
+++ b/libbrep/package.xml
@@ -1,4 +1,16 @@
<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="package" version="1">
+ <changeset version="32">
+ <alter-table name="tenant">
+ <add-column name="build_toolchain_name" type="TEXT" null="true"/>
+ <add-column name="build_toolchain_version_epoch" type="INTEGER" null="true"/>
+ <add-column name="build_toolchain_version_canonical_upstream" type="TEXT" null="true"/>
+ <add-column name="build_toolchain_version_canonical_release" type="TEXT" null="true"/>
+ <add-column name="build_toolchain_version_revision" type="INTEGER" null="true"/>
+ <add-column name="build_toolchain_version_upstream" type="TEXT" null="true"/>
+ <add-column name="build_toolchain_version_release" type="TEXT" null="true"/>
+ </alter-table>
+ </changeset>
+
<changeset version="31">
<add-table name="package_build_auxiliaries" kind="container">
<column name="tenant" type="TEXT" null="false"/>
diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx
index e2f69f7..773d041 100644
--- a/mod/mod-build-task.cxx
+++ b/mod/mod-build-task.cxx
@@ -231,7 +231,7 @@ handle (request& rq, response& rs)
// The resulting task manifest and the related build, package, and
// configuration objects. Note that the latter 3 are only meaningful if the
- // session in the task manifest is not empty.
+ // the task manifest is present.
//
task_response_manifest task_response;
shared_ptr<build> task_build;
@@ -1344,9 +1344,9 @@ handle (request& rq, response& rs)
//
bool unforced (true);
- for (bool done (false); task_response.session.empty () && !done; )
+ for (bool done (false); !task_response.task && !done; )
{
- transaction t (conn->begin ());
+ transaction tr (conn->begin ());
// We need to be careful in the random package ordering mode not to
// miss the end after having wrapped around.
@@ -1381,7 +1381,7 @@ handle (request& rq, response& rs)
//
if (chunk_size == 0)
{
- t.commit ();
+ tr.commit ();
if (start_offset != 0 && offset >= start_offset)
offset = 0;
@@ -1398,12 +1398,24 @@ handle (request& rq, response& rs)
// to bail out in the random package ordering mode for some reason (no
// more untried positions, need to restart, etc).
//
+ // Note that it is not uncommon for the sequentially examined packages
+ // to belong to the same tenant (single tenant mode, etc). Thus, we
+ // will cache the loaded tenant objects.
+ //
+ shared_ptr<build_tenant> t;
+
for (auto& bp: packages)
{
shared_ptr<build_package>& p (bp.package);
id = p->id;
+ // Reset the tenant cache if the current package belongs to a
+ // different tenant.
+ //
+ if (t != nullptr && t->id != id.tenant)
+ t = nullptr;
+
// If we are in the random package ordering mode, then cache the
// tenant the start offset refers to, if not cached yet, and check
// if we are still iterating over packages from this tenant
@@ -1463,8 +1475,6 @@ handle (request& rq, response& rs)
//
if (bp.interactive)
{
- shared_ptr<build_tenant> t;
-
// Note that the tenant can be archived via some other package on
// some previous iteration. Skip the package if that's the case.
//
@@ -1473,7 +1483,9 @@ handle (request& rq, response& rs)
//
if (!bp.archived)
{
- t = build_db_->load<build_tenant> (id.tenant);
+ if (t == nullptr)
+ t = build_db_->load<build_tenant> (id.tenant);
+
bp.archived = t->archived;
}
@@ -1583,6 +1595,14 @@ handle (request& rq, response& rs)
}
}
+ // If true, then the package is (being) built for some
+ // configurations.
+ //
+ // Note that since we only query the built and forced rebuild
+ // objects there can be false negatives.
+ //
+ bool package_built (false);
+
for (const build_package_config& pc: p->configs)
{
pkg_config = pc.name;
@@ -1597,6 +1617,9 @@ handle (request& rq, response& rs)
config_machines configs (conf_machines); // Make copy for this pkg.
auto pkg_builds (bld_prep_query.execute ());
+ if (!package_built && !pkg_builds.empty ())
+ package_built = true;
+
for (auto i (pkg_builds.begin ()); i != pkg_builds.end (); ++i)
{
auto j (
@@ -1750,8 +1773,8 @@ handle (request& rq, response& rs)
build_db_->update (b);
}
- shared_ptr<build_tenant> t (
- build_db_->load<build_tenant> (b->tenant));
+ if (t == nullptr)
+ t = build_db_->load<build_tenant> (b->tenant);
// Archive an interactive tenant.
//
@@ -1798,7 +1821,7 @@ handle (request& rq, response& rs)
}
if (tsb != nullptr || tsq != nullptr)
- tss = make_pair (move (*t->service), b);
+ tss = make_pair (*t->service, b);
}
}
@@ -1814,25 +1837,51 @@ handle (request& rq, response& rs)
task_package = move (p);
task_config = &pc;
+ package_built = true;
+
break; // Bail out from the package configurations loop.
}
}
}
- // If the task response manifest is prepared, then bail out from the
- // package loop, commit the transaction and respond.
+ // If the task manifest is prepared, then bail out from the package
+ // loop, commit the transaction and respond. Otherwise, stash the
+ // build toolchain into the tenant, unless it is already stashed or
+ // the current package already has some configurations (being)
+ // built.
//
- if (!task_response.session.empty ())
+ if (!task_response.task)
+ {
+ // Note that since there can be false negatives for the
+ // package_built flag (see above), there can be redundant tenant
+ // queries which, however, seems harmless (query uses the primary
+ // key and the object memory footprint is small).
+ //
+ if (!package_built)
+ {
+ if (t == nullptr)
+ t = build_db_->load<build_tenant> (p->id.tenant);
+
+ if (!t->toolchain)
+ {
+ t->toolchain = build_toolchain {toolchain_name,
+ toolchain_version};
+
+ build_db_->update (t);
+ }
+ }
+ }
+ else
break;
}
- t.commit ();
+ tr.commit ();
}
// If we don't have an unbuilt package, then let's see if we have a
// build configuration to rebuild.
//
- if (task_response.session.empty () && !rebuilds.empty ())
+ if (!task_response.task && !rebuilds.empty ())
{
// Sort the configuration rebuild list with the following sort
// priority:
@@ -2047,15 +2096,15 @@ handle (request& rq, response& rs)
catch (const odb::deadlock&)
{
// Just try with the next rebuild. But first, reset the task
- // response manifest that we may have prepared.
+ // manifest and the session that we may have prepared.
//
task_response = task_response_manifest ();
}
- // If the task response manifest is prepared, then bail out from the
- // package configuration rebuilds loop and respond.
+ // If the task manifest is prepared, then bail out from the package
+ // configuration rebuilds loop and respond.
//
- if (!task_response.session.empty ())
+ if (task_response.task)
break;
}
}
@@ -2135,9 +2184,9 @@ handle (request& rq, response& rs)
update_tenant_service_state (conn, b.tenant, f);
}
- // If the task response manifest is prepared, then check that the number
- // of the build auxiliary machines is less than 10. If that's not the
- // case, then turn the build into the built state with the abort status.
+ // If the task manifest is prepared, then check that the number of the
+ // build auxiliary machines is less than 10. If that's not the case,
+ // then turn the build into the built state with the abort status.
//
if (task_response.task &&
task_response.task->auxiliary_machines.size () > 9)
diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx
index b0de618..30562f3 100644
--- a/mod/mod-builds.cxx
+++ b/mod/mod-builds.cxx
@@ -393,8 +393,11 @@ handle (request& rq, response& rs)
if (!tenant.empty ())
tn = tenant;
- // Return the list of distinct toolchain name/version pairs. The build db
- // transaction must be started.
+ // Return the list of distinct toolchain name/version pairs. If no builds
+ // are present for the tenant, then fallback to the toolchain recorded in
+ // the tenant object, if present.
+ //
+ // Note: the build db transaction must be started.
//
using toolchains = vector<pair<string, version>>;
@@ -410,11 +413,19 @@ handle (request& rq, response& rs)
false /* first */)))
r.emplace_back (move (t.name), move (t.version));
+ if (r.empty ())
+ {
+ shared_ptr<build_tenant> t (build_db_->find<build_tenant> (tenant));
+
+ if (t != nullptr && t->toolchain)
+ r.emplace_back (t->toolchain->name, t->toolchain->version);
+ }
+
return r;
};
auto print_form = [&s, &params, this] (const toolchains& toolchains,
- size_t build_count)
+ optional<size_t> build_count)
{
// Print the package builds filter form on the first page only.
//
@@ -525,7 +536,7 @@ handle (request& rq, response& rs)
conf_ids.push_back (c.first);
}
- size_t count;
+ optional<size_t> count;
size_t page (params.page ());
if (params.result () != "unbuilt") // Print package build configurations.
@@ -656,7 +667,7 @@ handle (request& rq, response& rs)
--print;
}
- ++count;
+ ++(*count);
}
}
@@ -937,7 +948,7 @@ handle (request& rq, response& rs)
count = npos - ncur;
}
else
- count = 0;
+ count = nullopt; // Unknown count.
}
// Print the filter form.
@@ -1148,7 +1159,11 @@ handle (request& rq, response& rs)
add_filter ("pc", pkg_cfg);
add_filter ("rs", params.result (), "*");
- s << DIV_PAGER (page, count, page_configs, options_->build_pages (), u)
+ s << DIV_PAGER (page,
+ count ? *count : 0,
+ page_configs,
+ options_->build_pages (),
+ u)
<< ~DIV
<< ~BODY
<< ~HTML;
diff --git a/mod/page.cxx b/mod/page.cxx
index 5483183..bc2e42d 100644
--- a/mod/page.cxx
+++ b/mod/page.cxx
@@ -137,9 +137,17 @@ namespace brep
void DIV_COUNTER::
operator() (serializer& s) const
{
- s << DIV(ID="count")
- << count_ << " "
- << (count_ % 10 == 1 && count_ % 100 != 11 ? singular_ : plural_)
+ s << DIV(ID="count");
+
+ if (count_)
+ s << *count_;
+ else
+ s << '?';
+
+ s << ' '
+ << (count_ && *count_ % 10 == 1 && *count_ % 100 != 11
+ ? singular_
+ : plural_)
<< ~DIV;
}
diff --git a/mod/page.hxx b/mod/page.hxx
index cac2b8b..7329e2d 100644
--- a/mod/page.hxx
+++ b/mod/page.hxx
@@ -82,21 +82,24 @@ namespace brep
// Generate counter element.
//
- // It could be redunant to distinguish between singular and plural word forms
- // if it wouldn't be so cheap in English, and phrase '1 Packages' wouldn't
- // look that ugly.
+ // If the count argument is nullopt, then it is assumed that the count is
+ // unknown and the '?' character is printed instead of the number.
+ //
+ // Note that it could be redunant to distinguish between singular and plural
+ // word forms if it wouldn't be so cheap in English, and phrase '1 Packages'
+ // wouldn't look that ugly.
//
class DIV_COUNTER
{
public:
- DIV_COUNTER (size_t c, const char* s, const char* p)
+ DIV_COUNTER (optional<size_t> c, const char* s, const char* p)
: count_ (c), singular_ (s), plural_ (p) {}
void
operator() (xml::serializer&) const;
private:
- size_t count_;
+ optional<size_t> count_;
const char* singular_;
const char* plural_;
};