1 |
Support /etc/portage/binrepos.conf as a replacement for the |
2 |
PORTAGE_BINHOST variable. Behavior is similar to repos.conf, |
3 |
initially supporting just the sync-uri attribute. Both binrepos.conf |
4 |
and PORTAGE_BINHOST can be used simultaneously, in the same way that |
5 |
repos.conf and PORTDIR_OVERLAY can be used simultaneously. |
6 |
|
7 |
The emerge --info output for binrepos.conf looks like this: |
8 |
|
9 |
Binary Repositories: |
10 |
|
11 |
example-binhost |
12 |
sync-uri: https://example.com/packages |
13 |
|
14 |
Bug: https://bugs.gentoo.org/668334 |
15 |
Signed-off-by: Zac Medico <zmedico@g.o> |
16 |
--- |
17 |
lib/_emerge/actions.py | 13 ++- |
18 |
lib/portage/binrepo/__init__.py | 0 |
19 |
lib/portage/binrepo/config.py | 131 ++++++++++++++++++++++++ |
20 |
lib/portage/const.py | 1 + |
21 |
lib/portage/dbapi/bintree.py | 14 ++- |
22 |
lib/portage/tests/emerge/test_simple.py | 14 ++- |
23 |
man/make.conf.5 | 3 +- |
24 |
man/portage.5 | 38 +++++++ |
25 |
8 files changed, 206 insertions(+), 8 deletions(-) |
26 |
create mode 100644 lib/portage/binrepo/__init__.py |
27 |
create mode 100644 lib/portage/binrepo/config.py |
28 |
|
29 |
diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py |
30 |
index a4ecfe43d..34344f046 100644 |
31 |
--- a/lib/_emerge/actions.py |
32 |
+++ b/lib/_emerge/actions.py |
33 |
@@ -32,7 +32,8 @@ portage.proxy.lazyimport.lazyimport(globals(), |
34 |
from portage import os |
35 |
from portage import shutil |
36 |
from portage import _encodings, _unicode_decode |
37 |
-from portage.const import _DEPCLEAN_LIB_CHECK_DEFAULT |
38 |
+from portage.binrepo.config import BinRepoConfigLoader |
39 |
+from portage.const import BINREPOS_CONF_FILE, _DEPCLEAN_LIB_CHECK_DEFAULT |
40 |
from portage.dbapi.dep_expand import dep_expand |
41 |
from portage.dbapi._expand_new_virt import expand_new_virt |
42 |
from portage.dbapi.IndexedPortdb import IndexedPortdb |
43 |
@@ -1836,6 +1837,16 @@ def action_info(settings, trees, myopts, myfiles): |
44 |
for repo in repos: |
45 |
append(repo.info_string()) |
46 |
|
47 |
+ binrepos_conf_path = os.path.join(settings['PORTAGE_CONFIGROOT'], BINREPOS_CONF_FILE) |
48 |
+ binrepos_conf = BinRepoConfigLoader((binrepos_conf_path,), settings) |
49 |
+ if binrepos_conf and any(repo.name for repo in binrepos_conf.values()): |
50 |
+ append("Binary Repositories:\n") |
51 |
+ for repo in reversed(list(binrepos_conf.values())): |
52 |
+ # Omit repos from the PORTAGE_BINHOST variable, since they |
53 |
+ # do not have a name to label them with. |
54 |
+ if repo.name: |
55 |
+ append(repo.info_string()) |
56 |
+ |
57 |
installed_sets = sorted(s for s in |
58 |
root_config.sets['selected'].getNonAtoms() if s.startswith(SETPREFIX)) |
59 |
if installed_sets: |
60 |
diff --git a/lib/portage/binrepo/__init__.py b/lib/portage/binrepo/__init__.py |
61 |
new file mode 100644 |
62 |
index 000000000..e69de29bb |
63 |
diff --git a/lib/portage/binrepo/config.py b/lib/portage/binrepo/config.py |
64 |
new file mode 100644 |
65 |
index 000000000..aa3ff7a77 |
66 |
--- /dev/null |
67 |
+++ b/lib/portage/binrepo/config.py |
68 |
@@ -0,0 +1,131 @@ |
69 |
+# Copyright 2020 Gentoo Authors |
70 |
+# Distributed under the terms of the GNU General Public License v2 |
71 |
+ |
72 |
+from collections import OrderedDict |
73 |
+from collections.abc import Mapping |
74 |
+from hashlib import md5 |
75 |
+ |
76 |
+from portage.localization import _ |
77 |
+from portage.util import _recursive_file_list, writemsg |
78 |
+from portage.util.configparser import (SafeConfigParser, ConfigParserError, |
79 |
+ read_configs) |
80 |
+ |
81 |
+ |
82 |
+class BinRepoConfig: |
83 |
+ __slots__ = ( |
84 |
+ 'name', |
85 |
+ 'name_fallback', |
86 |
+ 'priority', |
87 |
+ 'sync_uri', |
88 |
+ ) |
89 |
+ def __init__(self, opts): |
90 |
+ """ |
91 |
+ Create a BinRepoConfig with options in opts. |
92 |
+ """ |
93 |
+ for k in self.__slots__: |
94 |
+ setattr(self, k, opts.get(k.replace('_', '-'))) |
95 |
+ |
96 |
+ def info_string(self): |
97 |
+ """ |
98 |
+ Returns a formatted string containing informations about the repository. |
99 |
+ Used by emerge --info. |
100 |
+ """ |
101 |
+ indent = " " * 4 |
102 |
+ repo_msg = [] |
103 |
+ repo_msg.append(self.name or self.name_fallback) |
104 |
+ if self.priority is not None: |
105 |
+ repo_msg.append(indent + "priority: " + str(self.priority)) |
106 |
+ repo_msg.append(indent + "sync-uri: " + self.sync_uri) |
107 |
+ repo_msg.append("") |
108 |
+ return "\n".join(repo_msg) |
109 |
+ |
110 |
+ |
111 |
+class BinRepoConfigLoader(Mapping): |
112 |
+ def __init__(self, paths, settings): |
113 |
+ """Load config from files in paths""" |
114 |
+ |
115 |
+ # Defaults for value interpolation. |
116 |
+ parser_defaults = { |
117 |
+ "EPREFIX" : settings["EPREFIX"], |
118 |
+ "EROOT" : settings["EROOT"], |
119 |
+ "PORTAGE_CONFIGROOT" : settings["PORTAGE_CONFIGROOT"], |
120 |
+ "ROOT" : settings["ROOT"], |
121 |
+ } |
122 |
+ |
123 |
+ try: |
124 |
+ parser = self._parse(paths, parser_defaults) |
125 |
+ except ConfigParserError as e: |
126 |
+ writemsg( |
127 |
+ _("!!! Error while reading binrepo config file: %s\n") % e, |
128 |
+ noiselevel=-1) |
129 |
+ parser = SafeConfigParser(defaults=parser_defaults) |
130 |
+ |
131 |
+ repos = [] |
132 |
+ sync_uris = [] |
133 |
+ for section_name in parser.sections(): |
134 |
+ repo_data = dict(parser[section_name].items()) |
135 |
+ repo_data['name'] = section_name |
136 |
+ repo = BinRepoConfig(repo_data) |
137 |
+ if repo.sync_uri is None: |
138 |
+ writemsg(_("!!! Missing sync-uri setting for binrepo %s\n") % (repo.name,), noiselevel=-1) |
139 |
+ continue |
140 |
+ |
141 |
+ sync_uri = self._normalize_uri(repo.sync_uri) |
142 |
+ sync_uris.append(sync_uri) |
143 |
+ repo.sync_uri = sync_uri |
144 |
+ if repo.priority is not None: |
145 |
+ try: |
146 |
+ repo.priority = int(repo.priority) |
147 |
+ except ValueError: |
148 |
+ repo.priority = None |
149 |
+ repos.append(repo) |
150 |
+ |
151 |
+ sync_uris = set(sync_uris) |
152 |
+ current_priority = 0 |
153 |
+ for sync_uri in reversed(settings.get("PORTAGE_BINHOST", "").split()): |
154 |
+ sync_uri = self._normalize_uri(sync_uri) |
155 |
+ if sync_uri not in sync_uris: |
156 |
+ current_priority += 1 |
157 |
+ sync_uris.add(sync_uri) |
158 |
+ repos.append(BinRepoConfig({ |
159 |
+ 'name-fallback': self._digest_uri(sync_uri), |
160 |
+ 'name': None, |
161 |
+ 'priority': current_priority, |
162 |
+ 'sync-uri': sync_uri, |
163 |
+ })) |
164 |
+ |
165 |
+ self._data = OrderedDict((repo.name, repo) for repo in |
166 |
+ sorted(repos, key=lambda repo: (repo.priority or 0, repo.name or repo.name_fallback))) |
167 |
+ |
168 |
+ @staticmethod |
169 |
+ def _digest_uri(uri): |
170 |
+ return md5(uri.encode('utf_8')).hexdigest() |
171 |
+ |
172 |
+ @staticmethod |
173 |
+ def _normalize_uri(uri): |
174 |
+ return uri.rstrip('/') |
175 |
+ |
176 |
+ @staticmethod |
177 |
+ def _parse(paths, defaults): |
178 |
+ parser = SafeConfigParser(defaults=defaults) |
179 |
+ recursive_paths = [] |
180 |
+ for p in paths: |
181 |
+ if isinstance(p, str): |
182 |
+ recursive_paths.extend(_recursive_file_list(p)) |
183 |
+ else: |
184 |
+ recursive_paths.append(p) |
185 |
+ |
186 |
+ read_configs(parser, recursive_paths) |
187 |
+ return parser |
188 |
+ |
189 |
+ def __iter__(self): |
190 |
+ return iter(self._data) |
191 |
+ |
192 |
+ def __contains__(self, key): |
193 |
+ return key in self._data |
194 |
+ |
195 |
+ def __getitem__(self, key): |
196 |
+ return self._data[key] |
197 |
+ |
198 |
+ def __len__(self): |
199 |
+ return len(self._data) |
200 |
diff --git a/lib/portage/const.py b/lib/portage/const.py |
201 |
index 9a7ea23bd..b895f0fa9 100644 |
202 |
--- a/lib/portage/const.py |
203 |
+++ b/lib/portage/const.py |
204 |
@@ -28,6 +28,7 @@ import os |
205 |
|
206 |
# variables used with config_root (these need to be relative) |
207 |
USER_CONFIG_PATH = "etc/portage" |
208 |
+BINREPOS_CONF_FILE = USER_CONFIG_PATH + "/binrepos.conf" |
209 |
MAKE_CONF_FILE = USER_CONFIG_PATH + "/make.conf" |
210 |
MODULES_FILE_PATH = USER_CONFIG_PATH + "/modules" |
211 |
CUSTOM_PROFILE_PATH = USER_CONFIG_PATH + "/profile" |
212 |
diff --git a/lib/portage/dbapi/bintree.py b/lib/portage/dbapi/bintree.py |
213 |
index 620865a79..a96183561 100644 |
214 |
--- a/lib/portage/dbapi/bintree.py |
215 |
+++ b/lib/portage/dbapi/bintree.py |
216 |
@@ -22,8 +22,9 @@ portage.proxy.lazyimport.lazyimport(globals(), |
217 |
'portage.versions:best,catpkgsplit,catsplit,_pkg_str', |
218 |
) |
219 |
|
220 |
+from portage.binrepo.config import BinRepoConfigLoader |
221 |
from portage.cache.mappings import slot_dict_class |
222 |
-from portage.const import CACHE_PATH, SUPPORTED_XPAK_EXTENSIONS |
223 |
+from portage.const import BINREPOS_CONF_FILE, CACHE_PATH, SUPPORTED_XPAK_EXTENSIONS |
224 |
from portage.dbapi.virtual import fakedbapi |
225 |
from portage.dep import Atom, use_reduce, paren_enclose |
226 |
from portage.exception import AlarmSignal, InvalidData, InvalidPackageName, \ |
227 |
@@ -364,6 +365,7 @@ class binarytree: |
228 |
self.move_slot_ent = self.dbapi.move_slot_ent |
229 |
self.populated = 0 |
230 |
self.tree = {} |
231 |
+ self._binrepos_conf = None |
232 |
self._remote_has_index = False |
233 |
self._remotepkgs = None # remote metadata indexed by cpv |
234 |
self._additional_pkgs = {} |
235 |
@@ -628,8 +630,10 @@ class binarytree: |
236 |
self._populate_additional(add_repos) |
237 |
|
238 |
if getbinpkgs: |
239 |
- if not self.settings.get("PORTAGE_BINHOST"): |
240 |
- writemsg(_("!!! PORTAGE_BINHOST unset, but use is requested.\n"), |
241 |
+ config_path = os.path.join(self.settings['PORTAGE_CONFIGROOT'], BINREPOS_CONF_FILE) |
242 |
+ self._binrepos_conf = BinRepoConfigLoader((config_path,), self.settings) |
243 |
+ if not self._binrepos_conf: |
244 |
+ writemsg(_("!!! %s is missing (or PORTAGE_BINHOST is unset), but use is requested.\n") % (config_path,), |
245 |
noiselevel=-1) |
246 |
else: |
247 |
self._populate_remote(getbinpkg_refresh=getbinpkg_refresh) |
248 |
@@ -903,7 +907,9 @@ class binarytree: |
249 |
|
250 |
self._remote_has_index = False |
251 |
self._remotepkgs = {} |
252 |
- for base_url in self.settings["PORTAGE_BINHOST"].split(): |
253 |
+ # Order by descending priority. |
254 |
+ for repo in reversed(list(self._binrepos_conf.values())): |
255 |
+ base_url = repo.sync_uri |
256 |
parsed_url = urlparse(base_url) |
257 |
host = parsed_url.netloc |
258 |
port = parsed_url.port |
259 |
diff --git a/lib/portage/tests/emerge/test_simple.py b/lib/portage/tests/emerge/test_simple.py |
260 |
index c24f5c603..8635b70e4 100644 |
261 |
--- a/lib/portage/tests/emerge/test_simple.py |
262 |
+++ b/lib/portage/tests/emerge/test_simple.py |
263 |
@@ -7,7 +7,7 @@ import sys |
264 |
import portage |
265 |
from portage import shutil, os |
266 |
from portage import _unicode_decode |
267 |
-from portage.const import (BASH_BINARY, PORTAGE_PYM_PATH, USER_CONFIG_PATH) |
268 |
+from portage.const import (BASH_BINARY, BINREPOS_CONF_FILE, PORTAGE_PYM_PATH, USER_CONFIG_PATH) |
269 |
from portage.cache.mappings import Mapping |
270 |
from portage.process import find_binary |
271 |
from portage.tests import TestCase |
272 |
@@ -419,13 +419,23 @@ call_has_and_best_version() { |
273 |
) |
274 |
|
275 |
# Test binhost support if FETCHCOMMAND is available. |
276 |
+ binrepos_conf_file = os.path.join(os.sep, eprefix, BINREPOS_CONF_FILE) |
277 |
+ with open(binrepos_conf_file, 'wt') as f: |
278 |
+ f.write('[test-binhost]\n') |
279 |
+ f.write('sync-uri = {}\n'.format(binhost_uri)) |
280 |
fetchcommand = portage.util.shlex_split(playground.settings['FETCHCOMMAND']) |
281 |
fetch_bin = portage.process.find_binary(fetchcommand[0]) |
282 |
if fetch_bin is not None: |
283 |
test_commands = test_commands + ( |
284 |
+ lambda: os.rename(pkgdir, binhost_dir), |
285 |
+ emerge_cmd + ("-e", "--getbinpkgonly", "dev-libs/A"), |
286 |
+ lambda: shutil.rmtree(pkgdir), |
287 |
+ lambda: os.rename(binhost_dir, pkgdir), |
288 |
+ # Remove binrepos.conf and test PORTAGE_BINHOST. |
289 |
+ lambda: os.unlink(binrepos_conf_file), |
290 |
lambda: os.rename(pkgdir, binhost_dir), |
291 |
({"PORTAGE_BINHOST": binhost_uri},) + \ |
292 |
- emerge_cmd + ("-e", "--getbinpkgonly", "dev-libs/A"), |
293 |
+ emerge_cmd + ("-fe", "--getbinpkgonly", "dev-libs/A"), |
294 |
lambda: shutil.rmtree(pkgdir), |
295 |
lambda: os.rename(binhost_dir, pkgdir), |
296 |
) |
297 |
diff --git a/man/make.conf.5 b/man/make.conf.5 |
298 |
index eb812150f..f7b2460e5 100644 |
299 |
--- a/man/make.conf.5 |
300 |
+++ b/man/make.conf.5 |
301 |
@@ -853,7 +853,8 @@ Each entry in the list must specify the full address of a directory |
302 |
serving tbz2's for your system (this directory must contain a 'Packages' index |
303 |
file). This is only used when running with |
304 |
the get binary pkg options are given to \fBemerge\fR. Review \fBemerge\fR(1) |
305 |
-for more information. |
306 |
+for more information. The \fBPORTAGE_BINHOST\fR variable is deprecated in |
307 |
+favor of the \fBbinrepos.conf\fR configuration file (see \fBportage\fR(5)). |
308 |
.TP |
309 |
\fBPORTAGE_BINHOST_HEADER_URI\fR = \ |
310 |
\fI"ftp://login:pass@××××××××××.site/pub/grp/i686/athlon\-xp/"\fR |
311 |
diff --git a/man/portage.5 b/man/portage.5 |
312 |
index 7472972cc..890a22adb 100644 |
313 |
--- a/man/portage.5 |
314 |
+++ b/man/portage.5 |
315 |
@@ -47,6 +47,7 @@ virtuals |
316 |
.BR /etc/portage/ |
317 |
.nf |
318 |
bashrc |
319 |
+binrepos.conf |
320 |
categories |
321 |
color.map |
322 |
license_groups |
323 |
@@ -620,6 +621,43 @@ any other bash script. |
324 |
|
325 |
Additional package-specific bashrc files can be created in /etc/portage/env. |
326 |
.TP |
327 |
+.BR binrepos.conf |
328 |
+Specifies remote binary package repository configuration information. This |
329 |
+is intended to be used as a replacement for the \fBmake.conf\fR(5) |
330 |
+\fBPORTAGE_BINHOST\fR variable. |
331 |
+ |
332 |
+.I Format: |
333 |
+.nf |
334 |
+\- comments begin with # (no inline comments) |
335 |
+\- configuration of each repository is specified in a section starting with \ |
336 |
+"[${repository_name}]" |
337 |
+\- attributes are specified in "${attribute} = ${value}" format |
338 |
+.fi |
339 |
+ |
340 |
+.RS |
341 |
+.I Attributes supported in sections of repositories: |
342 |
+.RS |
343 |
+.TP |
344 |
+.B priority |
345 |
+Specifies priority of given repository. When a package exists in multiple |
346 |
+repositories, those with higher priority are preferred. |
347 |
+.TP |
348 |
+.B sync\-uri |
349 |
+Specifies URI of repository used for `emerge \-\-getbinpkg`. |
350 |
+.RE |
351 |
+.RE |
352 |
+ |
353 |
+.I Example: |
354 |
+.nf |
355 |
+[example-binhost] |
356 |
+# repos with higher priorities are preferred when packages with equal |
357 |
+# versions are found in multiple repos |
358 |
+priority = 9999 |
359 |
+# packages are fetched from here |
360 |
+sync-uri = https://example.com/binhost |
361 |
+ |
362 |
+.fi |
363 |
+.TP |
364 |
.BR categories |
365 |
A simple list of valid categories that may be used in repositories and PKGDIR |
366 |
(see \fBmake.conf\fR(5)). This allows for custom categories to be created. |
367 |
-- |
368 |
2.25.3 |