Bug #324191 - Add support for FEATURES=compress-build-logs. The causes
authorZac Medico <zmedico@gentoo.org>
Sat, 21 Aug 2010 18:18:40 +0000 (11:18 -0700)
committerZac Medico <zmedico@gentoo.org>
Sat, 21 Aug 2010 18:18:40 +0000 (11:18 -0700)
all build logs to be compressed while they are being written. Log file
names have an extension that is appropriate for the compression type.
Currently, only gzip(1) compression is supported, so build logs will
have a '.gz' extension when this feature is enabled.

17 files changed:
bin/misc-functions.sh
man/make.conf.5
pym/_emerge/AbstractEbuildProcess.py
pym/_emerge/Binpkg.py
pym/_emerge/BinpkgVerifier.py
pym/_emerge/EbuildBuild.py
pym/_emerge/EbuildFetcher.py
pym/_emerge/EbuildPhase.py
pym/_emerge/MetadataRegen.py
pym/_emerge/PackageUninstall.py
pym/_emerge/PollScheduler.py
pym/_emerge/QueueScheduler.py
pym/_emerge/Scheduler.py
pym/_emerge/SpawnProcess.py
pym/portage/const.py
pym/portage/package/ebuild/doebuild.py
pym/portage/package/ebuild/prepare_build_dirs.py

index 3a199bdf7a72421430a6e46744f97d769481b28d..123edc0ecc4a72078d60acbad08e518346a1b2e4 100755 (executable)
@@ -474,10 +474,12 @@ install_qa_check() {
                )
                abort="no"
                i=0
+               local grep_cmd=grep
+               [[ $PORTAGE_LOG_FILE = *.gz ]] && grep_cmd=zgrep
                while [[ -n ${msgs[${i}]} ]] ; do
                        m=${msgs[$((i++))]}
                        # force C locale to work around slow unicode locales #160234
-                       f=$(LC_ALL=C grep "${m}" "${PORTAGE_LOG_FILE}")
+                       f=$(LC_ALL=C $grep_cmd "${m}" "${PORTAGE_LOG_FILE}")
                        if [[ -n ${f} ]] ; then
                                vecho -ne '\a\n'
                                eqawarn "QA Notice: Package has poor programming practices which may compile"
