/
package_manager.py
233 lines (190 loc) · 9.93 KB
/
package_manager.py
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
import platform
from conans.client.graph.graph import CONTEXT_BUILD
from conans.errors import ConanException
class _SystemPackageManagerTool(object):
mode_check = "check"
mode_install = "install"
tool_name = None
install_command = ""
update_command = ""
check_command = ""
def __init__(self, conanfile):
self._conanfile = conanfile
self._active_tool = self._conanfile.conf.get("tools.system.package_manager:tool", default=self.get_default_tool())
self._sudo = self._conanfile.conf.get("tools.system.package_manager:sudo", default=False, check_type=bool)
self._sudo_askpass = self._conanfile.conf.get("tools.system.package_manager:sudo_askpass", default=False, check_type=bool)
self._mode = self._conanfile.conf.get("tools.system.package_manager:mode", default=self.mode_check)
self._arch = self._conanfile.settings_build.get_safe('arch') \
if self._conanfile.context == CONTEXT_BUILD else self._conanfile.settings.get_safe('arch')
self._arch_names = {}
self._arch_separator = ""
def get_default_tool(self):
os_name = platform.system()
if os_name in ["Linux", "FreeBSD"]:
import distro
os_name = distro.id() or os_name
elif os_name == "Windows" and self._conanfile.conf.get("tools.microsoft.bash:subsystem") == "msys2":
os_name = "msys2"
manager_mapping = {"apt-get": ["Linux", "ubuntu", "debian", "raspbian"],
"yum": ["pidora", "scientific", "xenserver", "amazon", "oracle", "amzn",
"almalinux", "rocky"],
"dnf": ["fedora", "rhel", "centos", "mageia"],
"brew": ["Darwin"],
"pacman": ["arch", "manjaro", "msys2"],
"choco": ["Windows"],
"zypper": ["opensuse", "sles"],
"pkg": ["freebsd"],
"pkgutil": ["Solaris"]}
for tool, distros in manager_mapping.items():
if os_name in distros:
return tool
def get_package_name(self, package):
# TODO: should we only add the arch if cross-building?
if self._arch in self._arch_names:
return "{}{}{}".format(package, self._arch_separator,
self._arch_names.get(self._arch))
return package
@property
def sudo_str(self):
sudo = "sudo " if self._sudo else ""
askpass = "-A " if self._sudo and self._sudo_askpass else ""
return "{}{}".format(sudo, askpass)
def run(self, method, *args, **kwargs):
if self._active_tool == self.__class__.tool_name:
return method(*args, **kwargs)
def install_substitutes(self, *args, **kwargs):
return self.run(self._install_substitutes, *args, **kwargs)
def install(self, *args, **kwargs):
return self.run(self._install, *args, **kwargs)
def update(self, *args, **kwargs):
return self.run(self._update, *args, **kwargs)
def check(self, *args, **kwargs):
return self.run(self._check, *args, **kwargs)
def _install_substitutes(self, *packages_substitutes, update=False, check=True, **kwargs):
errors = []
for packages in packages_substitutes:
try:
return self.install(packages, update, check, **kwargs)
except ConanException as e:
errors.append(e)
for error in errors:
self._conanfile.output.warn(str(error))
raise ConanException("None of the installs for the package substitutes succeeded.")
def _install(self, packages, update=False, check=True, **kwargs):
if update:
self.update()
if check:
packages = self.check(packages)
if self._mode == self.mode_check and packages:
raise ConanException("System requirements: '{0}' are missing but can't install "
"because tools.system.package_manager:mode is '{1}'."
"Please update packages manually or set "
"'tools.system.package_manager:mode' "
"to '{2}' in the [conf] section of the profile, "
"or in the command line using "
"'-c tools.system.package_manager:mode={2}'".format(", ".join(packages),
self.mode_check,
self.mode_install))
elif packages:
packages_arch = [self.get_package_name(package) for package in packages]
if packages_arch:
command = self.install_command.format(sudo=self.sudo_str,
tool=self.tool_name,
packages=" ".join(packages_arch),
**kwargs)
return self._conanfile.run(command)
else:
self._conanfile.output.info("System requirements: {} already "
"installed".format(" ".join(packages)))
def _update(self):
if self._mode == self.mode_check:
raise ConanException("Can't update because tools.system.package_manager:mode is '{0}'."
"Please update packages manually or set "
"'tools.system.package_manager:mode' "
"to '{1}' in the [conf] section of the profile, "
"or in the command line using "
"'-c tools.system.package_manager:mode={1}'".format(self.mode_check,
self.mode_install))
command = self.update_command.format(sudo=self.sudo_str, tool=self.tool_name)
return self._conanfile.run(command)
def _check(self, packages):
missing = [pkg for pkg in packages if self.check_package(self.get_package_name(pkg)) != 0]
return missing
def check_package(self, package):
command = self.check_command.format(tool=self.tool_name,
package=package)
return self._conanfile.run(command, ignore_errors=True)
class Apt(_SystemPackageManagerTool):
# TODO: apt? apt-get?
tool_name = "apt-get"
install_command = "{sudo}{tool} install -y {recommends}{packages}"
update_command = "{sudo}{tool} update"
check_command = "dpkg-query -W -f='${{Status}}' {package} | grep -q \"ok installed\""
def __init__(self, conanfile, arch_names=None):
super(Apt, self).__init__(conanfile)
self._arch_names = {"x86_64": "amd64",
"x86": "i386",
"ppc32": "powerpc",
"ppc64le": "ppc64el",
"armv7": "arm",
"armv7hf": "armhf",
"armv8": "arm64",
"s390x": "s390x"} if arch_names is None else arch_names
self._arch_separator = ":"
def install(self, packages, update=False, check=False, recommends=False):
recommends_str = '' if recommends else '--no-install-recommends '
return super(Apt, self).install(packages, update=update, check=check,
recommends=recommends_str)
class Yum(_SystemPackageManagerTool):
tool_name = "yum"
install_command = "{sudo}{tool} install -y {packages}"
update_command = "{sudo}{tool} check-update -y"
check_command = "rpm -q {package}"
def __init__(self, conanfile, arch_names=None):
super(Yum, self).__init__(conanfile)
self._arch_names = {"x86_64": "x86_64",
"x86": "i?86",
"ppc32": "powerpc",
"ppc64le": "ppc64le",
"armv7": "armv7",
"armv7hf": "armv7hl",
"armv8": "aarch64",
"s390x": "s390x"} if arch_names is None else arch_names
self._arch_separator = "."
class Dnf(Yum):
tool_name = "dnf"
class Brew(_SystemPackageManagerTool):
tool_name = "brew"
install_command = "{sudo}{tool} install {packages}"
update_command = "{sudo}{tool} update"
check_command = 'test -n "$({tool} ls --versions {package})"'
class Pkg(_SystemPackageManagerTool):
tool_name = "pkg"
install_command = "{sudo}{tool} install -y {packages}"
update_command = "{sudo}{tool} update"
check_command = "{tool} info {package}"
class PkgUtil(_SystemPackageManagerTool):
tool_name = "pkgutil"
install_command = "{sudo}{tool} --install --yes {packages}"
update_command = "{sudo}{tool} --catalog"
check_command = 'test -n "`{tool} --list {package}`"'
class Chocolatey(_SystemPackageManagerTool):
tool_name = "choco"
install_command = "{tool} --install --yes {packages}"
update_command = "{tool} outdated"
check_command = '{tool} search --local-only --exact {package} | ' \
'findstr /c:"1 packages installed."'
class PacMan(_SystemPackageManagerTool):
tool_name = "pacman"
install_command = "{sudo}{tool} -S --noconfirm {packages}"
update_command = "{sudo}{tool} -Syyu --noconfirm"
check_command = "{tool} -Qi {package}"
def __init__(self, conanfile, arch_names=None):
super(PacMan, self).__init__(conanfile)
self._arch_names = {"x86": "lib32"} if arch_names is None else arch_names
self._arch_separator = "-"
class Zypper(_SystemPackageManagerTool):
tool_name = "zypper"
install_command = "{sudo}{tool} --non-interactive in {packages}"
update_command = "{sudo}{tool} --non-interactive ref"
check_command = "rpm -q {package}"