commit:     c6e3af2b1e419d70443a74575e9b762a101a3912
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Wed Feb 18 02:31:28 2015 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Wed Mar  4 21:32:07 2015 +0000
URL:        
http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=c6e3af2b

binpkg-multi-instance 7 of 7

Support "profile-formats = build-id" setting for layout.conf. When
this is enabled in layout.conf of the containing repository, a
dependency atom in the profile can refer to a specific build, using the
build-id that is assigned when FEATURES=binpkg-multi-instance is
enabled. A build-id atom is identical to a version-specific atom,
except that the version is followed by a hyphen and an integer build-id.

With the build-id profile format, it is possible to assemble a system
using specific builds of binary packages, as users of "binary"
distros might be accustomed to. For example, an atom in the "packages"
file can pull a specific build of a package into the @system set, and
an atom in the "package.keywords" file can be used to modify the
effective KEYWORDS of a specific build of a package.

Refering to specific builds can be useful for a number of reasons. For
example, if a particular build needs to undergo a large amount of
testing in a complex environment in order to verify reliability, then
it can be useful to lock a profile to a specific build that has been
thoroughly tested.

 man/portage.5                                      |   8 +-
 pym/_emerge/is_valid_package_atom.py               |   5 +-
 pym/portage/_sets/ProfilePackageSet.py             |   3 +-
 pym/portage/_sets/profiles.py                      |   3 +-
 pym/portage/dep/__init__.py                        |  38 +++++-
 .../package/ebuild/_config/KeywordsManager.py      |   3 +-
 .../package/ebuild/_config/LocationsManager.py     |   8 +-
 pym/portage/package/ebuild/_config/MaskManager.py  |  21 +++-
 pym/portage/package/ebuild/_config/UseManager.py   |  14 ++-
 pym/portage/package/ebuild/config.py               |  15 ++-
 pym/portage/repository/config.py                   |   2 +-
 pym/portage/tests/dep/test_isvalidatom.py          |   8 +-
 .../test_build_id_profile_format.py                | 134 +++++++++++++++++++++
 pym/portage/util/__init__.py                       |  13 +-
 14 files changed, 237 insertions(+), 38 deletions(-)

diff --git a/man/portage.5 b/man/portage.5
index 3b099ff..e77fc6e 100644
--- a/man/portage.5
+++ b/man/portage.5
@@ -1220,7 +1220,7 @@ and the newer/faster "md5-dict" format.  Default is to 
detect dirs.
 The EAPI to use for profiles when unspecified. This attribute is
 supported only if profile-default-eapi is included in profile-formats.
 .TP
-.BR profile\-formats " = [pms] [portage-1] [portage-2] [profile-bashrcs] 
[profile-set] [profile-default-eapi]"
+.BR profile\-formats " = [pms] [portage-1] [portage-2] [profile-bashrcs] 
[profile-set] [profile-default-eapi] [build-id]"
 Control functionality available to profiles in this repo such as which files
 may be dirs, or the syntax available in parent files.  Use "portage-2" if 
you're
 unsure.  The default is "portage-1-compat" mode which is meant to be compatible
@@ -1230,7 +1230,11 @@ Setting profile-bashrcs will enable the per-profile 
bashrc mechanism
 profile \fBpackages\fR file to add atoms to the @profile package set.
 See the profile \fBpackages\fR section for more information.
 Setting profile-default-eapi enables support for the
-profile_eapi_when_unspecified attribute.
+profile_eapi_when_unspecified attribute. Setting build\-id allows
+dependency atoms in the profile to refer to specific builds (see the
+binpkg\-multi\-instance FEATURES setting in \fBmake.conf\fR(5)). A
+build\-id atom is identical to a version-specific atom, except that the
+version is followed by a hyphen and an integer build\-id.
 .RE
 .RE
 

diff --git a/pym/_emerge/is_valid_package_atom.py 
b/pym/_emerge/is_valid_package_atom.py
index 112afc1..17f7642 100644
--- a/pym/_emerge/is_valid_package_atom.py
+++ b/pym/_emerge/is_valid_package_atom.py
@@ -14,9 +14,10 @@ def insert_category_into_atom(atom, category):
                ret = None
        return ret
 
