aboutsummaryrefslogtreecommitdiff
path: root/libbrep
diff options
context:
space:
mode:
Diffstat (limited to 'libbrep')
-rw-r--r--libbrep/.gitignore10
-rw-r--r--libbrep/build.cxx48
-rw-r--r--libbrep/build.hxx166
-rw-r--r--libbrep/build.xml67
-rw-r--r--libbrep/buildfile50
-rw-r--r--libbrep/common.cxx10
-rw-r--r--libbrep/common.hxx341
-rw-r--r--libbrep/database-lock.cxx44
-rw-r--r--libbrep/database-lock.hxx43
-rwxr-xr-xlibbrep/odb.sh40
-rw-r--r--libbrep/package-extra.sql130
-rw-r--r--libbrep/package-traits.cxx70
-rw-r--r--libbrep/package-traits.hxx38
-rw-r--r--libbrep/package.cxx169
-rw-r--r--libbrep/package.hxx500
-rw-r--r--libbrep/package.xml417
-rw-r--r--libbrep/types.hxx94
-rw-r--r--libbrep/utility.hxx34
-rw-r--r--libbrep/version.hxx.in74
-rw-r--r--libbrep/wrapper-traits.hxx63
20 files changed, 2408 insertions, 0 deletions
diff --git a/libbrep/.gitignore b/libbrep/.gitignore
new file mode 100644
index 0000000..3f251f5
--- /dev/null
+++ b/libbrep/.gitignore
@@ -0,0 +1,10 @@
+common-odb.?xx
+
+package-odb.?xx
+package.sql
+package-extra.hxx
+
+build-odb.?xx
+build.sql
+
+version.hxx
diff --git a/libbrep/build.cxx b/libbrep/build.cxx
new file mode 100644
index 0000000..710c0b2
--- /dev/null
+++ b/libbrep/build.cxx
@@ -0,0 +1,48 @@
+// file : libbrep/build.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbrep/build.hxx>
+
+namespace brep
+{
+ // build_state
+ //
+ string
+ to_string (build_state s)
+ {
+ switch (s)
+ {
+ case build_state::untested: return "untested";
+ case build_state::testing: return "testing";
+ case build_state::tested: return "tested";
+ }
+
+ return string (); // Should never reach.
+ }
+
+ build_state
+ to_build_state (const string& s)
+ {
+ if (s == "untested") return build_state::untested;
+ else if (s == "testing") return build_state::testing;
+ else if (s == "tested") return build_state::tested;
+ else throw invalid_argument ("invalid build state '" + s + "'");
+ }
+
+ // build
+ //
+ build::
+ build (string pnm, version pvr, string cfg, string mnm, string msm)
+ : id (package_id (move (pnm), pvr), move (cfg)),
+ package_name (id.package.name),
+ package_version (move (pvr)),
+ configuration (id.configuration),
+ state (build_state::testing),
+ timestamp (timestamp_type::clock::now ()),
+ forced (false),
+ machine (move (mnm)),
+ machine_summary (move (msm))
+ {
+ }
+}
diff --git a/libbrep/build.hxx b/libbrep/build.hxx
new file mode 100644
index 0000000..d28f5ab
--- /dev/null
+++ b/libbrep/build.hxx
@@ -0,0 +1,166 @@
+// file : libbrep/build.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBREP_BUILD_HXX
+#define LIBBREP_BUILD_HXX
+
+#include <chrono>
+
+#include <odb/core.hxx>
+#include <odb/section.hxx>
+
+#include <libbbot/manifest.hxx>
+
+#include <libbrep/types.hxx>
+#include <libbrep/utility.hxx>
+
+#include <libbrep/common.hxx> // Must be included last (see assert).
+
+// Used by the data migration entries.
+//
+#define LIBBREP_BUILD_SCHEMA_VERSION_BASE 1
+
+#pragma db model version(LIBBREP_BUILD_SCHEMA_VERSION_BASE, 1, open)
+
+// We have to keep these mappings at the global scope instead of inside
+// the brep namespace because they need to be also effective in the
+// bbot namespace from which we "borrow" types (and some of them use the mapped
+// types).
+//
+#pragma db map type(bbot::result_status) as(std::string) \
+ to(to_string (?)) \
+ from(bbot::to_result_status (?))
+
+namespace brep
+{
+ #pragma db value
+ struct build_id
+ {
+ package_id package;
+ string configuration;
+
+ build_id () = default;
+ build_id (package_id p, string c)
+ : package (move (p)), configuration (move (c)) {}
+ };
+
+ inline bool
+ operator< (const build_id& x, const build_id& y)
+ {
+ return
+ x.package < y.package ? true :
+ y.package < x.package ? false :
+ x.configuration < y.configuration;
+ }
+
+ // build_state
+ //
+ enum class build_state: std::uint8_t
+ {
+ untested,
+ testing,
+ tested
+ };
+
+ string
+ to_string (build_state);
+
+ build_state
+ to_build_state (const string&); // May throw invalid_argument.
+
+ inline ostream&
+ operator<< (ostream& os, build_state s) {return os << to_string (s);}
+
+ #pragma db map type(build_state) as(string) \
+ to(to_string (?)) \
+ from(brep::to_build_state (?))
+
+ // result_status
+ //
+ using bbot::result_status;
+
+ using optional_result_status = optional<result_status>;
+
+ #pragma db map type(optional_result_status) as(optional_string) \
+ to((?) ? bbot::to_string (*(?)) : brep::optional_string ()) \
+ from((?) \
+ ? bbot::to_result_status (*(?)) \
+ : brep::optional_result_status ())
+
+ // operation_results
+ //
+ using bbot::operation_result;
+ #pragma db value(operation_result) definition
+
+ using bbot::operation_results;
+
+ #pragma db object pointer(shared_ptr) session
+ class build
+ {
+ public:
+ using timestamp_type = brep::timestamp;
+
+ // Create the build object with the testing state, non-existent status,
+ // the timestamp set to now and the forced flag set to false.
+ //
+ build (string name, version,
+ string configuration,
+ string machine, string machine_summary);
+
+ build_id id;
+
+ string& package_name; // Tracks id.package.name.
+ upstream_version package_version; // Original of id.package.version.
+ string& configuration; // Tracks id.configuration.
+
+ build_state state;
+
+ // Time of the last state change (the creation time initially).
+ //
+ timestamp_type timestamp;
+
+ // True if the package rebuild has been forced.
+ //
+ bool forced;
+
+ // Present only if the state is 'tested'.
+ //
+ optional<result_status> status;
+
+ // Present only if the state is 'testing' or 'tested'.
+ //
+ optional<string> machine;
+ optional<string> machine_summary;
+
+ // Note that the logs are stored as std::string/TEXT which is Ok since
+ // they are UTF-8 and our database is UTF-8.
+ //
+ #pragma db section(results_section)
+ operation_results results;
+
+ #pragma db load(lazy) update(always)
+ odb::section results_section;
+
+ // Database mapping.
+ //
+ #pragma db member(id) id column("")
+
+ #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(results) id_column("") value_column("")
+
+ build (const build&) = delete;
+ build& operator= (const build&) = delete;
+
+ private:
+ friend class odb::access;
+ build ()
+ : package_name (id.package.name), configuration (id.configuration) {}
+ };
+}
+
+#endif // LIBBREP_BUILD_HXX
diff --git a/libbrep/build.xml b/libbrep/build.xml
new file mode 100644
index 0000000..8d9178a
--- /dev/null
+++ b/libbrep/build.xml
@@ -0,0 +1,67 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="build" version="1">
+ <model version="1">
+ <table name="build" kind="object">
+ <column name="package_name" type="TEXT" 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="package_version_upstream" type="TEXT" null="false"/>
+ <column name="package_version_release" type="TEXT" null="true"/>
+ <column name="state" type="TEXT" null="false"/>
+ <column name="timestamp" type="BIGINT" null="false"/>
+ <column name="forced" type="BOOLEAN" null="false"/>
+ <column name="status" type="TEXT" null="true"/>
+ <column name="machine" type="TEXT" null="true"/>
+ <column name="machine_summary" type="TEXT" null="true"/>
+ <primary-key>
+ <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"/>
+ </primary-key>
+ </table>
+ <table name="build_results" kind="container">
+ <column name="package_name" type="TEXT" 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="index" type="BIGINT" null="false"/>
+ <column name="operation" type="TEXT" null="false"/>
+ <column name="status" type="TEXT" null="false"/>
+ <column name="log" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <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"/>
+ <references table="build">
+ <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"/>
+ </references>
+ </foreign-key>
+ <index name="build_results_object_id_i">
+ <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"/>
+ </index>
+ <index name="build_results_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ </model>
+</changelog>
diff --git a/libbrep/buildfile b/libbrep/buildfile
new file mode 100644
index 0000000..b4005ab
--- /dev/null
+++ b/libbrep/buildfile
@@ -0,0 +1,50 @@
+# file : libbrep/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+define sql: file
+sql{*}: extension = sql
+sql{*}: install = data/
+
+import int_libs = libodb%lib{odb}
+import int_libs += libodb-pgsql%lib{odb-pgsql}
+import int_libs += libbutl%lib{butl}
+import int_libs += libbpkg%lib{bpkg}
+import int_libs += libbbot%lib{bbot}
+
+lib{brep}: \
+{hxx cxx}{ build } \
+{file }{ build.xml } \
+{hxx ixx cxx}{ build-odb } \
+{hxx cxx}{ common } \
+{hxx ixx cxx}{ common-odb } \
+{hxx cxx}{ package } \
+{file }{ package.xml } \
+{hxx ixx cxx}{ package-odb } \
+{hxx }{ package-extra } \
+{hxx cxx}{ package-traits } \
+{hxx cxx}{ database-lock } \
+{hxx }{ types } \
+{hxx }{ utility } \
+{hxx }{ version } \
+{hxx }{ wrapper-traits } \
+ $int_libs \
+sql{build package package-extra}
+
+hxx{version}: in{version} $src_root/file{manifest}
+hxx{version}: dist = true
+
+# For pre-releases use the complete version to make sure they cannot be used
+# in place of another pre-release or the final version.
+#
+if $version.pre_release
+ lib{brep}: bin.lib.version = @"-$version.project_id"
+else
+ lib{brep}: bin.lib.version = @"-$version.major.$version.minor"
+
+lib{brep}: cxx.export.poptions = "-I$out_root" "-I$src_root"
+lib{brep}: cxx.export.libs = $int_libs
+
+# Install into the libbrep/ subdirectory of, say, /usr/include/.
+#
+install.include = $install.include/libbrep/
diff --git a/libbrep/common.cxx b/libbrep/common.cxx
new file mode 100644
index 0000000..39fd0c7
--- /dev/null
+++ b/libbrep/common.cxx
@@ -0,0 +1,10 @@
+// file : libbrep/common.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbrep/common.hxx>
+
+namespace brep
+{
+ const version wildcard_version (0, "0", nullopt, 0);
+}
diff --git a/libbrep/common.hxx b/libbrep/common.hxx
new file mode 100644
index 0000000..bf60491
--- /dev/null
+++ b/libbrep/common.hxx
@@ -0,0 +1,341 @@
+// file : libbrep/common.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBREP_COMMON_HXX
+#define LIBBREP_COMMON_HXX
+
+#include <ratio>
+#include <chrono>
+#include <type_traits> // static_assert
+
+#include <libbrep/types.hxx>
+#include <libbrep/utility.hxx>
+
+// The uint16_t value range is not fully covered by SMALLINT PostgreSQL type
+// to which uint16_t is mapped by default.
+//
+#pragma db value(uint16_t) type("INTEGER")
+
+namespace brep
+{
+ // Use an image type to map bpkg::version to the database since there
+ // is no way to modify individual components directly.
+ //
+ #pragma db value
+ struct _version
+ {
+ uint16_t epoch;
+ string canonical_upstream;
+ string canonical_release;
+ uint16_t revision;
+ string upstream;
+ optional<string> release;
+ };
+}
+
+#include <bpkg/manifest>
+
+namespace brep
+{
+ using optional_version = optional<bpkg::version>;
+ using _optional_version = optional<_version>;
+}
+
+// Prevent assert() macro expansion in get/set expressions. This should
+// appear after all #include directives since the assert() macro is
+// redefined in each <assert.h> inclusion.
+//
+#ifdef ODB_COMPILER
+# undef assert
+# define assert assert
+void assert (int);
+#endif
+
+// 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
+// bpkg namespace from which we "borrow" types (and some of them use version).
+//
+#pragma db map type(bpkg::version) as(brep::_version) \
+ to(brep::_version{(?).epoch, \
+ (?).canonical_upstream, \
+ (?).canonical_release, \
+ (?).revision, \
+ (?).upstream, \
+ (?).release}) \
+ from(bpkg::version ((?).epoch, \
+ std::move ((?).upstream), \
+ std::move ((?).release), \
+ (?).revision))
+
+#pragma db map type(brep::optional_version) as(brep::_optional_version) \
+ to((?) \
+ ? brep::_version{(?)->epoch, \
+ (?)->canonical_upstream, \
+ (?)->canonical_release, \
+ (?)->revision, \
+ (?)->upstream, \
+ (?)->release} \
+ : brep::_optional_version ()) \
+ from((?) \
+ ? bpkg::version ((?)->epoch, \
+ std::move ((?)->upstream), \
+ std::move ((?)->release), \
+ (?)->revision) \
+ : brep::optional_version ())
+
+namespace brep
+{
+ // path
+ //
+ #pragma db map type(path) as(string) to((?).string ()) from(brep::path (?))
+
+ using optional_path = optional<path>;
+ using optional_string = optional<string>;
+
+ #pragma db map type(optional_path) as(brep::optional_string) \
+ to((?) ? (?)->string () : brep::optional_string ()) \
+ from((?) ? brep::path (*(?)) : brep::optional_path ())
+
+ #pragma db map type(dir_path) as(string) \
+ to((?).string ()) from(brep::dir_path (?))
+
+ // Ensure that timestamp can be represented in nonoseconds without loss of
+ // accuracy, so the following ODB mapping is adequate.
+ //
+ static_assert(
+ std::ratio_greater_equal<timestamp::period,
+ std::chrono::nanoseconds::period>::value,
+ "The following timestamp ODB mapping is invalid");
+
+ // As it pointed out in butl/timestamp we will overflow in year 2262, but
+ // by that time some larger basic type will be available for mapping.
+ //
+ #pragma db map type(timestamp) as(uint64_t) \
+ to(std::chrono::duration_cast<std::chrono::nanoseconds> ( \
+ (?).time_since_epoch ()).count ()) \
+ from(brep::timestamp ( \
+ std::chrono::duration_cast<brep::timestamp::duration> ( \
+ std::chrono::nanoseconds (?))))
+
+ // version
+ //
+ using bpkg::version;
+
+ #pragma db value
+ struct canonical_version
+ {
+ uint16_t epoch;
+ string canonical_upstream;
+ string canonical_release;
+ uint16_t revision;
+
+ bool
+ empty () const noexcept
+ {
+ // Note that an empty canonical_upstream doesn't denote an empty
+ // canonical_version. Remeber, that canonical_upstream doesn't include
+ // rightmost digit-only zero components? So non-empty version("0") has
+ // an empty canonical_upstream.
+ //
+ return epoch == 0 && canonical_upstream.empty () &&
+ canonical_release.empty () && revision == 0;
+ }
+
+ // Change collation to ensure the proper comparison of the "absent" release
+ // with a specified one.
+ //
+ // The default collation for UTF8-encoded TEXT columns in PostgreSQL is
+ // UCA-compliant. This makes the statement 'a' < '~' to be false, which
+ // in turn makes the statement 2.1.alpha < 2.1 to be false as well.
+ //
+ // Unicode Collation Algorithm (UCA): http://unicode.org/reports/tr10/
+ //
+ #pragma db member(canonical_release) options("COLLATE \"C\"")
+ };
+
+ #pragma db value transient
+ struct upstream_version: version
+ {
+ #pragma db member(upstream_) virtual(string) \
+ get(this.upstream) \
+ set(this = brep::version (0, std::move (?), std::string (), 0))
+
+ #pragma db member(release_) virtual(optional_string) \
+ get(this.release) \
+ set(this = brep::version ( \
+ 0, std::move (this.upstream), std::move (?), 0))
+
+ upstream_version () = default;
+ upstream_version (version v): version (move (v)) {}
+ upstream_version&
+ operator= (version v) {version& b (*this); b = v; return *this;}
+
+ void
+ init (const canonical_version& cv, const upstream_version& uv)
+ {
+ *this = version (cv.epoch, uv.upstream, uv.release, cv.revision);
+ assert (cv.canonical_upstream == canonical_upstream &&
+ cv.canonical_release == canonical_release);
+ }
+ };
+
+ // Wildcard version. Satisfies any dependency constraint and is represented
+ // as 0+0 (which is also the "stub version"; since a real version is always
+ // greater than the stub version, we reuse it to signify a special case).
+ //
+ extern const version wildcard_version;
+
+ #pragma db value
+ struct package_id
+ {
+ string name;
+ canonical_version version;
+
+ package_id () = default;
+ package_id (string n, const brep::version& v)
+ : name (move (n)),
+ version {
+ v.epoch, v.canonical_upstream, v.canonical_release, v.revision}
+ {
+ }
+ };
+
+ // Version comparison operators.
+ //
+ // They allow comparing objects that have epoch, canonical_upstream,
+ // canonical_release, and revision data members. The idea is that this
+ // works for both query members of types version and canonical_version
+ // as well as for comparing canonical_version to version.
+ //
+ template <typename T1, typename T2>
+ inline auto
+ compare_version_eq (const T1& x, const T2& y, bool revision)
+ -> decltype (x.epoch == y.epoch)
+ {
+ // Since we don't quite know what T1 and T2 are (and where the resulting
+ // expression will run), let's not push our luck with something like
+ // (!revision || x.revision == y.revision).
+ //
+ auto r (x.epoch == y.epoch &&
+ x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release == y.canonical_release);
+
+ return revision
+ ? r && x.revision == y.revision
+ : r;
+ }
+
+ template <typename T1, typename T2>
+ inline auto
+ compare_version_ne (const T1& x, const T2& y, bool revision)
+ -> decltype (x.epoch == y.epoch)
+ {
+ auto r (x.epoch != y.epoch ||
+ x.canonical_upstream != y.canonical_upstream ||
+ x.canonical_release != y.canonical_release);
+
+ return revision
+ ? r || x.revision != y.revision
+ : r;
+ }
+
+ template <typename T1, typename T2>
+ inline auto
+ compare_version_lt (const T1& x, const T2& y, bool revision)
+ -> decltype (x.epoch == y.epoch)
+ {
+ auto r (
+ x.epoch < y.epoch ||
+ (x.epoch == y.epoch && x.canonical_upstream < y.canonical_upstream) ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release < y.canonical_release));
+
+ return revision
+ ? r ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release == y.canonical_release && x.revision < y.revision)
+ : r;
+ }
+
+ template <typename T1, typename T2>
+ inline auto
+ compare_version_le (const T1& x, const T2& y, bool revision)
+ -> decltype (x.epoch == y.epoch)
+ {
+ auto r (
+ x.epoch < y.epoch ||
+ (x.epoch == y.epoch && x.canonical_upstream < y.canonical_upstream));
+
+ return revision
+ ? r ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release < y.canonical_release) ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release == y.canonical_release && x.revision <= y.revision)
+ : r ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release <= y.canonical_release);
+ }
+
+ template <typename T1, typename T2>
+ inline auto
+ compare_version_gt (const T1& x, const T2& y, bool revision)
+ -> decltype (x.epoch == y.epoch)
+ {
+ auto r (
+ x.epoch > y.epoch ||
+ (x.epoch == y.epoch && x.canonical_upstream > y.canonical_upstream) ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release > y.canonical_release));
+
+ return revision
+ ? r ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release == y.canonical_release && x.revision > y.revision)
+ : r;
+ }
+
+ template <typename T1, typename T2>
+ inline auto
+ compare_version_ge (const T1& x, const T2& y, bool revision)
+ -> decltype (x.epoch == y.epoch)
+ {
+ auto r (
+ x.epoch > y.epoch ||
+ (x.epoch == y.epoch && x.canonical_upstream > y.canonical_upstream));
+
+ return revision
+ ? r ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release > y.canonical_release) ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release == y.canonical_release && x.revision >= y.revision)
+ : r ||
+ (x.epoch == y.epoch && x.canonical_upstream == y.canonical_upstream &&
+ x.canonical_release >= y.canonical_release);
+ }
+
+ template <typename T>
+ inline auto
+ order_by_version_desc (const T& x) -> //decltype ("ORDER BY" + x.epoch)
+ decltype (x.epoch == 0)
+ {
+ return "ORDER BY"
+ + x.epoch + "DESC,"
+ + x.canonical_upstream + "DESC,"
+ + x.canonical_release + "DESC,"
+ + x.revision + "DESC";
+ }
+
+ inline bool
+ operator< (const package_id& x, const package_id& y)
+ {
+ if (int r = x.name.compare (y.name))
+ return r < 0;
+
+ return compare_version_lt (x.version, y.version, true);
+ }
+}
+
+#endif // LIBBREP_COMMON_HXX
diff --git a/libbrep/database-lock.cxx b/libbrep/database-lock.cxx
new file mode 100644
index 0000000..c69c240
--- /dev/null
+++ b/libbrep/database-lock.cxx
@@ -0,0 +1,44 @@
+// file : libbrep/database-lock.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbrep/database-lock.hxx>
+
+#include <odb/pgsql/database.hxx>
+#include <odb/pgsql/exceptions.hxx>
+#include <odb/pgsql/transaction.hxx>
+
+namespace brep
+{
+ using namespace odb::pgsql;
+
+ database_lock::
+ database_lock (database& db)
+ {
+ // Before locking the table make sure it exists.
+ //
+ {
+ transaction t (db.begin ());
+ db.execute ("CREATE TABLE IF NOT EXISTS database_mutex ()");
+ t.commit ();
+ }
+
+ connection_ = db.connection ();
+
+ // Don't make current. Will be rolled back in destructor.
+ //
+ transaction_.reset (new transaction (connection_->begin (), false));
+
+ try
+ {
+ connection_->execute ("LOCK TABLE database_mutex NOWAIT");
+ }
+ catch (const database_exception& e)
+ {
+ if (e.sqlstate () == "55P03") // The table is already locked.
+ throw database_locked ();
+
+ throw;
+ }
+ }
+}
diff --git a/libbrep/database-lock.hxx b/libbrep/database-lock.hxx
new file mode 100644
index 0000000..60d57a4
--- /dev/null
+++ b/libbrep/database-lock.hxx
@@ -0,0 +1,43 @@
+// file : libbrep/database-lock.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBREP_DATABASE_LOCK_HXX
+#define LIBBREP_DATABASE_LOCK_HXX
+
+#include <odb/pgsql/forward.hxx> // database, transaction
+#include <odb/pgsql/connection.hxx>
+
+#include <libbrep/types.hxx>
+#include <libbrep/utility.hxx>
+
+namespace brep
+{
+ struct database_locked: std::exception
+ {
+ virtual char const*
+ what () const throw () {return "database locked";}
+ };
+
+ // Try to "lock" the PostgreSQL database in the constructor and release the
+ // lock in the destructor. Throw database_locked if the database is already
+ // locked by someone else. May also throw odb::pgsql::database_exception.
+ //
+ // This mechanism is used by the brep loader and schema migration tool to
+ // make sure they don't step on each others toes.
+ //
+ // Note: movable but not copyable.
+ //
+ class database_lock
+ {
+ public:
+ explicit
+ database_lock (odb::pgsql::database&);
+
+ private:
+ odb::pgsql::connection_ptr connection_;
+ unique_ptr<odb::pgsql::transaction> transaction_;
+ };
+}
+
+#endif // LIBBREP_DATABASE_LOCK_HXX
diff --git a/libbrep/odb.sh b/libbrep/odb.sh
new file mode 100755
index 0000000..b794a2d
--- /dev/null
+++ b/libbrep/odb.sh
@@ -0,0 +1,40 @@
+#! /usr/bin/env bash
+
+trap 'exit 1' ERR
+
+odb=odb
+lib="\
+-I$HOME/work/odb/libodb-pgsql-default \
+-I$HOME/work/odb/libodb-pgsql \
+-I$HOME/work/odb/libodb-default \
+-I$HOME/work/odb/libodb"
+
+$odb $lib -d pgsql --std c++11 --generate-query \
+ --odb-epilogue '#include <libbrep/wrapper-traits.hxx>' \
+ --hxx-prologue '#include <libbrep/wrapper-traits.hxx>' \
+ -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \
+ -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \
+ --hxx-suffix ".hxx" --include-with-brackets \
+ --include-prefix libbrep --guard-prefix LIBBREP \
+ common.hxx
+
+$odb $lib -d pgsql --std c++11 --generate-query --generate-schema \
+ --schema-format sql --schema-format embedded --schema-name package \
+ --odb-epilogue '#include <libbrep/wrapper-traits.hxx>' \
+ --hxx-prologue '#include <libbrep/package-traits.hxx>' \
+ --generate-prepared -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \
+ -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \
+ --hxx-suffix ".hxx" --include-with-brackets \
+ --include-prefix libbrep --guard-prefix LIBBREP \
+ package.hxx
+
+xxd -i <package-extra.sql >package-extra.hxx
+
+$odb $lib -d pgsql --std c++11 --generate-query --generate-schema \
+ --schema-format sql --schema-format embedded --schema-name build \
+ --odb-epilogue '#include <libbrep/wrapper-traits.hxx>' \
+ --generate-prepared -DLIBODB_BUILD2 -DLIBODB_PGSQL_BUILD2 \
+ -I .. -I ../../libbbot -I ../../libbpkg -I ../../libbutl \
+ --hxx-suffix ".hxx" --include-with-brackets \
+ --include-prefix libbrep --guard-prefix LIBBREP \
+ build.hxx
diff --git a/libbrep/package-extra.sql b/libbrep/package-extra.sql
new file mode 100644
index 0000000..823c3af
--- /dev/null
+++ b/libbrep/package-extra.sql
@@ -0,0 +1,130 @@
+-- This file should be parsable by the brep-migrate utility. To decrease the
+-- parser complexity, the following restrictions are placed:
+--
+-- * comments must start with -- at the beginning of the line (ignoring
+-- leading spaces)
+-- * only CREATE and DROP statements for FUNCTION and TYPE
+-- * function bodies must be defined using $$-quoted strings
+-- * strings other then function bodies must be quoted with ' or "
+-- * statements must end with ";\n"
+--
+
+-- There is no need to drop to_tsvector() explicitly, as we can rely on "DROP
+-- TYPE IF EXISTS weighted_text CASCADE" statement below, which will drop all
+-- objects that depend on this type. Moreover this DROP FUNCTION statement will
+-- fail for old versions of PostgreSQL (experienced for 9.2.14) with error:
+-- type "weighted_text" does not exist.
+--
+-- DROP FUNCTION IF EXISTS to_tsvector(IN document weighted_text);
+--
+DROP FUNCTION IF EXISTS search_packages(IN query tsquery, INOUT name TEXT);
+DROP FUNCTION IF EXISTS search_latest_packages(IN query tsquery);
+DROP FUNCTION IF EXISTS latest_package(INOUT name TEXT);
+DROP FUNCTION IF EXISTS latest_packages();
+
+DROP TYPE IF EXISTS weighted_text CASCADE;
+CREATE TYPE weighted_text AS (a TEXT, b TEXT, c TEXT, d TEXT);
+
+-- Return the latest versions of internal packages as a set of package rows.
+--
+CREATE FUNCTION
+latest_packages()
+RETURNS SETOF package AS $$
+ SELECT p1.*
+ FROM package p1 LEFT JOIN package p2 ON (
+ p1.internal_repository IS NOT NULL AND p1.name = p2.name AND
+ p2.internal_repository IS NOT NULL AND
+ (p1.version_epoch < p2.version_epoch OR
+ p1.version_epoch = p2.version_epoch AND
+ (p1.version_canonical_upstream < p2.version_canonical_upstream OR
+ p1.version_canonical_upstream = p2.version_canonical_upstream AND
+ (p1.version_canonical_release < p2.version_canonical_release OR
+ p1.version_canonical_release = p2.version_canonical_release AND
+ p1.version_revision < p2.version_revision))))
+ WHERE
+ p1.internal_repository IS NOT NULL AND p2.name IS NULL;
+$$ LANGUAGE SQL STABLE;
+
+-- Find the latest version of an internal package having the specified name.
+-- Return a single row containing the package id, empty row set if the package
+-- not found.
+--
+CREATE FUNCTION
+latest_package(INOUT name TEXT,
+ OUT version_epoch INTEGER,
+ OUT version_canonical_upstream TEXT,
+ OUT version_canonical_release TEXT,
+ OUT version_revision INTEGER)
+RETURNS SETOF record AS $$
+ SELECT name, version_epoch, version_canonical_upstream,
+ version_canonical_release, version_revision
+ FROM latest_packages()
+ WHERE name = latest_package.name;
+$$ LANGUAGE SQL STABLE;
+
+-- Search for the latest version of an internal packages matching the specified
+-- search query. Return a set of rows containing the package id and search
+-- rank. If query is NULL, then match all packages and return 0 rank for
+-- all rows.
+--
+CREATE FUNCTION
+search_latest_packages(IN query tsquery,
+ OUT name TEXT,
+ OUT version_epoch INTEGER,
+ OUT version_canonical_upstream TEXT,
+ OUT version_canonical_release TEXT,
+ OUT version_revision INTEGER,
+ OUT rank real)
+RETURNS SETOF record AS $$
+ SELECT name, version_epoch, version_canonical_upstream,
+ version_canonical_release, version_revision,
+ CASE
+ WHEN query IS NULL THEN 0
+-- Weight mapping: D C B A
+ ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query)
+ END AS rank
+ FROM latest_packages()
+ WHERE query IS NULL OR search_index @@ query;
+$$ LANGUAGE SQL STABLE;
+
+-- Search for packages matching the search query and having the specified name.
+-- Return a set of rows containing the package id and search rank. If query
+-- is NULL, then match all packages and return 0 rank for all rows.
+--
+CREATE FUNCTION
+search_packages(IN query tsquery,
+ INOUT name TEXT,
+ OUT version_epoch INTEGER,
+ OUT version_canonical_upstream TEXT,
+ OUT version_canonical_release TEXT,
+ OUT version_revision INTEGER,
+ OUT rank real)
+RETURNS SETOF record AS $$
+ SELECT name, version_epoch, version_canonical_upstream,
+ version_canonical_release, version_revision,
+ CASE
+ WHEN query IS NULL THEN 0
+-- Weight mapping: D C B A
+ ELSE ts_rank_cd('{0.05, 0.2, 0.9, 1.0}', search_index, query)
+ END AS rank
+ FROM package
+ WHERE
+ internal_repository IS NOT NULL AND name = search_packages.name AND
+ (query IS NULL OR search_index @@ query);
+$$ LANGUAGE SQL STABLE;
+
+-- Parse weighted_text to tsvector.
+--
+CREATE FUNCTION
+to_tsvector(IN document weighted_text)
+RETURNS tsvector AS $$
+ SELECT
+ CASE
+ WHEN document IS NULL THEN NULL
+ ELSE
+ setweight(to_tsvector(document.a), 'A') ||
+ setweight(to_tsvector(document.b), 'B') ||
+ setweight(to_tsvector(document.c), 'C') ||
+ setweight(to_tsvector(document.d), 'D')
+ END
+$$ LANGUAGE SQL IMMUTABLE;
diff --git a/libbrep/package-traits.cxx b/libbrep/package-traits.cxx
new file mode 100644
index 0000000..a03cbac
--- /dev/null
+++ b/libbrep/package-traits.cxx
@@ -0,0 +1,70 @@
+// file : libbrep/package-traits.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbrep/package-traits.hxx>
+
+#include <string>
+#include <ostream>
+#include <sstream>
+#include <cstring> // memcpy
+
+#include <odb/pgsql/traits.hxx>
+
+using namespace std;
+
+namespace odb
+{
+ namespace pgsql
+ {
+ static inline void
+ to_pg_string (ostream& os, const string& s)
+ {
+ os << '"';
+
+ for (auto c: s)
+ {
+ if (c == '\\' || c == '"')
+ os << '\\';
+
+ os << c;
+ }
+
+ os << '"';
+ }
+
+ // Convert C++ weighted_text struct to PostgreSQL weighted_text
+ // composite type.
+ //
+ void value_traits<brep::weighted_text, id_string>::
+ set_image (details::buffer& b,
+ size_t& n,
+ bool& is_null,
+ const value_type& v)
+ {
+ is_null = v.a.empty () && v.b.empty () && v.c.empty () && v.d.empty ();
+
+ if (!is_null)
+ {
+ ostringstream o;
+ o << "(";
+ to_pg_string (o, v.a);
+ o << ",";
+ to_pg_string (o, v.b);
+ o << ",";
+ to_pg_string (o, v.c);
+ o << ",";
+ to_pg_string (o, v.d);
+ o << ")";
+
+ const string& s (o.str ());
+ n = s.size ();
+
+ if (n > b.capacity ())
+ b.capacity (n);
+
+ memcpy (b.data (), s.c_str (), n);
+ }
+ }
+ }
+}
diff --git a/libbrep/package-traits.hxx b/libbrep/package-traits.hxx
new file mode 100644
index 0000000..b626ea8
--- /dev/null
+++ b/libbrep/package-traits.hxx
@@ -0,0 +1,38 @@
+// file : brep/package-traits -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BREP_PACKAGE_TRAITS
+#define BREP_PACKAGE_TRAITS
+
+#include <cstddef> // size_t
+
+#include <odb/pgsql/traits.hxx>
+
+#include <libbrep/package.hxx> // weighted_text
+
+namespace odb
+{
+ namespace pgsql
+ {
+ template <>
+ class value_traits<brep::weighted_text, id_string>
+ {
+ public:
+ typedef brep::weighted_text value_type;
+ typedef value_type query_type;
+ typedef details::buffer image_type;
+
+ static void
+ set_value (value_type&, const details::buffer&, std::size_t, bool) {}
+
+ static void
+ set_image (details::buffer&,
+ std::size_t& n,
+ bool& is_null,
+ const value_type&);
+ };
+ }
+}
+
+#endif // BREP_PACKAGE_TRAITS
diff --git a/libbrep/package.cxx b/libbrep/package.cxx
new file mode 100644
index 0000000..20be387
--- /dev/null
+++ b/libbrep/package.cxx
@@ -0,0 +1,169 @@
+// file : libbrep/package.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbrep/package.hxx>
+
+#include <odb/database.hxx>
+
+#include <libbrep/package-odb.hxx>
+
+using namespace std;
+using namespace odb::core;
+
+namespace brep
+{
+ // dependency
+ //
+ string dependency::
+ name () const
+ {
+ return package.object_id ().name;
+ }
+
+ ostream&
+ operator<< (ostream& o, const dependency& d)
+ {
+ o << d.name ();
+
+ if (d.constraint)
+ o << ' ' << *d.constraint;
+
+ return o;
+ }
+
+ bool
+ operator== (const dependency& x, const dependency& y)
+ {
+ return x.name () == y.name () && x.constraint == y.constraint;
+ }
+
+ bool
+ operator!= (const dependency& x, const dependency& y)
+ {
+ return !(x == y);
+ }
+
+ // package
+ //
+ package::
+ package (string nm,
+ version_type vr,
+ priority_type pr,
+ string sm,
+ license_alternatives_type la,
+ strings tg,
+ optional<string> ds,
+ string ch,
+ url_type ur,
+ optional<url_type> pu,
+ email_type em,
+ optional<email_type> pe,
+ optional<email_type> be,
+ dependencies_type dp,
+ requirements_type rq,
+ optional<path> lc,
+ optional<string> sh,
+ shared_ptr<repository_type> rp)
+ : id (move (nm), vr),
+ version (move (vr)),
+ priority (move (pr)),
+ summary (move (sm)),
+ license_alternatives (move (la)),
+ tags (move (tg)),
+ description (move (ds)),
+ changes (move (ch)),
+ url (move (ur)),
+ package_url (move (pu)),
+ email (move (em)),
+ package_email (move (pe)),
+ build_email (move (be)),
+ dependencies (move (dp)),
+ requirements (move (rq)),
+ internal_repository (move (rp)),
+ location (move (lc)),
+ sha256sum (move (sh))
+ {
+ assert (internal_repository->internal);
+ }
+
+ package::
+ package (string nm,
+ version_type vr,
+ shared_ptr<repository_type> rp)
+ : id (move (nm), vr),
+ version (move (vr))
+ {
+ assert (!rp->internal);
+ other_repositories.emplace_back (move (rp));
+ }
+
+ weighted_text package::
+ search_text () const
+ {
+ if (!internal ())
+ return weighted_text ();
+
+ // Derive keywords from the basic package information: name,
+ // version.
+ //
+ //@@ What about 'stable' from cppget.org/stable? Add path of
+ // the repository to keywords? Or is it too "polluting" and
+ // we will handle it in some other way (e.g., by allowing
+ // the user to specify repo location in the drop-down box)?
+ // Probably drop-box would be better as also tells what are
+ // the available internal repositories.
+ //
+ string k (id.name + " " + version.string () + " " + version.string (true));
+
+ // Add tags to keywords.
+ //
+ for (const auto& t: tags)
+ k += " " + t;
+
+ // Add licenses to keywords.
+ //
+ for (const auto& la: license_alternatives)
+ {
+ for (const auto& l: la)
+ {
+ k += " " + l;
+
+ // If license is say LGPLv2 then LGPL is also a keyword.
+ //
+ size_t n (l.size ());
+ if (n > 2 && l[n - 2] == 'v' && l[n - 1] >= '0' && l[n - 1] <= '9')
+ k += " " + string (l, 0, n - 2);
+ }
+ }
+
+ return {move (k), summary, description ? *description : "", changes};
+ }
+
+ // repository
+ //
+ repository::
+ repository (repository_location l,
+ string d,
+ repository_location h,
+ optional<certificate_type> c,
+ uint16_t r)
+ : name (l.canonical_name ()),
+ location (move (l)),
+ display_name (move (d)),
+ priority (r),
+ cache_location (move (h)),
+ certificate (move (c)),
+ internal (true)
+ {
+ }
+
+ repository::
+ repository (repository_location l)
+ : name (l.canonical_name ()),
+ location (move (l)),
+ priority (0),
+ internal (false)
+ {
+ }
+}
diff --git a/libbrep/package.hxx b/libbrep/package.hxx
new file mode 100644
index 0000000..8164639
--- /dev/null
+++ b/libbrep/package.hxx
@@ -0,0 +1,500 @@
+// file : libbrep/package.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBREP_PACKAGE_HXX
+#define LIBBREP_PACKAGE_HXX
+
+#include <map>
+#include <chrono>
+
+#include <odb/core.hxx>
+#include <odb/nested-container.hxx>
+
+#include <libbrep/types.hxx>
+#include <libbrep/utility.hxx>
+
+#include <libbrep/common.hxx> // Must be included last (see assert).
+
+// Used by the data migration entries.
+//
+#define LIBBREP_PACKAGE_SCHEMA_VERSION_BASE 4
+
+#pragma db model version(LIBBREP_PACKAGE_SCHEMA_VERSION_BASE, 4, open)
+
+namespace brep
+{
+ // @@ Might make sense to put some heavy members (e.g., description,
+ // containers) into a separate section.
+ //
+ // @@ Not sure there is a benefit in making tags a full-blown container
+ // (i.e., a separate table). Maybe provide a mapping of vector<string>
+ // to TEXT as a comma-separated list.
+ //
+
+ // Forward declarations.
+ //
+ class repository;
+ class package;
+
+ // priority
+ //
+ using bpkg::priority;
+
+ #pragma db value(priority) definition
+ #pragma db member(priority::value) column("")
+
+ // url
+ //
+ using bpkg::url;
+
+ #pragma db value(url) definition
+ #pragma db member(url::value) virtual(string) before access(this) column("")
+
+ // email
+ //
+ using bpkg::email;
+
+ #pragma db value(email) definition
+ #pragma db member(email::value) virtual(string) before access(this) column("")
+
+ // licenses
+ //
+ using bpkg::licenses;
+ using license_alternatives = vector<licenses>;
+
+ #pragma db value(licenses) definition
+
+ // dependencies
+ //
+ using bpkg::dependency_constraint;
+
+ #pragma db value(dependency_constraint) definition
+
+ // Notes:
+ //
+ // 1. Will the package be always resolvable? What if it is in
+ // another repository (i.e., a "chained" third-party repo).
+ // The question is then whether we will load such "third-
+ // party packages" (i.e., packages that are not in our
+ // repository). If the answer is yes, then we can have
+ // a pointer here. If the answer is no, then we can't.
+ // Also, if the answer is yes, we probably don't need to
+ // load as much information as for "our own" packages. We
+ // also shouldn't be showing them in search results, etc.
+ // I think all we need is to know which repository this
+ // package comes from so that we can tell the user. How are
+ // we going to capture this? Poly hierarchy of packages?
+ //
+ // 2. I believe we don't need to use a weak pointer here since
+ // there should be no package dependency cycles (and therefore
+ // ownership cycles).
+ //
+ // 3. Actually there can be dependency cycle as dependency referes not to
+ // just a package but a specific version, so for the same pair of
+ // packages dependency for different versions can have an opposite
+ // directions. The possible solution is instead of a package we point
+ // to the earliest version that satisfies the constraint. But this
+ // approach requires to ensure no cycles exist before instantiating
+ // package objects which in presense of "foreign" packages can be
+ // tricky. Can stick to just a package name until get some clarity on
+ // "foreign" package resolution.
+ //
+ // 4. As we left just the package class the dependency resolution come to
+ // finding the best version matching package object. The question is
+ // if to resolve dependencies on the loading phase or in the WEB interface
+ // when required. The arguments in favour of doing that during loading
+ // phase are:
+ //
+ // - WEB interface get offloaded from a possibly expensive queries
+ // which otherwise have to be executed multiple times for the same
+ // dependency no matter the result would be the same.
+ //
+ // - No need to complicate persisted object model with repository
+ // relations otherwise required just for dependency resolution.
+ //
+
+ #pragma db value
+ struct dependency
+ {
+ using package_type = brep::package;
+
+ lazy_shared_ptr<package_type> package;
+ optional<dependency_constraint> constraint;
+
+ // Prerequisite package name.
+ //
+ string
+ name () const;
+
+ // Database mapping.
+ //
+ #pragma db member(package) column("") not_null
+ #pragma db member(constraint) column("")
+ };
+
+ ostream&
+ operator<< (ostream&, const dependency&);
+
+ bool
+ operator== (const dependency&, const dependency&);
+
+ bool
+ operator!= (const dependency&, const dependency&);
+
+ #pragma db value
+ class dependency_alternatives: public vector<dependency>
+ {
+ public:
+ bool conditional;
+ bool buildtime;
+ string comment;
+
+ dependency_alternatives () = default;
+ dependency_alternatives (bool d, bool b, string c)
+ : conditional (d), buildtime (b), comment (move (c)) {}
+ };
+
+ using dependencies = vector<dependency_alternatives>;
+
+ // requirements
+ //
+ using bpkg::requirement_alternatives;
+ using requirements = vector<requirement_alternatives>;
+
+ #pragma db value(requirement_alternatives) definition
+
+ // repository_location
+ //
+ using bpkg::repository_location;
+
+ #pragma db map type(repository_location) as(string) \
+ to((?).string ()) from(brep::repository_location (?))
+
+ #pragma db value
+ class certificate
+ {
+ public:
+ string fingerprint; // SHA256 fingerprint.
+ string name; // CN component of Subject.
+ string organization; // O component of Subject.
+ string email; // email: in Subject Alternative Name.
+ string pem; // PEM representation.
+ };
+
+ #pragma db object pointer(shared_ptr) session
+ class repository
+ {
+ public:
+ using email_type = brep::email;
+ using certificate_type = brep::certificate;
+
+ // Create internal repository.
+ //
+ repository (repository_location,
+ string display_name,
+ repository_location cache_location,
+ optional<certificate_type>,
+ uint16_t priority);
+
+ // Create external repository.
+ //
+ explicit
+ repository (repository_location);
+
+ string name; // Object id (canonical name).
+ repository_location location;
+ string display_name;
+
+ // The order in the internal repositories configuration file, starting from
+ // 1. 0 for external repositories.
+ //
+ uint16_t priority;
+
+ optional<string> url;
+
+ // Present only for internal repositories.
+ //
+ optional<email_type> email;
+ optional<string> summary;
+ optional<string> description;
+
+ // Location of the repository local cache. Non empty for internal
+ // repositories and external ones with a filesystem path location.
+ //
+ repository_location cache_location;
+
+ // Present only for internal signed repositories.
+ //
+ optional<certificate_type> certificate;
+
+ // Initialized with timestamp_nonexistent by default.
+ //
+ timestamp packages_timestamp;
+
+ // Initialized with timestamp_nonexistent by default.
+ //
+ timestamp repositories_timestamp;
+
+ bool internal;
+ vector<lazy_weak_ptr<repository>> complements;
+ vector<lazy_weak_ptr<repository>> prerequisites;
+
+ // Database mapping.
+ //
+ #pragma db member(name) id
+
+ #pragma db member(location) \
+ set(this.location = std::move (?); \
+ assert (this.name == this.location.canonical_name ()))
+
+ #pragma db member(complements) id_column("repository") \
+ value_column("complement") value_not_null
+
+ #pragma db member(prerequisites) id_column("repository") \
+ value_column("prerequisite") value_not_null
+
+ private:
+ friend class odb::access;
+ repository () = default;
+ };
+
+ // The 'to' expression calls the PostgreSQL to_tsvector(weighted_text)
+ // function overload (package-extra.sql). Since we are only interested
+ // in "write-only" members of this type, make the 'from' expression
+ // always return empty string (we still have to work the placeholder
+ // in to keep overprotective ODB happy).
+ //
+ #pragma db map type("tsvector") as("TEXT") \
+ to("to_tsvector((?)::weighted_text)") from("COALESCE('',(?))")
+
+ // C++ type for weighted PostgreSQL tsvector.
+ //
+ #pragma db value type("tsvector")
+ struct weighted_text
+ {
+ string a;
+ string b;
+ string c;
+ string d;
+ };
+
+ #pragma db object pointer(shared_ptr) session
+ class package
+ {
+ public:
+ using repository_type = brep::repository;
+ using version_type = brep::version;
+ using priority_type = brep::priority;
+ using license_alternatives_type = brep::license_alternatives;
+ using url_type = brep::url;
+ using email_type = brep::email;
+ using dependencies_type = brep::dependencies;
+ using requirements_type = brep::requirements;
+
+ // Create internal package object.
+ //
+ package (string name,
+ version_type,
+ priority_type,
+ string summary,
+ license_alternatives_type,
+ strings tags,
+ optional<string> description,
+ string changes,
+ url_type,
+ optional<url_type> package_url,
+ email_type,
+ optional<email_type> package_email,
+ optional<email_type> build_email,
+ dependencies_type,
+ requirements_type,
+ optional<path> location,
+ optional<string> sha256sum,
+ shared_ptr<repository_type>);
+
+ // Create external package object.
+ //
+ // External repository packages can appear on the WEB interface only in
+ // dependency list in the form of a link to the corresponding WEB page.
+ // The only package information required to compose such a link is the
+ // package name, version, and repository location.
+ //
+ package (string name, version_type, shared_ptr<repository_type>);
+
+ bool
+ internal () const noexcept {return internal_repository != nullptr;}
+
+ // Manifest data.
+ //
+ package_id id;
+ upstream_version version;
+ priority_type priority;
+ string summary;
+ license_alternatives_type license_alternatives;
+ strings tags;
+ optional<string> description;
+ string changes;
+ url_type url;
+ optional<url_type> package_url;
+ email_type email;
+ optional<email_type> package_email;
+ optional<email_type> build_email;
+ dependencies_type dependencies;
+ requirements_type requirements;
+ lazy_shared_ptr<repository_type> internal_repository;
+
+ // Path to the package file. Present only for internal packages.
+ //
+ optional<path> location;
+
+ // Present only for internal packages.
+ //
+ optional<string> sha256sum;
+
+ vector<lazy_shared_ptr<repository_type>> other_repositories;
+
+ // Database mapping.
+ //
+ #pragma db member(id) id column("")
+ #pragma db member(version) set(this.version.init (this.id.version, (?)))
+
+ // license
+ //
+ using _license_key = odb::nested_key<licenses>;
+ using _licenses_type = std::map<_license_key, string>;
+
+ #pragma db value(_license_key)
+ #pragma db member(_license_key::outer) column("alternative_index")
+ #pragma db member(_license_key::inner) column("index")
+
+ #pragma db member(license_alternatives) id_column("") value_column("")
+ #pragma db member(licenses) \
+ virtual(_licenses_type) \
+ after(license_alternatives) \
+ get(odb::nested_get (this.license_alternatives)) \
+ set(odb::nested_set (this.license_alternatives, std::move (?))) \
+ id_column("") key_column("") value_column("license")
+
+ // tags
+ //
+ #pragma db member(tags) id_column("") value_column("tag")
+
+ // dependencies
+ //
+ using _dependency_key = odb::nested_key<dependency_alternatives>;
+ using _dependency_alternatives_type =
+ std::map<_dependency_key, dependency>;
+
+ #pragma db value(_dependency_key)
+ #pragma db member(_dependency_key::outer) column("dependency_index")
+ #pragma db member(_dependency_key::inner) column("index")
+
+ #pragma db member(dependencies) id_column("") value_column("")
+ #pragma db member(dependency_alternatives) \
+ virtual(_dependency_alternatives_type) \
+ after(dependencies) \
+ get(odb::nested_get (this.dependencies)) \
+ set(odb::nested_set (this.dependencies, std::move (?))) \
+ id_column("") key_column("") value_column("dep_")
+
+ // requirements
+ //
+ using _requirement_key = odb::nested_key<requirement_alternatives>;
+ using _requirement_alternatives_type =
+ std::map<_requirement_key, string>;
+
+ #pragma db value(_requirement_key)
+ #pragma db member(_requirement_key::outer) column("requirement_index")
+ #pragma db member(_requirement_key::inner) column("index")
+
+ #pragma db member(requirements) id_column("") value_column("")
+ #pragma db member(requirement_alternatives) \
+ virtual(_requirement_alternatives_type) \
+ after(requirements) \
+ get(odb::nested_get (this.requirements)) \
+ set(odb::nested_set (this.requirements, std::move (?))) \
+ id_column("") key_column("") value_column("id")
+
+ // other_repositories
+ //
+ #pragma db member(other_repositories) \
+ id_column("") value_column("repository") value_not_null
+
+ // search_index
+ //
+ #pragma db member(search_index) virtual(weighted_text) null \
+ access(search_text)
+
+ #pragma db index method("GIN") member(search_index)
+
+ private:
+ friend class odb::access;
+ package () = default;
+
+ // Save keywords, summary, description, and changes to weighted_text
+ // a, b, c, d members, respectively. So a word found in keywords will
+ // have a higher weight than if it's found in the summary.
+ //
+ weighted_text
+ search_text () const;
+
+ // Noop as search_index is a write-only member.
+ //
+ void
+ search_text (const weighted_text&) {}
+ };
+
+ // Package search query matching rank.
+ //
+ #pragma db view query("/*CALL*/ SELECT * FROM search_latest_packages(?)")
+ struct latest_package_search_rank
+ {
+ package_id id;
+ double rank;
+ };
+
+ #pragma db view \
+ query("/*CALL*/ SELECT count(*) FROM search_latest_packages(?)")
+ struct latest_package_count
+ {
+ size_t result;
+
+ operator size_t () const {return result;}
+ };
+
+ #pragma db view query("/*CALL*/ SELECT * FROM search_packages(?)")
+ struct package_search_rank
+ {
+ package_id id;
+ double rank;
+ };
+
+ #pragma db view query("/*CALL*/ SELECT count(*) FROM search_packages(?)")
+ struct package_count
+ {
+ size_t result;
+
+ operator size_t () const {return result;}
+ };
+
+ #pragma db view query("/*CALL*/ SELECT * FROM latest_package(?)")
+ struct latest_package
+ {
+ package_id id;
+ };
+
+ #pragma db view object(package)
+ struct package_version
+ {
+ package_id id;
+ upstream_version version;
+
+ // Database mapping.
+ //
+ #pragma db member(id) column("")
+ #pragma db member(version) set(this.version.init (this.id.version, (?)))
+ };
+}
+
+#endif // LIBBREP_PACKAGE_HXX
diff --git a/libbrep/package.xml b/libbrep/package.xml
new file mode 100644
index 0000000..657c2fc
--- /dev/null
+++ b/libbrep/package.xml
@@ -0,0 +1,417 @@
+<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" schema-name="package" version="1">
+ <model version="4">
+ <table name="repository" kind="object">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="location" type="TEXT" null="false"/>
+ <column name="display_name" type="TEXT" null="false"/>
+ <column name="priority" type="INTEGER" null="false"/>
+ <column name="url" type="TEXT" null="true"/>
+ <column name="email" type="TEXT" null="true"/>
+ <column name="email_comment" type="TEXT" null="true"/>
+ <column name="summary" type="TEXT" null="true"/>
+ <column name="description" type="TEXT" null="true"/>
+ <column name="cache_location" type="TEXT" null="false"/>
+ <column name="certificate_fingerprint" type="TEXT" null="true"/>
+ <column name="certificate_name" type="TEXT" null="true"/>
+ <column name="certificate_organization" type="TEXT" null="true"/>
+ <column name="certificate_email" type="TEXT" null="true"/>
+ <column name="certificate_pem" type="TEXT" null="true"/>
+ <column name="packages_timestamp" type="BIGINT" null="false"/>
+ <column name="repositories_timestamp" type="BIGINT" null="false"/>
+ <column name="internal" type="BOOLEAN" null="false"/>
+ <primary-key>
+ <column name="name"/>
+ </primary-key>
+ </table>
+ <table name="repository_complements" kind="container">
+ <column name="repository" type="TEXT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="complement" type="TEXT" null="false"/>
+ <foreign-key name="repository_fk" on-delete="CASCADE">
+ <column name="repository"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ <index name="repository_complements_repository_i">
+ <column name="repository"/>
+ </index>
+ <index name="repository_complements_index_i">
+ <column name="index"/>
+ </index>
+ <foreign-key name="complement_fk" deferrable="DEFERRED">
+ <column name="complement"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="repository_prerequisites" kind="container">
+ <column name="repository" type="TEXT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="prerequisite" type="TEXT" null="false"/>
+ <foreign-key name="repository_fk" on-delete="CASCADE">
+ <column name="repository"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ <index name="repository_prerequisites_repository_i">
+ <column name="repository"/>
+ </index>
+ <index name="repository_prerequisites_index_i">
+ <column name="index"/>
+ </index>
+ <foreign-key name="prerequisite_fk" deferrable="DEFERRED">
+ <column name="prerequisite"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="package" kind="object">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="version_upstream" type="TEXT" null="false"/>
+ <column name="version_release" type="TEXT" null="true"/>
+ <column name="priority" type="INTEGER" null="false"/>
+ <column name="priority_comment" type="TEXT" null="false"/>
+ <column name="summary" type="TEXT" null="false"/>
+ <column name="description" type="TEXT" null="true"/>
+ <column name="changes" type="TEXT" null="false"/>
+ <column name="url" type="TEXT" null="false"/>
+ <column name="url_comment" type="TEXT" null="false"/>
+ <column name="package_url" type="TEXT" null="true"/>
+ <column name="package_url_comment" type="TEXT" null="true"/>
+ <column name="email" type="TEXT" null="false"/>
+ <column name="email_comment" type="TEXT" null="false"/>
+ <column name="package_email" type="TEXT" null="true"/>
+ <column name="package_email_comment" type="TEXT" null="true"/>
+ <column name="build_email" type="TEXT" null="true"/>
+ <column name="build_email_comment" type="TEXT" null="true"/>
+ <column name="internal_repository" type="TEXT" null="true"/>
+ <column name="location" type="TEXT" null="true"/>
+ <column name="sha256sum" type="TEXT" null="true"/>
+ <column name="search_index" type="tsvector" null="true"/>
+ <primary-key>
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </primary-key>
+ <foreign-key name="internal_repository_fk" deferrable="DEFERRED">
+ <column name="internal_repository"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ <index name="package_search_index_i" method="GIN">
+ <column name="search_index"/>
+ </index>
+ </table>
+ <table name="package_license_alternatives" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="comment" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_license_alternatives_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ <index name="package_license_alternatives_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="package_licenses" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="alternative_index" type="BIGINT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="license" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_licenses_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ </table>
+ <table name="package_tags" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="tag" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_tags_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ <index name="package_tags_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="package_dependencies" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="conditional" type="BOOLEAN" null="false"/>
+ <column name="buildtime" type="BOOLEAN" null="false"/>
+ <column name="comment" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_dependencies_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ <index name="package_dependencies_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="package_dependency_alternatives" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="dependency_index" type="BIGINT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="dep_name" type="TEXT" null="false"/>
+ <column name="dep_version_epoch" type="INTEGER" null="false"/>
+ <column name="dep_version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="dep_version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="dep_version_revision" type="INTEGER" null="false"/>
+ <column name="dep_min_version_epoch" type="INTEGER" null="true"/>
+ <column name="dep_min_version_canonical_upstream" type="TEXT" null="true"/>
+ <column name="dep_min_version_canonical_release" type="TEXT" null="true"/>
+ <column name="dep_min_version_revision" type="INTEGER" null="true"/>
+ <column name="dep_min_version_upstream" type="TEXT" null="true"/>
+ <column name="dep_min_version_release" type="TEXT" null="true"/>
+ <column name="dep_max_version_epoch" type="INTEGER" null="true"/>
+ <column name="dep_max_version_canonical_upstream" type="TEXT" null="true"/>
+ <column name="dep_max_version_canonical_release" type="TEXT" null="true"/>
+ <column name="dep_max_version_revision" type="INTEGER" null="true"/>
+ <column name="dep_max_version_upstream" type="TEXT" null="true"/>
+ <column name="dep_max_version_release" type="TEXT" null="true"/>
+ <column name="dep_min_open" type="BOOLEAN" null="true"/>
+ <column name="dep_max_open" type="BOOLEAN" null="true"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_dependency_alternatives_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ <foreign-key name="dep_package_fk" deferrable="DEFERRED">
+ <column name="dep_name"/>
+ <column name="dep_version_epoch"/>
+ <column name="dep_version_canonical_upstream"/>
+ <column name="dep_version_canonical_release"/>
+ <column name="dep_version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="package_requirements" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="conditional" type="BOOLEAN" null="false"/>
+ <column name="buildtime" type="BOOLEAN" null="false"/>
+ <column name="comment" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_requirements_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ <index name="package_requirements_index_i">
+ <column name="index"/>
+ </index>
+ </table>
+ <table name="package_requirement_alternatives" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="requirement_index" type="BIGINT" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="id" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_requirement_alternatives_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ </table>
+ <table name="package_other_repositories" kind="container">
+ <column name="name" type="TEXT" null="false"/>
+ <column name="version_epoch" type="INTEGER" null="false"/>
+ <column name="version_canonical_upstream" type="TEXT" null="false"/>
+ <column name="version_canonical_release" type="TEXT" null="false" options="COLLATE &quot;C&quot;"/>
+ <column name="version_revision" type="INTEGER" null="false"/>
+ <column name="index" type="BIGINT" null="false"/>
+ <column name="repository" type="TEXT" null="false"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ <references table="package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="package_other_repositories_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_canonical_upstream"/>
+ <column name="version_canonical_release"/>
+ <column name="version_revision"/>
+ </index>
+ <index name="package_other_repositories_index_i">
+ <column name="index"/>
+ </index>
+ <foreign-key name="repository_fk" deferrable="DEFERRED">
+ <column name="repository"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ </table>
+ </model>
+</changelog>
diff --git a/libbrep/types.hxx b/libbrep/types.hxx
new file mode 100644
index 0000000..a0001a7
--- /dev/null
+++ b/libbrep/types.hxx
@@ -0,0 +1,94 @@
+// file : libbrep/types.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBREP_TYPES_HXX
+#define LIBBREP_TYPES_HXX
+
+#include <vector>
+#include <string>
+#include <memory> // unique_ptr, shared_ptr
+#include <utility> // pair
+#include <cstddef> // size_t, nullptr_t
+#include <cstdint> // uint{8,16,32,64}_t
+#include <istream>
+#include <ostream>
+#include <functional> // function, reference_wrapper
+
+#include <ios> // ios_base::failure
+#include <exception> // exception
+#include <stdexcept> // logic_error, invalid_argument, runtime_error
+#include <system_error>
+
+#include <odb/lazy-ptr.hxx>
+
+#include <butl/path>
+#include <butl/path-io>
+#include <butl/optional>
+#include <butl/timestamp>
+
+namespace brep
+{
+ // Commonly-used types.
+ //
+ using std::uint8_t;
+ using std::uint16_t;
+ using std::uint32_t;
+ using std::uint64_t;
+
+ using std::size_t;
+ using std::nullptr_t;
+
+ using std::pair;
+ using std::string;
+ using std::function;
+ using std::reference_wrapper;
+
+ using std::unique_ptr;
+ using std::shared_ptr;
+ using std::weak_ptr;
+
+ using std::vector;
+
+ using strings = vector<string>;
+ using cstrings = vector<const char*>;
+
+ using std::istream;
+ using std::ostream;
+
+ // Exceptions. While <exception> is included, there is no using for
+ // std::exception -- use qualified.
+ //
+ using std::logic_error;
+ using std::invalid_argument;
+ using std::runtime_error;
+ using std::system_error;
+ using io_error = std::ios_base::failure;
+
+ // <butl/optional>
+ //
+ using butl::optional;
+ using butl::nullopt;
+
+ // ODB smart pointers.
+ //
+ using odb::lazy_shared_ptr;
+ using odb::lazy_weak_ptr;
+
+ // <butl/path>
+ //
+ using butl::path;
+ using butl::dir_path;
+ using butl::basic_path;
+ using butl::invalid_path;
+
+ using paths = std::vector<path>;
+ using dir_paths = std::vector<dir_path>;
+
+ // <butl/timestamp>
+ //
+ using butl::timestamp;
+ using butl::timestamp_nonexistent;
+}
+
+#endif // LIBBREP_TYPES_HXX
diff --git a/libbrep/utility.hxx b/libbrep/utility.hxx
new file mode 100644
index 0000000..f5948dd
--- /dev/null
+++ b/libbrep/utility.hxx
@@ -0,0 +1,34 @@
+// file : libbrep/utility.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBREP_UTILITY_HXX
+#define LIBBREP_UTILITY_HXX
+
+#include <memory> // make_shared()
+#include <string> // to_string()
+#include <utility> // move(), forward(), declval(), make_pair()
+#include <cassert> // assert()
+#include <iterator> // make_move_iterator()
+
+#include <butl/utility> // reverse_iterate(), operator<<(ostream, exception)
+
+namespace brep
+{
+ using std::move;
+ using std::forward;
+ using std::declval;
+
+ using std::make_pair;
+ using std::make_shared;
+ using std::make_move_iterator;
+ using std::to_string;
+
+ // <butl/utility>
+ //
+ using butl::reverse_iterate;
+}
+
+#include <libbrep/version.hxx>
+
+#endif // LIBBREP_UTILITY_HXX
diff --git a/libbrep/version.hxx.in b/libbrep/version.hxx.in
new file mode 100644
index 0000000..e1b60a2
--- /dev/null
+++ b/libbrep/version.hxx.in
@@ -0,0 +1,74 @@
+// file : libbrep/version.hxx.in -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BREP_VERSION // Note: using the version macro itself.
+
+// Note: using build2 standard versioning scheme. The numeric version format
+// is AAABBBCCCDDDE where:
+//
+// AAA - major version number
+// BBB - minor version number
+// CCC - bugfix version number
+// DDD - alpha / beta (DDD + 500) version number
+// E - final (0) / snapshot (1)
+//
+// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example:
+//
+// Version AAABBBCCCDDDE
+//
+// 0.1.0 0000010000000
+// 0.1.2 0000010010000
+// 1.2.3 0010020030000
+// 2.2.0-a.1 0020019990010
+// 3.0.0-b.2 0029999995020
+// 2.2.0-a.1.z 0020019990011
+//
+#define BREP_VERSION $brep.version.project_number$ULL
+#define BREP_VERSION_STR "$brep.version.project$"
+#define BREP_VERSION_ID "$brep.version.project_id$"
+
+#define BREP_VERSION_MAJOR $brep.version.major$
+#define BREP_VERSION_MINOR $brep.version.minor$
+#define BREP_VERSION_PATCH $brep.version.patch$
+
+#define BREP_PRE_RELEASE $brep.version.pre_release$
+
+#define BREP_SNAPSHOT $brep.version.snapshot_sn$ULL
+#define BREP_SNAPSHOT_ID "$brep.version.snapshot_id$"
+
+#include <butl/version>
+
+$libbutl.check(LIBBUTL_VERSION, LIBBUTL_SNAPSHOT)$
+
+#include <bpkg/version>
+
+$libbpkg.check(LIBBPKG_VERSION, LIBBPKG_SNAPSHOT)$
+
+#include <libbbot/version.hxx>
+
+$libbbot.check(LIBBBOT_VERSION, LIBBBOT_SNAPSHOT)$
+
+#include <odb/version.hxx>
+
+$libodb.check(LIBODB_VERSION, LIBODB_SNAPSHOT)$
+
+#include <odb/pgsql/version.hxx>
+
+$libodb-pgsql.check(LIBODB_PGSQL_VERSION, LIBODB_PGSQL_SNAPSHOT)$
+
+// @@ Not really the correct place for the check since we don't use
+// it here.
+//
+/*
+#include <xml/version>
+
+$libstudxml.check(LIBSTUDXML_VERSION, LIBSTUDXML_SNAPSHOT)$
+*/
+
+// For now these are the same.
+//
+#define LIBBREP_VERSION BREP_VERSION
+#define LIBBREP_VERSION_STR BREP_VERSION_STR
+
+#endif // BREP_VERSION
diff --git a/libbrep/wrapper-traits.hxx b/libbrep/wrapper-traits.hxx
new file mode 100644
index 0000000..ac039e3
--- /dev/null
+++ b/libbrep/wrapper-traits.hxx
@@ -0,0 +1,63 @@
+// file : libbrep/wrapper-traits.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef LIBBREP_WRAPPER_TRAITS_HXX
+#define LIBBREP_WRAPPER_TRAITS_HXX
+
+#include <odb/pre.hxx>
+
+#include <butl/optional>
+
+#include <odb/wrapper-traits.hxx>
+
+namespace odb
+{
+ template <typename T>
+ class wrapper_traits<butl::optional<T>>
+ {
+ public:
+ typedef T wrapped_type;
+ typedef butl::optional<T> wrapper_type;
+
+ // T can be const.
+ //
+ typedef
+ typename odb::details::meta::remove_const<T>::result
+ unrestricted_wrapped_type;
+
+ static const bool null_handler = true;
+ static const bool null_default = true;
+
+ static bool
+ get_null (const wrapper_type& o)
+ {
+ return !o;
+ }
+
+ static void
+ set_null (wrapper_type& o)
+ {
+ o = wrapper_type ();
+ }
+
+ static const wrapped_type&
+ get_ref (const wrapper_type& o)
+ {
+ return *o;
+ }
+
+ static unrestricted_wrapped_type&
+ set_ref (wrapper_type& o)
+ {
+ if (!o)
+ o = unrestricted_wrapped_type ();
+
+ return const_cast<unrestricted_wrapped_type&> (*o);
+ }
+ };
+}
+
+#include <odb/post.hxx>
+
+#endif // LIBBREP_WRAPPER_TRAITS_HXX