Merged from trunk -r13839:13846
authorFabian Groffen <grobian@gentoo.org>
Wed, 22 Jul 2009 17:35:55 +0000 (17:35 -0000)
committerFabian Groffen <grobian@gentoo.org>
Wed, 22 Jul 2009 17:35:55 +0000 (17:35 -0000)
   | 13840   | Use a lazy reference to sys.stdout, in case the API consumer |
   | zmedico | has temporarily overridden stdout.                           |

   | 13841   | Move mtimedb resume list code from action_build() to         |
   | zmedico | Scheduler._save_resume_list().                               |

   | 13842   | Skip Blocker instances inside _add_prefetchers().            |
   | zmedico |                                                              |

   | 13843   | Remove empty log files inside dblink.unmerge(). Thanks to    |
   | zmedico | Diego Pettenò <flameeyes@gentoo.org> for reporting.         |

   | 13844   | FEATURES=unmerge-logs Keep logs from successful unmerge      |
   | zmedico | phases. This is relevant only when PORT_LOGDIR is set.       |
   |         | Thanks to Diego Pettenò <flameeyes@gentoo.org> for the      |
   |         | suggestion.                                                  |

   | 13845   | Always pass encodings='utf_8' to codecs.open(), since        |
   | zmedico | otherwise it can return non-unicode strings (at least in     |
   |         | some cases, observed with python-2.6.2). Don't use unicode   |
   |         | in portage.util.getconfig() for now, since shlex doesn't     |
   |         | seem to support it (spurious \0 characters). If we use       |
   |         | unicode for config variables, it breaks shlex.split() calls  |
   |         | on those variables due to the same issue (spurious \0        |
   |         | characters).                                                 |

   | 13846   | Add comment in calc_depclean() about topological sort        |
   | zmedico | optimization that minimizes issues with implicit deps.       |

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

bin/repoman
cnf/make.globals
man/make.conf.5
pym/_emerge/JobStatusDisplay.py
pym/_emerge/Scheduler.py
pym/_emerge/actions.py
pym/portage/__init__.py
pym/portage/dbapi/vartree.py
pym/portage/env/loaders.py
pym/portage/output.py
pym/portage/util.py

index e921a0fe5bc6c9653a36b0ec8c27988e324b5865..933504808c465edefb4d8f9c7425ec5800255100 100755 (executable)
@@ -590,7 +590,8 @@ for path in portdb.porttrees:
 
        desc_path = os.path.join(path, 'profiles', 'profiles.desc')
        try:
-               desc_file = codecs.open(desc_path, mode='r', errors='replace')
+               desc_file = codecs.open(desc_path, mode='r',
+                       encoding='utf_8', errors='replace')
        except EnvironmentError:
                pass
        else:
@@ -960,7 +961,8 @@ for x in scanlist:
                        continue
                try:
                        line = 1
-                       for l in codecs.open(checkdir+"/"+y, "r", "utf8"):
+                       for l in codecs.open(os.path.join(checkdir, y), mode='r',
+                               encoding='utf_8'):
                                line +=1
                except UnicodeDecodeError, ue:
                        stats["file.UTF8"] += 1
index 66935ca8f853a5d056e30a7ad0db443143724d4d..8c9faa56d16d9161199b408c2c49289cbf72c752 100644 (file)
@@ -39,7 +39,7 @@ RESUMECOMMAND="@PORTAGE_WGET@ -c -t 5 -T 60 --passive-ftp -O \"\${DISTDIR}/\${FI
 
 # Default user options
 FEATURES="assume-digests distlocks fixpackages parallel-fetch protect-owned
-          sandbox sfperms strict unmerge-orphans userfetch"
+          sandbox sfperms strict unmerge-logs unmerge-orphans userfetch"
 
 # Ignore file collisions in /lib/modules since files inside this directory
 # are never unmerged, and therefore collisions must be ignored in order for
index 2b5a7645f470bb1ff84589e97a2acdf658b6b1f9..229e50b3fb49b470ccc76bd202dd721bb7043fa5 100644 (file)
@@ -373,6 +373,10 @@ continue to execute the remaining phases as if the failure had not occurred.
 Note that the test phase for a specific package may be disabled by masking
 the "test" \fBUSE\fR flag in \fBpackage.use.mask\fR (see \fBportage\fR(5)).
 .TP
+.B unmerge\-logs
+Keep logs from successful unmerge phases. This is relevant only when
+\fBPORT_LOGDIR\fR is set.
+.TP
 .B unmerge\-orphans
 If a file is not claimed by another package in the same slot and it is not
 protected by \fICONFIG_PROTECT\fR, unmerge it even if the modification time or
index 35dac62fb7b92048024394d92c9826cbd1f54bd0..63c91e31479c82a904609c31b9530cccafd90e27 100644 (file)
@@ -46,8 +46,7 @@ class JobStatusDisplay(object):
                'newline'         : 'nel',
        }
 