-def is_valid_package_atom(x, allow_repo=False):
+def is_valid_package_atom(x, allow_repo=False, allow_build_id=True):
        if "/" not in x.split(":")[0]:
                x2 = insert_category_into_atom(x, 'cat')
                if x2 != None:
                        x = x2
-       return isvalidatom(x, allow_blockers=False, allow_repo=allow_repo)
+       return isvalidatom(x, allow_blockers=False, allow_repo=allow_repo,
+               allow_build_id=allow_build_id)

diff --git a/pym/portage/_sets/ProfilePackageSet.py 
b/pym/portage/_sets/ProfilePackageSet.py
index 2fcafb6..fec9373 100644
--- a/pym/portage/_sets/ProfilePackageSet.py
+++ b/pym/portage/_sets/ProfilePackageSet.py
@@ -23,7 +23,8 @@ class ProfilePackageSet(PackageSet):
        def load(self):
                self._setAtoms(x for x in stack_lists(
                        [grabfile_package(os.path.join(y.location, "packages"),
-                       verify_eapi=True, eapi=y.eapi, eapi_default=None)
+                       verify_eapi=True, eapi=y.eapi, eapi_default=None,
+                       allow_build_id=y.allow_build_id)
                        for y in self._profiles
                        if "profile-set" in y.profile_formats],
                        incremental=1) if x[:1] != "*")

diff --git a/pym/portage/_sets/profiles.py b/pym/portage/_sets/profiles.py
index ccb3432..bccc02e 100644
--- a/pym/portage/_sets/profiles.py
+++ b/pym/portage/_sets/profiles.py
@@ -34,7 +34,8 @@ class PackagesSystemSet(PackageSet):
                                (self._profiles,), level=logging.DEBUG, 
noiselevel=-1)
 
                mylist = [grabfile_package(os.path.join(x.location, "packages"),
-                       verify_eapi=True, eapi=x.eapi, eapi_default=None)
+                       verify_eapi=True, eapi=x.eapi, eapi_default=None,
+                       allow_build_id=x.allow_build_id)
                        for x in self._profiles]
 
                if debug:

diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py
index a8c748d..0a13d9f 100644
--- a/pym/portage/dep/__init__.py
+++ b/pym/portage/dep/__init__.py
@@ -1193,11 +1193,11 @@ class Atom(_unicode):
                        self.overlap = self._overlap(forbid=forbid_overlap)
 
        def __new__(cls, s, unevaluated_atom=None, allow_wildcard=False, 
allow_repo=None,
-               _use=None, eapi=None, is_valid_flag=None):
+               _use=None, eapi=None, is_valid_flag=None, allow_build_id=None):
                return _unicode.__new__(cls, s)
 
        def __init__(self, s, unevaluated_atom=None, allow_wildcard=False, 
allow_repo=None,
-               _use=None, eapi=None, is_valid_flag=None):
+               _use=None, eapi=None, is_valid_flag=None, allow_build_id=None):
                if isinstance(s, Atom):
                        # This is an efficiency assertion, to ensure that the 
Atom
                        # constructor is not called redundantly.
@@ -1218,8 +1218,13 @@ class Atom(_unicode):
                        # Ignore allow_repo when eapi is specified.
                        allow_repo = eapi_attrs.repo_deps
                else:
+                       # These parameters have "smart" defaults that are only
+                       # applied when the caller does not explicitly pass in a
+                       # True or False value.
                        if allow_repo is None:
                                allow_repo = True
+                       if allow_build_id is None:
+                               allow_build_id = True
 
                blocker_prefix = ""
                if "!" == s[:1]:
@@ -1234,6 +1239,7 @@ class Atom(_unicode):
                        blocker = False
                self.__dict__['blocker'] = blocker
                m = atom_re.match(s)
