}
register_die_hook() {
- export EBUILD_DEATH_HOOKS="${EBUILD_DEATH_HOOKS} $*"
+ local x
+ for x in $* ; do
+ hasq $x $EBUILD_DEATH_HOOKS || \
+ export EBUILD_DEATH_HOOKS="$EBUILD_DEATH_HOOKS $x"
+ done
+}
+
+register_success_hook() {
+ local x
+ for x in $* ; do
+ hasq $x $EBUILD_SUCCESS_HOOKS || \
+ export EBUILD_SUCCESS_HOOKS="$EBUILD_SUCCESS_HOOKS $x"
+ done
}
# Ensure that $PWD is sane whenever possible, to protect against
if [[ $EMERGE_FROM = binary ]] || ! hasq keepwork $FEATURES; then
rm -f "$PORTAGE_BUILDDIR"/.{ebuild_changed,exit_status,logid,unpacked,prepared} \
- "$PORTAGE_BUILDDIR"/.{configured,compiled,tested,packaged}
+ "$PORTAGE_BUILDDIR"/.{configured,compiled,tested,packaged} \
+ "$PORTAGE_BUILDDIR"/.die_hooks
rm -rf "${PORTAGE_BUILDDIR}/build-info"
rm -rf "${WORKDIR}"
for x in $EBUILD_DEATH_HOOKS; do
${x} "$@" >&2 1>&2
done
+ > "$PORTAGE_BUILDDIR/.die_hooks"
fi
[[ -n ${PORTAGE_LOG_FILE} ]] \
die "Failed to move rpm"
}
+die_hooks() {
+ [[ -f $PORTAGE_BUILDDIR/.die_hooks ]] && return
+ local x
+ for x in $EBUILD_DEATH_HOOKS ; do
+ $x >&2
+ done
+ > "$PORTAGE_BUILDDIR/.die_hooks"
+}
+
+success_hooks() {
+ local x
+ for x in $EBUILD_SUCCESS_HOOKS ; do
+ $x
+ done
+}
+
if [ -n "${MISC_FUNCTIONS_ARGS}" ]; then
source_all_bashrcs
[ "$PORTAGE_DEBUG" == "1" ] && set -x
"darcs",
"git",
"mercurial",
- "subversion"
+ "subversion",
+ "tla",
])
suspect_rdepend = frozenset([
class = portage.sets.dbapi.VariableSet
world-candidate = False
variable = INHERITED
-includes = bzr cvs darcs git mercurial subversion
+includes = bzr cvs darcs git mercurial subversion tla
# Installed packages that own files inside /lib/modules.
[module-rebuild]
.br
(VERINS now has the value "net\-ftp/glftpd\-1.27" if glftpd\-1.27 is installed)
.RE
+.SH "HELPER FUNCTIONS: HOOKS"
+.TP
+\fBregister_die_hook\fR \fI[list of function names]\fR
+Register one or more functions to call when the ebuild fails for any reason,
+including file collisions with other packages.
+.TP
+\fBregister_success_hook\fR \fI[list of function names]\fR
+Register one or more functions to call when the ebuild builds and/or installs
+successfully.
+.TP
+.RE
.SH "HELPER FUNCTIONS: OUTPUT"
.TP
\fBeinfo\fR \fI"disposable message"\fR
--- /dev/null
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+from _emerge.SpawnProcess import SpawnProcess
+import portage
+from portage import os
+
+class AbstractEbuildProcess(SpawnProcess):
+
+ __slots__ = ('phase', 'pkg', 'settings',)
+
+ def _pipe(self, fd_pipes):
+ stdout_pipe = fd_pipes.get(1)
+ got_pty, master_fd, slave_fd = \
+ portage._create_pty_or_pipe(copy_term_size=stdout_pipe)
+ return (master_fd, slave_fd)
+
+ def _can_log(self, slave_fd):
+ # With sesandbox, logging works through a pty but not through a
+ # normal pipe. So, disable logging if ptys are broken.
+ # See Bug #162404.
+ return not ('sesandbox' in self.settings.features \
+ and self.settings.selinux_enabled()) or os.isatty(slave_fd)
from _emerge.EbuildMerge import EbuildMerge
from _emerge.EbuildFetchonly import EbuildFetchonly
from _emerge.EbuildBuildDir import EbuildBuildDir
+from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
from portage.util import writemsg
import portage
from portage import os
return
if self.opts.buildpkgonly:
- # Need to call "clean" phase for buildpkgonly mode
- portage.elog.elog_process(self.pkg.cpv, self.settings)
- phase = "clean"
- clean_phase = EbuildPhase(background=self.background,
- pkg=self.pkg, phase=phase,
- scheduler=self.scheduler, settings=self.settings,
- tree=self._tree)
- self._start_task(clean_phase, self._clean_exit)
+ phase = 'success_hooks'
+ success_hooks = MiscFunctionsProcess(
+ background=self.background,
+ commands=[phase], phase=phase, pkg=self.pkg,
+ scheduler=self.scheduler, settings=self.settings)
+ self._start_task(success_hooks,
+ self._buildpkgonly_success_hook_exit)
return
# Continue holding the builddir lock until
self.returncode = packager.returncode
self.wait()
+ def _buildpkgonly_success_hook_exit(self, success_hooks):
+ self._default_exit(success_hooks)
+ self.returncode = None
+ # Need to call "clean" phase for buildpkgonly mode
+ portage.elog.elog_process(self.pkg.cpv, self.settings)
+ phase = 'clean'
+ clean_phase = EbuildPhase(background=self.background,
+ pkg=self.pkg, phase=phase,
+ scheduler=self.scheduler, settings=self.settings,
+ tree=self._tree)
+ self._start_task(clean_phase, self._clean_exit)
+
def _clean_exit(self, clean_phase):
if self._final_exit(clean_phase) != os.EX_OK or \
self.opts.buildpkgonly:
"darcs",
"git",
"mercurial",
- "subversion"
+ "subversion",
+ "tla",
])
def _start(self):
from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
from _emerge.EbuildProcess import EbuildProcess
from _emerge.CompositeTask import CompositeTask
-from portage.util import writemsg
+from portage.util import writemsg, writemsg_stdout
import portage
from portage import os
from portage import _encodings
+from portage import _unicode_decode
from portage import _unicode_encode
import codecs
def _ebuild_exit(self, ebuild_process):
if self.phase == "install":
- out = None
+ out = portage.StringIO()
log_path = self.settings.get("PORTAGE_LOG_FILE")
log_file = None
- if self.background and log_path is not 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')
- out = log_file
try:
portage._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()
if self._default_exit(ebuild_process) != os.EX_OK:
- if self.phase != 'clean' and \
- 'noclean' not in self.settings.features and \
- 'fail-clean' in self.settings.features:
- self._fail_clean()
- return
- self.wait()
+ self._die_hooks()
return
settings = self.settings
if self._final_exit(post_phase) != os.EX_OK:
writemsg("!!! post %s failed; exiting.\n" % self.phase,
noiselevel=-1)
- if self.phase != 'clean' and \
- 'noclean' not in self.settings.features and \
- 'fail-clean' in self.settings.features:
- self._fail_clean()
- return
+ self._die_hooks()
+ return
self._current_task = None
self.wait()
return
+ def _die_hooks(self):
+ self.returncode = None
+ phase = 'die_hooks'
+ die_hooks = MiscFunctionsProcess(background=self.background,
+ commands=[phase], phase=phase, pkg=self.pkg,
+ scheduler=self.scheduler, settings=self.settings)
+ self._start_task(die_hooks, self._die_hooks_exit)
+
+ def _die_hooks_exit(self, die_hooks):
+ if self.phase != 'clean' and \
+ 'noclean' not in self.settings.features and \
+ 'fail-clean' in self.settings.features:
+ self._default_exit(die_hooks)
+ self._fail_clean()
+ return
+ self._final_exit(die_hooks)
+ self.returncode = 1
+ self.wait()
+
def _fail_clean(self):
self.returncode = None
portage.elog.elog_process(self.pkg.cpv, self.settings)
# Distributed under the terms of the GNU General Public License v2
# $Id$
-from _emerge.SpawnProcess import SpawnProcess
+from _emerge.AbstractEbuildProcess import AbstractEbuildProcess
import portage
from portage import os
-class EbuildProcess(SpawnProcess):
+class EbuildProcess(AbstractEbuildProcess):
- __slots__ = ("phase", "pkg", "settings", "tree")
+ __slots__ = ('tree',)
def _start(self):
# Don't open the log file during the clean phase since the
# prevents the clean phase from removing $T.
if self.phase not in ("clean", "cleanrm"):
self.logfile = self.settings.get("PORTAGE_LOG_FILE")
- SpawnProcess._start(self)
-
- def _pipe(self, fd_pipes):
- stdout_pipe = fd_pipes.get(1)
- got_pty, master_fd, slave_fd = \
- portage._create_pty_or_pipe(copy_term_size=stdout_pipe)
- return (master_fd, slave_fd)
+ AbstractEbuildProcess._start(self)
def _spawn(self, args, **kwargs):
return rval
def _set_returncode(self, wait_retval):
- SpawnProcess._set_returncode(self, wait_retval)
+ AbstractEbuildProcess._set_returncode(self, wait_retval)
if self.phase not in ("clean", "cleanrm"):
self.returncode = portage._doebuild_exit_status_check_and_log(
import formatter
import sys
import time
-try:
- from io import StringIO
-except ImportError:
- # Needed for python-2.6 with USE=build since
- # io imports threading which imports thread
- # which is unavailable.
- from StringIO import StringIO
import portage
+from portage import StringIO
from portage import os
from portage import _encodings
from portage import _unicode_decode
# Distributed under the terms of the GNU General Public License v2
# $Id$
-from _emerge.SpawnProcess import SpawnProcess
+from _emerge.AbstractEbuildProcess import AbstractEbuildProcess
import portage
from portage import os
-class MiscFunctionsProcess(SpawnProcess):
+class MiscFunctionsProcess(AbstractEbuildProcess):
"""
Spawns misc-functions.sh with an existing ebuild environment.
"""
- __slots__ = ("commands", "phase", "pkg", "settings")
+ __slots__ = ('commands',)
def _start(self):
settings = self.settings
portage._doebuild_exit_status_unlink(
settings.get("EBUILD_EXIT_STATUS_FILE"))
- SpawnProcess._start(self)
+ AbstractEbuildProcess._start(self)
def _spawn(self, args, **kwargs):
settings = self.settings
debug=debug, **kwargs)
def _set_returncode(self, wait_retval):
- SpawnProcess._set_returncode(self, wait_retval)
+ AbstractEbuildProcess._set_returncode(self, wait_retval)
self.returncode = portage._doebuild_exit_status_check_and_log(
self.settings, self.phase, self.returncode)
import time
import weakref
-try:
- from io import StringIO
-except ImportError:
- # Needed for python-2.6 with USE=build since
- # io imports threading which imports thread
- # which is unavailable.
- from StringIO import StringIO
-
import portage
+from portage import StringIO
from portage import os
from portage import _encodings
from portage import _unicode_decode
from _emerge._flush_elog_mod_echo import _flush_elog_mod_echo
from _emerge.JobStatusDisplay import JobStatusDisplay
from _emerge.MergeListItem import MergeListItem
+from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
from _emerge.Package import Package
from _emerge.PackageMerge import PackageMerge
from _emerge.PollScheduler import PollScheduler
background = self._background
log_path = settings.get("PORTAGE_LOG_FILE")
- ebuild_phase = EbuildPhase(background=background,
- pkg=pkg, phase=phase, scheduler=scheduler,
- settings=settings, tree=pkg_dblink.treetype)
+ if phase in ('die_hooks', 'success_hooks'):
+ ebuild_phase = MiscFunctionsProcess(background=background,
+ commands=[phase], phase=phase, pkg=pkg,
+ scheduler=scheduler, settings=settings)
+ else:
+ ebuild_phase = EbuildPhase(background=background,
+ pkg=pkg, phase=phase, scheduler=scheduler,
+ settings=settings, tree=pkg_dblink.treetype)
ebuild_phase.start()
ebuild_phase.wait()
if fd == sys.stderr.fileno():
sys.stderr.flush()
- logfile = self.logfile
self._files = self._files_dict()
files = self._files
fcntl.fcntl(master_fd, fcntl.F_SETFL,
fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK)
+ logfile = None
+ if self._can_log(slave_fd):
+ logfile = self.logfile
+
null_input = None
fd_pipes_orig = fd_pipes.copy()
if self.background:
self.pid = retval[0]
portage.process.spawned_pids.remove(self.pid)
+ def _can_log(self, slave_fd):
+ return True
+
def _pipe(self, fd_pipes):
"""
@type fd_pipes: dict
from subprocess import getstatusoutput as subprocess_getstatusoutput
except ImportError:
from commands import getstatusoutput as subprocess_getstatusoutput
+
+ try:
+ from io import StringIO
+ except ImportError:
+ # Needed for python-2.6 with USE=build since
+ # io imports threading which imports thread
+ # which is unavailable.
+ from StringIO import StringIO
+
from time import sleep
from random import shuffle
from itertools import chain
# initialize self.features
self.regenerate()
- if not portage.process.sandbox_capable and \
- ("sandbox" in self.features or "usersandbox" in self.features):
- if self.profile_path is not None and \
- os.path.realpath(self.profile_path) == \
- os.path.realpath(os.path.join(config_root, PROFILE_PATH)):
- """ Don't show this warning when running repoman and the
- sandbox feature came from a profile that doesn't belong to
- the user."""
- writemsg(colorize("BAD", _("!!! Problem with sandbox"
- " binary. Disabling...\n\n")), noiselevel=-1)
- if "sandbox" in self.features:
- self.features.remove("sandbox")
- if "usersandbox" in self.features:
- self.features.remove("usersandbox")
-
if bsd_chflags:
self.features.add('chflags')
writemsg("!!! /etc/portage/profile/virtuals. Please move it to\n")
writemsg("!!! this new location.\n\n")
+ if not process.sandbox_capable and \
+ ("sandbox" in self.features or "usersandbox" in self.features):
+ if self.profile_path is not None and \
+ os.path.realpath(self.profile_path) == \
+ os.path.realpath(os.path.join(
+ self["PORTAGE_CONFIGROOT"], PROFILE_PATH)):
+ # Don't show this warning when running repoman and the
+ # sandbox feature came from a profile that doesn't belong
+ # to the user.
+ writemsg(colorize("BAD", _("!!! Problem with sandbox"
+ " binary. Disabling...\n\n")), noiselevel=-1)
+
if "fakeroot" in self.features and \
not portage.process.fakeroot_capable:
writemsg(_("!!! FEATURES=fakeroot is enabled, but the "
if 1 not in fd_pipes or 2 not in fd_pipes:
raise ValueError(fd_pipes)
+ got_pty, master_fd, slave_fd = \
+ _create_pty_or_pipe(copy_term_size=fd_pipes[1])
+
+ if not got_pty and 'sesandbox' in mysettings.features \
+ and mysettings.selinux_enabled():
+ # With sesandbox, logging works through a pty but not through a
+ # normal pipe. So, disable logging if ptys are broken.
+ # See Bug #162404.
+ logfile = None
+ os.close(master_fd)
+ master_fd = None
+ os.close(slave_fd)
+ slave_fd = None
+
+ if logfile:
+
fd_pipes.setdefault(0, sys.stdin.fileno())
fd_pipes_orig = fd_pipes.copy()
- got_pty, master_fd, slave_fd = \
- _create_pty_or_pipe(copy_term_size=fd_pipes_orig[1])
-
# We must set non-blocking mode before we close the slave_fd
# since otherwise the fcntl call can fail on FreeBSD (the child
# process might have already exited and closed slave_fd so we
(not droppriv and "sandbox" not in features and \
"usersandbox" not in features and not fakeroot))
+ if not free and not (fakeroot or process.sandbox_capable):
+ free = True
+
if free or "SANDBOX_ACTIVE" in os.environ:
keywords["opt_name"] += " bash"
spawn_func = portage.process.spawn_bash
_post_phase_userpriv_perms(mysettings)
if mydo == "install":
- _check_build_log(mysettings)
+ out = StringIO()
+ _check_build_log(mysettings, out=out)
+ msg = _unicode_decode(out.getvalue(),
+ encoding=_encodings['content'], errors='replace')
+ if msg:
+ writemsg_stdout(msg, noiselevel=-1)
+ if logfile is not None:
+ try:
+ f = codecs.open(_unicode_encode(logfile,
+ encoding=_encodings['fs'], errors='strict'),
+ mode='a', encoding=_encodings['content'],
+ errors='replace')
+ except EnvironmentError:
+ pass
+ else:
+ f.write(msg)
+ f.close()
if phase_retval == os.EX_OK:
_post_src_install_chost_fix(mysettings)
phase_retval = _post_src_install_checks(mysettings)
def _post_src_install_checks(mysettings):
_post_src_install_uid_fix(mysettings)
global _post_phase_cmds
- retval = _spawn_misc_sh(mysettings, _post_phase_cmds["install"])
+ retval = _spawn_misc_sh(mysettings, _post_phase_cmds["install"],
+ phase='internal_post_src_install')
if retval != os.EX_OK:
writemsg(_("!!! install_qa_check failed; exiting.\n"),
noiselevel=-1)
return myargs
-def _spawn_misc_sh(mysettings, commands, **kwargs):
+def _spawn_misc_sh(mysettings, commands, phase=None, **kwargs):
"""
@param mysettings: the ebuild config
@type mysettings: config
mysettings.get("EBUILD_EXIT_STATUS_FILE"))
debug = mysettings.get("PORTAGE_DEBUG") == "1"
logfile = mysettings.get("PORTAGE_LOG_FILE")
- mydo = mysettings["EBUILD_PHASE"]
+ mysettings.pop("EBUILD_PHASE", None)
try:
rval = spawn(mycommand, mysettings, debug=debug,
logfile=logfile, **kwargs)
finally:
pass
- msg = _doebuild_exit_status_check(mydo, mysettings)
+ msg = _doebuild_exit_status_check(phase, mysettings)
if msg:
if rval == os.EX_OK:
rval = 1
writemsg(_("File Not Found: '%s'\n") % str(e), noiselevel=-1)
return 1
+ # Reset state for things like noauto and keepwork in FEATURES.
+ for x in ('.die_hooks',):
+ try:
+ os.unlink(os.path.join(mysettings['PORTAGE_BUILDDIR'], x))
+ except OSError:
+ pass
+
_prepare_workdir(mysettings)
if mysettings.get('EBUILD_PHASE') != 'fetch':
# Avoid spurious permissions adjustments when fetching with
nosandbox = ("sandbox" not in features and \
"usersandbox" not in features)
+ if not process.sandbox_capable:
+ nosandbox = True
+
sesandbox = mysettings.selinux_enabled() and \
"sesandbox" in mysettings.features
retval = self.treewalk(mergeroot, myroot, inforoot, myebuild,
cleanup=cleanup, mydbapi=mydbapi, prev_mtimes=prev_mtimes)
+ if retval == os.EX_OK:
+ phase = 'success_hooks'
+ else:
+ phase = 'die_hooks'
+
+ if self._scheduler is None:
+ portage._spawn_misc_sh(self.settings, [phase],
+ phase=phase)
+ else:
+ self._scheduler.dblinkEbuildPhase(
+ self, mydbapi, myebuild, phase)
+
# Process ebuild logfiles
elog_process(self.mycpv, self.settings, phasefilter=filter_mergephases)
if 'noclean' not in self.settings.features and \
config_file = os.path.join(abs_user_config, x)
if os.path.isdir(config_file):
for parent, dirs, files in os.walk(config_file):
- for y in dirs:
+ try:
+ parent = _unicode_decode(parent,
+ encoding=_encodings['fs'], errors='strict')
+ except UnicodeDecodeError:
+ continue
+ for y_enc in dirs:
try:
- y = _unicode_decode(y,
+ y = _unicode_decode(y_enc,
encoding=_encodings['fs'], errors='strict')
except UnicodeDecodeError:
- dirs.remove(y)
+ dirs.remove(y_enc)
continue
if y.startswith("."):
- dirs.remove(y)
+ dirs.remove(y_enc)
for y in files:
try:
y = _unicode_decode(y,
import stat
import string
import sys
-try:
- from io import StringIO
-except ImportError:
- # Needed for python-2.6 with USE=build since
- # io imports threading which imports thread
- # which is unavailable.
- from StringIO import StringIO
import portage
+from portage import StringIO
from portage import os
from portage import _encodings
from portage import _os_merge