From: Fabian Groffen Date: Fri, 3 Sep 2010 15:30:54 +0000 (+0200) Subject: Merge remote branch 'overlays-gentoo-org/master' into prefix X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=94ce7d265005e1922902ce4c3fb0321d183d0cd6;p=portage.git Merge remote branch 'overlays-gentoo-org/master' into prefix Conflicts: bin/ebuild bin/ebuild-helpers/ecompressdir bin/ebuild-helpers/prepalldocs bin/ebuild-helpers/prepallman bin/ebuild-helpers/prepinfo bin/ebuild-helpers/prepman bin/egencache bin/regenworld mkrelease.sh pym/_emerge/AtomArg.py pym/_emerge/Binpkg.py pym/_emerge/BinpkgExtractorAsync.py pym/_emerge/Blocker.py pym/_emerge/Package.py pym/_emerge/actions.py pym/_emerge/depgraph.py pym/portage/_legacy_globals.py pym/portage/data.py pym/portage/dbapi/vartree.py pym/portage/package/ebuild/config.py pym/portage/package/ebuild/fetch.py --- 94ce7d265005e1922902ce4c3fb0321d183d0cd6 diff --cc bin/ebuild index a00e5ed7d,a419d264b..283c2ff2f --- a/bin/ebuild +++ b/bin/ebuild @@@ -1,5 -1,5 +1,5 @@@ -#!/usr/bin/python -O +#!@PORTAGE_PYTHON@ -O - # Copyright 1999-2006 Gentoo Foundation + # Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function diff --cc bin/ebuild-helpers/ecompressdir index 9bcdd9342,acdb1cd7f..3576443bb --- a/bin/ebuild-helpers/ecompressdir +++ b/bin/ebuild-helpers/ecompressdir @@@ -13,7 -13,8 +13,8 @@@ case $1 i --ignore) shift for skip in "$@" ; do - [[ -d ${ED}${skip} ]] && touch "${ED}${skip}.ecompress.skip" - [[ -d ${D}${skip} || -f ${D}${skip} ]] \ ++ [[ -d ${ED}${skip} || -f ${ED}${skip} ]] \ + && touch "${D}${skip}.ecompress.skip" done exit 0 ;; diff --cc bin/ebuild-helpers/prepalldocs index 4dda218b0,3b16f9726..5c8b8e939 --- a/bin/ebuild-helpers/prepalldocs +++ b/bin/ebuild-helpers/prepalldocs @@@ -1,14 -1,20 +1,20 @@@ -#!/bin/bash +#!@PORTAGE_BASH@ - # Copyright 1999-2007 Gentoo Foundation + # Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh +source "${PORTAGE_BIN_PATH:-@PORTAGE_BASE@/bin}"/isolated-functions.sh + if ! hasq "${EAPI}" 0 1 2 3; then + eqawarn "QA Notice: Deprecated call to 'prepalldocs'" + exit 0 + fi + if [[ -n $1 ]] ; then vecho "${0##*/}: invalid usage; takes no arguments" 1>&2 fi - [[ -d ${ED}usr/share/doc ]] || exit 0 + cd "${D}" -[[ -d usr/share/doc ]] || exit 0 ++[[ -d ${EPREFIX}usr/share/doc ]] || exit 0 ecompressdir --ignore /usr/share/doc/${PF}/html ecompressdir --queue /usr/share/doc diff --cc bin/ebuild-helpers/prepallman index ea0485bf3,4568bac1d..6e95f0d20 --- a/bin/ebuild-helpers/prepallman +++ b/bin/ebuild-helpers/prepallman @@@ -1,14 -1,17 +1,19 @@@ -#!/bin/bash +#!@PORTAGE_BASH@ - # Copyright 1999-2007 Gentoo Foundation + # Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh ++source "${PORTAGE_BIN_PATH:-@PORTAGE_BASE@/bin}"/isolated-functions.sh + + # replaced by controllable compression in EAPI 4 + hasq "${EAPI}" 0 1 2 3 || exit 0 + ret=0 -find "${D}" -type d -name man > "${T}"/prepallman.filelist +[[ -d ${ED} ]] || exit ${ret} + +find "${ED}" -type d -name man > "${T}"/prepallman.filelist while read mandir ; do - mandir=${mandir#${D}} + mandir=${mandir#${ED}} prepman "${mandir%/man}" ((ret|=$?)) done < "${T}"/prepallman.filelist diff --cc bin/ebuild-helpers/prepinfo index d372c1bb8,fa37e5e64..2200b89b2 --- a/bin/ebuild-helpers/prepinfo +++ b/bin/ebuild-helpers/prepinfo @@@ -1,16 -1,16 +1,16 @@@ -#!/bin/bash +#!@PORTAGE_BASH@ - # Copyright 1999-2007 Gentoo Foundation + # Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh +source "${PORTAGE_BIN_PATH:-@PORTAGE_BASE@/bin}"/isolated-functions.sh if [[ -z $1 ]] ; then - infodir="/usr/share/info" + infodir="${EPREFIX}/usr/share/info" else - if [[ -d ${D}$1/share/info ]] ; then - infodir="$1/share/info" + if [[ -d ${ED}$1/share/info ]] ; then + infodir="${EPREFIX}$1/share/info" else - infodir="$1/info" + infodir="${EPREFIX}$1/info" fi fi diff --cc bin/ebuild-helpers/prepman index 78f91012b,346640248..231f9aef8 --- a/bin/ebuild-helpers/prepman +++ b/bin/ebuild-helpers/prepman @@@ -1,13 -1,13 +1,13 @@@ -#!/bin/bash +#!@PORTAGE_BASH@ - # Copyright 1999-2007 Gentoo Foundation + # Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/isolated-functions.sh +source "${PORTAGE_BIN_PATH:-@PORTAGE_BASE@/bin}"/isolated-functions.sh if [[ -z $1 ]] ; then - mandir="${D}usr/share/man" + mandir="${ED}usr/share/man" else - mandir="${D}$1/man" + mandir="${ED}$1/man" fi if [[ ! -d ${mandir} ]] ; then diff --cc bin/ebuild.sh index 5938d686b,73ea8c807..8bf22ea5b --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@@ -20,15 -20,15 +20,21 @@@ ROOTPATH=${ROOTPATH##: ROOTPATH=${ROOTPATH%%:} PREROOTPATH=${PREROOTPATH##:} PREROOTPATH=${PREROOTPATH%%:} -PATH=$PORTAGE_BIN_PATH/ebuild-helpers:$PREROOTPATH${PREROOTPATH:+:}/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin${ROOTPATH:+:}$ROOTPATH +#PATH=$PORTAGE_BIN_PATH/ebuild-helpers:$PREROOTPATH${PREROOTPATH:+:}/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin${ROOTPATH:+:}$ROOTPATH +# PREFIX: our DEFAULT_PATH is equal to the above when not using an +# offset prefix. With such prefix, the usr/local bits are excluded, and +# the prefixed variants of {usr/,}{s,}bin are taken. The additional +# paths given during configure, always come as last thing since they +# should never override anything from the prefix itself. +PATH="$PORTAGE_BIN_PATH/ebuild-helpers:$PREROOTPATH${PREROOTPATH:+:}${DEFAULT_PATH}${ROOTPATH:+:}$ROOTPATH${EXTRA_PATH:+:}${EXTRA_PATH}" export PATH + # This is just a temporary workaround for portage-9999 users since + # earlier portage versions do not detect a version change in this case + # (9999 to 9999) and therefore they try execute an incompatible version of + # ebuild.sh during the upgrade. + export PORTAGE_BZIP2_COMMAND=${PORTAGE_BZIP2_COMMAND:-bzip2} + # These two functions wrap sourcing and calling respectively. At present they # perform a qa check to make sure eclasses and ebuilds and profiles don't mess # with shell opts (shopts). Ebuilds/eclasses changing shopts should reset them diff --cc bin/egencache index 2b89d2811,a16ac4f93..3dc65f09a --- a/bin/egencache +++ b/bin/egencache @@@ -1,5 -1,5 +1,5 @@@ -#!/usr/bin/python +#!@PORTAGE_PYTHON@ - # Copyright 2009 Gentoo Foundation + # Copyright 2009-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function diff --cc bin/misc-functions.sh index bb936e88f,1edacadca..a175c0f17 mode 100644,100755..100644 --- a/bin/misc-functions.sh +++ b/bin/misc-functions.sh @@@ -1461,10 -855,10 +1565,10 @@@ dyn_package() PORTAGE_BINPKG_TMPFILE="${PKGDIR}/${CATEGORY}/${PF}.tbz2" mkdir -p "${PORTAGE_BINPKG_TMPFILE%/*}" || die "mkdir failed" tar $tar_options -cf - $PORTAGE_BINPKG_TAR_OPTS -C "${D}" . | \ - bzip2 -cf > "$PORTAGE_BINPKG_TMPFILE" + $PORTAGE_BZIP2_COMMAND -c > "$PORTAGE_BINPKG_TMPFILE" assert "failed to pack binary package: '$PORTAGE_BINPKG_TMPFILE'" PYTHONPATH=${PORTAGE_PYM_PATH}${PYTHONPATH:+:}${PYTHONPATH} \ - "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH"/xpak-helper.py recompose \ + "${PORTAGE_PYTHON:-@PORTAGE_PYTHON@}" "$PORTAGE_BIN_PATH"/xpak-helper.py recompose \ "$PORTAGE_BINPKG_TMPFILE" "$PORTAGE_BUILDDIR/build-info" if [ $? -ne 0 ]; then rm -f "${PORTAGE_BINPKG_TMPFILE}" diff --cc bin/regenworld index 03815858a,292eac1ac..ee185131f --- a/bin/regenworld +++ b/bin/regenworld @@@ -1,23 -1,23 +1,22 @@@ -#!/usr/bin/python +#!@PORTAGE_PYTHON@ - # Copyright 1999-2006 Gentoo Foundation + # Copyright 1999-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 - # NOTE: this file does not respect ROOT - from __future__ import print_function import sys -try: - import portage -except ImportError: - from os import path as osp - sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")) - import portage - +# for an explanation on this logic, see pym/_emerge/__init__.py +from os import environ as ose +from os import path as osp +if ose.__contains__("PORTAGE_PYTHONPATH"): + sys.path.insert(0, ose["PORTAGE_PYTHONPATH"]) +else: + sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp. realpath(__file__))), "pym")) +import portage from portage import os - import portage.exception - import re, portage.const -from portage._sets.files import StaticFileSet, WorldSelectedSet - + import re + import tempfile + import textwrap __candidatematcher__ = re.compile("^[0-9]+: \\*\\*\\* emerge ") __noncandidatematcher__ = re.compile(" sync( |$)| clean( |$)| search( |$)|--oneshot|--fetchonly| unmerge( |$)") diff --cc pym/_emerge/Binpkg.py index 5632fa8d7,ac9a68a0f..5f190df59 --- a/pym/_emerge/Binpkg.py +++ b/pym/_emerge/Binpkg.py @@@ -299,15 -273,9 +299,16 @@@ class Binpkg(CompositeTask) self.wait() return + # if the prefix differs, we copy it to the image after + # extraction using chpathtool + if (self._buildprefix != EPREFIX): + pkgloc = self._work_dir + else: + pkgloc = self._image_dir + extractor = BinpkgExtractorAsync(background=self.background, + env=self.settings.environ(), - image_dir=self._image_dir, + image_dir=pkgloc, pkg=self.pkg, pkg_path=self._pkg_path, logfile=self.settings.get("PORTAGE_LOG_FILE"), scheduler=self.scheduler) diff --cc pym/_emerge/Package.py index 35b7879e0,0a4f2ab7a..e8ac2ff2c --- a/pym/_emerge/Package.py +++ b/pym/_emerge/Package.py @@@ -6,11 -5,11 +5,12 @@@ import sy from itertools import chain import portage from portage.cache.mappings import slot_dict_class - from portage.dep import isvalidatom, use_reduce, \ + from portage.dep import Atom, check_required_use, use_reduce, \ paren_enclose, _slot_re from portage.eapi import eapi_has_iuse_defaults, eapi_has_required_use - from portage.const import EPREFIX + from portage.exception import InvalidDependString from _emerge.Task import Task ++from portage.const import EPREFIX if sys.hexversion >= 0x3000000: basestring = str @@@ -32,8 -31,11 +32,11 @@@ class Package(Task) "INHERITED", "IUSE", "KEYWORDS", "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND", "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE", - "_mtime_", "DEFINED_PHASES", "REQUIRED_USE"] + "_mtime_", "DEFINED_PHASES", "REQUIRED_USE", "EPREFIX"] + _dep_keys = ('DEPEND', 'PDEPEND', 'RDEPEND',) + _use_conditional_misc_keys = ('LICENSE', 'PROPERTIES', 'RESTRICT') + def __init__(self, **kwargs): Task.__init__(self, **kwargs) self.root = self.root_config.root @@@ -137,9 -200,7 +208,10 @@@ if 'EAPI.unsupported' in masks: return False - if 'invalid' in masks: + if self.built and not self.installed and ( \ + 'EPREFIX.missing' in masks or \ - 'EPREFIX.tooshort' in masks): ++ 'EPREFIX.tooshort' in masks) or \ ++ 'invalid' in masks: return False if not self.installed and ( \ diff --cc pym/_emerge/actions.py index bb6d7d55f,d84e434d6..6fe7480c5 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@@ -25,9 -25,10 +25,10 @@@ from itertools import chai import portage from portage import os from portage import digraph - from portage import _unicode_decode, _unicode_encode + from portage import _unicode_decode from portage.cache.cache_errors import CacheError -from portage.const import GLOBAL_CONFIG_PATH, NEWS_LIB_PATH +from portage.const import GLOBAL_CONFIG_PATH, NEWS_LIB_PATH, EPREFIX + from portage.const import _ENABLE_DYN_LINK_MAP from portage.dbapi.dep_expand import dep_expand from portage.output import blue, bold, colorize, create_color_func, darkgreen, \ red, yellow diff --cc pym/_emerge/depgraph.py index 7e2b0d1a1,a51bd7fac..e46659d00 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@@ -6119,12 -6150,12 +6150,18 @@@ def _get_masking_status(pkg, pkgsetting msg = "violated use flag constraints: '%s'" % required_use mreasons.append(_MaskReason("REQUIRED_USE", "REQUIRED_USE violated")) + if pkg.built and not pkg.installed: + if not "EPREFIX" in pkg.metadata or not pkg.metadata["EPREFIX"]: + mreasons.append("missing EPREFIX") + elif len(pkg.metadata["EPREFIX"].strip()) < len(pkgsettings["EPREFIX"]): + mreasons.append("EPREFIX: '%s' too small" % pkg.metadata["EPREFIX"]) + + if pkg.invalid: + for msg_type, msgs in pkg.invalid.items(): + for msg in msgs: + mreasons.append( + _MaskReason("invalid", "invalid: %s" % (msg,))) + if not pkg.metadata["SLOT"]: mreasons.append( _MaskReason("invalid", "SLOT: undefined")) diff --cc pym/portage/_sets/profiles.py index 87ac0ac96,e47f08db3..d2e29aa2d --- a/pym/portage/_sets/profiles.py +++ b/pym/portage/_sets/profiles.py @@@ -5,10 -5,9 +5,10 @@@ import loggin from portage import os from portage.util import grabfile_package, stack_lists - from portage.sets.base import PackageSet - from portage.sets import get_boolean + from portage._sets.base import PackageSet + from portage._sets import get_boolean from portage.util import writemsg_level +from portage.const import EPREFIX __all__ = ["PackagesSystemSet"] diff --cc pym/portage/data.py index 006ee1342,c38fa17bc..b1018edd0 --- a/pym/portage/data.py +++ b/pym/portage/data.py @@@ -1,9 -1,8 +1,9 @@@ # data.py -- Calculated/Discovered Data Values - # Copyright 1998-2009 Gentoo Foundation + # Copyright 1998-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 - import os, sys, pwd, grp, platform + import os, pwd, grp, platform +from portage.const import PORTAGE_GROUPNAME, PORTAGE_USERNAME, rootuid, EPREFIX import portage portage.proxy.lazyimport.lazyimport(globals(), diff --cc pym/portage/dbapi/bintree.py index 22565d1f1,8cb59255d..874c4247e --- a/pym/portage/dbapi/bintree.py +++ b/pym/portage/dbapi/bintree.py @@@ -18,10 -18,9 +18,10 @@@ portage.proxy.lazyimport.lazyimport(glo from portage.cache.mappings import slot_dict_class from portage.dbapi.virtual import fakedbapi - from portage.dep import use_reduce, paren_enclose + from portage.dep import Atom, use_reduce, paren_enclose from portage.exception import InvalidPackageName, \ PermissionDenied, PortageException +from portage.const import EAPI from portage.localization import _ from portage import _movefile diff --cc pym/portage/dbapi/vartree.py index c693476d4,25956f198..854aba916 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@@ -1,8 -1,7 +1,8 @@@ # Copyright 1998-2010 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 - __all__ = ["PreservedLibsRegistry", "LinkageMap", + __all__ = [ + "LinkageMapMachO", "LinkageMapPeCoff", "LinkageMapXCoff" "vardbapi", "vartree", "dblink"] + \ ["write_contents", "tar_contents"] @@@ -31,7 -32,8 +33,8 @@@ portage.proxy.lazyimport.lazyimport(glo ) from portage.const import CACHE_PATH, CONFIG_MEMORY_FILE, \ - PORTAGE_PACKAGE_ATOM, PRIVATE_PATH, VDB_PATH + PORTAGE_PACKAGE_ATOM, PRIVATE_PATH, VDB_PATH, EPREFIX, EPREFIX_LSTRIP, BASH_BINARY + from portage.const import _ENABLE_DYN_LINK_MAP, _ENABLE_PRESERVE_LIBS from portage.dbapi import dbapi from portage.exception import CommandNotFound, \ InvalidData, InvalidPackageName, \ @@@ -73,1713 -74,6 +75,987 @@@ if sys.hexversion >= 0x3000000 basestring = str long = int - class PreservedLibsRegistry(object): - """ This class handles the tracking of preserved library objects """ - def __init__(self, root, filename, autocommit=True): - """ - @param root: root used to check existence of paths in pruneNonExisting - @type root: String - @param filename: absolute path for saving the preserved libs records - @type filename: String - @param autocommit: determines if the file is written after every update - @type autocommit: Boolean - """ - self._root = root - self._filename = filename - self._autocommit = autocommit - self.load() - self.pruneNonExisting() - - def load(self): - """ Reload the registry data from file """ - self._data = None - try: - self._data = pickle.load( - open(_unicode_encode(self._filename, - encoding=_encodings['fs'], errors='strict'), 'rb')) - except (ValueError, pickle.UnpicklingError) as e: - writemsg_level(_("!!! Error loading '%s': %s\n") % \ - (self._filename, e), level=logging.ERROR, noiselevel=-1) - except (EOFError, IOError) as e: - if isinstance(e, EOFError) or e.errno == errno.ENOENT: - pass - elif e.errno == PermissionDenied.errno: - raise PermissionDenied(self._filename) - else: - raise - if self._data is None: - self._data = {} - self._data_orig = self._data.copy() - def store(self): - """ Store the registry data to file. No need to call this if autocommit - was enabled. - """ - if os.environ.get("SANDBOX_ON") == "1" or \ - self._data == self._data_orig: - return - try: - f = atomic_ofstream(self._filename, 'wb') - pickle.dump(self._data, f, protocol=2) - f.close() - except EnvironmentError as e: - if e.errno != PermissionDenied.errno: - writemsg("!!! %s %s\n" % (e, self._filename), noiselevel=-1) - else: - self._data_orig = self._data.copy() - - def register(self, cpv, slot, counter, paths): - """ Register new objects in the registry. If there is a record with the - same packagename (internally derived from cpv) and slot it is - overwritten with the new data. - @param cpv: package instance that owns the objects - @type cpv: CPV (as String) - @param slot: the value of SLOT of the given package instance - @type slot: String - @param counter: vdb counter value for the package instace - @type counter: Integer - @param paths: absolute paths of objects that got preserved during an update - @type paths: List - """ - cp = "/".join(catpkgsplit(cpv)[:2]) - cps = cp+":"+slot - if len(paths) == 0 and cps in self._data \ - and self._data[cps][0] == cpv and int(self._data[cps][1]) == int(counter): - del self._data[cps] - elif len(paths) > 0: - self._data[cps] = (cpv, counter, paths) - if self._autocommit: - self.store() - - def unregister(self, cpv, slot, counter): - """ Remove a previous registration of preserved objects for the given package. - @param cpv: package instance whose records should be removed - @type cpv: CPV (as String) - @param slot: the value of SLOT of the given package instance - @type slot: String - """ - self.register(cpv, slot, counter, []) - - def pruneNonExisting(self): - """ Remove all records for objects that no longer exist on the filesystem. """ - - os = _os_merge - - for cps in list(self._data): - cpv, counter, paths = self._data[cps] - paths = [f for f in paths \ - if os.path.exists(os.path.join(self._root, f.lstrip(os.sep)))] - if len(paths) > 0: - self._data[cps] = (cpv, counter, paths) - else: - del self._data[cps] - if self._autocommit: - self.store() - - def hasEntries(self): - """ Check if this registry contains any records. """ - return len(self._data) > 0 - - def getPreservedLibs(self): - """ Return a mapping of packages->preserved objects. - @returns mapping of package instances to preserved objects - @rtype Dict cpv->list-of-paths - """ - rValue = {} - for cps in self._data: - rValue[self._data[cps][0]] = self._data[cps][2] - return rValue - - class LinkageMap(object): - - """Models dynamic linker dependencies.""" - - _needed_aux_key = "NEEDED.ELF.2" - _soname_map_class = slot_dict_class( - ("consumers", "providers"), prefix="") - - def __init__(self, vardbapi): - self._dbapi = vardbapi - self._root = self._dbapi._eroot - self._libs = {} - self._obj_properties = {} - self._obj_key_cache = {} - self._defpath = set() - self._path_key_cache = {} - - def _clear_cache(self): - self._libs.clear() - self._obj_properties.clear() - self._obj_key_cache.clear() - self._defpath.clear() - self._path_key_cache.clear() - - def _path_key(self, path): - key = self._path_key_cache.get(path) - if key is None: - key = self._ObjectKey(path, self._root) - self._path_key_cache[path] = key - return key - - def _obj_key(self, path): - key = self._obj_key_cache.get(path) - if key is None: - key = self._ObjectKey(path, self._root) - self._obj_key_cache[path] = key - return key - - class _ObjectKey(object): - - """Helper class used as _obj_properties keys for objects.""" - - __slots__ = ("__weakref__", "_key") - - def __init__(self, obj, root): - """ - This takes a path to an object. - - @param object: path to a file - @type object: string (example: '/usr/bin/bar') - - """ - self._key = self._generate_object_key(obj, root) - - def __hash__(self): - return hash(self._key) - - def __eq__(self, other): - return self._key == other._key - - def _generate_object_key(self, obj, root): - """ - Generate object key for a given object. - - @param object: path to a file - @type object: string (example: '/usr/bin/bar') - @rtype: 2-tuple of types (long, int) if object exists. string if - object does not exist. - @return: - 1. 2-tuple of object's inode and device from a stat call, if object - exists. - 2. realpath of object if object does not exist. - - """ - - os = _os_merge - - try: - _unicode_encode(obj, - encoding=_encodings['merge'], errors='strict') - except UnicodeEncodeError: - # The package appears to have been merged with a - # different value of sys.getfilesystemencoding(), - # so fall back to utf_8 if appropriate. - try: - _unicode_encode(obj, - encoding=_encodings['fs'], errors='strict') - except UnicodeEncodeError: - pass - else: - os = portage.os - - abs_path = os.path.join(root, obj.lstrip(os.sep)) - try: - object_stat = os.stat(abs_path) - except OSError: - # Use the realpath as the key if the file does not exists on the - # filesystem. - return os.path.realpath(abs_path) - # Return a tuple of the device and inode. - return (object_stat.st_dev, object_stat.st_ino) - - def file_exists(self): - """ - Determine if the file for this key exists on the filesystem. - - @rtype: Boolean - @return: - 1. True if the file exists. - 2. False if the file does not exist or is a broken symlink. - - """ - return isinstance(self._key, tuple) - - class _LibGraphNode(_ObjectKey): - __slots__ = ("alt_paths",) - - def __init__(self, obj, root): - LinkageMap._ObjectKey.__init__(self, obj, root) - self.alt_paths = set() - - def __str__(self): - return str(sorted(self.alt_paths)) - - def rebuild(self, exclude_pkgs=None, include_file=None): - """ - Raises CommandNotFound if there are preserved libs - and the scanelf binary is not available. - """ - - os = _os_merge - root = self._root - root_len = len(root) - 1 - self._clear_cache() - self._defpath.update(getlibpaths(self._root)) - libs = self._libs - obj_key_cache = self._obj_key_cache - obj_properties = self._obj_properties - - lines = [] - - # Data from include_file is processed first so that it - # overrides any data from previously installed files. - if include_file is not None: - lines += grabfile(include_file) - - aux_keys = [self._needed_aux_key] - for cpv in self._dbapi.cpv_all(): - if exclude_pkgs is not None and cpv in exclude_pkgs: - continue - lines += self._dbapi.aux_get(cpv, aux_keys)[0].split('\n') - # Cache NEEDED.* files avoid doing excessive IO for every rebuild. - self._dbapi.flush_cache() - - # have to call scanelf for preserved libs here as they aren't - # registered in NEEDED.ELF.2 files - plibs = set() - if self._dbapi.plib_registry and self._dbapi.plib_registry.getPreservedLibs(): - args = [EPREFIX+"/usr/bin/scanelf", "-qF", "%a;%F;%S;%r;%n"] - for items in self._dbapi.plib_registry.getPreservedLibs().values(): - plibs.update(items) - args.extend(os.path.join(root, x.lstrip("." + os.sep)) \ - for x in items) - try: - proc = subprocess.Popen(args, stdout=subprocess.PIPE) - except EnvironmentError as e: - if e.errno != errno.ENOENT: - raise - raise CommandNotFound(args[0]) - else: - for l in proc.stdout: - try: - l = _unicode_decode(l, - encoding=_encodings['content'], errors='strict') - except UnicodeDecodeError: - l = _unicode_decode(l, - encoding=_encodings['content'], errors='replace') - writemsg_level(_("\nError decoding characters " \ - "returned from scanelf: %s\n\n") % (l,), - level=logging.ERROR, noiselevel=-1) - l = l[3:].rstrip("\n") - if not l: - continue - fields = l.split(";") - if len(fields) < 5: - writemsg_level(_("\nWrong number of fields " \ - "returned from scanelf: %s\n\n") % (l,), - level=logging.ERROR, noiselevel=-1) - continue - fields[1] = fields[1][root_len:] - plibs.discard(fields[1]) - lines.append(";".join(fields)) - proc.wait() - - if plibs: - # Preserved libraries that did not appear in the scanelf output. - # This is known to happen with statically linked libraries. - # Generate dummy lines for these, so we can assume that every - # preserved library has an entry in self._obj_properties. This - # is important in order to prevent findConsumers from raising - # an unwanted KeyError. - for x in plibs: - lines.append(";".join(['', x, '', '', ''])) - - for l in lines: - l = l.rstrip("\n") - if not l: - continue - fields = l.split(";") - if len(fields) < 5: - writemsg_level(_("\nWrong number of fields " \ - "in %s: %s\n\n") % (self._needed_aux_key, l), - level=logging.ERROR, noiselevel=-1) - continue - arch = fields[0] - obj = fields[1] - soname = fields[2] - path = set([normalize_path(x) \ - for x in filter(None, fields[3].replace( - "${ORIGIN}", os.path.dirname(obj)).replace( - "$ORIGIN", os.path.dirname(obj)).split(":"))]) - needed = [x for x in fields[4].split(",") if x] - - obj_key = self._obj_key(obj) - indexed = True - myprops = obj_properties.get(obj_key) - if myprops is None: - indexed = False - myprops = (arch, needed, path, soname, set()) - obj_properties[obj_key] = myprops - # All object paths are added into the obj_properties tuple. - myprops[4].add(obj) - - # Don't index the same file more that once since only one - # set of data can be correct and therefore mixing data - # may corrupt the index (include_file overrides previously - # installed). - if indexed: - continue - - arch_map = libs.get(arch) - if arch_map is None: - arch_map = {} - libs[arch] = arch_map - if soname: - soname_map = arch_map.get(soname) - if soname_map is None: - soname_map = self._soname_map_class( - providers=set(), consumers=set()) - arch_map[soname] = soname_map - soname_map.providers.add(obj_key) - for needed_soname in needed: - soname_map = arch_map.get(needed_soname) - if soname_map is None: - soname_map = self._soname_map_class( - providers=set(), consumers=set()) - arch_map[needed_soname] = soname_map - soname_map.consumers.add(obj_key) - - def listBrokenBinaries(self, debug=False): - """ - Find binaries and their needed sonames, which have no providers. - - @param debug: Boolean to enable debug output - @type debug: Boolean - @rtype: dict (example: {'/usr/bin/foo': set(['libbar.so'])}) - @return: The return value is an object -> set-of-sonames mapping, where - object is a broken binary and the set consists of sonames needed by - object that have no corresponding libraries to fulfill the dependency. - - """ - - os = _os_merge - - class _LibraryCache(object): - - """ - Caches properties associated with paths. - - The purpose of this class is to prevent multiple instances of - _ObjectKey for the same paths. - - """ - - def __init__(cache_self): - cache_self.cache = {} - - def get(cache_self, obj): - """ - Caches and returns properties associated with an object. - - @param obj: absolute path (can be symlink) - @type obj: string (example: '/usr/lib/libfoo.so') - @rtype: 4-tuple with types - (string or None, string or None, 2-tuple, Boolean) - @return: 4-tuple with the following components: - 1. arch as a string or None if it does not exist, - 2. soname as a string or None if it does not exist, - 3. obj_key as 2-tuple, - 4. Boolean representing whether the object exists. - (example: ('libfoo.so.1', (123L, 456L), True)) - - """ - if obj in cache_self.cache: - return cache_self.cache[obj] - else: - obj_key = self._obj_key(obj) - # Check that the library exists on the filesystem. - if obj_key.file_exists(): - # Get the arch and soname from LinkageMap._obj_properties if - # it exists. Otherwise, None. - arch, _needed, _path, soname, _objs = \ - self._obj_properties.get(obj_key, (None,)*5) - return cache_self.cache.setdefault(obj, \ - (arch, soname, obj_key, True)) - else: - return cache_self.cache.setdefault(obj, \ - (None, None, obj_key, False)) - - rValue = {} - cache = _LibraryCache() - providers = self.listProviders() - - # Iterate over all obj_keys and their providers. - for obj_key, sonames in providers.items(): - arch, _needed, path, _soname, objs = self._obj_properties[obj_key] - path = path.union(self._defpath) - # Iterate over each needed soname and the set of library paths that - # fulfill the soname to determine if the dependency is broken. - for soname, libraries in sonames.items(): - # validLibraries is used to store libraries, which satisfy soname, - # so if no valid libraries are found, the soname is not satisfied - # for obj_key. If unsatisfied, objects associated with obj_key - # must be emerged. - validLibraries = set() - # It could be the case that the library to satisfy the soname is - # not in the obj's runpath, but a symlink to the library is (eg - # libnvidia-tls.so.1 in nvidia-drivers). Also, since LinkageMap - # does not catalog symlinks, broken or missing symlinks may go - # unnoticed. As a result of these cases, check that a file with - # the same name as the soname exists in obj's runpath. - # XXX If we catalog symlinks in LinkageMap, this could be improved. - for directory in path: - cachedArch, cachedSoname, cachedKey, cachedExists = \ - cache.get(os.path.join(directory, soname)) - # Check that this library provides the needed soname. Doing - # this, however, will cause consumers of libraries missing - # sonames to be unnecessarily emerged. (eg libmix.so) - if cachedSoname == soname and cachedArch == arch: - validLibraries.add(cachedKey) - if debug and cachedKey not in \ - set(map(self._obj_key_cache.get, libraries)): - # XXX This is most often due to soname symlinks not in - # a library's directory. We could catalog symlinks in - # LinkageMap to avoid checking for this edge case here. - writemsg( - _("Found provider outside of findProviders:") + \ - (" %s -> %s %s\n" % (os.path.join(directory, soname), - self._obj_properties[cachedKey][4], libraries)), - noiselevel=-1) - # A valid library has been found, so there is no need to - # continue. - break - if debug and cachedArch == arch and \ - cachedKey in self._obj_properties: - writemsg((_("Broken symlink or missing/bad soname: " + \ - "%(dir_soname)s -> %(cachedKey)s " + \ - "with soname %(cachedSoname)s but expecting %(soname)s") % \ - {"dir_soname":os.path.join(directory, soname), - "cachedKey": self._obj_properties[cachedKey], - "cachedSoname": cachedSoname, "soname":soname}) + "\n", - noiselevel=-1) - # This conditional checks if there are no libraries to satisfy the - # soname (empty set). - if not validLibraries: - for obj in objs: - rValue.setdefault(obj, set()).add(soname) - # If no valid libraries have been found by this point, then - # there are no files named with the soname within obj's runpath, - # but if there are libraries (from the providers mapping), it is - # likely that soname symlinks or the actual libraries are - # missing or broken. Thus those libraries are added to rValue - # in order to emerge corrupt library packages. - for lib in libraries: - rValue.setdefault(lib, set()).add(soname) - if debug: - if not os.path.isfile(lib): - writemsg(_("Missing library:") + " %s\n" % (lib,), - noiselevel=-1) - else: - writemsg(_("Possibly missing symlink:") + \ - "%s\n" % (os.path.join(os.path.dirname(lib), soname)), - noiselevel=-1) - return rValue - - def listProviders(self): - """ - Find the providers for all object keys in LinkageMap. - - @rtype: dict (example: - {(123L, 456L): {'libbar.so': set(['/lib/libbar.so.1.5'])}}) - @return: The return value is an object key -> providers mapping, where - providers is a mapping of soname -> set-of-library-paths returned - from the findProviders method. - - """ - rValue = {} - if not self._libs: - self.rebuild() - # Iterate over all object keys within LinkageMap. - for obj_key in self._obj_properties: - rValue.setdefault(obj_key, self.findProviders(obj_key)) - return rValue - - def isMasterLink(self, obj): - """ - Determine whether an object is a master link. - - @param obj: absolute path to an object - @type obj: string (example: '/usr/bin/foo') - @rtype: Boolean - @return: - 1. True if obj is a master link - 2. False if obj is not a master link - - """ - os = _os_merge - basename = os.path.basename(obj) - obj_key = self._obj_key(obj) - if obj_key not in self._obj_properties: - raise KeyError("%s (%s) not in object list" % (obj_key, obj)) - soname = self._obj_properties[obj_key][3] - return (len(basename) < len(soname)) - - def listLibraryObjects(self): - """ - Return a list of library objects. - - Known limitation: library objects lacking an soname are not included. - - @rtype: list of strings - @return: list of paths to all providers - - """ - rValue = [] - if not self._libs: - self.rebuild() - for arch_map in self._libs.values(): - for soname_map in arch_map.values(): - for obj_key in soname_map.providers: - rValue.extend(self._obj_properties[obj_key][4]) - return rValue - - def getSoname(self, obj): - """ - Return the soname associated with an object. - - @param obj: absolute path to an object - @type obj: string (example: '/usr/bin/bar') - @rtype: string - @return: soname as a string - - """ - if not self._libs: - self.rebuild() - if isinstance(obj, self._ObjectKey): - obj_key = obj - if obj_key not in self._obj_properties: - raise KeyError("%s not in object list" % obj_key) - return self._obj_properties[obj_key][3] - if obj not in self._obj_key_cache: - raise KeyError("%s not in object list" % obj) - return self._obj_properties[self._obj_key_cache[obj]][3] - - def findProviders(self, obj): - """ - Find providers for an object or object key. - - This method may be called with a key from _obj_properties. - - In some cases, not all valid libraries are returned. This may occur when - an soname symlink referencing a library is in an object's runpath while - the actual library is not. We should consider cataloging symlinks within - LinkageMap as this would avoid those cases and would be a better model of - library dependencies (since the dynamic linker actually searches for - files named with the soname in the runpaths). - - @param obj: absolute path to an object or a key from _obj_properties - @type obj: string (example: '/usr/bin/bar') or _ObjectKey - @rtype: dict (example: {'libbar.so': set(['/lib/libbar.so.1.5'])}) - @return: The return value is a soname -> set-of-library-paths, where - set-of-library-paths satisfy soname. - - """ - - os = _os_merge - - rValue = {} - - if not self._libs: - self.rebuild() - - # Determine the obj_key from the arguments. - if isinstance(obj, self._ObjectKey): - obj_key = obj - if obj_key not in self._obj_properties: - raise KeyError("%s not in object list" % obj_key) - else: - obj_key = self._obj_key(obj) - if obj_key not in self._obj_properties: - raise KeyError("%s (%s) not in object list" % (obj_key, obj)) - - arch, needed, path, _soname, _objs = self._obj_properties[obj_key] - path_keys = set(self._path_key(x) for x in path.union(self._defpath)) - for soname in needed: - rValue[soname] = set() - if arch not in self._libs or soname not in self._libs[arch]: - continue - # For each potential provider of the soname, add it to rValue if it - # resides in the obj's runpath. - for provider_key in self._libs[arch][soname].providers: - providers = self._obj_properties[provider_key][4] - for provider in providers: - if self._path_key(os.path.dirname(provider)) in path_keys: - rValue[soname].add(provider) - return rValue - - def findConsumers(self, obj): - """ - Find consumers of an object or object key. - - This method may be called with a key from _obj_properties. If this - method is going to be called with an object key, to avoid not catching - shadowed libraries, do not pass new _ObjectKey instances to this method. - Instead pass the obj as a string. - - In some cases, not all consumers are returned. This may occur when - an soname symlink referencing a library is in an object's runpath while - the actual library is not. For example, this problem is noticeable for - binutils since it's libraries are added to the path via symlinks that - are gemerated in the /usr/$CHOST/lib/ directory by binutils-config. - Failure to recognize consumers of these symlinks makes preserve-libs - fail to preserve binutils libs that are needed by these unrecognized - consumers. - - Note that library consumption via dlopen (common for kde plugins) is - currently undetected. However, it is possible to use the - corresponding libtool archive (*.la) files to detect such consumers - (revdep-rebuild is able to detect them). - - @param obj: absolute path to an object or a key from _obj_properties - @type obj: string (example: '/usr/bin/bar') or _ObjectKey - @rtype: set of strings (example: set(['/bin/foo', '/usr/bin/bar'])) - @return: The return value is a soname -> set-of-library-paths, where - set-of-library-paths satisfy soname. - - """ - - os = _os_merge - - rValue = set() - - if not self._libs: - self.rebuild() - - # Determine the obj_key and the set of objects matching the arguments. - if isinstance(obj, self._ObjectKey): - obj_key = obj - if obj_key not in self._obj_properties: - raise KeyError("%s not in object list" % obj_key) - objs = self._obj_properties[obj_key][4] - else: - objs = set([obj]) - obj_key = self._obj_key(obj) - if obj_key not in self._obj_properties: - raise KeyError("%s (%s) not in object list" % (obj_key, obj)) - - # If there is another version of this lib with the - # same soname and the master link points to that - # other version, this lib will be shadowed and won't - # have any consumers. - if not isinstance(obj, self._ObjectKey): - soname = self._obj_properties[obj_key][3] - master_link = os.path.join(self._root, - os.path.dirname(obj).lstrip(os.path.sep), soname) - try: - master_st = os.stat(master_link) - obj_st = os.stat(obj) - except OSError: - pass - else: - if (obj_st.st_dev, obj_st.st_ino) != \ - (master_st.st_dev, master_st.st_ino): - return set() - - # Determine the directory(ies) from the set of objects. - objs_dir_keys = set(self._path_key(os.path.dirname(x)) for x in objs) - defpath_keys = set(self._path_key(x) for x in self._defpath) - - arch, _needed, _path, soname, _objs = self._obj_properties[obj_key] - if arch in self._libs and soname in self._libs[arch]: - # For each potential consumer, add it to rValue if an object from the - # arguments resides in the consumer's runpath. - for consumer_key in self._libs[arch][soname].consumers: - _arch, _needed, path, _soname, consumer_objs = \ - self._obj_properties[consumer_key] - path_keys = defpath_keys.union(self._path_key(x) for x in path) - if objs_dir_keys.intersection(path_keys): - rValue.update(consumer_objs) - return rValue + +class LinkageMapMachO(object): + + """Models dynamic linker dependencies.""" + + _needed_aux_key = "NEEDED.MACHO.3" + _installname_map_class = slot_dict_class( + ("consumers", "providers"), prefix="") + + def __init__(self, vardbapi): + self._dbapi = vardbapi + self._root = self._dbapi.root + self._libs = {} + self._obj_properties = {} + self._obj_key_cache = {} + self._path_key_cache = {} + + def _clear_cache(self): + self._libs.clear() + self._obj_properties.clear() + self._obj_key_cache.clear() + self._path_key_cache.clear() + + def _path_key(self, path): + key = self._path_key_cache.get(path) + if key is None: + key = self._ObjectKey(path, self._root) + self._path_key_cache[path] = key + return key + + def _obj_key(self, path): + key = self._obj_key_cache.get(path) + if key is None: + key = self._ObjectKey(path, self._root) + self._obj_key_cache[path] = key + return key + + class _ObjectKey(object): + + """Helper class used as _obj_properties keys for objects.""" + + __slots__ = ("__weakref__", "_key") + + def __init__(self, obj, root): + """ + This takes a path to an object. + + @param object: path to a file + @type object: string (example: '/usr/bin/bar') + + """ + self._key = self._generate_object_key(obj, root) + + def __hash__(self): + return hash(self._key) + + def __eq__(self, other): + return self._key == other._key + + def _generate_object_key(self, obj, root): + """ + Generate object key for a given object. + + @param object: path to a file + @type object: string (example: '/usr/bin/bar') + @rtype: 2-tuple of types (long, int) if object exists. string if + object does not exist. + @return: + 1. 2-tuple of object's inode and device from a stat call, if object + exists. + 2. realpath of object if object does not exist. + + """ + + os = _os_merge + + try: + _unicode_encode(obj, + encoding=_encodings['merge'], errors='strict') + except UnicodeEncodeError: + # The package appears to have been merged with a + # different value of sys.getfilesystemencoding(), + # so fall back to utf_8 if appropriate. + try: + _unicode_encode(obj, + encoding=_encodings['fs'], errors='strict') + except UnicodeEncodeError: + pass + else: + os = portage.os + + abs_path = os.path.join(root, obj.lstrip(os.path.sep)) + try: + object_stat = os.stat(abs_path) + except OSError: + # Use the realpath as the key if the file does not exists on the + # filesystem. + return os.path.realpath(abs_path) + # Return a tuple of the device and inode. + return (object_stat.st_dev, object_stat.st_ino) + + def file_exists(self): + """ + Determine if the file for this key exists on the filesystem. + + @rtype: Boolean + @return: + 1. True if the file exists. + 2. False if the file does not exist or is a broken symlink. + + """ + return isinstance(self._key, tuple) + + class _LibGraphNode(_ObjectKey): + __slots__ = ("alt_paths",) + + def __init__(self, obj, root): + LinkageMapMachO._ObjectKey.__init__(self, obj, root) + self.alt_paths = set() + + def __str__(self): + return str(sorted(self.alt_paths)) + + def rebuild(self, exclude_pkgs=None, include_file=None): + """ + Raises CommandNotFound if there are preserved libs + and the scanmacho binary is not available. + """ + root = self._root + root_len = len(root) - 1 + self._clear_cache() + libs = self._libs + obj_key_cache = self._obj_key_cache + obj_properties = self._obj_properties + + lines = [] + + # Data from include_file is processed first so that it + # overrides any data from previously installed files. + if include_file is not None: + lines += grabfile(include_file) + + aux_keys = [self._needed_aux_key] + for cpv in self._dbapi.cpv_all(): + if exclude_pkgs is not None and cpv in exclude_pkgs: + continue + lines += self._dbapi.aux_get(cpv, aux_keys)[0].split('\n') + # Cache NEEDED.* files avoid doing excessive IO for every rebuild. + self._dbapi.flush_cache() + + # have to call scanmacho for preserved libs here as they aren't + # registered in NEEDED.MACHO.3 files + plibs = set() + if self._dbapi.plib_registry and self._dbapi.plib_registry.getPreservedLibs(): + args = [EPREFIX+"/usr/bin/scanmacho", "-qF", "%a;%F;%S;%n"] + for items in self._dbapi.plib_registry.getPreservedLibs().values(): + plibs.update(items) + args.extend(os.path.join(root, x.lstrip("." + os.sep)) \ + for x in items) + try: + proc = subprocess.Popen(args, stdout=subprocess.PIPE) + except EnvironmentError, e: + if e.errno != errno.ENOENT: + raise + raise CommandNotFound(args[0]) + else: + for l in proc.stdout: + if not isinstance(l, unicode): + l = unicode(l, encoding='utf_8', errors='replace') + l = l.rstrip("\n") + if not l: + continue + fields = l.split(";") + if len(fields) < 4: + writemsg_level("\nWrong number of fields " + \ + "returned from scanmacho: %s\n\n" % (l,), + level=logging.ERROR, noiselevel=-1) + continue + fields[1] = fields[1][root_len:] + plibs.discard(fields[1]) + lines.append(";".join(fields)) + proc.wait() + + if plibs: + # Preserved libraries that did not appear in the scanmacho + # output. This is known to happen with statically linked + # libraries. Generate dummy lines for these, so we can + # assume that every preserved library has an entry in + # self._obj_properties. This is important in order to + # prevent findConsumers from raising an unwanted KeyError. + for x in plibs: + lines.append(";".join(['', x, '', ''])) + + for l in lines: + l = l.rstrip("\n") + if not l: + continue + fields = l.split(";") + if len(fields) < 4: + writemsg_level("\nWrong number of fields " + \ + "in %s: %s\n\n" % (self._needed_aux_key, l), + level=logging.ERROR, noiselevel=-1) + continue + arch = fields[0] + obj = fields[1] + install_name = os.path.normpath(fields[2]) + needed = filter(None, fields[3].split(",")) + + obj_key = self._obj_key(obj) + indexed = True + myprops = obj_properties.get(obj_key) + if myprops is None: + indexed = False + myprops = (arch, needed, install_name, set()) + obj_properties[obj_key] = myprops + # All object paths are added into the obj_properties tuple. + myprops[3].add(obj) + + # Don't index the same file more that once since only one + # set of data can be correct and therefore mixing data + # may corrupt the index (include_file overrides previously + # installed). + if indexed: + continue + + arch_map = libs.get(arch) + if arch_map is None: + arch_map = {} + libs[arch] = arch_map + if install_name: + installname_map = arch_map.get(install_name) + if installname_map is None: + installname_map = self._installname_map_class( + providers=set(), consumers=set()) + arch_map[install_name] = installname_map + installname_map.providers.add(obj_key) + for needed_installname in needed: + installname_map = arch_map.get(needed_installname) + if installname_map is None: + installname_map = self._installname_map_class( + providers=set(), consumers=set()) + arch_map[needed_installname] = installname_map + installname_map.consumers.add(obj_key) + + def listBrokenBinaries(self, debug=False): + """ + Find binaries and their needed install_names, which have no providers. + + @param debug: Boolean to enable debug output + @type debug: Boolean + @rtype: dict (example: {'/usr/bin/foo': set(['/usr/lib/libbar.dylib'])}) + @return: The return value is an object -> set-of-install_names + mapping, where object is a broken binary and the set + consists of install_names needed by object that have no + corresponding libraries to fulfill the dependency. + + """ + class _LibraryCache(object): + + """ + Caches properties associated with paths. + + The purpose of this class is to prevent multiple instances of + _ObjectKey for the same paths. + + """ + + def __init__(cache_self): + cache_self.cache = {} + + def get(cache_self, obj): + """ + Caches and returns properties associated with an object. + + @param obj: absolute path (can be symlink) + @type obj: string (example: '/usr/lib/libfoo.dylib') + @rtype: 4-tuple with types + (string or None, string or None, 2-tuple, Boolean) + @return: 4-tuple with the following components: + 1. arch as a string or None if it does not exist, + 2. soname as a string or None if it does not exist, + 3. obj_key as 2-tuple, + 4. Boolean representing whether the object exists. + (example: ('libfoo.1.dylib', (123L, 456L), True)) + + """ + if obj in cache_self.cache: + return cache_self.cache[obj] + else: + obj_key = self._obj_key(obj) + # Check that the library exists on the filesystem. + if obj_key.file_exists(): + # Get the install_name from LinkageMapMachO._obj_properties if + # it exists. Otherwise, None. + arch, _, install_name, _ = \ + self._obj_properties.get(obj_key, (None,)*4) + return cache_self.cache.setdefault(obj, \ + (arch, install_name, obj_key, True)) + else: + return cache_self.cache.setdefault(obj, \ + (None, None, obj_key, False)) + + rValue = {} + cache = _LibraryCache() + providers = self.listProviders() + + # Iterate over all obj_keys and their providers. + for obj_key, install_names in providers.items(): + arch, _, _, objs = self._obj_properties[obj_key] + # Iterate over each needed install_name and the set of + # library paths that fulfill the install_name to determine + # if the dependency is broken. + for install_name, libraries in install_names.items(): + # validLibraries is used to store libraries, which + # satisfy install_name, so if no valid libraries are + # found, the install_name is not satisfied for obj_key. + # If unsatisfied, objects associated with obj_key must + # be emerged. + validLibrary = set() # for compat with LinkageMap + cachedArch, cachedInstallname, cachedKey, cachedExists = \ + cache.get(install_name) + # Check that the this library provides the needed soname. Doing + # this, however, will cause consumers of libraries missing + # sonames to be unnecessarily emerged. (eg libmix.so) + if cachedInstallname == install_name and cachedArch == arch: + validLibrary.add(cachedKey) + if debug and cachedKey not in \ + set(map(self._obj_key_cache.get, libraries)): + # XXX This is most often due to soname symlinks not in + # a library's directory. We could catalog symlinks in + # LinkageMap to avoid checking for this edge case here. + print(_("Found provider outside of findProviders:"), \ + install_name, "->", cachedRealpath) + if debug and cachedArch == arch and \ + cachedKey in self._obj_properties: + print(_("Broken symlink or missing/bad install_name:"), \ + install_name, '->', cachedRealpath, \ + "with install_name", cachedInstallname, "but expecting", install_name) + # This conditional checks if there are no libraries to + # satisfy the install_name (empty set). + if not validLibrary: + for obj in objs: + rValue.setdefault(obj, set()).add(install_name) + # If no valid libraries have been found by this + # point, then the install_name does not exist in the + # filesystem, but if there are libraries (from the + # providers mapping), it is likely that soname + # symlinks or the actual libraries are missing or + # broken. Thus those libraries are added to rValue + # in order to emerge corrupt library packages. + for lib in libraries: + rValue.setdefault(lib, set()).add(install_name) + if debug: + if not os.path.isfile(lib): + print(_("Missing library:"), lib) + else: + print(_("Possibly missing symlink:"), \ + install_name) + return rValue + + def listProviders(self): + """ + Find the providers for all object keys in LinkageMap. + + @rtype: dict (example: + {(123L, 456L): {'libbar.dylib': set(['/lib/libbar.1.5.dylib'])}}) + @return: The return value is an object -> providers mapping, where + providers is a mapping of install_name -> + set-of-library-paths returned from the findProviders method. + + """ + rValue = {} + if not self._libs: + self.rebuild() + # Iterate over all binaries within LinkageMapMachO. + for obj_key in self._obj_properties: + rValue.setdefault(obj_key, self.findProviders(obj_key)) + return rValue + + def isMasterLink(self, obj): + """ + Determine whether an object is a master link. + + @param obj: absolute path to an object + @type obj: string (example: '/usr/bin/foo') + @rtype: Boolean + @return: + 1. True if obj is a master link + 2. False if obj is not a master link + + """ + basename = os.path.basename(obj) + obj_key = self._obj_key(obj) + if obj_key not in self._obj_properties: + raise KeyError("%s (%s) not in object list" % (obj_key, obj)) + install_name = self._obj_properties[obj_key][2] + return (len(basename) < len(os.path.basename(install_name))) + + def listLibraryObjects(self): + """ + Return a list of library objects. + + Known limitation: library objects lacking an soname are not included. + + @rtype: list of strings + @return: list of paths to all providers + + """ + rValue = [] + if not self._libs: + self.rebuild() + for arch_map in self._libs.itervalues(): + for soname_map in arch_map.itervalues(): + for obj_key in soname_map.providers: + rValue.extend(self._obj_properties[obj_key][3]) + return rValue + + def getSoname(self, obj): + """ + Return the soname associated with an object. + + @param obj: absolute path to an object + @type obj: string (example: '/usr/bin/bar') + @rtype: string + @return: soname as a string + + """ + if not self._libs: + self.rebuild() + if isinstance(obj, self._ObjectKey): + obj_key = obj + if obj_key not in self._obj_properties: + raise KeyError("%s not in object list" % obj_key) + return self._obj_properties[obj_key][2] + if obj not in self._obj_key_cache: + raise KeyError("%s not in object list" % obj) + return self._obj_properties[self._obj_key_cache[obj]][2] + + def findProviders(self, obj): + """ + Find providers for an object or object key. + + This method may be called with a key from _obj_properties. + + In some cases, not all valid libraries are returned. This may occur when + an soname symlink referencing a library is in an object's runpath while + the actual library is not. We should consider cataloging symlinks within + LinkageMap as this would avoid those cases and would be a better model of + library dependencies (since the dynamic linker actually searches for + files named with the soname in the runpaths). + + @param obj: absolute path to an object or a key from _obj_properties + @type obj: string (example: '/usr/bin/bar') or _ObjectKey + @rtype: dict (example: {'libbar.dylib': set(['/lib/libbar.1.5.dylib'])}) + @return: The return value is a install_name -> set-of-library-paths, where + set-of-library-paths satisfy install_name. + + """ + rValue = {} + + if not self._libs: + self.rebuild() + + # Determine the obj_key from the arguments. + if isinstance(obj, self._ObjectKey): + obj_key = obj + if obj_key not in self._obj_properties: + raise KeyError("%s not in object list" % obj_key) + else: + obj_key = self._obj_key(obj) + if obj_key not in self._obj_properties: + raise KeyError("%s (%s) not in object list" % (obj_key, obj)) + + arch, needed, install_name, _ = self._obj_properties[obj_key] + for install_name in needed: + rValue[install_name] = set() + if arch not in self._libs or install_name not in self._libs[arch]: + continue + # For each potential provider of the install_name, add it to + # rValue if it exists. (Should be one) + for provider_key in self._libs[arch][install_name].providers: + providers = self._obj_properties[provider_key][3] + for provider in providers: + if os.path.exists(provider): + rValue[install_name].add(provider) + return rValue + + def findConsumers(self, obj): + """ + Find consumers of an object or object key. + + This method may be called with a key from _obj_properties. If this + method is going to be called with an object key, to avoid not catching + shadowed libraries, do not pass new _ObjectKey instances to this method. + Instead pass the obj as a string. + + In some cases, not all consumers are returned. This may occur when + an soname symlink referencing a library is in an object's runpath while + the actual library is not. For example, this problem is noticeable for + binutils since it's libraries are added to the path via symlinks that + are gemerated in the /usr/$CHOST/lib/ directory by binutils-config. + Failure to recognize consumers of these symlinks makes preserve-libs + fail to preserve binutils libs that are needed by these unrecognized + consumers. + + @param obj: absolute path to an object or a key from _obj_properties + @type obj: string (example: '/usr/bin/bar') or _ObjectKey + @rtype: set of strings (example: set(['/bin/foo', '/usr/bin/bar'])) + @return: The return value is a install_name -> set-of-library-paths, where + set-of-library-paths satisfy install_name. + + """ + rValue = set() + + if not self._libs: + self.rebuild() + + # Determine the obj_key and the set of objects matching the arguments. + if isinstance(obj, self._ObjectKey): + obj_key = obj + if obj_key not in self._obj_properties: + raise KeyError("%s not in object list" % obj_key) + objs = self._obj_properties[obj_key][3] + else: + objs = set([obj]) + obj_key = self._obj_key(obj) + if obj_key not in self._obj_properties: + raise KeyError("%s (%s) not in object list" % (obj_key, obj)) + + # If there is another version of this lib with the + # same soname and the master link points to that + # other version, this lib will be shadowed and won't + # have any consumers. + if not isinstance(obj, self._ObjectKey): + install_name = self._obj_properties[obj_key][2] + master_link = os.path.join(self._root, + install_name.lstrip(os.path.sep)) + try: + master_st = os.stat(master_link) + obj_st = os.stat(obj) + except OSError: + pass + else: + if (obj_st.st_dev, obj_st.st_ino) != \ + (master_st.st_dev, master_st.st_ino): + return set() + + arch, _, install_name, _ = self._obj_properties[obj_key] + if arch in self._libs and install_name in self._libs[arch]: + # For each potential consumer, add it to rValue if an object from the + # arguments resides in the consumer's runpath. + for consumer_key in self._libs[arch][install_name].consumers: + _, _, _, consumer_objs = \ + self._obj_properties[consumer_key] + rValue.update(consumer_objs) + return rValue + +class LinkageMapPeCoff(LinkageMap): + + """Models dynamic linker dependencies.""" + + # NEEDED.PECOFF.1 has effectively the _same_ format as NEEDED.ELF.2, + # but we keep up the relation "scanelf" -> "NEEDED.ELF", "readpecoff" -> + # "NEEDED.PECOFF", "scanmacho" -> "NEEDED.MACHO", etc. others will follow. + _needed_aux_key = "NEEDED.PECOFF.1" + + class _ObjectKey(LinkageMap._ObjectKey): + + """Helper class used as _obj_properties keys for objects.""" + + def _generate_object_key(self, obj, root): + """ + Generate object key for a given object. This is different from the + Linux implementation, since some systems (e.g. interix) don't have + "inodes", thus the inode field is always zero, or a random value, + making it inappropriate for identifying a file... :) + + @param object: path to a file + @type object: string (example: '/usr/bin/bar') + @rtype: 2-tuple of types (bool, string) + @return: + 2-tuple of boolean indicating existance, and absolut path + """ + abs_path = os.path.join(root, obj.lstrip(os.sep)) + try: + object_stat = os.stat(abs_path) + except OSError: + return (False, os.path.realpath(abs_path)) + # On Interix, the inode field may always be zero, since the + # filesystem (NTFS) has no inodes ... + return (True, os.path.realpath(abs_path)) + + def file_exists(self): + """ + Determine if the file for this key exists on the filesystem. + + @rtype: Boolean + @return: + 1. True if the file exists. + 2. False if the file does not exist or is a broken symlink. + + """ + return self._key[0] + + class _LibGraphNode(_ObjectKey): + __slots__ = ("alt_paths",) + + def __init__(self, obj, root): + LinkageMapPeCoff._ObjectKey.__init__(self, obj, root) + self.alt_paths = set() + + def __str__(self): + return str(sorted(self.alt_paths)) + + def rebuild(self, exclude_pkgs=None, include_file=None): + """ + Raises CommandNotFound if there are preserved libs + and the readpecoff binary is not available. + """ + root = self._root + root_len = len(root) - 1 + self._clear_cache() + self._defpath.update(getlibpaths(self._root)) + libs = self._libs + obj_key_cache = self._obj_key_cache + obj_properties = self._obj_properties + + lines = [] + + # Data from include_file is processed first so that it + # overrides any data from previously installed files. + if include_file is not None: + lines += grabfile(include_file) + + aux_keys = [self._needed_aux_key] + for cpv in self._dbapi.cpv_all(): + if exclude_pkgs is not None and cpv in exclude_pkgs: + continue + lines += self._dbapi.aux_get(cpv, aux_keys)[0].split('\n') + # Cache NEEDED.* files avoid doing excessive IO for every rebuild. + self._dbapi.flush_cache() + + # have to call readpecoff for preserved libs here as they aren't + # registered in NEEDED.PECOFF.1 files + if self._dbapi.plib_registry and self._dbapi.plib_registry.getPreservedLibs(): + args = ["readpecoff", self._dbapi.settings.get('CHOST')] + for items in self._dbapi.plib_registry.getPreservedLibs().values(): + args.extend(os.path.join(root, x.lstrip("." + os.path.sep)) \ + for x in items) + try: + proc = subprocess.Popen(args, stdout=subprocess.PIPE) + except EnvironmentError, e: + if e.errno != errno.ENOENT: + raise + raise CommandNotFound(args[0]) + else: + for l in proc.stdout: + if not isinstance(l, unicode): + l = unicode(l, encoding='utf_8', errors='replace') + l = l.lstrip().rstrip() + if not l: + continue + lines.append(l) + proc.wait() + + for l in lines: + l = l.rstrip("\n") + if not l: + continue + fields = l.split(";") + if len(fields) < 5: + writemsg_level(_("\nWrong number of fields " \ + "in %s: %s\n\n") % (self._needed_aux_key, l), + level=logging.ERROR, noiselevel=-1) + continue + arch = fields[0] + obj = fields[1] + soname = fields[2] + path = set([normalize_path(x) \ + for x in filter(None, fields[3].replace( + "${ORIGIN}", os.path.dirname(obj)).replace( + "$ORIGIN", os.path.dirname(obj)).split(":"))]) + needed = filter(None, fields[4].split(",")) + + obj_key = self._obj_key(obj) + indexed = True + myprops = obj_properties.get(obj_key) + if myprops is None: + indexed = False + myprops = (arch, needed, path, soname, set()) + obj_properties[obj_key] = myprops + # All object paths are added into the obj_properties tuple. + myprops[4].add(obj) + + # Don't index the same file more that once since only one + # set of data can be correct and therefore mixing data + # may corrupt the index (include_file overrides previously + # installed). + if indexed: + continue + + arch_map = libs.get(arch) + if arch_map is None: + arch_map = {} + libs[arch] = arch_map + if soname: + soname_map = arch_map.get(soname) + if soname_map is None: + soname_map = self._soname_map_class( + providers=set(), consumers=set()) + arch_map[soname] = soname_map + soname_map.providers.add(obj_key) + for needed_soname in needed: + soname_map = arch_map.get(needed_soname) + if soname_map is None: + soname_map = self._soname_map_class( + providers=set(), consumers=set()) + arch_map[needed_soname] = soname_map + soname_map.consumers.add(obj_key) + +class LinkageMapXCoff(LinkageMap): + + """Models dynamic linker dependencies.""" + + _needed_aux_key = "NEEDED.XCOFF.1" + + class _ObjectKey(LinkageMap._ObjectKey): + + def __init__(self, obj, root): + LinkageMap._ObjectKey.__init__(self, obj, root) + + def _generate_object_key(self, obj, root): + """ + Generate object key for a given object. + + @param object: path to a file + @type object: string (example: '/usr/bin/bar') + @rtype: 2-tuple of types (long, int) if object exists. string if + object does not exist. + @return: + 1. 2-tuple of object's inode and device from a stat call, if object + exists. + 2. realpath of object if object does not exist. + + """ + + os = _os_merge + + try: + _unicode_encode(obj, + encoding=_encodings['merge'], errors='strict') + except UnicodeEncodeError: + # The package appears to have been merged with a + # different value of sys.getfilesystemencoding(), + # so fall back to utf_8 if appropriate. + try: + _unicode_encode(obj, + encoding=_encodings['fs'], errors='strict') + except UnicodeEncodeError: + pass + else: + os = portage.os + + abs_path = os.path.join(root, obj.lstrip(os.sep)) + try: + object_stat = os.stat(abs_path) + except OSError: + # Use the realpath as the key if the file does not exists on the + # filesystem. + return os.path.realpath(abs_path) + # Return a tuple of the device and inode, as well as the basename, + # because of hardlinks the device and inode might be identical. + return (object_stat.st_dev, object_stat.st_ino, os.path.basename(abs_path.rstrip(os.sep))) + + def file_exists(self): + """ + Determine if the file for this key exists on the filesystem. + + @rtype: Boolean + @return: + 1. True if the file exists. + 2. False if the file does not exist or is a broken symlink. + + """ + return isinstance(self._key, tuple) + + class _LibGraphNode(_ObjectKey): + __slots__ = ("alt_paths",) + + def __init__(self, obj, root): + LinkageMapXCoff._ObjectKey.__init__(self, obj, root) + self.alt_paths = set() + + def __str__(self): + return str(sorted(self.alt_paths)) + + def rebuild(self, exclude_pkgs=None, include_file=None): + """ + Raises CommandNotFound if there are preserved libs + and the scanelf binary is not available. + """ + + os = _os_merge + root = self._root + root_len = len(root) - 1 + self._clear_cache() + self._defpath.update(getlibpaths(self._root)) + libs = self._libs + obj_key_cache = self._obj_key_cache + obj_properties = self._obj_properties + + lines = [] + + # Data from include_file is processed first so that it + # overrides any data from previously installed files. + if include_file is not None: + lines += grabfile(include_file) + + aux_keys = [self._needed_aux_key] + for cpv in self._dbapi.cpv_all(): + if exclude_pkgs is not None and cpv in exclude_pkgs: + continue + lines += self._dbapi.aux_get(cpv, aux_keys)[0].split('\n') + # Cache NEEDED.* files avoid doing excessive IO for every rebuild. + self._dbapi.flush_cache() + + # have to call scanelf for preserved libs here as they aren't + # registered in NEEDED.XCOFF.1 files + plibs = set() + if self._dbapi.plib_registry and self._dbapi.plib_registry.getPreservedLibs(): + for items in self._dbapi.plib_registry.getPreservedLibs().values(): + plibs.update(items) + for x in items: + args = [BASH_BINARY, "-c", ':' + + '; member="' + x + '"' + + '; archive=${member}' + + '; if [[ ${member##*/} == .*"["*"]" ]]' + + '; then member=${member%/.*}/${member##*/.}' + + '; archive=${member%[*}' + + '; fi' + + '; member=${member#${archive}}' + + '; [[ -r ${archive} ]] || chmod a+r "${archive}"' + + '; eval $(aixdll-query "${archive}${member}" FILE MEMBER FLAGS FORMAT RUNPATH DEPLIBS)' + + '; [[ -n ${member} ]] && needed=${FILE##*/} || needed=' + + '; for deplib in ${DEPLIBS}' + + '; do eval deplib=${deplib}' + + '; if [[ ${deplib} != "." && ${deplib} != ".." ]]' + + '; then needed="${needed}${needed:+,}${deplib}"' + + '; fi' + + '; done' + + '; [[ -n ${MEMBER} ]] && MEMBER="[${MEMBER}]"' + + '; [[ " ${FLAGS} " == *" SHROBJ "* ]] && soname=${FILE##*/}${MEMBER} || soname=' + + '; echo "${FORMAT##* }${FORMAT%%-*};${FILE#${ROOT%/}}${MEMBER};${soname};${RUNPATH};${needed}"' + + '; [[ -z ${member} && -n ${MEMBER} ]] && echo "${FORMAT##* }${FORMAT%%-*};${FILE#${ROOT%/}};${FILE##*/};;"' + ] + try: + proc = subprocess.Popen(args, stdout=subprocess.PIPE) + except EnvironmentError as e: + if e.errno != errno.ENOENT: + raise + raise CommandNotFound("aixdll-query via " + argv[0]) + else: + for l in proc.stdout: + try: + l = _unicode_decode(l, + encoding=_encodings['content'], errors='strict') + except UnicodeDecodeError: + l = _unicode_decode(l, + encoding=_encodings['content'], errors='replace') + writemsg_level(_("\nError decoding characters " \ + "returned from aixdll-query: %s\n\n") % (l,), + level=logging.ERROR, noiselevel=-1) + l = l.rstrip("\n") + if not l: + continue + fields = l.split(";") + if len(fields) < 5: + writemsg_level(_("\nWrong number of fields " \ + "returned from aixdll-query: %s\n\n") % (l,), + level=logging.ERROR, noiselevel=-1) + continue + fields[1] = fields[1][root_len:] + plibs.discard(fields[1]) + lines.append(";".join(fields)) + proc.wait() + + if plibs: + # Preserved libraries that did not appear in the bash + # aixdll-query code output. This is known to happen with + # statically linked libraries. Generate dummy lines for + # these, so we can assume that every preserved library has + # an entry in self._obj_properties. This is important in + # order to prevent findConsumers from raising an unwanted + # KeyError. + for x in plibs: + lines.append(";".join(['', x, '', '', ''])) + + for l in lines: + l = l.rstrip("\n") + if not l: + continue + fields = l.split(";") + if len(fields) < 5: + writemsg_level(_("\nWrong number of fields " \ + "in %s: %s\n\n") % (self._needed_aux_key, l), + level=logging.ERROR, noiselevel=-1) + continue + arch = fields[0] + + def as_contentmember(obj): + if obj.endswith("]"): + if obj.find("/") >= 0: + return obj[:obj.rfind("/")] + "/." + obj[obj.rfind("/")+1:] + return "." + obj + return obj + + obj = as_contentmember(fields[1]) + soname = as_contentmember(fields[2]) + path = set([normalize_path(x) \ + for x in filter(None, fields[3].replace( + "${ORIGIN}", os.path.dirname(obj)).replace( + "$ORIGIN", os.path.dirname(obj)).split(":"))]) + needed = [as_contentmember(x) for x in fields[4].split(",") if x] + + obj_key = self._obj_key(obj) + indexed = True + myprops = obj_properties.get(obj_key) + if myprops is None: + indexed = False + myprops = (arch, needed, path, soname, set()) + obj_properties[obj_key] = myprops + # All object paths are added into the obj_properties tuple. + myprops[4].add(obj) + + # Don't index the same file more that once since only one + # set of data can be correct and therefore mixing data + # may corrupt the index (include_file overrides previously + # installed). + if indexed: + continue + + arch_map = libs.get(arch) + if arch_map is None: + arch_map = {} + libs[arch] = arch_map + if soname: + soname_map = arch_map.get(soname) + if soname_map is None: + soname_map = self._soname_map_class( + providers=set(), consumers=set()) + arch_map[soname] = soname_map + soname_map.providers.add(obj_key) + for needed_soname in needed: + soname_map = arch_map.get(needed_soname) + if soname_map is None: + soname_map = self._soname_map_class( + providers=set(), consumers=set()) + arch_map[needed_soname] = soname_map + soname_map.consumers.add(obj_key) + + def getSoname(self, obj): + """ + Return the soname associated with an object. + + @param obj: absolute path to an object + @type obj: string (example: '/usr/bin/bar') + @rtype: string + @return: soname as a string + + """ + if not self._libs: + self.rebuild() + if isinstance(obj, self._ObjectKey): + obj_key = obj + if obj_key not in self._obj_properties: + raise KeyError("%s not in object list" % obj_key) + return self._obj_properties[obj_key][3] + if obj not in self._obj_key_cache: + raise KeyError("%s not in object list" % obj) + return self._obj_properties[self._obj_key_cache[obj]][3] + class vardbapi(dbapi): _excluded_dirs = ["CVS", "lost+found"] @@@ -1846,24 -140,19 +1122,29 @@@ self._counter_path = os.path.join(self._eroot, CACHE_PATH, "counter") - try: - self.plib_registry = PreservedLibsRegistry(self._eroot, - os.path.join(self._eroot, PRIVATE_PATH, "preserved_libs_registry")) - except PermissionDenied: - # apparently this user isn't allowed to access PRIVATE_PATH - self.plib_registry = None - - chost = self.settings.get('CHOST') - if not chost: - chost = 'lunix?' # this happens when profiles are not available - if chost.find('darwin') >= 0: - self.linkmap = LinkageMapMachO(self) - elif chost.find('interix') >= 0 or chost.find('winnt') >= 0: - self.linkmap = LinkageMapPeCoff(self) - elif chost.find('aix') >= 0: - self.linkmap = LinkageMapXCoff(self) - else: - self.linkmap = LinkageMap(self) + self._plib_registry = None + if _ENABLE_PRESERVE_LIBS: + try: + self._plib_registry = PreservedLibsRegistry(self._eroot, + os.path.join(self._eroot, PRIVATE_PATH, + "preserved_libs_registry")) + except PermissionDenied: + # apparently this user isn't allowed to access PRIVATE_PATH + pass + + self._linkmap = None + if _ENABLE_DYN_LINK_MAP: - self._linkmap = LinkageMap(self) ++ chost = self.settings.get('CHOST') ++ if not chost: ++ chost = 'lunix?' # this happens when profiles are not available ++ if chost.find('darwin') >= 0: ++ self.linkmap = LinkageMapMachO(self) ++ elif chost.find('interix') >= 0 or chost.find('winnt') >= 0: ++ self.linkmap = LinkageMapPeCoff(self) ++ elif chost.find('aix') >= 0: ++ self.linkmap = LinkageMapXCoff(self) ++ else: ++ self.linkmap = LinkageMap(self) self._owners = self._owners_db(self) def getpath(self, mykey, filename=None): diff --cc pym/portage/package/ebuild/config.py index 83f4c1b78,443a735eb..e5eaf921a --- a/pym/portage/package/ebuild/config.py +++ b/pym/portage/package/ebuild/config.py @@@ -21,13 -22,13 +22,13 @@@ import portag portage.proxy.lazyimport.lazyimport(globals(), 'portage.data:portage_gid', ) - from portage import bsd_chflags, eapi_is_supported, \ + from portage import bsd_chflags, \ load_mod, os, selinux, _encodings, _unicode_encode, _unicode_decode - from portage.const import CACHE_PATH, CUSTOM_PROFILE_PATH, \ - DEPCACHE_PATH, GLOBAL_CONFIG_PATH, INCREMENTALS, MAKE_CONF_FILE, \ + from portage.const import CACHE_PATH, \ + DEPCACHE_PATH, INCREMENTALS, MAKE_CONF_FILE, \ MODULES_FILE_PATH, PORTAGE_BIN_PATH, PORTAGE_PYM_PATH, \ - PRIVATE_PATH, PROFILE_PATH, SUPPORTED_FEATURES, USER_CONFIG_PATH, \ + PRIVATE_PATH, PROFILE_PATH, USER_CONFIG_PATH, \ - USER_VIRTUALS_FILE + USER_VIRTUALS_FILE, EPREFIX, EPREFIX_LSTRIP, BPREFIX from portage.dbapi import dbapi from portage.dbapi.porttree import portdbapi from portage.dbapi.vartree import vartree diff --cc pym/portage/package/ebuild/fetch.py index cc6b4e98f,06cb90801..3d87e4914 --- a/pym/portage/package/ebuild/fetch.py +++ b/pym/portage/package/ebuild/fetch.py @@@ -28,8 -28,7 +28,8 @@@ from portage import OrderedDict, os, se _shell_quote, _unicode_encode from portage.checksum import perform_md5, verify_all from portage.const import BASH_BINARY, CUSTOM_MIRRORS_FILE, \ - EBUILD_SH_BINARY, GLOBAL_CONFIG_PATH + GLOBAL_CONFIG_PATH +from portage.const import rootgid from portage.data import portage_gid, portage_uid, secpass, userpriv_groups from portage.exception import FileNotFound, OperationNotPermitted, \ PermissionDenied, PortageException, TryAgain