aboutsummaryrefslogtreecommitdiff
path: root/bpkg/system-package-manager.hxx
blob: 78b157ae0a08ef854da95f6122deba0a9051a7d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// file      : bpkg/system-package-manager.hxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#ifndef BPKG_SYSTEM_PACKAGE_MANAGER_HXX
#define BPKG_SYSTEM_PACKAGE_MANAGER_HXX

#include <map>

#include <libbpkg/manifest.hxx>     // version
#include <libbpkg/package-name.hxx>

#include <bpkg/types.hxx>
#include <bpkg/utility.hxx>

#include <bpkg/package.hxx>
#include <bpkg/host-os-release.hxx>

namespace bpkg
{
  // The system package manager interface. Used by both pkg-build (to query
  // and install system packages) and by pkg-bindist (to build them).
  //
  class system_package_manager
  {
  public:
    struct package_status
    {
      // Downstream (as in, bpkg package) version.
      //
      bpkg::version version;

      // The system package can be either "available already installed",
      // "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.
      //
      // @@ But these could be multiple. Do we really need this?
      /*
      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.
    //
    // 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 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 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.
    //
    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 ();

    explicit
    system_package_manager (os_release&& osr)
        : os_release_ (osr) {}

  protected:
    // Given the available packages (as returned by find_available_all())
    // return the list of system package names.
    //
    // 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>
    // is a semver-like version (e.g, 10, 10.15, or 10.15.1) and return all
    // the values that are equal of less than the specified version_id
    // (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 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
    // something more elaborate, like translate version_id to the like_id's
    // version and try that).
    //
    static strings
    system_package_names (const available_packages&,
                          const string& name_id,
                          const string& version_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
  // target and optional manager name. If name is empty, return NULL if there
  // is no support for this platform.
  //
  // @@ TODO: need to assign names. Ideas:
  //
  //    dpkg-apt, rpm-dnf
  //    deb, rpm
  //    debian, fedora (i.e., follow  /etc/os-release ID_LIKE lead)
  //
  unique_ptr<system_package_manager>
  make_system_package_manager (const target_triplet&,
                               const string& name);
}

#endif // BPKG_SYSTEM_PACKAGE_MANAGER_HXX