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 7/7] binpkg-multi-instance 7 of 7
Date: Wed, 18 Feb 2015 03:07:09
Message-Id: 1424228745-7794-8-git-send-email-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH 0/7] Add FEATURES=binpkg-multi-instance (bug 150031) by Zac Medico
1 Support "profile-formats = build-id" setting for layout.conf. When
2 this is enabled in layout.conf of the containing repository, a
3 dependency atom in the profile can refer to a specific build, using the
4 build-id that is assigned when FEATURES=binpkg-multi-instance is
5 enabled. A build-id atom is identical to a version-specific atom,
6 except that the version is followed by a hyphen and an integer build-id.
7
8 With the build-id profile format, it is possible to assemble a system
9 using specific builds of binary packages, as users of "binary"
10 distros might be accustomed to. For example, an atom in the "packages"
11 file can pull a specific build of a package into the @system set, and
12 an atom in the "package.keywords" file can be used to modify the
13 effective KEYWORDS of a specific build of a package.
14
15 Refering to specific builds can be useful for a number of reasons. For
16 example, if a particular build needs to undergo a large amount of
17 testing in a complex environment in order to verify reliability, then
18 it can be useful to lock a profile to a specific build that has been
19 thoroughly tested.
20 ---
21 This patch is identical to "[PATCH 2/2] Add profile-formats=build-id
22 (bug 150031)" which was sent to the list earlier, except that it adds
23 a comment about "smart" defaults in the Atom class.
24
25 man/portage.5 | 8 +-
26 pym/_emerge/is_valid_package_atom.py | 5 +-
27 pym/portage/_sets/ProfilePackageSet.py | 3 +-
28 pym/portage/_sets/profiles.py | 3 +-
29 pym/portage/dep/__init__.py | 38 +++++-
30 .../package/ebuild/_config/KeywordsManager.py | 3 +-
31 .../package/ebuild/_config/LocationsManager.py | 8 +-
32 pym/portage/package/ebuild/_config/MaskManager.py | 21 +++-
33 pym/portage/package/ebuild/_config/UseManager.py | 14 ++-
34 pym/portage/package/ebuild/config.py | 15 ++-
35 pym/portage/repository/config.py | 2 +-
36 pym/portage/tests/dep/test_isvalidatom.py | 8 +-
37 .../test_build_id_profile_format.py | 134 +++++++++++++++++++++
38 pym/portage/util/__init__.py | 13 +-
39 14 files changed, 237 insertions(+), 38 deletions(-)
40 create mode 100644 pym/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py
41
42 diff --git a/man/portage.5 b/man/portage.5
43 index ed5140d..e062f9f 100644
44 --- a/man/portage.5
45 +++ b/man/portage.5
46 @@ -1180,7 +1180,7 @@ and the newer/faster "md5-dict" format. Default is to detect dirs.
47 The EAPI to use for profiles when unspecified. This attribute is
48 supported only if profile-default-eapi is included in profile-formats.
49 .TP
50 -.BR profile\-formats " = [pms] [portage-1] [portage-2] [profile-bashrcs] [profile-set] [profile-default-eapi]"
51 +.BR profile\-formats " = [pms] [portage-1] [portage-2] [profile-bashrcs] [profile-set] [profile-default-eapi] [build-id]"
52 Control functionality available to profiles in this repo such as which files
53 may be dirs, or the syntax available in parent files. Use "portage-2" if you're
54 unsure. The default is "portage-1-compat" mode which is meant to be compatible
55 @@ -1190,7 +1190,11 @@ Setting profile-bashrcs will enable the per-profile bashrc mechanism
56 profile \fBpackages\fR file to add atoms to the @profile package set.
57 See the profile \fBpackages\fR section for more information.
58 Setting profile-default-eapi enables support for the
59 -profile_eapi_when_unspecified attribute.
60 +profile_eapi_when_unspecified attribute. Setting build\-id allows
61 +dependency atoms in the profile to refer to specific builds (see the
62 +binpkg\-multi\-instance FEATURES setting in \fBmake.conf\fR(5)). A
63 +build\-id atom is identical to a version-specific atom, except that the
64 +version is followed by a hyphen and an integer build\-id.
65 .RE
66 .RE
67
68 diff --git a/pym/_emerge/is_valid_package_atom.py b/pym/_emerge/is_valid_package_atom.py
69 index 112afc1..17f7642 100644
70 --- a/pym/_emerge/is_valid_package_atom.py
71 +++ b/pym/_emerge/is_valid_package_atom.py
72 @@ -14,9 +14,10 @@ def insert_category_into_atom(atom, category):
73 ret = None
74 return ret
75
76 -def is_valid_package_atom(x, allow_repo=False):
77 +def is_valid_package_atom(x, allow_repo=False, allow_build_id=True):
78 if "/" not in x.split(":")[0]:
79 x2 = insert_category_into_atom(x, 'cat')
80 if x2 != None:
81 x = x2
82 - return isvalidatom(x, allow_blockers=False, allow_repo=allow_repo)
83 + return isvalidatom(x, allow_blockers=False, allow_repo=allow_repo,
84 + allow_build_id=allow_build_id)
85 diff --git a/pym/portage/_sets/ProfilePackageSet.py b/pym/portage/_sets/ProfilePackageSet.py
86 index 2fcafb6..fec9373 100644
87 --- a/pym/portage/_sets/ProfilePackageSet.py
88 +++ b/pym/portage/_sets/ProfilePackageSet.py
89 @@ -23,7 +23,8 @@ class ProfilePackageSet(PackageSet):
90 def load(self):
91 self._setAtoms(x for x in stack_lists(
92 [grabfile_package(os.path.join(y.location, "packages"),
93 - verify_eapi=True, eapi=y.eapi, eapi_default=None)
94 + verify_eapi=True, eapi=y.eapi, eapi_default=None,
95 + allow_build_id=y.allow_build_id)
96 for y in self._profiles
97 if "profile-set" in y.profile_formats],
98 incremental=1) if x[:1] != "*")
99 diff --git a/pym/portage/_sets/profiles.py b/pym/portage/_sets/profiles.py
100 index ccb3432..bccc02e 100644
101 --- a/pym/portage/_sets/profiles.py
102 +++ b/pym/portage/_sets/profiles.py
103 @@ -34,7 +34,8 @@ class PackagesSystemSet(PackageSet):
104 (self._profiles,), level=logging.DEBUG, noiselevel=-1)
105
106 mylist = [grabfile_package(os.path.join(x.location, "packages"),
107 - verify_eapi=True, eapi=x.eapi, eapi_default=None)
108 + verify_eapi=True, eapi=x.eapi, eapi_default=None,
109 + allow_build_id=x.allow_build_id)
110 for x in self._profiles]
111
112 if debug:
113 diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py
114 index e2e416c..1f2c8cd 100644
115 --- a/pym/portage/dep/__init__.py
116 +++ b/pym/portage/dep/__init__.py
117 @@ -1191,11 +1191,11 @@ class Atom(_unicode):
118 self.overlap = self._overlap(forbid=forbid_overlap)
119
120 def __new__(cls, s, unevaluated_atom=None, allow_wildcard=False, allow_repo=None,
121 - _use=None, eapi=None, is_valid_flag=None):
122 + _use=None, eapi=None, is_valid_flag=None, allow_build_id=None):
123 return _unicode.__new__(cls, s)
124
125 def __init__(self, s, unevaluated_atom=None, allow_wildcard=False, allow_repo=None,
126 - _use=None, eapi=None, is_valid_flag=None):
127 + _use=None, eapi=None, is_valid_flag=None, allow_build_id=None):
128 if isinstance(s, Atom):
129 # This is an efficiency assertion, to ensure that the Atom
130 # constructor is not called redundantly.
131 @@ -1216,8 +1216,13 @@ class Atom(_unicode):
132 # Ignore allow_repo when eapi is specified.
133 allow_repo = eapi_attrs.repo_deps
134 else:
135 + # These parameters have "smart" defaults that are only
136 + # applied when the caller does not explicitly pass in a
137 + # True or False value.
138 if allow_repo is None:
139 allow_repo = True
140 + if allow_build_id is None:
141 + allow_build_id = True
142
143 blocker_prefix = ""
144 if "!" == s[:1]:
145 @@ -1232,6 +1237,7 @@ class Atom(_unicode):
146 blocker = False
147 self.__dict__['blocker'] = blocker
148 m = atom_re.match(s)
149 + build_id = None
150 extended_syntax = False
151 extended_version = None
152 if m is None:
153 @@ -1268,8 +1274,22 @@ class Atom(_unicode):
154 slot = m.group(atom_re.groups - 2)
155 repo = m.group(atom_re.groups - 1)
156 use_str = m.group(atom_re.groups)
157 - if m.group(base + 4) is not None:
158 - raise InvalidAtom(self)
159 + version = m.group(base + 4)
160 + if version is not None:
161 + if allow_build_id:
162 + cpv_build_id = cpv
163 + cpv = cp
164 + cp = cp[:-len(version)]
165 + build_id = cpv_build_id[len(cpv)+1:]
166 + if len(build_id) > 1 and build_id[:1] == "0":
167 + # Leading zeros are not allowed.
168 + raise InvalidAtom(self)
169 + try:
170 + build_id = int(build_id)
171 + except ValueError:
172 + raise InvalidAtom(self)
173 + else:
174 + raise InvalidAtom(self)
175 elif m.group('star') is not None:
176 base = atom_re.groupindex['star']
177 op = '=*'
178 @@ -1332,6 +1352,7 @@ class Atom(_unicode):
179 self.__dict__['slot_operator'] = None
180 self.__dict__['operator'] = op
181 self.__dict__['extended_syntax'] = extended_syntax
182 + self.__dict__['build_id'] = build_id
183
184 if not (repo is None or allow_repo):
185 raise InvalidAtom(self)
186 @@ -1877,7 +1898,7 @@ def dep_getusedeps( depend ):
187 return tuple(use_list)
188
189 def isvalidatom(atom, allow_blockers=False, allow_wildcard=False,
190 - allow_repo=False, eapi=None):
191 + allow_repo=False, eapi=None, allow_build_id=False):
192 """
193 Check to see if a depend atom is valid
194
195 @@ -1902,7 +1923,8 @@ def isvalidatom(atom, allow_blockers=False, allow_wildcard=False,
196 try:
197 if not isinstance(atom, Atom):
198 atom = Atom(atom, allow_wildcard=allow_wildcard,
199 - allow_repo=allow_repo, eapi=eapi)
200 + allow_repo=allow_repo, eapi=eapi,
201 + allow_build_id=allow_build_id)
202 if not allow_blockers and atom.blocker:
203 return False
204 return True
205 @@ -2107,6 +2129,7 @@ def match_from_list(mydep, candidate_list):
206 mycpv = mydep.cpv
207 mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific
208 slot = mydep.slot
209 + build_id = mydep.build_id
210
211 if not mycpv_cps:
212 cat, pkg = catsplit(mycpv)
213 @@ -2181,6 +2204,9 @@ def match_from_list(mydep, candidate_list):
214 xcpv = remove_slot(x)
215 if not cpvequal(xcpv, mycpv):
216 continue
217 + if (build_id is not None and
218 + getattr(xcpv, "build_id", None) != build_id):
219 + continue
220 mylist.append(x)
221
222 elif operator == "=*": # glob match
223 diff --git a/pym/portage/package/ebuild/_config/KeywordsManager.py b/pym/portage/package/ebuild/_config/KeywordsManager.py
224 index e1a8e2b..72e24b9 100644
225 --- a/pym/portage/package/ebuild/_config/KeywordsManager.py
226 +++ b/pym/portage/package/ebuild/_config/KeywordsManager.py
227 @@ -22,7 +22,8 @@ class KeywordsManager(object):
228 rawpkeywords = [grabdict_package(
229 os.path.join(x.location, "package.keywords"),
230 recursive=x.portage1_directories,
231 - verify_eapi=True, eapi=x.eapi, eapi_default=None)
232 + verify_eapi=True, eapi=x.eapi, eapi_default=None,
233 + allow_build_id=x.allow_build_id)
234 for x in profiles]
235 for pkeyworddict in rawpkeywords:
236 if not pkeyworddict:
237 diff --git a/pym/portage/package/ebuild/_config/LocationsManager.py b/pym/portage/package/ebuild/_config/LocationsManager.py
238 index 34b33e9..55b8c08 100644
239 --- a/pym/portage/package/ebuild/_config/LocationsManager.py
240 +++ b/pym/portage/package/ebuild/_config/LocationsManager.py
241 @@ -31,7 +31,8 @@ _PORTAGE1_DIRECTORIES = frozenset([
242 'use.mask', 'use.force'])
243
244 _profile_node = collections.namedtuple('_profile_node',
245 - 'location portage1_directories user_config profile_formats eapi')
246 + ('location', 'portage1_directories', 'user_config',
247 + 'profile_formats', 'eapi', 'allow_build_id'))
248
249 _allow_parent_colon = frozenset(
250 ["portage-2"])
251 @@ -142,7 +143,8 @@ class LocationsManager(object):
252 _profile_node(custom_prof, True, True,
253 ('profile-bashrcs', 'profile-set'),
254 read_corresponding_eapi_file(
255 - custom_prof + os.sep, default=None)))
256 + custom_prof + os.sep, default=None),
257 + True))
258 del custom_prof
259
260 self.profiles = tuple(self.profiles)
261 @@ -253,7 +255,7 @@ class LocationsManager(object):
262 self.profiles.append(currentPath)
263 self.profiles_complex.append(
264 _profile_node(currentPath, allow_directories, False,
265 - current_formats, eapi))
266 + current_formats, eapi, 'build-id' in current_formats))
267
268 def _expand_parent_colon(self, parentsFile, parentPath,
269 repo_loc, repositories):
270 diff --git a/pym/portage/package/ebuild/_config/MaskManager.py b/pym/portage/package/ebuild/_config/MaskManager.py
271 index 55c8c7a..44aba23 100644
272 --- a/pym/portage/package/ebuild/_config/MaskManager.py
273 +++ b/pym/portage/package/ebuild/_config/MaskManager.py
274 @@ -40,7 +40,9 @@ class MaskManager(object):
275 pmask_cache[loc] = grabfile_package(path,
276 recursive=repo_config.portage1_profiles,
277 remember_source_file=True, verify_eapi=True,
278 - eapi_default=repo_config.eapi)
279 + eapi_default=repo_config.eapi,
280 + allow_build_id=("build-id"
281 + in repo_config.profile_formats))
282 if repo_config.portage1_profiles_compat and os.path.isdir(path):
283 warnings.warn(_("Repository '%(repo_name)s' is implicitly using "
284 "'portage-1' profile format in its profiles/package.mask, but "
285 @@ -107,7 +109,8 @@ class MaskManager(object):
286 continue
287 repo_lines = grabfile_package(os.path.join(repo.location, "profiles", "package.unmask"), \
288 recursive=1, remember_source_file=True,
289 - verify_eapi=True, eapi_default=repo.eapi)
290 + verify_eapi=True, eapi_default=repo.eapi,
291 + allow_build_id=("build-id" in repo.profile_formats))
292 lines = stack_lists([repo_lines], incremental=1, \
293 remember_source_file=True, warn_for_unmatched_removal=True,
294 strict_warn_for_unmatched_removal=strict_umatched_removal)
295 @@ -122,13 +125,15 @@ class MaskManager(object):
296 os.path.join(x.location, "package.mask"),
297 recursive=x.portage1_directories,
298 remember_source_file=True, verify_eapi=True,
299 - eapi=x.eapi, eapi_default=None))
300 + eapi=x.eapi, eapi_default=None,
301 + allow_build_id=x.allow_build_id))
302 if x.portage1_directories:
303 profile_pkgunmasklines.append(grabfile_package(
304 os.path.join(x.location, "package.unmask"),
305 recursive=x.portage1_directories,
306 remember_source_file=True, verify_eapi=True,
307 - eapi=x.eapi, eapi_default=None))
308 + eapi=x.eapi, eapi_default=None,
309 + allow_build_id=x.allow_build_id))
310 profile_pkgmasklines = stack_lists(profile_pkgmasklines, incremental=1, \
311 remember_source_file=True, warn_for_unmatched_removal=True,
312 strict_warn_for_unmatched_removal=strict_umatched_removal)
313 @@ -143,10 +148,14 @@ class MaskManager(object):
314 if user_config:
315 user_pkgmasklines = grabfile_package(
316 os.path.join(abs_user_config, "package.mask"), recursive=1, \
317 - allow_wildcard=True, allow_repo=True, remember_source_file=True, verify_eapi=False)
318 + allow_wildcard=True, allow_repo=True,
319 + remember_source_file=True, verify_eapi=False,
320 + allow_build_id=True)
321 user_pkgunmasklines = grabfile_package(
322 os.path.join(abs_user_config, "package.unmask"), recursive=1, \
323 - allow_wildcard=True, allow_repo=True, remember_source_file=True, verify_eapi=False)
324 + allow_wildcard=True, allow_repo=True,
325 + remember_source_file=True, verify_eapi=False,
326 + allow_build_id=True)
327
328 #Stack everything together. At this point, only user_pkgmasklines may contain -atoms.
329 #Don't warn for unmatched -atoms here, since we don't do it for any other user config file.
330 diff --git a/pym/portage/package/ebuild/_config/UseManager.py b/pym/portage/package/ebuild/_config/UseManager.py
331 index 60d5f92..a93ea5c 100644
332 --- a/pym/portage/package/ebuild/_config/UseManager.py
333 +++ b/pym/portage/package/ebuild/_config/UseManager.py
334 @@ -153,7 +153,8 @@ class UseManager(object):
335 return tuple(ret)
336
337 def _parse_file_to_dict(self, file_name, juststrings=False, recursive=True,
338 - eapi_filter=None, user_config=False, eapi=None, eapi_default="0"):
339 + eapi_filter=None, user_config=False, eapi=None, eapi_default="0",
340 + allow_build_id=False):
341 """
342 @param file_name: input file name
343 @type file_name: str
344 @@ -176,6 +177,9 @@ class UseManager(object):
345 @param eapi_default: the default EAPI which applies if the
346 current profile node does not define a local EAPI
347 @type eapi_default: str
348 + @param allow_build_id: allow atoms to specify a particular
349 + build-id
350 + @type allow_build_id: bool
351 @rtype: tuple
352 @return: collection of USE flags
353 """
354 @@ -192,7 +196,7 @@ class UseManager(object):
355 file_dict = grabdict_package(file_name, recursive=recursive,
356 allow_wildcard=extended_syntax, allow_repo=extended_syntax,
357 verify_eapi=(not extended_syntax), eapi=eapi,
358 - eapi_default=eapi_default)
359 + eapi_default=eapi_default, allow_build_id=allow_build_id)
360 if eapi is not None and eapi_filter is not None and not eapi_filter(eapi):
361 if file_dict:
362 writemsg(_("--- EAPI '%s' does not support '%s': '%s'\n") %
363 @@ -262,7 +266,8 @@ class UseManager(object):
364 for repo in repositories.repos_with_profiles():
365 ret[repo.name] = self._parse_file_to_dict(
366 os.path.join(repo.location, "profiles", file_name),
367 - eapi_filter=eapi_filter, eapi_default=repo.eapi)
368 + eapi_filter=eapi_filter, eapi_default=repo.eapi,
369 + allow_build_id=("build-id" in repo.profile_formats))
370 return ret
371
372 def _parse_profile_files_to_tuple_of_tuples(self, file_name, locations,
373 @@ -279,7 +284,8 @@ class UseManager(object):
374 os.path.join(profile.location, file_name), juststrings,
375 recursive=profile.portage1_directories, eapi_filter=eapi_filter,
376 user_config=profile.user_config, eapi=profile.eapi,
377 - eapi_default=None) for profile in locations)
378 + eapi_default=None, allow_build_id=profile.allow_build_id)
379 + for profile in locations)
380
381 def _parse_repository_usealiases(self, repositories):
382 ret = {}
383 diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py
384 index 71fe4df..f16d16e 100644
385 --- a/pym/portage/package/ebuild/config.py
386 +++ b/pym/portage/package/ebuild/config.py
387 @@ -569,7 +569,8 @@ class config(object):
388 try:
389 packages_list = [grabfile_package(
390 os.path.join(x.location, "packages"),
391 - verify_eapi=True, eapi=x.eapi, eapi_default=None)
392 + verify_eapi=True, eapi=x.eapi, eapi_default=None,
393 + allow_build_id=x.allow_build_id)
394 for x in profiles_complex]
395 except IOError as e:
396 if e.errno == IsADirectory.errno:
397 @@ -707,7 +708,8 @@ class config(object):
398 #package.properties
399 propdict = grabdict_package(os.path.join(
400 abs_user_config, "package.properties"), recursive=1, allow_wildcard=True, \
401 - allow_repo=True, verify_eapi=False)
402 + allow_repo=True, verify_eapi=False,
403 + allow_build_id=True)
404 v = propdict.pop("*/*", None)
405 if v is not None:
406 if "ACCEPT_PROPERTIES" in self.configdict["conf"]:
407 @@ -721,7 +723,8 @@ class config(object):
408 d = grabdict_package(os.path.join(
409 abs_user_config, "package.accept_restrict"),
410 recursive=True, allow_wildcard=True,
411 - allow_repo=True, verify_eapi=False)
412 + allow_repo=True, verify_eapi=False,
413 + allow_build_id=True)
414 v = d.pop("*/*", None)
415 if v is not None:
416 if "ACCEPT_RESTRICT" in self.configdict["conf"]:
417 @@ -734,7 +737,8 @@ class config(object):
418 #package.env
419 penvdict = grabdict_package(os.path.join(
420 abs_user_config, "package.env"), recursive=1, allow_wildcard=True, \
421 - allow_repo=True, verify_eapi=False)
422 + allow_repo=True, verify_eapi=False,
423 + allow_build_id=True)
424 v = penvdict.pop("*/*", None)
425 if v is not None:
426 global_wildcard_conf = {}
427 @@ -764,7 +768,8 @@ class config(object):
428 bashrc = grabdict_package(os.path.join(profile.location,
429 "package.bashrc"), recursive=1, allow_wildcard=True,
430 allow_repo=True, verify_eapi=True,
431 - eapi=profile.eapi, eapi_default=None)
432 + eapi=profile.eapi, eapi_default=None,
433 + allow_build_id=profile.allow_build_id)
434 if not bashrc:
435 continue
436
437 diff --git a/pym/portage/repository/config.py b/pym/portage/repository/config.py
438 index a884156..5da1810 100644
439 --- a/pym/portage/repository/config.py
440 +++ b/pym/portage/repository/config.py
441 @@ -42,7 +42,7 @@ _invalid_path_char_re = re.compile(r'[^a-zA-Z0-9._\-+:/]')
442
443 _valid_profile_formats = frozenset(
444 ['pms', 'portage-1', 'portage-2', 'profile-bashrcs', 'profile-set',
445 - 'profile-default-eapi'])
446 + 'profile-default-eapi', 'build-id'])
447
448 _portage1_profiles_allow_directories = frozenset(
449 ["portage-1-compat", "portage-1", 'portage-2'])
450 diff --git a/pym/portage/tests/dep/test_isvalidatom.py b/pym/portage/tests/dep/test_isvalidatom.py
451 index 67ba603..9d3367a 100644
452 --- a/pym/portage/tests/dep/test_isvalidatom.py
453 +++ b/pym/portage/tests/dep/test_isvalidatom.py
454 @@ -5,11 +5,13 @@ from portage.tests import TestCase
455 from portage.dep import isvalidatom
456
457 class IsValidAtomTestCase(object):
458 - def __init__(self, atom, expected, allow_wildcard=False, allow_repo=False):
459 + def __init__(self, atom, expected, allow_wildcard=False,
460 + allow_repo=False, allow_build_id=False):
461 self.atom = atom
462 self.expected = expected
463 self.allow_wildcard = allow_wildcard
464 self.allow_repo = allow_repo
465 + self.allow_build_id = allow_build_id
466
467 class IsValidAtom(TestCase):
468
469 @@ -154,5 +156,7 @@ class IsValidAtom(TestCase):
470 else:
471 atom_type = "invalid"
472 self.assertEqual(bool(isvalidatom(test_case.atom, allow_wildcard=test_case.allow_wildcard,
473 - allow_repo=test_case.allow_repo)), test_case.expected,
474 + allow_repo=test_case.allow_repo,
475 + allow_build_id=test_case.allow_build_id)),
476 + test_case.expected,
477 msg="isvalidatom(%s) != %s" % (test_case.atom, test_case.expected))
478 diff --git a/pym/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py b/pym/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py
479 new file mode 100644
480 index 0000000..0397509
481 --- /dev/null
482 +++ b/pym/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py
483 @@ -0,0 +1,134 @@
484 +# Copyright 2015 Gentoo Foundation
485 +# Distributed under the terms of the GNU General Public License v2
486 +
487 +from portage.tests import TestCase
488 +from portage.tests.resolver.ResolverPlayground import (ResolverPlayground,
489 + ResolverPlaygroundTestCase)
490 +
491 +class BuildIdProfileFormatTestCase(TestCase):
492 +
493 + def testBuildIdProfileFormat(self):
494 +
495 + profile = {
496 + "packages": ("=app-misc/A-1-2",),
497 + "package.provided": ("sys-libs/zlib-1.2.8-r1",),
498 + }
499 +
500 + repo_configs = {
501 + "test_repo": {
502 + "layout.conf": (
503 + "profile-formats = build-id profile-set",
504 + ),
505 + }
506 + }
507 +
508 + user_config = {
509 + "make.conf":
510 + (
511 + "FEATURES=\"binpkg-multi-instance\"",
512 + ),
513 + }
514 +
515 + ebuilds = {
516 + "app-misc/A-1" : {
517 + "EAPI": "5",
518 + "RDEPEND": "sys-libs/zlib dev-libs/B[foo]",
519 + "DEPEND": "sys-libs/zlib dev-libs/B[foo]",
520 + },
521 + "dev-libs/B-1" : {
522 + "EAPI": "5",
523 + "IUSE": "foo",
524 + },
525 + }
526 +
527 + binpkgs = (
528 + ("app-misc/A-1", {
529 + "EAPI": "5",
530 + "BUILD_ID": "1",
531 + "BUILD_TIME": "1",
532 + "RDEPEND": "sys-libs/zlib dev-libs/B[foo]",
533 + "DEPEND": "sys-libs/zlib dev-libs/B[foo]",
534 + }),
535 + ("app-misc/A-1", {
536 + "EAPI": "5",
537 + "BUILD_ID": "2",
538 + "BUILD_TIME": "2",
539 + "RDEPEND": "sys-libs/zlib dev-libs/B[foo]",
540 + "DEPEND": "sys-libs/zlib dev-libs/B[foo]",
541 + }),
542 + ("app-misc/A-1", {
543 + "EAPI": "5",
544 + "BUILD_ID": "3",
545 + "BUILD_TIME": "3",
546 + "RDEPEND": "sys-libs/zlib dev-libs/B[foo]",
547 + "DEPEND": "sys-libs/zlib dev-libs/B[foo]",
548 + }),
549 + ("dev-libs/B-1", {
550 + "EAPI": "5",
551 + "IUSE": "foo",
552 + "USE": "",
553 + "BUILD_ID": "1",
554 + "BUILD_TIME": "1",
555 + }),
556 + ("dev-libs/B-1", {
557 + "EAPI": "5",
558 + "IUSE": "foo",
559 + "USE": "foo",
560 + "BUILD_ID": "2",
561 + "BUILD_TIME": "2",
562 + }),
563 + ("dev-libs/B-1", {
564 + "EAPI": "5",
565 + "IUSE": "foo",
566 + "USE": "",
567 + "BUILD_ID": "3",
568 + "BUILD_TIME": "3",
569 + }),
570 + )
571 +
572 + installed = {
573 + "app-misc/A-1" : {
574 + "EAPI": "5",
575 + "BUILD_ID": "1",
576 + "BUILD_TIME": "1",
577 + "RDEPEND": "sys-libs/zlib",
578 + "DEPEND": "sys-libs/zlib",
579 + },
580 + "dev-libs/B-1" : {
581 + "EAPI": "5",
582 + "IUSE": "foo",
583 + "USE": "foo",
584 + "BUILD_ID": "2",
585 + "BUILD_TIME": "2",
586 + },
587 + }
588 +
589 + world = ()
590 +
591 + test_cases = (
592 +
593 + ResolverPlaygroundTestCase(
594 + ["@world"],
595 + options = {"--emptytree": True, "--usepkgonly": True},
596 + success = True,
597 + mergelist = [
598 + "[binary]dev-libs/B-1-2",
599 + "[binary]app-misc/A-1-2"
600 + ]
601 + ),
602 +
603 + )
604 +
605 + playground = ResolverPlayground(debug=False,
606 + binpkgs=binpkgs, ebuilds=ebuilds, installed=installed,
607 + repo_configs=repo_configs, profile=profile,
608 + user_config=user_config, world=world)
609 + try:
610 + for test_case in test_cases:
611 + playground.run_TestCase(test_case)
612 + self.assertEqual(test_case.test_success, True,
613 + test_case.fail_msg)
614 + finally:
615 + # Disable debug so that cleanup works.
616 + #playground.debug = False
617 + playground.cleanup()
618 diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py
619 index b6f5787..aeb951e 100644
620 --- a/pym/portage/util/__init__.py
621 +++ b/pym/portage/util/__init__.py
622 @@ -424,7 +424,8 @@ def read_corresponding_eapi_file(filename, default="0"):
623 return default
624 return eapi
625
626 -def grabdict_package(myfilename, juststrings=0, recursive=0, allow_wildcard=False, allow_repo=False,
627 +def grabdict_package(myfilename, juststrings=0, recursive=0,
628 + allow_wildcard=False, allow_repo=False, allow_build_id=False,
629 verify_eapi=False, eapi=None, eapi_default="0"):
630 """ Does the same thing as grabdict except it validates keys
631 with isvalidatom()"""
632 @@ -447,7 +448,8 @@ def grabdict_package(myfilename, juststrings=0, recursive=0, allow_wildcard=Fals
633 for k, v in d.items():
634 try:
635 k = Atom(k, allow_wildcard=allow_wildcard,
636 - allow_repo=allow_repo, eapi=eapi)
637 + allow_repo=allow_repo,
638 + allow_build_id=allow_build_id, eapi=eapi)
639 except InvalidAtom as e:
640 writemsg(_("--- Invalid atom in %s: %s\n") % (filename, e),
641 noiselevel=-1)
642 @@ -460,7 +462,8 @@ def grabdict_package(myfilename, juststrings=0, recursive=0, allow_wildcard=Fals
643
644 return atoms
645
646 -def grabfile_package(myfilename, compatlevel=0, recursive=0, allow_wildcard=False, allow_repo=False,
647 +def grabfile_package(myfilename, compatlevel=0, recursive=0,
648 + allow_wildcard=False, allow_repo=False, allow_build_id=False,
649 remember_source_file=False, verify_eapi=False, eapi=None,
650 eapi_default="0"):
651
652 @@ -480,7 +483,9 @@ def grabfile_package(myfilename, compatlevel=0, recursive=0, allow_wildcard=Fals
653 if pkg[:1] == '*' and mybasename == 'packages':
654 pkg = pkg[1:]
655 try:
656 - pkg = Atom(pkg, allow_wildcard=allow_wildcard, allow_repo=allow_repo, eapi=eapi)
657 + pkg = Atom(pkg, allow_wildcard=allow_wildcard,
658 + allow_repo=allow_repo, allow_build_id=allow_build_id,
659 + eapi=eapi)
660 except InvalidAtom as e:
661 writemsg(_("--- Invalid atom in %s: %s\n") % (source_file, e),
662 noiselevel=-1)
663 --
664 2.0.5