aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2023-01-17 12:53:18 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2023-01-17 12:53:18 +0200
commitd1f58962fa9953a9ed0d2c72be5d86f3d6605804 (patch)
treec6ea6f67ada08e3eb470710a2eda96676aebedc0
parente9fadc9dc3f75131d8765e4d1c621f26909fd5b6 (diff)
Switch to multi-version interface, start Debian implementation
-rw-r--r--bpkg/system-package-manager-debian.cxx88
-rw-r--r--bpkg/system-package-manager-debian.hxx9
-rw-r--r--bpkg/system-package-manager.cxx51
-rw-r--r--bpkg/system-package-manager.hxx107
4 files changed, 189 insertions, 66 deletions
diff --git a/bpkg/system-package-manager-debian.cxx b/bpkg/system-package-manager-debian.cxx
index 1e43206..21b7e04 100644
--- a/bpkg/system-package-manager-debian.cxx
+++ b/bpkg/system-package-manager-debian.cxx
@@ -25,4 +25,92 @@ namespace bpkg
// (e.g., openssl-devel) so probably should track the status of
// individual system packages. What if we "installed any version"
// first and then need to install specific?
+
+ auto system_package_manager_debian::
+ pkg_status (const package_name& pn,
+ const available_packages* aps,
+ bool install,
+ bool fetch) -> const vector<package_status>*
+ {
+ // First check the cache.
+ //
+ {
+ auto i (status_cache_.find (pn));
+
+ if (i != status_cache_.end ())
+ return &i->second;
+
+ if (aps == nullptr)
+ return nullptr;
+ }
+
+ // Translate our package name to the system package names.
+ //
+ strings spns (system_package_names (*aps,
+ os_release_.name_id,
+ os_release_.version_id,
+ os_release_.like_ids));
+
+ // @@ TODO: fallback to our package name if empty (plus -dev if lib).
+ // @@ TODO: split into packages/components
+
+ vector<package_status> r;
+
+ // First look for an already installed package.
+ //
+
+
+ // Next look for available versions if we are allowed to install.
+ //
+ // We only do this if we don't have a package already installed. This is
+ // because while Debian generally supports installing multiple versions in
+ // parallel, this is unlikely to be supported for the packages we are
+ // interested in due to the underlying limitations.
+ //
+ // Specifically, the packages that we are primarily interested in are
+ // libraries with headers and executables (tools). While Debian is able to
+ // install multiple libraries in parallel, it normally can only install a
+ // single set of headers, static libraries, pkg-config files, etc., (i.e.,
+ // the -dev package) at a time due to them being installed into the same
+ // location (e.g., /usr/include). The same holds for executables, which
+ // are installed into the same location (e.g., /usr/bin).
+ //
+ // It is possible that a certain library has made arrangements for
+ // multiple of its versions to co-exist. For example, hypothetically, our
+ // libssl package could be mapped to both libssl1.1 libssl1.1-dev and
+ // libssl3 libssl3-dev which could be installed at the same time (note
+ // that it is not the case in reality; there is only libssl-dev). However,
+ // in this case, we should probably also have two packages with separate
+ // names (e.g., libssl and libssl3) that can also co-exist. An example of
+ // this would be libQt5Core and libQt6Core. (Note that strictly speaking
+ // there could be different degrees of co-existence: for the system
+ // package manager it is sufficient for different versions not to clobber
+ // each other's files while for us we may also need the ability to use
+ // different versions in the base build).
+ //
+ // Note also that the above reasoning is quite C/C++-centric and it's
+ // possible that multiple versions of libraries (or equivalent) for other
+ // languages (e.g., Rust) can always co-exist. In this case we may need to
+ // revise this decision.
+ //
+ if (install && r.empty ())
+ {
+ if (fetch && !fetched_)
+ {
+ // @@ TODO: apt-get update
+
+ fetched_ = true;
+ }
+ }
+
+ // Cache.
+ //
+ return &status_cache_.emplace (pn, move (r)).first->second;
+ }
+
+ bool system_package_manager_debian::
+ pkg_install (const package_name&, const version&)
+ {
+ return false;
+ }
}
diff --git a/bpkg/system-package-manager-debian.hxx b/bpkg/system-package-manager-debian.hxx
index 5b1f764..ed74db0 100644
--- a/bpkg/system-package-manager-debian.hxx
+++ b/bpkg/system-package-manager-debian.hxx
@@ -17,18 +17,25 @@ namespace bpkg
class system_package_manager_debian: public system_package_manager
{
public:
- virtual optional<const package_status*>
+ virtual const vector<package_status>*
pkg_status (const package_name&,
const available_packages*,
bool install,
bool fetch) override;
+ virtual bool
+ pkg_install (const package_name&, const version&) override;
+
public:
+ // Note: expects os_release::name_id to be "debian" or os_release::like_id
+ // to contain "debian".
+ //
explicit
system_package_manager_debian (os_release&& osr)
: system_package_manager (move (osr)) {}
protected:
+ bool fetched_ = false; // True if already fetched metadata.
};
}
diff --git a/bpkg/system-package-manager.cxx b/bpkg/system-package-manager.cxx
index 215ea47..d535140 100644
--- a/bpkg/system-package-manager.cxx
+++ b/bpkg/system-package-manager.cxx
@@ -31,16 +31,27 @@ namespace bpkg
if (optional<os_release> osr = host_os_release (host))
{
+ auto is_or_like = [&osr] (const char* id)
+ {
+ return (osr->name_id == id ||
+ find_if (osr->like_ids.begin (), osr->like_ids.end (),
+ [id] (const string& n)
+ {
+ return n == id;
+ }) != osr->like_ids.end ());
+ };
+
if (host.class_ == "linux")
{
- if (osr->name_id == "debian" ||
- osr->name_id == "ubuntu" ||
- find_if (osr->like_ids.begin (), osr->like_ids.end (),
- [] (const string& n)
- {
- return n == "debian" || n == "ubuntu";
- }) != osr->like_ids.end ())
+ if (is_or_like ("debian") ||
+ is_or_like ("ubuntu"))
{
+ // If we recognized this as Debian-like in an ad hoc manner, then
+ // add debian to like_ids.
+ //
+ if (osr->name_id != "debian" && !is_or_like ("debian"))
+ osr->like_ids.push_back ("debian");
+
// @@ TODO: verify name if specified.
// @@ TMP
@@ -64,7 +75,7 @@ namespace bpkg
system_package_names (const available_packages& aps,
const string& name_id,
const string& version_id,
- const string& like_id)
+ const vector<string>& like_ids)
{
assert (!aps.empty ());
@@ -169,20 +180,30 @@ namespace bpkg
//
values vs (name_values (name_id, vid));
- // If the resulting list is empty and the like id is specified, then
+ // If the resulting list is empty and the like ids are specified, then
// re-collect but now using the like id and "0" version id instead.
//
- if (vs.empty () && !like_id.empty ())
- vs = name_values (like_id, semantic_version (0, 0, 0));
+ if (vs.empty ())
+ {
+ for (const string& like_id: like_ids)
+ {
+ vs = name_values (like_id, semantic_version (0, 0, 0));
+ if (!vs.empty ())
+ break;
+ }
+ }
// Return the values of the collected name/values list.
//
strings r;
- r.reserve (vs.size ());
+ if (size_t n = vs.size ())
+ {
+ r.reserve (n);
- transform (vs.begin (), vs.end (),
- back_inserter (r),
- [] (const distribution_name_value& v) {return v.value;});
+ transform (vs.begin (), vs.end (),
+ back_inserter (r),
+ [] (const distribution_name_value& v) {return v.value;});
+ }
return r;
}
diff --git a/bpkg/system-package-manager.hxx b/bpkg/system-package-manager.hxx
index fb90e2c..78b157a 100644
--- a/bpkg/system-package-manager.hxx
+++ b/bpkg/system-package-manager.hxx
@@ -4,6 +4,8 @@
#ifndef BPKG_SYSTEM_PACKAGE_MANAGER_HXX
#define BPKG_SYSTEM_PACKAGE_MANAGER_HXX
+#include <map>
+
#include <libbpkg/manifest.hxx> // version
#include <libbpkg/package-name.hxx>
@@ -31,6 +33,10 @@ namespace bpkg
// "available partially installed" (for example, libfoo but not
// libfoo-dev is installed) or "available not yet installed".
//
+ // Whether not_installed versions can be returned along with installed
+ // or partially_installed depends on whether the packager manager can
+ // install multiple versions side-by-side.
+ //
enum {installed, partially_installed, not_installed} status;
// System (as in, distribution package) name and version.
@@ -40,6 +46,32 @@ namespace bpkg
string system_name;
string system_version;
*/
+
+ // Package manager implementation-specific data.
+ //
+ public:
+ using data_ptr = unique_ptr<void, void (*) (void*)>;
+
+ template <typename T>
+ T&
+ data () { return *static_cast<T*> (data_.get ()); }
+
+ template <typename T>
+ const T&
+ data () const { return *static_cast<const T*> (data_.get ()); }
+
+ template <typename T>
+ T&
+ data (T* d)
+ {
+ data_ = data_ptr (d, [] (void* p) { delete static_cast<T*> (p); });
+ return *d;
+ }
+
+ static void
+ null_data_deleter (void* p) { assert (p == nullptr); }
+
+ data_ptr data_ = {nullptr, null_data_deleter};
};
// Query the system package status.
@@ -47,62 +79,36 @@ namespace bpkg
// This function has two modes: cache-only (available_packages is NULL)
// and full (available_packages is not NULL). In the cache-only mode this
// function returns the status of this package if it has already been
- // queried and nullopt otherwise. This allows the caller to only collect
- // all the available packages (for the name/version mapping information)
- // if really necessary.
+ // queried and NULL otherwise. This allows the caller to only collect all
+ // the available packages (for the name/version mapping information) if
+ // really necessary.
//
- // The returned value can be NULL, which indicates that no such package is
- // available from the system package manager. Note that NULL is returned
- // if no fully installed package is available from the system and install
- // is false.
+ // The returned value can be empty, which indicates that no such package
+ // is available from the system package manager. Note that empty is also
+ // returned if no fully installed package is available from the system and
+ // the install argument is false.
//
// If fetch is false, then do not re-fetch the system package repository
// metadata (that is, available packages/versions) before querying for
// the available version of the not yet installed or partially installed
// packages.
//
- // Note that currently the result is a single available version. While
- // some package managers may support installing multiple versions in
- // parallel, this is unlikely to be suppored for the packages we are
- // interested in due to the underlying limitations.
- //
- // Specifically, the packages that we are primarily interested in are
- // libraries with headers and executables (tools). While most package
- // managers (e.g., Debian, Fedora) are able to install multiple libraries
- // in parallel, they normally can only install a single set of headers,
- // static libraries, pkg-config files, etc., (e.g., -dev/-devel package)
- // at a time due to them being installed into the same location (e.g.,
- // /usr/include). The same holds for executables, which are installed into
- // the same location (e.g., /usr/bin).
- //
- // @@ But it's still plausible to have multiple available versions but
- // only being able to install one at a time?
- //
- // It is possible that a certain library has made arrangements for
- // multiple of its versions to co-exist. For example, hypothetically, our
- // libssl package could be mapped to both libssl1.1 libssl1.1-dev and
- // libssl3 libssl3-dev which could be installed at the same time (note
- // that it is not the case in reality; there is only libssl-dev). However,
- // in this case, we should probably also have two packages with separate
- // names (e.g., libssl and libssl3) that can also co-exist. An example of
- // this would be libQt5Core and libQt6Core. (Note that strictly speaking
- // there could be different degrees of co-existence: for the system
- // package manager it is sufficient for different versions not to clobber
- // each other's files while for us we may also need the ability to use
- // different versions in the base build).
- //
- // Note also that the above reasoning is quite C/C++-centric and it's
- // possible that multiple versions of libraries (or equivalent) for other
- // languages (e.g., Rust) can always co-exist. In this case we may need to
- // revise this decision to only return a single version (and pick the best
- // suitable version as part of the constraint resolution).
- //
- virtual optional<const package_status*>
+ virtual const vector<package_status>*
pkg_status (const package_name&,
const available_packages*,
bool install,
bool fetch) = 0;
+ // Install the previously-queried package that is not installed or
+ // partially installed.
+ //
+ // Return false if the installation was aborted by the user (for example,
+ // the user answered 'N' to the prompt). @@ Do we really need this? We
+ // may not always be able to distinguish.
+ //
+ virtual bool
+ pkg_install (const package_name&, const version&) = 0;
+
public:
virtual
~system_package_manager ();
@@ -115,9 +121,9 @@ namespace bpkg
// Given the available packages (as returned by find_available_all())
// return the list of system package names.
//
- // The name_id, version_id, and like_id are the values from the os_release
- // struct (refer there for background). If version_id is empty, then it's
- // treated as "0".
+ // The name_id, version_id, and like_ids are the values from os_release
+ // (refer there for background). If version_id is empty, then it's treated
+ // as "0".
//
// First consider <distribution>-name values corresponding to name_id.
// Assume <distribution> has the <name>[_<version>] form, where <version>
@@ -126,8 +132,8 @@ namespace bpkg
// (include the value with the absent <version>). In a sense, absent
// <version> can be treated as 0 semver-like versions.
//
- // If no value is found and like_id is not empty, then repeat the above
- // process for like_id instead of name_id and version_id equal 0.
+ // If no value is found then repeat the above process for every like_ids
+ // entry (from left to right) instead of name_id with version_id equal 0.
//
// If still no value is found, then return empty list (in which case the
// caller may choose to fallback to the downstream package name or do
@@ -138,10 +144,11 @@ namespace bpkg
system_package_names (const available_packages&,
const string& name_id,
const string& version_id,
- const string& like_id);
+ const vector<string>& like_ids);
protected:
os_release os_release_;
+ std::map<package_name, vector<package_status>> status_cache_;
};
// Create a package manager instance corresponding to the specified host