-       def __init__(self, out=sys.stdout, quiet=False, xterm_titles=True):
-               object.__setattr__(self, "out", out)
+       def __init__(self, quiet=False, xterm_titles=True):
                object.__setattr__(self, "quiet", quiet)
                object.__setattr__(self, "xterm_titles", xterm_titles)
                object.__setattr__(self, "maxval", 0)
@@ -58,7 +57,7 @@ class JobStatusDisplay(object):
                object.__setattr__(self, "width", 80)
                self.reset()
 
-               isatty = hasattr(out, "isatty") and out.isatty()
+               isatty = hasattr(self.out, "isatty") and self.out.isatty()
                object.__setattr__(self, "_isatty", isatty)
                if not isatty or not self._init_term():
                        term_codes = {}
@@ -70,6 +69,12 @@ class JobStatusDisplay(object):
                        if not isinstance(v, basestring):
                                self._term_codes[k] = v.decode(encoding, 'replace')
 
+       @property
+       def out(self):
+               """Use a lazy reference to sys.stdout, in case the API consumer has
+               temporarily overridden stdout."""
+               return sys.stdout
+
        def _init_term(self):
                """
                Initialize term control codes.
index 79bbaebd656cd195e44f97354a764555ac3366d2..ed054b5b54766ed2c90e077d9f35bf418a392d8d 100644 (file)
@@ -639,7 +639,8 @@ class Scheduler(PollScheduler):
                        # at the beginning, which annoy users, never
                        # spawn a prefetcher for the first package.
                        for pkg in self._mergelist[1:]:
-                               if pkg.operation == "uninstall":
+                               # mergelist can contain solved Blocker instances
+                               if not isinstance(pkg, Package) or pkg.operation == "uninstall":
                                        continue
                                prefetcher = self._create_prefetcher(pkg)
                                if prefetcher is not None:
@@ -1426,6 +1427,15 @@ class Scheduler(PollScheduler):
                a non-essential package with a broken digest.
                """
                mtimedb = self._mtimedb
+
+               mtimedb["resume"] = {}
+               # Stored as a dict starting with portage-2.1.6_rc1, and supported
+               # by >=portage-2.1.3_rc8. Versions <portage-2.1.3_rc8 only support
+               # a list type for options.
+               mtimedb["resume"]["myopts"] = self.myopts.copy()
+
+               # Convert Atom instances to plain str.
+               mtimedb["resume"]["favorites"] = [str(x) for x in self._favorites]
                mtimedb["resume"]["mergelist"] = [list(x) \
                        for x in self._mergelist \
                        if isinstance(x, Package) and x.operation == "merge"]