+               build_id = None
                extended_syntax = False
                extended_version = None
                if m is None:
@@ -1270,8 +1276,22 @@ class Atom(_unicode):
                        slot = m.group(atom_re.groups - 2)
                        repo = m.group(atom_re.groups - 1)
                        use_str = m.group(atom_re.groups)
-                       if m.group(base + 4) is not None:
-                               raise InvalidAtom(self)
+                       version = m.group(base + 4)
+                       if version is not None:
+                               if allow_build_id:
+                                       cpv_build_id = cpv
+                                       cpv = cp
+                                       cp = cp[:-len(version)]
+                                       build_id = cpv_build_id[len(cpv)+1:]
+                                       if len(build_id) > 1 and build_id[:1] 
== "0":
+                                               # Leading zeros are not allowed.
+                                               raise InvalidAtom(self)
+                                       try:
+                                               build_id = int(build_id)
+                                       except ValueError:
+                                               raise InvalidAtom(self)
+                               else:
+                                       raise InvalidAtom(self)
                elif m.group('star') is not None:
                        base = atom_re.groupindex['star']
                        op = '=*'
@@ -1334,6 +1354,7 @@ class Atom(_unicode):
                                self.__dict__['slot_operator'] = None
                self.__dict__['operator'] = op
                self.__dict__['extended_syntax'] = extended_syntax
+               self.__dict__['build_id'] = build_id
 
                if not (repo is None or allow_repo):
                        raise InvalidAtom(self)
@@ -1879,7 +1900,7 @@ def dep_getusedeps( depend ):
        return tuple(use_list)
 
 def isvalidatom(atom, allow_blockers=False, allow_wildcard=False,
-       allow_repo=False, eapi=None):
+       allow_repo=False, eapi=None, allow_build_id=False):
        """
        Check to see if a depend atom is valid
 
@@ -1904,7 +1925,8 @@ def isvalidatom(atom, allow_blockers=False, 
allow_wildcard=False,
        try:
                if not isinstance(atom, Atom):
                        atom = Atom(atom, allow_wildcard=allow_wildcard,
-                               allow_repo=allow_repo, eapi=eapi)
+                               allow_repo=allow_repo, eapi=eapi,
+                               allow_build_id=allow_build_id)
                if not allow_blockers and atom.blocker:
                        return False
                return True
@@ -2109,6 +2131,7 @@ def match_from_list(mydep, candidate_list):
        mycpv     = mydep.cpv
        mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific
        slot      = mydep.slot
+       build_id  = mydep.build_id
 
        if not mycpv_cps:
                cat, pkg = catsplit(mycpv)
@@ -2183,6 +2206,9 @@ def match_from_list(mydep, candidate_list):
                                xcpv = remove_slot(x)
                        if not cpvequal(xcpv, mycpv):
                                continue
+                       if (build_id is not None and
+                               getattr(xcpv, "build_id", None) != build_id):
+                               continue
                        mylist.append(x)
 
        elif operator == "=*": # glob match

diff --git a/pym/portage/package/ebuild/_config/KeywordsManager.py 
b/pym/portage/package/ebuild/_config/KeywordsManager.py
index e1a8e2b..72e24b9 100644
--- a/pym/portage/package/ebuild/_config/KeywordsManager.py
+++ b/pym/portage/package/ebuild/_config/KeywordsManager.py
@@ -22,7 +22,8 @@ class KeywordsManager(object):
                rawpkeywords = [grabdict_package(
                        os.path.join(x.location, "package.keywords"),
                        recursive=x.portage1_directories,
-                       verify_eapi=True, eapi=x.eapi, eapi_default=None)
+                       verify_eapi=True, eapi=x.eapi, eapi_default=None,
+                       allow_build_id=x.allow_build_id)
                        for x in profiles]
                for pkeyworddict in rawpkeywords:
                        if not pkeyworddict:

diff --git a/pym/portage/package/ebuild/_config/LocationsManager.py 
b/pym/portage/package/ebuild/_config/LocationsManager.py
index 34b33e9..55b8c08 100644
--- a/pym/portage/package/ebuild/_config/LocationsManager.py
+++ b/pym/portage/package/ebuild/_config/LocationsManager.py
@@ -31,7 +31,8 @@ _PORTAGE1_DIRECTORIES = frozenset([
        'use.mask', 'use.force'])
 
 _profile_node = collections.namedtuple('_profile_node',
-       'location portage1_directories user_config profile_formats eapi')
+       ('location', 'portage1_directories', 'user_config',
+       'profile_formats', 'eapi', 'allow_build_id'))
 
 _allow_parent_colon = frozenset(
        ["portage-2"])
@@ -142,7 +143,8 @@ class LocationsManager(object):
                                        _profile_node(custom_prof, True, True,
                                        ('profile-bashrcs', 'profile-set'),
                                        read_corresponding_eapi_file(
-                                       custom_prof + os.sep, default=None)))
+                                       custom_prof + os.sep, default=None),
+                                       True))
                        del custom_prof
 
                self.profiles = tuple(self.profiles)
@@ -253,7 +255,7 @@ class LocationsManager(object):
                self.profiles.append(currentPath)
                self.profiles_complex.append(
                        _profile_node(currentPath, allow_directories, False,
-                               current_formats, eapi))
+                               current_formats, eapi, 'build-id' in 
current_formats))
 
        def _expand_parent_colon(self, parentsFile, parentPath,
                repo_loc, repositories):

diff --git a/pym/portage/package/ebuild/_config/MaskManager.py 
b/pym/portage/package/ebuild/_config/MaskManager.py
index 55c8c7a..44aba23 100644
--- a/pym/portage/package/ebuild/_config/MaskManager.py
+++ b/pym/portage/package/ebuild/_config/MaskManager.py
@@ -40,7 +40,9 @@ class MaskManager(object):
                                pmask_cache[loc] = grabfile_package(path,
                                                
recursive=repo_config.portage1_profiles,
                                                remember_source_file=True, 
verify_eapi=True,
-                                               eapi_default=repo_config.eapi)
+                                               eapi_default=repo_config.eapi,
+                                               allow_build_id=("build-id"
+                                               in repo_config.profile_formats))
                                if repo_config.portage1_profiles_compat and 
os.path.isdir(path):
                                        warnings.warn(_("Repository 
'%(repo_name)s' is implicitly using "
                                                "'portage-1' profile format in 
its profiles/package.mask, but "
@@ -107,7 +109,8 @@ class MaskManager(object):
                                continue
                        repo_lines = 
grabfile_package(os.path.join(repo.location, "profiles", "package.unmask"), \
                                recursive=1, remember_source_file=True,
-                               verify_eapi=True, eapi_default=repo.eapi)
+                               verify_eapi=True, eapi_default=repo.eapi,
+                               allow_build_id=("build-id" in 
repo.profile_formats))
                        lines = stack_lists([repo_lines], incremental=1, \
                                remember_source_file=True, 
warn_for_unmatched_removal=True,
                                
strict_warn_for_unmatched_removal=strict_umatched_removal)
@@ -122,13 +125,15 @@ class MaskManager(object):
                                os.path.join(x.location, "package.mask"),
                                recursive=x.portage1_directories,
                                remember_source_file=True, verify_eapi=True,
-                               eapi=x.eapi, eapi_default=None))
+                               eapi=x.eapi, eapi_default=None,
+                               allow_build_id=x.allow_build_id))
                        if x.portage1_directories:
                                profile_pkgunmasklines.append(grabfile_package(
                                        os.path.join(x.location, 
"package.unmask"),
                                        recursive=x.portage1_directories,
                                        remember_source_file=True, 
verify_eapi=True,
-                                       eapi=x.eapi, eapi_default=None))
+                                       eapi=x.eapi, eapi_default=None,
+                                       allow_build_id=x.allow_build_id))
                profile_pkgmasklines = stack_lists(profile_pkgmasklines, 
incremental=1, \
                        remember_source_file=True, 
warn_for_unmatched_removal=True,
                        
strict_warn_for_unmatched_removal=strict_umatched_removal)
@@ -143,10 +148,14 @@ class MaskManager(object):
                if user_config:
                        user_pkgmasklines = grabfile_package(
                                os.path.join(abs_user_config, "package.mask"), 
recursive=1, \
-                               allow_wildcard=True, allow_repo=True, 
remember_source_file=True, verify_eapi=False)
+                               allow_wildcard=True, allow_repo=True,
+                               remember_source_file=True, verify_eapi=False,
+                               allow_build_id=True)
                        user_pkgunmasklines = grabfile_package(
                                os.path.join(abs_user_config, 
"package.unmask"), recursive=1, \
-                               allow_wildcard=True, allow_repo=True, 
remember_source_file=True, verify_eapi=False)
+                               allow_wildcard=True, allow_repo=True,
+                               remember_source_file=True, verify_eapi=False,
+                               allow_build_id=True)
 
                #Stack everything together. At this point, only 
user_pkgmasklines may contain -atoms.
                #Don't warn for unmatched -atoms here, since we don't do it for 
any other user config file.

diff --git a/pym/portage/package/ebuild/_config/UseManager.py 
b/pym/portage/package/ebuild/_config/UseManager.py
index 60d5f92..a93ea5c 100644
--- a/pym/portage/package/ebuild/_config/UseManager.py
+++ b/pym/portage/package/ebuild/_config/UseManager.py
@@ -153,7 +153,8 @@ class UseManager(object):
                return tuple(ret)
 
        def _parse_file_to_dict(self, file_name, juststrings=False, 
recursive=True,
-               eapi_filter=None, user_config=False, eapi=None, 
eapi_default="0"):
+               eapi_filter=None, user_config=False, eapi=None, 
eapi_default="0",
+               allow_build_id=False):
                """
                @param file_name: input file name
                @type file_name: str
@@ -176,6 +177,9 @@ class UseManager(object):
                @param eapi_default: the default EAPI which applies if the
                        current profile node does not define a local EAPI
                @type eapi_default: str
+               @param allow_build_id: allow atoms to specify a particular
+                       build-id
+               @type allow_build_id: bool
                @rtype: tuple
                @return: collection of USE flags
                """
@@ -192,7 +196,7 @@ class UseManager(object):
                file_dict = grabdict_package(file_name, recursive=recursive,
                        allow_wildcard=extended_syntax, 
allow_repo=extended_syntax,
                        verify_eapi=(not extended_syntax), eapi=eapi,
-                       eapi_default=eapi_default)
+                       eapi_default=eapi_default, 
allow_build_id=allow_build_id)
                if eapi is not None and eapi_filter is not None and not 
eapi_filter(eapi):
                        if file_dict:
                                writemsg(_("--- EAPI '%s' does not support 
'%s': '%s'\n") %
@@ -262,7 +266,8 @@ class UseManager(object):
                for repo in repositories.repos_with_profiles():
                        ret[repo.name] = self._parse_file_to_dict(
                                os.path.join(repo.location, "profiles", 
file_name),
-                               eapi_filter=eapi_filter, eapi_default=repo.eapi)
+                               eapi_filter=eapi_filter, eapi_default=repo.eapi,
+                               allow_build_id=("build-id" in 
repo.profile_formats))
                return ret
 
        def _parse_profile_files_to_tuple_of_tuples(self, file_name, locations,
@@ -279,7 +284,8 @@ class UseManager(object):
                        os.path.join(profile.location, file_name), juststrings,
                        recursive=profile.portage1_directories, 
eapi_filter=eapi_filter,
                        user_config=profile.user_config, eapi=profile.eapi,
-                       eapi_default=None) for profile in locations)
+                       eapi_default=None, 
allow_build_id=profile.allow_build_id)
+                       for profile in locations)
 
        def _parse_repository_usealiases(self, repositories):
                ret = {}

diff --git a/pym/portage/package/ebuild/config.py 
b/pym/portage/package/ebuild/config.py
index 3c0018f..3a4007b 100644
--- a/pym/portage/package/ebuild/config.py
+++ b/pym/portage/package/ebuild/config.py
@@ -570,7 +570,8 @@ class config(object):
                        try:
                                packages_list = [grabfile_package(
                                        os.path.join(x.location, "packages"),
-                                       verify_eapi=True, eapi=x.eapi, 
eapi_default=None)
+                                       verify_eapi=True, eapi=x.eapi, 
eapi_default=None,
+                                       allow_build_id=x.allow_build_id)
                                        for x in profiles_complex]
                        except IOError as e:
                                if e.errno == IsADirectory.errno:
@@ -708,7 +709,8 @@ class config(object):
                                #package.properties
                                propdict = grabdict_package(os.path.join(
                                        abs_user_config, "package.properties"), 
recursive=1, allow_wildcard=True, \
-                                       allow_repo=True, verify_eapi=False)
+                                       allow_repo=True, verify_eapi=False,
+                                       allow_build_id=True)
                                v = propdict.pop("*/*", None)
                                if v is not None:
                                        if "ACCEPT_PROPERTIES" in 
self.configdict["conf"]:
@@ -722,7 +724,8 @@ class config(object):
                                d = grabdict_package(os.path.join(
                                        abs_user_config, 
"package.accept_restrict"),
                                        recursive=True, allow_wildcard=True,
-                                       allow_repo=True, verify_eapi=False)
+                                       allow_repo=True, verify_eapi=False,
+                                       allow_build_id=True)
                                v = d.pop("*/*", None)
                                if v is not None:
                                        if "ACCEPT_RESTRICT" in 
self.configdict["conf"]:
@@ -735,7 +738,8 @@ class config(object):
                                #package.env
                                penvdict = grabdict_package(os.path.join(
                                        abs_user_config, "package.env"), 
recursive=1, allow_wildcard=True, \
-                                       allow_repo=True, verify_eapi=False)
+                                       allow_repo=True, verify_eapi=False,
+                                       allow_build_id=True)
                                v = penvdict.pop("*/*", None)
                                if v is not None:
                                        global_wildcard_conf = {}
@@ -765,7 +769,8 @@ class config(object):
                                        bashrc = 
grabdict_package(os.path.join(profile.location,
                                                "package.bashrc"), recursive=1, 
allow_wildcard=True,
                                                                
allow_repo=True, verify_eapi=True,
-                                                               
eapi=profile.eapi, eapi_default=None)
+                                                               
eapi=profile.eapi, eapi_default=None,
+                                                               
allow_build_id=profile.allow_build_id)
                                        if not bashrc:
                                                continue
 

diff --git a/pym/portage/repository/config.py b/pym/portage/repository/config.py
index a884156..5da1810 100644
--- a/pym/portage/repository/config.py
+++ b/pym/portage/repository/config.py
@@ -42,7 +42,7 @@ _invalid_path_char_re = re.compile(r'[^a-zA-Z0-9._\-+:/]')
 
 _valid_profile_formats = frozenset(
        ['pms', 'portage-1', 'portage-2', 'profile-bashrcs', 'profile-set',
-       'profile-default-eapi'])
+       'profile-default-eapi', 'build-id'])
 
 _portage1_profiles_allow_directories = frozenset(
        ["portage-1-compat", "portage-1", 'portage-2'])

diff --git a/pym/portage/tests/dep/test_isvalidatom.py 
b/pym/portage/tests/dep/test_isvalidatom.py
index 67ba603..9d3367a 100644
--- a/pym/portage/tests/dep/test_isvalidatom.py
+++ b/pym/portage/tests/dep/test_isvalidatom.py
@@ -5,11 +5,13 @@ from portage.tests import TestCase
 from portage.dep import isvalidatom
 
 class IsValidAtomTestCase(object):
-       def __init__(self, atom, expected, allow_wildcard=False, 
allow_repo=False):
+       def __init__(self, atom, expected, allow_wildcard=False,
+               allow_repo=False, allow_build_id=False):
                self.atom = atom
                self.expected = expected
                self.allow_wildcard = allow_wildcard
                self.allow_repo = allow_repo
+               self.allow_build_id = allow_build_id
 
 class IsValidAtom(TestCase):
 
@@ -154,5 +156,7 @@ class IsValidAtom(TestCase):
                        else:
                                atom_type = "invalid"
                        self.assertEqual(bool(isvalidatom(test_case.atom, 
allow_wildcard=test_case.allow_wildcard,
-                               allow_repo=test_case.allow_repo)), 
test_case.expected,
+                               allow_repo=test_case.allow_repo,
+                               allow_build_id=test_case.allow_build_id)),
+                               test_case.expected,
                                msg="isvalidatom(%s) != %s" % (test_case.atom, 
test_case.expected))

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
new file mode 100644
index 0000000..0397509
--- /dev/null
+++ 
b/pym/portage/tests/resolver/binpkg_multi_instance/test_build_id_profile_format.py
@@ -0,0 +1,134 @@
+# Copyright 2015 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.tests import TestCase
+from portage.tests.resolver.ResolverPlayground import (ResolverPlayground,
+       ResolverPlaygroundTestCase)
+
+class BuildIdProfileFormatTestCase(TestCase):
+
+       def testBuildIdProfileFormat(self):
+
+               profile = {
+                       "packages": ("=app-misc/A-1-2",),
+                       "package.provided": ("sys-libs/zlib-1.2.8-r1",),
+               }
+
+               repo_configs = {
+                       "test_repo": {
+                               "layout.conf": (
+                                       "profile-formats = build-id 
profile-set",
+                               ),
+                       }
+               }
+
+               user_config = {
+                       "make.conf":
+                               (
+                                       "FEATURES=\"binpkg-multi-instance\"",
+                               ),
+               }
+
+               ebuilds = {
+                       "app-misc/A-1" : {
+                               "EAPI": "5",
+                               "RDEPEND": "sys-libs/zlib dev-libs/B[foo]",
+                               "DEPEND": "sys-libs/zlib dev-libs/B[foo]",
+                       },
+                       "dev-libs/B-1" : {
+                               "EAPI": "5",
+                               "IUSE": "foo",
+                       },
+               }
+
+               binpkgs = (
+                       ("app-misc/A-1", {
+                               "EAPI": "5",
+                               "BUILD_ID": "1",
+                               "BUILD_TIME": "1",
+                               "RDEPEND": "sys-libs/zlib dev-libs/B[foo]",
+                               "DEPEND": "sys-libs/zlib dev-libs/B[foo]",
+                       }),
+                       ("app-misc/A-1", {
+                               "EAPI": "5",
+                               "BUILD_ID": "2",
+                               "BUILD_TIME": "2",
+                               "RDEPEND": "sys-libs/zlib dev-libs/B[foo]",
+                               "DEPEND": "sys-libs/zlib dev-libs/B[foo]",
+                       }),
+                       ("app-misc/A-1", {
+                               "EAPI": "5",
+                               "BUILD_ID": "3",
+                               "BUILD_TIME": "3",
+                               "RDEPEND": "sys-libs/zlib dev-libs/B[foo]",
+                               "DEPEND": "sys-libs/zlib dev-libs/B[foo]",
+                       }),
+                       ("dev-libs/B-1", {
+                               "EAPI": "5",
+                               "IUSE": "foo",
+                               "USE": "",
+                               "BUILD_ID": "1",
+                               "BUILD_TIME": "1",
+                       }),
+                       ("dev-libs/B-1", {
+                               "EAPI": "5",
+                               "IUSE": "foo",
+                               "USE": "foo",
+                               "BUILD_ID": "2",
+                               "BUILD_TIME": "2",
+                       }),
+                       ("dev-libs/B-1", {
+                               "EAPI": "5",
+                               "IUSE": "foo",
+                               "USE": "",
+                               "BUILD_ID": "3",
+                               "BUILD_TIME": "3",
+                       }),
+               )
+
+               installed = {
+                       "app-misc/A-1" : {
+                               "EAPI": "5",
+                               "BUILD_ID": "1",
+                               "BUILD_TIME": "1",
+                               "RDEPEND": "sys-libs/zlib",
+                               "DEPEND": "sys-libs/zlib",
+                       },
+                       "dev-libs/B-1" : {
+                               "EAPI": "5",
+                               "IUSE": "foo",
+                               "USE": "foo",
+                               "BUILD_ID": "2",
+                               "BUILD_TIME": "2",
+                       },
+               }
+
+               world = ()
+
+               test_cases = (
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--emptytree": True, "--usepkgonly": 
True},
+                               success = True,
+                               mergelist = [
+                                       "[binary]dev-libs/B-1-2",
+                                       "[binary]app-misc/A-1-2"
+                               ]
+                       ),
+
+               )
+
+               playground = ResolverPlayground(debug=False,
+                       binpkgs=binpkgs, ebuilds=ebuilds, installed=installed,
+                       repo_configs=repo_configs, profile=profile,
+                       user_config=user_config, world=world)
+               try:
+                       for test_case in test_cases:
+                               playground.run_TestCase(test_case)
+                               self.assertEqual(test_case.test_success, True,
+                                       test_case.fail_msg)
+               finally:
+                       # Disable debug so that cleanup works.
+                       #playground.debug = False
+                       playground.cleanup()

diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py
index b6f5787..aeb951e 100644
--- a/pym/portage/util/__init__.py
+++ b/pym/portage/util/__init__.py
@@ -424,7 +424,8 @@ def read_corresponding_eapi_file(filename, default="0"):
                return default
        return eapi
 
-def grabdict_package(myfilename, juststrings=0, recursive=0, 
allow_wildcard=False, allow_repo=False,
+def grabdict_package(myfilename, juststrings=0, recursive=0,
+       allow_wildcard=False, allow_repo=False, allow_build_id=False,
        verify_eapi=False, eapi=None, eapi_default="0"):
        """ Does the same thing as grabdict except it validates keys
                with isvalidatom()"""
@@ -447,7 +448,8 @@ def grabdict_package(myfilename, juststrings=0, 
recursive=0, allow_wildcard=Fals
                for k, v in d.items():
                        try:
                                k = Atom(k, allow_wildcard=allow_wildcard,
-                                       allow_repo=allow_repo, eapi=eapi)
+                                       allow_repo=allow_repo,
+                                       allow_build_id=allow_build_id, 
eapi=eapi)
                        except InvalidAtom as e:
                                writemsg(_("--- Invalid atom in %s: %s\n") % 
(filename, e),
                                        noiselevel=-1)
@@ -460,7 +462,8 @@ def grabdict_package(myfilename, juststrings=0, 
recursive=0, allow_wildcard=Fals
 
        return atoms
 
-def grabfile_package(myfilename, compatlevel=0, recursive=0, 
allow_wildcard=False, allow_repo=False,
+def grabfile_package(myfilename, compatlevel=0, recursive=0,
+       allow_wildcard=False, allow_repo=False, allow_build_id=False,
        remember_source_file=False, verify_eapi=False, eapi=None,
        eapi_default="0"):
 
@@ -480,7 +483,9 @@ def grabfile_package(myfilename, compatlevel=0, 
recursive=0, allow_wildcard=Fals
                if pkg[:1] == '*' and mybasename == 'packages':
                        pkg = pkg[1:]
                try:
-                       pkg = Atom(pkg, allow_wildcard=allow_wildcard, 
allow_repo=allow_repo, eapi=eapi)
+                       pkg = Atom(pkg, allow_wildcard=allow_wildcard,
+                               allow_repo=allow_repo, 
allow_build_id=allow_build_id,
+                               eapi=eapi)
                except InvalidAtom as e:
                        writemsg(_("--- Invalid atom in %s: %s\n") % 
(source_file, e),
                                noiselevel=-1)

Reply via email to