aboutsummaryrefslogtreecommitdiff
path: root/bpkg/system-package-manager-debian.hxx
blob: 8783b512dfebdf0665563bc1cc902d20657e8c66 (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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
// file      : bpkg/system-package-manager-debian.hxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#ifndef BPKG_SYSTEM_PACKAGE_MANAGER_DEBIAN_HXX
#define BPKG_SYSTEM_PACKAGE_MANAGER_DEBIAN_HXX

#include <map>

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

#include <bpkg/system-package-manager.hxx>

namespace bpkg
{
  // The system package manager implementation for Debian and alike (Ubuntu,
  // etc) using the APT frontend.
  //
  // For background, a library in Debian is normally split up into several
  // packages: the shared library package (e.g., libfoo1 where 1 is the ABI
  // version), the development files package (e.g., libfoo-dev), the
  // documentation files package (e.g., libfoo-doc), the debug symbols
  // package (e.g., libfoo1-dbg), and the architecture-independent files
  // (e.g., libfoo1-common). All the packages except -dev are optional
  // and there is quite a bit of variability here. Here are a few examples:
  //
  // libz3-4 libz3-dev
  //
  // libssl1.1 libssl-dev libssl-doc
  // libssl3 libssl-dev libssl-doc
  //
  // libcurl4 libcurl4-openssl-dev libcurl4-doc
  // libcurl3-gnutls libcurl4-gnutls-dev libcurl4-doc  (yes, 3 and 4)
  //
  // Based on that, it seems our best bet when trying to automatically map our
  // library package name to Debian package names is to go for the -dev
  // package first and figure out the shared library package from that based
  // on the fact that the -dev package should have the == dependency on the
  // shared library package with the same version and its name should normally
  // start with the -dev package's stem.
  //
  // For executable packages there is normally no -dev packages but -dbg,
  // -doc, and -common are plausible.
  //
  // The format of the debian-name (or alike) manifest value is a comma-
  // separated list of one or more package groups:
  //
  // <package-group> [, <package-group>...]
  //
  // Where each <package-group> is the space-separated list of one or more
  // package names:
  //
  // <package-name> [ <package-name>...]
  //
  // All the packages in the group should be "package components" (for the
  // lack of a better term) of the same "logical package", such as -dev, -doc,
  // -common packages. They normally have the same version.
  //
  // The first group is called the main group and the first package in the
  // group is called the main package. Note that all the groups are consumed
  // (installed) but only the main group is produced (packaged).
  //
  // We allow/recommend specifying the -dev package instead of the main
  // package for libraries (the bpkg package name starts with lib), seeing
  // that we are capable of detecting the main package automatically. If the
  // library name happens to end with -dev (which poses an ambiguity), then
  // the -dev package should be specified explicitly as the second package to
  // disambiguate this situation (if a non-library name happened to start with
  // lib and end with -dev, well, you are out of luck, I guess).
  //
  // Note also that for now we treat all the packages from the non-main groups
  // as extras but in the future we may decide to sort them out like the main
  // group (see parse_name_value() for details).
  //
  // The Debian package version has the [<epoch>:]<upstream>[-<revision>] form
  // (see deb-version(5) for details). If no explicit mapping to bpkg version
  // is specified with the debian-to-downstream-version manifest values (or
  // alike), then we fallback to using the <upstream> part as bpkg version. If
  // explicit mapping is specified, then we match it against the
  // [<epoch>:]<upstream> parts ignoring <revision>.
  //
  struct system_package_status_debian: system_package_status
  {
    string main;
    string dev;
    string doc;
    string dbg;
    string common;
    strings extras;

    // The `apt-cache policy` output.
    //
    struct package_policy
    {
      string name;
      string installed_version; // Empty if none.
      string candidate_version; // Empty if none and no installed_version.

      explicit
      package_policy (string n): name (move (n)) {}
    };

    vector<package_policy> package_policies;
    size_t package_policies_main = 0; // Size of the main group.

    explicit
    system_package_status_debian (string m, string d = {})
        : main (move (m)), dev (move (d))
    {
      assert (!main.empty () || !dev.empty ());
    }

    system_package_status_debian () = default;
  };

  class system_package_manager_debian: public system_package_manager
  {
  public:
    virtual optional<const system_package_status*>
    pkg_status (const package_name&, const available_packages*) override;

    virtual void
    pkg_install (const vector<package_name>&) override;

  public:
    // Expects os_release::name_id to be "debian" or os_release::like_ids to
    // contain "debian".
    //
    using system_package_manager::system_package_manager;

    // Implementation details exposed for testing (see definitions for
    // documentation).
    //
  public:
    using package_status = system_package_status_debian;
    using package_policy = package_status::package_policy;

    void
    apt_cache_policy (vector<package_policy>&, size_t = 0);

    string
    apt_cache_show (const string&, const string&);

    void
    apt_get_update ();

    void
    apt_get_install (const strings&);

    pair<cstrings, const process_path&>
    apt_get_common (const char*);

    static package_status
    parse_name_value (const package_name&, const string&, bool, bool);

    static string
    main_from_dev (const string&, const string&, const string&);

    // If simulate is not NULL, then instead of executing the actual apt-cache
    // and apt-get commands simulate their execution: (1) for apt-cache by
    // printing their command lines and reading the results from files
    // specified in the below apt_cache_* maps and (2) for apt-get by printing
    // their command lines and failing if requested.
    //
    // In the (1) case if the corresponding map entry does not exist or the
    // path is empty, then act as if the specified package/version is
    // unknown. If the path is special "-" then read from stdin. For apt-cache
    // different post-fetch and (for policy) post-install results can be
    // specified (if the result is not found in one of the later maps, the
    // previous map is used as a fallback). Note that the keys in the
    // apt_cache_policy_* maps are the package sets and the corresponding
    // result file is expected to contain (or not) the results for all of
    // them. See apt_cache_policy() and apt_cache_show() implementations for
    // details on the expected results.
    //
    struct simulation
    {
      std::map<strings, path> apt_cache_policy_;
      std::map<strings, path> apt_cache_policy_fetched_;
      std::map<strings, path> apt_cache_policy_installed_;

      std::map<pair<string, string>, path> apt_cache_show_;
      std::map<pair<string, string>, path> apt_cache_show_fetched_;

      bool apt_get_update_fail_ = false;
      bool apt_get_install_fail_ = false;
    };

    const simulation* simulate_ = nullptr;

  protected:
    bool fetched_ = false;   // True if already fetched metadata.
    bool installed_ = false; // True if already installed.

    std::map<package_name, optional<system_package_status_debian>> status_cache_;
  };
}

#endif // BPKG_SYSTEM_PACKAGE_MANAGER_DEBIAN_HXX