index ecd3dda7f4d9b1647bfb0627af6d0334798b5bac..758f054938bb4a22e31e252ce31b8422f5bec221 100644 (file)
@@ -454,14 +454,6 @@ def action_build(settings, trees, mtimedb,
                                mtimedb["resume_backup"] = mtimedb["resume"]
                                del mtimedb["resume"]
                                mtimedb.commit()
-                       mtimedb["resume"]={}
-                       # Stored as a dict starting with portage-2.1.6_rc1, and supported
-                       # by >=portage-2.1.3_rc8. Versions <portage-2.1.3_rc8 only support
-                       # a list type for options.
-                       mtimedb["resume"]["myopts"] = myopts.copy()
-
-                       # Convert Atom instances to plain str.
-                       mtimedb["resume"]["favorites"] = [str(x) for x in favorites]
 
                        pkglist = mydepgraph.altlist()
                        mydepgraph.saveNomergeFavorites()
@@ -1152,7 +1144,8 @@ def calc_depclean(settings, trees, ldpath_mtimes,
                        cleanlist = [pkg.cpv for pkg in graph.order]
                else:
                        # Order nodes from lowest to highest overall reference count for
-                       # optimal root node selection.
+                       # optimal root node selection (this can help minimize issues
+                       # with unaccounted implicit dependencies).
                        node_refcounts = {}
                        for node in graph.order:
                                node_refcounts[node] = len(graph.parent_nodes(node))
index 6fb82381f33fc4fcf5be3f491c345676bb393890..f65197cfb8ab4d63458690132059c077c2a629ba 100644 (file)
@@ -1693,8 +1693,8 @@ class config(object):
                                repo_conf_parser = SafeConfigParser()
                                try:
                                        repo_conf_parser.readfp(
-                                               codecs.open(self._local_repo_conf_path,
-                                               mode='r', errors='replace'))
+                                               codecs.open(self._local_repo_conf_path, mode='r',
+                                               encoding='utf_8', errors='replace'))
                                except EnvironmentError, e:
                                        if e.errno != errno.ENOENT:
                                                raise
index 11ce804ddda6089da84d419571adaff8189dc11f..d871146f56b18fdd9f0662ee1c62083d91341d90 100644 (file)
@@ -2564,6 +2564,7 @@ class dblink(object):
                catdir_lock = None
                scheduler = self._scheduler
                retval = -1
+               failures = 0
                try:
                        if myebuildpath:
                                catdir_lock = lockdir(catdir)
@@ -2591,6 +2592,7 @@ class dblink(object):
 
                                # XXX: Decide how to handle failures here.
                                if retval != os.EX_OK:
+                                       failures += 1
                                        writemsg(_("!!! FAILED prerm: %s\n") % retval, noiselevel=-1)
 
                        self._unmerge_pkgfiles(pkgfiles, others_in_slot)
@@ -2613,6 +2615,7 @@ class dblink(object):
 
                                # XXX: Decide how to handle failures here.
                                if retval != os.EX_OK:
+                                       failures += 1
                                        writemsg(_("!!! FAILED postrm: %s\n") % retval, noiselevel=-1)
 
                        # Skip this if another package in the same slot has just been
@@ -2713,6 +2716,25 @@ class dblink(object):
                                                del e
                                        unlockdir(catdir_lock)
 
+               if log_path is not None:
+
+                       if not failures and 'unmerge-logs' not in self.settings.features:
+                               try:
+                                       os.unlink(log_path)
+                               except OSError:
+                                       pass
+
+                       try:
+                               st = os.stat(log_path)
+                       except OSError:
+                               pass
+                       else:
+                               if st.st_size == 0:
+                                       try:
+                                               os.unlink(log_path)
+                                       except OSError:
+                                               pass
+
                if log_path is not None and os.path.exists(log_path):
                        # Restore this since it gets lost somewhere above and it
                        # needs to be set for _display_merge() to be able to log.
index 7cd2600aefd237224ab9ce98cdb5bad6d22ade16..7b4d727213bb8face02bc92b62af196d4de5614e 100644 (file)
@@ -139,7 +139,7 @@ class FileLoader(DataLoader):
                # once, which may be expensive due to digging in child classes.
                func = self.lineParser
                for fn in RecursiveFileLoader(self.fname):
-                       f = codecs.open(fn, mode='r', errors='replace')
+                       f = codecs.open(fn, mode='r', encoding='utf_8', errors='replace')
                        for line_num, line in enumerate(f):
                                func(line, line_num, data, errors)
                return (data, errors)
index 5e3d3d5180924a8de5aef6d9da017945ca2fbd05..ab28b2e4aa8108fa644418702f70c02d3fa040b6 100644 (file)
@@ -165,7 +165,8 @@ def _parse_color_map(onerror=None):
                return token
        try:
                lineno=0
-               for line in codecs.open( myfile, mode = 'r', errors = 'replace' ):
+               for line in codecs.open( myfile, mode='r',
+                       encoding='utf_8', errors='replace' ):
                        lineno += 1
 
                        commenter_pos = line.find("#")
index fc69d7aef56d173dbf5b08f92b03b4ee0402b297..91f831babaa246f9b6473ca22875b62411516e66 100644 (file)
@@ -319,7 +319,7 @@ def grablines(myfilename,recursive=0):
        else:
                try:
                        myfile = codecs.open(myfilename, mode='r',
-                               encoding=sys.getdefaultencoding(), errors='replace')
+                               encoding='utf_8', errors='replace')
                        mylines = myfile.readlines()
                        myfile.close()
                except IOError, e:
@@ -369,10 +369,11 @@ def getconfig(mycfg, tolerant=0, allow_sourcing=False, expand=True):
                # Workaround for avoiding a silent error in shlex that
                # is triggered by a source statement at the end of the file without a
                # trailing newline after the source statement
-               content = codecs.open(mycfg, mode='r', errors='replace').read()
-               if content and content[-1] != u'\n':
-                       content += u'\n'
-               f = StringIO(content)
+               # NOTE: shex doesn't seem to supported unicode objects
+               # (produces spurious \0 characters with python-2.6.2)
+               content = open(mycfg).read()
+               if content and content[-1] != '\n':
+                       content += '\n'
        except IOError, e:
                if e.errno == PermissionDenied.errno:
                        raise PermissionDenied(mycfg)
@@ -388,7 +389,7 @@ def getconfig(mycfg, tolerant=0, allow_sourcing=False, expand=True):
                # The default shlex.sourcehook() implementation
                # only joins relative paths when the infile
                # attribute is properly set.
-               lex = shlex_class(f, infile=mycfg, posix=True)
+               lex = shlex_class(content, infile=mycfg, posix=True)
                lex.wordchars = string.digits + string.ascii_letters + \
                        "~!@#$%*_\:;?,./-+{}"
                lex.quotes="\"'"
@@ -875,6 +876,7 @@ class atomic_ofstream(ObjectProxy):
                        open_func = open
                else:
                        open_func = codecs.open
+                       kargs.setdefault('encoding', 'utf_8')
                        kargs.setdefault('errors', 'replace')
 
                if follow_links: