Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH 1/2] Add binrepos.conf to replace PORTAGE_BINHOST (bug 668334)
Date: Mon, 07 Sep 2020 06:35:26
Message-Id: 20200907063131.395951-2-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH 0/2] Add binrepos.conf to support fetchcommand customization (bug 661332) by Zac Medico
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