From 084be42ce816ad98a868d3f06e5fd45ce23c157e Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Sun, 2 Dec 2007 21:29:30 +0000 Subject: [PATCH] Merged from trunk 8729:8754 | 8731 | If portage is reinstalling itself, create a temporary copy | | zmedico | of PORTAGE_BIN_PATH in order to avoid relying on on the new | | | versions which may be incompatible. | | 8732 | Enable the EBUILD_EXIT_STATUS_FILE sanity check when portage | | zmedico | is reinstalling itself since the check is reliable in this | | | case now that a temporary PORTAGE_BIN_PATH is created. | | 8733 | Fix references to PORTAGE_BIN_PATH in doebuild() to use the | | zmedico | config setting since it may be a temp directory when portage | | | is reinstalling itself. | | 8734 | In doebuild, fix paths to ebuild.sh and misc-functions.sh so | | zmedico | that they work with variable PORTAGE_BIN_PATH. | | 8735 | Fix more references to MISC_SH_BINARY so that they work | | zmedico | properly with variable PORTAGE_BIN_PATH. | | 8736 | Fix a typo. | | zmedico | | | 8737 | Make dblink.treewalk() properly delete the installed | | zmedico | instance after it unmerges it. | | 8738 | Fix references to EBUILD_SH_BINARY so that they work | | zmedico | properly with variable PORTAGE_BIN_PATH. | | 8739 | Remove stray print statement. | | zmedico | | | 8741 | In dblink.treewalk(), make portage unmerge multiple | | zmedico | instances os sys-apps/portage in the same slot if necessary | | | (needed when AUTOCLEAN=no is set). | | 8743 | When portage reinstalls itself, copy both the bin and pym | | zmedico | directories to a temp dir. Insert the temporary | | | PORTAGE_PYM_PATH as the first element of sys.path and | | | register an atexit hook to clean up the temporary | | | directories. | | 8745 | Adjust PYTHONPATH when calling portageq so that variable | | zmedico | PORTAGE_PYM_PATH works. | | 8747 | Don't modify sys.path inside dblink.merge() because it | | zmedico | doesn't seem to help and it triggers import errors for elog | | | modules when downgrading to versions of portage that use the | | | old namespace. | | 8749 | When portage reinstalls itself, pre-load elog modules in | | zmedico | dblink.merge() since we won't be able to later if they get | | | unmerged (happens when namespace changes). | | 8751 | Make elog_process() pre-load log modules that it can be | | zmedico | called just for that purpose. | | 8753 | Move environment.bz2 extraction from ebuild.sh to doebuild() | | zmedico | on the python side. The python will be able to use it's | | | awareness of the ${T}/environment to decide what type of | | | ebuild environment should be generated. For example, if the | | | ebuild environment should be able to unset variables that | | | have been inherited from the calling environment, the | | | existence of ${T}/environment will indicate that the ebuild | | | environment should be isolated from the calling environment. | | 8754 | Bug #189417 - When ${T}/environment exists, isolate the | | zmedico | ebuild environment from the calling environment. This makes | | | it possible for the build to unset a variable that was | | | inherited from the calling environment, and the variable | | | will remain unset between phases. | svn path=/main/branches/prefix/; revision=8812 --- bin/ebuild.sh | 33 +++----- bin/isolated-functions.sh | 2 +- pym/portage/__init__.py | 143 ++++++++++++++++++++++++++--------- pym/portage/dbapi/vartree.py | 56 +++++++++++++- pym/portage/elog/__init__.py | 31 +++++++- 5 files changed, 201 insertions(+), 64 deletions(-) diff --git a/bin/ebuild.sh b/bin/ebuild.sh index 317507d3b..5ae30bda9 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -150,6 +150,7 @@ has_version() { fi # return shell-true/shell-false if exists. # Takes single depend-type atoms. + PYTHONPATH="${PORTAGE_PYM_PATH}:${PYTHONPATH}" \ "${PORTAGE_BIN_PATH}"/portageq has_version "${ROOT}" "$1" local retval=$? case "${retval}" in @@ -169,6 +170,7 @@ portageq() { if [ "${EBUILD_PHASE}" == "depend" ]; then die "portageq calls are not allowed in the global scope" fi + PYTHONPATH="${PORTAGE_PYM_PATH}:${PYTHONPATH}" \ "${PORTAGE_BIN_PATH}/portageq" "$@" } @@ -184,6 +186,7 @@ best_version() { fi # returns the best/most-current match. # Takes single depend-type atoms. + PYTHONPATH="${PORTAGE_PYM_PATH}:${PYTHONPATH}" \ "${PORTAGE_BIN_PATH}/portageq" 'best_version' "${ROOT}" "$1" } @@ -1363,7 +1366,7 @@ READONLY_EBUILD_METADATA="DEPEND DESCRIPTION EAPI HOMEPAGE INHERITED IUSE KEYWORDS LICENSE PDEPEND PROVIDE RDEPEND RESTRICT SLOT SRC_URI" -READONLY_PORTAGE_VARS="A CATEGORY D EBUILD EBUILD_PHASE \ +READONLY_PORTAGE_VARS="A CATEGORY D EBUILD EBUILD_ENV_FILE EBUILD_PHASE \ EBUILD_SH_ARGS EMERGE_FROM FILESDIR P PF PN \ PORTAGE_BIN_PATH PORTAGE_PYM_PATH PORTAGE_MUTABLE_FILTERED_VARS \ PORTAGE_TMPDIR PR PV PVR T WORKDIR ED" @@ -1565,32 +1568,14 @@ if hasq "depend" "${EBUILD_SH_ARGS}"; then unset BIN_PATH BIN BODY FUNC_SRC fi -# Automatically try to load environment.bz2 whenever -# "${T}/environment" does not exist, except for phases -# such as nofetch that do not require ${T} to exist. -if ! hasq ${EBUILD_SH_ARGS} clean depend nofetch && \ - [ ! -f "${T}/environment" ] ; then - bzip2 -dc "${EBUILD%/*}"/environment.bz2 > \ - "${T}/environment" 2> /dev/null - if [ $? -eq 0 ] && [ -s "${T}/environment" ] ; then - preprocess_ebuild_env || \ - die "error processing '${EBUILD%/*}/environment.bz2'" - else - rm -f "${T}/environment" - fi -fi - if hasq ${EBUILD_SH_ARGS} clean ; then true elif ! hasq ${EBUILD_PHASE} depend && [ -f "${T}"/environment ] ; then - if [ "${PN}" == "portage" ] && [ -n "${EBUILD_SH_ARGS}" ] ; then - # When portage reinstalls itself, during inst/rm phases, the - # environment may have been saved by a different version of ebuild.sh, - # so it can't trusted that it's been properly filtered. Therefore, - # always preprocess the environment when ${PN} == portage. - preprocess_ebuild_env || \ - die "error processing environment" - fi + # The environment may have been extracted from environment.bz2 or + # may have come from another version of ebuild.sh or something. + # In any case, preprocess it to prevent any potential interference. + preprocess_ebuild_env || \ + die "error processing environment" # Colon separated SANDBOX_* variables need to be cumulative. for x in SANDBOX_DENY SANDBOX_READ SANDBOX_PREDICT SANDBOX_WRITE ; do eval PORTAGE_${x}=\${!x} diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh index 68075a399..c08044cbf 100755 --- a/bin/isolated-functions.sh +++ b/bin/isolated-functions.sh @@ -425,7 +425,7 @@ save_ebuild_env() { EBUILD_EXIT_STATUS_FILE EBUILD_MASTER_PID \ ECLASSDIR ECLASS_DEPTH ENDCOL FAKEROOTKEY FEATURES \ GOOD HILITE HOME IMAGE \ - KV LAST_E_CMD LAST_E_LEN LD_PRELOAD MOPREFIX \ + KV LAST_E_CMD LAST_E_LEN LD_PRELOAD MISC_FUNCTIONS_ARGS MOPREFIX \ NORMAL PATH PKGDIR PKGUSE PKG_LOGDIR PKG_TMPDIR \ PORTAGE_ACTUAL_DISTDIR PORTAGE_ARCHLIST PORTAGE_BASHRC \ PORTAGE_BINPKG_TMPFILE PORTAGE_BUILDDIR \ diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 1c44fce59..cd1ee4a2d 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -863,6 +863,17 @@ class config(object): virtuals ...etc you look in here. """ + # Preserve backupenv values that are initialized in the config + # constructor. Also, preserve XARGS since it is set by the + # portage.data module. + _environ_whitelist = frozenset([ + "FEATURES", "PORTAGE_BIN_PATH", + "PORTAGE_CONFIGROOT", "PORTAGE_DEPCACHEDIR", + "PORTAGE_GID", "PORTAGE_INST_GID", "PORTAGE_INST_UID", + "PORTAGE_PYM_PATH", "PORTDIR_OVERLAY", "ROOT", "USE_ORDER", + "XARGS", + ]) + # Filter selected variables in the config.environ() method so that # they don't needlessly propagate down into the ebuild environment. _environ_filter = [] @@ -926,6 +937,7 @@ class config(object): self.already_in_regenerate = 0 + self._filter_calling_env = False self.locked = 0 self.mycpv = None self.puse = [] @@ -951,6 +963,7 @@ class config(object): self._use_wildcards = False if clone: + self._filter_calling_env = copy.deepcopy(clone._filter_calling_env) self.incrementals = copy.deepcopy(clone.incrementals) self.profile_path = copy.deepcopy(clone.profile_path) self.user_profile_dir = copy.deepcopy(clone.user_profile_dir) @@ -2544,6 +2557,9 @@ class config(object): "return our locally-maintained environment" mydict={} environ_filter = self._environ_filter + filter_calling_env = self._filter_calling_env + environ_whitelist = self._environ_whitelist + env_d = self.configdict["env.d"] for x in self: if x in environ_filter: continue @@ -2552,6 +2568,11 @@ class config(object): writemsg("!!! Non-string value in config: %s=%s\n" % \ (x, myvalue), noiselevel=-1) continue + if filter_calling_env and \ + x not in environ_whitelist: + if myvalue == env_d.get(x) or \ + myvalue == os.environ.get(x): + continue mydict[x] = myvalue if not mydict.has_key("HOME") and mydict.has_key("BUILD_PREFIX"): writemsg("*** HOME not set. Setting to "+mydict["BUILD_PREFIX"]+"\n") @@ -3594,8 +3615,15 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None): mysettings["EBUILD_PHASE"] = mydo _doebuild_exit_status_unlink( mysettings.get("EBUILD_EXIT_STATUS_FILE")) - phase_retval = spawn(actionmap[mydo]["cmd"] % mydo, mysettings, debug=debug, logfile=logfile, **kwargs) - mysettings["EBUILD_PHASE"] = "" + filter_calling_env_state = mysettings._filter_calling_env + if os.path.exists(os.path.join(mysettings["T"], "environment")): + mysettings._filter_calling_env = True + try: + phase_retval = spawn(actionmap[mydo]["cmd"] % mydo, + mysettings, debug=debug, logfile=logfile, **kwargs) + finally: + mysettings["EBUILD_PHASE"] = "" + mysettings._filter_calling_env = filter_calling_env_state msg = _doebuild_exit_status_check(mydo, mysettings) if msg: phase_retval = 1 @@ -3636,7 +3664,13 @@ def spawnebuild(mydo,actionmap,mysettings,debug,alwaysdep=0,logfile=None): apply_secpass_permissions(fpath, uid=myuid, gid=mygid, mode=mystat.st_mode, stat_cached=mystat, follow_links=False) - mycommand = " ".join([MISC_SH_BINARY, "install_qa_check", "install_symlink_html_docs"]) + # Note: PORTAGE_BIN_PATH may differ from the global + # constant when portage is reinstalling itself. + portage_bin_path = mysettings["PORTAGE_BIN_PATH"] + misc_sh_binary = os.path.join(portage_bin_path, + os.path.basename(MISC_SH_BINARY)) + mycommand = " ".join([misc_sh_binary, + "install_qa_check", "install_symlink_html_docs"]) qa_retval = spawn(mycommand, mysettings, debug=debug, logfile=logfile, **kwargs) if qa_retval: writemsg("!!! install_qa_check failed; exiting.\n", @@ -3786,8 +3820,11 @@ def doebuild_environment(myebuild, mydo, myroot, mysettings, debug, use_cache, m mysplit=mysettings["PATH"].split(":") else: mysplit=[] - if PORTAGE_BIN_PATH not in mysplit: - mysettings["PATH"]=PORTAGE_BIN_PATH+":"+mysettings["PATH"] + # Note: PORTAGE_BIN_PATH may differ from the global constant + # when portage is reinstalling itself. + portage_bin_path = mysettings["PORTAGE_BIN_PATH"] + if portage_bin_path not in mysplit: + mysettings["PATH"] = portage_bin_path + ":" + mysettings["PATH"] # Sandbox needs cannonical paths. mysettings["PORTAGE_TMPDIR"] = os.path.realpath( @@ -4024,12 +4061,6 @@ def _doebuild_exit_status_check(mydo, settings): Returns an error string if the shell appeared to exit unsuccessfully, None otherwise. """ - if settings.get("ROOT") == "/" and \ - settings.get("PN") == "portage": - # portage upgrade or downgrade invalidates this check - # since ebuild.sh portage version may differ from the - # current instance that is running in python. - return None exit_status_file = settings.get("EBUILD_EXIT_STATUS_FILE") if not exit_status_file or \ os.path.exists(exit_status_file): @@ -4211,10 +4242,19 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, eerror(l, phase=mydo, key=mysettings.mycpv) return retval + # Note: PORTAGE_BIN_PATH may differ from the global + # constant when portage is reinstalling itself. + portage_bin_path = mysettings["PORTAGE_BIN_PATH"] + ebuild_sh_binary = os.path.join(portage_bin_path, + os.path.basename(EBUILD_SH_BINARY)) + misc_sh_binary = os.path.join(portage_bin_path, + os.path.basename(MISC_SH_BINARY)) + logfile=None builddir_lock = None tmpdir = None tmpdir_orig = None + filter_calling_env_state = mysettings._filter_calling_env try: if mydo in ("digest", "manifest", "help"): # Temporarily exempt the depend phase from manifest checks, in case @@ -4242,7 +4282,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, mysettings["dbkey"] = "" pr, pw = os.pipe() fd_pipes = {0:0, 1:1, 2:2, 9:pw} - mypids = spawn(EBUILD_SH_BINARY + " depend", mysettings, + mypids = spawn(ebuild_sh_binary + " depend", mysettings, fd_pipes=fd_pipes, returnpid=True, droppriv=droppriv) os.close(pw) # belongs exclusively to the child process now maxbytes = 1024 @@ -4271,7 +4311,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, mysettings["dbkey"] = \ os.path.join(mysettings.depcachedir, "aux_db_key_temp") - return spawn(EBUILD_SH_BINARY + " depend", mysettings, + return spawn(ebuild_sh_binary + " depend", mysettings, droppriv=droppriv) # Validate dependency metadata here to ensure that ebuilds with invalid @@ -4337,6 +4377,46 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, if logfile and not os.access(os.path.dirname(logfile), os.W_OK): logfile = None if have_build_dirs: + env_file = os.path.join(mysettings["T"], "environment") + env_stat = None + saved_env = None + try: + env_stat = os.stat(env_file) + except OSError, e: + if e.errno != errno.ENOENT: + raise + del e + if not env_stat: + saved_env = os.path.join( + os.path.dirname(myebuild), "environment.bz2") + if not os.path.isfile(saved_env): + saved_env = None + if saved_env: + retval = os.system( + "bzip2 -dc '%s' > '%s'" % (saved_env, env_file)) + try: + env_stat = os.stat(env_file) + except OSError, e: + if e.errno != errno.ENOENT: + raise + del e + if os.WIFEXITED(retval) and \ + os.WEXITSTATUS(retval) == os.EX_OK and \ + env_stat and env_stat.st_size > 0: + pass + else: + writemsg("!!! Error extracting saved environment: '%s'" % \ + saved_env, noiselevel=-1) + try: + os.unlink(env_file) + except OSError, e: + if e.errno != errno.ENOENT: + raise + del e + env_stat = None + if env_stat: + mysettings._filter_calling_env = True + del env_file, env_stat, saved_env _doebuild_exit_status_unlink( mysettings.get("EBUILD_EXIT_STATUS_FILE")) else: @@ -4345,10 +4425,10 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, # if any of these are being called, handle them -- running them out of # the sandbox -- and stop now. if mydo in ["clean","cleanrm"]: - return spawn(EBUILD_SH_BINARY + " clean", mysettings, + return spawn(ebuild_sh_binary + " clean", mysettings, debug=debug, free=1, logfile=None) elif mydo == "help": - return spawn(EBUILD_SH_BINARY + " " + mydo, mysettings, + return spawn(ebuild_sh_binary + " " + mydo, mysettings, debug=debug, free=1, logfile=logfile) elif mydo == "setup": infodir = os.path.join( @@ -4357,7 +4437,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, """Load USE flags for setup phase of a binary package. Ideally, the environment.bz2 would be used instead.""" mysettings.load_infodir(infodir) - retval = spawn(EBUILD_SH_BINARY + " " + mydo, mysettings, + retval = spawn(ebuild_sh_binary + " " + mydo, mysettings, debug=debug, free=1, logfile=logfile) retval = exit_status_check(retval) if secpass >= 2: @@ -4368,13 +4448,13 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, filemode=060, filemask=0) return retval elif mydo == "preinst": - phase_retval = spawn(" ".join((EBUILD_SH_BINARY, mydo)), + phase_retval = spawn(" ".join((ebuild_sh_binary, mydo)), mysettings, debug=debug, free=1, logfile=logfile) phase_retval = exit_status_check(phase_retval) if phase_retval == os.EX_OK: # Post phase logic and tasks that have been factored out of # ebuild.sh. - myargs = [MISC_SH_BINARY, "preinst_bsdflags", "preinst_mask", + myargs = [misc_sh_binary, "preinst_bsdflags", "preinst_mask", "preinst_sfperms", "preinst_selinux_labels", "preinst_suid_scan"] _doebuild_exit_status_unlink( @@ -4389,13 +4469,13 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, return phase_retval elif mydo == "postinst": mysettings.load_infodir(mysettings["O"]) - phase_retval = spawn(" ".join((EBUILD_SH_BINARY, mydo)), + phase_retval = spawn(" ".join((ebuild_sh_binary, mydo)), mysettings, debug=debug, free=1, logfile=logfile) phase_retval = exit_status_check(phase_retval) if phase_retval == os.EX_OK: # Post phase logic and tasks that have been factored out of # ebuild.sh. - myargs = [MISC_SH_BINARY, "postinst_bsdflags"] + myargs = [misc_sh_binary, "postinst_bsdflags"] _doebuild_exit_status_unlink( mysettings.get("EBUILD_EXIT_STATUS_FILE")) mysettings["EBUILD_PHASE"] = "" @@ -4408,7 +4488,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, return phase_retval elif mydo in ("prerm", "postrm", "config", "info"): mysettings.load_infodir(mysettings["O"]) - retval = spawn(EBUILD_SH_BINARY + " " + mydo, + retval = spawn(ebuild_sh_binary + " " + mydo, mysettings, debug=debug, free=1, logfile=logfile) retval = exit_status_check(retval) return retval @@ -4464,11 +4544,9 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, # mod_echo module might push the original message off of the # top of the terminal and prevent the user from being able to # see it. - mysettings["EBUILD_PHASE"] = "unpack" - cmd = "source '%s/isolated-functions.sh' ; " % PORTAGE_BIN_PATH - cmd += "eerror \"Fetch failed for '%s'\"" % mycpv - portage.process.spawn(["bash", "-c", cmd], - env=mysettings.environ()) + from portage.elog.messages import eerror + eerror("Fetch failed for '%s'" % mycpv, + phase="unpack", key=mycpv) from portage.elog import elog_process elog_process(mysettings.mycpv, mysettings) return 1 @@ -4546,8 +4624,8 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, fakeroot = "fakeroot" in mysettings.features - ebuild_sh = EBUILD_SH_BINARY + " %s" - misc_sh = MISC_SH_BINARY + " dyn_%s" + ebuild_sh = ebuild_sh_binary + " %s" + misc_sh = misc_sh_binary + " dyn_%s" # args are for the to spawn function actionmap = { @@ -4609,6 +4687,7 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0, return retval finally: + mysettings._filter_calling_env = filter_calling_env_state if tmpdir: mysettings["PORTAGE_TMPDIR"] = tmpdir_orig shutil.rmtree(tmpdir) @@ -6004,15 +6083,11 @@ def create_trees(config_root=None, target_root=None, trees=None): # with ROOT != "/", so we wipe out the "backupenv" for the # config that is associated with ROOT == "/" and regenerate # it's incrementals. - # Preserve backupenv values that are initialized in the config # constructor. Also, preserve XARGS since it is set by the # portage.data module. - backupenv_whitelist = set(["FEATURES", "PORTAGE_BIN_PATH", - "PORTAGE_CONFIGROOT", "PORTAGE_DEPCACHEDIR", - "PORTAGE_GID", "PORTAGE_INST_GID", "PORTAGE_INST_UID", - "PORTAGE_PYM_PATH", "PORTDIR_OVERLAY", "ROOT", "USE_ORDER", - "XARGS"]) + + backupenv_whitelist = settings._environ_whitelist backupenv = settings.configdict["backupenv"] for k, v in os.environ.iteritems(): if k in backupenv_whitelist: diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 610831474..1fc9b213d 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -2004,8 +2004,18 @@ class dblink(object): outfile.flush() outfile.close() + # If portage is reinstalling itself, remove the old + # version now since we want to use the temporary + # PORTAGE_BIN_PATH that will be removed when we return. + reinstall_self = False + if self.myroot == "/" and \ + "sys-apps" == self.cat and \ + "portage" == pkgsplit(self.pkg)[0]: + reinstall_self = True + for dblnk in others_in_slot: - if dblnk.mycpv != self.mycpv: + if dblnk.mycpv != self.mycpv and \ + not reinstall_self: continue writemsg_stdout(">>> Safely unmerging already-installed instance...\n") # These caches are populated during collision-protect and the data @@ -2019,8 +2029,11 @@ class dblink(object): others_in_slot.remove(dblnk) # dblnk will unmerge itself now dblnk.unmerge(trimworld=0, ldpath_mtimes=prev_mtimes, others_in_slot=others_in_slot) + # TODO: Check status and abort if necessary. + dblnk.delete() writemsg_stdout(">>> Original instance of package unmerged safely.\n") - break + if not reinstall_self: + break # We hold both directory locks. self.dbdir = self.dbpkgdir @@ -2336,6 +2349,45 @@ class dblink(object): writemsg_stdout(zing + " " + mydest + "\n") def merge(self, mergeroot, inforoot, myroot, myebuild=None, cleanup=0, + mydbapi=None, prev_mtimes=None): + """ + If portage is reinstalling itself, create temporary + copies of PORTAGE_BIN_PATH and PORTAGE_PYM_PATH in order + to avoid relying on the new versions which may be + incompatible. Register an atexit hook to clean up the + temporary directories. Pre-load elog modules here since + we won't be able to later if they get unmerged (happens + when namespace changes). + """ + if self.myroot == "/" and \ + "sys-apps" == self.cat and \ + "portage" == pkgsplit(self.pkg)[0]: + settings = self.settings + base_path_orig = os.path.dirname(settings["PORTAGE_BIN_PATH"]) + from tempfile import mkdtemp + import shutil + base_path_tmp = mkdtemp() + from portage.process import atexit_register + atexit_register(shutil.rmtree, base_path_tmp) + dir_perms = 0755 + for subdir in "bin", "pym": + var_name = "PORTAGE_%s_PATH" % subdir.upper() + var_orig = settings[var_name] + var_new = os.path.join(base_path_tmp, subdir) + settings[var_name] = var_new + settings.backup_changes(var_name) + shutil.copytree(var_orig, var_new, symlinks=True) + os.chmod(var_new, dir_perms) + os.chmod(base_path_tmp, dir_perms) + # This serves so pre-load the modules. + elog_process(self.mycpv, self.settings, + phasefilter=filter_mergephases) + + return self._merge(mergeroot, inforoot, + myroot, myebuild=myebuild, cleanup=cleanup, + mydbapi=mydbapi, prev_mtimes=prev_mtimes) + + def _merge(self, mergeroot, inforoot, myroot, myebuild=None, cleanup=0, mydbapi=None, prev_mtimes=None): retval = -1 self.lockdb() diff --git a/pym/portage/elog/__init__.py b/pym/portage/elog/__init__.py index e1df6d9bd..d3e333534 100644 --- a/pym/portage/elog/__init__.py +++ b/pym/portage/elog/__init__.py @@ -42,9 +42,31 @@ def _combine_logentries(logentries): rValue.append("\n") return "".join(rValue) +_elog_mod_imports = {} _elog_atexit_handlers = [] _preserve_logentries = {} def elog_process(cpv, mysettings, phasefilter=None): + + global _elog_mod_imports + logsystems = mysettings.get("PORTAGE_ELOG_SYSTEM","").split() + for s in logsystems: + # allow per module overrides of PORTAGE_ELOG_CLASSES + if ":" in s: + s, levels = s.split(":", 1) + levels = levels.split(",") + # - is nicer than _ for module names, so allow people to use it. + s = s.replace("-", "_") + try: + name = "portage.elog.mod_" + s + m = _elog_mod_imports.get(name) + if m is None: + m = __import__(name) + for comp in name.split(".")[1:]: + m = getattr(m, comp) + _elog_mod_imports[name] = m + except ImportError: + pass + ebuild_logentries = collect_ebuild_messages(os.path.join(mysettings["T"], "logging")) all_logentries = collect_messages() if all_logentries.has_key(cpv): @@ -94,9 +116,12 @@ def elog_process(cpv, mysettings, phasefilter=None): # FIXME: ugly ad.hoc import code # TODO: implement a common portage module loader name = "portage.elog.mod_" + s - m = __import__(name) - for comp in name.split(".")[1:]: - m = getattr(m, comp) + m = _elog_mod_imports.get(name) + if m is None: + m = __import__(name) + for comp in name.split(".")[1:]: + m = getattr(m, comp) + _elog_mod_imports[name] = m def timeout_handler(signum, frame): raise PortageException("Timeout in elog_process for system '%s'" % s) import signal -- 2.26.2