aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-03-18 22:17:49 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-03-27 17:28:44 +0300
commit35359f038f571dc46de3d14af72a2bc911fb0a24 (patch)
treede3e89d678e78b9efc4d395274fd7ccc68f4a213
parent8ad672cc7211952716ffe1fbf76c179b4f1149e3 (diff)
Implement brep-monitor
-rw-r--r--INSTALL72
-rw-r--r--INSTALL-DEV4
-rw-r--r--clean/clean.cli4
-rw-r--r--clean/clean.cxx10
-rw-r--r--doc/buildfile9
-rwxr-xr-xdoc/cli.sh2
-rw-r--r--doc/manual.cli3
-rw-r--r--etc/brep-module.conf5
-rw-r--r--etc/systemd/brep-clean.timer4
-rw-r--r--etc/systemd/brep-monitor.service14
-rw-r--r--etc/systemd/brep-monitor.timer23
-rw-r--r--libbrep/build-package.hxx2
-rw-r--r--libbrep/build.cxx21
-rw-r--r--libbrep/build.hxx82
-rw-r--r--libbrep/build.xml40
-rw-r--r--libbrep/common.hxx30
-rw-r--r--load/load.cli4
-rw-r--r--load/load.cxx2
-rw-r--r--migrate/migrate.cli4
-rw-r--r--migrate/migrate.cxx4
-rw-r--r--mod/.gitignore2
-rw-r--r--mod/build-config-module.cxx236
-rw-r--r--mod/build-config-module.hxx40
-rw-r--r--mod/build-config.cxx249
-rw-r--r--mod/build-config.hxx45
-rw-r--r--mod/build.cxx2
-rw-r--r--mod/buildfile31
-rw-r--r--mod/database-module.cxx2
-rw-r--r--mod/database-module.hxx2
-rw-r--r--mod/mod-build-configs.cxx7
-rw-r--r--mod/mod-build-configs.hxx2
-rw-r--r--mod/mod-build-force.cxx4
-rw-r--r--mod/mod-build-force.hxx2
-rw-r--r--mod/mod-build-log.cxx4
-rw-r--r--mod/mod-build-log.hxx2
-rw-r--r--mod/mod-build-result.cxx7
-rw-r--r--mod/mod-build-result.hxx2
-rw-r--r--mod/mod-build-task.cxx22
-rw-r--r--mod/mod-build-task.hxx2
-rw-r--r--mod/mod-builds.cxx37
-rw-r--r--mod/mod-builds.hxx2
-rw-r--r--mod/mod-ci.cxx11
-rw-r--r--mod/mod-ci.hxx4
-rw-r--r--mod/mod-package-details.cxx9
-rw-r--r--mod/mod-package-details.hxx2
-rw-r--r--mod/mod-package-version-details.cxx9
-rw-r--r--mod/mod-package-version-details.hxx2
-rw-r--r--mod/mod-packages.cxx9
-rw-r--r--mod/mod-packages.hxx2
-rw-r--r--mod/mod-repository-details.cxx9
-rw-r--r--mod/mod-repository-details.hxx2
-rw-r--r--mod/mod-repository-root.cxx4
-rw-r--r--mod/mod-repository-root.hxx2
-rw-r--r--mod/mod-submit.cxx7
-rw-r--r--mod/mod-submit.hxx4
-rw-r--r--mod/module.cli (renamed from mod/options.cli)8
-rw-r--r--mod/module.cxx6
-rw-r--r--mod/module.hxx4
-rw-r--r--mod/page.cxx7
-rw-r--r--mod/page.hxx2
-rw-r--r--mod/services.cxx2
-rw-r--r--mod/types-parsers.cxx2
-rw-r--r--mod/types-parsers.hxx2
-rw-r--r--monitor/.gitignore2
-rw-r--r--monitor/buildfile45
-rw-r--r--monitor/module.cli16
-rw-r--r--monitor/monitor.cli176
-rw-r--r--monitor/monitor.cxx766
-rw-r--r--tests/web/xhtml/buildfile4
-rw-r--r--tests/web/xhtml/driver.cxx2
-rw-r--r--web/server/apache/log.hxx (renamed from web/apache/log.hxx)10
-rw-r--r--web/server/apache/request.cxx (renamed from web/apache/request.cxx)6
-rw-r--r--web/server/apache/request.hxx (renamed from web/apache/request.hxx)14
-rw-r--r--web/server/apache/request.ixx (renamed from web/apache/request.ixx)2
-rw-r--r--web/server/apache/service.cxx (renamed from web/apache/service.cxx)8
-rw-r--r--web/server/apache/service.hxx (renamed from web/apache/service.hxx)16
-rw-r--r--web/server/apache/service.txx (renamed from web/apache/service.txx)2
-rw-r--r--web/server/apache/stream.hxx (renamed from web/apache/stream.hxx)10
-rw-r--r--web/server/buildfile (renamed from web/buildfile)7
-rw-r--r--web/server/mime-url-encoding.cxx (renamed from web/mime-url-encoding.cxx)4
-rw-r--r--web/server/mime-url-encoding.hxx (renamed from web/mime-url-encoding.hxx)8
-rw-r--r--web/server/module.hxx (renamed from web/module.hxx)8
-rw-r--r--web/version.hxx.in11
-rw-r--r--web/xhtml/.gitignore (renamed from web/.gitignore)0
-rw-r--r--web/xhtml/buildfile10
-rw-r--r--web/xhtml/fragment.cxx (renamed from web/xhtml-fragment.cxx)6
-rw-r--r--web/xhtml/fragment.hxx (renamed from web/xhtml-fragment.hxx)2
-rw-r--r--web/xhtml/serialization.hxx (renamed from web/xhtml.hxx)10
-rw-r--r--web/xhtml/version.hxx.in11
89 files changed, 1800 insertions, 506 deletions
diff --git a/INSTALL b/INSTALL
index 59cc081..00f7975 100644
--- a/INSTALL
+++ b/INSTALL
@@ -11,8 +11,9 @@ can be omitted.
1. Create 'brep' User
This user will be used to run the brep package database loader, build database
-cleaner, and the database schemes migration utility. We will also use its home
-directory to build and install the brep module, store its configuration, etc.
+cleaner, monitor, and database schemas migration utility. We will also use its
+home directory to build and install the brep module, store its configuration,
+etc.
Note: if the deployment machine employs SELinux, then this approach may
require additional configuration steps (not shown) in order to allow Apache2
@@ -194,7 +195,7 @@ CREATE EXTENSION citext;
Exit psql (^D)
-5. Create Database Schemes and Load Repositories
+5. Create Database Schemas and Load Repositories
$ mkdir config
$ edit config/loadtab # Loader configuration, see brep-load(1).
@@ -275,7 +276,7 @@ can also find this fragment in install/share/brep/etc/brep-apache2.conf):
#
brep-email admin@example.org
- # Repository host. It specifies the scheme and the host address (but
+ # Repository host. It specifies the schema and the host address (but
# not the root path; see brep-root below) that will be used whenever
# brep needs to construct an absolute URL to one of its locations (for
# example, a link to a build log that is being send via email).
@@ -384,15 +385,15 @@ $ cd install/share/brep/www/
$ for i in *.scss; do sassc -s compressed $i `basename -s .scss $i`.css; done
-8. Setup Periodic Loader and Cleaner Execution
+8. Setup Periodic Loader, Cleaner, and Monitor Execution
Initially this guide suggested using systemd user session support to run the
-loader and the cleaner. However, the current state of user sessions has one
-major drawback: they are not started/attached-to when logging in with su -l
-(see Debian bug #813789 for details). This limitation makes them unusable in
-our setup. If you still would like to use systemd to run the loader and the
-cleaner, then you can set it up as a system-wide service which runs the
-utilities as the brep user/group. Otherwise, a cron job is a natural choice.
+loader, cleaner, and monitor. However, the current state of user sessions has
+one major drawback: they are not started/attached-to when logging in with su
+-l (see Debian bug #813789 for details). This limitation makes them unusable
+in our setup. If you still would like to use systemd to run the utilities,
+then you can set it up as a system-wide service which runs them as the brep
+user/group. Otherwise, a cron job is a natural choice.
Note that the builds cleaner execution is optional and is only required if the
build2 build bot functionality is enabled (see the build bot documentation for
@@ -402,29 +403,37 @@ parts in the subsequent subsections.
If the CI request functionality is enabled you most likely will want to
additionally setup the tenants cleanup.
+The monitor execution is also optional and currently only makes sense if the
+build2 build bot functionality is enabled. Note that you may need to replace
+the public toolchain name argument in the monitor utility command with a real
+list of toolchain names (and optionally versions) used in the brep build
+infrastructure.
-8.a Setup Periodic Loader and Cleaner Execution with cron
-The following crontab entries will execute the loader every five minutes
-and the tenants and builds cleaners once a day at midnight:
+8.a Setup Periodic Loader, Cleaner, and Monitor Execution with cron
+
+The following crontab entries will execute the loader every five minutes, the
+tenants and builds cleaners once a day at midnight, and the monitor every hour
+(all shifted by a few minutes in order not to clash with other jobs):
$ crontab -l
MAILTO=<brep-admin-email>
PATH=/usr/local/bin:/bin:/usr/bin
*/5 * * * * $HOME/install/bin/brep-load $HOME/config/loadtab
-0 0 * * * $HOME/install/bin/brep-clean tenants 240
-0 0 * * * $HOME/install/bin/brep-clean builds $HOME/config/buildtab
+1 0 * * * $HOME/install/bin/brep-clean tenants 240
+2 0 * * * $HOME/install/bin/brep-clean builds $HOME/config/buildtab
+3 * * * * $HOME/install/bin/brep-monitor --report-timeout 86400 --clean $HOME/config/brep-module.conf public
^D
Note that here we assume that bpkg (which is executed by brep-load) is in one
of the PATH's directories (usually /usr/local/bin).
-8.b Setup Periodic Loader and Cleaner Execution with systemd
+8.b Setup Periodic Loader, Cleaner, and Monitor Execution with systemd
In this version we will use the systemd user session to periodically run the
-loader and the cleaner as the brep user. If your installation doesn't use
-systemd, then a cron job would be a natural alternative (see above).
+loader, cleaner, and monitor as the brep user. If your installation doesn't
+use systemd, then a cron job would be a natural alternative (see above).
As the first step, make sure systemd user sessions support is working for the
brep user:
@@ -443,6 +452,7 @@ $ sudo loginctl enable-linger brep
$ mkdir -p .config/systemd/user
$ cp install/share/brep/etc/systemd/brep-load.* .config/systemd/user/
$ cp install/share/brep/etc/systemd/brep-clean.* .config/systemd/user/
+$ cp install/share/brep/etc/systemd/brep-monitor.* .config/systemd/user/
Start the service to make sure there are no issues:
@@ -452,16 +462,21 @@ $ journalctl
$ systemctl --user start brep-clean.service
$ journalctl
+$ systemctl --user start brep-monitor.service
+$ journalctl
+
Start the timers and monitor them to make sure they fire:
$ systemctl --user start brep-load.timer
$ systemctl --user start brep-clean.timer
+$ systemctl --user start brep-monitor.timer
$ journalctl -f
If everything looks good, enable the timer to be started at boot time:
$ systemctl --user enable brep-load.timer
$ systemctl --user enable brep-clean.timer
+$ systemctl --user enable brep-monitor.timer
9. Upgrade Procedure
@@ -483,18 +498,20 @@ $ cd brep
$ bpkg fetch
$ bpkg build brep
-If you are using a systemd-based setup, then stop and disable the loader and
-the cleaner:
+If you are using a systemd-based setup, then stop and disable the loader,
+cleaner, and monitor:
$ systemctl --user disable --now brep-load.timer
$ systemctl --user disable --now brep-clean.timer
+$ systemctl --user disable --now brep-monitor.timer
$ systemctl --user stop brep-load.service
$ systemctl --user stop brep-clean.service
+$ systemctl --user stop brep-monitor.service
If you are using a cron-based setup, then it is not worth it commenting out the
-job entries. If the new version of the loader or the cleaner gets executed
-before or during the migration, then it will fail and you will get an email
-with the diagnostics. Other than that, it should be harmless.
+job entries. If the new version of the brep utilities gets executed before or
+during the migration, then it will fail and you will get an email with the
+diagnostics. Other than that, it should be harmless.
Stop apache:
@@ -510,7 +527,7 @@ Review brep-module.conf changes that may need to be merged:
$ diff -u install/share/brep/etc/brep-module.conf config/brep-module.conf
-Migrate database schemes:
+Migrate database schemas:
$ install/bin/brep-migrate package
$ install/bin/brep-migrate build
@@ -521,17 +538,20 @@ is not possible), then one way to do it would be:
$ psql -d brep_package -c 'DROP OWNED BY brep'
$ psql -d brep_build -c 'DROP OWNED BY brep'
-If using systemd, then start and enable the loader and the cleaner:
+If using systemd, then start and enable the loader, cleaner, and monitor:
$ systemctl --user start brep-load.service
$ systemctl --user status brep-load.service
$ systemctl --user start brep-clean.service
$ systemctl --user status brep-clean.service
+$ systemctl --user start brep-monitor.service
+$ systemctl --user status brep-monitor.service
If everything looks good, enable periodic execution:
$ systemctl --user enable --now brep-load.timer
$ systemctl --user enable --now brep-clean.timer
+$ systemctl --user enable --now brep-monitor.timer
If using cron, then simply wait for the next run.
diff --git a/INSTALL-DEV b/INSTALL-DEV
index 101d9d7..af5c06e 100644
--- a/INSTALL-DEV
+++ b/INSTALL-DEV
@@ -113,7 +113,7 @@ CREATE EXTENSION citext;
Exit psql (^D)
-2. Create Database Schemes and Load the Repository
+2. Create Database Schemas and Load the Repository
All the commands are executed from brep project root.
@@ -205,7 +205,7 @@ $ sudo tail -f /var/log/apache2/error.log
4. Reloading During Development
-To do a "complete reload" (i.e., recreate database schemes, load the repository
+To do a "complete reload" (i.e., recreate database schemas, load the repository
data, and reload the Apache2 plugin), execute the following from brep/:
$ migrate/brep-migrate --recreate package
diff --git a/clean/clean.cli b/clean/clean.cli
index 3c710fe..d3be4d6 100644
--- a/clean/clean.cli
+++ b/clean/clean.cli
@@ -127,8 +127,8 @@ Fatal error.|
\li|\cb{2}
-An instance of \cb{brep-clean} or \l{brep-migrate(1)} is already running. Try
-again.|
+An instance of \cb{brep-clean} or some other \cb{brep} utility is already
+running. Try again.|
\li|\cb{3}
diff --git a/clean/clean.cxx b/clean/clean.cxx
index d7a7731..5401ab1 100644
--- a/clean/clean.cxx
+++ b/clean/clean.cxx
@@ -111,8 +111,8 @@ namespace brep
ops.db_port (),
"options='-c default_transaction_isolation=serializable'");
- // Prevent several brep-clean/migrate instances from updating build
- // database simultaneously.
+ // Prevent several brep utility instances from updating the database
+ // simultaneously.
//
database_lock l (db);
@@ -316,6 +316,12 @@ namespace brep
? i->second
: default_timeout);
+ // @@ Note that this approach doesn't consider the case when both
+ // the configuration and the package still exists but the package
+ // now excludes the configuration (configuration is now of the
+ // legacy class instead of the default class, etc). We should
+ // probably re-implement it in a way brep-monitor does it.
+ //
bool cleanup (
// Check that the build is not stale.
//
diff --git a/doc/buildfile b/doc/buildfile
index 2595879..f0a9387 100644
--- a/doc/buildfile
+++ b/doc/buildfile
@@ -1,10 +1,11 @@
# file : doc/buildfile
# license : MIT; see accompanying LICENSE file
-cmds = \
-brep-clean \
-brep-load \
-brep-migrate
+cmds = \
+brep-clean \
+brep-load \
+brep-migrate \
+brep-monitor
./: {man1 xhtml}{$cmds} \
css{common pre-box man} \
diff --git a/doc/cli.sh b/doc/cli.sh
index b8c4c00..42303dc 100755
--- a/doc/cli.sh
+++ b/doc/cli.sh
@@ -71,7 +71,7 @@ o="--output-prefix brep-"
#
#compile "brep" $o --output-prefix ""
-pages="clean/clean load/load migrate/migrate"
+pages="clean/clean load/load migrate/migrate monitor/monitor"
for p in $pages; do
compile $p $o
diff --git a/doc/manual.cli b/doc/manual.cli
index 0b3b038..71a25a5 100644
--- a/doc/manual.cli
+++ b/doc/manual.cli
@@ -15,7 +15,8 @@
This document describes \c{brep}, the \c{build2} package repository web
interface. For the command line interface of \c{brep} utilities refer to the
-\l{brep-load(1)}, \l{brep-clean(1)}, and \l{brep-migrate(1)} man pages.
+\l{brep-load(1)}, \l{brep-clean(1)}, \l{brep-migrate(1)}, and
+\l{brep-monitor(1)} man pages.
\h1#submit|Package Submission|
diff --git a/etc/brep-module.conf b/etc/brep-module.conf
index 458261e..12e96cd 100644
--- a/etc/brep-module.conf
+++ b/etc/brep-module.conf
@@ -3,6 +3,11 @@
# brep-). See brep(1) for detailed description of each configuration option.
# Commented out options indicate their default values.
#
+# Besides being parsed by the brep module, this file may also be parsed by
+# brep utilities that are normally only interested in the subset of the
+# options. To simplify skipping of unrecognized, this file must always have an
+# option name and its value on the same line.
+#
# Package search page title. It is placed inside XHTML5 <title> element.
#
diff --git a/etc/systemd/brep-clean.timer b/etc/systemd/brep-clean.timer
index f4c587e..8e1e6e7 100644
--- a/etc/systemd/brep-clean.timer
+++ b/etc/systemd/brep-clean.timer
@@ -10,9 +10,9 @@ Unit=brep-clean.service
#
Persistent=false
-# Wait 20 seconds until the first run.
+# Wait 30 seconds until the first run.
#
-OnBootSec=20
+OnBootSec=30
# Then wait 5 minutes until the next run.
#
diff --git a/etc/systemd/brep-monitor.service b/etc/systemd/brep-monitor.service
new file mode 100644
index 0000000..0a5c25e
--- /dev/null
+++ b/etc/systemd/brep-monitor.service
@@ -0,0 +1,14 @@
+[Unit]
+Description=brep infrastructure monitor service
+
+[Service]
+Type=oneshot
+#User=brep
+#Group=brep
+
+# Replace the public toolchain name with a real list of toolchains.
+#
+ExecStart=/home/brep/install/bin/brep-monitor --report-timeout 86400 --clean /home/brep/config/brep-module.conf public
+
+[Install]
+WantedBy=default.target
diff --git a/etc/systemd/brep-monitor.timer b/etc/systemd/brep-monitor.timer
new file mode 100644
index 0000000..f5f5a64
--- /dev/null
+++ b/etc/systemd/brep-monitor.timer
@@ -0,0 +1,23 @@
+[Unit]
+Description=brep infrastructure monitor timer
+RefuseManualStart=no
+RefuseManualStop=no
+
+[Timer]
+Unit=brep-monitor.service
+
+# Don't keep track of the timer across reboots.
+#
+Persistent=false
+
+# Wait 40 seconds until the first run.
+#
+OnBootSec=40
+
+# Then wait 1 hour until the next run.
+#
+OnUnitInactiveSec=1h
+
+
+[Install]
+WantedBy=timers.target
diff --git a/libbrep/build-package.hxx b/libbrep/build-package.hxx
index 22a8151..702f937 100644
--- a/libbrep/build-package.hxx
+++ b/libbrep/build-package.hxx
@@ -118,6 +118,8 @@ namespace brep
package_id id;
upstream_version version;
+ bool archived; // True if the tenant the package belongs to is archived.
+
// Database mapping.
//
#pragma db member(version) set(this.version.init (this.id.version, (?)))
diff --git a/libbrep/build.cxx b/libbrep/build.cxx
index b6a07c7..db5bda2 100644
--- a/libbrep/build.cxx
+++ b/libbrep/build.cxx
@@ -80,4 +80,25 @@ namespace brep
target (move (trg))
{
}
+
+ // build_delay
+ //
+ build_delay::
+ build_delay (string tnt,
+ package_name_type pnm, version pvr,
+ string cfg,
+ string tnm, version tvr,
+ timestamp ptm)
+ : id (package_id (move (tnt), move (pnm), pvr),
+ move (cfg),
+ move (tnm), tvr),
+ tenant (id.package.tenant),
+ package_name (id.package.name),
+ package_version (move (pvr)),
+ configuration (id.configuration),
+ toolchain_name (id.toolchain_name),
+ toolchain_version (move (tvr)),
+ package_timestamp (ptm)
+ {
+ }
}
diff --git a/libbrep/build.hxx b/libbrep/build.hxx
index 7e548a4..83b30a8 100644
--- a/libbrep/build.hxx
+++ b/libbrep/build.hxx
@@ -25,7 +25,7 @@
//
#define LIBBREP_BUILD_SCHEMA_VERSION_BASE 9
-#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 9, closed)
+#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 10, 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
@@ -212,6 +212,16 @@ namespace brep
//
optional<result_status> status;
+ // Time of setting the result status that can be considered as the build
+ // task completion (currently all the result_status values). Initialized
+ // with timestamp_nonexistent by default.
+ //
+ // Note that in the future we may not consider abort and abnormal as the
+ // task completion and, for example, proceed with automatic rebuild (the
+ // flake monitor idea).
+ //
+ timestamp_type completion_timestamp;
+
// May be present only for the building state.
//
optional<string> agent_fingerprint;
@@ -244,6 +254,10 @@ namespace brep
//
#pragma db member(timestamp) index
+ // @@ TMP remove when 0.13.0 is released.
+ //
+ #pragma db member(completion_timestamp) default(0)
+
#pragma db member(results) id_column("") value_column("") \
section(results_section)
@@ -259,9 +273,7 @@ namespace brep
: tenant (id.package.tenant),
package_name (id.package.name),
configuration (id.configuration),
- toolchain_name (id.toolchain_name)
- {
- }
+ toolchain_name (id.toolchain_name) {}
};
// Note that ADL can't find the equal operator in join conditions, so we use
@@ -340,6 +352,68 @@ namespace brep
//
#pragma db member(result) column("count(" + build::id.package.name + ")")
};
+
+ // Used to track the package build delays since the last build or, if not
+ // present, since the first opportunity to build the package.
+ //
+ #pragma db object pointer(shared_ptr) session
+ class build_delay
+ {
+ public:
+ using package_name_type = brep::package_name;
+
+ // If toolchain version is empty, then the object represents a minimum
+ // delay across all versions of the toolchain.
+ //
+ build_delay (string tenant,
+ package_name_type, version,
+ string configuration,
+ string toolchain_name, version toolchain_version,
+ timestamp package_timestamp);
+
+ build_id id;
+
+ string& tenant; // Tracks id.package.tenant.
+ package_name_type& package_name; // Tracks id.package.name.
+ upstream_version package_version; // Original of id.package.version.
+ string& configuration; // Tracks id.configuration.
+ string& toolchain_name; // Tracks id.toolchain_name.
+ upstream_version toolchain_version; // Original of id.toolchain_version.
+
+ // Time of the latest delay report. Initialized with timestamp_nonexistent
+ // by default.
+ //
+ timestamp report_timestamp;
+
+ // Time when the package is initially considered as buildable for this
+ // configuration and toolchain. It is used to track the build delay if the
+ // build object is absent (the first build task is not yet issued, the
+ // build is removed by brep-clean, etc).
+ //
+ timestamp package_timestamp;
+
+ // Database mapping.
+ //
+ #pragma db member(id) id column("")
+
+ #pragma db member(tenant) transient
+ #pragma db member(package_name) transient
+ #pragma db member(package_version) \
+ set(this.package_version.init (this.id.package.version, (?)))
+ #pragma db member(configuration) transient
+ #pragma db member(toolchain_name) transient
+ #pragma db member(toolchain_version) \
+ set(this.toolchain_version.init (this.id.toolchain_version, (?)))
+
+ private:
+ friend class odb::access;
+
+ build_delay ()
+ : tenant (id.package.tenant),
+ package_name (id.package.name),
+ configuration (id.configuration),
+ toolchain_name (id.toolchain_name) {}
+ };
}
#endif // LIBBREP_BUILD_HXX
diff --git a/libbrep/build.xml b/libbrep/build.xml
index 3ade7c8..bf8920b 100644
--- a/libbrep/build.xml
+++ b/libbrep/build.xml
@@ -1,4 +1,44 @@
<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="build" version="1">
+ <changeset version="10">
+ <alter-table name="build">
+ <add-column name="completion_timestamp" type="BIGINT" null="false" default="0"/>
+ </alter-table>
+ <add-table name="build_delay" kind="object">
+ <column name="package_tenant" type="TEXT" null="false"/>
+ <column name="package_name" type="CITEXT" null="false"/>
+ <column name="package_version_epoch" type="INTEGER" null="false"/>
+ <column name="package_version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="package_version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="package_version_revision" type="INTEGER" null="false"/>
+ <column name="configuration" type="TEXT" null="false"/>
+ <column name="toolchain_name" type="TEXT" null="false"/>
+ <column name="toolchain_version_epoch" type="INTEGER" null="false"/>
+ <column name="toolchain_version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="toolchain_version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="toolchain_version_revision" type="INTEGER" null="false"/>
+ <column name="package_version_upstream" type="TEXT" null="false"/>
+ <column name="package_version_release" type="TEXT" null="true"/>
+ <column name="toolchain_version_upstream" type="TEXT" null="false"/>
+ <column name="toolchain_version_release" type="TEXT" null="true"/>
+ <column name="report_timestamp" type="BIGINT" null="false"/>
+ <column name="package_timestamp" type="BIGINT" null="false"/>
+ <primary-key>
+ <column name="package_tenant"/>
+ <column name="package_name"/>
+ <column name="package_version_epoch"/>
+ <column name="package_version_canonical_upstream"/>
+ <column name="package_version_canonical_release"/>
+ <column name="package_version_revision"/>
+ <column name="configuration"/>
+ <column name="toolchain_name"/>
+ <column name="toolchain_version_epoch"/>
+ <column name="toolchain_version_canonical_upstream"/>
+ <column name="toolchain_version_canonical_release"/>
+ <column name="toolchain_version_revision"/>
+ </primary-key>
+ </add-table>
+ </changeset>
+
<model version="9">
<table name="build" kind="object">
<column name="package_tenant" type="TEXT" null="false"/>
diff --git a/libbrep/common.hxx b/libbrep/common.hxx
index 44028df..b7fc2da 100644
--- a/libbrep/common.hxx
+++ b/libbrep/common.hxx
@@ -8,6 +8,8 @@
#include <chrono>
#include <type_traits> // static_assert
+#include <odb/query.hxx>
+
#include <libbpkg/package-name.hxx>
#include <libbrep/types.hxx>
@@ -510,6 +512,34 @@ namespace brep
compare_version_ne (x.version, y.version, true);
}
+ // Allow comparing the query members with the query parameters bound by
+ // reference to variables of the package id type (in particular in the
+ // prepared queries).
+ //
+ // Note that it is not operator==() since the query template parameter type
+ // can not be deduced from the function parameter types and needs to be
+ // specified explicitly.
+ //
+ template <typename T, typename ID>
+ inline auto
+ equal (const ID& x, const package_id& y)
+ -> decltype (x.tenant == odb::query<T>::_ref (y.tenant) &&
+ x.name == odb::query<T>::_ref (y.name) &&
+ x.version.epoch == odb::query<T>::_ref (y.version.epoch))
+ {
+ using query = odb::query<T>;
+
+ const auto& qv (x.version);
+ const canonical_version& v (y.version);
+
+ return x.tenant == query::_ref (y.tenant) &&
+ x.name == query::_ref (y.name) &&
+ qv.epoch == query::_ref (v.epoch) &&
+ qv.canonical_upstream == query::_ref (v.canonical_upstream) &&
+ qv.canonical_release == query::_ref (v.canonical_release) &&
+ qv.revision == query::_ref (v.revision);
+ }
+
// Repository id comparison operators.
//
inline bool
diff --git a/load/load.cli b/load/load.cli
index 1fc2e17..be19ebf 100644
--- a/load/load.cli
+++ b/load/load.cli
@@ -161,8 +161,8 @@ Fatal error.|
\li|\cb{2}
-An instance of \cb{brep-load} or \l{brep-migrate(1)} is already running. Try
-again.|
+An instance of \cb{brep-load} or some other \cb{brep} utility is already
+running. Try again.|
\li|\cb{3}
diff --git a/load/load.cxx b/load/load.cxx
index 5638553..bf8584c 100644
--- a/load/load.cxx
+++ b/load/load.cxx
@@ -1276,7 +1276,7 @@ try
ops.db_port (),
"options='-c default_transaction_isolation=serializable'");
- // Prevent several brep-load/migrate instances from updating DB
+ // Prevent several brep utility instances from updating the package database
// simultaneously.
//
database_lock l (db);
diff --git a/migrate/migrate.cli b/migrate/migrate.cli
index ba9a572..177f991 100644
--- a/migrate/migrate.cli
+++ b/migrate/migrate.cli
@@ -125,8 +125,8 @@ Fatal error.|
\li|\cb{2}
-An instance of \cb{brep-migrate} or \l{brep-load(1)} is already running. Try
-again.|
+An instance of \cb{brep-migrate} or some other \cb{brep} utility is already
+running. Try again.|
\li|\cb{3}
diff --git a/migrate/migrate.cxx b/migrate/migrate.cxx
index 8b083fc..81c4543 100644
--- a/migrate/migrate.cxx
+++ b/migrate/migrate.cxx
@@ -300,12 +300,12 @@ try
ops.db_port (),
"options='-c default_transaction_isolation=serializable'");
- // Prevent several brep-migrate/load instances from updating DB
+ // Prevent several brep utility instances from updating the database
// simultaneously.
//
database_lock l (db);
- // Currently we don't support data migration for the manual database scheme
+ // Currently we don't support data migration for the manual database schema
// migration.
//
if (db.schema_migration (db_schema))
diff --git a/mod/.gitignore b/mod/.gitignore
index c6e608b..6b64ad0 100644
--- a/mod/.gitignore
+++ b/mod/.gitignore
@@ -1 +1 @@
-options.?xx
+*-options.?xx
diff --git a/mod/build-config-module.cxx b/mod/build-config-module.cxx
index b1818b7..831cb78 100644
--- a/mod/build-config-module.cxx
+++ b/mod/build-config-module.cxx
@@ -9,10 +9,9 @@
#include <sstream>
#include <libbutl/sha256.mxx>
-#include <libbutl/utility.mxx> // throw_generic_error(), alpha(), etc.
+#include <libbutl/utility.mxx> // throw_generic_error()
#include <libbutl/openssl.mxx>
#include <libbutl/filesystem.mxx> // dir_iterator, dir_entry
-#include <libbutl/path-pattern.mxx>
namespace brep
{
@@ -157,184 +156,6 @@ namespace brep
build_conf_map_ = make_shared<conf_map_type> (move (conf_map));
}
- // The default underlying class set expression (see below).
- //
- static const build_class_expr default_ucs_expr (
- {"default"}, '+', "Default.");
-
- bool build_config_module::
- exclude (const small_vector<build_class_expr, 1>& exprs,
- const vector<build_constraint>& constrs,
- const build_config& cfg,
- string* reason) const
- {
- // Save the first sentence of the reason, lower-case the first letter if
- // the beginning looks like a word (all subsequent characters until a
- // whitespace are lower-case letters).
- //
- auto sanitize = [] (const string& reason)
- {
- string r (reason.substr (0, reason.find ('.')));
-
- char c (r[0]); // Can be '\0'.
- if (alpha (c) && c == ucase (c))
- {
- bool word (true);
-
- for (size_t i (1);
- i != r.size () && (c = r[i]) != ' ' && c != '\t' && c != '\n';
- ++i)
- {
- // Is not a word if contains a non-letter or an upper-case letter.
- //
- if (!alpha (c) || c == ucase (c))
- {
- word = false;
- break;
- }
- }
-
- if (word)
- r[0] = lcase (r[0]);
- }
-
- return r;
- };
-
- // First, match the configuration against the package underlying build
- // class set and expressions.
- //
- bool m (false);
-
- // Match the configuration against an expression, updating the match
- // result.
- //
- // We will use a comment of the first encountered excluding expression
- // (changing the result from true to false) or non-including one (leaving
- // the false result) as an exclusion reason.
- //
- auto match = [&cfg, &m, reason, &sanitize, this]
- (const build_class_expr& e)
- {
- bool pm (m);
- e.match (cfg.classes, build_conf_->class_inheritance_map, m);
-
- if (reason != nullptr)
- {
- // Reset the reason which, if saved, makes no sense anymore.
- //
- if (m)
- {
- reason->clear ();
- }
- else if (reason->empty () &&
- //
- // Exclusion.
- //
- (pm ||
- //
- // Non-inclusion. Make sure that the build class expression
- // is empty or starts with an addition (+...).
- //
- e.expr.empty () ||
- e.expr.front ().operation == '+'))
- {
- *reason = sanitize (e.comment);
- }
- }
- };
-
- // Determine the underlying class set. Note that in the future we can
- // potentially extend the underlying set with special classes.
- //
- const build_class_expr* ucs (
- !exprs.empty () && !exprs.front ().underlying_classes.empty ()
- ? &exprs.front ()
- : nullptr);
-
- // Note that the combined package build configuration class expression can
- // be represented as the underlying class set used as a starting set for
- // the original expressions and a restricting set, simultaneously. For
- // example, for the expression:
- //
- // default legacy : -msvc
- //
- // the resulting expression will be:
- //
- // +( +default +legacy ) -msvc &( +default +legacy )
- //
- // Let's, however, optimize it a bit based on the following facts:
- //
- // - If the underlying class set expression (+default +legacy in the above
- // example) evaluates to false, then the resulting expression also
- // evaluates to false due to the trailing '&' operation. Thus, we don't
- // need to evaluate further if that's the case.
- //
- // - On the other hand, if the underlying class set expression evaluates
- // to true, then we don't need to apply the trailing '&' operation as it
- // cannot affect the result.
- //
- const build_class_expr& ucs_expr (
- ucs != nullptr
- ? build_class_expr (ucs->underlying_classes, '+', ucs->comment)
- : default_ucs_expr);
-
- match (ucs_expr);
-
- if (m)
- {
- for (const build_class_expr& e: exprs)
- match (e);
- }
-
- // Exclude the configuration if it doesn't match the compound expression.
- //
- if (!m)
- return true;
-
- // Now check if the configuration is excluded/included via the patterns.
- //
- // To implement matching of absent name components with wildcard-only
- // pattern components we are going to convert names to paths (see
- // dash_components_to_path() for details).
- //
- // And if any of the build-{include,exclude} values (which is legal) or
- // the build configuration name/target (illegal) are invalid paths, then
- // we assume no match.
- //
- if (!constrs.empty ())
- try
- {
- path cn (dash_components_to_path (cfg.name));
- path tg (dash_components_to_path (cfg.target.string ()));
-
- for (const build_constraint& c: constrs)
- {
- if (path_match (cn,
- dash_components_to_path (c.config),
- dir_path () /* start */,
- path_match_flags::match_absent) &&
- (!c.target ||
- path_match (tg,
- dash_components_to_path (*c.target),
- dir_path () /* start */,
- path_match_flags::match_absent)))
- {
- if (!c.exclusion)
- return false;
-
- if (reason != nullptr)
- *reason = sanitize (c.comment);
-
- return true;
- }
- }
- }
- catch (const invalid_path&) {}
-
- return false;
- }
-
bool build_config_module::
belongs (const bbot::build_config& cfg, const char* cls) const
{
@@ -360,59 +181,4 @@ namespace brep
return false;
}
-
- path build_config_module::
- dash_components_to_path (const string& pattern)
- {
- string r;
- size_t nstar (0);
- for (const path_pattern_term& pt: path_pattern_iterator (pattern))
- {
- switch (pt.type)
- {
- case path_pattern_term_type::star:
- {
- // Replace ** with */**/* and skip all the remaining stars that may
- // follow in this sequence.
- //
- if (nstar == 0)
- r += "*";
- else if (nstar == 1)
- r += "/**/*"; // The first star is already copied.
-
- break;
- }
- case path_pattern_term_type::literal:
- {
- // Replace '-' with '/' and fall through otherwise.
- //
- if (get_literal (pt) == '-')
- {
- r += '/';
- break;
- }
- }
- // Fall through.
- default:
- {
- r.append (pt.begin, pt.end); // Copy the pattern term as is.
- }
- }
-
- nstar = pt.star () ? nstar + 1 : 0;
- }
-
- // Append the trailing slash to match the resulting paths as directories.
- // This is required for the trailing /* we could append to match absent
- // directory path components (see path_match_flags::match_absent for
- // details).
- //
- // Note that valid dash components may not contain a trailing dash.
- // Anyway, any extra trailing slashes will be ignored by the path
- // constructor.
- //
- r += '/';
-
- return path (move (r));
- }
}
diff --git a/mod/build-config-module.hxx b/mod/build-config-module.hxx
index 4b23056..04fd5b1 100644
--- a/mod/build-config-module.hxx
+++ b/mod/build-config-module.hxx
@@ -5,7 +5,6 @@
#define MOD_BUILD_CONFIG_MODULE_HXX
#include <map>
-#include <algorithm> // find()
#include <libbutl/utility.mxx> // compare_c_string
@@ -16,8 +15,8 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/module.hxx>
-#include <mod/options.hxx>
+#include <mod/build-config.hxx>
+#include <mod/module-options.hxx>
// Base class for modules that utilize the build controller configuration.
//
@@ -39,17 +38,18 @@ namespace brep
void
init (const options::build&);
- // 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.
- //
bool
- exclude (const small_vector<bpkg::build_class_expr, 1>&,
- const vector<bpkg::build_constraint>&,
- const bbot::build_config&,
- string* reason = nullptr) const;
+ exclude (const small_vector<bpkg::build_class_expr, 1>& exprs,
+ const vector<bpkg::build_constraint>& constrs,
+ const bbot::build_config& cfg,
+ string* reason = nullptr) const
+ {
+ return brep::exclude (exprs,
+ constrs,
+ cfg,
+ build_conf_->class_inheritance_map,
+ reason);
+ }
// Check if the configuration belongs to the specified class.
//
@@ -62,20 +62,6 @@ namespace brep
return belongs (cfg, cls.c_str ());
}
- // 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.
- //
- static path
- dash_components_to_path (const string&);
-
// Configuration/toolchain combination that, in particular, can be used as
// a set value.
//
diff --git a/mod/build-config.cxx b/mod/build-config.cxx
new file mode 100644
index 0000000..2d64aec
--- /dev/null
+++ b/mod/build-config.cxx
@@ -0,0 +1,249 @@
+// file : mod/build-config-module.cxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#include <mod/build-config.hxx>
+
+#include <libbutl/utility.mxx> // alpha(), etc.
+#include <libbutl/path-pattern.mxx>
+
+namespace brep
+{
+ using namespace std;
+ using namespace butl;
+ using namespace bpkg;
+ using namespace bbot;
+
+ // The default underlying class set expression (see below).
+ //
+ static const build_class_expr default_ucs_expr (
+ {"default"}, '+', "Default.");
+
+ bool
+ exclude (const small_vector<build_class_expr, 1>& exprs,
+ const vector<build_constraint>& constrs,
+ const build_config& cfg,
+ const map<string, string>& class_inheritance_map,
+ string* reason)
+ {
+ // Save the first sentence of the reason, lower-case the first letter if
+ // the beginning looks like a word (all subsequent characters until a
+ // whitespace are lower-case letters).
+ //
+ auto sanitize = [] (const string& reason)
+ {
+ string r (reason.substr (0, reason.find ('.')));
+
+ char c (r[0]); // Can be '\0'.
+ if (alpha (c) && c == ucase (c))
+ {
+ bool word (true);
+
+ for (size_t i (1);
+ i != r.size () && (c = r[i]) != ' ' && c != '\t' && c != '\n';
+ ++i)
+ {
+ // Is not a word if contains a non-letter or an upper-case letter.
+ //
+ if (!alpha (c) || c == ucase (c))
+ {
+ word = false;
+ break;
+ }
+ }
+
+ if (word)
+ r[0] = lcase (r[0]);
+ }
+
+ return r;
+ };
+
+ // First, match the configuration against the package underlying build
+ // class set and expressions.
+ //
+ bool m (false);
+
+ // Match the configuration against an expression, updating the match
+ // result.
+ //
+ // We will use a comment of the first encountered excluding expression
+ // (changing the result from true to false) or non-including one (leaving
+ // the false result) as an exclusion reason.
+ //
+ auto match = [&cfg, &m, reason, &sanitize, &class_inheritance_map]
+ (const build_class_expr& e)
+ {
+ bool pm (m);
+ e.match (cfg.classes, class_inheritance_map, m);
+
+ if (reason != nullptr)
+ {
+ // Reset the reason which, if saved, makes no sense anymore.
+ //
+ if (m)
+ {
+ reason->clear ();
+ }
+ else if (reason->empty () &&
+ //
+ // Exclusion.
+ //
+ (pm ||
+ //
+ // Non-inclusion. Make sure that the build class expression
+ // is empty or starts with an addition (+...).
+ //
+ e.expr.empty () ||
+ e.expr.front ().operation == '+'))
+ {
+ *reason = sanitize (e.comment);
+ }
+ }
+ };
+
+ // Determine the underlying class set. Note that in the future we can
+ // potentially extend the underlying set with special classes.
+ //
+ const build_class_expr* ucs (
+ !exprs.empty () && !exprs.front ().underlying_classes.empty ()
+ ? &exprs.front ()
+ : nullptr);
+
+ // Note that the combined package build configuration class expression can
+ // be represented as the underlying class set used as a starting set for
+ // the original expressions and a restricting set, simultaneously. For
+ // example, for the expression:
+ //
+ // default legacy : -msvc
+ //
+ // the resulting expression will be:
+ //
+ // +( +default +legacy ) -msvc &( +default +legacy )
+ //
+ // Let's, however, optimize it a bit based on the following facts:
+ //
+ // - If the underlying class set expression (+default +legacy in the above
+ // example) evaluates to false, then the resulting expression also
+ // evaluates to false due to the trailing '&' operation. Thus, we don't
+ // need to evaluate further if that's the case.
+ //
+ // - On the other hand, if the underlying class set expression evaluates
+ // to true, then we don't need to apply the trailing '&' operation as it
+ // cannot affect the result.
+ //
+ const build_class_expr& ucs_expr (
+ ucs != nullptr
+ ? build_class_expr (ucs->underlying_classes, '+', ucs->comment)
+ : default_ucs_expr);
+
+ match (ucs_expr);
+
+ if (m)
+ {
+ for (const build_class_expr& e: exprs)
+ match (e);
+ }
+
+ // Exclude the configuration if it doesn't match the compound expression.
+ //
+ if (!m)
+ return true;
+
+ // Now check if the configuration is excluded/included via the patterns.
+ //
+ // To implement matching of absent name components with wildcard-only
+ // pattern components we are going to convert names to paths (see
+ // dash_components_to_path() for details).
+ //
+ // And if any of the build-{include,exclude} values (which is legal) or
+ // the build configuration name/target (illegal) are invalid paths, then
+ // we assume no match.
+ //
+ if (!constrs.empty ())
+ try
+ {
+ path cn (dash_components_to_path (cfg.name));
+ path tg (dash_components_to_path (cfg.target.string ()));
+
+ for (const build_constraint& c: constrs)
+ {
+ if (path_match (cn,
+ dash_components_to_path (c.config),
+ dir_path () /* start */,
+ path_match_flags::match_absent) &&
+ (!c.target ||
+ path_match (tg,
+ dash_components_to_path (*c.target),
+ dir_path () /* start */,
+ path_match_flags::match_absent)))
+ {
+ if (!c.exclusion)
+ return false;
+
+ if (reason != nullptr)
+ *reason = sanitize (c.comment);
+
+ return true;
+ }
+ }
+ }
+ catch (const invalid_path&) {}
+
+ return false;
+ }
+
+ path
+ dash_components_to_path (const string& pattern)
+ {
+ string r;
+ size_t nstar (0);
+ for (const path_pattern_term& pt: path_pattern_iterator (pattern))
+ {
+ switch (pt.type)
+ {
+ case path_pattern_term_type::star:
+ {
+ // Replace ** with */**/* and skip all the remaining stars that may
+ // follow in this sequence.
+ //
+ if (nstar == 0)
+ r += "*";
+ else if (nstar == 1)
+ r += "/**/*"; // The first star is already copied.
+
+ break;
+ }
+ case path_pattern_term_type::literal:
+ {
+ // Replace '-' with '/' and fall through otherwise.
+ //
+ if (get_literal (pt) == '-')
+ {
+ r += '/';
+ break;
+ }
+ }
+ // Fall through.
+ default:
+ {
+ r.append (pt.begin, pt.end); // Copy the pattern term as is.
+ }
+ }
+
+ nstar = pt.star () ? nstar + 1 : 0;
+ }
+
+ // Append the trailing slash to match the resulting paths as directories.
+ // This is required for the trailing /* we could append to match absent
+ // directory path components (see path_match_flags::match_absent for
+ // details).
+ //
+ // Note that valid dash components may not contain a trailing dash.
+ // Anyway, any extra trailing slashes will be ignored by the path
+ // constructor.
+ //
+ r += '/';
+
+ return path (move (r));
+ }
+}
diff --git a/mod/build-config.hxx b/mod/build-config.hxx
new file mode 100644
index 0000000..d5e44ce
--- /dev/null
+++ b/mod/build-config.hxx
@@ -0,0 +1,45 @@
+// 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 <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.
+ //
+ 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);
+
+ // 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&);
+}
+
+#endif // MOD_BUILD_CONFIG
diff --git a/mod/build.cxx b/mod/build.cxx
index cdbaa60..5b9d8aa 100644
--- a/mod/build.cxx
+++ b/mod/build.cxx
@@ -3,7 +3,7 @@
#include <mod/build.hxx>
-#include <web/mime-url-encoding.hxx>
+#include <web/server/mime-url-encoding.hxx>
#include <mod/utility.hxx>
diff --git a/mod/buildfile b/mod/buildfile
index 9300faf..ca46bc4 100644
--- a/mod/buildfile
+++ b/mod/buildfile
@@ -19,25 +19,38 @@ import libs += libbpkg%lib{bpkg}
import libs += libbbot%lib{bbot}
include ../libbrep/
-include ../web/
-mod{brep}: {hxx ixx txx cxx}{* -options} \
- {hxx ixx cxx}{ options} \
- ../libbrep/lib{brep} ../web/libus{web} $libs
+include ../web/xhtml/
+include ../web/server/
+
+./: mod{brep} {libue libus}{mod}
+
+libu_src = options-types types-parsers build-config
+
+mod{brep}: {hxx ixx txx cxx}{* -module-options -{$libu_src}} \
+ libus{mod} ../libbrep/lib{brep} ../web/server/libus{web-server} \
+ $libs
+
+{libue libus}{mod}: {hxx ixx cxx}{module-options} \
+ {hxx ixx txx cxx}{+{$libu_src} } \
+ $libs
+
+libus{mod}: ../web/xhtml/libus{xhtml}
+libue{mod}: ../web/xhtml/libue{xhtml}
# Generated options parser.
#
if $cli.configured
{
- cli.cxx{options}: cli{options}
+ cli.cxx{module-options}: cli{module}
# Set option prefix to the empty value to handle all unknown request
# parameters uniformly with a single catch block.
#
- cli.options += --std c++11 -I $src_root --include-with-brackets \
---include-prefix mod --guard-prefix MOD --generate-specifier \
---cxx-prologue "#include <mod/types-parsers.hxx>" \
---cli-namespace brep::cli --generate-file-scanner --suppress-usage \
+ cli.options += --std c++11 -I $src_root --include-with-brackets \
+--include-prefix mod --guard-prefix MOD --generate-specifier \
+--cxx-prologue "#include <mod/types-parsers.hxx>" \
+--cli-namespace brep::cli --generate-file-scanner --option-length 38 \
--generate-modifier --generate-description --option-prefix ""
# Include the generated cli files into the distribution and don't remove
diff --git a/mod/database-module.cxx b/mod/database-module.cxx
index 5516730..f598bfd 100644
--- a/mod/database-module.cxx
+++ b/mod/database-module.cxx
@@ -5,8 +5,8 @@
#include <odb/exceptions.hxx>
-#include <mod/options.hxx>
#include <mod/database.hxx>
+#include <mod/module-options.hxx>
namespace brep
{
diff --git a/mod/database-module.hxx b/mod/database-module.hxx
index a41752d..f72ba83 100644
--- a/mod/database-module.hxx
+++ b/mod/database-module.hxx
@@ -10,7 +10,7 @@
#include <libbrep/utility.hxx>
#include <mod/module.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
namespace brep
{
diff --git a/mod/mod-build-configs.cxx b/mod/mod-build-configs.cxx
index 8efc6c9..6731b28 100644
--- a/mod/mod-build-configs.cxx
+++ b/mod/mod-build-configs.cxx
@@ -7,11 +7,12 @@
#include <libstudxml/serializer.hxx>
-#include <web/xhtml.hxx>
-#include <web/module.hxx>
+#include <web/server/module.hxx>
+
+#include <web/xhtml/serialization.hxx>
#include <mod/page.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace bbot;
diff --git a/mod/mod-build-configs.hxx b/mod/mod-build-configs.hxx
index 333680a..562ac6d 100644
--- a/mod/mod-build-configs.hxx
+++ b/mod/mod-build-configs.hxx
@@ -8,7 +8,7 @@
#include <libbrep/utility.hxx>
#include <mod/module.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/build-config-module.hxx>
namespace brep
diff --git a/mod/mod-build-force.cxx b/mod/mod-build-force.cxx
index 4dc71c8..bd172e3 100644
--- a/mod/mod-build-force.cxx
+++ b/mod/mod-build-force.cxx
@@ -8,12 +8,12 @@
#include <odb/database.hxx>
#include <odb/transaction.hxx>
-#include <web/module.hxx>
+#include <web/server/module.hxx>
#include <libbrep/build.hxx>
#include <libbrep/build-odb.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace bbot;
diff --git a/mod/mod-build-force.hxx b/mod/mod-build-force.hxx
index 7b6b3b6..22df383 100644
--- a/mod/mod-build-force.hxx
+++ b/mod/mod-build-force.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
#include <mod/build-config-module.hxx>
diff --git a/mod/mod-build-log.cxx b/mod/mod-build-log.cxx
index 16cc965..3032e52 100644
--- a/mod/mod-build-log.cxx
+++ b/mod/mod-build-log.cxx
@@ -10,12 +10,12 @@
#include <libbutl/timestamp.mxx> // to_stream()
-#include <web/module.hxx>
+#include <web/server/module.hxx>
#include <libbrep/build.hxx>
#include <libbrep/build-odb.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace bbot;
diff --git a/mod/mod-build-log.hxx b/mod/mod-build-log.hxx
index 9f9d1d9..a2f4e48 100644
--- a/mod/mod-build-log.hxx
+++ b/mod/mod-build-log.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
#include <mod/build-config-module.hxx>
diff --git a/mod/mod-build-result.cxx b/mod/mod-build-result.cxx
index b3467d2..734ea5c 100644
--- a/mod/mod-build-result.cxx
+++ b/mod/mod-build-result.cxx
@@ -15,15 +15,15 @@
#include <libbbot/manifest.hxx>
-#include <web/module.hxx>
+#include <web/server/module.hxx>
#include <libbrep/build.hxx>
#include <libbrep/build-odb.hxx>
#include <libbrep/package.hxx>
#include <libbrep/package-odb.hxx>
-#include <mod/build.hxx> // *_url()
-#include <mod/options.hxx>
+#include <mod/build.hxx> // *_url()
+#include <mod/module-options.hxx>
using namespace std;
using namespace butl;
@@ -409,6 +409,7 @@ handle (request& rq, response&)
b->results = move (rqm.result.results);
b->timestamp = system_clock::now ();
+ b->completion_timestamp = b->timestamp;
build_db_->update (b);
diff --git a/mod/mod-build-result.hxx b/mod/mod-build-result.hxx
index b3911e1..71a60f9 100644
--- a/mod/mod-build-result.hxx
+++ b/mod/mod-build-result.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
#include <mod/build-config-module.hxx>
diff --git a/mod/mod-build-task.cxx b/mod/mod-build-task.cxx
index c232815..17bc15e 100644
--- a/mod/mod-build-task.cxx
+++ b/mod/mod-build-task.cxx
@@ -22,14 +22,14 @@
#include <libbbot/manifest.hxx>
#include <libbbot/build-config.hxx>
-#include <web/module.hxx>
+#include <web/server/module.hxx>
#include <libbrep/build.hxx>
#include <libbrep/build-odb.hxx>
#include <libbrep/build-package.hxx>
#include <libbrep/build-package-odb.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace butl;
@@ -384,28 +384,18 @@ handle (request& rq, response& rs)
using prep_bld_query = prepared_query<build>;
package_id id;
- const auto& qv (bld_query::id.package.version);
bld_query bq (
- bld_query::id.package.tenant == bld_query::_ref (id.tenant) &&
-
- bld_query::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) &&
+ equal<build> (bld_query::id.package, id) &&
bld_query::id.configuration.in_range (cfg_names.begin (),
- cfg_names.end ()) &&
+ cfg_names.end ()) &&
- bld_query::id.toolchain_name == tqm.toolchain_name &&
+ bld_query::id.toolchain_name == tqm.toolchain_name &&
compare_version_eq (bld_query::id.toolchain_version,
canonical_version (toolchain_version),
- true /* revision */) &&
+ true /* revision */) &&
(bld_query::state == "built" ||
((bld_query::force == "forcing" &&
diff --git a/mod/mod-build-task.hxx b/mod/mod-build-task.hxx
index 5f4c14a..7875db1 100644
--- a/mod/mod-build-task.hxx
+++ b/mod/mod-build-task.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
#include <mod/build-config-module.hxx>
diff --git a/mod/mod-builds.cxx b/mod/mod-builds.cxx
index 77ebc05..ab9e93e 100644
--- a/mod/mod-builds.cxx
+++ b/mod/mod-builds.cxx
@@ -16,9 +16,10 @@
#include <libbbot/manifest.hxx> // to_result_status(), to_string(result_status)
-#include <web/xhtml.hxx>
-#include <web/module.hxx>
-#include <web/mime-url-encoding.hxx>
+#include <web/server/module.hxx>
+#include <web/server/mime-url-encoding.hxx>
+
+#include <web/xhtml/serialization.hxx>
#include <libbrep/build.hxx>
#include <libbrep/build-odb.hxx>
@@ -26,7 +27,7 @@
#include <libbrep/build-package-odb.hxx>
#include <mod/page.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace butl;
@@ -231,7 +232,10 @@ build_query (const brep::cstrings* configs,
else
{
query sq (qb::status == rs);
- result_status st (to_result_status(rs)); // May throw invalid_argument.
+
+ // May throw invalid_argument.
+ //
+ result_status st (to_result_status (rs));
if (st != result_status::success)
{
@@ -312,22 +316,6 @@ 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.tenant == query::_ref (y.tenant) &&
- 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>"},
{"*", "*"},
@@ -821,9 +809,8 @@ handle (request& rq, response& rs)
const auto& bid (bld_query::build::id);
- bld_query bq (
- package_id_eq<package_build_count> (bid.package, id) &&
- bid.configuration == bld_query::_ref (config) &&
+ bld_query bq (equal<package_build_count> (bid.package, id) &&
+ bid.configuration == bld_query::_ref (config) &&
// Note that the query already constrains configurations via the
// configuration name and the tenant via the build package id.
@@ -936,7 +923,7 @@ handle (request& rq, response& rs)
package_id id;
bld_query bq (
- package_id_eq<package_build> (bld_query::build::id.package, id) &&
+ equal<package_build> (bld_query::build::id.package, id) &&
// Note that the query already constrains the tenant via the build
// package id.
diff --git a/mod/mod-builds.hxx b/mod/mod-builds.hxx
index 714b374..0aa7916 100644
--- a/mod/mod-builds.hxx
+++ b/mod/mod-builds.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
#include <mod/build-config-module.hxx>
diff --git a/mod/mod-ci.cxx b/mod/mod-ci.cxx
index 77377eb..d2da93f 100644
--- a/mod/mod-ci.cxx
+++ b/mod/mod-ci.cxx
@@ -17,11 +17,12 @@
#include <libbpkg/manifest.hxx>
#include <libbpkg/package-name.hxx>
-#include <web/xhtml.hxx>
-#include <web/module.hxx>
+#include <web/server/module.hxx>
+
+#include <web/xhtml/serialization.hxx>
#include <mod/page.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/external-handler.hxx>
using namespace std;
@@ -116,8 +117,8 @@ handle (request& rq, response& rs)
// latter case we will always respond with the same neutral message for
// security reason, logging the error details. Note that descriptions of
// exceptions caught by the web server are returned to the client (see
- // web/module.hxx for details), and we want to avoid this when there is a
- // danger of exposing sensitive data.
+ // web/server/module.hxx for details), and we want to avoid this when there
+ // is a danger of exposing sensitive data.
//
// Also we will pass through exceptions thrown by the underlying API, unless
// we need to handle them or add details for the description, in which case
diff --git a/mod/mod-ci.hxx b/mod/mod-ci.hxx
index 1228714..431f53b 100644
--- a/mod/mod-ci.hxx
+++ b/mod/mod-ci.hxx
@@ -4,13 +4,13 @@
#ifndef MOD_MOD_CI_HXX
#define MOD_MOD_CI_HXX
-#include <web/xhtml-fragment.hxx>
+#include <web/xhtml/fragment.hxx>
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
#include <mod/module.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
namespace brep
{
diff --git a/mod/mod-package-details.cxx b/mod/mod-package-details.cxx
index c7973d3..e0bd1ef 100644
--- a/mod/mod-package-details.cxx
+++ b/mod/mod-package-details.cxx
@@ -9,15 +9,16 @@
#include <odb/database.hxx>
#include <odb/transaction.hxx>
-#include <web/xhtml.hxx>
-#include <web/module.hxx>
-#include <web/mime-url-encoding.hxx>
+#include <web/server/module.hxx>
+#include <web/server/mime-url-encoding.hxx>
+
+#include <web/xhtml/serialization.hxx>
#include <libbrep/package.hxx>
#include <libbrep/package-odb.hxx>
#include <mod/page.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace odb::core;
using namespace brep::cli;
diff --git a/mod/mod-package-details.hxx b/mod/mod-package-details.hxx
index 16f8c3e..e1b0a9c 100644
--- a/mod/mod-package-details.hxx
+++ b/mod/mod-package-details.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
namespace brep
diff --git a/mod/mod-package-version-details.cxx b/mod/mod-package-version-details.cxx
index cde65b0..bfc08b0 100644
--- a/mod/mod-package-version-details.cxx
+++ b/mod/mod-package-version-details.cxx
@@ -9,9 +9,10 @@
#include <odb/database.hxx>
#include <odb/transaction.hxx>
-#include <web/xhtml.hxx>
-#include <web/module.hxx>
-#include <web/mime-url-encoding.hxx>
+#include <web/server/module.hxx>
+#include <web/server/mime-url-encoding.hxx>
+
+#include <web/xhtml/serialization.hxx>
#include <libbrep/build.hxx>
#include <libbrep/build-odb.hxx>
@@ -19,7 +20,7 @@
#include <libbrep/package-odb.hxx>
#include <mod/page.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace butl;
diff --git a/mod/mod-package-version-details.hxx b/mod/mod-package-version-details.hxx
index 8d0d373..a88d6c2 100644
--- a/mod/mod-package-version-details.hxx
+++ b/mod/mod-package-version-details.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
#include <mod/build-config-module.hxx>
diff --git a/mod/mod-packages.cxx b/mod/mod-packages.cxx
index 81cf83c..65c7c5b 100644
--- a/mod/mod-packages.cxx
+++ b/mod/mod-packages.cxx
@@ -10,15 +10,16 @@
#include <odb/transaction.hxx>
#include <odb/schema-catalog.hxx>
-#include <web/xhtml.hxx>
-#include <web/module.hxx>
-#include <web/mime-url-encoding.hxx>
+#include <web/server/module.hxx>
+#include <web/server/mime-url-encoding.hxx>
+
+#include <web/xhtml/serialization.hxx>
#include <libbrep/package.hxx>
#include <libbrep/package-odb.hxx>
#include <mod/page.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace odb::core;
using namespace brep::cli;
diff --git a/mod/mod-packages.hxx b/mod/mod-packages.hxx
index d1c4677..611d63c 100644
--- a/mod/mod-packages.hxx
+++ b/mod/mod-packages.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
namespace brep
diff --git a/mod/mod-repository-details.cxx b/mod/mod-repository-details.cxx
index 988c445..813b738 100644
--- a/mod/mod-repository-details.cxx
+++ b/mod/mod-repository-details.cxx
@@ -12,15 +12,16 @@
#include <libbutl/timestamp.mxx> // to_string()
-#include <web/xhtml.hxx>
-#include <web/module.hxx>
-#include <web/mime-url-encoding.hxx>
+#include <web/server/module.hxx>
+#include <web/server/mime-url-encoding.hxx>
+
+#include <web/xhtml/serialization.hxx>
#include <libbrep/package.hxx>
#include <libbrep/package-odb.hxx>
#include <mod/page.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace odb::core;
diff --git a/mod/mod-repository-details.hxx b/mod/mod-repository-details.hxx
index bd4b3ba..e83831d 100644
--- a/mod/mod-repository-details.hxx
+++ b/mod/mod-repository-details.hxx
@@ -7,7 +7,7 @@
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/database-module.hxx>
namespace brep
diff --git a/mod/mod-repository-root.cxx b/mod/mod-repository-root.cxx
index b6c54b8..02d6c93 100644
--- a/mod/mod-repository-root.cxx
+++ b/mod/mod-repository-root.cxx
@@ -10,10 +10,10 @@
#include <sstream>
#include <algorithm> // find()
-#include <web/module.hxx>
+#include <web/server/module.hxx>
#include <mod/module.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/mod-ci.hxx>
#include <mod/mod-submit.hxx>
diff --git a/mod/mod-repository-root.hxx b/mod/mod-repository-root.hxx
index ac4b254..9e28797 100644
--- a/mod/mod-repository-root.hxx
+++ b/mod/mod-repository-root.hxx
@@ -8,7 +8,7 @@
#include <libbrep/utility.hxx>
#include <mod/module.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
namespace brep
{
diff --git a/mod/mod-submit.cxx b/mod/mod-submit.cxx
index 0dea2b7..9c93a36 100644
--- a/mod/mod-submit.cxx
+++ b/mod/mod-submit.cxx
@@ -14,11 +14,12 @@
#include <libbutl/manifest-types.mxx>
#include <libbutl/manifest-serializer.mxx>
-#include <web/xhtml.hxx>
-#include <web/module.hxx>
+#include <web/server/module.hxx>
+
+#include <web/xhtml/serialization.hxx>
#include <mod/page.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
#include <mod/external-handler.hxx>
using namespace std;
diff --git a/mod/mod-submit.hxx b/mod/mod-submit.hxx
index 96a60f9..fc5f8d4 100644
--- a/mod/mod-submit.hxx
+++ b/mod/mod-submit.hxx
@@ -4,13 +4,13 @@
#ifndef MOD_MOD_SUBMIT_HXX
#define MOD_MOD_SUBMIT_HXX
-#include <web/xhtml-fragment.hxx>
+#include <web/xhtml/fragment.hxx>
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
#include <mod/module.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
namespace brep
{
diff --git a/mod/options.cli b/mod/module.cli
index f02d7a6..fa1d2cc 100644
--- a/mod/options.cli
+++ b/mod/module.cli
@@ -3,7 +3,7 @@
include <libbpkg/manifest.hxx>; // repository_location
-include <web/xhtml-fragment.hxx>;
+include <web/xhtml/fragment.hxx>;
include <libbrep/types.hxx>;
@@ -37,7 +37,7 @@ namespace brep
dir_path root = "/"
{
- "<path>"
+ "<path>",
"Repository root. That is, this is the part of the URL between the
host name and the start of the repository. For example, root value
'\cb{/pkg}' means the repository URL is \cb{http://example.org/pkg/}.
@@ -214,7 +214,7 @@ namespace brep
{
"<user>",
"Build database execution user name. If not empty then the login
- user will be switched (with \c{SET ROLE}) to this user prior to
+ user will be switched (with \c{SET ROLE}) to this user prior to
executing any statements. If not specified, then \cb{brep} is used."
}
@@ -565,7 +565,7 @@ namespace brep
string root-tenant-view = "packages"
{
- "<service>"
+ "<service>",
"The default view to display for the tenant repository root. The
<service> argument is one of the supported services (\c{packages},
\c{builds}, \c{submit}, \c{ci}, etc). The default service is
diff --git a/mod/module.cxx b/mod/module.cxx
index 8f306fd..06799d7 100644
--- a/mod/module.cxx
+++ b/mod/module.cxx
@@ -10,10 +10,10 @@
#include <cstring> // strchr()
#include <functional> // bind()
-#include <web/module.hxx>
-#include <web/apache/log.hxx>
+#include <web/server/module.hxx>
+#include <web/server/apache/log.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace placeholders; // For std::bind's _1, etc.
diff --git a/mod/module.hxx b/mod/module.hxx
index 2c62166..b3ed67b 100644
--- a/mod/module.hxx
+++ b/mod/module.hxx
@@ -4,14 +4,14 @@
#ifndef MOD_MODULE_HXX
#define MOD_MODULE_HXX
-#include <web/module.hxx>
+#include <web/server/module.hxx>
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
#include <mod/utility.hxx>
-#include <mod/options.hxx>
#include <mod/diagnostics.hxx>
+#include <mod/module-options.hxx>
namespace brep
{
diff --git a/mod/page.cxx b/mod/page.cxx
index 64e31c0..c7dc403 100644
--- a/mod/page.cxx
+++ b/mod/page.cxx
@@ -16,9 +16,10 @@
#include <libbutl/url.mxx>
-#include <web/xhtml.hxx>
-#include <web/xhtml-fragment.hxx>
-#include <web/mime-url-encoding.hxx>
+#include <web/xhtml/fragment.hxx>
+#include <web/xhtml/serialization.hxx>
+
+#include <web/server/mime-url-encoding.hxx>
#include <libbrep/package.hxx>
#include <libbrep/package-odb.hxx>
diff --git a/mod/page.hxx b/mod/page.hxx
index 8c92d10..49d8608 100644
--- a/mod/page.hxx
+++ b/mod/page.hxx
@@ -8,7 +8,7 @@
#include <libbbot/manifest.hxx>
-#include <web/xhtml-fragment.hxx>
+#include <web/xhtml/fragment.hxx>
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
diff --git a/mod/services.cxx b/mod/services.cxx
index 7739011..b17e32e 100644
--- a/mod/services.cxx
+++ b/mod/services.cxx
@@ -3,7 +3,7 @@
#include <ap_config.h> // AP_MODULE_DECLARE_DATA
-#include <web/apache/service.hxx>
+#include <web/server/apache/service.hxx>
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
diff --git a/mod/types-parsers.cxx b/mod/types-parsers.cxx
index 70d77dd..ceaab29 100644
--- a/mod/types-parsers.cxx
+++ b/mod/types-parsers.cxx
@@ -3,7 +3,7 @@
#include <mod/types-parsers.hxx>
-#include <mod/options.hxx>
+#include <mod/module-options.hxx>
using namespace std;
using namespace bpkg;
diff --git a/mod/types-parsers.hxx b/mod/types-parsers.hxx
index a81ef90..091c868 100644
--- a/mod/types-parsers.hxx
+++ b/mod/types-parsers.hxx
@@ -9,7 +9,7 @@
#include <libbpkg/manifest.hxx> // repository_location
-#include <web/xhtml-fragment.hxx>
+#include <web/xhtml/fragment.hxx>
#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>
diff --git a/monitor/.gitignore b/monitor/.gitignore
new file mode 100644
index 0000000..21c0e0b
--- /dev/null
+++ b/monitor/.gitignore
@@ -0,0 +1,2 @@
+*-options.?xx
+brep-monitor
diff --git a/monitor/buildfile b/monitor/buildfile
new file mode 100644
index 0000000..dc49a98
--- /dev/null
+++ b/monitor/buildfile
@@ -0,0 +1,45 @@
+# file : monitor/buildfile
+# license : MIT; see accompanying LICENSE file
+
+import libs = libodb%lib{odb}
+import libs += libodb-pgsql%lib{odb-pgsql}
+import libs += libbutl%lib{butl}
+import libs += libbbot%lib{bbot}
+
+include ../libbrep/
+include ../mod/
+
+exe{brep-monitor}: {hxx ixx cxx}{* -*-options} \
+ {hxx ixx cxx}{monitor-options module-options} \
+ ../mod/libue{mod} ../libbrep/lib{brep} $libs
+
+# Build options.
+#
+obj{monitor}: cxx.poptions += -DBREP_COPYRIGHT=\"$copyright\"
+
+# Generated options parser.
+#
+if $cli.configured
+{
+ cli.cxx{monitor-options}: cli{monitor}
+ cli.cxx{module-options}: cli{module}
+
+ cli.options += --std c++11 -I $src_root --include-with-brackets \
+--include-prefix monitor --guard-prefix MONITOR --generate-specifier \
+--cli-namespace brep::cli
+
+ cli.cxx{monitor-options}: cli.options += \
+--page-usage print_ --ansi-color --long-usage
+
+ cli.cxx{module-options}: cli.options += --suppress-usage --generate-parse
+
+ # Include the generated cli files into the distribution and don't remove
+ # them when cleaning in src (so that clean results in a state identical to
+ # distributed).
+ #
+ cli.cxx{*}:
+ {
+ dist = true
+ clean = ($src_root != $out_root)
+ }
+}
diff --git a/monitor/module.cli b/monitor/module.cli
new file mode 100644
index 0000000..c299c5f
--- /dev/null
+++ b/monitor/module.cli
@@ -0,0 +1,16 @@
+// file : monitor/module.cli
+// license : MIT; see accompanying LICENSE file
+
+include <mod/module.cli>;
+
+namespace brep
+{
+ namespace options
+ {
+ // brep web module configuration options we are interested in.
+ //
+ class module: build_task
+ {
+ };
+ }
+}
diff --git a/monitor/monitor.cli b/monitor/monitor.cli
new file mode 100644
index 0000000..33b05f7
--- /dev/null
+++ b/monitor/monitor.cli
@@ -0,0 +1,176 @@
+// file : monitor/monitor.cli
+// license : MIT; see accompanying LICENSE file
+
+include <vector>;
+include <string>;
+include <cstddef>; // size_t
+include <cstdint>; // uint16_t
+
+include <mod/module.cli>; // Reuse CLI support types.
+
+"\section=1"
+"\name=brep-monitor"
+"\summary=monitor brep infrastructure"
+
+namespace brep
+{
+ namespace options
+ {
+ {
+ "<options> <brep-config> <toolchain> <name> <version>",
+
+ "\h|SYNOPSIS|
+
+ \c{\b{brep-monitor --help}\n
+ \b{brep-monitor --version}\n
+ \b{brep-monitor} [<options>] <brep-config> <toolchain> [<toolchain>...]}
+
+ \c{<toolchain> = <name>[\b{/}<version>]}
+
+ \h|DESCRIPTION|
+
+ \cb{brep-monitor} analyzes the \cb{brep} internal state and reports the
+ infrastructure issues printing their descriptions to \cb{stderr}.
+
+ The specified \cb{brep} configuration file (<brep-config>) is used to
+ retrieve information required to access the databases and deduce the
+ expected behavior. Most of this information can be overridden via the
+ command line options.
+
+ Currently, only delayed package builds for the specified toolchains are
+ reported. If toolchain version is omitted then all package builds with
+ this toolchain name are considered.
+
+ \cb{brep-monitor} maintains its own state in the brep \cb{build}
+ database. In particular, it records timestamps of the reported package
+ build delays and optionally omits them from being reported again during
+ the timeout specified with the \cb{--report-timeout} option.
+
+ By default, a brief report is printed. Use the \cb{--full-report}
+ option to obtain the full report (which may be large).
+
+ Note that \cb{brep-monitor} expects the \cb{build} database schema to
+ have already been created using \l{brep-migrate(1)}."
+ }
+
+ class monitor
+ {
+ "\h|OPTIONS|"
+
+ std::size_t --build-timeout
+ {
+ "<seconds>",
+ "Time to wait (in seconds) before considering a package build as
+ delayed. If unspecified, the sum of \cb{brep}'s
+ \cb{build-normal-rebuild-timeout} and \cb{build-result-timeout}
+ configuration option values is used. Note also that an archived
+ package that is unbuilt is always considered delayed."
+ }
+
+ std::size_t --report-timeout
+ {
+ "<seconds>",
+ "Time to wait (in seconds) before repeating a report of a package
+ build delay. By default there is no delay and all reports are
+ repeated."
+ }
+
+ bool --full-report
+ {
+ "Print the list of delayed package builds rather than just their number
+ per build configuration."
+ }
+
+ bool --clean
+ {
+ "Additionally clean the monitor state removing outdated information
+ related to non-existent packages, configurations, etc."
+ }
+
+ // Note that the web service would normally logs in under a different
+ // user (and potentially switch the role afterwords) and so falling back
+ // to brep's user name and password wouldn't make much sense.
+ //
+ std::string --build-db-user|-u
+ {
+ "<user>",
+ "\cb{build} database user name. If unspecified, then operating system
+ (login) name is used."
+ }
+
+ std::string --build-db-password
+ {
+ "<pass>",
+ "\cb{build} database password. If unspecified, then login without
+ password is expected to work."
+ }
+
+ std::string --build-db-name|-n = "brep_package"
+ {
+ "<name>",
+ "\cb{build} database name. If unspecified, then \cb{brep}'s
+ \cb{build-db-name} configuration option value is used."
+ }
+
+ std::string --build-db-host|-h
+ {
+ "<host>",
+ "\cb{build} database host name, address, or socket. If unspecified,
+ then \cb{brep}'s \cb{build-db-host} configuration option value is
+ used."
+ }
+
+ std::uint16_t --build-db-port|-p
+ {
+ "<port>",
+ "\cb{build} database port number. If unspecified, then \cb{brep}'s
+ \cb{build-db-port} configuration option value is used."
+ }
+
+ std::string --pager // String to allow empty value.
+ {
+ "<path>",
+ "The pager program to be used to show long text. Commonly used pager
+ programs are \cb{less} and \cb{more}. You can also specify additional
+ options that should be passed to the pager program with
+ \cb{--pager-option}. If an empty string is specified as the pager
+ program, then no pager will be used. If the pager program is not
+ explicitly specified, then \cb{brep-monitor} will try to use
+ \cb{less}. If it is not available, then no pager will be used."
+ }
+
+ std::vector<std::string> --pager-option
+ {
+ "<opt>",
+ "Additional option to be passed to the pager program. See \cb{--pager}
+ for more information on the pager program. Repeat this option to
+ specify multiple pager options."
+ }
+
+ bool --help {"Print usage information and exit."}
+ bool --version {"Print version and exit."}
+ };
+
+ "\h|EXIT STATUS|
+
+ \dl|
+
+ \li|\cb{0}
+
+ Success.|
+
+ \li|\cb{1}
+
+ Fatal error.|
+
+ \li|\cb{2}
+
+ An instance of \cb{brep-monitor} or some other \cb{brep} utility is
+ already running. Try again.|
+
+ \li|\cb{3}
+
+ Recoverable database error. Try again.||
+ "
+ }
+}
diff --git a/monitor/monitor.cxx b/monitor/monitor.cxx
new file mode 100644
index 0000000..e04c5e1
--- /dev/null
+++ b/monitor/monitor.cxx
@@ -0,0 +1,766 @@
+// file : monitor/monitor.cxx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#include <map>
+#include <set>
+#include <chrono>
+#include <iostream>
+#include <algorithm> // find_if()
+
+#include <odb/database.hxx>
+#include <odb/transaction.hxx>
+#include <odb/schema-catalog.hxx>
+
+#include <odb/pgsql/database.hxx>
+
+#include <libbutl/pager.mxx>
+#include <libbutl/utility.mxx> // compare_c_string
+
+#include <libbbot/build-config.hxx>
+
+#include <libbrep/build.hxx>
+#include <libbrep/common.hxx>
+#include <libbrep/build-odb.hxx>
+#include <libbrep/build-package.hxx>
+#include <libbrep/build-package-odb.hxx>
+#include <libbrep/database-lock.hxx>
+
+#include <mod/build-config.hxx>
+
+#include <monitor/module-options.hxx>
+#include <monitor/monitor-options.hxx>
+
+using namespace std;
+using namespace butl;
+using namespace bbot;
+using namespace odb::core;
+
+namespace brep
+{
+ // Operation failed, diagnostics has already been issued.
+ //
+ struct failed {};
+
+ static const char* help_info (
+ " info: run 'brep-monitor --help' for more information");
+
+ static int
+ main (int argc, char* argv[])
+ try
+ {
+ cli::argv_scanner scan (argc, argv);
+ options::monitor ops (scan);
+
+ // Version.
+ //
+ if (ops.version ())
+ {
+ cout << "brep-monitor " << BREP_VERSION_ID << endl
+ << "libbrep " << LIBBREP_VERSION_ID << endl
+ << "libbbot " << LIBBBOT_VERSION_ID << endl
+ << "libbpkg " << LIBBPKG_VERSION_ID << endl
+ << "libbutl " << LIBBUTL_VERSION_ID << endl
+ << "Copyright (c) " << BREP_COPYRIGHT << "." << endl
+ << "This is free software released under the MIT license." << endl;
+
+ return 0;
+ }
+
+ // Help.
+ //
+ if (ops.help ())
+ {
+ pager p ("brep-monitor help",
+ false,
+ ops.pager_specified () ? &ops.pager () : nullptr,
+ &ops.pager_option ());
+
+ print_usage (p.stream ());
+
+ // If the pager failed, assume it has issued some diagnostics.
+ //
+ return p.wait () ? 0 : 1;
+ }
+
+ // Parse the brep module configuration.
+ //
+ options::module mod_ops;
+ {
+ if (!scan.more ())
+ {
+ cerr << "error: brep module configuration file is expected" << endl
+ << help_info << endl;
+ return 1;
+ }
+
+ string f (scan.next ());
+
+ try
+ {
+ cli::argv_file_scanner scan (f, "" /* option */);
+
+ // Parse the brep module options skipping those we don't recognize.
+ //
+ while (scan.more ())
+ {
+ // Parse until an unknown option is encountered.
+ //
+ mod_ops.parse (scan,
+ cli::unknown_mode::stop,
+ cli::unknown_mode::stop);
+
+ // Skip the unknown option, unless we are done.
+ //
+ if (scan.more ())
+ {
+ // Skip the option name.
+ //
+ size_t l (scan.peek_line ());
+ scan.skip ();
+
+ // Skip the option value, if present.
+ //
+ // Note that here we rely on the configuration file having both
+ // the option name and its value on the same line.
+ //
+ if (scan.more () && scan.peek_line () == l)
+ scan.skip ();
+ }
+ }
+ }
+ catch (const cli::file_io_failure& e)
+ {
+ cerr << "error: unable to parse brep module configuration: " << e
+ << endl;
+ return 1;
+ }
+ catch (const cli::exception& e)
+ {
+ cerr << "error: unable to parse brep module configuration file '" << f
+ << "': " << e << endl;
+ return 1;
+ }
+ }
+
+ if (!mod_ops.build_config_specified ())
+ {
+ cerr << "warning: package building functionality is disabled" << endl;
+ return 0;
+ }
+
+ // Parse the toolchains suppressing duplicates.
+ //
+ // Note that specifying a toolchain both with and without version doesn't
+ // make sense, so we fail if that's the case.
+ //
+ vector<pair<string, version>> toolchains;
+
+ if (!scan.more ())
+ {
+ cerr << "error: toolchain is expected" << endl << help_info << endl;
+ return 1;
+ }
+
+ while (scan.more ())
+ {
+ string s (scan.next ());
+
+ string tn;
+ version tv;
+
+ try
+ {
+ size_t p (s.find ('/'));
+
+ if (p == string::npos)
+ tn = move (s);
+ else
+ {
+ tn.assign (s, 0, p);
+ tv = version (string (s, p + 1));
+ }
+
+ bool dup (false);
+ for (const pair<string, version>& t: toolchains)
+ {
+ if (tn == t.first)
+ {
+ if (tv == t.second)
+ {
+ dup = true;
+ break;
+ }
+
+ if (tv.empty () != t.second.empty ())
+ {
+ cerr << "error: toolchain '" << tn << "' is specified both "
+ << "with and without version" << endl;
+ return 1;
+ }
+ }
+ }
+
+ if (!dup)
+ toolchains.emplace_back (move (tn), move (tv));
+ }
+ catch (const invalid_argument& e)
+ {
+ cerr << "error: invalid toolchain '" << s << "': " << e << endl;
+ return 1;
+ }
+ }
+
+ // Parse buildtab.
+ //
+ build_configs configs;
+
+ try
+ {
+ configs = parse_buildtab (mod_ops.build_config ());
+ }
+ catch (const tab_parsing& e)
+ {
+ cerr << "error: unable to parse buildtab: " << e << endl;
+ return 1;
+ }
+ catch (const io_error& e)
+ {
+ cerr << "error: unable to read '" << mod_ops.build_config () << "': "
+ << e << endl;
+ return 1;
+ }
+
+ // Create the database instance.
+ //
+ odb::pgsql::database db (
+ ops.build_db_user (),
+ ops.build_db_password (),
+ (ops.build_db_name_specified ()
+ ? ops.build_db_name ()
+ : mod_ops.build_db_name ()),
+ (ops.build_db_host_specified ()
+ ? ops.build_db_host ()
+ : mod_ops.build_db_host ()),
+ (ops.build_db_port_specified ()
+ ? ops.build_db_port ()
+ : mod_ops.build_db_port ()),
+ "options='-c default_transaction_isolation=serializable'");
+
+ // Prevent several brep utility instances from updating the build database
+ // simultaneously.
+ //
+ database_lock l (db);
+
+ // Check that the database schema matches the current one.
+ //
+ const string ds ("build");
+ if (schema_catalog::current_version (db, ds) != db.schema_version (ds))
+ {
+ cerr << "error: build database schema differs from the current one"
+ << endl
+ << " info: use brep-migrate to migrate the database" << endl;
+ return 1;
+ }
+
+ // If requested, cleanup delays for package builds that are not expected
+ // anymore (build configuration is not present, etc).
+ //
+ if (ops.clean ())
+ {
+ using config_map = map<const char*,
+ const build_config*,
+ compare_c_string>;
+
+ config_map conf_map;
+ for (const build_config& c: configs)
+ conf_map[c.name.c_str ()] = &c;
+
+ // Prepare the build delay prepared query.
+ //
+ // Query package build delays in chunks in order not to hold locks for
+ // too long. Sort the result by package version as a first priority to
+ // minimize number of queries to the package database. Note that we
+ // still need to sort by configuration and toolchain to make sure that
+ // build delays are sorted consistently across queries and we don't miss
+ // any of them.
+ //
+ using query = query<build_delay>;
+ using prep_query = prepared_query<build_delay>;
+
+ // Specify the portion.
+ //
+ size_t offset (0);
+
+ query q ("ORDER BY" +
+ query::id.package.tenant + "," +
+ query::id.package.name +
+ order_by_version (query::id.package.version,
+ false /* first */) + "," +
+ query::id.configuration + "," +
+ query::id.toolchain_name +
+ order_by_version (query::id.toolchain_version,
+ false /* first */) +
+ "OFFSET" + query::_ref (offset) + "LIMIT 100");
+
+ connection_ptr conn (db.connection ());
+
+ prep_query pq (
+ conn->prepare_query<build_delay> ("build-delay-query", q));
+
+ // Cache the delayed build package object to reuse it in case the next
+ // delay refers to the same package (which is often the case due to the
+ // query result sorting criteria we use).
+ //
+ package_id pid;
+ shared_ptr<build_package> p;
+
+ for (bool ne (true); ne; )
+ {
+ transaction t (conn->begin ());
+
+ // Query delays.
+ //
+ auto delays (pq.execute ());
+
+ if ((ne = !delays.empty ()))
+ {
+ // Iterate over the build delays and cleanup the outdated ones.
+ //
+ for (const build_delay& d: delays)
+ {
+ config_map::const_iterator ci;
+
+ bool cleanup (
+ // Check that the toolchain is still used.
+ //
+ find_if (toolchains.begin (), toolchains.end (),
+ [&d] (const pair<string, version>& t)
+ {
+ return t.first == d.toolchain_name &&
+ t.second == d.toolchain_version;
+ }) == toolchains.end () ||
+ //
+ // Check that the build configuration is still present.
+ //
+ (ci = conf_map.find (d.configuration.c_str ())) ==
+ conf_map.end ());
+
+ // Check that the package still present, is buildable and doesn't
+ // exclude the build configuration.
+ //
+ if (!cleanup)
+ {
+ if (d.id.package != pid)
+ {
+ pid = d.id.package;
+ p = db.find<build_package> (pid);
+ }
+
+ cleanup = (p == nullptr ||
+ !p->buildable ||
+ exclude (p->builds,
+ p->constraints,
+ *ci->second,
+ configs.class_inheritance_map));
+ }
+
+ if (cleanup)
+ db.erase (d);
+ else
+ ++offset;
+ }
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Collect and report delays as separate steps not to hold database locks
+ // while printing to stderr. Also we need to properly order delays for
+ // printing.
+ //
+ // Iterate through all possible package builds creating the list of delays
+ // with the following sort priority:
+ //
+ // 1: toolchain name
+ // 2: toolchain version (descending)
+ // 3: configuration name
+ // 4: tenant
+ // 5: package name
+ // 6: package version (descending)
+ //
+ // Such ordering will allow us to group build delays by toolchain and
+ // configuration while printing the report.
+ //
+ struct compare_delay
+ {
+ bool
+ operator() (const shared_ptr<const build_delay>& x,
+ const shared_ptr<const build_delay>& y) const
+ {
+ if (int r = x->toolchain_name.compare (y->toolchain_name))
+ return r < 0;
+
+ if (int r = x->toolchain_version.compare (y->toolchain_version))
+ return r > 0;
+
+ if (int r = x->configuration.compare (y->configuration))
+ return r < 0;
+
+ if (int r = x->tenant.compare (y->tenant))
+ return r < 0;
+
+ if (int r = x->package_name.compare (y->package_name))
+ return r < 0;
+
+ return x->package_version.compare (y->package_version) > 0;
+ }
+ };
+
+ set<shared_ptr<const build_delay>, compare_delay> delays;
+ {
+ connection_ptr conn (db.connection ());
+
+ // Prepare the buildable package prepared query.
+ //
+ // Query buildable packages in chunks in order not to hold locks for
+ // too long.
+ //
+ using pquery = query<buildable_package>;
+ using prep_pquery = prepared_query<buildable_package>;
+
+ // Specify the portion.
+ //
+ size_t offset (0);
+
+ pquery pq ("ORDER BY" +
+ pquery::build_package::id.tenant + "," +
+ pquery::build_package::id.name +
+ order_by_version (pquery::build_package::id.version,
+ false /* first */) +
+ "OFFSET" + pquery::_ref (offset) + "LIMIT 50");
+
+ prep_pquery ppq (
+ conn->prepare_query<buildable_package> ("buildable-package-query",
+ pq));
+
+ // Prepare the package build prepared query.
+ //
+ // This query will only be used for toolchains that have no version
+ // specified on the command line to obtain the latest build across all
+ // toolchain versions.
+ //
+ using bquery = query<package_build>;
+ using prep_bquery = prepared_query<package_build>;
+
+ build_id id;
+ const auto& bid (bquery::build::id);
+
+ bquery bq ((equal<package_build> (bid.package, id.package) &&
+ bid.configuration == bquery::_ref (id.configuration) &&
+ bid.toolchain_name == bquery::_ref (id.toolchain_name)) +
+ "ORDER BY" + bquery::build::timestamp + "DESC" + "LIMIT 1");
+
+ prep_bquery pbq (
+ conn->prepare_query<package_build> ("package-build-query", bq));
+
+ timestamp::duration build_timeout (
+ ops.build_timeout_specified ()
+ ? chrono::seconds (ops.build_timeout ())
+ : chrono::seconds (mod_ops.build_normal_rebuild_timeout () +
+ mod_ops.build_result_timeout ()));
+
+ timestamp now (system_clock::now ());
+
+ timestamp build_expiration (now - build_timeout);
+
+ timestamp report_expiration (
+ now - chrono::seconds (ops.report_timeout ()));
+
+ for (bool ne (true); ne; )
+ {
+ transaction t (conn->begin ());
+
+ // Query buildable packages (and cache the result).
+ //
+ auto bps (ppq.execute ());
+
+ if ((ne = !bps.empty ()))
+ {
+ offset += bps.size ();
+
+ for (auto& bp: bps)
+ {
+ shared_ptr<build_package> p (db.load<build_package> (bp.id));
+
+ for (const build_config& c: configs)
+ {
+ if (exclude (p->builds,
+ p->constraints,
+ c,
+ configs.class_inheritance_map))
+ continue;
+
+ for (const pair<string, version>& t: toolchains)
+ {
+ id = build_id (p->id, c.name, t.first, t.second);
+
+ // If the toolchain version is not specified then search for
+ // the latest build across all toolchain versions and search
+ // for a specific build otherwise.
+ //
+ shared_ptr<build> b;
+
+ if (id.toolchain_version.empty ())
+ {
+ auto pbs (pbq.execute ());
+
+ if (!pbs.empty ())
+ b = move (pbs.begin ()->build);
+ }
+ else
+ b = db.find<build> (id);
+
+ // Note that we consider a build as delayed if it is not
+ // completed in the expected timeframe. So even if the build
+ // task have been issued recently we may still consider the
+ // build as delayed.
+ //
+ timestamp bct (b != nullptr
+ ? b->completion_timestamp
+ : timestamp_nonexistent);
+
+ // Create the delay object to record a timestamp when the
+ // package build could have potentially been started, unless
+ // it already exists.
+ //
+ shared_ptr<build_delay> d (db.find<build_delay> (id));
+
+ if (d == nullptr)
+ {
+ // If the archived package has no build nor build delay
+ // for this configuration, then we assume that the
+ // configuration was added after the package tenant has
+ // been archived and so the package could have never been
+ // built for this configuration. Thus, we don't consider
+ // this build as delayed and so skip it.
+ //
+ if (bp.archived && b == nullptr)
+ continue;
+
+ // Use the build completion or build status change
+ // timestamp, whichever is earlier, as the build delay
+ // tracking starting point and fallback to the current time
+ // if there is no build yet.
+ //
+ timestamp pts (
+ b == nullptr ? now :
+ bct != timestamp_nonexistent && bct < b->timestamp ? bct :
+ b->timestamp);
+
+ d = make_shared<build_delay> (move (id.package.tenant),
+ move (id.package.name),
+ p->version,
+ move (id.configuration),
+ move (id.toolchain_name),
+ t.second,
+ pts);
+ db.persist (d);
+ }
+
+ // Handle package builds differently based on their tenant's
+ // archive status.
+ //
+ // If the package is not archived then consider it as delayed
+ // if it is not (re-)built by the expiration time. Otherwise,
+ // consider it as delayed if it is unbuilt.
+ //
+ bool delayed;
+
+ if (!bp.archived)
+ {
+ timestamp bts (bct != timestamp_nonexistent
+ ? bct
+ : d->package_timestamp);
+
+ delayed = (bts <= build_expiration);
+ }
+ else
+ delayed = (bct == timestamp_nonexistent);
+
+ // If the report timeout is not specified then report the
+ // delay unconditionally. Otherwise, report the active package
+ // build delay if the report timeout is expired and the
+ // archived package build delay if it was never reported. Note
+ // that fixing the building infrastructure won't help building
+ // an archived package, so reporting its build delays
+ // repeatedly is meaningless.
+ //
+ if (delayed &&
+ (!ops.report_timeout_specified () ||
+ (!bp.archived
+ ? d->report_timestamp <= report_expiration
+ : d->report_timestamp == timestamp_nonexistent)))
+ {
+ // Note that we update the delay objects persistent state
+ // later, after we successfully print the report.
+ //
+ d->report_timestamp = now;
+ delays.insert (move (d));
+ }
+ }
+ }
+ }
+ }
+
+ t.commit ();
+ }
+ }
+
+ // Report package build delays, if any.
+ //
+ if (!delays.empty ())
+ try
+ {
+ // Print the report.
+ //
+ cerr.exceptions (ostream::badbit | ostream::failbit);
+
+ cerr << "Package build delays (" << delays.size () << "):" << endl;
+
+ // Group the printed delays by toolchain and configuration.
+ //
+ const string* toolchain_name (nullptr);
+ const version* toolchain_version (nullptr);
+ const string* configuration (nullptr);
+
+ // Print the delayed package build number per configuration rather than
+ // the packages themselves in the brief report mode.
+ //
+ size_t config_build_count (0);
+
+ auto brief_config = [&configuration, &config_build_count] ()
+ {
+ if (configuration != nullptr)
+ {
+ cerr << " " << *configuration << " (" << config_build_count
+ << ")" << endl;
+
+ config_build_count = 0;
+ }
+ };
+
+ for (shared_ptr<const build_delay> d: delays)
+ {
+ // Print the toolchain, if changed.
+ //
+ if (toolchain_name == nullptr ||
+ d->toolchain_name != *toolchain_name ||
+ d->toolchain_version != *toolchain_version)
+ {
+ if (!ops.full_report ())
+ brief_config ();
+
+ if (toolchain_name != nullptr)
+ cerr << endl;
+
+ cerr << " " << d->toolchain_name;
+
+ if (!d->toolchain_version.empty ())
+ cerr << "/" << d->toolchain_version;
+
+ cerr << endl;
+
+ toolchain_name = &d->toolchain_name;
+ toolchain_version = &d->toolchain_version;
+ configuration = nullptr;
+ }
+
+ // Print the configuration, if changed.
+ //
+ if (configuration == nullptr || d->configuration != *configuration)
+ {
+ if (ops.full_report ())
+ {
+ if (configuration != nullptr)
+ cerr << endl;
+
+ cerr << " " << d->configuration << endl;
+ }
+ else
+ brief_config ();
+
+ configuration = &d->configuration;
+ }
+
+ // Print the delayed build package in the full report mode and count
+ // configuration builds otherwise.
+ //
+ if (ops.full_report ())
+ {
+ // We can potentially extend this information with the archived flag
+ // or the delay duration.
+ //
+ cerr << " " << d->package_name << "/" << d->package_version;
+
+ if (!d->tenant.empty ())
+ cerr << " " << d->tenant;
+
+ cerr << endl;
+ }
+ else
+ ++config_build_count;
+ }
+
+ if (!ops.full_report ())
+ brief_config ();
+
+ // Persist the delay report timestamps.
+ //
+ transaction t (db.begin ());
+
+ for (shared_ptr<const build_delay> d: delays)
+ db.update (d);
+
+ t.commit ();
+ }
+ catch (const io_error&)
+ {
+ return 1; // Not much we can do on stderr writing failure.
+ }
+
+ return 0;
+ }
+ catch (const database_locked&)
+ {
+ cerr << "brep-monitor or some other brep utility is running" << endl;
+ return 2;
+ }
+ catch (const recoverable& e)
+ {
+ cerr << "recoverable database error: " << e << endl;
+ return 3;
+ }
+ catch (const cli::exception& e)
+ {
+ cerr << "error: " << e << endl << help_info << endl;
+ return 1;
+ }
+ catch (const failed&)
+ {
+ return 1; // Diagnostics has already been issued.
+ }
+ // Fully qualified to avoid ambiguity with odb exception.
+ //
+ catch (const std::exception& e)
+ {
+ cerr << "error: " << e << endl;
+ return 1;
+ }
+}
+
+int
+main (int argc, char* argv[])
+{
+ return brep::main (argc, argv);
+}
diff --git a/tests/web/xhtml/buildfile b/tests/web/xhtml/buildfile
index 6ddd5ae..ff683b9 100644
--- a/tests/web/xhtml/buildfile
+++ b/tests/web/xhtml/buildfile
@@ -1,7 +1,7 @@
# file : tests/web/xhtml/buildfile
# license : MIT; see accompanying LICENSE file
-include ../../../web/
+include ../../../web/xhtml/
-exe{driver}: {hxx cxx}{*} ../../../web/libus{web}
+exe{driver}: {hxx cxx}{*} ../../../web/xhtml/libue{xhtml}
exe{driver}: file{test.out}: test.stdout = true
diff --git a/tests/web/xhtml/driver.cxx b/tests/web/xhtml/driver.cxx
index ff554e4..a0135de 100644
--- a/tests/web/xhtml/driver.cxx
+++ b/tests/web/xhtml/driver.cxx
@@ -6,7 +6,7 @@
#include <libstudxml/serializer.hxx>
-#include <web/xhtml.hxx>
+#include <web/xhtml/serialization.hxx>
using namespace std;
using namespace xml;
diff --git a/web/apache/log.hxx b/web/server/apache/log.hxx
index 6609190..f7738ef 100644
--- a/web/apache/log.hxx
+++ b/web/server/apache/log.hxx
@@ -1,8 +1,8 @@
-// file : web/apache/log.hxx -*- C++ -*-
+// file : web/server/apache/log.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef WEB_APACHE_LOG_HXX
-#define WEB_APACHE_LOG_HXX
+#ifndef WEB_SERVER_APACHE_LOG_HXX
+#define WEB_SERVER_APACHE_LOG_HXX
#include <httpd.h> // request_rec, server_rec
#include <http_log.h>
@@ -11,7 +11,7 @@
#include <cstdint> // uint64_t
#include <algorithm> // min()
-#include <web/module.hxx>
+#include <web/server/module.hxx>
namespace web
{
@@ -77,4 +77,4 @@ namespace web
}
}
-#endif // WEB_APACHE_LOG_HXX
+#endif // WEB_SERVER_APACHE_LOG_HXX
diff --git a/web/apache/request.cxx b/web/server/apache/request.cxx
index 4722b7f..a413081 100644
--- a/web/apache/request.cxx
+++ b/web/server/apache/request.cxx
@@ -1,7 +1,7 @@
-// file : web/apache/request.cxx -*- C++ -*-
+// file : web/server/apache/request.cxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#include <web/apache/request.hxx>
+#include <web/server/apache/request.hxx>
#include <apr.h> // APR_SIZE_MAX
#include <apr_errno.h> // apr_status_t, APR_SUCCESS, APR_E*, apr_strerror()
@@ -38,7 +38,7 @@
#include <libbutl/optional.mxx>
#include <libbutl/timestamp.mxx>
-#include <web/mime-url-encoding.hxx>
+#include <web/server/mime-url-encoding.hxx>
using namespace std;
using namespace butl;
diff --git a/web/apache/request.hxx b/web/server/apache/request.hxx
index 793a09d..bc105ec 100644
--- a/web/apache/request.hxx
+++ b/web/server/apache/request.hxx
@@ -1,8 +1,8 @@
-// file : web/apache/request.hxx -*- C++ -*-
+// file : web/server/apache/request.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef WEB_APACHE_REQUEST_HXX
-#define WEB_APACHE_REQUEST_HXX
+#ifndef WEB_SERVER_APACHE_REQUEST_HXX
+#define WEB_SERVER_APACHE_REQUEST_HXX
#include <httpd.h> // request_rec, HTTP_*, OK, M_POST
@@ -14,8 +14,8 @@
#include <ostream>
#include <streambuf>
-#include <web/module.hxx>
-#include <web/apache/stream.hxx>
+#include <web/server/module.hxx>
+#include <web/server/apache/stream.hxx>
namespace web
{
@@ -228,6 +228,6 @@ namespace web
}
}
-#include <web/apache/request.ixx>
+#include <web/server/apache/request.ixx>
-#endif // WEB_APACHE_REQUEST_HXX
+#endif // WEB_SERVER_APACHE_REQUEST_HXX
diff --git a/web/apache/request.ixx b/web/server/apache/request.ixx
index 3a1c01a..119fd2e 100644
--- a/web/apache/request.ixx
+++ b/web/server/apache/request.ixx
@@ -1,4 +1,4 @@
-// file : web/apache/request.ixx -*- C++ -*-
+// file : web/server/apache/request.ixx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
#include <http_protocol.h> // ap_*()
diff --git a/web/apache/service.cxx b/web/server/apache/service.cxx
index 1eeb65e..9fb23da 100644
--- a/web/apache/service.cxx
+++ b/web/server/apache/service.cxx
@@ -1,7 +1,7 @@
-// file : web/apache/service.cxx -*- C++ -*-
+// file : web/server/apache/service.cxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#include <web/apache/service.hxx>
+#include <web/server/apache/service.hxx>
#include <apr_pools.h> // apr_palloc()
@@ -18,8 +18,8 @@
#include <libbutl/utility.mxx> // function_cast()
#include <libbutl/optional.mxx>
-#include <web/module.hxx>
-#include <web/apache/log.hxx>
+#include <web/server/module.hxx>
+#include <web/server/apache/log.hxx>
using namespace std;
using namespace butl;
diff --git a/web/apache/service.hxx b/web/server/apache/service.hxx
index aaf006e..ad54d2c 100644
--- a/web/apache/service.hxx
+++ b/web/server/apache/service.hxx
@@ -1,8 +1,8 @@
-// file : web/apache/service.hxx -*- C++ -*-
+// file : web/server/apache/service.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef WEB_APACHE_SERVICE_HXX
-#define WEB_APACHE_SERVICE_HXX
+#ifndef WEB_SERVER_APACHE_SERVICE_HXX
+#define WEB_SERVER_APACHE_SERVICE_HXX
#include <apr_pools.h> // apr_pool_t
#include <apr_hooks.h> // APR_HOOK_*
@@ -15,9 +15,9 @@
#include <string>
#include <cassert>
-#include <web/module.hxx>
-#include <web/apache/log.hxx>
-#include <web/apache/request.hxx>
+#include <web/server/module.hxx>
+#include <web/server/apache/log.hxx>
+#include <web/server/apache/request.hxx>
namespace web
{
@@ -328,6 +328,6 @@ namespace web
}
}
-#include <web/apache/service.txx>
+#include <web/server/apache/service.txx>
-#endif // WEB_APACHE_SERVICE_HXX
+#endif // WEB_SERVER_APACHE_SERVICE_HXX
diff --git a/web/apache/service.txx b/web/server/apache/service.txx
index bda8e10..1b16d0b 100644
--- a/web/apache/service.txx
+++ b/web/server/apache/service.txx
@@ -1,4 +1,4 @@
-// file : web/apache/service.txx -*- C++ -*-
+// file : web/server/apache/service.txx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
#include <httpd.h> // APEXIT_CHILDSICK
diff --git a/web/apache/stream.hxx b/web/server/apache/stream.hxx
index ed0018e..77145af 100644
--- a/web/apache/stream.hxx
+++ b/web/server/apache/stream.hxx
@@ -1,8 +1,8 @@
-// file : web/apache/stream.hxx -*- C++ -*-
+// file : web/server/apache/stream.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef WEB_APACHE_STREAM_HXX
-#define WEB_APACHE_STREAM_HXX
+#ifndef WEB_SERVER_APACHE_STREAM_HXX
+#define WEB_SERVER_APACHE_STREAM_HXX
#include <httpd.h> // request_rec, HTTP_*
#include <http_protocol.h> // ap_*()
@@ -13,7 +13,7 @@
#include <streambuf>
#include <algorithm> // min(), max()
-#include <web/module.hxx> // invalid_request
+#include <web/server/module.hxx> // invalid_request
namespace web
{
@@ -145,4 +145,4 @@ namespace web
}
}
-#endif // WEB_APACHE_STREAM_HXX
+#endif // WEB_SERVER_APACHE_STREAM_HXX
diff --git a/web/buildfile b/web/server/buildfile
index 0ca6cf6..26de70f 100644
--- a/web/buildfile
+++ b/web/server/buildfile
@@ -1,4 +1,4 @@
-# file : web/buildfile
+# file : web/server/buildfile
# license : MIT; see accompanying LICENSE file
# This is currently part of the brep apache module but lives in a separate
@@ -8,11 +8,8 @@
#
import libs = libapr1%lib{apr-1}
import libs += libapreq2%lib{apreq2}
-import libs += libstudxml%lib{studxml}
import libs += libbutl%lib{butl}
-libus{web}: {hxx ixx txx cxx}{** -version} {hxx}{version} $libs
-
-hxx{version}: in{version} $src_root/manifest
+libus{web-server}: {hxx ixx txx cxx}{**} $libs
{hxx ixx txx}{*}: install = false
diff --git a/web/mime-url-encoding.cxx b/web/server/mime-url-encoding.cxx
index e202f08..fd1e4e8 100644
--- a/web/mime-url-encoding.cxx
+++ b/web/server/mime-url-encoding.cxx
@@ -1,7 +1,7 @@
-// file : web/mime-url-encoding.cxx -*- C++ -*-
+// file : web/server/mime-url-encoding.cxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#include <web/mime-url-encoding.hxx>
+#include <web/server/mime-url-encoding.hxx>
#include <string>
#include <iterator> // back_inserter
diff --git a/web/mime-url-encoding.hxx b/web/server/mime-url-encoding.hxx
index b9d2a76..34172a4 100644
--- a/web/mime-url-encoding.hxx
+++ b/web/server/mime-url-encoding.hxx
@@ -1,8 +1,8 @@
-// file : web/mime-url-encoding.hxx -*- C++ -*-
+// file : web/server/mime-url-encoding.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef WEB_MIME_URL_ENCODING_HXX
-#define WEB_MIME_URL_ENCODING_HXX
+#ifndef WEB_SERVER_MIME_URL_ENCODING_HXX
+#define WEB_SERVER_MIME_URL_ENCODING_HXX
#include <string>
@@ -29,4 +29,4 @@ namespace web
bool query = true);
}
-#endif // WEB_MIME_URL_ENCODING_HXX
+#endif // WEB_SERVER_MIME_URL_ENCODING_HXX
diff --git a/web/module.hxx b/web/server/module.hxx
index 5e9959e..beda73c 100644
--- a/web/module.hxx
+++ b/web/server/module.hxx
@@ -1,8 +1,8 @@
-// file : web/module.hxx -*- C++ -*-
+// file : web/server/module.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef WEB_MODULE_HXX
-#define WEB_MODULE_HXX
+#ifndef WEB_SERVER_MODULE_HXX
+#define WEB_SERVER_MODULE_HXX
#include <map>
#include <string>
@@ -296,4 +296,4 @@ namespace web
};
}
-#endif // WEB_MODULE_HXX
+#endif // WEB_SERVER_MODULE_HXX
diff --git a/web/version.hxx.in b/web/version.hxx.in
deleted file mode 100644
index ba51d44..0000000
--- a/web/version.hxx.in
+++ /dev/null
@@ -1,11 +0,0 @@
-// file : web/version.hxx.in -*- C++ -*-
-// license : MIT; see accompanying LICENSE file
-
-#ifndef WEB_VERSION_HXX_IN
-#define WEB_VERSION_HXX_IN
-
-#include <libstudxml/version.hxx>
-
-$libstudxml.check(LIBSTUDXML_VERSION, LIBSTUDXML_SNAPSHOT)$
-
-#endif // WEB_VERSION_HXX_IN
diff --git a/web/.gitignore b/web/xhtml/.gitignore
index 426db9e..426db9e 100644
--- a/web/.gitignore
+++ b/web/xhtml/.gitignore
diff --git a/web/xhtml/buildfile b/web/xhtml/buildfile
new file mode 100644
index 0000000..06dd34c
--- /dev/null
+++ b/web/xhtml/buildfile
@@ -0,0 +1,10 @@
+# file : web/xhtml/buildfile
+# license : MIT; see accompanying LICENSE file
+
+import libs = libstudxml%lib{studxml}
+
+./: {libue libus}{xhtml}: {hxx ixx txx cxx}{** -version} {hxx}{version} $libs
+
+hxx{version}: in{version} $src_root/manifest
+
+{hxx ixx txx}{*}: install = false
diff --git a/web/xhtml-fragment.cxx b/web/xhtml/fragment.cxx
index dbe0f0f..843db82 100644
--- a/web/xhtml-fragment.cxx
+++ b/web/xhtml/fragment.cxx
@@ -1,7 +1,7 @@
-// file : web/xhtml-fragment.cxx -*- C++ -*-
+// file : web/xhtml/fragment.cxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#include <web/xhtml-fragment.hxx>
+#include <web/xhtml/fragment.hxx>
#include <string>
#include <cassert>
@@ -9,7 +9,7 @@
#include <libstudxml/parser.hxx>
#include <libstudxml/serializer.hxx>
-#include <web/xhtml.hxx>
+#include <web/xhtml/serialization.hxx>
using namespace std;
using namespace xml;
diff --git a/web/xhtml-fragment.hxx b/web/xhtml/fragment.hxx
index 832d9eb..eab4335 100644
--- a/web/xhtml-fragment.hxx
+++ b/web/xhtml/fragment.hxx
@@ -1,4 +1,4 @@
-// file : web/xhtml-fragment.hxx -*- C++ -*-
+// file : web/xhtml/fragment.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
#ifndef WEB_XHTML_FRAGMENT_HXX
diff --git a/web/xhtml.hxx b/web/xhtml/serialization.hxx
index 727ad5f..03e72ff 100644
--- a/web/xhtml.hxx
+++ b/web/xhtml/serialization.hxx
@@ -1,12 +1,12 @@
-// file : web/xhtml.hxx -*- C++ -*-
+// file : web/xhtml/serialization.hxx -*- C++ -*-
// license : MIT; see accompanying LICENSE file
-#ifndef WEB_XHTML_HXX
-#define WEB_XHTML_HXX
+#ifndef WEB_XHTML_SERIALIZATION_HXX
+#define WEB_XHTML_SERIALIZATION_HXX
#include <libstudxml/serializer.hxx>
-#include <web/version.hxx>
+#include <web/xhtml/version.hxx>
namespace web
{
@@ -355,4 +355,4 @@ namespace web
}
}
-#endif // WEB_XHTML_HXX
+#endif // WEB_XHTML_SERIALIZATION_HXX
diff --git a/web/xhtml/version.hxx.in b/web/xhtml/version.hxx.in
new file mode 100644
index 0000000..fe3e4e5
--- /dev/null
+++ b/web/xhtml/version.hxx.in
@@ -0,0 +1,11 @@
+// file : web/xhtml/version.hxx.in -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#ifndef WEB_XHTML_VERSION_HXX_IN
+#define WEB_XHTML_VERSION_HXX_IN
+
+#include <libstudxml/version.hxx>
+
+$libstudxml.check(LIBSTUDXML_VERSION, LIBSTUDXML_SNAPSHOT)$
+
+#endif // WEB_XHTML_VERSION_HXX_IN