Merged from trunk 8340:8355
authorFabian Groffen <grobian@gentoo.org>
Thu, 1 Nov 2007 19:23:20 +0000 (19:23 -0000)
committerFabian Groffen <grobian@gentoo.org>
Thu, 1 Nov 2007 19:23:20 +0000 (19:23 -0000)
   | 8341    | Implement a "consistent" depgraph parameter (enabled by      |
   | zmedico | --consistent) that can be used ensure that installation of   |
   |         | new packages does not break any deep dependencies of         |
   |         | required sets (args, system, or world). Unfortunately, the   |
   |         | performance penalty for small dep calculations is too great  |
   |         | to enable this parameter by default. At least it will be     |
   |         | useful for testing backtracking behavior when that is        |
   |         | implemented.                                                 |

   | 8342    | Optimize xmatch "bestmatch-visible" to do fewer metadata     |
   | zmedico | accesses by using the same code as "minimum-visible" but     |
   |         | with a reverse iterator.                                     |

   | 8343    | Implement license filtering for xmatch minimum-visible and   |
   | zmedico | bestmatch-visible. Also, make the gvisible license filtering |
   |         | conditional on config.local_config since repoman doesn't do  |
   |         | license filtering.                                           |

   | 8344    | Fix "args" set handling wrt $ROOT in                         |
   | zmedico | depgraph._complete_graph().                                  |

   | 8345    | Remove a few unused depgraph attributes.                     |
   | zmedico |                                                              |

   | 8346    | Gather some ignored dependencies so that they can be         |
   | zmedico | properly processed by the depgraph when "conistent" mode is  |
   |         | enabled.                                                     |

   | 8347    | Defer selection of package dependency atoms until they are   |
   | zmedico | actually needed since the appropriate atom selection         |
   |         | behavior can change between the time that the package is     |
   |         | initially added to the graph and when it's dependencies are  |
   |         | traversed (like when --consistent is enabled).               |

   | 8348    | Update the FEATURES=mirror docs to more accurately reflect   |
   | zmedico | behavior.                                                    |

   | 8349    | Fix diefunc() so that it doesn't report that the ebuild is   |
   | zmedico | from an overlay when EMERGE_FROM == binary.                  |

   | 8350    | Make depgraph.select_files() recursively expand sets given   |
   | zmedico | as arguments so that containment tests in _get_parent_sets() |
   |         | properly match atoms in nested sets (like if world contains  |
   |         | system). Otherwise, atoms from nested sets would get         |
   |         | recorded in the world file.                                  |

   | 8351    | Add a check for redundant cd "${S}" statements on the first  |
   | zmedico | line of src_(compile|install|test) ebuild methods. Thanks to |
   |         | Petteri Räty <betelgeuse@gentoo.org> for this patch.        |

   | 8352    | Avoid making unnecessary copies of PackageSet._atoms by      |
   | zmedico | replacing calls to self.getAtoms() with self._load().        |

   | 8353    | Make the EbuildUselessCdS check for both quoted and unquoted |
   | zmedico | ${S} so the user doesn't have to fix quoting before this     |
   |         | check will recognize the violation.                          |

   | 8354    | Remove an extra \$ from the EbuildQuote.var_reference regex  |
   | zmedico | so that it will properly match things like variable          |
   |         | references without braces, like $S.                          |

   | 8355    | Properly escape { and } characters in regular expressions    |
   | zmedico | even though it seems to work either way.                     |

svn path=/main/branches/prefix/; revision=8356

bin/isolated-functions.sh
bin/repoman
man/make.conf.5
pym/_emerge/__init__.py
pym/portage/dbapi/porttree.py
pym/portage/sets/base.py
pym/portage/sets/files.py
pym/repoman/checks.py
pym/repoman/errors.py

