aboutsummaryrefslogtreecommitdiff
path: root/bpkg/package.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'bpkg/package.hxx')
-rw-r--r--bpkg/package.hxx874
1 files changed, 698 insertions, 176 deletions
diff --git a/bpkg/package.hxx b/bpkg/package.hxx
index f10e670..400519a 100644
--- a/bpkg/package.hxx
+++ b/bpkg/package.hxx
@@ -11,39 +11,33 @@
#include <type_traits> // static_assert
#include <odb/core.hxx>
+#include <odb/section.hxx>
#include <odb/nested-container.hxx>
-#include <libbutl/timestamp.mxx>
+#include <libbutl/timestamp.hxx>
#include <libbpkg/package-name.hxx>
#include <bpkg/types.hxx>
-#include <bpkg/forward.hxx> // transaction
+#include <bpkg/forward.hxx> // database, linked_databases, transaction
#include <bpkg/utility.hxx>
#include <bpkg/diagnostics.hxx>
// Used by the data migration entries.
//
-#define DB_SCHEMA_VERSION_BASE 5
+// NOTE: drop all the `#pragma db member(...) default(...)` pragmas when
+// migration is no longer supported (i.e., the current and base schema
+// versions are the same).
+//
+#define DB_SCHEMA_VERSION_BASE 12
-#pragma db model version(DB_SCHEMA_VERSION_BASE, 7, closed)
+#pragma db model version(DB_SCHEMA_VERSION_BASE, 26, closed)
namespace bpkg
{
- // Compare two lazy pointers via the pointed-to object ids.
- //
- struct compare_lazy_ptr
- {
- template <typename P>
- bool
- operator() (const P& x, const P& y) const
- {
- return x.object_id () < y.object_id ();
- }
- };
-
using optional_string = optional<string>;
+ using optional_uint64_t = optional<uint64_t>; // Preserve uint64_t alias.
// path
//
@@ -67,6 +61,10 @@ namespace bpkg
to((?) ? (?)->string () : bpkg::optional_string ()) \
from((?) ? bpkg::dir_path (*(?)) : bpkg::optional_dir_path ())
+ // uuid
+ //
+ #pragma db map type(uuid) as(string) to((?).string ()) from(bpkg::uuid (?))
+
// timestamp
//
using butl::timestamp;
@@ -80,7 +78,7 @@ namespace bpkg
std::chrono::nanoseconds::period>::value,
"The following timestamp ODB mapping is invalid");
- // As pointed out in libbutl/timestamp.mxx we will overflow in year 2262, but
+ // As pointed out in libbutl/timestamp.hxx 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) \
@@ -106,7 +104,7 @@ namespace bpkg
string upstream;
optional<string> release;
- // @@ TMP: work around MSVC 16.2 bug.
+ // Work around MSVC 16.2 bug.
//
_version () = default;
_version (uint16_t e,
@@ -122,8 +120,6 @@ namespace bpkg
#include <libbpkg/manifest.hxx>
-#include <bpkg/system-repository.hxx>
-
// 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.
@@ -136,6 +132,97 @@ void assert (int);
namespace bpkg
{
+ // Linked bpkg configuration.
+ //
+ // Link with id 0 is the special self-link which captures information about
+ // the current configuration. This information is cached in links of other
+ // configurations.
+ //
+ // Note that linked configurations information will normally be accessed
+ // through the database object functions, which load and cache this
+ // information on the first call. This makes the session support for the
+ // configuration class redundant. Moreover, with the session support
+ // disabled the database implementation can freely move out the data from
+ // the configuration objects into the internal cache and safely load them
+ // from the temporary database objects (see database::attach() for details).
+ //
+ #pragma db object pointer(shared_ptr)
+ class configuration
+ {
+ public:
+ using uuid_type = bpkg::uuid;
+
+ // Link id.
+ //
+ // Zero for the self-link and is auto-assigned for linked configurations
+ // when the object is persisted.
+ //
+ optional_uint64_t id; // Object id.
+
+ uuid_type uuid;
+ optional<string> name;
+ string type;
+ dir_path path; // Empty for the self-link.
+
+ // True if the link is created explicitly by the user rather than
+ // automatically as a backlink.
+ //
+ bool expl;
+
+ // Database mapping.
+ //
+ #pragma db member(id) id auto
+ #pragma db member(uuid) unique
+ #pragma db member(name) unique
+ #pragma db member(path) unique
+ #pragma db member(expl) column("explicit")
+
+ public:
+ // Create the self-link. Generate the UUID, unless specified.
+ //
+ configuration (optional<string> n,
+ string t,
+ optional<uuid_type> uid = nullopt);
+
+ // Create a linked configuration.
+ //
+ configuration (const uuid_type& uid,
+ optional<string> n,
+ string t,
+ dir_path p,
+ bool e)
+ : uuid (uid),
+ name (move (n)),
+ type (move (t)),
+ path (move (p)),
+ expl (e) {}
+
+ // If the configuration path is absolute, then return it as is. Otherwise,
+ // return it completed relative to the specified linked configuration
+ // directory path and then normalized. The specified directory path should
+ // be absolute and normalized. Issue diagnostics and fail on the path
+ // conversion error.
+ //
+ // Note that the self-link object is naturally supported by this function,
+ // since its path is empty.
+ //
+ dir_path
+ effective_path (const dir_path&) const;
+
+ const dir_path&
+ make_effective_path (const dir_path& d)
+ {
+ if (path.relative ())
+ path = effective_path (d);
+
+ return path;
+ }
+
+ private:
+ friend class odb::access;
+ configuration () = default;
+ };
+
// version
//
// Sometimes we need to split the version into two parts: the part
@@ -261,7 +348,7 @@ namespace bpkg
repository_url url;
repository_type type;
- // @@ TMP: work around MSVC 16.2 bug.
+ // Work around MSVC 16.2 bug.
//
_repository_location () = default;
_repository_location (repository_url u, repository_type t)
@@ -328,7 +415,8 @@ namespace bpkg
//
// Also note that these point to repositories, not repository fragments.
//
- using dependencies = std::set<lazy_weak_ptr<repository>, compare_lazy_ptr>;
+ using dependencies = std::set<lazy_weak_ptr<repository>,
+ compare_lazy_ptr_id>;
dependencies complements;
dependencies prerequisites;
@@ -390,11 +478,19 @@ namespace bpkg
optional<string> certificate; // PEM representation.
fragments_type fragments;
+ // While we could potentially calculate this flag on the fly, that would
+ // complicate the database queries significantly.
+ //
+ optional<bool> local; // nullopt for root repository.
+
public:
explicit
repository (repository_location l): location (move (l))
{
name = location.canonical_name ();
+
+ if (!name.empty ()) // Non-root?
+ local = location.local ();
}
// Database mapping.
@@ -422,6 +518,10 @@ namespace bpkg
operator size_t () const {return result;}
};
+ // language
+ //
+ #pragma db value(language) definition
+
// package_location
//
#pragma db value
@@ -444,6 +544,7 @@ namespace bpkg
#pragma db value(version_constraint) definition
#pragma db value(dependency) definition
#pragma db member(dependency::constraint) column("")
+ #pragma db value(dependency_alternative) definition
#pragma db value(dependency_alternatives) definition
// Extend dependency_alternatives to also represent the special test
@@ -464,12 +565,15 @@ namespace bpkg
dependency_alternatives_ex (dependency_alternatives da)
: dependency_alternatives (move (da)) {}
+ // As above but built incrementally.
+ //
+ dependency_alternatives_ex (bool b, std::string c)
+ : dependency_alternatives (b, move (c)) {}
+
// Create the special test dependencies object (built incrementally).
//
- dependency_alternatives_ex (test_dependency_type t)
- : dependency_alternatives (false /* conditional */,
- false /* buildtime */,
- "" /* comment */),
+ dependency_alternatives_ex (test_dependency_type t, bool buildtime)
+ : dependency_alternatives (buildtime, "" /* comment */),
type (t) {}
};
@@ -485,10 +589,41 @@ namespace bpkg
make_move_iterator (das.end ()));
}
+ // Return true if this is a toolchain build-time dependency. If the package
+ // argument is specified and this is a toolchain build-time dependency then
+ // also verify its constraint and fail if it is unsatisfied. Note that the
+ // package argument is used for diagnostics only.
+ //
+ class common_options;
+
+ bool
+ toolchain_buildtime_dependency (const common_options&,
+ const dependency_alternatives&,
+ const package_name*);
+
+ // Return true if any dependency other than toolchain build-time
+ // dependencies is specified. Optionally, verify toolchain build-time
+ // dependencies specifying the package argument which will be used for
+ // diagnostics only.
+ //
+ bool
+ has_dependencies (const common_options&,
+ const dependencies&,
+ const package_name* = nullptr);
+
+ // Return true if some clause that is a buildfile fragment is specified for
+ // any of the dependencies.
+ //
+ template <typename T>
+ bool
+ has_buildfile_clause (const vector<T>& dependencies);
+
// tests
//
#pragma db value(test_dependency) definition
+ #pragma db member(test_dependency::buildtime) default(false)
+
using optional_test_dependency_type = optional<test_dependency_type>;
#pragma db map type(test_dependency_type) as(string) \
@@ -508,6 +643,19 @@ namespace bpkg
//
extern const version wildcard_version;
+ // Return true if the version constraint represents the wildcard version.
+ //
+ inline bool
+ wildcard (const version_constraint& vc)
+ {
+ bool r (vc.min_version && *vc.min_version == wildcard_version);
+
+ if (r)
+ assert (vc.max_version == vc.min_version);
+
+ return r;
+ }
+
// package_name
//
#pragma db value(package_name) type("TEXT") options("COLLATE NOCASE")
@@ -524,17 +672,31 @@ namespace bpkg
available_package_id (package_name, const bpkg::version&);
};
- bool
- operator< (const available_package_id&, const available_package_id&);
+ // buildfile
+ //
+ #pragma db value(buildfile) definition
+
+ // distribution_name_value
+ //
+ #pragma db value(distribution_name_value) definition
#pragma db object pointer(shared_ptr) session
class available_package
{
public:
using version_type = bpkg::version;
+ using upstream_version_type = bpkg::upstream_version;
available_package_id id;
- upstream_version version;
+ upstream_version_type version;
+
+ optional<string> upstream_version;
+ optional<string> type;
+
+ small_vector<language, 1> languages;
+ odb::section languages_section;
+
+ optional<package_name> project;
// List of repository fragments to which this package version belongs
// (yes, in our world, it can be in multiple, unrelated repositories)
@@ -554,8 +716,17 @@ namespace bpkg
// Package manifest data and, potentially, the special test dependencies.
//
- // Note that there can be only one special test dependencies entry in the
- // list and it's always the last one, if present.
+ // Note that there can only be one special test dependencies entry in the
+ // list. It can only be present for a test package and specifies all the
+ // main packages as the alternative dependencies. If present, it is
+ // located right after the last explicit depends clause which specifies a
+ // main package for this test package, if such a clause is present, and as
+ // the first entry otherwise. The idea here is to inject the special
+ // depends clause as early as possible, so that the other clauses could
+ // potentially refer to the reflection variables it may set. But not too
+ // early, so that the explicit main package dependencies are already
+ // resolved by the time of resolving the special clause to avoid the
+ // 'unable to select dependency alternative' error.
//
using dependencies_type = bpkg::dependencies;
@@ -563,6 +734,18 @@ namespace bpkg
small_vector<test_dependency, 1> tests;
+ // Note that while the bootstrap buildfile is always present for stub
+ // packages, we don't save buildfiles for stubs of any kind (can come from
+ // repository, be based on system selected package, etc), leaving *_build
+ // as nullopt and buildfiles empty.
+ //
+ optional<bool> alt_naming;
+ optional<string> bootstrap_build;
+ optional<string> root_build;
+ vector<buildfile> buildfiles;
+
+ vector<distribution_name_value> distribution_values;
+
// Present for non-transient objects only (and only for certain repository
// types).
//
@@ -573,14 +756,31 @@ namespace bpkg
mutable optional<version_type> system_version_;
public:
- // Note: version constraints must be complete.
+ // Note: version constraints must be complete and the bootstrap build must
+ // be present, unless this is a stub.
//
available_package (package_manifest&& m)
: id (move (m.name), m.version),
version (move (m.version)),
+ upstream_version (move (m.upstream_version)),
+ type (move (m.type)),
+ languages (move (m.languages)),
+ project (move (m.project)),
dependencies (convert (move (m.dependencies))),
tests (move (m.tests)),
- sha256sum (move (m.sha256sum)) {}
+ distribution_values (move (m.distribution_values)),
+ sha256sum (move (m.sha256sum))
+ {
+ if (!stub ())
+ {
+ assert (m.bootstrap_build.has_value () && m.alt_naming.has_value ());
+
+ alt_naming = m.alt_naming;
+ bootstrap_build = move (m.bootstrap_build);
+ root_build = move (m.root_build);
+ buildfiles = move (m.buildfiles);
+ }
+ }
// Create available stub package.
//
@@ -600,88 +800,139 @@ namespace bpkg
bool
stub () const {return version.compare (wildcard_version, true) == 0;}
+ string
+ effective_type () const
+ {
+ return package_manifest::effective_type (type, id.name);
+ }
+
+ small_vector<language, 1>
+ effective_languages () const
+ {
+ return package_manifest::effective_languages (languages, id.name);
+ }
+
// Return package system version if one has been discovered. Note that
// we do not implicitly assume a wildcard version.
//
const version_type*
- system_version () const
- {
- if (!system_version_)
- {
- if (const system_package* sp = system_repository.find (id.name))
- {
- // Only cache if it is authoritative.
- //
- if (sp->authoritative)
- system_version_ = sp->version;
- else
- return &sp->version;
- }
- }
-
- return system_version_ ? &*system_version_ : nullptr;
- }
+ system_version (database&) const;
// As above but also return an indication if the version information is
// authoritative.
//
pair<const version_type*, bool>
- system_version_authoritative () const
- {
- const system_package* sp (system_repository.find (id.name));
-
- if (!system_version_)
- {
- if (sp != nullptr)
- {
- // Only cache if it is authoritative.
- //
- if (sp->authoritative)
- system_version_ = sp->version;
- else
- return make_pair (&sp->version, false);
- }
- }
-
- return make_pair (system_version_ ? &*system_version_ : nullptr,
- sp != nullptr ? sp->authoritative : false);
- }
+ system_version_authoritative (database&) const;
// Database mapping.
//
#pragma db member(id) id column("")
#pragma db member(version) set(this.version.init (this.id.version, (?)))
+
+ // languages
+ //
+ #pragma db member(languages) id_column("") value_column("language_") \
+ section(languages_section)
+
+ #pragma db member(languages_section) load(lazy) update(always)
+
+ // locations
+ //
#pragma db member(locations) id_column("") value_column("") \
unordered value_not_null
// dependencies
//
- using _dependency_key = odb::nested_key<dependency_alternatives_ex>;
- using _dependency_alternatives_ex_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")
+ // Note that this is a 2-level nested container which is mapped to three
+ // container tables each containing data of each dimension.
+ // Container of the dependency_alternatives_ex values.
+ //
#pragma db member(dependencies) id_column("") value_column("")
- #pragma db member(dependency_alternatives_ex) \
- table("available_package_dependency_alternatives") \
- virtual(_dependency_alternatives_ex_type) \
+
+ // Container of the dependency_alternative values.
+ //
+ using _dependency_alternative_key =
+ odb::nested_key<dependency_alternatives_ex>;
+
+ using _dependency_alternatives_type =
+ std::map<_dependency_alternative_key, dependency_alternative>;
+
+ #pragma db value(_dependency_alternative_key)
+ #pragma db member(_dependency_alternative_key::outer) column("dependency_index")
+ #pragma db member(_dependency_alternative_key::inner) column("index")
+
+ #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("")
+
+ // Container of the dependency values.
+ //
+ using _dependency_key = odb::nested2_key<dependency_alternatives_ex>;
+ using _dependency_alternative_dependencies_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::middle) column("alternative_index")
+ #pragma db member(_dependency_key::inner) column("index")
+
+ #pragma db member(dependency_alternative_dependencies) \
+ virtual(_dependency_alternative_dependencies_type) \
+ after(dependency_alternatives) \
+ get(odb::nested2_get (this.dependencies)) \
+ set(odb::nested2_set (this.dependencies, std::move (?))) \
id_column("") key_column("") value_column("dep_")
// tests
//
#pragma db member(tests) id_column("") value_column("test_")
+ // distribution_values
+ //
+ #pragma db member(distribution_values) id_column("") value_column("dist_")
+
+ // alt_naming
+ //
+ // Note that since no real packages with alternative buildfile naming use
+ // conditional dependencies yet, we can just set alt_naming to false
+ // during migration to the database schema version 20. Also we never rely
+ // on alt_naming to be nullopt for the stub packages, so let's not
+ // complicate things and set alt_naming to false for them either.
+ //
+ #pragma db member(alt_naming) default(false)
+
+ // *_build
+ //
+ // Note that since no real packages use conditional dependencies yet, we
+ // can just set bootstrap_build to the empty string during migration to
+ // the database schema version 15. Also we never rely on bootstrap_build
+ // to be nullopt for the stub packages, so let's not complicate things and
+ // set bootstrap_build to the empty string for them either.
+ //
+ #pragma db member(bootstrap_build) default("")
+
+ // buildfiles
+ //
+ #pragma db member(buildfiles) id_column("") value_column("")
+
private:
friend class odb::access;
available_package () = default;
};
+ // The available packages together with the repository fragments they belong
+ // to.
+ //
+ // Note that lazy_shared_ptr is used to also convey the databases the
+ // objects belong to.
+ //
+ using available_packages = vector<pair<shared_ptr<available_package>,
+ lazy_shared_ptr<repository_fragment>>>;
+
#pragma db view object(available_package)
struct available_package_count
{
@@ -698,7 +949,7 @@ namespace bpkg
// DISTINCT clause is not required.
//
#pragma db view object(available_package = package) \
- table("available_package_dependencies" = "pd" inner: \
+ table("main.available_package_dependencies" = "pd" inner: \
"pd.type IN ('tests', 'examples', 'benchmarks') AND " \
"pd.name = " + package::id.name + "AND" + \
"pd.version_epoch = " + package::id.version.epoch + "AND" + \
@@ -717,7 +968,7 @@ namespace bpkg
// external test packages.
//
#pragma db view object(available_package = package) \
- table("available_package_tests" = "pt" inner: \
+ table("main.available_package_tests" = "pt" inner: \
"pt.name = " + package::id.name + "AND" + \
"pt.version_epoch = " + package::id.version.epoch + "AND" + \
"pt.version_canonical_upstream = " + \
@@ -732,55 +983,18 @@ namespace bpkg
shared_ptr<available_package> package;
};
- // Query the available packages that optionally satisfy the specified
- // version constraint and return them in the version descending order, by
- // default. Note that a stub satisfies any constraint.
- //
- odb::result<available_package>
- query_available (database&,
- const package_name&,
- const optional<version_constraint>&,
- bool order = true);
-
- // Only return packages that are in the specified repository fragments, their
- // complements or prerequisites (if prereq is true), recursively. While you
- // could maybe come up with a (barely comprehensible) view/query to achieve
- // this, doing it on the "client side" is definitely more straightforward.
- //
- vector<shared_ptr<available_package>>
- filter (const shared_ptr<repository_fragment>&,
- odb::result<available_package>&&,
- bool prereq = true);
-
- pair<shared_ptr<available_package>, shared_ptr<repository_fragment>>
- filter_one (const shared_ptr<repository_fragment>&,
- odb::result<available_package>&&,
- bool prereq = true);
-
- shared_ptr<repository_fragment>
- filter (const shared_ptr<repository_fragment>&,
- const shared_ptr<available_package>&,
- bool prereq = true);
-
- vector<pair<shared_ptr<available_package>, shared_ptr<repository_fragment>>>
- filter (const vector<shared_ptr<repository_fragment>>&,
- odb::result<available_package>&&,
- bool prereq = true);
-
- pair<shared_ptr<available_package>, shared_ptr<repository_fragment>>
- filter_one (const vector<shared_ptr<repository_fragment>>&,
- odb::result<available_package>&&,
- bool prereq = true);
-
- // Check if there are packages available in the configuration. If that's not
- // the case then print the info message into the diag record or, if it is
- // NULL, print the error message and fail.
+ // Check if there are packages available in the specified configurations. If
+ // that's not the case then print the info message into the diag record or,
+ // if it is NULL, print the error message and fail.
//
void
- check_any_available (const dir_path& configuration,
+ check_any_available (const linked_databases&,
transaction&,
const diag_record* = nullptr);
+ void
+ check_any_available (database&, transaction&, const diag_record* = nullptr);
+
// package_state
//
enum class package_state
@@ -854,17 +1068,97 @@ namespace bpkg
const optional<version_constraint>&,
bool system = false);
+ // Return true if the package is a build2 build system module.
+ //
+ inline bool
+ build2_module (const package_name& name)
+ {
+ return name.string ().compare (0, 10, "libbuild2-") == 0;
+ }
+
// A map of "effective" prerequisites (i.e., pointers to other selected
- // packages) to optional version constraint. Note that because it is a
- // single constraint, we don't support multiple dependencies on the same
- // package (e.g., two ranges of versions). See pkg_configure().
+ // packages) to optional version constraint (plus some other info). Note
+ // that because it is a single constraint, we don't support multiple
+ // dependencies on the same package (e.g., two ranges of versions). See
+ // pkg_configure().
+ //
+ // Note also that the pointer can refer to a selected package in another
+ // database.
//
class selected_package;
+ #pragma db value
+ struct prerequisite_info
+ {
+ // The "tightest" version constraint among all dependencies resolved to
+ // this prerequisite.
+ //
+ optional<version_constraint> constraint;
+
+ // Database mapping.
+ //
+ #pragma db member(constraint) column("")
+ };
+
+ // Note that the keys for this map need to be created with the database
+ // passed to their constructor, which is required for persisting them (see
+ // _selected_package_ref() implementation for details).
+ //
using package_prerequisites = std::map<lazy_shared_ptr<selected_package>,
- optional<version_constraint>,
+ prerequisite_info,
compare_lazy_ptr>;
+ // Database mapping for lazy_shared_ptr<selected_package> to configuration
+ // UUID and package name.
+ //
+ #pragma db value
+ struct _selected_package_ref
+ {
+ using ptr = lazy_shared_ptr<selected_package>;
+
+ uuid configuration;
+ package_name prerequisite;
+
+ explicit
+ _selected_package_ref (const ptr&);
+
+ _selected_package_ref () = default;
+
+ ptr
+ to_ptr (odb::database&) &&;
+
+ #pragma db member(configuration)
+ };
+
+ #pragma db map type(_selected_package_ref::ptr) \
+ as(_selected_package_ref) \
+ to(bpkg::_selected_package_ref (?)) \
+ from(std::move (?).to_ptr (*db))
+
+ enum class config_source
+ {
+ user, // User configuration specified on command line.
+ dependent, // Dependent-imposed configuration from prefer/require clauses.
+ reflect // Package-reflected configuration from reflect clause.
+ };
+
+ string
+ to_string (config_source);
+
+ config_source
+ to_config_source (const string&); // May throw std::invalid_argument.
+
+ #pragma db map type(config_source) as(string) \
+ to(to_string (?)) \
+ from(bpkg::to_config_source (?))
+
+ #pragma db value
+ struct config_variable
+ {
+ string name;
+ config_source source;
+ };
+
#pragma db object pointer(shared_ptr) session
class selected_package
{
@@ -912,27 +1206,65 @@ namespace bpkg
optional<dir_path> src_root;
bool purge_src;
- // The checksum of the manifest file located in the source directory.
+ // The checksum of the manifest file located in the source directory and
+ // the subproject set. Changes to this information should trigger the
+ // package version revision increment. In particular, new subprojects
+ // should trigger the package reconfiguration.
//
- // Must be present if the source directory is present, unless the object
- // is created/updated during the package build simulation (see pkg-build
- // for details). Note that during the simulation the manifest may not be
+ // Only present for external packages, unless the objects are
+ // created/updated during the package build simulation (see pkg-build for
+ // details). Note that during the simulation the manifest may not be
// available.
//
+ // @@ Currently we don't consider subprojects recursively (would most
+ // likely require extension to b info, also could be a performance
+ // concern).
+ //
+ // @@ We should probably rename it if/when ODB add support for that for
+ // SQlite.
+ //
optional<std::string> manifest_checksum;
- // Path to the output directory of this package, if any. It is
- // always relative to the configuration directory, and is <name>
- // for external packages and <name>-<version> for others. It is
- // only set once the package is configured and its main purse is
- // to keep track of what needs to be cleaned by the user before
- // a broken package can be purged. Note that it could be the
- // same as src_root.
+ // Only present for external packages which have buildfile clauses in the
+ // dependencies, unless the objects are created/updated during the package
+ // build simulation (see pkg-build for details).
+ //
+ // Note that the checksum is always calculated over the files rather than
+ // the *-build manifest values. This is "parallel" to the package skeleton
+ // logic.
+ //
+ optional<std::string> buildfiles_checksum;
+
+ // Path to the output directory of this package, if any. It is always
+ // relative to the configuration directory, and is <name> for external
+ // packages and <name>-<version> for others. It is only set once the
+ // package is configured and its main purpose is to keep track of what
+ // needs to be cleaned by the user before a broken package can be
+ // purged. Note that it could be the same as src_root.
//
optional<dir_path> out_root;
package_prerequisites prerequisites;
+ // 1-based indexes of the selected dependency alternatives which the
+ // prerequisite packages are resolved from. Parallel to the dependencies
+ // member of the respective available package. Entries which don't
+ // correspond to a selected alternative (toolchain build-time dependency,
+ // not enabled alternatives, etc) are set to 0.
+ //
+ using indexes_type = vector<size_t>; // Make sure ODB maps it portably.
+ indexes_type dependency_alternatives;
+ odb::section dependency_alternatives_section;
+
+ // Project configuration variable names and their sources.
+ //
+ vector<config_variable> config_variables;
+
+ // SHA256 checksum of variables (names and values) referred to by the
+ // config_variables member.
+ //
+ std::string config_checksum;
+
public:
bool
system () const
@@ -956,18 +1288,39 @@ namespace bpkg
// pkg-unpack --existing <dir>
//
- (repository_fragment.empty () && !archive);
+ // Note that the system package can have no repository associated (see
+ // imaginary system repository in pkg-build.cxx for details).
+ //
+ (repository_fragment.empty () && !archive && !system ());
}
// Represent the wildcard version with the "*" string. Represent naturally
// all other versions.
//
std::string
- version_string () const;
+ version_string () const
+ {
+ return version != wildcard_version ? version.string () : "*";
+ }
std::string
string () const {return package_string (name, version, system ());}
+ std::string
+ string (database&) const;
+
+ // Return the relative archive path completed using the configuration
+ // directory. Return the absolute archive path as is.
+ //
+ path
+ effective_archive (const dir_path& configuration) const
+ {
+ // Cast for compiling with ODB (see above).
+ //
+ assert (static_cast<bool> (archive));
+ return archive->absolute () ? *archive : configuration / *archive;
+ }
+
// Return the relative source directory completed using the configuration
// directory. Return the absolute source directory as is.
//
@@ -980,8 +1333,7 @@ namespace bpkg
return src_root->absolute () ? *src_root : configuration / *src_root;
}
- // Return the output directory using the configuration directory. Note
- // that the output directory is always relative.
+ // Return the output directory using the configuration directory.
//
dir_path
effective_out_root (const dir_path& configuration) const
@@ -989,6 +1341,9 @@ namespace bpkg
// Cast for compiling with ODB (see above).
//
assert (static_cast<bool> (out_root));
+
+ // Note that out_root is always relative.
+ //
return configuration / *out_root;
}
@@ -996,8 +1351,23 @@ namespace bpkg
//
#pragma db member(name) id
- #pragma db member(prerequisites) id_column("package") \
- key_column("prerequisite") key_not_null value_column("")
+ #pragma db member(prerequisites) id_column("package") \
+ key_column("") value_column("")
+
+ #pragma db member(dependency_alternatives) id_column("package") \
+ value_column("position") section(dependency_alternatives_section)
+
+ #pragma db member(dependency_alternatives_section) load(lazy) update(always)
+
+ #pragma db member(config_variables) id_column("package") value_column("")
+
+ // For the sake of simplicity let's not calculate the checksum during
+ // migration. It seems that the only drawback of this approach is a
+ // (single) spurious reconfiguration of a dependency of a dependent with
+ // configuration clause previously configured by bpkg with the database
+ // schema version prior to 24.
+ //
+ #pragma db member(config_checksum) default("")
// Explicit aggregate initialization for C++20 (private default ctor).
//
@@ -1013,6 +1383,7 @@ namespace bpkg
optional<dir_path> sr,
bool ps,
optional<std::string> mc,
+ optional<std::string> bc,
optional<dir_path> o,
package_prerequisites pps)
: name (move (n)),
@@ -1027,6 +1398,7 @@ namespace bpkg
src_root (move (sr)),
purge_src (ps),
manifest_checksum (move (mc)),
+ buildfiles_checksum (move (bc)),
out_root (move (o)),
prerequisites (move (pps)) {}
@@ -1041,23 +1413,49 @@ namespace bpkg
return os << p.string ();
}
+ // Create a transient (or fake, if you prefer) available_package object
+ // corresponding to the specified selected object, which is expected to not
+ // be in the broken state. Note that the package locations list is left
+ // empty.
+ //
+ shared_ptr<available_package>
+ make_available (const common_options&,
+ database&,
+ const shared_ptr<selected_package>&);
+
+ // Try to find a dependency in the dependency configurations (see
+ // database::dependency_configs() for details). Return pointers to the found
+ // package and the configuration it belongs to. Return a pair of NULLs if no
+ // package is found and issue diagnostics and fail if multiple packages (in
+ // multiple configurations) are found.
+ //
+ pair<shared_ptr<selected_package>, database*>
+ find_dependency (database&, const package_name&, bool buildtime);
+
// Check if the directory containing the specified package version should be
// considered its iteration. Return the version of this iteration if that's
// the case and nullopt otherwise.
//
+ // Pass the build2 project info for the package, if available, to speed up
+ // the call and NULL otherwise (in which case it will be queried by the
+ // implementation). In the former case it is assumed that the package info
+ // has been retrieved with the b_info_flags::subprojects flag.
+ //
// Notes:
//
// - The package directory is considered an iteration of the package if this
// upstream version and revision is already present (selected) in the
- // configuration and has a source directory. If that's the case, then the
- // specified directory path and the checksum of the manifest file it
- // contains are compared to the ones of the package present in the
- // configuration. If both match, then the present package version
- // (including its iteration, if any) is returned. Otherwise (the package
- // has moved and/or the packaging information has changed), the present
- // package version with the incremented iteration number is returned. Note
- // that the directory path is matched only for the external selected
- // packages.
+ // configuration and has a source directory. If that's the case and if the
+ // present version is not external (the package is being switched to a
+ // local potentially amended version), then the present package version
+ // with the incremented iteration number is returned. Otherwise (the
+ // present package is external), the specified directory path and the
+ // package checksum (see package_checksum() for details) are compared to
+ // the ones of the package present in the configuration. If both match,
+ // then the present package version (including its iteration, if any) is
+ // returned. Otherwise (the package has moved and/or the package
+ // information has changed), the present package version with the
+ // incremented iteration number is returned.
//
// - Only a single package iteration is valid per version in the
// configuration. This, in particular, means that a package of the
@@ -1071,15 +1469,16 @@ namespace bpkg
// - The manifest file located in the specified directory is not parsed, and
// so is not checked to match the specified package name and version.
//
- class common_options;
-
+ // Note: loads selected packages.
+ //
optional<version>
package_iteration (const common_options&,
- const dir_path& configuration,
+ database&,
transaction&,
const dir_path&,
const package_name&,
const version&,
+ const package_info*,
bool check_external);
// certificate
@@ -1171,25 +1570,22 @@ namespace bpkg
// Return a list of packages that depend on this package along with
// their constraints.
//
+ // @@ Using raw container table since ODB doesn't support containers in
+ // views yet.
+ //
/*
- #pragma db view object(selected_package) \
- container(selected_package::prerequisites = pp inner: pp.key)
+ #pragma db view container(selected_package::prerequisites = pp)
struct package_dependent
{
- #pragma db column(pp.id)
- string name;
+ #pragma db column("pp.package")
+ package_name name;
- #pragma db column(pp.value)
+ #pragma db column("pp.")
optional<version_constraint> constraint;
};
*/
- // @@ Using raw container table since ODB doesn't support containers
- // in views yet.
- //
- #pragma db view object(selected_package) \
- table("selected_package_prerequisites" = "pp" inner: \
- "pp.prerequisite = " + selected_package::name)
+ #pragma db view table("main.selected_package_prerequisites" = "pp")
struct package_dependent
{
#pragma db column("pp.package")
@@ -1199,9 +1595,128 @@ namespace bpkg
optional<version_constraint> constraint;
};
+ // In the specified database query dependents of a dependency that resided
+ // in a potentially different database (yeah, it's a mouthful).
+ //
+ odb::result<package_dependent>
+ query_dependents (database& dependent_db,
+ const package_name& dependency,
+ database& dependency_db);
+
+ // As above but cache the result in a vector. This version should be used if
+ // query_dependents*() may be called recursively.
+ //
+ vector<package_dependent>
+ query_dependents_cache (database&, const package_name&, database&);
+
+ // Database and package name pair.
+ //
+ // It is normally used as a key for maps containing data for packages across
+ // multiple linked configurations. Assumes that the respective databases are
+ // not detached during such map lifetimes. Considers both package name and
+ // database for objects comparison.
+ //
+ struct package_key
+ {
+ reference_wrapper<database> db;
+ package_name name;
+
+ package_key (database& d, package_name n): db (d), name (move (n)) {}
+
+ bool
+ operator== (const package_key& v) const
+ {
+ // See operator==(database, database).
+ //
+ return name == v.name && &db.get () == &v.db.get ();
+ }
+
+ bool
+ operator!= (const package_key& v) const
+ {
+ return !(*this == v);
+ }
+
+ bool
+ operator< (const package_key&) const;
+
+ // Return the package string representation in the form:
+ //
+ // <name>[ <config-dir>]
+ //
+ std::string
+ string () const;
+ };
+
+ inline ostream&
+ operator<< (ostream& os, const package_key& p)
+ {
+ return os << p.string ();
+ }
+
+ // Database, package name, and package version.
+ //
+ // It is normally used as a key for maps containing data for package
+ // versions across multiple linked configurations. Assumes that the
+ // respective databases are not detached during such map lifetimes.
+ // Considers all package name, package version, and database for objects
+ // comparison.
+ //
+ // The package name can be a pseudo-package (command line as a dependent,
+ // etc), in which case the version is absent. The version can also be empty,
+ // denoting a package of an unknown version.
+ //
+ struct package_version_key
+ {
+ reference_wrapper<database> db;
+ package_name name;
+ optional<bpkg::version> version;
+
+ package_version_key (database& d, package_name n, bpkg::version v)
+ : db (d), name (move (n)), version (move (v)) {}
+
+ // Create a pseudo-package (command line as a dependent, etc).
+ //
+ package_version_key (database& d, string n)
+ : db (d),
+ name (move (n), package_name::raw_string) {}
+
+ bool
+ operator== (const package_version_key& v) const
+ {
+ // See operator==(database, database).
+ //
+ return name == v.name &&
+ version == v.version &&
+ &db.get () == &v.db.get ();
+ }
+
+ bool
+ operator!= (const package_version_key& v) const
+ {
+ return !(*this == v);
+ }
+
+ bool
+ operator< (const package_version_key&) const;
+
+ // Return the package string representation in the form:
+ //
+ // <name>[/<version>] [ <config-dir>]
+ //
+ std::string
+ string (bool ignore_version = false) const;
+ };
+
+ inline ostream&
+ operator<< (ostream& os, const package_version_key& p)
+ {
+ return os << p.string ();
+ }
+
// Return a count of repositories that contain this repository fragment.
//
- #pragma db view table("repository_fragments")
+ #pragma db view table("main.repository_fragments")
struct fragment_repository_count
{
#pragma db column("count(*)")
@@ -1213,7 +1728,7 @@ namespace bpkg
// Return a list of repositories that contain this repository fragment.
//
#pragma db view object(repository) \
- table("repository_fragments" = "rfs" inner: \
+ table("main.repository_fragments" = "rfs" inner: \
"rfs.repository = " + repository::name) \
object(repository_fragment inner: "rfs.fragment = " + \
repository_fragment::name)
@@ -1228,7 +1743,7 @@ namespace bpkg
// complement.
//
#pragma db view object(repository = complement) \
- table("repository_fragment_complements" = "rfc" inner: \
+ table("main.repository_fragment_complements" = "rfc" inner: \
"rfc.complement = " + complement::name) \
object(repository_fragment inner: "rfc.repository_fragment = " + \
repository_fragment::name)
@@ -1243,7 +1758,7 @@ namespace bpkg
// prerequisite.
//
#pragma db view object(repository = prerequisite) \
- table("repository_fragment_prerequisites" = "rfp" inner: \
+ table("main.repository_fragment_prerequisites" = "rfp" inner: \
"rfp.prerequisite = " + prerequisite::name) \
object(repository_fragment inner: "rfp.repository_fragment = " + \
repository_fragment::name)
@@ -1257,7 +1772,7 @@ namespace bpkg
// Return a list of packages available from this repository fragment.
//
#pragma db view object(repository_fragment) \
- table("available_package_locations" = "pl" inner: \
+ table("main.available_package_locations" = "pl" inner: \
"pl.repository_fragment = " + repository_fragment::name) \
object(available_package = package inner: \
"pl.name = " + package::id.name + "AND" + \
@@ -1278,7 +1793,7 @@ namespace bpkg
// Return a list of repository fragments the packages come from.
//
#pragma db view object(repository_fragment) \
- table("available_package_locations" = "pl" inner: \
+ table("main.available_package_locations" = "pl" inner: \
"pl.repository_fragment = " + repository_fragment::name) \
object(available_package = package inner: \
"pl.name = " + package::id.name + "AND" + \
@@ -1464,6 +1979,13 @@ namespace bpkg
}
*/
+ inline bool
+ operator< (const available_package_id& x, const available_package_id& y)
+ {
+ int r (x.name.compare (y.name));
+ return r != 0 ? r < 0 : x.version < y.version;
+ }
+
template <typename T1, typename T2>
inline auto
compare_version_gt (const T1& x, const T2& y, bool revision, bool iteration)