aboutsummaryrefslogtreecommitdiff
path: root/bpkg/system-package-manager-debian.hxx
blob: d3b9f028e1964c22021421574d35c4fab33cd721 (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
// 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-doc libcurl4-openssl-dev
  // libcurl3-gnutls libcurl4-gnutls-dev         (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 a manual mapping we will require the user to always specify the
  // shared library package and the -dev package names explicitly.
  //
  // For executable packages there is normally no -dev packages but -dbg,
  // -doc, and -common are plausible.
  //
  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:
    // Note: expects os_release::name_id to be "debian" or os_release::like_id
    // 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 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