index 30296761888562c93a89019a79bbfd37b2b23012..3991285cf5bd9094ebfab1e4ec3cd5c38a3ccf5e 100755 (executable)
@@ -90,7 +90,8 @@ diefunc() {
                        eerror "  ${x}"
                done
        fi
-       if [[ "${EBUILD#${PORTDIR}/}" == "${EBUILD}" ]] ; then
+       if [ "${EMERGE_FROM}" != "binary" ] && \
+               [ "${EBUILD#${PORTDIR}/}" == "${EBUILD}" ] ; then
                local overlay=${EBUILD%/*}
                overlay=${overlay%/*}
                overlay=${overlay%/*}
index dcb0bc1764e70a3b9d6a6adc9d454ecbdf0dac0d..a596f231494fc4784dd4df76d0464bfaf545b7a4 100755 (executable)
@@ -45,12 +45,12 @@ del os.environ["PORTAGE_LEGACY_GLOBALS"]
 
 try:
        from repoman.checks import EbuildWhitespace, EbuildHeader, EbuildQuote, \
-               EbuildAssignment, EbuildNestedDie, EbuildUselessDodoc
+               EbuildAssignment, EbuildNestedDie, EbuildUselessDodoc, EbuildUselessCdS
 except ImportError:
        from os import path as osp
        sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), 'pym'))
        from repoman.checks import EbuildWhitespace, EbuildHeader, EbuildQuote, \
-               EbuildAssignment, EbuildNestedDie, EbuildUselessDodoc
+               EbuildAssignment, EbuildNestedDie, EbuildUselessDodoc, EbuildUselessCdS
 
 try:
        import cStringIO as StringIO
@@ -1436,7 +1436,7 @@ for x in scanlist:
                myear = time.gmtime(os.stat(path)[ST_MTIME])[0]
                contents = StringIO.StringIO(open(path, 'rb').read())
                for check in (EbuildWhitespace, EbuildQuote,
-                       EbuildAssignment, EbuildUselessDodoc):
+                       EbuildAssignment, EbuildUselessDodoc, EbuildUselessCdS):
                        c = check(contents)
                        errors = c.Run()
                        for e in errors:
index bea1f9ef203c241d8e33e113ff2d5b6c2866467f..c612b89fdf7031a6f5692474855682e8fdf93121 100644 (file)
@@ -211,8 +211,8 @@ enabled for more than a short period of time.
 Automatically perform a metadata transfer when `emerge \-\-sync` is run.
 .TP
 .B mirror
-This will cause \fBFETCHCOMMAND\fR to always be run even if 
-the file already exists in \fBDISTDIR\fR.
+Fetch everything in \fBSRC_URI\fR regardless of \fBUSE\fR settings,
+except do not fetch anything when \fImirror\fR is in \fBRESTRICT\fR.
 .TP
 .B noauto
 When utilizing \fBebuild\fR(1), only run the function requested.
index 4ba9467d98bf1d60218c8c49e64d8c082f1a08f8..6c8d0b48db59a099da39649141ceb8e6197249af 100644 (file)
@@ -189,6 +189,7 @@ options=[
 "--ask",          "--alphabetical",
 "--buildpkg",     "--buildpkgonly",
 "--changelog",    "--columns",
+"--consistent",
 "--debug",        "--deep",
 "--digest",
 "--emptytree",
@@ -365,6 +366,9 @@ def create_depgraph_params(myopts, myaction):
        # recurse:   go into the dependencies
        # deep:      go into the dependencies of already merged packages
        # empty:     pretend nothing is merged
+       # consistent: ensure that installation of new packages does not break
+       #            any deep dependencies of required sets (args, system, or
+       #            world).
        myparams = set(["recurse"])
        if "--update" in myopts or \
                "--newuse" in myopts or \
@@ -378,6 +382,8 @@ def create_depgraph_params(myopts, myaction):
                myparams.discard("recurse")
        if "--deep" in myopts:
                myparams.add("deep")
+       if "--consistent" in myopts:
+               myparams.add("consistent")
        return myparams
 
 
@@ -891,7 +897,7 @@ def iter_atoms(deps):
                                yield x
 
 class Package(object):
-       __slots__ = ("__weakref__", "built", "cpv",
+       __slots__ = ("__weakref__", "built", "cpv", "depth",
                "installed", "metadata", "root", "onlydeps", "type_name",
                "_digraph_node", "_slot_atom")
        def __init__(self, **kwargs):
@@ -918,7 +924,7 @@ class Package(object):
                return self._digraph_node
 
 class Dependency(object):
-       __slots__ = ("__weakref__", "arg", "atom", "blocker", "depth",
+       __slots__ = ("__weakref__", "atom", "blocker", "depth",
                "parent", "priority", "root")
        def __init__(self, **kwargs):
                for myattr in self.__slots__:
@@ -1105,6 +1111,9 @@ class depgraph(object):
                # Contains a filtered view of preferred packages that are selected
                # from available repositories.
                self._filtered_trees = {}
+               # Contains installed packages and new packages that have been added
+               # to the graph.
+               self._graph_trees = {}
                for myroot in trees:
                        self.trees[myroot] = {}
                        for tree in ("porttree", "bintree"):
@@ -1132,6 +1141,12 @@ class depgraph(object):
                                        fakedb.cpv_inject(pkg,
                                                metadata=dict(izip(self._mydbapi_keys,
                                                vardb.aux_get(pkg, self._mydbapi_keys))))
+                       def graph_tree():
+                               pass
+                       graph_tree.dbapi = fakedb
+                       self._graph_trees[myroot] = {}
+                       self._graph_trees[myroot]["porttree"] = graph_tree
+                       self._graph_trees[myroot]["vartree"] = self.trees[myroot]["vartree"]
                        del vardb, fakedb
                        self._filtered_trees[myroot] = {}
                        self._filtered_trees[myroot]["vartree"] = self.trees[myroot]["vartree"]
@@ -1161,13 +1176,10 @@ class depgraph(object):
                                        "--getbinpkgonly" in self.myopts)
                del trees
 
-               self.missingbins=[]
                self.digraph=portage.digraph()
                # Tracks simple parent/child relationships (PDEPEND relationships are
                # not reversed).
                self._parent_child_digraph = digraph()
-               self.orderedkeys=[]
-               self.outdatedpackages=[]
                # contains all sets added to the graph
                self._sets = {}
                # contains atoms given as arguments
@@ -1188,6 +1200,11 @@ class depgraph(object):
                self._pprovided_args = []
                self._missing_args = []
                self._dep_stack = []
+               self._unsatisfied_deps = []
+               self._ignored_deps = []
+               self._required_set_names = set(["args", "system", "world"])
+               self._select_atoms = self._select_atoms_highest_available
+               self._select_package = self._select_pkg_highest_available
 
        def _show_slot_collision_notice(self, packages):
                """Show an informational message advising the user to mask one of the
@@ -1260,15 +1277,20 @@ class depgraph(object):
                                return flags
                return None
 
-       def _create_graph(self):
+       def _create_graph(self, allow_unsatisfied=False):
                debug = "--debug" in self.myopts
                buildpkgonly = "--buildpkgonly" in self.myopts
                nodeps = "--nodeps" in self.myopts
                empty = "empty" in self.myparams
                deep = "deep" in self.myparams
+               consistent = "consistent" in self.myparams
                dep_stack = self._dep_stack
                while dep_stack:
                        dep = dep_stack.pop()
+                       if isinstance(dep, Package):
+                               if not self._add_pkg_deps(dep):
+                                       return 0
+                               continue
                        update = "--update" in self.myopts and dep.depth <= 1
                        if dep.blocker:
                                if not buildpkgonly and \
@@ -1286,6 +1308,9 @@ class depgraph(object):
                                continue
                        dep_pkg, existing_node = self._select_package(dep.root, dep.atom)
                        if not dep_pkg:
+                               if allow_unsatisfied:
+                                       self._unsatisfied_deps.append(dep)
+                                       continue
                                self._show_unsatisfied_dep(dep.root, dep.atom,
                                        myparent=dep.parent.digraph_node)
                                return 0
@@ -1307,6 +1332,8 @@ class depgraph(object):
                                                        # should have been masked.
                                                        raise
                                if not myarg:
+                                       if consistent:
+                                               self._ignored_deps.append(dep)
                                        continue
 
                        if not self._add_pkg(dep_pkg, myparent=dep.parent,
@@ -1345,10 +1372,7 @@ class depgraph(object):
 
                # select the correct /var database that we'll be checking against
                vardbapi = self.trees[myroot]["vartree"].dbapi
-               portdb = self.trees[myroot]["porttree"].dbapi
-               bindb = self.trees[myroot]["bintree"].dbapi
                pkgsettings = self.pkgsettings[myroot]
-               myuse = metadata["USE"].split()
 
                if not arg and myroot == self.target_root:
                        try:
@@ -1467,17 +1491,32 @@ class depgraph(object):
                    emerge --deep <pkgspec>; we need to recursively check dependencies of pkgspec
                    If we are in --nodeps (no recursion) mode, we obviously only check 1 level of dependencies.
                """
-               if "deep" not in self.myparams and not merging and \
-                       not ("--update" in self.myopts and arg and merging):
-                       return 1
-               elif "recurse" not in self.myparams:
+               dep_stack = self._dep_stack
+               if "recurse" not in self.myparams:
                        return 1
+               elif pkg.installed and \
+                       "deep" not in self.myparams:
+                       if "consistent" not in self.myparams:
+                               return 1
+                       dep_stack = self._ignored_deps
 
                self.spinner.update()
 
-               """ Check DEPEND/RDEPEND/PDEPEND/SLOT
-               Pull from bintree if it's binary package, porttree if it's ebuild.
-               Binpkg's can be either remote or local. """
+               if arg:
+                       depth = 0
+               pkg.depth = depth
+               dep_stack.append(pkg)
+               return 1
+
+       def _add_pkg_deps(self, pkg):
+
+               mytype = pkg.type_name
+               myroot = pkg.root
+               mykey = pkg.cpv
+               metadata = pkg.metadata
+               myuse = metadata["USE"].split()
+               jbigkey = pkg.digraph_node
+               depth = pkg.depth + 1
 
                edepend={}
                depkeys = ["DEPEND","RDEPEND","PDEPEND"]
@@ -1510,9 +1549,6 @@ class depgraph(object):
                        (myroot, edepend["PDEPEND"], DepPriority(runtime_post=True))
                )
 
-               if arg:
-                       depth = 0
-               depth += 1
                debug = "--debug" in self.myopts
                strict = mytype != "installed"
                try:
@@ -1541,7 +1577,7 @@ class depgraph(object):
                                        if not blocker and vardb.match(atom):
                                                mypriority.satisfied = True
                                        self._dep_stack.append(
-                                               Dependency(arg=arg, atom=atom,
+                                               Dependency(atom=atom,
                                                        blocker=blocker, depth=depth, parent=pkg,
                                                        priority=mypriority, root=dep_root))
                                if debug:
@@ -1561,6 +1597,7 @@ class depgraph(object):
                                        "!!! This binary package cannot be installed: '%s'\n" % \
                                        mykey, noiselevel=-1)
                        elif mytype == "ebuild":
+                               portdb = self.roots[myroot].trees["porttree"].dbapi
                                myebuild, mylocation = portdb.findname2(mykey)
                                portage.writemsg("!!! This ebuild cannot be installed: " + \
                                        "'%s'\n" % myebuild, noiselevel=-1)
@@ -1614,7 +1651,14 @@ class depgraph(object):
 
        def select_files(self, myfiles, mysets):
                "given a list of .tbz2s, .ebuilds and deps, create the appropriate depgraph and return a favorite list"
-               self._sets.update(mysets)
+               # Recursively expand sets so that containment tests in
+               # self._get_parent_sets() properly match atoms in nested
+               # sets (like if world contains system). Otherwise, atoms
+               # from nested sets would get recorded in the world file.
+               setconfig = self.settings.setconfig
+               for set_name in mysets:
+                       self._sets[set_name] = InternalPackageSet(
+                               initial_atoms=setconfig.getSetAtoms(set_name))
                myfavorites=[]
                myroot = self.target_root
                dbs = self._filtered_trees[myroot]["dbs"]
@@ -1818,6 +1862,9 @@ class depgraph(object):
                                        missing += 1
                                        print "Missing binary for:",xs[2]
 
+               if not self._complete_graph():
+                       return False, myfavorites
+
                if not self.validate_blockers():
                        return False, myfavorites
                
@@ -1950,8 +1997,17 @@ class depgraph(object):
                                if atom_populated:
                                        break
 
-       def _select_atoms(self, root, depstring, myuse=None, strict=True,
-               trees=None):
+       def _select_atoms_from_graph(self, *pargs, **kwargs):
+               """
+               Prefer atoms matching packages that have already been
+               added to the graph or those that are installed and have
+               not been scheduled for replacement.
+               """
+               kwargs["trees"] = self._graph_trees
+               return self._select_atoms_highest_available(*pargs, **kwargs)
+
+       def _select_atoms_highest_available(self, root, depstring,
+               myuse=None, strict=True, trees=None):
                """This will raise InvalidDependString if necessary. If trees is
                None then self._filtered_trees is used."""
                pkgsettings = self.pkgsettings[root]
@@ -2073,7 +2129,7 @@ class depgraph(object):
                        print xfrom
                print
 
-       def _select_package(self, root, atom, onlydeps=False):
+       def _select_pkg_highest_available(self, root, atom, onlydeps=False):
                pkgsettings = self.pkgsettings[root]
                dbs = self._filtered_trees[root]["dbs"]
                vardb = self.roots[root].trees["vartree"].dbapi
@@ -2262,6 +2318,110 @@ class depgraph(object):
                # ordered by type preference ("ebuild" type is the last resort)
                return  matched_packages[-1], existing_node
 
+       def _select_pkg_from_graph(self, root, atom, onlydeps=False):
+               """
+               Select packages that have already been added to the graph or
+               those that are installed and have not been scheduled for
+               replacement.
+               """
+               graph_db = self._graph_trees[root]["porttree"].dbapi
+               matches = graph_db.match(atom)
+               if not matches:
+                       return None, None
+               cpv = matches[-1] # highest match
+               slot_atom = "%s:%s" % (portage.cpv_getkey(cpv),
+                       graph_db.aux_get(cpv, ["SLOT"])[0])
+               e_pkg = self._slot_pkg_map[root].get(slot_atom)
+               if e_pkg:
+                       return e_pkg, e_pkg.digraph_node
+               metadata = dict(izip(self._mydbapi_keys,
+                       graph_db.aux_get(cpv, self._mydbapi_keys)))
+               pkg = Package(cpv=cpv, built=True,
+                       installed=True, type_name="installed",
+                       metadata=metadata, root=root)
+               return pkg, None
+
+       def _complete_graph(self):
+               """
+               Add any deep dependencies of required sets (args, system, world) that
+               have not been pulled into the graph yet. This ensures that the graph
+               is consistent such that initially satisfied deep dependencies are not
+               broken in the new graph. Initially unsatisfied dependencies are
+               irrelevant since we only want to avoid breaking dependencies that are
+               intially satisfied.
+
+               Since this method can consume enough time to disturb users, it is
+               currently only enabled by the --consistent option.
+               """
+               if "consistent" not in self.myparams:
+                       # Skip this to avoid consuming enough time to disturb users.
+                       return 1
+
+               if "--buildpkgonly" in self.myopts or \
+                       "recurse" not in self.myparams:
+                       return 1
+
+               # Put the depgraph into a mode that causes it to only
+               # select packages that have already been added to the
+               # graph or those that are installed and have not been
+               # scheduled for replacement. Also, toggle the "deep"
+               # parameter so that all dependencies are traversed and
+               # accounted for.
+               self._select_atoms = self._select_atoms_from_graph
+               self._select_package = self._select_pkg_from_graph
+               self.myparams.add("deep")
+
+               for root in self.roots:
+                       required_set_names = self._required_set_names.copy()
+                       if root == self.target_root and \
+                               ("deep" in self.myparams or "empty" in self.myparams):
+                               required_set_names.difference_update(self._sets)
+                       if not required_set_names and not self._ignored_deps:
+                               continue
+                       setconfig = self.roots[root].settings.setconfig
+                       required_set_atoms = set()
+                       for s in required_set_names:
+                               if s == "args":
+                                       if root == self.target_root:
+                                               required_set_atoms.update(self._sets["args"])
+                               else:
+                                       required_set_atoms.update(setconfig.getSetAtoms(s))
+                       vardb = self.roots[root].trees["vartree"].dbapi
+                       for atom in required_set_atoms:
+                               self._dep_stack.append(
+                                       Dependency(atom=atom, depth=0,
+                                       priority=DepPriority(), root=root))
+                       if self._ignored_deps:
+                               self._dep_stack.extend(self._ignored_deps)
+                               self._ignored_deps = []
+                       if not self._create_graph(allow_unsatisfied=True):
+                               return 0
+                       # Check the unsatisfied deps to see if any initially satisfied deps
+                       # will become unsatisfied due to an upgrade. Initially unsatisfied
+                       # deps are irrelevant since we only want to avoid breaking deps
+                       # that are initially satisfied.
+                       while self._unsatisfied_deps:
+                               dep = self._unsatisfied_deps.pop()
+                               matches = vardb.match(dep.atom)
+                               if not matches:
+                                       # Initially unsatisfied.
+                                       continue
+                               # An scheduled installation broke a deep dependency.
+                               # Add the installed package to the graph so that it
+                               # will be appropriately reported as a slot collision
+                               # (possibly solvable via backtracking).
+                               cpv = matches[-1] # highest match
+                               metadata = dict(izip(self._mydbapi_keys,
+                                       vardb.aux_get(cpv, self._mydbapi_keys)))
+                               pkg = Package(type_name="installed", root=root,
+                                       cpv=cpv, metadata=metadata, built=True,
+                                       installed=True)
+                               if not self._add_pkg(pkg, myparent=dep.parent):
+                                       return 0
+                               if not self._create_graph(allow_unsatisfied=True):
+                                       return 0
+               return 1
+
        def validate_blockers(self):
                """Remove any blockers from the digraph that do not match any of the
                packages within the graph.  If necessary, create hard deps to ensure
@@ -2520,6 +2680,18 @@ class depgraph(object):
                        self._altlist_cache[reversed] = retlist[:]
                        return retlist
                mygraph=self.digraph.copy()
+               # Prune "nomerge" root nodes if nothing depends on them, since
+               # otherwise they slow down merge order calculation. Don't remove
+               # non-root nodes since they help optimize merge order in some cases
+               # such as revdep-rebuild.
+               while True:
+                       removed_something = False
+                       for node in mygraph.root_nodes():
+                               if node[-1] == "nomerge":
+                                       mygraph.remove(node)
+                                       removed_something = True
+                       if not removed_something:
+                               break
                self._merge_order_bias(mygraph)
                myblockers = self.blocker_digraph.copy()
                retlist=[]
@@ -5829,7 +6001,8 @@ def action_depclean(settings, trees, ldpath_mtimes,
                                try:
                                        arg_atom = args_set.findAtomForPackage(pkg, metadata)
                                except portage.exception.InvalidDependString, e:
-                                       file_path = os.path.join(myroot, VDB_PATH, pkg, "PROVIDE")
+                                       file_path = os.path.join(
+                                               myroot, portage.VDB_PATH, pkg, "PROVIDE")
                                        portage.writemsg("\n\nInvalid PROVIDE: %s\n" % str(s),
                                                noiselevel=-1)
                                        portage.writemsg("See '%s'\n" % file_path,
@@ -6103,15 +6276,6 @@ def action_build(settings, trees, mtimedb,
                if "--quiet" not in myopts and "--nodeps" not in myopts:
                        print "\b\b... done!"
 
-               if ("--usepkgonly" in myopts) and mydepgraph.missingbins:
-                       sys.stderr.write(red("The following binaries are not available for merging...\n"))
-
-               if mydepgraph.missingbins:
-                       for x in mydepgraph.missingbins:
-                               sys.stderr.write("   "+str(x)+"\n")
-                       sys.stderr.write("\nThese are required by '--usepkgonly' -- Terminating.\n\n")
-                       return 1
-
        if "--pretend" not in myopts and \
                ("--ask" in myopts or "--tree" in myopts or \
                "--verbose" in myopts) and \
index ec38e0850dfc6a0c36462ee83454d13671734a6f..230c96cb637d122f384752089894bd6ef040ce27 100644 (file)
@@ -573,11 +573,6 @@ class portdbapi(dbapi):
                        #myval = self.visible(self.cp_list(mykey))
 
                        myval = self.gvisible(self.visible(self.cp_list(mykey)))
-               elif level == "bestmatch-visible":
-                       #dep match -- best match of all visible packages
-                       #get all visible matches (from xmatch()), then choose the best one
-
-                       myval = best(self.xmatch("match-visible", None, mydep=mydep, mykey=mykey))
                elif level == "minimum-all":
                        # Find the minimum matching version. This is optimized to
                        # minimize the number of metadata accesses (improves performance
@@ -598,19 +593,22 @@ class portdbapi(dbapi):
                                                                break
                                                except KeyError:
                                                        pass # ebuild masked by corruption
-               elif level == "minimum-visible":
+               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
                        # especially in cases where metadata needs to be generated).
-                       # This does not implement LICENSE filtering since it's only
-                       # intended for use by repoman.
                        if mydep == mykey:
                                mylist = self.cp_list(mykey)
                        else:
                                mylist = match_from_list(mydep, self.cp_list(mykey))
                        myval = ""
                        settings = self.mysettings
-                       for cpv in mylist:
+                       local_config = settings.local_config
+                       if level == "minimum-visible":
+                               iterfunc = iter
+                       else:
+                               iterfunc = reversed
+                       for cpv in iterfunc(mylist):
                                try:
                                        metadata = dict(izip(self._aux_cache_keys,
                                                self.aux_get(cpv, self._aux_cache_keys)))
@@ -627,6 +625,16 @@ class portdbapi(dbapi):
                                        continue
                                if settings.getProfileMaskAtom(cpv, metadata):
                                        continue
+                               if local_config:
+                                       metadata["USE"] = ""
+                                       if "?" in metadata["LICENSE"]:
+                                               self.doebuild_settings.setcpv(cpv, mydb=metadata)
+                                               metadata["USE"] = self.doebuild_settings.get("USE", "")
+                                       try:
+                                               if settings.getMissingLicenses(cpv, metadata):
+                                                       continue
+                                       except InvalidDependString:
+                                               continue
                                myval = cpv
                                break
                elif level == "bestmatch-list":
@@ -703,6 +711,7 @@ class portdbapi(dbapi):
                newlist=[]
                aux_keys = ["IUSE", "KEYWORDS", "LICENSE", "EAPI", "SLOT"]
                metadata = {}
+               local_config = self.mysettings.local_config
                for mycpv in mylist:
                        metadata.clear()
                        try:
@@ -719,15 +728,16 @@ class portdbapi(dbapi):
                                continue
                        if self.mysettings.getMissingKeywords(mycpv, metadata):
                                continue
-                       metadata["USE"] = ""
-                       if "?" in metadata["LICENSE"]:
-                               self.doebuild_settings.setcpv(mycpv, mydb=metadata)
-                               metadata["USE"] = self.doebuild_settings.get("USE", "")
-                       try:
-                               if self.mysettings.getMissingLicenses(mycpv, metadata):
+                       if local_config:
+                               metadata["USE"] = ""
+                               if "?" in metadata["LICENSE"]:
+                                       self.doebuild_settings.setcpv(mycpv, mydb=metadata)
+                                       metadata["USE"] = self.doebuild_settings.get("USE", "")
+                               try:
+                                       if self.mysettings.getMissingLicenses(mycpv, metadata):
+                                               continue
+                               except InvalidDependString:
                                        continue
-                       except InvalidDependString:
-                               continue
                        newlist.append(mycpv)
                return newlist
 
index 8e5863cbaa8d4b82efd0bbe5051eaaa7e4b757d2..85f2e410b64a31af6f76cf7a8f01fa605fb9baf3 100644 (file)
@@ -30,9 +30,12 @@ class PackageSet(object):
                return atom in self._atoms or atom in self._nonatoms
        
        def __iter__(self):
-               for x in self.getAtoms():
+               self._load()
+               for x in self._atoms:
                        yield x
-       
+               for x in self._nonatoms:
+                       yield x
+
        def supportsOperation(self, op):
                if not op in OPERATIONS:
                        raise ValueError(op)
@@ -72,7 +75,8 @@ class PackageSet(object):
                raise NotImplementedError()
 
        def containsCPV(self, cpv):
-               for a in self.getAtoms():
+               self._load()
+               for a in self._atoms:
                        if match_from_list(a, [cpv]):
                                return True
                return False
@@ -101,7 +105,7 @@ class PackageSet(object):
                if an error occurs while parsing PROVIDE."""
                cpv_slot = "%s:%s" % (cpv, metadata["SLOT"])
                cp = dep_getkey(cpv)
-               self.getAtoms() # make sure the atoms are loaded
+               self._load() # make sure the atoms are loaded
                atoms = self._atommap.get(cp)
                if atoms:
                        best_match = best_match_to_list(cpv_slot, atoms)
@@ -125,7 +129,7 @@ class PackageSet(object):
 class EditablePackageSet(PackageSet):
 
        def update(self, atoms):
-               self.getAtoms()
+               self._load()
                modified = False
                normal_atoms = []
                for a in atoms:
@@ -149,13 +153,14 @@ class EditablePackageSet(PackageSet):
                self.write()
 
        def remove(self, atom):
-               self.getAtoms()
+               self._load()
                self._atoms.discard(atom)
                self._updateAtomMap()
                self.write()
 
        def removePackageAtoms(self, cp):
-               for a in list(self.getAtoms()):
+               self._load()
+               for a in list(self._atoms):
                        if dep_getkey(a) == cp:
                                self.remove(a)
                self.write()
index d300eaad20d27dc528c98c4353cd5b4965eaf540..242138710b52894efd2edc8070197c888c40d24a 100644 (file)
@@ -149,7 +149,8 @@ class WorldSet(StaticFileSet):
 
        def cleanPackage(self, vardb, cpv):
                self.lock()
-               worldlist = list(self.getAtoms()) # loads latest from disk
+               self._load() # loads latest from disk
+               worldlist = list(self._atoms)
                mykey = cpv_getkey(cpv)
                newworldlist = []
                for x in worldlist:
index 93c3222b338debfddd36ca10743b1b49d0a6940d..b6c430569407d4df160b6f3b590e12f6ebe6680a 100644 (file)
@@ -9,7 +9,7 @@ import os
 
 from repoman.errors import COPYRIGHT_ERROR, LICENSE_ERROR, CVS_HEADER_ERROR, \
        LEADING_SPACES_ERROR, READONLY_ASSIGNMENT_ERROR, TRAILING_WHITESPACE_ERROR, \
-       MISSING_QUOTES_ERROR, NESTED_DIE_ERROR
+       MISSING_QUOTES_ERROR, NESTED_DIE_ERROR, REDUNDANT_CD_S_ERROR
 
 
 class ContentCheckException(Exception):
@@ -120,10 +120,10 @@ class EbuildQuote(ContentCheck):
        repoman_check_name = 'ebuild.minorsyn'
        ignore_line = re.compile(r'(^$)|(^\s*#.*)|(^\s*\w+=.*)|(^\s*(local|export)\s+)')
        var_names = r'(D|S|T|ROOT|FILESDIR|WORKDIR)'
-       var_reference = re.compile(r'\$({'+var_names+'}|' + \
-               r'\$' + var_names + '\W)')
-       missing_quotes = re.compile(r'(\s|^)[^"\s]*\${?' + var_names + \
-               r'}?[^"\s]*(\s|$)')
+       var_reference = re.compile(r'\$(\{'+var_names+'\}|' + \
+               var_names + '\W)')
+       missing_quotes = re.compile(r'(\s|^)[^"\s]*\$\{?' + var_names + \
+               r'\}?[^"\s]*(\s|$)')
        cond_begin =  re.compile(r'(^|\s+)\[\[($|\\$|\s+)')
        cond_end =  re.compile(r'(^|\s+)\]\]($|\\$|\s+)')
        
@@ -243,3 +243,24 @@ class EbuildUselessDodoc(ContentCheck):
                                errors.append((num + 1, "Useless dodoc '%s'" % \
                                        (match.group(2), ) + " on line: %d"))
                return errors
+
+class EbuildUselessCdS(ContentCheck):
+       """Check for redundant cd ${S} statements"""
+       repoman_check_name = 'ebuild.minorsyn'
+       method_re = re.compile(r'^\s*src_(compile|install|test)\s*\(\)')
+       cds_re = re.compile(r'^\s*cd\s+("\$(\{S\}|S)"|\$(\{S\}|S))\s')
+
+       def __init__(self, contents):
+               ContentCheck.__init__(self, contents)
+
+       def Run(self):
+               errors = []
+               check_next_line = False
+               for num, line in enumerate(self.contents):
+                       if check_next_line:
+                               check_next_line = False
+                               if self.cds_re.match(line):
+                                       errors.append((num + 1, REDUNDANT_CD_S_ERROR))
+                       elif self.method_re.match(line):
+                               check_next_line = True
+               return errors
index 8378ed4d8ca16a7cf276ecc1ee0cb862cdc6bb91..3b3b3c179ff368a7a3fdb295826e245743a7f807 100644 (file)
@@ -11,3 +11,4 @@ TRAILING_WHITESPACE_ERROR = 'Trailing whitespace error on line: %d'
 READONLY_ASSIGNMENT_ERROR = 'Ebuild contains assignment to read-only variable on line: %d'
 MISSING_QUOTES_ERROR = 'Unquoted Variable on line: %d'
 NESTED_DIE_ERROR = 'Ebuild calls die in a subshell'
+REDUNDANT_CD_S_ERROR = 'Ebuild has redundant cd ${S} statement on line: %d'