@@ -487,8 +489,10 @@ install_qa_check() {
                                abort="yes"
                        fi
                done
+               local cat_cmd=cat
+               [[ $PORTAGE_LOG_FILE = *.gz ]] && cat_cmd=zcat
                [[ $reset_debug = 1 ]] && set -x
-               f=$(cat "${PORTAGE_LOG_FILE}" | \
+               f=$($cat_cmd "${PORTAGE_LOG_FILE}" | \
                        "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH"/check-implicit-pointer-usage.py || die "check-implicit-pointer-usage.py failed")
                if [[ -n ${f} ]] ; then
 
index 0827db11af6c21a37e3d745b4d767209b753a17b..74b24576bfa2ceb870d1a725adf0e6b1635af629 100644 (file)
@@ -231,6 +231,12 @@ A QA\-feature to ensure that a package doesn't overwrite files it doesn't own.
 The \fICOLLISION_IGNORE\fR variable can be used to selectively disable this
 feature. Also see the related \fIprotect\-owned\fR feature.
 .TP
+.B compress\-build\-logs
+The causes all build logs to be compressed while they are being written.
+Log file names have an extension that is appropriate for the compression
+type. Currently, only \fBgzip\fR(1) compression is supported, so build
+logs will have a '.gz' extension when this feature is enabled.
+.TP
 .B digest
 Autogenerate digests for packages when running the \fBemerge\fR(1) command.  If
 the \fIassume\-digests\fR feature is also enabled then existing SRC_URI digests
index aca254191974668a8e3409a641c1edda4469de77..8c87812f57b53329e22b913d8539aee130e4f5bf 100644 (file)
@@ -183,21 +183,11 @@ class AbstractEbuildProcess(SpawnProcess):
                phase = self.phase
                for line in lines:
                        eerror(line, phase=phase, key=self.settings.mycpv, out=out)
-               logfile = self.logfile
-               if logfile is None:
-                       logfile = self.settings.get("PORTAGE_LOG_FILE")
                msg = _unicode_decode(out.getvalue(),
                        encoding=_encodings['content'], errors='replace')
                if msg:
-                       if not self.background:
-                               writemsg_stdout(msg, noiselevel=-1)
-                       if logfile is not None:
-                               log_file = codecs.open(_unicode_encode(logfile,
-                                       encoding=_encodings['fs'], errors='strict'),
-                                       mode='a', encoding=_encodings['content'],
-                                       errors='backslashreplace')
-                               log_file.write(msg)
-                               log_file.close()
+                       self.scheduler.output(msg,
+                               log_path=self.settings.get("PORTAGE_LOG_FILE"))
 
        def _set_returncode(self, wait_retval):
                SpawnProcess._set_returncode(self, wait_retval)
index 66b7bd424d2e64f35384b98cf98f9f0a0e50ef93..2201decaffae672073ba6a956f154c8d13e69b65 100644 (file)
@@ -27,20 +27,8 @@ class Binpkg(CompositeTask):
                "_image_dir", "_infloc", "_pkg_path", "_tree", "_verify")
 
        def _writemsg_level(self, msg, level=0, noiselevel=0):
-
-               if not self.background:
-                       portage.util.writemsg_level(msg,
-                               level=level, noiselevel=noiselevel)
-
-               log_path = self.settings.get("PORTAGE_LOG_FILE")
-               if  log_path is not None:
-                       f = codecs.open(_unicode_encode(log_path,
-                               encoding=_encodings['fs'], errors='strict'),
-                               mode='a', encoding=_encodings['content'], errors='replace')
-                       try:
-                               f.write(msg)
-                       finally:
-                               f.close()
+               self.scheduler.output(msg, level=level, noiselevel=noiselevel,
+                       log_path=self.settings.get("PORTAGE_LOG_FILE"))
 
        def _start(self):
 
@@ -146,9 +134,7 @@ class Binpkg(CompositeTask):
 
                verifier = None
                if self._verify:
-                       logfile = None
-                       if self.background:
-                               logfile = self.settings.get("PORTAGE_LOG_FILE")
+                       logfile = self.settings.get("PORTAGE_LOG_FILE")
                        verifier = BinpkgVerifier(background=self.background,
                                logfile=logfile, pkg=self.pkg)
                        self._start_task(verifier, self._verifier_exit)
index 83a02c76c9471462306c9ea4f308c395f55a860a..02a942c943db9eb9710b800f5234bdae70192e01 100644 (file)
@@ -7,7 +7,9 @@ import sys
 import portage
 from portage import os
 from portage import _encodings
+from portage import _unicode_decode
 from portage import _unicode_encode
+from portage import StringIO
 from portage.package.ebuild.fetch import _checksum_failure_temp_file
 import codecs
 
@@ -27,27 +29,10 @@ class BinpkgVerifier(AsynchronousTask):
                rval = os.EX_OK
                stdout_orig = sys.stdout
                stderr_orig = sys.stderr
-               log_file = None
-               if self.background and self.logfile is not None:
-                       if sys.hexversion >= 0x3000000:
-                               # Since we are replacing the sys.std* streams,
-                               # we need to use the normal open() function
-                               # so that we get the right class (otherwise our
-                               # code that expects the 'buffer' attribute
-                               # will break).
-                               log_file = open(_unicode_encode(self.logfile,
-                                       encoding=_encodings['fs'], errors='strict'),
-                                       mode='a', encoding=_encodings['content'],
-                                       errors='backslashreplace')
-                       else:
-                               # For python2, sys.std* are expected to be binary streams.
-                               log_file = open(_unicode_encode(self.logfile,
-                                       encoding=_encodings['fs'], errors='strict'),
-                                       mode='ab')
+               out = portage.StringIO()
                try:
-                       if log_file is not None:
-                               sys.stdout = log_file
-                               sys.stderr = log_file
+                       sys.stdout = out
+                       sys.stderr = out
                        try:
                                bintree.digestCheck(pkg)
                        except portage.exception.FileNotFound:
@@ -69,7 +54,7 @@ class BinpkgVerifier(AsynchronousTask):
                        if rval == os.EX_OK:
                                # If this was successful, discard the log here since otherwise
                                # we'll get multiple logs for the same package.
-                               if log_file is not None:
+                               if self.logfile is not None:
                                        try:
                                                os.unlink(self.logfile)
                                        except OSError:
@@ -83,8 +68,11 @@ class BinpkgVerifier(AsynchronousTask):
                finally:
                        sys.stdout = stdout_orig
                        sys.stderr = stderr_orig
-                       if log_file is not None:
-                               log_file.close()
+
+               msg = _unicode_decode(out.getvalue(),
+                       encoding=_encodings['content'], errors='replace')
+               if msg:
+                       self.scheduler.output(msg, log_path=self.logfile)
 
                self.returncode = rval
                self.wait()
index b8694df96911b57178267c421627880dc6f345da..5c108fee4d6a21a7cadb6031f7ae50794485ba7a 100644 (file)
@@ -218,19 +218,8 @@ class EbuildBuild(CompositeTask):
                if self._issyspkg:
                        msg = ">>> This is a system package, " + \
                                "let's pack a rescue tarball.\n"
-
-                       log_path = self.settings.get("PORTAGE_LOG_FILE")
-                       if log_path is not None:
-                               log_file = codecs.open(_unicode_encode(log_path,
-                                       encoding=_encodings['fs'], errors='strict'),
-                                       mode='a', encoding=_encodings['content'], errors='replace')
-                               try:
-                                       log_file.write(msg)
-                               finally:
-                                       log_file.close()
-
-                       if not self.background:
-                               portage.writemsg_stdout(msg, noiselevel=-1)
+                       self.scheduler.output(msg,
+                               log_path=self.settings.get("PORTAGE_LOG_FILE"))
 
                packager = EbuildBinpkg(background=self.background, pkg=self.pkg,
                        scheduler=self.scheduler, settings=self.settings)
index 7f5bc6df40cf5f34dc2a42875cabed2b4c3e00d2..63e423771cdd3ee145ddcadbe6d1637281af7150 100644 (file)
@@ -158,19 +158,10 @@ class EbuildFetcher(SpawnProcess):
                out = portage.StringIO()
                for line in lines:
                        eerror(line, phase="unpack", key=self.pkg.cpv, out=out)
-               logfile = self.logfile
                msg = _unicode_decode(out.getvalue(),
                        encoding=_encodings['content'], errors='replace')
                if msg:
-                       if not self.background:
-                               writemsg_stdout(msg, noiselevel=-1)
-                       if logfile is not None:
-                               log_file = codecs.open(_unicode_encode(logfile,
-                                       encoding=_encodings['fs'], errors='strict'),
-                                       mode='a', encoding=_encodings['content'],
-                                       errors='backslashreplace')
-                               log_file.write(msg)
-                               log_file.close()
+                       self.scheduler.output(msg, log_path=self.logfile)
 
        def _set_returncode(self, wait_retval):
                SpawnProcess._set_returncode(self, wait_retval)
index 7fbc668493b3154ee3d453dae040547de078c1d6..d2d4dce3a15878598bfcec6874b73d1ef7e0f15c 100644 (file)
@@ -75,24 +75,11 @@ class EbuildPhase(CompositeTask):
 
                if self.phase == "install":
                        out = portage.StringIO()
-                       log_path = self.settings.get("PORTAGE_LOG_FILE")
-                       log_file = None
-                       if log_path is not None:
-                               log_file = codecs.open(_unicode_encode(log_path,
-                                       encoding=_encodings['fs'], errors='strict'),
-                                       mode='a', encoding=_encodings['content'], errors='replace')
-                       try:
-                               _check_build_log(self.settings, out=out)
-                               msg = _unicode_decode(out.getvalue(),
-                                       encoding=_encodings['content'], errors='replace')
-                               if msg:
-                                       if not self.background:
-                                               writemsg_stdout(msg, noiselevel=-1)
-                                       if log_file is not None:
-                                               log_file.write(msg)
-                       finally:
-                               if log_file is not None:
-                                       log_file.close()
+                       _check_build_log(self.settings, out=out)
+                       msg = _unicode_decode(out.getvalue(),
+                               encoding=_encodings['content'], errors='replace')
+                       self.scheduler.output(msg,
+                               log_path=self.settings.get("PORTAGE_LOG_FILE"))
 
                if fail:
                        self._die_hooks()
@@ -108,15 +95,8 @@ class EbuildPhase(CompositeTask):
                        msg = _unicode_decode(out.getvalue(),
                                encoding=_encodings['content'], errors='replace')
                        if msg:
-                               if not self.background:
-                                       writemsg_stdout(msg, noiselevel=-1)
-                               log_path = self.settings.get("PORTAGE_LOG_FILE")
-                               if log_path is not None:
-                                       log_file = codecs.open(_unicode_encode(log_path,
-                                               encoding=_encodings['fs'], errors='strict'),
-                                               mode='a', encoding=_encodings['content'], errors='replace')
-                                       log_file.write(msg)
-                                       log_file.close()
+                               self.scheduler.output(msg,
+                                       log_path=self.settings.get("PORTAGE_LOG_FILE"))
 
                post_phase_cmds = _post_phase_cmds.get(self.phase)
                if post_phase_cmds is not None:
index f819570d90b49bcab881232e2be8e4e929282e54..6c6dd6b9031c66abac9ff7e5d55fa2a6842e12fd 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 1999-2009 Gentoo Foundation
+# Copyright 1999-2010 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import portage
@@ -26,10 +26,6 @@ class MetadataRegen(PollScheduler):
 
                self._max_jobs = max_jobs
                self._max_load = max_load
-               self._sched_iface = self._sched_iface_class(
-                       register=self._register,
-                       schedule=self._schedule_wait,
-                       unregister=self._unregister)
 
                self._valid_pkgs = set()
                self._cp_set = set()
index 6f528663a2b0dc6bdf9b0f92233917258cabd8bf..e806bc154213c5038f95a73b0ecb083b4b7229fd 100644 (file)
@@ -42,15 +42,4 @@ class PackageUninstall(AsynchronousTask):
                                portage.util.writemsg_level(msg,
                                        level=level, noiselevel=noiselevel)
                else:
-                       if not background:
-                               portage.util.writemsg_level(msg,
-                                       level=level, noiselevel=noiselevel)
-
-                       f = codecs.open(_unicode_encode(log_path,
-                               encoding=_encodings['fs'], errors='strict'),
-                               mode='a', encoding=_encodings['content'], errors='replace')
-                       try:
-                               f.write(msg)
-                       finally:
-                               f.close()
-
+                       self.scheduler.output(msg, level=level, noiselevel=noiselevel)
index 2a77f869e909cf2e89692815d32dee918ef22914..2c9eb2a8c97714c66c6b8e59027c3be5e72314b0 100644 (file)
@@ -1,10 +1,13 @@
-# Copyright 1999-2009 Gentoo Foundation
+# Copyright 1999-2010 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
+import gzip
 import logging
 import select
 import time
 
+from portage import _encodings
+from portage import _unicode_encode
 from portage import os
 from portage.util import writemsg_level
 
@@ -16,7 +19,7 @@ from _emerge.PollSelectAdapter import PollSelectAdapter
 class PollScheduler(object):
 
        class _sched_iface_class(SlotObject):
-               __slots__ = ("register", "schedule", "unregister")
+               __slots__ = ("output", "register", "schedule", "unregister")
 
        def __init__(self):
                self._max_jobs = 1
@@ -29,6 +32,12 @@ class PollScheduler(object):
                self._event_handler_id = 0
                self._poll_obj = create_poll_instance()
                self._scheduling = False
+               self._background = False
+               self.sched_iface = self._sched_iface_class(
+                       output=self._task_output,
+                       register=self._register,
+                       schedule=self._schedule_wait,
+                       unregister=self._unregister)
 
        def _schedule(self):
                """
@@ -228,6 +237,31 @@ class PollScheduler(object):
 
                return event_handled
 
+       def _task_output(self, msg, log_path=None, level=0, noiselevel=-1):
+               """
+               Output msg to stdout if not self._background. If log_path
+               is not None then append msg to the log (appends with
+               compression if the filename extension of log_path
+               corresponds to a supported compression type).
+               """
+
+               if not self._background:
+                       writemsg_level(msg, level=level, noiselevel=noiselevel)
+
+               if log_path is not None:
+                       f = open(_unicode_encode(log_path,
+                               encoding=_encodings['fs'], errors='strict'),
+                               mode='ab')
+
+                       if log_path.endswith('.gz'):
+                               # NOTE: The empty filename argument prevents us from triggering
+                               # a bug in python3 which causes GzipFile to raise AttributeError
+                               # if fileobj.name is bytes instead of unicode.
+                               f =  gzip.GzipFile(filename='', mode='ab', fileobj=f)
+
+                       f.write(_unicode_encode(msg))
+                       f.close()
+
 _can_poll_device = None
 
 def can_poll_device():
index 8e1837c03245a9bb5b4c4e08acc2445a4b055fb7..0e39d6ad3553c2a9ef6b5a9fc25d1387b0a6782f 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 1999-2009 Gentoo Foundation
+# Copyright 1999-2010 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 from _emerge.PollScheduler import PollScheduler
@@ -18,10 +18,6 @@ class QueueScheduler(PollScheduler):
 
                self._max_jobs = max_jobs
                self._max_load = max_load
-               self.sched_iface = self._sched_iface_class(
-                       register=self._register,
-                       schedule=self._schedule_wait,
-                       unregister=self._unregister)
 
                self._queues = []
                self._schedule_listeners = []
index 1c732c3090d80d5016be3c8e3e26f703c74d69d0..aa0a402907ff017d77b9955de68746863daf4fcc 100644 (file)
@@ -4,6 +4,7 @@
 from __future__ import print_function
 
 import codecs
+import gzip
 import logging
 import shutil
 import sys
@@ -78,7 +79,8 @@ class Scheduler(PollScheduler):
 
        class _iface_class(SlotObject):
                __slots__ = ("dblinkEbuildPhase", "dblinkDisplayMerge",
-                       "dblinkElog", "dblinkEmergeLog", "fetch", "register", "schedule",
+                       "dblinkElog", "dblinkEmergeLog", "fetch",
+                       "output", "register", "schedule",
                        "scheduleSetup", "scheduleUnpack", "scheduleYield",
                        "unregister")
 
@@ -209,7 +211,8 @@ class Scheduler(PollScheduler):
                        dblinkDisplayMerge=self._dblink_display_merge,
                        dblinkElog=self._dblink_elog,
                        dblinkEmergeLog=self._dblink_emerge_log,
-                       fetch=fetch_iface, register=self._register,
+                       fetch=fetch_iface, output=self._task_output,
+                       register=self._register,
                        schedule=self._schedule_wait,
                        scheduleSetup=self._schedule_setup,
                        scheduleUnpack=self._schedule_unpack,
@@ -562,22 +565,9 @@ class Scheduler(PollScheduler):
                installed = type_name == "installed"
                return self._pkg(cpv, type_name, root_config, installed=installed)
 
-       def _append_to_log_path(self, log_path, msg):
-
-               f = codecs.open(_unicode_encode(log_path,
-                       encoding=_encodings['fs'], errors='strict'),
-                       mode='a', encoding=_encodings['content'],
-                       errors='backslashreplace')
-               try:
-                       f.write(_unicode_decode(msg,
-                               encoding=_encodings['content'], errors='replace'))
-               finally:
-                       f.close()
-
        def _dblink_elog(self, pkg_dblink, phase, func, msgs):
 
                log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
-               background = self._background
                out = StringIO()
 
                for msg in msgs:
@@ -585,11 +575,7 @@ class Scheduler(PollScheduler):
 
                out_str = out.getvalue()
 
-               if not background:
-                       portage.util.writemsg_stdout(out_str, noiselevel=-1)
-
-               if log_path is not None:
-                       self._append_to_log_path(log_path, out_str)
+               self._task_output(out_str, log_path=log_path)
 
        def _dblink_emerge_log(self, msg):
                self._logger.log(msg)
@@ -603,10 +589,7 @@ class Scheduler(PollScheduler):
                                portage.util.writemsg_level(msg,
                                        level=level, noiselevel=noiselevel)
                else:
-                       if not background:
-                               portage.util.writemsg_level(msg,
-                                       level=level, noiselevel=noiselevel)
-                       self._append_to_log_path(log_path, msg)
+                       self._task_output(msg, log_path=log_path)
 
        def _dblink_ebuild_phase(self,
                pkg_dblink, pkg_dbapi, ebuild_path, phase):
@@ -1100,11 +1083,14 @@ class Scheduler(PollScheduler):
                        log_path = self._locate_failure_log(failed_pkg)
                        if log_path is not None:
                                try:
-                                       log_file = codecs.open(_unicode_encode(log_path,
-                                       encoding=_encodings['fs'], errors='strict'),
-                                       mode='r', encoding=_encodings['content'], errors='replace')
+                                       log_file = open(_unicode_encode(log_path,
+                                               encoding=_encodings['fs'], errors='strict'), mode='rb')
                                except IOError:
                                        pass
+                               else:
+                                       if log_path.endswith('.gz'):
+                                               log_file =  gzip.GzipFile(filename='',
+                                                       mode='rb', fileobj=log_file)
 
                        if log_file is not None:
                                try:
index 6e0586815adef07efcfe84691e691c4ad4f54607..aeb206a06b67b26f8c2dc234bd5c7e54036e7740 100644 (file)
@@ -6,10 +6,13 @@ from _emerge.PollConstants import PollConstants
 import sys
 from portage.cache.mappings import slot_dict_class
 import portage
+from portage import _encodings
+from portage import _unicode_encode
 from portage import os
 import fcntl
 import errno
 import array
+import gzip
 
 class SpawnProcess(SubProcess):
 
@@ -77,7 +80,12 @@ class SpawnProcess(SubProcess):
                        fd_pipes[1] = slave_fd
                        fd_pipes[2] = slave_fd
 
-                       files.log = open(logfile, mode='ab')
+                       files.log = open(_unicode_encode(logfile,
+                               encoding=_encodings['fs'], errors='strict'), mode='ab')
+                       if logfile.endswith('.gz'):
+                               files.log = gzip.GzipFile(filename='', mode='ab',
+                                       fileobj=files.log)
+
                        portage.util.apply_secpass_permissions(logfile,
                                uid=portage.portage_uid, gid=portage.portage_gid,
                                mode=0o660)
@@ -188,7 +196,11 @@ class SpawnProcess(SubProcess):
                                                                fcntl.fcntl(files.stdout.fileno(),
                                                                fcntl.F_GETFL) ^ os.O_NONBLOCK)
 
-                               buf.tofile(files.log)
+                               try:
+                                       buf.tofile(files.log)
+                               except TypeError:
+                                       # array.tofile() doesn't work with GzipFile
+                                       files.log.write(buf.tostring())
                                files.log.flush()
                        else:
                                self._unregister()
index 62d3ceaf27c5f4bf8de093b8eb64f52a984cbac2..bd2c5be8da6540dfd5111ac4faa94b342c7d7885 100644 (file)
@@ -85,7 +85,8 @@ EBUILD_PHASES            = ("pretend", "setup", "unpack", "prepare", "configure"
                            "nofetch", "config", "info", "other")
 SUPPORTED_FEATURES       = frozenset([
                            "assume-digests", "buildpkg", "buildsyspkg", "ccache",
-                           "collision-protect", "digest", "distcc", "distlocks",
+                           "collision-protect", "compress-build-logs",
+                           "digest", "distcc", "distlocks",
                            "fakeroot", "fail-clean", "fixpackages", "getbinpkg",
                            "installsources", "keeptemp", "keepwork", "fixlafiles", "lmirror",
                            "metadata-transfer", "mirror", "multilib-strict", "news",
index 21f716beb15cd0cdb1fbb9504c97ec90598047e9..b340e2759eb2451f32e10ef741ceac51b90dd013 100644 (file)
@@ -5,6 +5,7 @@ __all__ = ['doebuild', 'doebuild_environment', 'spawn', 'spawnebuild']
 
 import codecs
 import errno
+import gzip
 from itertools import chain
 import logging
 import os as _os
@@ -1179,12 +1180,14 @@ def _check_build_log(mysettings, out=None):
        if logfile is None:
                return
        try:
-               f = codecs.open(_unicode_encode(logfile,
-                       encoding=_encodings['fs'], errors='strict'),
-                       mode='r', encoding=_encodings['content'], errors='replace')
+               f = open(_unicode_encode(logfile, encoding=_encodings['fs'],
+                       errors='strict'), mode='rb')
        except EnvironmentError:
                return
 
+       if logfile.endswith('.gz'):
+               f =  gzip.GzipFile(filename='', mode='rb', fileobj=f)
+
        am_maintainer_mode = []
        bash_command_not_found = []
        bash_command_not_found_re = re.compile(
@@ -1212,6 +1215,7 @@ def _check_build_log(mysettings, out=None):
 
        try:
                for line in f:
+                       line = _unicode_decode(line)
                        if am_maintainer_mode_re.search(line) is not None and \
                                am_maintainer_mode_exclude_re.search(line) is None:
                                am_maintainer_mode.append(line.rstrip("\n"))
index 0ae60342ff12235900cbbc265f6e502bbefa8619..25e5beb6eacf213a96cccec6f763dc35a37e28c3 100644 (file)
@@ -3,8 +3,8 @@
 
 __all__ = ['prepare_build_dirs']
 
-import codecs
 import errno
+import gzip
 import shutil
 import stat
 import time
@@ -117,15 +117,17 @@ def _adjust_perms_msg(settings, msg):
 
        if background and log_path is not None:
                try:
-                       log_file = codecs.open(_unicode_encode(log_path,
-                               encoding=_encodings['fs'], errors='strict'),
-                               mode='a', encoding=_encodings['content'], errors='replace')
+                       log_file = open(_unicode_encode(log_path,
+                               encoding=_encodings['fs'], errors='strict'), mode='ab')
                except IOError:
                        def write(msg):
                                pass
                else:
+                       if log_path.endswith('.gz'):
+                               log_file =  gzip.GzipFile(filename='',
+                                       mode='ab', fileobj=log_file)
                        def write(msg):
-                               log_file.write(_unicode_decode(msg))
+                               log_file.write(_unicode_encode(msg))
                                log_file.flush()
 
        try:
@@ -281,6 +283,11 @@ def _prepare_workdir(mysettings):
                        writemsg(_("!!! Disabling logging.\n"), noiselevel=-1)
                        while "PORT_LOGDIR" in mysettings:
                                del mysettings["PORT_LOGDIR"]
+
+       compress_log_ext = ''
+       if 'compress-build-logs' in mysettings.features:
+               compress_log_ext = '.gz'
+
        if "PORT_LOGDIR" in mysettings and \
                os.access(mysettings["PORT_LOGDIR"], os.W_OK):
                logid_path = os.path.join(mysettings["PORTAGE_BUILDDIR"], ".logid")
@@ -292,12 +299,14 @@ def _prepare_workdir(mysettings):
 
                if "split-log" in mysettings.features:
                        mysettings["PORTAGE_LOG_FILE"] = os.path.join(
-                               mysettings["PORT_LOGDIR"], "build", "%s/%s:%s.log" % \
-                               (mysettings["CATEGORY"], mysettings["PF"], logid_time))
+                               mysettings["PORT_LOGDIR"], "build", "%s/%s:%s.log%s" % \
+                               (mysettings["CATEGORY"], mysettings["PF"], logid_time,
+                               compress_log_ext))
                else:
                        mysettings["PORTAGE_LOG_FILE"] = os.path.join(
-                               mysettings["PORT_LOGDIR"], "%s:%s:%s.log" % \
-                               (mysettings["CATEGORY"], mysettings["PF"], logid_time))
+                               mysettings["PORT_LOGDIR"], "%s:%s:%s.log%s" % \
+                               (mysettings["CATEGORY"], mysettings["PF"], logid_time,
+                               compress_log_ext))
 
                ensure_dirs(os.path.dirname(mysettings["PORTAGE_LOG_FILE"]))
 
@@ -307,4 +316,4 @@ def _prepare_workdir(mysettings):
                # current policy will allow it to work when a pty is available, but
                # not through a normal pipe. See bug #162404.
                mysettings["PORTAGE_LOG_FILE"] = os.path.join(
-                       mysettings["T"], "build.log")
+                       mysettings["T"], "build.log%s" % compress_log_ext)