From fc078d3d83ac9cc2d9373fa15f82096150edb564 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Sun, 25 May 2008 17:51:33 +0000 Subject: [PATCH] Merged from trunk 10382:10393 | 10383 | Raise an error if the named repository doesn't exist | | genone | | | 10387 | Bug #2272 - Extend dependency atom sytax to specify enabled | | zmedico | or disabled states of USE flags. Matching with the new | | | syntax is currently only supported in the dbapi classes and | | | dependency resolver (use matching does not work yet in | | | config files such as package.mask). | | 10388 | Bug #220671 - Fix 'RuntimeError: Set changed size during | | zmedico | iteration' error. | | 10389 | Bug #223417 - use floating point mtime for finer grained | | zmedico | validation in cachedir(). | | 10390 | Bug #223417 - Make the vardbapi.cpv_all() use_cache | | zmedico | parameter useful for forcing direct os.listdir() calls. This | | | is more of an issue now that these listdir() calls are | | | frequently triggered when merging packages (due to things | | | like blocker and preserve-libs handling). | | 10391 | Fix use_reduce() so that it appropriately raises an | | zmedico | InvalidDependString instead of an IndexError in some cases | | | (avoid IndexError by using slice notation). | | 10392 | Fix paren_reduce() so that it appropriately raises an | | zmedico | InvalidDependString() in some cases, instead of a nonsense | | | AttributeError. | | 10393 | Fix paren_reduce() so that it appropriately raises an | | zmedico | InvalidDependString() in some cases, instead of a nonsense | | | ValueError. | svn path=/main/branches/prefix/; revision=10417 --- bin/repoman | 9 +++- pym/_emerge/__init__.py | 77 ++++++++++++++++++++++++---- pym/portage/__init__.py | 95 ++++++++++++++++------------------- pym/portage/dbapi/__init__.py | 54 +++++++++++++++++--- pym/portage/dbapi/porttree.py | 40 +++++++-------- pym/portage/dbapi/vartree.py | 40 +++++++++------ pym/portage/dep.py | 87 +++++++++++++++++++++++++++++--- pym/portage/sets/files.py | 10 +++- 8 files changed, 300 insertions(+), 112 deletions(-) diff --git a/bin/repoman b/bin/repoman index 6db5168e4..d19bca864 100755 --- a/bin/repoman +++ b/bin/repoman @@ -507,7 +507,7 @@ portdb = trees["/"]["porttree"].dbapi portdb.mysettings = repoman_settings # We really only need to cache the metadata that's necessary for visibility # filtering. Anything else can be discarded to reduce memory consumption. -for k in ("DEPEND", "IUSE", "LICENCE", "PDEPEND", +for k in ("DEPEND", "LICENCE", "PDEPEND", "PROVIDE", "RDEPEND", "RESTRICT", "repository"): portdb._aux_cache_keys.discard(k) # dep_zapdeps looks at the vardbapi, but it shouldn't for repoman. @@ -1256,6 +1256,7 @@ for x in scanlist: is_blocker = atom.startswith("!") if is_blocker: atom = token.lstrip("!") + atom = portage.dep.Atom(atom) if mytype == "DEPEND" and \ not is_blocker and \ not inherited_java_eclass and \ @@ -1275,6 +1276,12 @@ for x in scanlist: (relative_path + ": %s slot dependency" + \ " not supported with EAPI='%s':" + \ " '%s'") % (mytype, eapi, atom)) + if atom.use and eapi in ("0", "1"): + stats['EAPI.incompatible'] += 1 + fails['EAPI.incompatible'].append( + (relative_path + ": %s use dependency" + \ + " not supported with EAPI='%s':" + \ + " '%s'") % (mytype, eapi, atom)) type_list.extend([mytype] * (len(badsyntax) - len(type_list))) diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index 118d12ff8..e652026b6 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -1013,7 +1013,7 @@ class FakeVartree(portage.vartree): vdb_lock = portage.locks.lockdir(vdb_path) real_dbapi = real_vartree.dbapi slot_counters = {} - for cpv in real_dbapi.cpv_all(): + for cpv in real_dbapi.cpv_all(use_cache=0): cache_key = ("installed", self.root, cpv, "nomerge") pkg = self._pkg_cache.get(cache_key) if pkg is not None: @@ -2841,6 +2841,13 @@ class depgraph(object): return selected_atoms def _show_unsatisfied_dep(self, root, atom, myparent=None, arg=None): + atom = portage.dep.Atom(atom) + atom_without_use = atom + if atom.use: + atom_without_use = portage.dep.remove_slot(atom) + if atom.slot: + atom_without_use += ":" + atom.slot + atom_without_use = portage.dep.Atom(atom_without_use) xinfo = '"%s"' % atom if arg: xinfo='"%s"' % arg @@ -2851,9 +2858,11 @@ class depgraph(object): green('"%s"' % myparent[2]) + \ red(' [%s]' % myparent[0]) + ')' masked_packages = [] + missing_use = [] missing_licenses = [] have_eapi_mask = False pkgsettings = self.pkgsettings[root] + implicit_iuse = pkgsettings._get_implicit_iuse() root_config = self.roots[root] portdb = self.roots[root].trees["porttree"].dbapi dbs = self._filtered_trees[root]["dbs"] @@ -2862,18 +2871,61 @@ class depgraph(object): continue match = db.match if hasattr(db, "xmatch"): - cpv_list = db.xmatch("match-all", atom) + cpv_list = db.xmatch("match-all", atom_without_use) else: - cpv_list = db.match(atom) + cpv_list = db.match(atom_without_use) # descending order cpv_list.reverse() for cpv in cpv_list: metadata, mreasons = get_mask_info(root_config, cpv, pkgsettings, db, pkg_type, built, installed, db_keys) - masked_packages.append( - (root_config, pkgsettings, cpv, metadata, mreasons)) - - if masked_packages: + if atom.use and not mreasons: + missing_use.append(Package(built=built, cpv=cpv, + installed=installed, metadata=metadata, root=root)) + else: + masked_packages.append( + (root_config, pkgsettings, cpv, metadata, mreasons)) + + missing_use_reasons = [] + missing_iuse_reasons = [] + for pkg in missing_use: + use = pkg.metadata["USE"].split() + iuse = implicit_iuse.union(x.lstrip("+-") \ + for x in pkg.metadata["IUSE"].split()) + iuse_re = re.compile("^(%s)$" % "|".join(iuse)) + missing_iuse = [] + for x in atom.use.required: + if iuse_re.match(x) is None: + missing_iuse.append(x) + mreasons = [] + if missing_iuse: + mreasons.append("Missing IUSE: %s" % " ".join(missing_iuse)) + missing_iuse_reasons.append((pkg, mreasons)) + else: + need_enable = sorted(atom.use.enabled.difference(use)) + need_disable = sorted(atom.use.disabled.intersection(use)) + if need_enable or need_disable: + changes = [] + changes.extend(colorize("red", "+" + x) \ + for x in need_enable) + changes.extend(colorize("blue", "-" + x) \ + for x in need_disable) + mreasons.append("Change USE: %s" % " ".join(changes)) + missing_use_reasons.append((pkg, mreasons)) + + if missing_iuse_reasons and not missing_use_reasons: + missing_use_reasons = missing_iuse_reasons + elif missing_use_reasons: + # Only show the latest version. + del missing_use_reasons[1:] + + if missing_use_reasons: + print "\nemerge: there are no ebuilds built with USE flags to satisfy "+green(xinfo)+"." + print "!!! One of the following packages is required to complete your request:" + for pkg, mreasons in missing_use_reasons: + print "- "+pkg.cpv+" ("+", ".join(mreasons)+")" + + elif masked_packages: print "\n!!! "+red("All ebuilds that could satisfy ")+green(xinfo)+red(" have been masked.") print "!!! One of the following masked packages is required to complete your request:" have_eapi_mask = show_masked_packages(masked_packages) @@ -2923,7 +2975,8 @@ class depgraph(object): # List of acceptable packages, ordered by type preference. matched_packages = [] highest_version = None - atom_cp = portage.dep_getkey(atom) + atom = portage.dep.Atom(atom) + atom_cp = atom.cp existing_node = None myeb = None usepkgonly = "--usepkgonly" in self.myopts @@ -3055,11 +3108,17 @@ class depgraph(object): # it's not the same version. continue - if not built and not calculated_use: + if not pkg.built and not calculated_use: # This is avoided whenever possible because # it's expensive. pkgsettings.setcpv(cpv, mydb=pkg.metadata) pkg.metadata["USE"] = pkgsettings["PORTAGE_USE"] + if atom.use and not pkg.built: + use = pkg.metadata["USE"].split() + if atom.use.enabled.difference(use): + continue + if atom.use.disabled.intersection(use): + continue if pkg.cp == atom_cp: if highest_version is None: highest_version = pkg diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 10dd13270..50e47f0dc 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -203,7 +203,7 @@ def cacheddir(my_original_path, ignorecvs, ignorelist, EmptyOnError, followSymli try: pathstat = os.stat(mypath) if stat.S_ISDIR(pathstat[stat.ST_MODE]): - mtime = pathstat[stat.ST_MTIME] + mtime = pathstat.st_mtime else: raise portage.exception.DirectoryNotFound(mypath) except EnvironmentError, e: @@ -2050,56 +2050,13 @@ class config(object): # Filter out USE flags that aren't part of IUSE. This has to # be done for every setcpv() call since practically every - # package has different IUSE. Some flags are considered to - # be implicit members of IUSE: - # - # * Flags derived from ARCH - # * Flags derived from USE_EXPAND_HIDDEN variables - # * Masked flags, such as those from {,package}use.mask - # * Forced flags, such as those from {,package}use.force - # * build and bootstrap flags used by bootstrap.sh - + # package has different IUSE. use = set(self["USE"].split()) - iuse_implicit = set(x.lstrip("+-") for x in iuse.split()) - - # Flags derived from ARCH. - arch = self.configdict["defaults"].get("ARCH") - if arch: - iuse_implicit.add(arch) - iuse_implicit.update(self.get("PORTAGE_ARCHLIST", "").split()) - - # Flags derived from USE_EXPAND_HIDDEN variables - # such as ELIBC, KERNEL, and USERLAND. - use_expand_hidden = self.get("USE_EXPAND_HIDDEN", "").split() - use_expand_hidden_raw = use_expand_hidden - if use_expand_hidden: - use_expand_hidden = re.compile("^(%s)_.*" % \ - ("|".join(x.lower() for x in use_expand_hidden))) - for x in use: - if use_expand_hidden.match(x): - iuse_implicit.add(x) - - # Flags that have been masked or forced. - iuse_implicit.update(self.usemask) - iuse_implicit.update(self.useforce) - - # build and bootstrap flags used by bootstrap.sh - iuse_implicit.add("build") - iuse_implicit.add("bootstrap") - - # prefix flag is used in Prefix - iuse_implicit.add("prefix") + iuse_implicit = self._get_implicit_iuse() + iuse_implicit.update(x.lstrip("+-") for x in iuse.split()) - if ebuild_phase: - iuse_grep = iuse_implicit.copy() - if use_expand_hidden_raw: - for x in use_expand_hidden_raw: - iuse_grep.add(x.lower() + "_.*") - if iuse_grep: - iuse_grep = "^(%s)$" % "|".join(sorted(iuse_grep)) - else: - iuse_grep = "" - self.configdict["pkg"]["PORTAGE_IUSE"] = iuse_grep + self.configdict["pkg"]["PORTAGE_IUSE"] = \ + "^(%s)$" % "|".join(sorted(iuse_implicit)) ebuild_force_test = self.get("EBUILD_FORCE_TEST") == "1" if ebuild_force_test and ebuild_phase and \ @@ -2193,6 +2150,42 @@ class config(object): x for x in use if \ x in iuse_implicit)) + def _get_implicit_iuse(self): + """ + Some flags are considered to + be implicit members of IUSE: + * Flags derived from ARCH + * Flags derived from USE_EXPAND_HIDDEN variables + * Masked flags, such as those from {,package}use.mask + * Forced flags, such as those from {,package}use.force + * build and bootstrap flags used by bootstrap.sh + """ + iuse_implicit = set() + # Flags derived from ARCH. + arch = self.configdict["defaults"].get("ARCH") + if arch: + iuse_implicit.add(arch) + iuse_implicit.update(self.get("PORTAGE_ARCHLIST", "").split()) + + # Flags derived from USE_EXPAND_HIDDEN variables + # such as ELIBC, KERNEL, and USERLAND. + use_expand_hidden = self.get("USE_EXPAND_HIDDEN", "").split() + for x in use_expand_hidden: + iuse_implicit.add(x.lower() + "_.*") + + # Flags that have been masked or forced. + iuse_implicit.update(self.usemask) + iuse_implicit.update(self.useforce) + + # build and bootstrap flags used by bootstrap.sh + iuse_implicit.add("build") + iuse_implicit.add("bootstrap") + + # prefix flag is used in Prefix + iuse_implicit.add("prefix") + + return iuse_implicit + def getMaskAtom(self, cpv, metadata): """ Take a package and return a matching package.mask atom, or None if no @@ -5775,8 +5768,8 @@ def dep_expand(mydep, mydb=None, use_cache=1, settings=None): myindex = orig_dep.index(mydep) prefix = orig_dep[:myindex] postfix = orig_dep[myindex+len(mydep):] - return prefix + cpv_expand( - mydep, mydb=mydb, use_cache=use_cache, settings=settings) + postfix + return portage.dep.Atom(prefix + cpv_expand( + mydep, mydb=mydb, use_cache=use_cache, settings=settings) + postfix) def dep_check(depstring, mydbapi, mysettings, use="yes", mode=None, myuse=None, use_cache=1, use_binaries=0, myroot="/", trees=None): diff --git a/pym/portage/dbapi/__init__.py b/pym/portage/dbapi/__init__.py index 1431c1074..95219322f 100644 --- a/pym/portage/dbapi/__init__.py +++ b/pym/portage/dbapi/__init__.py @@ -4,7 +4,8 @@ import os import re -from portage.dep import dep_getslot, dep_getkey, match_from_list +from portage.dep import Atom, dep_getslot, dep_getkey, \ + dep_getusedeps, match_from_list from portage.locks import unlockfile from portage.output import red from portage.util import writemsg @@ -16,6 +17,8 @@ class dbapi(object): _category_re = re.compile(r'^\w[-.+\w]*$') _pkg_dir_name_re = re.compile(r'^\w[-+\w]*$') _categories = None + _iuse_implicit = None + _use_mutable = False _known_keys = frozenset(x for x in auxdbkeys if not x.startswith("UNUSED_0")) def __init__(self): @@ -119,13 +122,48 @@ class dbapi(object): a list of packages that match origdep """ mydep = dep_expand(origdep, mydb=self, settings=self.settings) - mykey = dep_getkey(mydep) - mylist = match_from_list(mydep, self.cp_list(mykey, use_cache=use_cache)) - myslot = dep_getslot(mydep) - if myslot is not None: - mylist = [cpv for cpv in mylist \ - if self.aux_get(cpv, ["SLOT"])[0] == myslot] - return mylist + return list(self._iter_match(mydep, + self.cp_list(mydep.cp, use_cache=use_cache))) + + def _iter_match(self, atom, cpv_iter): + cpv_iter = match_from_list(atom, cpv_iter) + if atom.slot: + cpv_iter = self._iter_match_slot(atom, cpv_iter) + if atom.use: + cpv_iter = self._iter_match_use(atom, cpv_iter) + return cpv_iter + + def _iter_match_slot(self, atom, cpv_iter): + for cpv in cpv_iter: + if self.aux_get(cpv, ["SLOT"])[0] == atom.slot: + yield cpv + + def _iter_match_use(self, atom, cpv_iter): + """ + 1) Check for required IUSE intersection (need implicit IUSE here). + 2) Check enabled/disabled flag states. + """ + if self._iuse_implicit is None: + self._iuse_implicit = self.settings._get_implicit_iuse() + for cpv in cpv_iter: + iuse, use = self.aux_get(cpv, ["IUSE", "USE"]) + use = use.split() + iuse = self._iuse_implicit.union( + x.lstrip("+-") for x in iuse.split()) + iuse_re = re.compile("^(%s)$" % "|".join(iuse)) + missing_iuse = False + for x in atom.use.required: + if iuse_re.match(x) is None: + missing_iuse = True + break + if missing_iuse: + continue + if not self._use_mutable: + if atom.use.enabled.difference(use): + continue + if atom.use.disabled.intersection(use): + continue + yield cpv def invalidentry(self, mypath): if mypath.endswith('portage_lockfile'): diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py index 70135be34..7fe361570 100644 --- a/pym/portage/dbapi/porttree.py +++ b/pym/portage/dbapi/porttree.py @@ -27,6 +27,7 @@ from itertools import izip class portdbapi(dbapi): """this tree will scan a portage directory located at root (passed to init)""" portdbapi_instances = [] + _use_mutable = True def __init__(self, porttree_root, mysettings=None): portdbapi.portdbapi_instances.append(self) @@ -36,6 +37,7 @@ class portdbapi(dbapi): else: from portage import settings self.mysettings = config(clone=settings) + self._iuse_implicit = self.mysettings._get_implicit_iuse() self._categories = set(self.mysettings.categories) # This is strictly for use in aux_get() doebuild calls when metadata # is generated by the depend phase. It's safest to use a clone for @@ -615,22 +617,14 @@ class portdbapi(dbapi): # Find the minimum matching version. This is optimized to # minimize the number of metadata accesses (improves performance # especially in cases where metadata needs to be generated). - if mydep == mykey: - mylist = self.cp_list(mykey) - else: - mylist = match_from_list(mydep, self.cp_list(mykey)) + cpv_iter = iter(self.cp_list(mykey)) + if mydep != mykey: + cpv_iter = self._iter_match(mydep, cpv_iter) myval = "" - if mylist: - if myslot is None: - myval = mylist[0] - else: - for cpv in mylist: - try: - if self.aux_get(cpv, ["SLOT"])[0] == myslot: - myval = cpv - break - except KeyError: - pass # ebuild masked by corruption + for cpv in cpv_iter: + myval = cpv + break + elif level in ("minimum-visible", "bestmatch-visible"): # Find the minimum matching visible version. This is optimized to # minimize the number of metadata accesses (improves performance @@ -674,29 +668,35 @@ class portdbapi(dbapi): continue except InvalidDependString: continue + if mydep.use: + has_iuse = False + for has_iuse in self._iter_match_use(mydep, [cpv]): + break + if not has_iuse: + continue myval = cpv break elif level == "bestmatch-list": #dep match -- find best match but restrict search to sublist #no point in calling xmatch again since we're not caching list deps - myval = best(match_from_list(mydep, mylist)) + myval = best(list(self._iter_match(mydep, mylist))) elif level == "match-list": #dep match -- find all matches but restrict search to sublist (used in 2nd half of visible()) - myval = match_from_list(mydep, mylist) + myval = list(self._iter_match(mydep, mylist)) elif level == "match-visible": #dep match -- find all visible matches #get all visible packages, then get the matching ones - myval = match_from_list(mydep, - self.xmatch("list-visible", mykey, mydep=mykey, mykey=mykey)) + myval = list(self._iter_match(mydep, + self.xmatch("list-visible", mykey, mydep=mykey, mykey=mykey))) elif level == "match-all": #match *all* visible *and* masked packages if mydep == mykey: myval = self.cp_list(mykey) else: - myval = match_from_list(mydep, self.cp_list(mykey)) + myval = list(self._iter_match(mydep, self.cp_list(mykey))) else: print "ERROR: xmatch doesn't handle", level, "query!" raise KeyError diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index dc6de78cd..94b80591b 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -136,7 +136,7 @@ class LinkageMap(object): libs = {} obj_properties = {} lines = [] - for cpv in self._dbapi.cpv_all(): + for cpv in self._dbapi.cpv_all(use_cache=0): lines += self._dbapi.aux_get(cpv, ["NEEDED.ELF.2"])[0].split('\n') # Cache NEEDED.* files avoid doing excessive IO for every rebuild. self._dbapi.flush_cache() @@ -558,8 +558,25 @@ class vardbapi(dbapi): return returnme def cpv_all(self, use_cache=1): + """ + Set use_cache=0 to bypass the portage.cachedir() cache in cases + when the accuracy of mtime staleness checks should not be trusted + (generally this is only necessary in critical sections that + involve merge or unmerge of packages). + """ returnme = [] basepath = os.path.join(self.root, VDB_PATH) + os.path.sep + + if use_cache: + from portage import listdir + else: + def listdir(p, **kwargs): + try: + return [x for x in os.listdir(p) \ + if os.path.isdir(os.path.join(p, x))] + except EnvironmentError: + return [] + for x in listdir(basepath, EmptyOnError=1, ignorecvs=1, dirsonly=1): if self._excluded_dirs.match(x) is not None: continue @@ -610,13 +627,8 @@ class vardbapi(dbapi): if self.matchcache.has_key(mycat): del self.mtdircache[mycat] del self.matchcache[mycat] - mymatch = match_from_list(mydep, - self.cp_list(mykey, use_cache=use_cache)) - myslot = dep_getslot(mydep) - if myslot is not None: - mymatch = [cpv for cpv in mymatch \ - if self.aux_get(cpv, ["SLOT"])[0] == myslot] - return mymatch + return list(self._iter_match(mydep, + self.cp_list(mydep.cp, use_cache=use_cache))) try: curmtime = os.stat(self.root+VDB_PATH+"/"+mycat)[stat.ST_MTIME] except (IOError, OSError): @@ -627,11 +639,8 @@ class vardbapi(dbapi): self.mtdircache[mycat] = curmtime self.matchcache[mycat] = {} if not self.matchcache[mycat].has_key(mydep): - mymatch = match_from_list(mydep, self.cp_list(mykey, use_cache=use_cache)) - myslot = dep_getslot(mydep) - if myslot is not None: - mymatch = [cpv for cpv in mymatch \ - if self.aux_get(cpv, ["SLOT"])[0] == myslot] + mymatch = list(self._iter_match(mydep, + self.cp_list(mydep.cp, use_cache=use_cache))) self.matchcache[mycat][mydep] = mymatch return self.matchcache[mycat][mydep][:] @@ -1893,7 +1902,9 @@ class dblink(object): # inject files that should be preserved into our image dir import shutil missing_paths = [] - for x in candidates: + candidates_stack = list(candidates) + while candidates_stack: + x = candidates_stack.pop() # skip existing files so the 'new' libs aren't overwritten if os.path.exists(os.path.join(srcroot, x.lstrip(os.sep))): missing_paths.append(x) @@ -1916,6 +1927,7 @@ class dblink(object): if linktarget[0] != os.sep: linktarget = os.path.join(os.path.dirname(x), linktarget) candidates.add(linktarget) + candidates_stack.append(linktarget) else: shutil.copy2(os.path.join(destroot, x.lstrip(os.sep)), os.path.join(srcroot, x.lstrip(os.sep))) diff --git a/pym/portage/dep.py b/pym/portage/dep.py index b12411d63..fa87a7719 100644 --- a/pym/portage/dep.py +++ b/pym/portage/dep.py @@ -108,13 +108,20 @@ def paren_reduce(mystr,tokenize=1): "missing right parenthesis: '%s'" % mystr) elif has_left_paren and left_paren < right_paren: freesec,subsec = mystr.split("(",1) - subsec,tail = paren_reduce(subsec,tokenize) + sublist = paren_reduce(subsec, tokenize=tokenize) + if len(sublist) != 2: + raise portage.exception.InvalidDependString( + "malformed syntax: '%s'" % mystr) + subsec, tail = sublist else: subsec,tail = mystr.split(")",1) if tokenize: subsec = strip_empty(subsec.split(" ")) return [mylist+subsec,tail] return mylist+[subsec],tail + if not isinstance(tail, basestring): + raise portage.exception.InvalidDependString( + "malformed syntax: '%s'" % mystr) mystr = tail if freesec: if tokenize: @@ -230,7 +237,7 @@ def use_reduce(deparray, uselist=[], masklist=[], matchall=0, excludeall=[]): rlist.append([]) else: - if head[-1] == "?": # Use reduce next group on fail. + if head[-1:] == "?": # Use reduce next group on fail. # Pull any other use conditions and the following atom or list into a separate array newdeparray = [head] while isinstance(newdeparray[-1], str) and newdeparray[-1][-1] == "?": @@ -329,6 +336,37 @@ def dep_opconvert(deplist): x += 1 return retlist +class _use_dep(object): + def __init__(self, use): + enabled_flags = [] + disabled_flags = [] + for x in use: + if "-" == x[:1]: + disabled_flags.append(x[1:]) + else: + enabled_flags.append(x) + self.enabled = frozenset(enabled_flags) + self.disabled = frozenset(disabled_flags) + self.required = self.enabled.union(self.disabled) + +class Atom(str): + + def __init__(self, s): + str.__init__(self, s) + if not isvalidatom(s, allow_blockers=True): + raise InvalidAtom(s) + self.blocker = "!" == s[:1] + if self.blocker: + s = s[1:] + self.cp = dep_getkey(s) + self.cpv = dep_getcpv(s) + self.slot = dep_getslot(s) + self.operator = get_operator(s) + #self.repo = self._get_repo(s) + self.use = dep_getusedeps(s) + if self.use: + self.use = _use_dep(self.use) + def get_operator(mydep): """ Return the operator used in a depstring. @@ -344,6 +382,9 @@ def get_operator(mydep): @return: The operator. One of: '~', '=', '>', '<', '=*', '>=', or '<=' """ + operator = getattr(mydep, "operator", None) + if operator is not None: + return operator if mydep: mydep = remove_slot(mydep) if not mydep: @@ -380,6 +421,9 @@ def dep_getcpv(mydep): @rtype: String @return: The depstring with the operator removed """ + cpv = getattr(mydep, "cpv", None) + if cpv is not None: + return cpv global _dep_getcpv_cache retval = _dep_getcpv_cache.get(mydep, None) if retval is not None: @@ -413,15 +457,32 @@ def dep_getslot(mydep): @rtype: String @return: The slot """ - colon = mydep.rfind(":") + slot = getattr(mydep, "slot", None) + if slot is not None: + return slot + colon = mydep.find(":") if colon != -1: - return mydep[colon+1:] + bracket = mydep.find("[", colon) + if bracket == -1: + return mydep[colon+1:] + else: + return mydep[colon+1:bracket] return None def remove_slot(mydep): - colon = mydep.rfind(":") + """ + Removes dep components from the right side of an atom: + * slot + * use + * repo + """ + colon = mydep.find(":") if colon != -1: mydep = mydep[:colon] + else: + bracket = mydep.find("[") + if bracket != -1: + mydep = mydep[:bracket] return mydep def dep_getusedeps( depend ): @@ -437,6 +498,9 @@ def dep_getusedeps( depend ): @rtype: List @return: List of use flags ( or [] if no flags exist ) """ + use = getattr(depend, "use", None) + if use is not None: + return use use_list = [] open_bracket = depend.find('[') # -1 = failure (think c++ string::npos) @@ -452,7 +516,7 @@ def dep_getusedeps( depend ): raise InvalidAtom("USE Dependency with no use flag ([]): %s" % depend ) # Find next use flag open_bracket = depend.find( '[', open_bracket+1 ) - return use_list + return tuple(use_list) _invalid_atom_chars_regexp = re.compile("[()|?@]") @@ -473,6 +537,10 @@ def isvalidatom(atom, allow_blockers=False): 1) 0 if the atom is invalid 2) 1 if the atom is valid """ + if isinstance(atom, Atom): + if atom.blocker and not allow_blockers: + return 0 + return 1 global _invalid_atom_chars_regexp if _invalid_atom_chars_regexp.search(atom): return 0 @@ -571,6 +639,9 @@ def dep_getkey(mydep): @rtype: String @return: The package category/package-version """ + cp = getattr(mydep, "cp", None) + if cp is not None: + return cp mydep = dep_getcpv(mydep) if mydep and isspecific(mydep): mysplit = catpkgsplit(mydep) @@ -646,8 +717,10 @@ def match_from_list(mydep, candidate_list): """ from portage.util import writemsg - if mydep[0] == "!": + if "!" == mydep[:1]: mydep = mydep[1:] + if not isinstance(mydep, Atom): + mydep = Atom(mydep) mycpv = dep_getcpv(mydep) mycpv_cps = catpkgsplit(mycpv) # Can be None if not specific diff --git a/pym/portage/sets/files.py b/pym/portage/sets/files.py index 7ed29e2ec..714915c6d 100644 --- a/pym/portage/sets/files.py +++ b/pym/portage/sets/files.py @@ -100,7 +100,10 @@ class StaticFileSet(EditablePackageSet): # look for repository path variables match = self._repopath_match.match(filename) if match: - filename = self._repopath_sub.sub(trees["porttree"].dbapi.treemap[match.groupdict()["reponame"]], filename) + try: + filename = self._repopath_sub.sub(trees["porttree"].dbapi.treemap[match.groupdict()["reponame"]], filename) + except KeyError: + raise SetConfigError("Could not find repository '%s'" % match.groupdict()["reponame"]) return StaticFileSet(filename, greedy=greedy, dbapi=trees["vartree"].dbapi) singleBuilder = classmethod(singleBuilder) @@ -114,7 +117,10 @@ class StaticFileSet(EditablePackageSet): # look for repository path variables match = self._repopath_match.match(directory) if match: - directory = self._repopath_sub.sub(trees["porttree"].dbapi.treemap[match.groupdict()["reponame"]], directory) + try: + directory = self._repopath_sub.sub(trees["porttree"].dbapi.treemap[match.groupdict()["reponame"]], directory) + except KeyError: + raise SetConfigError("Could not find repository '%s'" % match.groupdict()["reponame"]) if os.path.isdir(directory): for filename in os.listdir(directory): if filename.endswith(".metadata"): -- 2.26.2