From: Fabian Groffen Date: Sat, 14 Oct 2006 07:12:03 +0000 (-0000) Subject: Merged from trunk 3641:4674 X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=dfdc5d23e744ea8d67ddad325512b3146c05ecb2;p=portage.git Merged from trunk 3641:4674 vimdiff is nice, but still it's a lot of work with these changes... svn path=/main/branches/prefix/; revision=4681 --- diff --git a/bin/dispatch-conf b/bin/dispatch-conf index a2370de94..6f9c0209b 100755 --- a/bin/dispatch-conf +++ b/bin/dispatch-conf @@ -14,12 +14,12 @@ from stat import * from random import * -import os, shutil, sys, string, re, commands, atexit +import atexit, commands, os, re, shutil, stat, string, sys sys.path = ["@PORTAGE_BASE@/pym"]+sys.path import portage, dispatch_conf -FIND_EXTANT_CONFIGS = "find %s/ -iname '._cfg????_*' | sed -e 's://:/:g'" +FIND_EXTANT_CONFIGS = "find '%s' %s -iname '._cfg????_%s'" DIFF_CONTENTS = 'diff -Nu %s %s' DIFF_CVS_INTERP = 'diff -Nu %s %s | grep "^[+-][^+-]" | grep -v "# .Header:.*"' DIFF_WSCOMMENTS = 'diff -Nu %s %s | grep "^[+-][^+-]" | grep -v "^[-+]#" | grep -v "^[-+][:space:]*$"' @@ -78,10 +78,18 @@ class dispatch: # for path in config_paths.split (): - if not os.path.exists (path): + path = portage.normalize_path(path) + try: + mymode = os.lstat(path).st_mode + except OSError: continue + basename = "*" + find_opts = "" + if not stat.S_ISDIR(mymode): + path, basename = os.path.split(path) + find_opts = "-maxdepth 1" - confs += self.massage (os.popen (FIND_EXTANT_CONFIGS % (path,)).readlines ()) + confs += self.massage(os.popen(FIND_EXTANT_CONFIGS % (path, find_opts, basename)).readlines()) if self.options['use-rcs'] == 'yes' and ((os.system( "which rcs >/dev/null 2>&1" ) == 256) or (os.system( "which ci >/dev/null 2>&1" ) == 256) diff --git a/bin/dojar b/bin/dojar deleted file mode 100755 index 1a462d6c5..000000000 --- a/bin/dojar +++ /dev/null @@ -1,54 +0,0 @@ -#!@BASH@ -# Copyright 1999-2006 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/dojar,v 1.8 2004/10/04 13:56:50 vapier Exp $ -# Author Karl Trygve Kalleberg - -# -# Typical usage: -# dojar foo.jar bar.jar -# - installs foo.jar and bar.jar into /usr/share/${PN}/lib, and adds them -# both to /usr/share/${PN}/classpath.env -# -# Detailed usage -# dojar -# - installs into /usr/share/${PN}/lib and adds each to -# /usr/share/${PN}/classpath.env. -# -# The classpath.env file is currently merely a convenience for the user as -# it allows him to: -# export CLASSPATH=${CLASSPATH}:`cat /usr/share/foo/classpath.env` -# -# For many packages that set FOO_HOME, placing the jar files into -# lib will allow the user to set FOO_HOME=/usr/share/foo and have the -# scripts work as expected. -# -# Possibly a jarinto will be needed in the future. -# - -if [ -z "$JARDESTTREE" ] ; then - JARDESTTREE="lib" -fi - -jarroot="${DESTTREE}/share/${PN}/" -jardest="${DESTTREE}/share/${PN}/${JARDESTTREE}/" -pf="${D}${jarroot}/package.env" - -dodir "${jardest}" - -for i in $* ; do - bn="$(basename $i)" - - if [ -f "$pf" ] ; then - oldcp=`grep "CLASSPATH=" "$pf" | sed "s/CLASSPATH=//"` - grep -v "CLASSPATH=" "$pf" > "${pf}.new" - echo "CLASSPATH=${oldcp}:${jardest}${bn}" >> "${pf}.new" - mv "${pf}.new" "$pf" - else - echo "DESCRIPTION=\"${DESCRIPTION}\"" > "$pf" - echo "CLASSPATH=${jardest}${bn}" >> "$pf" - fi - - cp "$i" "${D}${jardest}/" - chmod 0444 "${D}${jardest}/${bn}" -done diff --git a/bin/doman b/bin/doman index 410a67fb3..dafcb6201 100755 --- a/bin/doman +++ b/bin/doman @@ -17,7 +17,7 @@ for x in "$@" ; do i18n=${x:6}/ continue fi - if [[ ${x} == ".keep" ]] ; then + if [[ ${x:0:6} == ".keep_" ]] ; then continue fi diff --git a/bin/dosed b/bin/dosed index e606844ae..637939b2c 100755 --- a/bin/dosed +++ b/bin/dosed @@ -7,7 +7,7 @@ mysed="s:${D}::g" for x in "$@" ; do y="${D}${x}" - if [ -a "${y}" ] ; then + if [ -e "${y}" ] ; then if [ -f "${y}" ] ; then mysrc="${T}/${y##*/}" cp "${y}" "${mysrc}" diff --git a/bin/ebuild b/bin/ebuild index f5be32ab4..5d7f2def8 100755 --- a/bin/ebuild +++ b/bin/ebuild @@ -12,9 +12,9 @@ if len(sys.argv) <= 2: sys.exit(1) -(opts, pargs) = getopt.getopt(sys.argv[1:], '', ['debug']) +opts, pargs = getopt.getopt(sys.argv[1:], '', ['debug', 'force']) debug = ("--debug",'') in opts - +force = ("--force",'') in opts if "merge" in pargs: print "Disabling noauto in features... merge disables it. (qmerge doesn't)" @@ -66,7 +66,7 @@ else: overlay = "/".join(ebuild_split[:-2]) os.environ["PORTDIR_OVERLAY"] = os.environ.get("PORTDIR_OVERLAY","") + " " + overlay print "Appending %s to PORTDIR_OVERLAY..." % overlay - + portage.close_portdbapi_caches() reload(portage) portage_ebuild = portage.portdb.findname(cpv) @@ -79,10 +79,33 @@ if len(pargs) > 1 and "config" in pargs: print "config must be called on it's own, not combined with any other phase" sys.exit(1) +def discard_digests(myebuild, mysettings, mydbapi): + """Discard all distfiles digests for the given ebuild. This is useful when + upstream has changed the identity of the distfiles and the user would + otherwise have to manually remove the Manifest and files/digest-* files in + order to ensure correct results.""" + pkgdir = os.path.dirname(myebuild) + fetchlist_dict = portage.FetchlistDict(pkgdir, mysettings, mydbapi) + cat, pkg = pkgdir.split(os.sep)[-2:] + cpv = cat + "/" + os.path.basename(myebuild)[:-7] + from portage_manifest import Manifest + mf = Manifest(pkgdir, mysettings["DISTDIR"], + fetchlist_dict=fetchlist_dict) + mf.create(requiredDistfiles=None, + assumeDistHashesSometimes=True, assumeDistHashesAlways=True) + distfiles = fetchlist_dict[cpv] + for myfile in distfiles: + try: + del mf.fhashdict["DIST"][myfile] + except KeyError: + pass + mf.write() for arg in pargs: try: tmpsettings = portage.config(clone=portage.settings) + if arg == "digest" and force: + discard_digests(ebuild, tmpsettings, portage.portdb) a = portage.doebuild(ebuild, arg, portage.root, tmpsettings, debug=debug, cleanup=("noauto" not in portage.features), tree=mytree) except KeyboardInterrupt: print "Interrupted." diff --git a/bin/ebuild.sh b/bin/ebuild.sh index 59aea5768..408ce7cdc 100755 --- a/bin/ebuild.sh +++ b/bin/ebuild.sh @@ -1,9 +1,14 @@ #!@BASH@ # Copyright 1999-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/ebuild.sh,v 1.201.2.42 2005/08/20 17:24:30 jstubbs Exp $ +# $Id$ -export SANDBOX_PREDICT="${SANDBOX_PREDICT}:/proc/self/maps:/dev/console:@PORTAGE_BASE@/pym:/dev/random" +PORTAGE_BIN_PATH="${PORTAGE_BIN_PATH:-@PORTAGE_BASE@/bin}" +PORTAGE_PYM_PATH="${PORTAGE_PYM_PATH:-@PORTAGE_BASE@/pym}" +declare -rx PORTAGE_BIN_PATH PORTAGE_PYM_PATH + +SANDBOX_PREDICT="${SANDBOX_PREDICT}:/proc/self/maps:/dev/console:/dev/random" +export SANDBOX_PREDICT="${SANDBOX_PREDICT}:${PORTAGE_PYM_PATH}:${PORTAGE_DEPCACHEDIR}" export SANDBOX_WRITE="${SANDBOX_WRITE}:/dev/shm:/dev/stdout:/dev/stderr:${PORTAGE_TMPDIR}" export SANDBOX_READ="${SANDBOX_READ}:/dev/shm:/dev/stdin:${PORTAGE_TMPDIR}" @@ -11,17 +16,17 @@ if [ ! -z "${PORTAGE_GPG_DIR}" ]; then SANDBOX_PREDICT="${SANDBOX_PREDICT}:${PORTAGE_GPG_DIR}" fi +declare -rx EBUILD_PHASE + if [ "$*" != "depend" ] && [ "$*" != "clean" ] && [ "$*" != "nofetch" ]; then if [ -f "${T}/environment" ]; then source "${T}/environment" &>/dev/null fi fi -if [ -n "$#" ]; then - ARGS="${*}" -fi +EBUILD_SH_ARGS="$*" -declare -rx EBUILD_PHASE="$*" +shift $# # Prevent aliases from causing portage to act inappropriately. # Make sure it's before everything so we don't mess aliases that follow. @@ -40,26 +45,11 @@ alias restore_IFS='if [ "${old_IFS:-unset}" != "unset" ]; then IFS="${old_IFS}"; OCC="$CC" OCXX="$CXX" -source ${EPREFIX}/etc/profile.env &>/dev/null -if [ -f "${PORTAGE_BASHRC}" ]; then - # If $- contains x, then tracing has already enabled elsewhere for some - # reason. We preserve it's state so as not to interfere. - if [ "$PORTAGE_DEBUG" != "1" ] || [ "${-/x/}" != "$-" ]; then - source "${PORTAGE_BASHRC}" - else - set -x - source "${PORTAGE_BASHRC}" - set +x - fi -fi -[ ! -z "$OCC" ] && export CC="$OCC" -[ ! -z "$OCXX" ] && export CXX="$OCXX" export PATH="${DEFAULT_PATH}:${ROOTPATH}" [ ! -z "$PREROOTPATH" ] && export PATH="${PREROOTPATH%%:}:$PATH" -source @PORTAGE_BASE@/bin/isolated-functions.sh &>/dev/null - +source "@PORTAGE_BASE@"/bin/isolated-functions.sh &>/dev/null case "${NOCOLOR:-false}" in yes|true) @@ -111,6 +101,20 @@ for dir in ${PROFILE_PATHS}; do done restore_IFS +if [ -f "${PORTAGE_BASHRC}" ]; then + # If $- contains x, then tracing has already enabled elsewhere for some + # reason. We preserve it's state so as not to interfere. + if [ "$PORTAGE_DEBUG" != "1" ] || [ "${-/x/}" != "$-" ]; then + source "${PORTAGE_BASHRC}" + else + set -x + source "${PORTAGE_BASHRC}" + set +x + fi +fi + +[ ! -z "$OCC" ] && export CC="$OCC" +[ ! -z "$OCXX" ] && export CXX="$OCXX" esyslog() { # Custom version of esyslog() to take care of the "Red Star" bug. @@ -118,7 +122,6 @@ esyslog() { return 0 } - use() { useq ${1} } @@ -143,10 +146,10 @@ useq() { # Make sure we have this USE flag in IUSE if ! hasq "${u}" ${IUSE} ${E_IUSE} && ! hasq "${u}" ${PORTAGE_ARCHLIST} selinux; then - echo "QA Notice: USE Flag '${u}' not in IUSE for ${CATEGORY}/${PF}" >&2 + vecho "QA Notice: USE Flag '${u}' not in IUSE for ${CATEGORY}/${PF}" >&2 fi - if [[ " ${USE} " == *" ${u} "* ]] ; then + if hasq ${u} ${USE} ; then return ${found} else return $((!found)) @@ -159,7 +162,7 @@ has_version() { fi # return shell-true/shell-false if exists. # Takes single depend-type atoms. - if @PORTAGE_BASE@/bin/portageq 'has_version' "${ROOT}" "$1"; then + if "@PORTAGE_BASE@"/bin/portageq 'has_version' "${ROOT}" "$1"; then return 0 else return 1 @@ -170,7 +173,7 @@ portageq() { if [ "${EBUILD_PHASE}" == "depend" ]; then die "portageq calls are not allowed in the global scope" fi - @PORTAGE_BASE@/bin/portageq "$@" + "@PORTAGE_BASE@"/bin/portageq "$@" } @@ -185,7 +188,7 @@ best_version() { fi # returns the best/most-current match. # Takes single depend-type atoms. - @PORTAGE_BASE@/bin/portageq 'best_version' "${ROOT}" "$1" + "@PORTAGE_BASE@"/bin/portageq 'best_version' "${ROOT}" "$1" } with_bindir() { @@ -454,10 +457,10 @@ export DESTTREE=/usr export INSDESTTREE="" export EXEDESTTREE="" export DOCDESTTREE="" -export INSOPTIONS="-m 0644" -export EXEOPTIONS="-m 0755" -export LIBOPTIONS="-m 0644" -export DIROPTIONS="-m 0755" +export INSOPTIONS="-m0644" +export EXEOPTIONS="-m0755" +export LIBOPTIONS="-m0644" +export DIROPTIONS="-m0755" export MOPREFIX=${PN} check_KV() { @@ -480,10 +483,10 @@ keepdir() { local x if [ "$1" == "-R" ] || [ "$1" == "-r" ]; then shift - find "$@" -type d -printf "${D}/%p/.keep\n" | tr "\n" "\0" | $XARGS -0 -n100 touch || die "Failed to recursive create .keep files" + find "$@" -type d -printf "${D}/%p/.keep_${CATEGORY}_${PN}-${SLOT}\n" | tr "\n" "\0" | $XARGS -0 -n100 touch || die "Failed to recursive create .keep files" else for x in "$@"; do - touch "${D}/${x}/.keep" || die "Failed to create .keep in ${D}/${x}" + touch "${D}/${x}/.keep_${CATEGORY}_${PN}-${SLOT}" || die "Failed to create .keep in ${D}/${x}" done fi } @@ -503,7 +506,7 @@ unpack() { [ -z "$*" ] && die "Nothing passed to the 'unpack' command" for x in "$@"; do - echo ">>> Unpacking ${x} to ${PWD}" + vecho ">>> Unpacking ${x} to ${PWD}" y=${x%.*} y=${y##*.} @@ -513,6 +516,8 @@ unpack() { else srcdir="${DISTDIR}/" fi + [[ ${x} == ${DISTDIR}* ]] && \ + die "Arguments to unpack() should not begin with \${DISTDIR}." [ ! -s "${srcdir}${x}" ] && die "$myfail" myfail="failure unpacking ${x}" @@ -537,7 +542,7 @@ unpack() { gzip -dc "${srcdir}${x}" > ${x%.*} || die "$myfail" fi ;; - bz2) + bz2|bz) if [ "${y}" == "tar" ]; then bzip2 -dc "${srcdir}${x}" | tar xf - ${tarvars} assert "$myfail" @@ -545,8 +550,16 @@ unpack() { bzip2 -dc "${srcdir}${x}" > ${x%.*} || die "$myfail" fi ;; + 7Z|7z) + local my_output + my_output="$(7z x -y "${srcdir}/${x}")" + if [ $? -ne 0 ]; then + echo "${my_output}" >&2 + die "$myfail" + fi + ;; RAR|rar) - unrar x -idq "${srcdir}/${x}" || die "$myfail" + unrar x -idq -o+ "${srcdir}/${x}" || die "$myfail" ;; LHa|LHA|lha|lzh) lha xqf "${srcdir}/${x}" || die "$myfail" @@ -555,17 +568,19 @@ unpack() { ar x "${srcdir}/${x}" || die "$myfail" ;; *) - echo "unpack ${x}: file format not recognized. Ignoring." + vecho "unpack ${x}: file format not recognized. Ignoring." ;; esac done chmod -Rf a+rX,u+w,g-w,o-w . } -strip_duplicate_slashes () { - if [ -n "${1}" ]; then - local removed="${1/\/\///}" - [ "${removed}" != "${removed/\/\///}" ] && removed=$(strip_duplicate_slashes "${removed}") +strip_duplicate_slashes() { + if [[ -n $1 ]] ; then + local removed=$1 + while [[ ${removed} == *//* ]] ; do + removed=${removed//\/\///} + done echo ${removed} fi } @@ -578,10 +593,10 @@ econf() { ECONF_SOURCE="." fi if [ -x "${ECONF_SOURCE}/configure" ]; then - if [ -e ${EPREFIX}/usr/share/gnuconfig/ ]; then + if [ -e "${EPREFIX}"/usr/share/gnuconfig/ ]; then for x in $(find "${WORKDIR}" -type f '(' -name config.guess -o -name config.sub ')') ; do - echo " * econf: updating ${x/${WORKDIR}\/} with ${EPREFIX}/usr/share/gnuconfig/${x##*/}" - cp -f ${EPREFIX}/usr/share/gnuconfig/${x##*/} ${x} + vecho " * econf: updating ${x/${WORKDIR}\/} with ${EPREFIX}/usr/share/gnuconfig/${x##*/}" + cp -f "${EPREFIX}"/usr/share/gnuconfig/${x##*/} ${x} done fi @@ -617,11 +632,7 @@ econf() { export CONF_PREFIX [ "${CONF_LIBDIR:0:1}" != "/" ] && CONF_LIBDIR="/${CONF_LIBDIR}" - CONF_LIBDIR_RESULT="${CONF_PREFIX}${CONF_LIBDIR}" - for x in 1 2 3; do - # The escaping is weird. It will break if you escape the last one. - CONF_LIBDIR_RESULT="${CONF_LIBDIR_RESULT//\/\///}" - done + CONF_LIBDIR_RESULT="$(strip_duplicate_slashes ${CONF_PREFIX}${CONF_LIBDIR})" LOCAL_EXTRA_ECONF="--libdir=${CONF_LIBDIR_RESULT} ${LOCAL_EXTRA_ECONF}" fi @@ -651,7 +662,7 @@ econf() { CONFCACHE= fi - echo ${CONFCACHE} ${CONFCACHE_ARG} ${TMP_CONFCACHE_DIR} "${ECONF_SOURCE}/configure" \ + vecho ${CONFCACHE} ${CONFCACHE_ARG} ${TMP_CONFCACHE_DIR} "${ECONF_SOURCE}/configure" \ --prefix=${EPREFIX}/usr \ --host=${CHOST} \ --mandir=${EPREFIX}/usr/share/man \ @@ -700,22 +711,22 @@ einstall() { fi if [ -f ./[mM]akefile -o -f ./GNUmakefile ] ; then - if [ ! -z "${PORTAGE_DEBUG}" ]; then - make -n prefix=${D}/usr \ - datadir=${D}/usr/share \ - infodir=${D}/usr/share/info \ - localstatedir=${D}/var/lib \ - mandir=${D}/usr/share/man \ - sysconfdir=${D}/etc \ + if [ "${PORTAGE_DEBUG}" == "1" ]; then + make -n prefix="${D}/usr" \ + datadir="${D}/usr/share" \ + infodir="${D}/usr/share/info" \ + localstatedir="${D}/var/lib" \ + mandir="${D}/usr/share/man" \ + sysconfdir="${D}/etc" \ ${EXTRA_EINSTALL} \ "$@" install fi - make prefix=${D}/usr \ - datadir=${D}/usr/share \ - infodir=${D}/usr/share/info \ - localstatedir=${D}/var/lib \ - mandir=${D}/usr/share/man \ - sysconfdir=${D}/etc \ + make prefix="${D}/usr" \ + datadir="${D}/usr/share" \ + infodir="${D}/usr/share/info" \ + localstatedir="${D}/var/lib" \ + mandir="${D}/usr/share/man" \ + sysconfdir="${D}/etc" \ ${EXTRA_EINSTALL} \ "$@" install || die "einstall failed" else @@ -732,7 +743,7 @@ pkg_nofetch() { echo "!!! The following are listed in SRC_URI for ${PN}:" local x - for x in `echo ${SRC_URI}`; do + for x in $(echo ${SRC_URI}); do echo "!!! ${x}" done } @@ -751,23 +762,21 @@ src_compile() { } src_test() { - addpredict / if emake -j1 check -n &> /dev/null; then - echo ">>> Test phase [check]: ${CATEGORY}/${PF}" + vecho ">>> Test phase [check]: ${CATEGORY}/${PF}" if ! emake -j1 check; then hasq test $FEATURES && die "Make check failed. See above for details." hasq test $FEATURES || eerror "Make check failed. See above for details." fi elif emake -j1 test -n &> /dev/null; then - echo ">>> Test phase [test]: ${CATEGORY}/${PF}" + vecho ">>> Test phase [test]: ${CATEGORY}/${PF}" if ! emake -j1 test; then hasq test $FEATURES && die "Make test failed. See above for details." hasq test $FEATURES || eerror "Make test failed. See above for details." fi else - echo ">>> Test phase [none]: ${CATEGORY}/${PF}" + vecho ">>> Test phase [none]: ${CATEGORY}/${PF}" fi - SANDBOX_PREDICT="${SANDBOX_PREDICT%:/}" } src_install() { @@ -817,40 +826,40 @@ dyn_unpack() { local x local checkme for x in ${AA}; do - echo ">>> Checking ${x}'s mtime..." + vecho ">>> Checking ${x}'s mtime..." if [ "${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}}/${x}" -nt "${WORKDIR}" ]; then - echo ">>> ${x} has been updated; recreating WORKDIR..." + vecho ">>> ${x} has been updated; recreating WORKDIR..." newstuff="yes" rm -rf "${WORKDIR}" break fi done if [ "${EBUILD}" -nt "${WORKDIR}" ]; then - echo ">>> ${EBUILD} has been updated; recreating WORKDIR..." + vecho ">>> ${EBUILD} has been updated; recreating WORKDIR..." newstuff="yes" rm -rf "${WORKDIR}" elif [ ! -f "${PORTAGE_BUILDDIR}/.unpacked" ]; then - echo ">>> Not marked as unpacked; recreating WORKDIR..." + vecho ">>> Not marked as unpacked; recreating WORKDIR..." newstuff="yes" rm -rf "${WORKDIR}" fi fi if [ -e "${WORKDIR}" ]; then if [ "$newstuff" == "no" ]; then - echo ">>> WORKDIR is up-to-date, keeping..." + vecho ">>> WORKDIR is up-to-date, keeping..." [ "$(type -t post_src_unpack)" == "function" ] && post_src_unpack return 0 fi fi if [ ! -d "${WORKDIR}" ]; then - install -m ${PORTAGE_WORKDIR_MODE:-0700} -d "${WORKDIR}" || die "Failed to create dir '${WORKDIR}'" + install -m${PORTAGE_WORKDIR_MODE:-0700} -d "${WORKDIR}" || die "Failed to create dir '${WORKDIR}'" fi cd "${WORKDIR}" || die "Directory change failed: \`cd '${WORKDIR}'\`" - echo ">>> Unpacking source..." + vecho ">>> Unpacking source..." src_unpack touch "${PORTAGE_BUILDDIR}/.unpacked" || die "IO Failure -- Failed 'touch .unpacked' in ${PORTAGE_BUILDDIR}" - echo ">>> Source unpacked." + vecho ">>> Source unpacked." cd "${PORTAGE_BUILDDIR}" [ "$(type -t post_src_unpack)" == "function" ] && post_src_unpack @@ -864,24 +873,21 @@ dyn_clean() { return 1 fi - if [ "$USERLAND" == "BSD" ] && type -p chflags &>/dev/null; then - chflags -R noschg,nouchg,nosappnd,nouappnd,nosunlnk,nouunlnk \ - "${PORTAGE_BUILDDIR}" - fi - - if [ "$USERLAND" == "Darwin" ] && type -p chflags &>/dev/null; then + if type -p chflags &>/dev/null; then chflags -R noschg,nouchg,nosappnd,nouappnd "${PORTAGE_BUILDDIR}" + chflags -R nosunlnk,nouunlnk "${PORTAGE_BUILDDIR}" 2>/dev/null fi - rm -rf "${PORTAGE_BUILDDIR}/image" + rm -rf "${PORTAGE_BUILDDIR}/image" "${PORTAGE_BUILDDIR}/homedir" if ! hasq keeptemp $FEATURES; then rm -rf "${T}" else - mv "${T}/environment" "${T}/environment.keeptemp" + [ -e "${T}/environment" ] && mv "${T}/environment" "${T}/environment.keeptemp" fi if ! hasq keepwork $FEATURES; then + rm -rf "${PORTAGE_BUILDDIR}/.logid" rm -rf "${PORTAGE_BUILDDIR}/.unpacked" rm -rf "${PORTAGE_BUILDDIR}/.compiled" rm -rf "${PORTAGE_BUILDDIR}/.tested" @@ -944,8 +950,8 @@ docinto() { export DOCDESTTREE="" else export DOCDESTTREE="$1" - if [ ! -d "${D}/usr/share/doc/${PF}/${DOCDESTTREE}" ]; then - install -d "${D}/usr/share/doc/${PF}/${DOCDESTTREE}" + if [ ! -d "${D}usr/share/doc/${PF}/${DOCDESTTREE}" ]; then + install -d "${D}usr/share/doc/${PF}/${DOCDESTTREE}" fi fi } @@ -954,7 +960,7 @@ insopts() { export INSOPTIONS="$@" # `install` should never be called with '-s' ... - [[ " ${INSOPTIONS} " == *" -s "* ]] && die "Never call insopts() with -s" + hasq -s ${INSOPTIONS} && die "Never call insopts() with -s" } diropts() { @@ -965,14 +971,14 @@ exeopts() { export EXEOPTIONS="$@" # `install` should never be called with '-s' ... - [[ " ${EXEOPTIONS} " == *" -s "* ]] && die "Never call exeopts() with -s" + hasq -s ${EXEOPTIONS} && die "Never call exeopts() with -s" } libopts() { export LIBOPTIONS="$@" # `install` should never be called with '-s' ... - [[ " ${LIBOPTIONS} " == *" -s "* ]] && die "Never call libopts() with -s" + hasq -s ${LIBOPTIONS} && die "Never call libopts() with -s" } abort_handler() { @@ -1033,6 +1039,12 @@ dyn_compile() { [ "${DISTCC_DIR-unset}" == "unset" ] && export DISTCC_DIR="${PORTAGE_TMPDIR}/.distcc" [ ! -z "${DISTCC_DIR}" ] && addwrite "${DISTCC_DIR}" + LIBDIR_VAR="LIBDIR_${ABI}" + if [ -z "${PKG_CONFIG_PATH}" -a -n "${ABI}" -a -n "${!LIBDIR_VAR}" ]; then + export PKG_CONFIG_PATH="${EPREFIX}/usr/${!LIBDIR_VAR}/pkgconfig" + fi + unset LIBDIR_VAR + if hasq noauto $FEATURES &>/dev/null && [ ! -f ${PORTAGE_BUILDDIR}/.unpacked ]; then echo echo "!!! We apparently haven't unpacked... This is probably not what you" @@ -1056,9 +1068,9 @@ dyn_compile() { fi cp "${EBUILD}" "build-info/${PF}.ebuild" - if [ "${PORTAGE_BUILDDIR}/.compiled" -nt "${WORKDIR}" ]; then - echo ">>> It appears that ${PN} is already compiled; skipping." - echo ">>> (clean to force compilation)" + if [[ ${PORTAGE_BUILDDIR}/.compiled -nt ${WORKDIR} ]] ; then + vecho ">>> It appears that '${PF}' is already compiled; skipping." + vecho ">>> Remove '${PORTAGE_BUILDDIR}/.compiled' to force install." trap SIGINT SIGQUIT [ "$(type -t post_src_compile)" == "function" ] && post_src_compile return @@ -1073,14 +1085,15 @@ dyn_compile() { #some packages use an alternative to $S to build in, cause #our libtool to create problematic .la files export PWORKDIR="$WORKDIR" - echo ">>> Compiling source in ${srcdir} ..." + vecho ">>> Compiling source in ${srcdir} ..." src_compile - echo ">>> Source compiled." + vecho ">>> Source compiled." #|| abort_compile "fail" cd "${PORTAGE_BUILDDIR}" touch .compiled cd build-info + set -f for f in ASFLAGS CATEGORY CBUILD CC CFLAGS CHOST CTARGET CXX \ CXXFLAGS DEPEND EXTRA_ECONF EXTRA_EINSTALL EXTRA_MAKE \ FEATURES INHERITED IUSE LDFLAGS LIBCFLAGS LIBCXXFLAGS \ @@ -1090,14 +1103,13 @@ dyn_compile() { done echo "${USE}" > USE echo "${EAPI:-0}" > EAPI + set +f set > environment export -p | sed 's:declare -rx:declare -x:' >> environment bzip2 -9 environment cp "${EBUILD}" "${PF}.ebuild" - if [[ " ${FEATURES} " == *" nostrip "* ]] || \ - [[ " ${RESTRICT} " == *" nostrip "* ]] || \ - [[ " ${RESTRICT} " == *" strip "* ]] + if hasq nostrip ${FEATURES} ${RESTRICT} || hasq strip ${RESTRICT} then touch DEBUGBUILD fi @@ -1110,7 +1122,7 @@ dyn_compile() { dyn_test() { [ "$(type -t pre_src_test)" == "function" ] && pre_src_test if [ "${PORTAGE_BUILDDIR}/.tested" -nt "${WORKDIR}" ]; then - echo ">>> It appears that ${PN} has already been tested; skipping." + vecho ">>> It appears that ${PN} has already been tested; skipping." [ "$(type -t post_src_test)" == "function" ] && post_src_test return fi @@ -1118,13 +1130,15 @@ dyn_test() { if [ -d "${S}" ]; then cd "${S}" fi - if hasq test $RESTRICT; then + if ! hasq test $FEATURES; then + vecho ">>> Test phase [not enabled]: ${CATEGORY}/${PF}" + elif hasq test $RESTRICT; then ewarn "Skipping make test/check due to ebuild restriction." - echo ">>> Test phase [explicitly disabled]: ${CATEGORY}/${PF}" - elif ! hasq test $FEATURES; then - echo ">>> Test phase [not enabled]: ${CATEGORY}/${PF}" + vecho ">>> Test phase [explicitly disabled]: ${CATEGORY}/${PF}" else + addpredict / src_test + SANDBOX_PREDICT="${SANDBOX_PREDICT%:/}" fi cd "${PORTAGE_BUILDDIR}" @@ -1135,6 +1149,13 @@ dyn_test() { dyn_install() { [ -z "$PORTAGE_BUILDDIR" ] && die "${FUNCNAME}: PORTAGE_BUILDDIR is unset" + if hasq noauto $FEATURES ; then + rm -f "${PORTAGE_BUILDDIR}/.installed" + elif [[ ${PORTAGE_BUILDDIR}/.installed -nt ${WORKDIR} ]] ; then + vecho ">>> It appears that '${PF}' is already installed; skipping." + vecho ">>> Remove '${PORTAGE_BUILDDIR}/.installed' to force install." + return 0 + fi trap "abort_install" SIGINT SIGQUIT [ "$(type -t pre_src_install)" == "function" ] && pre_src_install rm -rf "${PORTAGE_BUILDDIR}/image" @@ -1142,8 +1163,8 @@ dyn_install() { if [ -d "${S}" ]; then cd "${S}" fi - echo - echo ">>> Install ${PF} into ${D} category ${CATEGORY}" + vecho + vecho ">>> Install ${PF} into ${D} category ${CATEGORY}" #our custom version of libtool uses $S and $D to fix #invalid paths in .la files export S D @@ -1152,8 +1173,8 @@ dyn_install() { export PWORKDIR="$WORKDIR" src_install touch "${PORTAGE_BUILDDIR}/.installed" - echo ">>> Completed installing ${PF} into ${D}" - echo + vecho ">>> Completed installing ${PF} into ${D}" + vecho cd ${PORTAGE_BUILDDIR} [ "$(type -t post_src_install)" == "function" ] && post_src_install trap SIGINT SIGQUIT @@ -1217,9 +1238,7 @@ dyn_help() { echo " c++ flags : ${CXXFLAGS}" echo " make flags : ${MAKEOPTS}" echo -n " build mode : " - if [[ " ${FEATURES} " == *" nostrip "* ]] || \ - [[ " ${RESTRICT} " == *" nostrip "* ]] || \ - [[ " ${RESTRICT} " == *" strip "* ]] + if hasq nostrip ${FEATURES} ${RESTRICT} || hasq strip ${RESTRICT} ; then echo "debug (large)" else @@ -1303,9 +1322,9 @@ inherit() { if [ "$EBUILD_PHASE" != "depend" ]; then if ! hasq $ECLASS $INHERITED; then - echo - echo "QA Notice: ECLASS '$ECLASS' inherited illegally in $CATEGORY/$PF" >&2 - echo + vecho + vecho "QA Notice: ECLASS '$ECLASS' inherited illegally in $CATEGORY/$PF" >&2 + vecho fi fi @@ -1321,6 +1340,7 @@ inherit() { done fi debug-print "inherit: $1 -> $location" + [ ! -e "$location" ] && die "${1}.eclass could not be found by inherit()" #We need to back up the value of DEPEND and RDEPEND to B_DEPEND and B_RDEPEND #(if set).. and then restore them after the inherit call. @@ -1338,8 +1358,7 @@ inherit() { #turn on glob expansion set +f - source "$location" || export ERRORMSG="died sourcing $location in inherit()" - [ -z "${ERRORMSG}" ] || die "${ERRORMSG}" + source "$location" || die "died sourcing $location in inherit()" #turn off glob expansion set -f @@ -1480,7 +1499,7 @@ remove_path_entry() { # === === === === === functions end, main part begins === === === === === # === === === === === === === === === === === === === === === === === === -if [ "$*" != "depend" ] && [ "$*" != "clean" ] && [ "$*" != "setup" ]; then +if [[ ${EBUILD_SH_ARGS} != "depend" ]] && [[ ${EBUILD_SH_ARGS} != "clean" ]] && [[ ${EBUILD_SH_ARGS} != "setup" ]]; then cd ${PORTAGE_TMPDIR} &> /dev/null cd ${BUILD_PREFIX} &> /dev/null @@ -1489,7 +1508,7 @@ if [ "$*" != "depend" ] && [ "$*" != "clean" ] && [ "$*" != "setup" ]; then fi if hasq distcc ${FEATURES} &>/dev/null; then - if [ -d ${EPREFIX}/usr/lib/distcc/bin ]; then + if [ -d "${EPREFIX}"/usr/lib/distcc/bin ]; then #We can enable distributed compile support if [ -z "${PATH/*distcc*/}" ]; then # Remove the other reference. @@ -1498,8 +1517,12 @@ if [ "$*" != "depend" ] && [ "$*" != "clean" ] && [ "$*" != "setup" ]; then export PATH="${EPREFIX}/usr/lib/distcc/bin:${PATH}" [ ! -z "${DISTCC_LOG}" ] && addwrite "$(dirname ${DISTCC_LOG})" elif which distcc &>/dev/null; then - export CC="distcc $CC" - export CXX="distcc $CXX" + if ! hasq distcc $CC; then + export CC="distcc $CC" + fi + if ! hasq distcc $CXX; then + export CXX="distcc $CXX" + fi fi fi @@ -1510,9 +1533,9 @@ if [ "$*" != "depend" ] && [ "$*" != "clean" ] && [ "$*" != "setup" ]; then remove_path_entry "ccache" fi - if [ -d ${EPREFIX}/usr/lib/ccache/bin ]; then + if [ -d "${EPREFIX}"/usr/lib/ccache/bin ]; then export PATH="${EPREFIX}/usr/lib/ccache/bin:${PATH}" - elif [ -d ${EPREFIX}/usr/bin/ccache ]; then + elif [ -d "${EPREFIX}"/usr/bin/ccache ]; then export PATH="${EPREFIX}/usr/bin/ccache:${PATH}" fi @@ -1548,34 +1571,34 @@ for x in T P PN PV PVR PR CATEGORY A EBUILD EMERGE_FROM O PPID FILESDIR PORTAGE_ [[ ${!x-UNSET_VAR} != UNSET_VAR ]] && declare -r ${x} done # Need to be able to change D in dyn_preinst due to the IMAGE stuff -[[ $* != "preinst" ]] && declare -r D +[[ ${EBUILD_SH_ARGS} != "preinst" ]] && declare -r D unset x # Turn of extended glob matching so that g++ doesn't get incorrectly matched. shopt -u extglob -QA_INTERCEPTORS="javac java-config python python-config perl grep fgrep sed gcc g++ cc bash awk nawk gawk pkg-config" +QA_INTERCEPTORS="javac java-config python python-config perl grep egrep fgrep sed gcc g++ cc bash awk nawk gawk pkg-config" # level the QA interceptors if we're in depend -if hasq "depend" "$@"; then +if hasq "depend" "${EBUILD_SH_ARGS}"; then for BIN in ${QA_INTERCEPTORS}; do - BIN_PATH=`type -pf ${BIN}` + BIN_PATH=$(type -pf ${BIN}) if [ "$?" != "0" ]; then BODY="echo \"*** missing command: ${BIN}\" >&2; return 127" else BODY="${BIN_PATH} \"\$@\"; return \$?" fi FUNC_SRC="${BIN}() { - echo -n \"QA Notice: ${BIN} in global scope: \" >&2 + vecho -n \"QA Notice: ${BIN} in global scope: \" >&2 if [ \$ECLASS_DEPTH -gt 0 ]; then - echo \"eclass \${ECLASS}\" >&2 + vecho \"eclass \${ECLASS}\" >&2 else - echo \"\${CATEGORY}/\${PF}\" >&2 + vecho \"\${CATEGORY}/\${PF}\" >&2 fi ${BODY} }"; eval "$FUNC_SRC" || echo "error creating QA interceptor ${BIN}" >&2 done - unset src bin_path body + unset BIN_PATH BIN BODY FUNC_SRC fi # reset the EBUILD_DEATH_HOOKS so they don't multiple due to stable's re-sourcing of env. @@ -1587,7 +1610,6 @@ if ! hasq depend $EBUILD_PHASE; then RESTRICT="${PORTAGE_RESTRICT}" unset PORTAGE_RESTRICT fi -[ -z "${ERRORMSG}" ] || die "${ERRORMSG}" # Expand KEYWORDS # We need to turn off pathname expansion for -* in KEYWORDS and @@ -1596,9 +1618,7 @@ set -f KEYWORDS=$(eval echo ${KEYWORDS//~/\\~}) set +f -if [[ " ${FEATURES} " == *" nostrip "* ]] || \ - [[ " ${RESTRICT} " == *" nostrip "* ]] || \ - [[ " ${RESTRICT} " == *" strip "* ]] +if hasq nostrip ${FEATURES} ${RESTRICT} || hasq strip ${RESTRICT} then export DEBUGBUILD=1 fi @@ -1609,7 +1629,7 @@ if [ "$S" = "" ]; then fi #wipe the interceptors. we don't want saved. -if hasq "depend" "$@"; then +if hasq "depend" "${EBUILD_SH_ARGS}"; then unset -f $QA_INTERCEPTORS unset QA_INTERCEPTORS fi @@ -1648,7 +1668,7 @@ fi set +f -for myarg in $*; do +for myarg in ${EBUILD_SH_ARGS} ; do case $myarg in nofetch) pkg_nofetch @@ -1687,7 +1707,7 @@ for myarg in $*; do fi export SANDBOX_ON="0" ;; - help|clean|setup|preinst) + help|setup|preinst) #pkg_setup needs to be out of the sandbox for tmp file creation; #for example, awking and piping a file in /tmp requires a temp file to be created #in /etc. If pkg_setup is in the sandbox, both our lilo and apache ebuilds break. @@ -1708,35 +1728,35 @@ for myarg in $*; do #dbkey=${PORTAGE_CACHEDIR}/${CATEGORY}/${PF} if [ ! -d "${dbkey%/*}" ]; then - install -d -g ${PORTAGE_GID} -m 2775 "${dbkey%/*}" + install -d -g ${PORTAGE_GID} -m2775 "${dbkey%/*}" fi # Make it group writable. 666&~002==664 umask 002 - #the extra `echo` commands remove newlines - echo `echo "$DEPEND"` > $dbkey - echo `echo "$RDEPEND"` >> $dbkey - echo `echo "$SLOT"` >> $dbkey - echo `echo "$SRC_URI"` >> $dbkey - echo `echo "$RESTRICT"` >> $dbkey - echo `echo "$HOMEPAGE"` >> $dbkey - echo `echo "$LICENSE"` >> $dbkey - echo `echo "$DESCRIPTION"` >> $dbkey - echo `echo "$KEYWORDS"` >> $dbkey - echo `echo "$INHERITED"` >> $dbkey - echo `echo "$IUSE"` >> $dbkey + #the extra $(echo) commands remove newlines + echo $(echo "$DEPEND") > $dbkey + echo $(echo "$RDEPEND") >> $dbkey + echo $(echo "$SLOT") >> $dbkey + echo $(echo "$SRC_URI") >> $dbkey + echo $(echo "$RESTRICT") >> $dbkey + echo $(echo "$HOMEPAGE") >> $dbkey + echo $(echo "$LICENSE") >> $dbkey + echo $(echo "$DESCRIPTION") >> $dbkey + echo $(echo "$KEYWORDS") >> $dbkey + echo $(echo "$INHERITED") >> $dbkey + echo $(echo "$IUSE") >> $dbkey echo >> $dbkey - echo `echo "$PDEPEND"` >> $dbkey - echo `echo "$PROVIDE"` >> $dbkey - echo `echo "${EAPI:-0}"` >> $dbkey - echo `echo "$UNUSED_01"` >> $dbkey - echo `echo "$UNUSED_02"` >> $dbkey - echo `echo "$UNUSED_03"` >> $dbkey - echo `echo "$UNUSED_04"` >> $dbkey - echo `echo "$UNUSED_05"` >> $dbkey - echo `echo "$UNUSED_06"` >> $dbkey - echo `echo "$UNUSED_07"` >> $dbkey + echo $(echo "$PDEPEND") >> $dbkey + echo $(echo "$PROVIDE") >> $dbkey + echo $(echo "${EAPI:-0}") >> $dbkey + echo $(echo "$UNUSED_01") >> $dbkey + echo $(echo "$UNUSED_02") >> $dbkey + echo $(echo "$UNUSED_03") >> $dbkey + echo $(echo "$UNUSED_04") >> $dbkey + echo $(echo "$UNUSED_05") >> $dbkey + echo $(echo "$UNUSED_06") >> $dbkey + echo $(echo "$UNUSED_07") >> $dbkey set +f #make sure it is writable by our group: exit 0 diff --git a/bin/emaint b/bin/emaint index 05cc45a0a..f29013ae2 100755 --- a/bin/emaint +++ b/bin/emaint @@ -1,11 +1,12 @@ #!@PYTHON@ -O + import sys sys.path.insert(0, "@PORTAGE_BASE@/pym") - -import os, portage, portage_const, re -from copy import copy from optparse import OptionParser, OptionValueError +import re + +import os, portage, portage_const, portage_exception class WorldHandler(object): def name(): @@ -24,8 +25,6 @@ class WorldHandler(object): self.invalid.append(atom) elif not portage.db["/"]["vartree"].dbapi.match(atom): self.not_installed.append(atom) - elif not portage.db["/"]["porttree"].dbapi.match("match-visible", atom): - self.masked.append(atom) else: self.okay.append(atom) @@ -34,8 +33,6 @@ class WorldHandler(object): if self.found: errors += map(lambda x: "'%s' is not a valid atom" % x, self.invalid) errors += map(lambda x: "'%s' is not installed" % x, self.not_installed) - if self.masked: - errors += "Masked ebuilds can be manually unmasked with package.keywords or package.unmask" else: errors.append(self.world_file + " could not be opened for reading") return errors @@ -44,7 +41,7 @@ class WorldHandler(object): errors = [] try: portage.write_atomic(self.world_file, "\n".join(self.okay)) - except OSError: + except portage_exception.PortageException: errors.append(self.world_file + " could not be opened for writing") return errors @@ -106,16 +103,16 @@ class VdbKeyHandler(object): return errors -# this sucks, should track this in a different manner. -#modules = {"world" : WorldHandler, -# "vdbkeys": VdbKeyHandler} -modules = {"world" : WorldHandler} +def emaint_main(myargv): -module_names = modules.keys() -module_names.sort() -module_names.insert(0, "all") + # TODO: Create a system that allows external modules to be added without + # the need for hard coding. + modules = {"world" : WorldHandler} + + module_names = modules.keys() + module_names.sort() + module_names.insert(0, "all") -if __name__ == "__main__": def exclusive(option, *args, **kw): var = kw.get("var", None) if var is None: @@ -140,7 +137,7 @@ if __name__ == "__main__": parser.action = None - (options, args) = parser.parse_args() + (options, args) = parser.parse_args(args=myargv) if len(args) != 1: parser.error("Incorrect number of arguments") if args[0] not in module_names: @@ -176,3 +173,6 @@ if __name__ == "__main__": print "\n" print "Finished" + +if __name__ == "__main__": + emaint_main(sys.argv[1:]) diff --git a/bin/emerge b/bin/emerge index c73fd8d6c..3d2dd2c75 100755 --- a/bin/emerge +++ b/bin/emerge @@ -1,83 +1,104 @@ #!@PYTHON@ -O # Copyright 1999-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/emerge,v 1.345.2.38 2005/08/13 17:25:26 ferringb Exp $ - -import os,sys -os.environ["PORTAGE_CALLER"]="emerge" -sys.path = ["@PORTAGE_BASE@/pym"]+sys.path +# $Id$ +import sys +# This block ensures that ^C interrupts are handled quietly. try: - import portage + import signal + + def exithandler(signum,frame): + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + sys.exit(1) + + signal.signal(signal.SIGINT, exithandler) + signal.signal(signal.SIGTERM, exithandler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + except KeyboardInterrupt: - sys.exit(1) # If they control C during imports, just die silently + sys.exit(1) -import emergehelp,xpak,string,re,commands,time,shutil,traceback,signal,socket,types -from stat import * -from output import * +import os, stat +sys.path = ["@PORTAGE_BASE@/pym"]+sys.path + +os.environ["PORTAGE_LEGACY_GLOBALS"] = "false" +import portage +del os.environ["PORTAGE_LEGACY_GLOBALS"] +from portage import digraph + +import emergehelp, xpak, commands, errno, re, socket, string, time, types +from output import blue, bold, colorize, darkblue, darkgreen, darkred, green, \ + havecolor, nc_len, nocolor, red, teal, turquoise, white, xtermTitle, \ + xtermTitleReset, yellow +from output import create_color_func +good = create_color_func("GOOD") +bad = create_color_func("BAD") import portage_util import portage_locks import portage_exception import portage_const +from portage_data import secpass + +if not hasattr(__builtins__, "set"): + from sets import Set as set + +class stdout_spinner(object): + scroll_msgs = [ + "Gentoo Rocks ("+os.uname()[0]+")", + "Thank you for using Gentoo. :)", + "Are you actually trying to read this?", + "How many times have you stared at this?", + "We are generating the cache right now", + "You are paying too much attention.", + "A theory is better than its explanation.", + "Phasers locked on target, Captain.", + "Thrashing is just virtual crashing.", + "To be is to program.", + "Real Users hate Real Programmers.", + "When all else fails, read the instructions.", + "Functionality breeds Contempt.", + "The future lies ahead.", + "3.1415926535897932384626433832795028841971694", + "Sometimes insanity is the only alternative.", + "Inaccuracy saves a world of explanation.", + ] + + twirl_sequence = "/-\\|/-\\|/-\\|/-\\|\\-/|\\-/|\\-/|\\-/|" -spinner_msgs = ["Gentoo Rocks ("+os.uname()[0]+")", - "Thank you for using Gentoo. :)", - "Are you actually trying to read this?", - "How many times have you stared at this?", - "We are generating the cache right now", - "You are paying too much attention.", - "A theory is better than its explanation.", - "Phasers locked on target, Captain.", - "Thrashing is just virtual crashing.", - "To be is to program.", - "Real Users hate Real Programmers.", - "When all else fails, read the instructions.", - "Functionality breeds Contempt.", - "The future lies ahead.", - "3.1415926535897932384626433832795028841971694", - "Sometimes insanity is the only alternative.", - "Inaccuracy saves a world of explanation.", - ] - - -def update_basic_spinner(): - global spinner, spinpos - spinpos = (spinpos+1) % 500 - if (spinpos % 100) == 0: - if spinpos == 0: - sys.stdout.write(". ") - else: - sys.stdout.write(".") - sys.stdout.flush() - -def update_scroll_spinner(): - global spinner, spinpos - if(spinpos >= len(spinner)): - sys.stdout.write(darkgreen(" \b\b\b"+spinner[len(spinner)-1-(spinpos%len(spinner))])) - else: - sys.stdout.write(green("\b "+spinner[spinpos])) - sys.stdout.flush() - spinpos = (spinpos+1) % (2*len(spinner)) - -def update_twirl_spinner(): - global spinner, spinpos - spinpos = (spinpos+1) % len(spinner) - sys.stdout.write("\b\b "+spinner[spinpos]) - sys.stdout.flush() + def __init__(self): + self.spinpos = 0 + self.update = self.update_twirl + self.scroll_sequence = self.scroll_msgs[ + int(time.time() * 100) % len(self.scroll_msgs)] + + def update_basic(self): + self.spinpos = (self.spinpos + 1) % 500 + if (self.spinpos % 100) == 0: + if self.spinpos == 0: + sys.stdout.write(". ") + else: + sys.stdout.write(".") + sys.stdout.flush() -def update_quiet_spinner(): - return + def update_scroll(self): + if(self.spinpos >= len(self.scroll_sequence)): + sys.stdout.write(darkgreen(" \b\b\b" + self.scroll_sequence[ + len(self.scroll_sequence) - 1 - (self.spinpos % len(self.scroll_sequence))])) + else: + sys.stdout.write(green("\b " + self.scroll_sequence[self.spinpos])) + sys.stdout.flush() + self.spinpos = (self.spinpos + 1) % (2 * len(self.scroll_sequence)) -spinpos = 0 -spinner = "/-\\|/-\\|/-\\|/-\\|\\-/|\\-/|\\-/|\\-/|" -update_spinner = update_twirl_spinner -if "candy" in portage.settings.features: - spinner = spinner_msgs[int(time.time()*100)%len(spinner_msgs)] - update_spinner = update_scroll_spinner + def update_twirl(self): + self.spinpos = (self.spinpos + 1) % len(self.twirl_sequence) + sys.stdout.write("\b\b " + self.twirl_sequence[self.spinpos]) + sys.stdout.flush() -if (not sys.stdout.isatty()) or (portage.settings["NOCOLOR"] in ["yes","true"]): - nocolor() + def update_quiet(self): + return @@ -95,7 +116,8 @@ def normpath(mystr): def userquery(prompt, responses=None, colours=None): """Displays a prompt and a set of responses, then waits for a response which is checked against the responses and the first to match is - returned. + returned. An empty response will match the first value in responses. The + input buffer is *not* cleared prior to the prompt! prompt: a String. responses: a List of Strings. @@ -119,6 +141,7 @@ def userquery(prompt, responses=None, colours=None): while True: response=raw_input("["+string.join([colours[i](responses[i]) for i in range(len(responses))],"/")+"] ") for key in responses: + # An empty response will match the first value in responses. if response.upper()==key[:len(response)].upper(): return key print "Sorry, response '%s' not understood." % response, @@ -151,23 +174,7 @@ def sorted_versions(verlist): ret.insert(x, ver) return ret -if portage.settings.has_key("PORTAGE_NICENESS"): - try: - os.nice(int(portage.settings["PORTAGE_NICENESS"])) - except (OSError,ValueError), e: - print "!!! Failed to change nice value to '"+str(portage.settings["PORTAGE_NICENESS"])+"'" - print "!!!",e - -#Freeze the portdbapi for enhanced performance: -portage.portdb.freeze() - -# Kill noauto as it will break merges otherwise. -while 'noauto' in portage.features: - del portage.features[portage.features.index('noauto')] -#number of ebuilds merged -merged=0 -params=["selective", "deep", "self", "recurse", "empty"] actions=[ "clean", "config", "depclean", "info", "metadata", @@ -177,7 +184,7 @@ actions=[ options=[ "--ask", "--alphabetical", "--buildpkg", "--buildpkgonly", -"--changelog", "--columns", "--cols", +"--changelog", "--columns", "--debug", "--deep", "--digest", "--emptytree", @@ -191,7 +198,7 @@ options=[ "--onlydeps", "--pretend", "--quiet", "--resume", "--searchdesc", "--selective", -"--skipfirst", "--skip-first", +"--skipfirst", "--tree", "--update", "--usepkg", "--usepkgonly", @@ -220,230 +227,18 @@ shortmapping={ "v":"--verbose", "V":"--version" } -myaction=None -myopts=[] -myfiles=[] -edebug=0 - -# process short actions -tmpcmdline=sys.argv[1:] -if "--ignore-default-opts" not in tmpcmdline: - tmpcmdline.extend(portage.settings["EMERGE_DEFAULT_OPTS"].split()) -cmdline=[] -for x in tmpcmdline: - if x[0:1]=="-" and x[1:2]!="-": - for y in x[1:]: - if shortmapping.has_key(y): - if shortmapping[y] in cmdline: - print - print "*** Warning: Redundant use of",shortmapping[y] - else: - cmdline.append(shortmapping[y]) - else: - print "!!! Error: -"+y+" is an invalid short action or option." - sys.exit(1) - else: - cmdline.append(x) - -# process the options and command arguments -for x in cmdline: - if not x: - continue - if len(x)>=2 and x[0:2]=="--": - if x == "--cols": - x = "--columns" - elif x == "--skip-first": - x = "--skipfirst" - if x in options: - if x not in myopts: - myopts.append(x) - elif x[2:] in actions: - if myaction: - if myaction not in ["system", "world"]: - myaction="--"+myaction - print - print red("!!!")+green(" Multiple actions requested... Please choose one only.") - print red("!!!")+" '"+darkgreen(myaction)+"' "+red("or")+" '"+darkgreen(x)+"'" - print - sys.exit(1) - myaction=x[2:] - else: - print "!!! Error:",x,"is an invalid option." - sys.exit(1) - elif (not myaction) and (x in actions): - if x not in ["system", "world"]: - print red("*** Deprecated use of action '%s', use '--%s' instead" % (x,x)) - if myaction: - print - print red("!!!")+green(" Multiple actions requested... Please choose one only.") - print red("!!! '")+darkgreen(myaction)+"' "+red("or")+" '"+darkgreen(x)+"'" - print - sys.exit(1) - myaction=x - elif x[-1]=="/": - # this little conditional helps tab completion - myfiles.append(x[:-1]) - else: - myfiles.append(x) - - -if "moo" in myfiles: - print """ - - Larry loves Gentoo (""" + os.uname()[0] + """) - - _______________________ -< Have you mooed today? > - ----------------------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || - -""" - -if (myaction in ["world", "system"]) and myfiles: - print "emerge: please specify a package class (\"world\" or \"system\") or individual packages, but not both." - sys.exit(1) - -for x in myfiles: - ext = os.path.splitext(x)[1] - if (ext == ".ebuild" or ext == ".tbz2") and os.path.exists(os.path.abspath(x)): - print "emerging by path implies --oneshot... adding --oneshot to options." - print red("\n*** emerging by path is broken and may not always work!!!\n") - break - -if ("--tree" in myopts) and ("--columns" in myopts): - print "emerge: can't specify both of \"--tree\" and \"--columns\"." - sys.exit(1) - -if ("--quiet" in myopts): - update_spinner = update_quiet_spinner - portage_util.noiselimit = -1 - portage.settings.unlock() - portage.settings["PORTAGE_QUIET"]="1" - portage.settings.backup_changes("PORTAGE_QUIET") - portage.settings.lock() - -# Always create packages if FEATURES=buildpkg -# Imply --buildpkg if --buildpkgonly -if ("buildpkg" in portage.features) or ("--buildpkgonly" in myopts): - if "--buildpkg" not in myopts: - myopts.append("--buildpkg") - -# --tree only makes sense with --pretend -if "--tree" in myopts and not (("--pretend" in myopts) or ("--ask" in myopts)): - print ">>> --tree implies --pretend... adding --pretend to options." - myopts.append("--pretend") - -# Also allow -S to invoke search action (-sS) -if ("--searchdesc" in myopts): - if myaction and myaction != "search": - myfiles.append(myaction) - if "--search" not in myopts: - myopts.append("--search") - myaction = "search" - -# Always try and fetch binary packages if FEATURES=getbinpkg -if ("getbinpkg" in portage.features): - myopts.append("--getbinpkg") - -if "--skipfirst" in myopts and "--resume" not in myopts: - myopts.append("--resume") - -if ("--getbinpkgonly" in myopts) and not ("--usepkgonly" in myopts): - myopts.append("--usepkgonly") - -if ("--getbinpkgonly" in myopts) and not ("--getbinpkg" in myopts): - myopts.append("--getbinpkg") - -if ("--getbinpkg" in myopts) and not ("--usepkg" in myopts): - myopts.append("--usepkg") - -# Also allow -K to apply --usepkg/-k -if ("--usepkgonly" in myopts) and not ("--usepkg" in myopts): - myopts.append("--usepkg") - -if ("--newuse" in myopts) and not ("--update" in myopts): - print ">>> --newuse implies --update... adding --update to options." - myopts.append("--update") - -# Also allow -l to apply --pretend/-p, but if already in --ask mode -if ("--changelog" in myopts) and not (("--pretend" in myopts) or ("--ask" in myopts)): - print ">>> --changelog implies --pretend... adding --pretend to options." - myopts.append("--pretend") - -# Allow -p to remove --ask -if ("--pretend" in myopts) and ("--ask" in myopts): - print ">>> --pretend disables --ask... removing --ask from options." - myopts.remove("--ask") - -# forbid --ask when not in a terminal -# note: this breaks `emerge --ask | tee logfile`, but that doesn't work anyway. -if ("--ask" in myopts) and (not sys.stdin.isatty()): - portage.writemsg("!!! \"--ask\" should only be used in a terminal. Exiting.\n") - sys.exit(1) - -# Set so that configs will be merged regardless of remembered status -if ("--noconfmem" in myopts): - portage.settings.unlock() - portage.settings["NOCONFMEM"]="1" - portage.settings.backup_changes("NOCONFMEM") - portage.settings.lock() - -# Set various debug markers... They should be merged somehow. -if ("--debug" in myopts): - portage.settings.unlock() - portage.settings["PORTAGE_DEBUG"]="1" - portage.settings.backup_changes("PORTAGE_DEBUG") - portage.debug=1 - portage.settings.lock() - if "python-trace" in portage.features: - import portage_debug - portage_debug.set_trace(True) - -if ("--resume" in myopts): - if "--tree" in myopts: - print "* --tree is currently broken with --resume. Disabling..." - myopts.remove("--tree") - -# Set color output -if "--nocolor" in myopts or \ -portage.settings["NOCOLOR"] in ("yes","true"): - nocolor() - if "NOCOLOR" not in portage.settings: - portage.settings.unlock() - portage.settings["NOCOLOR"] = "true" - portage.settings.backup_changes("NOCOLOR") - portage.settings.lock() - -if not ("--quiet" in myopts): - if not sys.stdout.isatty() or ("--nospinner" in myopts): - update_spinner = update_basic_spinner - -CLEAN_DELAY = 5 -EMERGE_WARNING_DELAY = 10 -if portage.settings["CLEAN_DELAY"]: - CLEAN_DELAY = string.atoi("0"+portage.settings["CLEAN_DELAY"]) -if portage.settings["EMERGE_WARNING_DELAY"]: - EMERGE_WARNING_DELAY = string.atoi("0"+portage.settings["EMERGE_WARNING_DELAY"]) - -def emergelog(mystr,short_msg=None): - if "notitles" not in portage.features: +def emergelog(xterm_titles, mystr, short_msg=None): + if xterm_titles: if short_msg: xtermTitle(short_msg) else: xtermTitle(mystr) try: - #seems odd opening a file each write... - elog = portage_const.EPREFIX+"/var/log/emerge.log" - if not os.path.exists(elog): - mylogfile=open(elog, "w") - os.chmod(elog, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) - os.chown(elog, portage.portage_uid, portage.portage_gid) - else: - mylogfile=open(elog, "a") + file_path = portage_const.EPREFIX+"/var/log/emerge.log" + mylogfile = open(file_path, "a") + portage_util.apply_secpass_permissions(file_path, + uid=portage.portage_uid, gid=portage.portage_gid, + mode=0660) l=portage_locks.lockfile(mylogfile) # seek because we may have gotten held up by the lock. @@ -453,27 +248,9 @@ def emergelog(mystr,short_msg=None): mylogfile.flush() portage_locks.unlockfile(l) mylogfile.close() - except SystemExit, e: - raise # Needed else can't exit - except Exception, e: - if edebug: - print "emergelog():",e - pass - -def emergeexit(): - """This gets out final log message in before we quit.""" - if "--pretend" not in myopts: - emergelog(" *** terminating.") - if "notitles" not in portage.features: - xtermTitleReset() -portage.atexit_register(emergeexit) - -def emergeexitsig(signum, frame): - signal.signal(signal.SIGINT, signal.SIG_IGN) - portage.portageexit() - portage_util.writemsg("\n\nExiting on signal %(signal)s\n" % {"signal":signum}) - sys.exit(100+signum) -signal.signal(signal.SIGINT, emergeexitsig) + except (IOError,OSError,portage_exception.PortageException), e: + if secpass >= 1: + print >> sys.stderr, "emergelog():",e def countdown(secs=5, doing="Starting"): if secs: @@ -482,7 +259,7 @@ def countdown(secs=5, doing="Starting"): ticks=range(secs) ticks.reverse() for sec in ticks: - sys.stdout.write(red(str(sec+1)+" ")) + sys.stdout.write(colorize("UNMERGE_WARN", str(sec+1)+" ")) sys.stdout.flush() time.sleep(1) print @@ -499,87 +276,60 @@ def format_size(mysize): return mystr+" kB" -def getgccversion(): +def getgccversion(chost): """ rtype: C{str} return: the current in-use gcc version """ - gcc_env_dir = os.path.join('/', 'etc', 'env.d', 'gcc') - gcc_config_config = os.path.join(gcc_env_dir, 'config') gcc_ver_command = 'gcc -dumpversion' gcc_ver_prefix = 'gcc-' gcc_not_found_error = red( "!!! No gcc found. You probably need to 'source /etc/profile'\n" + "!!! to update the environment of this terminal and possibly\n" + - "!!! other terminals also." + "!!! other terminals also.\n" ) - gcc_distcc_broken_error = green( - '!!! Relying on the shell to locate gcc, this may break\n' + - '!!! DISTCC, installing gcc-config and setting your current gcc\n' + - '!!! profile will fix this' - ) - - def fallback(): - - print >>sys.stderr, gcc_distcc_broken_error - - gccout = commands.getstatusoutput(gcc_ver_command) - - if gccout[0] != 0: - print >>sys.stderr, gcc_not_found_error - gccver = "[unavailable]" + mystatus, myoutput = commands.getstatusoutput("eselect compiler show") + if mystatus == os.EX_OK and len(myoutput.split("/")) == 2: + part1, part2 = myoutput.split("/") + if part1.startswith(chost + "-"): + return myoutput.replace(chost + "-", gcc_ver_prefix, 1) + + mystatus, myoutput = commands.getstatusoutput("gcc-config -c") + if mystatus == os.EX_OK and len(myoutput.split("-")) > 0: + return gcc_ver_prefix + myoutput.split("-")[-1] + + mystatus, myoutput = commands.getstatusoutput( + chost + "-" + gcc_ver_command) + if mystatus == os.EX_OK: + return gcc_ver_prefix + myoutput + + mystatus, myoutput = commands.getstatusoutput(gcc_ver_command) + if mystatus == os.EX_OK: + return gcc_ver_prefix + myoutput + + portage.writemsg(gcc_not_found_error, noiselevel=-1) + return "[unavailable]" + +def getportageversion(portdir, target_root, profile, chost, vardb): + profilever = "unavailable" + if profile: + realpath = os.path.realpath(profile) + basepath = os.path.realpath(os.path.join(portdir, "profiles")) + if realpath.startswith(basepath): + profilever = realpath[1 + len(basepath):] else: - gccver = gcc_ver_prefix + gccout[1] - - return gccver - - if os.path.isfile(gcc_config_config): - try: - gccver_str = open(gcc_config_config).read().strip() - gccver = gcc_ver_prefix + string.join(gccver_str.split('-')[4:], '-') - except IndexError: - gccver = fallback() - - else: - import glob - dir_l = glob.glob(os.path.join(gcc_env_dir, '*-*')) - - if len(dir_l) == 1: try: - gccver = gcc_ver_prefix + dir_l[0].split('-')[-1] - except IndexError: - gccver = fallback() - - else: - # There was no "config" file in /etc/env.d/gcc and there was more - # than one profile in /etc/env.d/gcc so we can't actively - # determine what version of gcc we are using so we fall back on the - # old way that breaks distcc + profilever = "!" + os.readlink(profile) + except (OSError): + pass + del realpath, basepath - gccver = fallback() - - return gccver - -def getportageversion(): - try: - import re - profilever = os.path.normpath("///"+os.readlink(portage_const.EPREFIX+"/etc/make.profile")) - basepath = os.path.normpath("///"+portage.settings["PORTDIR"]+"/profiles") - if re.match(basepath,profilever): - profilever = profilever[len(basepath)+1:] - else: - profilever = "!"+profilever - del basepath - except SystemExit, e: - raise # Needed else can't exit - except: - profilever="unavailable" libcver=[] - libclist = portage.vardbapi(portage.root).match("virtual/libc") - libclist += portage.vardbapi(portage.root).match("virtual/glibc") + libclist = vardb.match("virtual/libc") + libclist += vardb.match("virtual/glibc") libclist = portage_util.unique_array(libclist) for x in libclist: xs=portage.catpkgsplit(x) @@ -590,84 +340,41 @@ def getportageversion(): if libcver==[]: libcver="unavailable" - gccver = getgccversion() + gccver = getgccversion(chost) unameout=os.uname()[2]+" "+os.uname()[4] return "Portage " + portage.VERSION +" ("+profilever+", "+gccver+", "+libcver+", "+unameout+")" -def help(): - # Move all the help stuff out of this file. - emergehelp.help(myaction,myopts,havecolor) - -if "--version" in myopts: - print getportageversion() - sys.exit(0) -elif "--help" in myopts: - help() - sys.exit(0) - -if "--debug" in myopts: - print "myaction", myaction - print "myopts", myopts - -# check if root user is the current user for the actions where emerge needs this -if portage.secpass < 2: - # We've already allowed "--version" and "--help" above. - if "--pretend" not in myopts and \ - myaction not in ("search","info"): - if portage.secpass >= 1: - if "--fetchonly" not in myopts and \ - "--fetch-all-uri" not in myopts: - print "emerge: root access required." - sys.exit(1) - else: - print "emerge: root access required." - sys.exit(1) - -if not "--pretend" in myopts: - emergelog("Started emerge on: "+time.strftime("%b %d, %Y %H:%M:%S", time.localtime())) - myelogstr="" - if myopts: - myelogstr=string.join(myopts, " ") - if myaction: - myelogstr+=" "+myaction - if myfiles: - myelogstr+=" "+string.join(myfiles, " ") - emergelog(" *** emerge "+myelogstr) - -#configure emerge engine parameters -# -# self: include _this_ package regardless of if it is merged. -# selective: exclude the package if it is merged -# recurse: go into the dependencies -# empty: pretend nothing is merged -myparams=["self","recurse"] -add=[] -sub=[] -if "--update" in myopts or myaction in ("system", "world"): - add.extend(["selective","empty"]) -if "--emptytree" in myopts: - add.extend(["empty"]) - sub.extend(["selective"]) -if "--nodeps" in myopts: - sub.extend(["recurse"]) -if "--noreplace" in myopts: - add.extend(["selective"]) -if "--deep" in myopts: - add.extend(["deep"]) -if "--selective" in myopts: - add.extend(["selective"]) -if myaction in ["world","system"]: - add.extend(["selective"]) -elif myaction in ["depclean"]: - add.extend(["empty"]) - sub.extend(["selective"]) -for x in add: - if (x not in myparams) and (x not in sub): - myparams.append(x) -for x in sub: - if x in myparams: - myparams.remove(x) +def create_depgraph_params(myopts, myaction): + #configure emerge engine parameters + # + # self: include _this_ package regardless of if it is merged. + # selective: exclude the package if it is merged + # recurse: go into the dependencies + # deep: go into the dependencies of already merged packages + # empty: pretend nothing is merged + myparams = ["recurse"] + add=[] + sub=[] + if "--update" in myopts or \ + "--newuse" in myopts or \ + "--noreplace" in myopts or \ + myaction in ("system", "world"): + add.extend(["selective"]) + if "--emptytree" in myopts: + add.extend(["empty"]) + sub.extend(["selective"]) + if "--nodeps" in myopts: + sub.extend(["recurse"]) + if "--deep" in myopts: + add.extend(["deep"]) + for x in add: + if (x not in myparams) and (x not in sub): + myparams.append(x) + for x in sub: + if x in myparams: + myparams.remove(x) + return myparams # search functionality class search: @@ -681,19 +388,24 @@ class search: # # public interface # - def __init__(self): + def __init__(self, settings, portdb, vartree, spinner, searchdesc, + verbose): """Searches the available and installed packages for the supplied search key. The list of available and installed packages is created at object instantiation. This makes successive searches faster.""" - self.installcache = portage.db["/"]["vartree"] + self.settings = settings + self.portdb = portdb + self.vartree = vartree + self.spinner = spinner + self.verbose = verbose + self.searchdesc = searchdesc def execute(self,searchkey): """Performs the search for the supplied search key""" - global myopts match_category = 0 self.searchkey=searchkey self.packagematches = [] - if "--searchdesc" in myopts: + if self.searchdesc: self.searchdesc=1 self.matches = {"pkg":[], "desc":[]} else: @@ -701,18 +413,19 @@ class search: self.matches = {"pkg":[]} print "Searching... ", + regexsearch = False + if self.searchkey[0] == '%': + regexsearch = True + self.searchkey = self.searchkey[1:] if self.searchkey[0] == '@': match_category = 1 self.searchkey = self.searchkey[1:] - if self.searchkey=="*": - #hack for people who aren't regular expression gurus - self.searchkey==".*" - if re.search("\+\+", self.searchkey): - #hack for people who aren't regular expression gurus - self.searchkey=re.sub("\+\+","\+\+",self.searchkey) - self.searchre=re.compile(self.searchkey.lower(),re.I) - for package in portage.portdb.cp_all(): - update_spinner() + if regexsearch: + self.searchre=re.compile(self.searchkey,re.I) + else: + self.searchre=re.compile(re.escape(self.searchkey), re.I) + for package in self.portdb.cp_all(): + self.spinner.update() if match_category: match_string = package[:] @@ -721,20 +434,22 @@ class search: masked=0 if self.searchre.search(match_string): - if not portage.portdb.xmatch("match-visible",package): + if not self.portdb.xmatch("match-visible", package): masked=1 self.matches["pkg"].append([package,masked]) elif self.searchdesc: # DESCRIPTION searching - full_package = portage.portdb.xmatch("bestmatch-visible",package) + full_package = self.portdb.xmatch("bestmatch-visible", package) if not full_package: #no match found; we don't want to query description - full_package=portage.best(portage.portdb.xmatch("match-all",package)) + full_package = portage.best( + self.portdb.xmatch("match-all", package)) if not full_package: continue else: masked=1 try: - full_desc = portage.portdb.aux_get(full_package,["DESCRIPTION"])[0] + full_desc = self.portdb.aux_get( + full_package, ["DESCRIPTION"])[0] except KeyError: print "emerge: search: aux_get() failed, skipping" continue @@ -754,18 +469,21 @@ class search: for match,masked in self.matches[mtype]: if mtype=="pkg": catpack=match - full_package = portage.portdb.xmatch("bestmatch-visible",match) + full_package = self.portdb.xmatch( + "bestmatch-visible", match) if not full_package: #no match found; we don't want to query description masked=1 - full_package=portage.best(portage.portdb.xmatch("match-all",match)) + full_package = portage.best( + self.portdb.xmatch("match-all",match)) else: full_package = match match = portage.pkgsplit(match)[0] if full_package: try: - desc, homepage, license = portage.portdb.aux_get(full_package,["DESCRIPTION","HOMEPAGE","LICENSE"]) + desc, homepage, license = self.portdb.aux_get( + full_package, ["DESCRIPTION","HOMEPAGE","LICENSE"]) except KeyError: print "emerge: search: aux_get() failed, skipping" continue @@ -778,28 +496,26 @@ class search: mysum = [0,0] mycat = match.split("/")[0] mypkg = match.split("/")[1] - - mydigest = portage.db["/"]["porttree"].dbapi.finddigest(mycat+"/"+mypkg + "-" + myversion) - + mycpv = match + "-" + myversion + myebuild = self.portdb.findname(mycpv) + pkgdir = os.path.dirname(myebuild) + import portage_manifest + mf = portage_manifest.Manifest( + pkgdir, self.settings["DISTDIR"]) + fetchlist = self.portdb.getfetchlist(mycpv, + mysettings=self.settings, all=True)[1] try: - myfile = open(mydigest,"r") - for line in myfile.readlines(): - mysum[0] += int(line.split(" ")[3]) - myfile.close() + mysum[0] = mf.getDistfilesSize(fetchlist) mystr = str(mysum[0]/1024) mycount=len(mystr) while (mycount > 3): mycount-=3 mystr=mystr[:mycount]+","+mystr[mycount:] mysum[0]=mystr+" kB" - except SystemExit, e: - raise # Needed else can't exit - except Exception, e: - if edebug: - print "!!! Exception:",e - mysum[0]=" [no/bad digest]" + except KeyError, e: + mysum[0] = "Unknown (missing digest for %s)" % str(e) - if "--quiet" not in myopts: + if self.verbose: print " ", darkgreen("Latest version available:"),myversion print " ", self.getInstallationStatus(mycat+'/'+mypkg) print " ", darkgreen("Size of files:"),mysum[0] @@ -812,7 +528,7 @@ class search: # private interface # def getInstallationStatus(self,package): - installed_package = self.installcache.dep_bestmatch(package) + installed_package = self.vartree.dep_bestmatch(package) result = "" version = self.getVersion(installed_package,search.VERSION_RELEASE) if len(version) > 0: @@ -834,20 +550,21 @@ class search: #build our package digraph -def getlist(mode): +def getlist(settings, mode): if mode=="system": - mylines=portage.settings.packages + mylines = settings.packages elif mode=="world": try: - myfile = open(os.path.join(portage.root, portage.WORLD_FILE), "r") - mylines=myfile.readlines() + file_path = os.path.join(settings["ROOT"], portage.WORLD_FILE) + myfile = open(file_path, "r") + mylines = myfile.readlines() myfile.close() - except OSError: - print "!!! Couldn't open "+pfile+"; exiting." - sys.exit(1) - except IOError: - #world file doesn't exist - mylines=[] + except (OSError, IOError), e: + if e.errno == errno.ENOENT: + portage.writemsg("\n!!! World file does not exist: '%s'\n" % file_path) + mylines=[] + else: + raise mynewlines=[] for x in mylines: myline=string.join(string.split(x)) @@ -863,232 +580,366 @@ def getlist(mode): # Remove everything that is package.provided from our list for atom in mynewlines[:]: - for expanded_atom in portage.flatten(portage.dep_virtual([atom], portage.settings)): + for expanded_atom in portage.flatten( + portage.dep_virtual([atom], settings)): mykey = portage.dep_getkey(expanded_atom) - if portage.settings.pprovideddict.has_key(mykey) and \ - portage.match_from_list(expanded_atom, portage.settings.pprovideddict[mykey]): + if mykey in settings.pprovideddict and \ + portage.match_from_list( + expanded_atom, settings.pprovideddict[mykey]): mynewlines.remove(atom) break return mynewlines +def clean_world(vardb, cpv): + """Remove a package from the world file when unmerged.""" + world_filename = os.path.join(vardb.root, portage.WORLD_FILE) + worldlist = portage_util.grabfile(world_filename) + mykey = portage.cpv_getkey(cpv) + newworldlist = [] + for x in worldlist: + if portage.dep_getkey(x) == mykey: + matches = vardb.match(x, use_cache=0) + if not matches: + #zap our world entry + pass + elif len(matches) == 1 and matches[0] == cpv: + #zap our world entry + pass + else: + #others are around; keep it. + newworldlist.append(x) + else: + #this doesn't match the package we're unmerging; keep it. + newworldlist.append(x) + + portage_util.ensure_dirs(os.path.join(vardb.root, portage.PRIVATE_PATH), + gid=portage.portage_gid, mode=02770) + portage_util.write_atomic(world_filename, "\n".join(newworldlist)) + def genericdict(mylist): mynewdict={} for x in mylist: mynewdict[portage.dep_getkey(x)]=x return mynewdict -olddbapi=None class depgraph: - def __init__(self,myaction,myopts): - global olddbapi - self.pkgsettings = portage.config(clone=portage.settings) - if not self.pkgsettings["ARCH"]: - portage.writemsg(red("\a!!! ARCH is not set... Are you missing the /etc/make.profile symlink?\n")) - portage.writemsg(red("\a!!! Is the symlink correct? Is your portage tree complete?\n\n")) - sys.exit(9) - self.applied_useflags = {} + pkg_tree_map = { + "ebuild":"porttree", + "binary":"bintree", + "installed":"vartree"} + + def __init__(self, settings, trees, myopts, myparams, spinner): + self.settings = settings + self.target_root = settings["ROOT"] + self.trees = trees + self.myopts = myopts + self.myparams = myparams + self.edebug = 0 + if settings.get("PORTAGE_DEBUG", "") == "1": + self.edebug = 1 + self.spinner = spinner + self.pkgsettings = {} + self.pkgsettings[self.target_root] = portage.config(clone=settings) + if self.target_root != "/": + self.pkgsettings["/"] = \ + portage.config(clone=trees["/"]["vartree"].settings) + self.useFlags = {} + self.useFlags[self.target_root] = {} + if self.target_root != "/": + self.useFlags["/"] = {} self.missingbins=[] - self.myaction=myaction self.digraph=portage.digraph() self.orderedkeys=[] self.outdatedpackages=[] self.mydbapi={} - self.mydbapi["/"] = portage.fakedbapi() - if "empty" not in myparams or portage.root != "/": - for pkg in portage.db["/"]["vartree"].getallcpv(): - self.mydbapi["/"].cpv_inject(pkg) - if portage.root != "/": - self.mydbapi[portage.root] = portage.fakedbapi() - if "empty" not in myparams: - for pkg in portage.db[portage.root]["vartree"].getallcpv(): - self.mydbapi[portage.root].cpv_inject(pkg) - - if "--usepkg" in myopts: - portage.db["/"]["bintree"].populate(("--getbinpkg" in myopts), ("--getbinpkgonly" in myopts)) - - def create(self,mybigkey,myparent=None,addme=1,myuse=None): - """creates the actual digraph of packages to merge. return 1 on success, 0 on failure - mybigkey = specification of package to merge; myparent = parent package (one depending on me); - addme = should I be added to the tree? (for the --onlydeps mode)""" + self.mydbapi["/"] = portage.fakedbapi(settings=settings) + vardb = self.trees["/"]["vartree"].dbapi + fakedb = self.mydbapi["/"] + if "empty" not in self.myparams or self.target_root != "/": + for pkg in vardb.cpv_all(): + myslot = vardb.aux_get(pkg, ["SLOT"])[0] + fakedb.cpv_inject(pkg, myslot=myslot) + if self.target_root != "/": + self.mydbapi[self.target_root] = \ + portage.fakedbapi(settings=settings) + vardb = self.trees[self.target_root]["vartree"].dbapi + fakedb = self.mydbapi[self.target_root] + if "empty" not in self.myparams: + for pkg in vardb.cpv_all(): + myslot = vardb.aux_get(pkg, ["SLOT"])[0] + fakedb.cpv_inject(pkg, myslot=myslot) + if "--usepkg" in self.myopts: + trees["/"]["bintree"].populate( + "--getbinpkg" in self.myopts, "--getbinpkgonly" in self.myopts) + if self.target_root != "/": + trees[self.target_root]["bintree"].populate( + "--getbinpkg" in self.myopts, + "--getbinpkgonly" in self.myopts) + self.args_keys = [] + self.global_updates = {} + + def create(self, mybigkey, myparent=None, addme=1, myuse=None, + priority=digraph.HARD, rev_dep=False, arg=None): + """ + Fills the digraph with nodes comprised of packages to merge. + mybigkey is the package spec of the package to merge. + myparent is the package depending on mybigkey ( or None ) + addme = Should we add this package to the digraph or are we just looking at it's deps? + Think --onlydeps, we need to ignore packages in that case. #stuff to add: #SLOT-aware emerge - #IUSE-aware emerge + #IUSE-aware emerge -> USE DEP aware depgraph #"no downgrade" emerge - #print "mybigkey:",mybigkey - - jbigkey=string.join(mybigkey) - if self.digraph.hasnode(jbigkey+" merge") or self.digraph.hasnode(jbigkey+" nomerge"): - #this conditional is needed to prevent infinite recursion on already-processed deps + """ + + jbigkey = " ".join(mybigkey) + " merge" + if self.digraph.hasnode(jbigkey): + if addme and jbigkey != myparent: + # Refuse to make a node depend on itself so that the we don't + # don't create a bogus circular dependency in self.altlist(). + if rev_dep and myparent: + self.digraph.addnode(myparent, jbigkey, priority=priority) + else: + self.digraph.addnode(jbigkey, myparent, priority=priority) return 1 - - update_spinner() - + jbigkey = " ".join(mybigkey) + " nomerge" + if self.digraph.hasnode(jbigkey): + """XXX: It's possible that this package has been explicitly + requested as a command line argument. This can be solved by + checking all args prior to marking packages as nomerge""" + if rev_dep and myparent: + self.digraph.addnode(myparent, jbigkey, priority=priority) + else: + self.digraph.addnode(jbigkey, myparent, priority=priority) + return 1 + + self.spinner.update() mytype,myroot,mykey=mybigkey + if mytype == "blocks": + if addme and "--buildpkgonly" not in self.myopts and myparent: + mybigkey[1] = myparent.split()[1] + self.digraph.addnode(" ".join(mybigkey), myparent, + priority=priority) + return 1 + if not arg: + arg = portage.best_match_to_list(mykey, self.args_keys) # select the correct /var database that we'll be checking against - vardbapi=portage.db[myroot]["vartree"].dbapi + vardbapi = self.trees[myroot]["vartree"].dbapi + portdb = self.trees[myroot]["porttree"].dbapi + bindb = self.trees[myroot]["bintree"].dbapi + pkgsettings = self.pkgsettings[myroot] # if the package is already on the system, we add a "nomerge" # directive, otherwise we add a "merge" directive. - if mytype=="blocks": - # we've encountered a "blocks" node. We will totally ignore this - # node and not add it to our digraph if it doesn't apply to us. - if addme and "--buildpkgonly" not in myopts and myparent and (self.mydbapi[myroot].match(mykey) or vardbapi.match(mykey)): - mybigkey.append(myparent.split()[2]) - self.digraph.addnode(string.join(mybigkey),myparent) - return 1 - if myuse == None: - self.pkgsettings.setcpv(mykey) - myuse = self.pkgsettings["USE"].split() - self.applied_useflags[mykey] = myuse + mydbapi = self.trees[myroot][self.pkg_tree_map[mytype]].dbapi + + if myuse is None: + self.pkgsettings[myroot].setcpv(mykey) + myuse = self.pkgsettings[myroot]["USE"].split() + self.useFlags[myroot][mykey] = myuse merging=1 - if addme: + if mytype == "installed": + merging = 0 + if addme and mytype != "installed": # this is where we add the node to the list of packages to merge - if not myparent: - # command-line specified or part of a world list... - if ("self" not in myparams) or (("selective" in myparams) and vardbapi.cpv_exists(mykey)): - # the package is on the system, so don't merge it. + if "selective" in self.myparams or not arg: + if "empty" not in self.myparams and vardbapi.cpv_exists(mykey): merging=0 - elif ("selective" in myparams) and vardbapi.cpv_exists(mykey): - merging=0 - if (merging==0 and "--newuse" in myopts and vardbapi.cpv_exists(mykey)): + """ If we aren't merging, perform the --newuse check. + If the package has new iuse flags or different use flags then if + --newuse is specified, we need to merge the package. """ + if merging==0 and "--newuse" in self.myopts and \ + vardbapi.cpv_exists(mykey): + pkgsettings.setcpv(mykey) + forced_flags = set() + forced_flags.update(pkgsettings.useforce) + forced_flags.update(pkgsettings.puseforce) + forced_flags.update(pkgsettings.usemask) + forced_flags.update(pkgsettings.pusemask) old_use = vardbapi.aux_get(mykey, ["USE"])[0].split() - if mytype == "binary": - iuses = portage.db["/"]["bintree"].dbapi.aux_get(mykey, ["IUSE"])[0].split() - else: - iuses = portage.db["/"]["porttree"].dbapi.aux_get(mykey, ["IUSE"])[0].split() - for x in iuses: - if (old_use.count(x) and not myuse.count(x)) or (not old_use.count(x) and myuse.count(x)): - merging=1 - break - else: - #onlydeps mode; don't merge - merging=2 - if merging==1: + iuses = set(mydbapi.aux_get(mykey, ["IUSE"])[0].split()) + old_iuse = set(vardbapi.aux_get(mykey, ["IUSE"])[0].split()) + if iuses.symmetric_difference( + old_iuse).difference(forced_flags): + merging = 1 + elif old_iuse.intersection(old_use) != \ + iuses.intersection(myuse): + merging=1 + + if addme and merging == 1: mybigkey.append("merge") else: mybigkey.append("nomerge") - # whatever the case, we need to add the node to our digraph so - # that children can depend upon it. - self.digraph.addnode(string.join(mybigkey),myparent) - if ("deep" not in myparams) and (not merging): + if merging == 0 and vardbapi.cpv_exists(mykey): + myuse = vardbapi.aux_get(mykey, ["USE"])[0].split() + self.useFlags[myroot][mykey] = myuse + + """ At this point, we have either hit a blocker and returned, found the package in the + depgraph already and returned, or we are here. Whether we are merging or not; we must + add the package to the depgraph; so we do that here. """ + if rev_dep and myparent: + self.digraph.addnode(myparent, " ".join(mybigkey), + priority=priority) + else: + self.digraph.addnode(" ".join(mybigkey), myparent, + priority=priority) + + """ This section determines whether we go deeper into dependencies or not. + We want to go deeper on a few occasions: + Installing package A, we need to make sure package A's deps are met. + emerge --deep ; we need to recursively check dependencies of pkgspec + If we are in --nodeps (no recursion) mode, we obviously only check 1 level of dependencies. + """ + if "deep" not in self.myparams and not merging and \ + not ("--update" in self.myopts and arg and merging): return 1 - elif "recurse" not in myparams: + elif "recurse" not in self.myparams: return 1 + """ Check DEPEND/RDEPEND/PDEPEND/SLOT + Pull from bintree if it's binary package, porttree if it's ebuild. + Binpkg's can be either remote or local. """ + edepend={} - if mytype=="binary": - mypkgparts=portage.catpkgsplit(mykey) - tbz2name = string.split(mykey, "/")[1]+".tbz2" - if tbz2name in portage.db[portage.root]["bintree"].invalids: - sys.stderr.write("\nINVALID PACKAGE (is required to continue): "+str(mykey)+"\n") - sys.exit(1) - if portage.db[portage.root]["bintree"].isremote(mykey): - edepend = portage.db[portage.root]["bintree"].remotepkgs[tbz2name] - edepend["DEPEND"] ="" - edepend["RDEPEND"]=string.join(string.split(edepend["RDEPEND"])," ") - edepend["PDEPEND"]=string.join(string.split(edepend["PDEPEND"])," ") - edepend["SLOT"] =string.strip(edepend["SLOT"]) - #portage.db[portage.root]["bintree"].gettbz2(mykey) - else: # It's local. - mytbz2=xpak.tbz2(portage.db[portage.root]["bintree"].getname(mykey)) - edepend["DEPEND"] ="" - edepend["RDEPEND"]=string.join(mytbz2.getelements("RDEPEND")," ") - edepend["PDEPEND"]=string.join(mytbz2.getelements("PDEPEND")," ") - edepend["SLOT"] =mytbz2.getfile("SLOT",mypkgparts[2]) - elif mytype=="ebuild": - try: - mymeta = ["DEPEND","RDEPEND","PDEPEND"] - myfoo = portage.portdb.aux_get(mykey, mymeta) - for index in range(0,len(mymeta)): - edepend[mymeta[index]] = myfoo[index] - if "--buildpkgonly" in myopts: - edepend["RDEPEND"] = "" - edepend["PDEPEND"] = "" - except (KeyError,IOError): - print "emerge: create(): aux_get() error on",mykey+"; aborting..." - sys.exit(1) - mydep={} + depkeys = ["DEPEND","RDEPEND","PDEPEND"] + depvalues = mydbapi.aux_get(mykey, depkeys) + for i in xrange(len(depkeys)): + edepend[depkeys[i]] = depvalues[i] + + if mytype == "ebuild": + if "--buildpkgonly" in self.myopts: + edepend["RDEPEND"] = "" + edepend["PDEPEND"] = "" + if self.myopts.get("--with-bdeps", "n") == "n" and \ + (mytype == "binary" or mybigkey[3] == "nomerge"): + edepend["DEPEND"] = "" + + """ We have retrieve the dependency information, now we need to recursively + process them. DEPEND gets processed for root = "/", {R,P}DEPEND in myroot. """ + mp=string.join(mybigkey) - if myroot=="/": - mydep["/"]=edepend["DEPEND"]+" "+edepend["RDEPEND"] - if not self.select_dep("/",mydep["/"],myparent=mp,myuse=myuse): - return 0 - else: - mydep["/"]=edepend["DEPEND"] - mydep[myroot]=edepend["RDEPEND"] - if not self.select_dep("/",mydep["/"],myparent=mp,myuse=myuse): - return 0 - if not self.select_dep(myroot,mydep[myroot],myparent=mp,myuse=myuse): + try: + if not self.select_dep("/", edepend["DEPEND"], myparent=mp, + myuse=myuse, parent_arg=arg): return 0 - - if edepend.has_key("PDEPEND") and edepend["PDEPEND"]: - # Post Depend -- Add to the list without a parent, as it depends - # on a package being present AND must be built after that package. - if not self.select_dep(myroot,edepend["PDEPEND"],myuse=myuse): + """RDEPEND is soft by definition. However, in order to ensure + correct merge order, we make it a hard dependency. Otherwise, a + build time dependency might not be usable due to it's run time + dependencies not being installed yet. + """ + if not self.select_dep(myroot,edepend["RDEPEND"], myparent=mp, + myuse=myuse, priority=digraph.MEDIUM, parent_arg=arg): return 0 - + if edepend.has_key("PDEPEND") and edepend["PDEPEND"]: + # Post Depend -- Add to the list without a parent, as it depends + # on a package being present AND must be built after that package. + if not self.select_dep(myroot, edepend["PDEPEND"], myparent=mp, + myuse=myuse, priority=digraph.SOFT, rev_deps=True, parent_arg=arg): + return 0 + except ValueError, e: + pkgs = e.args[0] + portage.writemsg("\n\n!!! An atom in the dependencies " + \ + "is not fully-qualified. Multiple matches:\n\n", noiselevel=-1) + for cpv in pkgs: + portage.writemsg(" %s\n" % cpv, noiselevel=-1) + portage.writemsg("\n", noiselevel=-1) + if mytype == "binary": + portage.writemsg( + "!!! This binary package cannot be installed: '%s'\n" % \ + mykey, noiselevel=-1) + elif mytype == "ebuild": + myebuild, mylocation = portdb.findname2(mykey) + portage.writemsg("!!! This ebuild cannot be installed: " + \ + "'%s'\n" % myebuild, noiselevel=-1) + portage.writemsg("!!! Please notify the package maintainer " + \ + "that atoms must be fully-qualified.\n", noiselevel=-1) + return 0 return 1 def select_files(self,myfiles): "given a list of .tbz2s, .ebuilds and deps, create the appropriate depgraph and return a favorite list" myfavorites=[] + myroot = self.target_root + portdb = self.trees[myroot]["porttree"].dbapi + bindb = self.trees[myroot]["bintree"].dbapi + pkgsettings = self.pkgsettings[myroot] + arg_atoms = [] for x in myfiles: ext = os.path.splitext(x)[1] if ext==".tbz2": if not os.path.exists(x): - if os.path.exists(self.pkgsettings["PKGDIR"]+"/All/"+x): - x=self.pkgsettings["PKGDIR"]+"/All/"+x - elif os.path.exists(self.pkgsettings["PKGDIR"]+"/"+x): - x=self.pkgsettings["PKGDIR"]+"/"+x + if os.path.exists( + os.path.join(pkgsettings["PKGDIR"], "All", x)): + x = os.path.join(pkgsettings["PKGDIR"], "All", x) + elif os.path.exists( + os.path.join(pkgsettings["PKGDIR"], x)): + x = os.path.join(pkgsettings["PKGDIR"], x) else: print "\n\n!!! Binary package '"+str(x)+"' does not exist." print "!!! Please ensure the tbz2 exists as specified.\n" sys.exit(1) mytbz2=xpak.tbz2(x) mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.splitext(os.path.basename(x))[0] - if os.path.realpath(portage.db["/"]["bintree"].getname(mykey)) != os.path.realpath(x): - print red("\n*** You need to adjust PKGDIR to emerge this package.\n") + if os.path.realpath(x) != \ + os.path.realpath(self.trees[myroot]["bintree"].getname(mykey)): + print colorize("BAD", "\n*** You need to adjust PKGDIR to emerge this package.\n") sys.exit(1) - if not self.create(["binary",portage.root,mykey],None,"--onlydeps" not in myopts): + if not self.create(["binary", myroot, mykey], + None, "--onlydeps" not in self.myopts): return (0,myfavorites) - elif not "--oneshot" in myopts: + elif not "--oneshot" in self.myopts: myfavorites.append(mykey) elif ext==".ebuild": x = os.path.realpath(x) mykey=os.path.basename(os.path.normpath(x+"/../.."))+"/"+os.path.splitext(os.path.basename(x))[0] - ebuild_path = portage.db["/"]["porttree"].dbapi.findname(mykey) + ebuild_path = portdb.findname(mykey) if ebuild_path: if os.path.realpath(ebuild_path) != x: - print red("\n*** You need to adjust PORTDIR or PORTDIR_OVERLAY to emerge this package.\n") + print colorize("BAD", "\n*** You need to adjust PORTDIR or PORTDIR_OVERLAY to emerge this package.\n") sys.exit(1) - if mykey not in portage.db["/"]["porttree"].dbapi.xmatch("match-visible", portage.dep_getkey(mykey)): - print red("\n*** You are emerging a masked package. It is MUCH better to use") - print red("*** /etc/portage/package.* to accomplish this. See portage(5) man") - print red("*** page for details.") - countdown(EMERGE_WARNING_DELAY, "Continuing...") + if mykey not in portdb.xmatch( + "match-visible", portage.dep_getkey(mykey)): + print colorize("BAD", "\n*** You are emerging a masked package. It is MUCH better to use") + print colorize("BAD", "*** /etc/portage/package.* to accomplish this. See portage(5) man") + print colorize("BAD", "*** page for details.") + countdown(int(self.settings["EMERGE_WARNING_DELAY"]), + "Continuing...") else: - print red("\n*** %s is not in a valid PORTDIR heirarchy or does not exist" % x) - sys.exit(1) - if not self.create(["ebuild",portage.root,mykey],None,"--onlydeps" not in myopts): + raise portage_exception.PackageNotFound( + "%s is not in a valid portage tree hierarchy or does not exist" % x) + if not self.create(["ebuild", myroot, mykey], + None, "--onlydeps" not in self.myopts): return (0,myfavorites) - elif not "--oneshot" in myopts: + elif not "--oneshot" in self.myopts: myfavorites.append(mykey) else: if not is_valid_package_atom(x): - portage.writemsg("\n\n!!! '%s' is not a valid package atom.\n" % x) + portage.writemsg("\n\n!!! '%s' is not a valid package atom.\n" % x, + noiselevel=-1) portage.writemsg("!!! Please check ebuild(5) for full details.\n") portage.writemsg("!!! (Did you specify a version but forget to prefix with '='?)\n") return (0,[]) try: - mykey=portage.dep_expand(x,mydb=portage.portdb) + mykey = None + if "--usepkg" in self.myopts: + mykey = portage.dep_expand(x, mydb=bindb, + settings=pkgsettings) + if (mykey and not mykey.startswith("null/")) or \ + "--usepkgonly" in self.myopts: + arg_atoms.append((x, mykey)) + continue + + mykey = portage.dep_expand(x, + mydb=portdb, settings=pkgsettings) + arg_atoms.append((x, mykey)) except ValueError, errpkgs: print "\n\n!!! The short ebuild name \"" + x + "\" is ambiguous. Please specify" print "!!! one of the following fully-qualified ebuild names instead:\n" @@ -1097,149 +948,292 @@ class depgraph: print sys.exit(1) - # select needs to return 0 on dep_check failure - - sys.stdout.flush() - sys.stderr.flush() - + if "--update" in self.myopts: + """Make sure all installed slots are updated when possible. Do this + with --emptytree also, to ensure that all slots are remerged.""" + vardb = self.trees[self.target_root]["vartree"].dbapi + greedy_atoms = [] + for myarg, myatom in arg_atoms: + greedy_atoms.append((myarg, myatom)) + myslots = set() + for cpv in vardb.match(myatom): + myslots.add(vardb.aux_get(cpv, ["SLOT"])[0]) + if len(myslots) > 1: + for myslot in myslots: + myslot_atom = "%s:%s" % \ + (portage.dep_getkey(myatom), myslot) + available = False + if "--usepkgonly" not in self.myopts and \ + self.trees[self.target_root][ + "porttree"].dbapi.match(myslot_atom): + available = True + elif "--usepkg" in self.myopts and \ + self.trees[self.target_root][ + "bintree"].dbapi.match(myslot_atom): + available = True + if available: + greedy_atoms.append((myarg, myslot_atom)) + arg_atoms = greedy_atoms + + """ These are used inside self.create() in order to ensure packages + that happen to match arguments are not incorrectly marked as nomerge.""" + self.args_keys = [x[1] for x in arg_atoms] + for myarg, myatom in arg_atoms: try: - self.mysd = self.select_dep(portage.root,mykey,arg=x) + self.mysd = self.select_dep(myroot, myatom, arg=myarg) except portage_exception.MissingSignature, e: portage.writemsg("\n\n!!! A missing gpg signature is preventing portage from calculating the\n") portage.writemsg("!!! required dependencies. This is a security feature enabled by the admin\n") portage.writemsg("!!! to aid in the detection of malicious intent.\n\n") portage.writemsg("!!! THIS IS A POSSIBLE INDICATION OF TAMPERED FILES -- CHECK CAREFULLY.\n") - portage.writemsg("!!! Affected file: %s\n" % (e)) + portage.writemsg("!!! Affected file: %s\n" % (e), noiselevel=-1) sys.exit(1) except portage_exception.InvalidSignature, e: portage.writemsg("\n\n!!! An invalid gpg signature is preventing portage from calculating the\n") portage.writemsg("!!! required dependencies. This is a security feature enabled by the admin\n") portage.writemsg("!!! to aid in the detection of malicious intent.\n\n") portage.writemsg("!!! THIS IS A POSSIBLE INDICATION OF TAMPERED FILES -- CHECK CAREFULLY.\n") - portage.writemsg("!!! Affected file: %s\n" % (e)) + portage.writemsg("!!! Affected file: %s\n" % (e), noiselevel=-1) sys.exit(1) except SystemExit, e: raise # Needed else can't exit except Exception, e: - if "--debug" in myopts: - raise - print "\n\n!!! Problem in",mykey,"dependencies." - print "!!!",str(e),e.__module__ - sys.exit(1) + print >> sys.stderr, "\n\n!!! Problem in '%s' dependencies." % mykey + print >> sys.stderr, "!!!", str(e), e.__module__ + raise if not self.mysd: return (0,myfavorites) - elif not "--oneshot" in myopts: - myfavorites.append(portage.dep_getkey(mykey)) + elif not "--oneshot" in self.myopts: + mykey = portage.dep_getkey(myatom) + if mykey not in myfavorites: + myfavorites.append(mykey) missing=0 - if "--usepkgonly" in myopts: - for x in self.digraph.dict.keys(): - xs=string.split(x," ") - if (xs[0] != "binary") and (xs[3]=="merge"): + if "--usepkgonly" in self.myopts: + for x in self.digraph.all_nodes(): + xs = x.split() + if len(xs) >= 4 and xs[0] != "binary" and xs[3] == "merge": if missing == 0: print missing += 1 print "Missing binary for:",xs[2] + self.validate_blockers() + # We're true here unless we are missing binaries. return (not missing,myfavorites) def is_newer_ver_installed(self,myroot,pkg,pkgver): "if there is a version of pkg installed newer than pkgver, return it" - vardbapi=portage.db[myroot]["vartree"].dbapi + vardbapi = self.trees[myroot]["vartree"].dbapi - matches=portage.db[myroot]["vartree"].dbapi.match(pkg) + matches = self.trees[myroot]["vartree"].dbapi.match(pkg) if matches: - myslot=portage.db["/"]["porttree"].getslot(pkgver) + myslot = self.trees[myroot]["porttree"].getslot(pkgver) for match in matches: if portage.pkgcmp(portage.catpkgsplit(pkgver)[1:], portage.catpkgsplit(match)[1:]) < 0: - curslot=portage.db[myroot]["vartree"].getslot(match) + curslot = self.trees[myroot]["vartree"].getslot(match) if curslot == myslot: return match - def select_dep(self,myroot,depstring,myparent=None,arg=None,myuse=None,raise_on_missing=False): - "given a dependency string, create the appropriate depgraph and return 1 on success and 0 on failure" - if "--debug" in myopts: + def select_dep(self, myroot, depstring, myparent=None, arg=None, + myuse=None, raise_on_missing=False, priority=digraph.HARD, rev_deps=False, + parent_arg=None): + """ Given a depstring, create the depgraph such that all dependencies are satisfied. + myroot = $ROOT from environment, where {R,P}DEPENDs are merged to. + myparent = the node whose depstring is being passed in + arg = package was specified on the command line, merge even if it's already installed + myuse = USE flags at present + raise_on_missing = Given that the depgraph is not proper, raise an exception if true + else continue trying. + return 1 on success, 0 for failure + """ + + portdb = self.trees[myroot]["porttree"].dbapi + bindb = self.trees[myroot]["bintree"].dbapi + vardb = self.trees[myroot]["vartree"].dbapi + pkgsettings = self.pkgsettings[myroot] + + if "--debug" in self.myopts: print print "Parent: ",myparent print "Depstring:",depstring - if not arg: - #processing dependencies - mycheck=portage.dep_check(depstring,self.mydbapi[myroot],self.pkgsettings,myuse=myuse,use_binaries=("--usepkgonly" in myopts),myroot=myroot) - - if not mycheck[0]: - mymerge=[] - else: - mymerge=mycheck[1] - - else: - #we're processing a command-line argument; unconditionally merge it even if it's already merged - mymerge=[depstring] + if rev_deps: + print "Reverse:", rev_deps + print "Priority:", priority + + #processing dependencies + """ Call portage.dep_check to evaluate the use? conditionals and make sure all + dependencies are satisfiable. """ + mycheck = portage.dep_check(depstring, self.mydbapi[myroot], + pkgsettings, myuse=myuse, + use_binaries=("--usepkgonly" in self.myopts), + myroot=myroot, trees=self.trees, return_all_deps=True) + + if not mycheck[0]: + sys.stderr.write("\n\n%s\n\n" % mycheck[1]) + sys.stderr.write("%s\n\n" % depstring) + return 0 + mymerge = mycheck[1] - # dep_check has been run so we can now add our parent to our - # build state to update virtuals and other settings. This - # happens after the package is added to the tree so that a - # package can depend on a virtual which it satisfies. if myparent: - myp = myparent.split() - if myp[3]=="merge": - self.mydbapi[myroot].cpv_inject(myp[2]) - if myp[0]=="binary": - self.pkgsettings.setinst(myp[2],portage.db["/"]["bintree"].dbapi) - else: - self.pkgsettings.setinst(myp[2],portage.db[myroot]["porttree"].dbapi) - - if not mymerge: - return 1 - - if "--debug" in myopts: + # In order to get past circular dependencies (in + # `emerge -e world`, for example), the parent must be allowed + # to satisfy dependencies of it's children. + + # The parent is added after it's own dep_check call so that it + # isn't allowed to satisfy a direct bootstrap dependency on itself + # or an old-style virtual that it satisfies (unfortunately, this + # only works for one level of deps, so it won't work for new-style + # virtuals since they introduce an additional level of dependency + # indirection). + + p_type, p_root, p_key, p_status = myparent.split() + if p_status == "merge": + """ This fakedbapi data is used in dep_check calls to determine + satisfied deps via dep_wordreduce""" + myslot = self.trees[p_root][ + self.pkg_tree_map[p_type]].dbapi.aux_get( + p_key, ["SLOT"])[0] + self.mydbapi[p_root].cpv_inject(p_key, myslot=myslot) + + # Update old-style virtuals if this package provides any. + # These are needed for dep_virtual calls inside dep_check. + self.pkgsettings[p_root].setinst(p_key, + self.trees[p_root][self.pkg_tree_map[p_type]].dbapi) + + if "--debug" in self.myopts: print "Candidates:",mymerge for x in mymerge: - myk=None - binpkguseflags=None + selected_pkg = None if x[0]=="!": # if this package is myself, don't append it to block list. - if "--debug" in myopts: + if "--debug" in self.myopts: print "Myparent",myparent if (myparent): - if myparent.split()[2] in portage.portdb.xmatch("match-all", x[1:]): + if myparent.split()[2] in \ + portdb.xmatch("match-all", x[1:]): # myself, so exit. continue # adding block - myk=["blocks",myroot,x[1:]] + selected_pkg = ["blocks", myroot, x[1:], None] else: #We are not processing a blocker but a normal dependency - myeb=None - myeb_matches = portage.portdb.xmatch("match-visible",x) - if ("--usepkgonly" not in myopts): - myeb=portage.best(myeb_matches) + if myparent: + """In some cases, dep_check will return deps that shouldn't + be proccessed any further, so they are identified and + discarded here.""" + if "empty" not in self.myparams and \ + "deep" not in self.myparams and \ + not ("--update" in self.myopts and parent_arg) and \ + vardb.match(x): + continue + + pkg_key = portage.dep_getkey(x) + if pkg_key in pkgsettings.pprovideddict and \ + portage.match_from_list( + x, pkgsettings.pprovideddict[pkg_key]): + continue + + # List of acceptable packages, ordered by type preference. + matched_packages = [] + myeb_matches = portdb.xmatch("match-visible", x) + myeb = None + if "--usepkgonly" not in self.myopts: + myeb = portage.best(myeb_matches) myeb_pkg=None - if ("--usepkg" in myopts): + if "--usepkg" in self.myopts: # The next line assumes the binarytree has been populated. # XXX: Need to work out how we use the binary tree with roots. - myeb_pkg_matches=portage.db["/"]["bintree"].dbapi.match(x) - if ("--usepkgonly" not in myopts): + myeb_pkg_matches = bindb.match(x) + if "--usepkgonly" not in self.myopts: # Remove any binary package entries that are masked in the portage tree (#55871) - for idx in range(len(myeb_pkg_matches)-1,-1,-1): - if myeb_pkg_matches[idx] not in myeb_matches: - del myeb_pkg_matches[idx] - myeb_pkg = portage.best(myeb_pkg_matches) - - if not myeb_pkg: - myeb_pkg = None - elif ("--newuse" in myopts): - iuses=string.split(portage.db["/"]["bintree"].dbapi.aux_get(myeb_pkg, ["IUSE"])[0]) - old_use=string.split(portage.db["/"]["bintree"].dbapi.aux_get(myeb_pkg, ["USE"])[0]) - self.pkgsettings.setcpv(myeb_pkg) - now_use=string.split(self.pkgsettings["USE"]) - for x in iuses: - if (old_use.count(x) and not now_use.count(x)) or (not old_use.count(x) and now_use.count(x)): - myeb_pkg = None - break - - if (not myeb) and (not myeb_pkg): + myeb_pkg_matches = [pkg for pkg in myeb_pkg_matches \ + if pkg in myeb_matches or \ + not portdb.cpv_exists(pkg)] + if myeb_pkg_matches: + myeb_pkg = portage.best(myeb_pkg_matches) + + if myeb_pkg and "--newuse" in self.myopts: + iuses = set(bindb.aux_get(myeb_pkg, ["IUSE"])[0].split()) + old_use = bindb.aux_get(myeb_pkg, ["USE"])[0].split() + pkgsettings.setcpv(myeb_pkg) + now_use = pkgsettings["USE"].split() + forced_flags = set() + forced_flags.update(pkgsettings.useforce) + forced_flags.update(pkgsettings.puseforce) + forced_flags.update(pkgsettings.usemask) + forced_flags.update(pkgsettings.pusemask) + cur_iuse = iuses + if "--usepkgonly" not in self.myopts and myeb: + cur_iuse = set(portdb.aux_get(myeb, ["IUSE"])[0].split()) + if iuses.symmetric_difference( + cur_iuse).difference(forced_flags): + myeb_pkg = None + elif iuses.intersection(old_use) != \ + cur_iuse.intersection(now_use): + myeb_pkg = None + if myeb_pkg: + binpkguseflags = \ + self.trees[myroot]["bintree"].dbapi.aux_get( + myeb_pkg, ["USE"])[0].split() + matched_packages.append( + ["binary", myroot, myeb_pkg, binpkguseflags]) + + if "--usepkgonly" not in self.myopts and myeb_matches: + matched_packages.append( + ["ebuild", myroot, myeb, None]) + + if not matched_packages: + """Fall back to the installed package database. This is a + last resort because the metadata tends to diverge from that + of the ebuild in the tree.""" + myeb_inst_matches = vardb.match(x) + if "--usepkgonly" not in self.myopts: + """ TODO: Improve masking check for installed and + binary packages. bug #149816""" + myeb_inst_matches = [pkg for pkg in myeb_inst_matches \ + if not portdb.cpv_exists(pkg)] + myeb_inst = None + if myeb_inst_matches: + myeb_inst = portage.best(myeb_inst_matches) + if myeb_inst: + binpkguseflags = vardb.aux_get( + myeb_inst, ["USE"])[0].split() + matched_packages.append( + ["installed", myroot, myeb_inst, binpkguseflags]) + """Now make sure that *DEPEND atoms are up to date. + This is done on the fly for single packages only when + necessary, since it can be time consuming to run this + on all installed packages.""" + from portage_update import grab_updates, \ + parse_updates, update_dbentries + if myroot not in self.global_updates: + updpath = os.path.join( + pkgsettings["PORTDIR"], "profiles", "updates") + try: + rawupdates = grab_updates(updpath) + except portage_exception.DirectoryNotFound: + rawupdates = [] + upd_commands = [] + for mykey, mystat, mycontent in rawupdates: + commands, errors = parse_updates(mycontent) + upd_commands.extend(commands) + del updpath, rawupdates + self.global_updates[myroot] = upd_commands + upd_commands = self.global_updates[myroot] + aux_keys = ["DEPEND", "RDEPEND", "PDEPEND"] + aux_vals = vardb.aux_get(myeb_inst, aux_keys) + aux_dict = dict(zip(aux_keys, aux_vals)) + updates = update_dbentries(upd_commands, aux_dict) + if updates: + vardb.aux_update(myeb_inst, updates) + del binpkguseflags, myeb_inst, upd_commands, \ + aux_keys, aux_vals, aux_dict, updates + + if not matched_packages: if raise_on_missing: raise ValueError if not arg: @@ -1248,16 +1242,18 @@ class depgraph: xinfo='"'+arg+'"' if myparent: xfrom = '(dependency required by '+green('"'+myparent.split()[2]+'"')+red(' ['+myparent.split()[0]+"]")+')' - alleb=portage.portdb.xmatch("match-all",x) + alleb = portdb.xmatch("match-all", x) if alleb: - if "--usepkgonly" not in myopts: + if "--usepkgonly" not in self.myopts: print "\n!!! "+red("All ebuilds that could satisfy ")+green(xinfo)+red(" have been masked.") print "!!! One of the following masked packages is required to complete your request:" oldcomment = "" for p in alleb: - mreasons = portage.getmaskingstatus(p) + mreasons = portage.getmaskingstatus(p, + settings=pkgsettings, portdb=portdb) print "- "+p+" (masked by: "+string.join(mreasons, ", ")+")" - comment = portage.getmaskingreason(p) + comment = portage.getmaskingreason(p, + settings=pkgsettings, portdb=portdb) if comment and comment != oldcomment: print comment oldcomment = comment @@ -1274,102 +1270,136 @@ class depgraph: print return 0 - if "--debug" in myopts: - print "ebuild:",myeb - print "binpkg:",myeb_pkg + if "--debug" in self.myopts: + for pkg in matched_packages: + print (pkg[0] + ":").rjust(10), pkg[2] - if myeb and myeb_pkg: - myeb_s = portage.catpkgsplit(myeb) - myeb_s = [myeb_s[0]+"/"+myeb_s[1], myeb_s[2], myeb_s[3]] - myeb_pkg_s = portage.catpkgsplit(myeb_pkg) - myeb_pkg_s = [myeb_pkg_s[0]+"/"+myeb_pkg_s[1], myeb_pkg_s[2], myeb_pkg_s[3]] + if len(matched_packages) > 1: + bestmatch = portage.best( + [pkg[2] for pkg in matched_packages]) + matched_packages = [pkg for pkg in matched_packages \ + if pkg[2] == bestmatch] - if portage.pkgcmp(myeb_s, myeb_pkg_s) == 0: # pkg is same version as ebuild - myeb = None - else: - myeb_pkg = None + # ordered by type preference ("ebuild" type is the last resort) + selected_pkg = matched_packages[0] - if myeb: - myk=["ebuild",myroot,myeb] - elif myeb_pkg: - binpkguseflags=portage.db[portage.root]["bintree"].get_use(myeb_pkg) - myk=["binary",myroot,myeb_pkg] - else: - sys.stderr.write("!!! Confused... Don't know what's being used for dependency info. :(\n") - sys.exit(1) - - #if "--usepkg" in myopts: - # #If we want to use packages, see if we have a pre-built one... - # mypk=portage.db["/"]["bintree"].dbapi.match(x) - # if myeb in mypk: - # #Use it only if it's exactly the version we want. - # myk=["binary",myroot,myeb] - # else: - # myk=["ebuild",myroot,myeb] - #else: - # myk=["ebuild",myroot,myeb] if myparent: #we are a dependency, so we want to be unconditionally added - if not self.create(myk,myparent,myuse=binpkguseflags): + mypriority = priority + if mypriority > digraph.SOFT and vardb.match(x): + mypriority = digraph.SOFT + if not self.create(selected_pkg[0:3], myparent, + myuse=selected_pkg[-1], priority=mypriority, + rev_dep=rev_deps, arg=arg): return 0 else: #if mysource is not set, then we are a command-line dependency and should not be added #if --onlydeps is specified. - if not self.create(myk,myparent,"--onlydeps" not in myopts,myuse=binpkguseflags): + if not self.create(selected_pkg[0:3], myparent, + addme=("--onlydeps" not in self.myopts), + myuse=selected_pkg[-1], rev_dep=rev_deps, arg=arg): return 0 - if "--debug" in myopts: + if "--debug" in self.myopts: print "Exiting...",myparent return 1 - - def altlist(self): + def validate_blockers(self): + """Remove any blockers from the digraph that do not match any of the + packages within the graph. Blockers are only matched against the + final state of the graph. Thus, it's possible that mutually blocking + packages will be installed simultaneously a some point(s) during the + transition from the initial to the final state.""" + + """ It's possible that some of the nodes haven't been added to the + fakedb yet, so make sure they're all accounted for.""" + for node in self.digraph.all_nodes(): + node_split = node.split() + mytype, myroot, mykey = node_split[0:3] + if mytype in self.pkg_tree_map: + mydb = self.trees[myroot][self.pkg_tree_map[mytype]].dbapi + myslot = mydb.aux_get(mykey, ["SLOT"])[0] + self.mydbapi[myroot].cpv_inject(mykey, myslot=myslot) + + all_blockers = [node for node in self.digraph.all_nodes() + if node.split()[0] == "blocks"] + for blocker in all_blockers: + mytype, myroot, mydep = blocker.split() + """Prior to being added to the digraph, any blockers against + old-style virtuals have been expanded to real packages via + dep_virtual calls inside dep_check.""" + if not self.mydbapi[myroot].match(mydep): + self.digraph.remove(blocker) + + def altlist(self, reversed=False): mygraph=self.digraph.copy() - dolist=["/"] retlist=[] - for x in portage.db.keys(): - portage.db[x]["merge"]=[] - if x not in dolist: - dolist.append(x) - while (not mygraph.empty()): - mycurkey=mygraph.firstzero() - if not mycurkey: + if reversed: + get_nodes = mygraph.root_nodes + else: + get_nodes = mygraph.leaf_nodes + while not mygraph.empty(): + for ignore_priority in \ + (digraph.NONE, digraph.SOFT, digraph.MEDIUM): + nodes = get_nodes(ignore_priority=ignore_priority) + if nodes: + break + selected_nodes = None + if nodes: + if ignore_priority <= digraph.SOFT: + selected_nodes = [nodes[0]] + else: + """Recursively gather a group of nodes that RDEPEND on + eachother. This ensures that they are merged as a group + and get their RDEPENDs satisfied as soon as possible.""" + def gather_deps(mergeable_nodes, selected_nodes, node): + if node in selected_nodes: + return True + if node not in mergeable_nodes: + return False + selected_nodes.add(node) + for child in mygraph.child_nodes(node, + ignore_priority=digraph.SOFT): + if not gather_deps( + mergeable_nodes, selected_nodes, child): + return False + return True + mergeable_nodes = set(nodes) + for node in nodes: + selected_nodes = set() + if gather_deps( + mergeable_nodes, selected_nodes, node): + break + else: + selected_nodes = None + + if not selected_nodes: print "!!! Error: circular dependencies:" print - for x in mygraph.dict.keys(): - for y in mygraph.dict[x][1]: - print y,"depends on",x - print + mygraph.debug_print() sys.exit(1) - splitski=string.split(mycurkey) - #I'm not sure of the significance of the following lines (vestigal?) so I'm commenting 'em out. - #These lines remove already-merged things from our alt-list - #if "--update" in myopts: - # if not portage.db["/"]["vartree"].exists_specific(splitski[2]): - # portage.db["/"]["merge"].append(splitski) - #else: - portage.db[splitski[1]]["merge"].append(splitski) - mygraph.delnode(mycurkey) - for x in dolist: - for y in portage.db[x]["merge"]: - retlist.append(y) + + for node in selected_nodes: + retlist.append(node.split()) + mygraph.remove(node) + return retlist def xcreate(self,mode="system"): - global syslist + vardb = self.trees[self.target_root]["vartree"].dbapi world_problems = False if mode=="system": - mylist=syslist + mylist = getlist(self.settings, "system") else: #world mode - worldlist=getlist("world") - sysdict=genericdict(syslist) + worldlist = getlist(self.settings, "world") + sysdict = genericdict(getlist(self.settings, "system")) worlddict=genericdict(worldlist) for x in worlddict.keys(): if not portage.isvalidatom(x): world_problems = True - elif not portage.db["/"]["vartree"].dbapi.match(x): + elif not self.trees[self.target_root]["vartree"].dbapi.match(x): world_problems = True else: sysdict[x]=worlddict[x] @@ -1378,36 +1408,61 @@ class depgraph: newlist = [] for atom in mylist: - if portage.dep_getkey(atom).split("/")[-1] == "portage": + mykey = portage.dep_getkey(atom) + if portage.catsplit(mykey)[-1] == "portage": newlist.insert(0, atom) else: newlist.append(atom) + """Make sure all installed slots are updated when possible. + Do this with --emptytree also, to ensure that all slots are + remerged.""" + myslots = set() + for cpv in vardb.match(mykey): + myslots.add(vardb.aux_get(cpv, ["SLOT"])[0]) + if len(myslots) > 1: + for myslot in myslots: + myslot_atom = "%s:%s" % (mykey, myslot) + available = False + if "--usepkgonly" not in self.myopts and \ + self.trees[self.target_root][ + "porttree"].dbapi.match(myslot_atom): + available = True + elif "--usepkg" in self.myopts and \ + self.trees[self.target_root][ + "bintree"].dbapi.match(myslot_atom): + available = True + if available: + newlist.append(myslot_atom) mylist = newlist missing_atoms = [] for mydep in mylist: try: - if not self.select_dep(portage.root, mydep, raise_on_missing=True): - print "\n\n!!! Problem resolving dependencies for", mydep + if not self.select_dep( + self.target_root, mydep, raise_on_missing=True, arg=mydep): + print >> sys.stderr, "\n\n!!! Problem resolving dependencies for", mydep return 0 except ValueError: - if "--debug" in myopts: - raise missing_atoms.append(mydep) + self.validate_blockers() + if world_problems: - print "\n!!! Problems have been detected with your world file" - print "!!! Please run "+green("emaint --check world")+"\n" + print >> sys.stderr, "\n!!! Problems have been detected with your world file" + print >> sys.stderr, "!!! Please run "+green("emaint --check world")+"\n" - if missing_atoms and "--verbose" in myopts: - print "\n!!! Packages for the following atoms are either all" - print "!!! masked or don't exist:" - print " ".join(missing_atoms) + "\n" + if missing_atoms: + print >> sys.stderr, "\n" + colorize("BAD", "!!!") + \ + " Ebuilds for the following packages are either all" + print >> sys.stderr, colorize("BAD", "!!!") + " masked or don't exist:" + print >> sys.stderr, " ".join(missing_atoms) + "\n" return 1 - def match(self,mydep,myroot=portage.root,mykey=None): + def match(self, mydep, myroot=None, mykey=None): # support mutual exclusive deps + if myroot is None: + myroot = self.target_root mydep2=mydep if mydep2[0]=="!": mydep2=mydep[1:] @@ -1416,7 +1471,7 @@ class depgraph: #add our blocker; it will be ignored later if necessary (if we are remerging the same pkg, for example) myk="blocks "+myroot+" "+mydep2 else: - myeb=portage.db[portage.root]["porttree"].dep_bestmatch(mydep2) + myeb = self.trees[myroot]["porttree"].dep_bestmatch(mydep2) if not myeb: if not mykey: print "\n!!! Error: couldn't find match for",mydep @@ -1425,90 +1480,124 @@ class depgraph: print sys.exit(1) - if "--usepkg" in myopts: - mypk=portage.db[portage.root]["bintree"].dep_bestmatch(mydep) + if "--usepkg" in self.myopts: + mypk = self.trees[myroot]["bintree"].dep_bestmatch(mydep) if myeb==mypk: - myk="binary "+portage.root+" "+mypk + myk = " ".join(("binary", myroot, mypk)) else: - myk="ebuild "+myroot+" "+myeb + myk = " ".join(("ebuild", myroot, myeb)) else: myk="ebuild "+myroot+" "+myeb return myk - def display(self,mylist,verbosity=("--quiet" in myopts and 1 or "--verbose" in myopts and 3 or 2)): + def display(self,mylist,verbosity=None): + if verbosity is None: + verbosity = ("--quiet" in self.myopts and 1 or \ + "--verbose" in self.myopts and 3 or 2) changelogs=[] p=[] totalsize=0 - if verbosity == 1: + if verbosity == 1 and "--verbose" not in self.myopts: def create_use_string(*args): return "" else: - def create_use_string(name, cur_iuse, cur_use, old_iuse, old_use, is_new, - all_flags=(verbosity == 3), alphabetical=("--alphabetical" in myopts)): + def create_use_string(name, cur_iuse, iuse_forced, cur_use, + old_iuse, old_use, + is_new, all_flags=(verbosity == 3 or "--quiet" in self.myopts), + alphabetical=("--alphabetical" in self.myopts)): enabled = [] if alphabetical: disabled = enabled + removed = enabled else: disabled = [] - for flag in cur_iuse: - if flag in cur_use: + removed = [] + cur_iuse = set(cur_iuse) + enabled_flags = cur_iuse.intersection(cur_use) + removed_iuse = set(old_iuse).difference(cur_iuse) + any_iuse = cur_iuse.union(old_iuse) + any_iuse = list(any_iuse) + any_iuse.sort() + for flag in any_iuse: + flag_str = None + isEnabled = False + if flag in enabled_flags: + isEnabled = True if is_new or flag in old_use and all_flags: - enabled.append(red(flag)) + flag_str = red(flag) elif flag not in old_iuse: - enabled.append(yellow(flag)+"%") + flag_str = yellow(flag) + "%*" elif flag not in old_use: - enabled.append(green(flag)+"*") + flag_str = green(flag) + "*" + elif flag in removed_iuse: + if all_flags: + flag_str = yellow("-" + flag) + "%" + if flag in old_use: + flag_str += "*" + flag_str = "(" + flag_str + ")" + removed.append(flag_str) + continue else: if is_new or flag in old_iuse and flag not in old_use and all_flags: - disabled.append(blue("-"+flag)) + flag_str = blue("-" + flag) elif flag not in old_iuse: - disabled.append(yellow("-"+flag)+"%") + flag_str = yellow("-" + flag) + if flag not in iuse_forced: + flag_str += "%" elif flag in old_use: - disabled.append(green("-"+flag)+"*") + flag_str = green("-" + flag) + "*" + if flag_str: + if flag in iuse_forced: + flag_str = "(" + flag_str + ")" + if isEnabled: + enabled.append(flag_str) + else: + disabled.append(flag_str) - enabled = " ".join(enabled) if alphabetical: - disabled = "" - else: - disabled = " ".join(disabled) - if enabled and disabled: - ret = enabled + " " + disabled - elif enabled: - ret = enabled + ret = " ".join(enabled) else: - ret = disabled + ret = " ".join(enabled + disabled + removed) if ret: ret = '%s="%s" ' % (name, ret) return ret if verbosity == 3: - overlays = string.split(portage.settings['PORTDIR_OVERLAY']) - - if "--tree" in myopts: - mylist.reverse() - mygraph=self.digraph.copy() - + # FIXME: account for the possibility of different overlays in + # /etc/make.conf vs. ${PORTAGE_CONFIGROOT}/etc/make.conf + overlays = self.settings["PORTDIR_OVERLAY"].split() + overlays_real = [os.path.realpath(t) \ + for t in self.settings["PORTDIR_OVERLAY"].split()] + + tree_nodes = [] + node_depth = {} i = 0 - while i < len(mylist): - if mylist[i][-1]=="nomerge": - if not ("--tree" in myopts): - # we don't care about this elements - mylist.pop(i) - continue - if (i == (len(mylist) - 1)) \ - or (mygraph.depth(string.join(mylist[i])) \ - >= mygraph.depth(string.join(mylist[i+1]))): - # end of a useless branch (may be the last one) - # -> delete the element and test the previous one - mylist.pop(i) - if i > 0: - i -= 1 - continue - # the branch continues, or we've found a good element. - # -> let's see what's next, if anything - i += 1 + depth = 0 + for x in mylist: + graph_key = " ".join(x) + if "--tree" in self.myopts: + depth = len(tree_nodes) + while depth and graph_key not in \ + self.digraph.child_nodes(tree_nodes[depth-1]): + depth -= 1 + tree_nodes = tree_nodes[:depth] + tree_nodes.append(graph_key) + node_depth[graph_key] = depth + + last_merge_depth = 0 + for i in xrange(len(mylist)-1,-1,-1): + graph_key = " ".join(mylist[i]) + if mylist[i][-1] != "nomerge": + last_merge_depth = node_depth[graph_key] + continue + if node_depth[graph_key] >= last_merge_depth or \ + i < len(mylist) - 1 and \ + node_depth[graph_key] >= node_depth[" ".join(mylist[i+1])]: + del mylist[i] + del node_depth[graph_key] + del tree_nodes display_overlays=False # files to fetch list - avoids counting a same file twice @@ -1516,48 +1605,57 @@ class depgraph: myfetchlist=[] for x in mylist: pkg_type = x[0] + myroot = x[1] pkg_key = x[2] - if pkg_key not in self.applied_useflags: + portdb = self.trees[myroot]["porttree"].dbapi + bindb = self.trees[myroot]["bintree"].dbapi + vartree = self.trees[myroot]["vartree"] + pkgsettings = self.pkgsettings[myroot] + pkgsettings.setcpv(pkg_key) + if pkg_key not in self.useFlags[myroot]: if "binary" == pkg_type: - self.applied_useflags[pkg_key] = portage.db["/"]["bintree"].dbapi.aux_get(pkg_key, ["USE"])[0].split() + self.useFlags[myroot][pkg_key] = bindb.aux_get( + pkg_key, ["USE"])[0].split() elif "ebuild" == pkg_type: - self.pkgsettings.setcpv(pkg_key) - self.applied_useflags[pkg_key] = self.pkgsettings["USE"].split() + self.useFlags[myroot][pkg_key] = pkgsettings["USE"].split() fetch=" " if x[0]=="blocks": addl=""+red("B")+" "+fetch+" " - resolved=portage.db[x[1]]["vartree"].resolve_key(x[2]) + resolved = self.trees[x[1]]["vartree"].resolve_key(x[2]) print "["+x[0]+" "+addl+"]",red(resolved), + block_parents = self.digraph.parent_nodes(" ".join(x)) + block_parents = [pnode.split()[2] for pnode in block_parents] + block_parents = ", ".join(block_parents) if resolved!=x[2]: - if x[3]: - print red("(\""+x[2]+"\" is blocking "+x[3]+")") - else: - print red("(\""+x[2]+"\")") + print bad("(\"%s\" is blocking %s)") % \ + (pkg_key, block_parents) else: - if x[3]: - print red("(is blocking "+x[3]+")") - else: - print + print bad("(is blocking %s)") % block_parents else: - if (x[0]!="binary") and ("fetch" in string.split(portage.portdb.aux_get(x[2],["RESTRICT"])[0])): + mydbapi = self.trees[myroot][self.pkg_tree_map[pkg_type]].dbapi + if "ebuild" == pkg_type and x[3] != "nomerge" and \ + "fetch" in portdb.aux_get( + x[2], ["RESTRICT"])[0].split(): fetch = red("F") - if portage.portdb.fetch_check(x[2], self.applied_useflags[x[2]]): + if portdb.fetch_check( + pkg_key, self.useFlags[myroot][pkg_key]): fetch = green("f") #we need to use "--emptrytree" testing here rather than "empty" param testing because "empty" #param is used for -u, where you still *do* want to see when something is being upgraded. myoldbest="" - if (not "--emptytree" in myopts) and portage.db[x[1]]["vartree"].exists_specific(x[2]): + if "--emptytree" not in self.myopts and \ + self.trees[x[1]]["vartree"].exists_specific(x[2]): addl=" "+yellow("R")+fetch+" " - elif (not "--emptytree" in myopts) and portage.db[x[1]]["vartree"].exists_specific_cat(x[2]): - if x[0] == "binary": - mynewslot=portage.db["/"]["bintree"].getslot(x[2]) - elif x[0] == "ebuild": - mynewslot=portage.db["/"]["porttree"].getslot(x[2]) - myoldlist=portage.db[x[1]]["vartree"].dbapi.match(portage.pkgsplit(x[2])[0]) - myinslotlist=filter((lambda p: portage.db[portage.root]["vartree"].getslot(p)==mynewslot),myoldlist) + elif "--emptytree" not in self.myopts and \ + self.trees[x[1]]["vartree"].exists_specific_cat(x[2]): + mynewslot = mydbapi.aux_get(pkg_key, ["SLOT"])[0] + myoldlist = self.trees[x[1]]["vartree"].dbapi.match( + portage.pkgsplit(x[2])[0]) + myinslotlist = [inst_pkg for inst_pkg in myoldlist + if mynewslot == vartree.getslot(inst_pkg)] if myinslotlist: myoldbest=portage.best(myinslotlist) addl=" "+fetch @@ -1571,38 +1669,39 @@ class depgraph: # New slot, mark it new. addl=" "+green("NS")+fetch+" " - if "--changelog" in myopts: + if "--changelog" in self.myopts: changelogs.extend(self.calc_changelog( - portage.portdb.findname(x[2]), - portage.db[x[1]]["vartree"].dep_bestmatch('/'.join(portage.catpkgsplit(x[2])[:2])), - x[2] - )) + portdb.findname(pkg_key), + self.trees[x[1]]["vartree"].dep_bestmatch( + '/'.join(portage.catpkgsplit(x[2])[:2])), x[2])) else: addl=" "+green("N")+" "+fetch+" " verboseadd="" - if x[2] in self.applied_useflags: + if pkg_key in self.useFlags[myroot]: # USE flag display - if x[0] == "binary": - cur_iuse = string.split(portage.db["/"]["bintree"].dbapi.aux_get(x[2],["IUSE"])[0]) - elif x[0] == "ebuild": - cur_iuse = string.split(portage.portdb.aux_get(x[2],["IUSE"])[0]) - else: - cur_iuse = [] + cur_iuse = mydbapi.aux_get(pkg_key, ["IUSE"])[0].split() + + forced_flags = set() + forced_flags.update(pkgsettings.useforce) + forced_flags.update(pkgsettings.puseforce) + forced_flags.update(pkgsettings.usemask) + forced_flags.update(pkgsettings.pusemask) cur_iuse = portage.unique_array(cur_iuse) - cur_iuse = [flag for flag in cur_iuse if flag not in portage.settings.usemask] cur_iuse.sort() - cur_use = self.applied_useflags[x[2]] + cur_use = self.useFlags[myroot][pkg_key] cur_use = [flag for flag in cur_use if flag in cur_iuse] if myoldbest: pkg = myoldbest else: pkg = x[2] - if portage.db[x[1]]["vartree"].dbapi.cpv_exists(pkg): - (old_iuse, old_use) = portage.db[x[1]]["vartree"].dbapi.aux_get(pkg, ["IUSE", "USE"]) + if self.trees[x[1]]["vartree"].dbapi.cpv_exists(pkg): + old_iuse, old_use = \ + self.trees[x[1]]["vartree"].dbapi.aux_get( + pkg, ["IUSE", "USE"]) old_iuse = portage.unique_array(old_iuse.split()) old_iuse.sort() old_use = old_use.split() @@ -1611,29 +1710,39 @@ class depgraph: old_iuse = [] old_use = [] is_new = True - old_iuse = [flag for flag in old_iuse if flag not in portage.settings.usemask] + old_use = [flag for flag in old_use if flag in old_iuse] - use_expand = portage.settings["USE_EXPAND"].lower().split() + use_expand = pkgsettings["USE_EXPAND"].lower().split() use_expand.sort() use_expand.reverse() - use_expand_hidden = portage.settings["USE_EXPAND_HIDDEN"].lower().split() + use_expand_hidden = \ + pkgsettings["USE_EXPAND_HIDDEN"].lower().split() - def map_to_use_expand(myvals): + def map_to_use_expand(myvals, forcedFlags=False): ret = {} + forced = {} for exp in use_expand: ret[exp] = [] + forced[exp] = set() for val in myvals[:]: if val.startswith(exp.lower()+"_"): + if val in forced_flags: + forced[exp].add(val[len(exp)+1:]) ret[exp].append(val[len(exp)+1:]) myvals.remove(val) ret["USE"] = myvals + forced["USE"] = [val for val in myvals \ + if val in forced_flags] for exp in use_expand_hidden: if exp in ret: del ret[exp] + if forcedFlags: + return ret, forced return ret - cur_iuse_map = map_to_use_expand(cur_iuse) + cur_iuse_map, iuse_forced = \ + map_to_use_expand(cur_iuse, forcedFlags=True) cur_use_map = map_to_use_expand(cur_use) old_iuse_map = map_to_use_expand(old_iuse) old_use_map = map_to_use_expand(old_use) @@ -1642,15 +1751,21 @@ class depgraph: use_expand.insert(0, "USE") for key in use_expand: - verboseadd += create_use_string(key.upper(), cur_iuse_map[key], cur_use_map[key], - old_iuse_map[key], old_use_map[key], is_new) + if key in use_expand_hidden: + continue + verboseadd += create_use_string(key.upper(), + cur_iuse_map[key], iuse_forced[key], + cur_use_map[key], old_iuse_map[key], + old_use_map[key], is_new) if verbosity == 3: # size verbose mysize=0 if x[0] == "ebuild" and x[-1]!="nomerge": - myfilesdict=portage.portdb.getfetchsizes(x[2], useflags=self.applied_useflags[x[2]], debug=edebug) - if myfilesdict==None: + myfilesdict = portdb.getfetchsizes( + pkg_key, useflags=self.useFlags[myroot][pkg_key], + debug=self.edebug) + if myfilesdict is None: myfilesdict="[empty/missing/bad digest]" else: for myfetchfile in myfilesdict.keys(): @@ -1663,33 +1778,36 @@ class depgraph: # overlay verbose # XXX: Invalid binaries have caused tracebacks here. 'if file_name' # x = ['binary', '/', 'sys-apps/pcmcia-cs-3.2.7.2.6', 'merge'] - file_name=portage.portdb.findname(x[2]) + file_name = portdb.findname(pkg_key) if file_name: # It might not exist in the tree dir_name=os.path.abspath(os.path.dirname(file_name)+"/../..") - if (overlays.count(dir_name)>0): - verboseadd+=teal("["+str(overlays.index(os.path.normpath(dir_name))+1)+"]")+" " + if (overlays_real.count(dir_name)>0): + verboseadd+=teal("["+str(overlays_real.index( + os.path.normpath(dir_name))+1)+"]")+" " display_overlays=True else: verboseadd += "[No ebuild?]" - verboseadd+="EAPI: "+blue(str(portage.portdb.aux_get(x[2],["EAPI"])[0])) - xs=portage.pkgsplit(x[2]) if xs[2]=="r0": xs[2]="" else: xs[2]="-"+xs[2] - if self.pkgsettings.has_key("COLUMNWIDTH"): - mywidth=int(self.pkgsettings.settings["COLUMNWIDTH"]) - else: - mywidth=130 + mywidth = 130 + if "COLUMNWIDTH" in self.settings: + try: + mywidth = int(self.settings["COLUMNWIDTH"]) + except ValueError, e: + portage.writemsg("!!! %s\n" % str(e), noiselevel=-1) + portage.writemsg( + "!!! Unable to parse COLUMNWIDTH='%s'\n" % \ + self.settings["COLUMNWIDTH"], noiselevel=-1) + del e oldlp=mywidth-30 newlp=oldlp-30 - indent="" - if ("--tree" in myopts): - indent=" "*mygraph.depth(string.join(x)) + indent = " " * node_depth[" ".join(x)] if myoldbest: myoldbest=portage.pkgsplit(myoldbest)[1]+"-"+portage.pkgsplit(myoldbest)[2] @@ -1698,12 +1816,14 @@ class depgraph: myoldbest=blue("["+myoldbest+"]") if x[1]!="/": - if "--columns" in myopts: - if "--quiet" in myopts: + if myoldbest: + myoldbest +=" " + if "--columns" in self.myopts: + if "--quiet" in self.myopts: myprint=addl+" "+indent+darkgreen(xs[0]) - myprint=myprint+darkblue(xs[1]+xs[2])+" " + myprint=myprint+darkblue(" "+xs[1]+xs[2])+" " myprint=myprint+myoldbest - myprint=myprint+darkgreen(" to "+x[1]) + myprint=myprint+darkgreen("to "+x[1]) else: myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(xs[0]) if (newlp-nc_len(myprint)) > 0: @@ -1712,12 +1832,12 @@ class depgraph: if (oldlp-nc_len(myprint)) > 0: myprint=myprint+" "*(oldlp-nc_len(myprint)) myprint=myprint+myoldbest - myprint=myprint+darkgreen(" to "+x[1])+" "+verboseadd + myprint=myprint+darkgreen("to "+x[1])+" "+verboseadd else: - myprint="["+x[0]+" "+addl+"] "+darkgreen(x[2])+" "+myoldbest+" "+darkgreen("to "+x[1])+" "+verboseadd + myprint="["+x[0]+" "+addl+"] "+darkgreen(x[2])+" "+myoldbest+darkgreen("to "+x[1])+" "+verboseadd else: - if "--columns" in myopts: - if "--quiet" in myopts: + if "--columns" in self.myopts: + if "--quiet" in self.myopts: myprint=addl+" "+indent+darkgreen(xs[0]) myprint=myprint+" "+green(xs[1]+xs[2])+" " myprint=myprint+myoldbest @@ -1736,27 +1856,28 @@ class depgraph: myprint="["+x[0]+" "+addl+"] "+indent+darkgreen(x[2])+" "+myoldbest+" "+verboseadd p.append(myprint) - if ("--tree" not in myopts): - mysplit=portage.pkgsplit(x[2]) - - # XXX mysplit _can_ be None.... Why? - if mysplit and (len(mysplit)==3): - if "--emptytree" not in myopts: - if mysplit[0]=="sys-apps/portage": - if ((mysplit[1]+mysplit[2]) != portage.VERSION) and \ - ("livecvsportage" not in portage.settings.features): - if mylist.index(x)>> Recording",myfavkey,"in \"world\" favorites file..." - if not "--fetchonly" in myopts: + if not ("--fetchonly" in self.myopts or \ + "--fetch-all-uri" in self.myopts or \ + "--pretend" in self.myopts): portage.write_atomic( - os.path.join(portage.root, portage.WORLD_FILE), - "\n".join(myfavdict.values())) - - portage.mtimedb["resume"]["mergelist"]=mymergelist[:] - - # We need to yank the harmful-to-new-builds settings from features. - myorigfeat=self.pkgsettings["FEATURES"] - myfeat=myorigfeat.split() - while ("keeptemp" in myfeat): - del myfeat[myfeat.index("keeptemp")] - while ("keepwork" in myfeat): - del myfeat[myfeat.index("keepwork")] - - self.pkgsettings["FEATURES"]=string.join(myfeat) - - if "parallel-fetch" in myfeat and not ("--ask" in myopts or "--pretend" in myopts or "--fetchonly" in myopts): + os.path.join(self.target_root, portage.WORLD_FILE), + "\n".join(myfavdict.values())) + + mtimedb["resume"]["mergelist"]=mymergelist[:] + mtimedb.commit() + + myfeat = self.settings.features[:] + bad_resume_opts = set(["--ask", "--tree", "--changelog", "--skipfirst", + "--resume"]) + if "parallel-fetch" in myfeat and \ + not ("--pretend" in self.myopts or \ + "--fetch-all-uri" in self.myopts or \ + "--fetchonly" in self.myopts): if "distlocks" not in myfeat: print red("!!!") print red("!!!")+" parallel-fetching requires the distlocks feature enabled" @@ -1904,245 +2044,329 @@ class depgraph: print red("!!!") elif len(mymergelist) > 1: print ">>> starting parallel fetching" - pid = os.fork() - if not pid: - sys.stdin.close() - sys.stdout.close() - sys.stderr.close() - time.sleep(3) # allow the parent to have first fetch - sys.stdout = open("/dev/null","w") - sys.stderr = open("/dev/null","w") - os.dup2(sys.stdout.fileno(), 1) - os.dup2(sys.stdout.fileno(), 2) - # wipe the mtimedb so that portage doesn't attempt to flush it. - # do not convert this code away from a fork without correcting this. - portage.mtimedb = None - for x in ("autoaddcvs", "cvs"): - try: myfeat.remove(x) - except ValueError: pass - self.pkgsettings["FEATURES"] = " ".join(myfeat) - ret = 0 - for x in mymergelist: - if x[0] != "ebuild": - continue - try: - ret = portage.doebuild(portage.portdb.findname(x[2]), "fetch", x[1], self.pkgsettings, - cleanup=0, fetchonly=True, tree="porttree") - except SystemExit: - raise - except Exception: - ret = 1 - sys.exit(0) - portage.portage_exec.spawned_pids.append(pid) + fetch_log = "/var/log/emerge-fetch.log" + logfile = open(fetch_log, "w") + fd_pipes = {1:logfile.fileno(), 2:logfile.fileno()} + portage_util.apply_secpass_permissions(fetch_log, + uid=portage.portage_uid, gid=portage.portage_gid, + mode=0660) + fetch_env = os.environ.copy() + fetch_env["FEATURES"] = fetch_env.get("FEATURES", "") + " -cvs" + fetch_env["PORTAGE_NICENESS"] = "0" + fetch_args = [sys.argv[0], "--resume", "--fetchonly"] + for myopt, myarg in self.myopts.iteritems(): + if myopt not in bad_resume_opts: + if myarg is True: + fetch_args.append(myopt) + else: + fetch_args.append(myopt +"="+ myarg) + portage.portage_exec.spawn(fetch_args, env=fetch_env, + fd_pipes=fd_pipes, returnpid=True) + logfile.close() # belongs to the spawned process mergecount=0 for x in mymergelist: mergecount+=1 myroot=x[1] + pkg_key = x[2] pkgindex=2 + portdb = self.trees[myroot]["porttree"].dbapi + bindb = self.trees[myroot]["bintree"].dbapi + vartree = self.trees[myroot]["vartree"] + pkgsettings = self.pkgsettings[myroot] if x[0]=="blocks": pkgindex=3 - y=portage.portdb.findname(x[pkgindex]) - if not "--pretend" in myopts: - print ">>> Emerging ("+str(mergecount)+" of "+str(len(mymergelist))+")",x[pkgindex],"to",x[1] - emergelog(" >>> emerge ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" to "+x[1]) - - self.pkgsettings["EMERGE_FROM"] = x[0][:] - self.pkgsettings.backup_changes("EMERGE_FROM") - self.pkgsettings.reset() + y = portdb.findname(pkg_key) + if "--pretend" not in self.myopts: + print "\n>>> Emerging (" + \ + colorize("MERGE_LIST_PROGRESS", str(mergecount)) + " of " + \ + colorize("MERGE_LIST_PROGRESS", str(len(mymergelist))) + ") " + \ + colorize("GOOD", x[pkgindex]) + " to " + x[1] + emergelog(xterm_titles, " >>> emerge ("+\ + str(mergecount)+" of "+str(len(mymergelist))+\ + ") "+x[pkgindex]+" to "+x[1]) + + pkgsettings["EMERGE_FROM"] = x[0] + pkgsettings.backup_changes("EMERGE_FROM") + pkgsettings.reset() #buildsyspkg: Check if we need to _force_ binary package creation issyspkg = ("buildsyspkg" in myfeat) \ and x[0] != "blocks" \ and mysysdict.has_key(portage.cpv_getkey(x[2])) \ - and not ("--buildpkg" in myopts) + and "--buildpkg" not in self.myopts if x[0] in ["ebuild","blocks"]: - if (x[0]=="blocks") and ("--fetchonly" not in myopts): + if x[0] == "blocks" and "--fetchonly" not in self.myopts: raise Exception, "Merging a blocker" - elif ("--fetchonly" in myopts) or ("--fetch-all-uri" in myopts): - if ("--fetch-all-uri" in myopts): - retval=portage.doebuild(y,"fetch",myroot,self.pkgsettings,edebug,("--pretend" in myopts),fetchonly=1,fetchall=1,tree="porttree") + elif "--fetchonly" in self.myopts or \ + "--fetch-all-uri" in self.myopts: + if "--fetch-all-uri" in self.myopts: + retval = portage.doebuild(y, "fetch", myroot, + pkgsettings, self.edebug, + "--pretend" in self.myopts, fetchonly=1, + fetchall=1, mydbapi=portdb, tree="porttree") else: - retval=portage.doebuild(y,"fetch",myroot,self.pkgsettings,edebug,("--pretend" in myopts),fetchonly=1,tree="porttree") - if (retval == None) or retval: + retval = portage.doebuild(y, "fetch", myroot, + pkgsettings, self.edebug, + "--pretend" in self.myopts, fetchonly=1, + mydbapi=portdb, tree="porttree") + if (retval is None) or retval: print print "!!! Fetch for",y,"failed, continuing..." print returnme=1 continue - elif "--buildpkg" in myopts or issyspkg: - #buildsyspkg: Sounds useful to display something, but I don't know if we should also log it - if issyspkg: - print ">>> This is a system package, let's pack a rescue tarball." - #emergelog(">>> This is a system package, let's pack a rescue tarball.") - #create pkg, then merge pkg - short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Clean" - emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Cleaning ("+x[pkgindex]+"::"+y+")", short_msg=short_msg) - retval=portage.doebuild(y,"clean",myroot,self.pkgsettings,edebug,cleanup=1,tree="porttree") - if (retval == None): - portage_util.writemsg("Unable to run required binary.\n") - sys.exit(127) - if retval: - sys.exit(retval) - short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Compile" - emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Compiling/Packaging ("+x[pkgindex]+"::"+y+")", short_msg=short_msg) - retval=portage.doebuild(y,"package",myroot,self.pkgsettings,edebug,tree="porttree") - if (retval == None): - portage_util.writemsg("Unable to run required binary.\n") - sys.exit(127) - if retval: - sys.exit(retval) - #dynamically update our database - if "--buildpkgonly" not in myopts: - portage.db[portage.root]["bintree"].inject(x[2]) - mytbz2=portage.db[portage.root]["bintree"].getname(x[2]) - short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Merge" - emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Merging ("+x[pkgindex]+"::"+y+")", short_msg=short_msg) - - self.pkgsettings["EMERGE_FROM"] = "binary" - self.pkgsettings.backup_changes("EMERGE_FROM") - - retval=portage.pkgmerge(mytbz2,myroot,self.pkgsettings) - if retval==None: - sys.exit(1) - else: - short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Clean" - emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Cleaning ("+x[pkgindex]+"::"+y+")", short_msg=short_msg) - retval=portage.doebuild(y,"clean",myroot,self.pkgsettings,edebug,cleanup=1,tree="porttree") - if (retval == None): - portage_util.writemsg("Unable to run required binary.\n") - sys.exit(127) - if retval: - sys.exit(retval) - short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Compile" - emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Compiling/Merging ("+x[pkgindex]+"::"+y+")", short_msg=short_msg) - retval=portage.doebuild(y,"merge",myroot,self.pkgsettings,edebug,tree="porttree") - if (retval == None): - portage_util.writemsg("Unable to run required binary.\n") - sys.exit(127) - if retval: - sys.exit(retval) - #dynamically update our database + + portage.doebuild_environment(y, "setup", myroot, + pkgsettings, self.edebug, 1, portdb) + catdir = os.path.dirname(pkgsettings["PORTAGE_BUILDDIR"]) + portage_util.ensure_dirs(os.path.dirname(catdir), + uid=portage.portage_uid, gid=portage.portage_gid, + mode=070, mask=0) + builddir_lock = None + catdir_lock = None + try: + catdir_lock = portage_locks.lockdir(catdir) + portage_util.ensure_dirs(catdir, + uid=portage.portage_uid, gid=portage.portage_gid, + mode=070, mask=0) + builddir_lock = portage_locks.lockdir( + pkgsettings["PORTAGE_BUILDDIR"]) + try: + portage_locks.unlockdir(catdir_lock) + finally: + catdir_lock = None + msg = " === (%s of %s) Cleaning (%s::%s)" % \ + (mergecount, len(mymergelist), pkg_key, y) + short_msg = "emerge: (%s of %s) %s Clean" % \ + (mergecount, len(mymergelist), pkg_key) + emergelog(xterm_titles, msg, short_msg=short_msg) + retval = portage.doebuild(y, "clean", myroot, + pkgsettings, self.edebug, cleanup=1, + mydbapi=portdb, tree="porttree") + if retval != os.EX_OK: + return retval + if "--buildpkg" in self.myopts or issyspkg: + if issyspkg: + print ">>> This is a system package, " + \ + "let's pack a rescue tarball." + msg = " === (%s of %s) Compiling/Packaging (%s::%s)" % \ + (mergecount, len(mymergelist), pkg_key, y) + short_msg = "emerge: (%s of %s) %s Compile" % \ + (mergecount, len(mymergelist), pkg_key) + emergelog(xterm_titles, msg, short_msg=short_msg) + retval = portage.doebuild(y, "package", myroot, + pkgsettings, self.edebug, mydbapi=portdb, + tree="porttree") + if retval != os.EX_OK: + return retval + if "--buildpkgonly" not in self.myopts: + bintree = self.trees[myroot]["bintree"] + bintree.inject(pkg_key) + mytbz2 = bintree.getname(pkg_key) + msg = " === (%s of %s) Merging (%s::%s)" % \ + (mergecount, len(mymergelist), pkg_key, y) + short_msg = "emerge: (%s of %s) %s Merge" % \ + (mergecount, len(mymergelist), pkg_key) + emergelog(xterm_titles, msg, short_msg=short_msg) + retval = portage.merge(pkgsettings["CATEGORY"], + pkgsettings["PF"], pkgsettings["D"], + os.path.join(pkgsettings["PORTAGE_BUILDDIR"], + "build-info"), myroot, pkgsettings, + myebuild=pkgsettings["EBUILD"], + mytree="porttree", mydbapi=portdb, + vartree=vartree, prev_mtimes=ldpath_mtimes) + if retval != os.EX_OK: + return retval + elif "noclean" not in pkgsettings.features: + portage.doebuild(y, "clean", myroot, + pkgsettings, self.edebug, mydbapi=portdb, + tree="porttree") + else: + msg = " === (%s of %s) Compiling/Merging (%s::%s)" % \ + (mergecount, len(mymergelist), pkg_key, y) + short_msg = "emerge: (%s of %s) %s Compile" % \ + (mergecount, len(mymergelist), pkg_key) + emergelog(xterm_titles, msg, short_msg=short_msg) + retval = portage.doebuild(y, "merge", myroot, + pkgsettings, self.edebug, vartree=vartree, + mydbapi=portdb, tree="porttree", + prev_mtimes=ldpath_mtimes) + if retval != os.EX_OK: + return retval + finally: + if builddir_lock: + portage_locks.unlockdir(builddir_lock) + try: + if not catdir_lock: + # Lock catdir for removal if empty. + catdir_lock = portage_locks.lockdir(catdir) + finally: + if catdir_lock: + try: + os.rmdir(catdir) + except OSError, e: + if e.errno != errno.ENOTEMPTY: + raise + del e + portage_locks.unlockdir(catdir_lock) + elif x[0]=="binary": #merge the tbz2 - mytbz2=portage.db[portage.root]["bintree"].getname(x[2]) - if portage.db[portage.root]["bintree"].isremote(x[2]): + mytbz2 = self.trees[myroot]["bintree"].getname(pkg_key) + if self.trees[myroot]["bintree"].isremote(pkg_key): short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Fetch" - emergelog(" --- ("+str(mergecount)+" of "+str(len(mymergelist))+") Fetching Binary ("+x[pkgindex]+"::"+mytbz2+")", short_msg=short_msg) - if not portage.db[portage.root]["bintree"].gettbz2(x[2]): + emergelog(xterm_titles, " --- ("+str(mergecount)+\ + " of "+str(len(mymergelist))+\ + ") Fetching Binary ("+x[pkgindex]+\ + "::"+mytbz2+")", short_msg=short_msg) + if not self.trees[myroot]["bintree"].gettbz2(pkg_key): sys.exit(1) - if ("--fetchonly" in myopts) or ("--fetch-all-uri" in myopts): + if "--fetchonly" in self.myopts or \ + "--fetch-all-uri" in self.myopts: continue short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Merge Binary" - emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Merging Binary ("+x[pkgindex]+"::"+mytbz2+")", short_msg=short_msg) - retval=portage.pkgmerge(mytbz2,x[1],self.pkgsettings) - if retval==None: + emergelog(xterm_titles, " === ("+str(mergecount)+\ + " of "+str(len(mymergelist))+") Merging Binary ("+\ + x[pkgindex]+"::"+mytbz2+")", short_msg=short_msg) + retval = portage.pkgmerge(mytbz2, x[1], pkgsettings, + mydbapi=bindb, + vartree=self.trees[myroot]["vartree"], + prev_mtimes=ldpath_mtimes) + if retval is None: sys.exit(1) #need to check for errors - if "--buildpkgonly" not in myopts: - portage.db[x[1]]["vartree"].inject(x[2]) + if "--buildpkgonly" not in self.myopts: + self.trees[x[1]]["vartree"].inject(x[2]) myfavkey=portage.cpv_getkey(x[2]) - if "--fetchonly" not in myopts and "--fetch-all-uri" not in myopts and myfavkey in favorites: + if "--fetchonly" not in self.myopts and \ + "--fetch-all-uri" not in self.myopts and \ + myfavkey in favorites: myfavs = portage.grabfile(os.path.join(myroot, portage.WORLD_FILE)) myfavdict=genericdict(myfavs) - mysysdict=genericdict(syslist) #don't record if already in system profile or already recorded if (not mysysdict.has_key(myfavkey)) and (not myfavdict.has_key(myfavkey)): #we don't have a favorites entry for this package yet; add one myfavdict[myfavkey]=myfavkey print ">>> Recording",myfavkey,"in \"world\" favorites file..." - emergelog(" === ("+str(mergecount)+" of "+str(len(mymergelist))+") Updating world file ("+x[pkgindex]+")") + emergelog(xterm_titles, " === ("+\ + str(mergecount)+" of "+\ + str(len(mymergelist))+\ + ") Updating world file ("+x[pkgindex]+")") portage.write_atomic( os.path.join(myroot, portage.WORLD_FILE), "\n".join(myfavdict.values())) - if ("--pretend" not in myopts) and ("--fetchonly" not in myopts) and ("--fetch-all-uri" not in myopts): + if "--pretend" not in self.myopts and \ + "--fetchonly" not in self.myopts and \ + "--fetch-all-uri" not in self.myopts: # Clean the old package that we have merged over top of it. - if self.pkgsettings["AUTOCLEAN"]=="yes": + if pkgsettings.get("AUTOCLEAN", "yes") == "yes": xsplit=portage.pkgsplit(x[2]) - emergelog(" >>> AUTOCLEAN: "+xsplit[0]) - retval=unmerge("clean", [xsplit[0]]) + emergelog(xterm_titles, " >>> AUTOCLEAN: " + xsplit[0]) + retval = unmerge(pkgsettings, self.myopts, vartree, + "clean", [xsplit[0]], ldpath_mtimes) if not retval: - emergelog(" --- AUTOCLEAN: Nothing unmerged.") + emergelog(xterm_titles, + " --- AUTOCLEAN: Nothing unmerged.") + else: + portage.writemsg_stdout(colorize("WARN", "WARNING:") + + " AUTOCLEAN is disabled. This can cause serious" + + " problems due to overlapping packages.\n") # Figure out if we need a restart. mysplit=portage.pkgsplit(x[2]) - if mysplit[0]=="sys-apps/portage": + if mysplit[0] == "sys-apps/portage" and x[1] == "/": myver=mysplit[1]+"-"+mysplit[2] if myver[-3:]=='-r0': myver=myver[:-3] if (myver != portage.VERSION) and \ - ("livecvsportage" not in portage.settings.features): + "livecvsportage" not in self.settings.features: if len(mymergelist) > mergecount: - emergelog(" *** RESTARTING emerge via exec() after change of portage version.") - del portage.mtimedb["resume"]["mergelist"][0] + emergelog(xterm_titles, + " ::: completed emerge ("+ \ + str(mergecount)+" of "+ \ + str(len(mymergelist))+") "+ \ + x[2]+" to "+x[1]) + emergelog(xterm_titles, " *** RESTARTING " + \ + "emerge via exec() after change of " + \ + "portage version.") + del mtimedb["resume"]["mergelist"][0] + mtimedb.commit() portage.run_exitfuncs() mynewargv=[sys.argv[0],"--resume"] - badlongopts = ("--ask","--tree","--changelog","--skipfirst","--resume") - for arg in myopts: - if arg in badlongopts: - continue - mynewargv.append(arg) + for myopt, myarg in self.myopts.iteritems(): + if myopt not in bad_resume_opts: + if myarg is True: + mynewargv.append(myopt) + else: + mynewargv.append(myopt +"="+ myarg) + # priority only needs to be adjusted on the first run + os.environ["PORTAGE_NICENESS"] = "0" os.execv(mynewargv[0], mynewargv) - if ("--pretend" not in myopts) and ("--fetchonly" not in myopts) and ("--fetch-all-uri" not in myopts): - if "noclean" not in portage.features: + if "--pretend" not in self.myopts and \ + "--fetchonly" not in self.myopts and \ + "--fetch-all-uri" not in self.myopts: + if "noclean" not in self.settings.features: short_msg = "emerge: (%s of %s) %s Clean Post" % \ (mergecount, len(mymergelist), x[pkgindex]) - emergelog(" === (%s of %s) Post-Build Cleaning (%s::%s)" % \ - (mergecount, len(mymergelist), x[pkgindex], y), short_msg=short_msg) - emergelog(" ::: completed emerge ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[2]+" to "+x[1]) + emergelog(xterm_titles, (" === (%s of %s) " + \ + "Post-Build Cleaning (%s::%s)") % \ + (mergecount, len(mymergelist), x[pkgindex], y), + short_msg=short_msg) + emergelog(xterm_titles, " ::: completed emerge ("+\ + str(mergecount)+" of "+str(len(mymergelist))+") "+\ + x[2]+" to "+x[1]) # Unsafe for parallel merges - del portage.mtimedb["resume"]["mergelist"][0] + del mtimedb["resume"]["mergelist"][0] # Commit after each merge so that --resume may still work in # in the event that portage is not allowed to exit normally # due to power failure, SIGKILL, etc... - portage.commit_mtimedb() + mtimedb.commit() - emergelog(" *** Finished. Cleaning up...") + if "--pretend" not in self.myopts: + emergelog(xterm_titles, " *** Finished. Cleaning up...") # We're out of the loop... We're done. Delete the resume data. - if portage.mtimedb.has_key("resume"): - del portage.mtimedb["resume"] - - if ("--pretend" not in myopts): - if ("--fetchonly" not in myopts) and ("--fetch-all-uri" not in myopts): - if (mergecount>0): - if retval: - portage.env_update() + if mtimedb.has_key("resume"): + del mtimedb["resume"] + mtimedb.commit() #by doing an exit this way, --fetchonly can continue to try to #fetch everything even if a particular download fails. - if "--fetchonly" in myopts or "--fetch-all-uri" in myopts: + if "--fetchonly" in self.myopts or "--fetch-all-uri" in self.myopts: if returnme: print "\n\n!!! Some fetch errors were encountered. Please see above for details.\n\n" sys.exit(returnme) else: sys.exit(0) + return os.EX_OK -def unmerge(unmerge_action, unmerge_files): +def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files, + ldpath_mtimes, raise_on_missing=True): candidate_catpkgs=[] global_unmerge=0 + xterm_titles = "notitles" not in settings.features - realsyslist = getlist("system") + realsyslist = getlist(settings, "system") syslist = [] for x in realsyslist: mycp = portage.dep_getkey(x) - if mycp in portage.settings.getvirtuals(): + if mycp in settings.getvirtuals(): providers = [] - for provider in portage.settings.getvirtuals()[mycp]: - if portage.db[portage.root]["vartree"].dbapi.match(provider): + for provider in settings.getvirtuals()[mycp]: + if vartree.dbapi.match(provider): providers.append(provider) if len(providers) == 1: syslist.extend(providers) else: syslist.append(mycp) - global myopts - mysettings = portage.config(clone=portage.settings) + mysettings = portage.config(clone=settings) if not unmerge_files or "world" in unmerge_files or "system" in unmerge_files: if "unmerge"==unmerge_action: @@ -2154,13 +2378,13 @@ def unmerge(unmerge_action, unmerge_files): else: global_unmerge=1 - localtree=portage.db[portage.root]["vartree"] + localtree = vartree # process all arguments and add all valid db entries to candidate_catpkgs if global_unmerge: if not unmerge_files or "world" in unmerge_files: candidate_catpkgs.extend(localtree.getallnodes()) elif "system" in unmerge_files: - candidate_catpkgs.extend(getlist("system")) + candidate_catpkgs.extend(getlist(settings, "system")) else: #we've got command-line arguments if not unmerge_files: @@ -2189,7 +2413,7 @@ def unmerge(unmerge_action, unmerge_files): sp_absx_len = len(sp_absx) - vdb_path = portage.root+portage.VDB_PATH + vdb_path = os.path.join(settings["ROOT"], portage.VDB_PATH) vdb_len = len(vdb_path) sp_vdb = vdb_path.split("/") @@ -2201,23 +2425,28 @@ def unmerge(unmerge_action, unmerge_files): if sp_absx_len <= sp_vdb_len: # The Path is shorter... so it can't be inside the vdb. - print spabsx + print sp_absx print absx - print "\n!!!",x,"cannot be inside "+(portage.root+portage.VDB_PATH)+"; aborting.\n" + print "\n!!!",x,"cannot be inside "+vdb_path+"; aborting.\n" return 0 for idx in range(0,sp_vdb_len): if (idx >= sp_absx_len) or (sp_vdb[idx] != sp_absx[idx]): print sp_absx print absx - print "\n!!!",x,"is not inside "+(portage.root+portage.VDB_PATH)+"; aborting.\n" + print "\n!!!",x,"is not inside "+vdb_path+"; aborting.\n" return 0 print "="+string.join(sp_absx[sp_vdb_len:],"/") candidate_catpkgs.append("="+string.join(sp_absx[sp_vdb_len:],"/")) + newline="" + if (not "--quiet" in myopts): + newline="\n" + if settings["ROOT"] != "/": + print darkgreen(newline+">>> Using system located in ROOT tree "+settings["ROOT"]) if (("--pretend" in myopts) or ("--ask" in myopts)) and not ("--quiet" in myopts): - print darkgreen("\n>>> These are the packages that would be unmerged:") + print darkgreen(newline+">>> These are the packages that would be unmerged:") pkgmap={} numselected=0 @@ -2239,9 +2468,15 @@ def unmerge(unmerge_action, unmerge_files): #add a "=" if missing mymatch=localtree.dep_match("="+x) if not mymatch: - print "\n--- Couldn't find " + white(x) + " to "+unmerge_action+"." - continue - mykey=portage.key_expand(portage.dep_getkey(mymatch[0]),portage.db["/"]["vartree"].dbapi) + if raise_on_missing: + raise portage_exception.PackageNotFound(x) + else: + portage.writemsg("\n--- Couldn't find '%s' to %s.\n" % \ + (x, unmerge_action), noiselevel=-1) + continue + mykey = portage.key_expand( + portage.dep_getkey( + mymatch[0]), mydb=vartree.dbapi, settings=settings) if not pkgmap.has_key(mykey): pkgmap[mykey]={"protected":[], "selected":[], "omitted":[] } if unmerge_action=="unmerge": @@ -2293,14 +2528,14 @@ def unmerge(unmerge_action, unmerge_files): #avoid cluttering the preview printout with stuff that isn't getting unmerged continue if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and (x in syslist): - print red("\a\n\n!!! '%s' is part of your system profile." % mykey) - print yellow("\a!!! Unmerging it may be damaging to your system.\n") + print colorize("BAD","\a\n\n!!! '%s' is part of your system profile." % x) + print colorize("WARN","\a!!! Unmerging it may be damaging to your system.\n") if "--pretend" not in myopts and "--ask" not in myopts: - global EMERGE_WARNING_DELAY - countdown(EMERGE_WARNING_DELAY,red("Press Ctrl-C to Stop")) + countdown(int(settings["EMERGE_WARNING_DELAY"]), + colorize("UNMERGE_WARN", "Press Ctrl-C to Stop")) print "\n "+white(x) for mytype in ["selected","protected","omitted"]: - print string.rjust(mytype,12)+":", + portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1) if pkgmap[x][mytype]: for mypkg in pkgmap[x][mytype]: mysplit=portage.catpkgsplit(mypkg) @@ -2309,86 +2544,65 @@ def unmerge(unmerge_action, unmerge_files): else: myversion=mysplit[2]+"-"+mysplit[3] if mytype=="selected": - print red(myversion), + portage.writemsg_stdout( + colorize("UNMERGE_WARN", myversion + " "), noiselevel=-1) else: - print green(myversion), + portage.writemsg_stdout( + colorize("GOOD", myversion + " "), noiselevel=-1) else: - print "none", - print - - if portage.settings["ROOT"]: - print string.rjust("ROOT:", 12) + " " + white(portage.settings["ROOT"]) + portage.writemsg_stdout("none", noiselevel=-1) + portage.writemsg_stdout("\n", noiselevel=-1) - print "\n>>>",red("'Selected'"),"packages are slated for removal." - print ">>>",green("'Protected'"),"and",green("'omitted'"),"packages will not be removed.\n" + portage.writemsg_stdout("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \ + " packages are slated for removal.\n") + portage.writemsg_stdout(">>> " + colorize("GOOD", "'Protected'") + \ + " and " + colorize("GOOD", "'omitted'") + \ + " packages will not be removed.\n\n") if "--pretend" in myopts: #we're done... return return 0 if "--ask" in myopts: - if userquery("Do you want me to unmerge these packages?")=="No": + if userquery("Would you like to unmerge these packages?")=="No": # enter pretend mode for correct formatting of results - myopts+=["--pretend"] + myopts["--pretend"] = True print print "Quitting." print return 0 #the real unmerging begins, after a short delay.... - - global CLEAN_DELAY - countdown(CLEAN_DELAY, ">>> Unmerging") + countdown(int(settings["CLEAN_DELAY"]), ">>> Unmerging") for x in pkgmap.keys(): for y in pkgmap[x]["selected"]: print ">>> Unmerging "+y+"..." - emergelog("=== Unmerging... ("+y+")") + emergelog(xterm_titles, "=== Unmerging... ("+y+")") mysplit=string.split(y,"/") #unmerge... - retval=portage.unmerge(mysplit[0],mysplit[1],portage.root,mysettings,unmerge_action not in ["clean","prune"]) - if retval: - emergelog(" !!! unmerge FAILURE: "+y) + retval = portage.unmerge(mysplit[0], mysplit[1], settings["ROOT"], + mysettings, unmerge_action not in ["clean","prune"], + vartree=vartree, ldpath_mtimes=ldpath_mtimes) + if retval != os.EX_OK: + emergelog(xterm_titles, " !!! unmerge FAILURE: "+y) else: - emergelog(" >>> unmerge success: "+y) - #run ldconfig, etc... - portage.env_update() - if not numselected: - return 0 - else: - return 1 - + clean_world(vartree.dbapi, y) + emergelog(xterm_titles, " >>> unmerge success: "+y) + return 1 -def chk_updated_info_files(retval): - root=portage.root - - infodirs=[] - infodirs.extend(string.split(portage.settings["INFOPATH"], ":")) - infodirs.extend(string.split(portage.settings["INFODIR"], ":")) +def chk_updated_info_files(root, infodirs, prev_mtimes, retval): print - if os.path.exists("install-info"): + if os.path.exists(portage_const.EPREFIX+"/usr/bin/install-info"): regen_infodirs=[] for z in infodirs: if z=='': continue inforoot=normpath(root+z) if os.path.isdir(inforoot): - try: - infomtime=os.stat(inforoot)[ST_MTIME] - except SystemExit, e: - raise # Needed else can't exit - except: - infomtime=0 - - if not portage.mtimedb.has_key("info"): - portage.mtimedb["info"]={} - if portage.mtimedb["info"].has_key(inforoot): - if portage.mtimedb["info"][inforoot]==infomtime: - pass - else: - portage.mtimedb["info"][inforoot]=infomtime + infomtime = long(os.stat(inforoot).st_mtime) + if inforoot not in prev_mtimes or \ + prev_mtimes[inforoot] != infomtime: regen_infodirs.append(inforoot) - else: - regen_infodirs.append(inforoot) if not regen_infodirs: print " "+green("*")+" GNU info directory index is up-to-date." @@ -2410,10 +2624,12 @@ def chk_updated_info_files(retval): if not os.path.isdir(inforoot): continue errmsg = "" - for x in os.listdir(inforoot): + file_list = os.listdir(inforoot) + file_list.sort() + for x in file_list: if (x[0] == ".") or (x in ["dir","dir.old"]) or (os.path.isdir(inforoot+"/"+x)): continue - myso=commands.getstatusoutput("LANG=C LANGUAGE=C install-info --dir-file="+inforoot+"/dir "+inforoot+"/"+x)[1] + myso=commands.getstatusoutput("LANG=C LANGUAGE=C "+portage_const.EPREFIX+"/usr/bin/install-info --dir-file="+inforoot+"/dir "+inforoot+"/"+x)[1] existsstr="already exists, for file `" if myso!="": if re.search(existsstr,myso): @@ -2430,7 +2646,7 @@ def chk_updated_info_files(retval): icount=icount+1 #update mtime so we can potentially avoid regenerating. - portage.mtimedb["info"][inforoot]=os.stat(inforoot)[ST_MTIME] + prev_mtimes[inforoot] = long(os.stat(inforoot).st_mtime) if badcount: print " "+yellow("*")+" Processed",icount,"info files;",badcount,"errors." @@ -2439,36 +2655,55 @@ def chk_updated_info_files(retval): print " "+green("*")+" Processed",icount,"info files." -def post_emerge(retval=0): - global myopts +def post_emerge(settings, mtimedb, retval): + target_root = settings["ROOT"] + info_mtimes = mtimedb["info"] + config_protect = settings.get("CONFIG_PROTECT","").split() + infodirs = settings.get("INFOPATH","").split(":") + \ + settings.get("INFODIR","").split(":") + os.chdir("/") - if "--pretend" in myopts: - sys.exit(retval) - emergelog(" *** exiting successfully.") + emergelog("notitles" not in settings.features, + " *** exiting successfully.") - if "noinfo" not in portage.settings.features: - chk_updated_info_files(retval) + if "noinfo" not in settings.features: + chk_updated_info_files(target_root, infodirs, info_mtimes, retval) - chk_updated_cfg_files() + chk_updated_cfg_files(target_root, config_protect) + mtimedb.commit() sys.exit(retval) -def chk_updated_cfg_files(): - if portage.settings["CONFIG_PROTECT"]: +def chk_updated_cfg_files(target_root, config_protect): + if config_protect: #number of directories with some protect files in them procount=0 - for x in string.split(portage.settings["CONFIG_PROTECT"]): - x = portage_const.EPREFIX + x - if os.path.isdir(x): - a=commands.getstatusoutput("cd "+x+"; find . -iname '._cfg????_*'") - if a[0]!=0: - print " "+red("*")+" error scanning",x - else: - files=string.split(a[1]) - if files: - procount=procount+1 - print " "+yellow("* IMPORTANT:")+"",len(files),"config files in",x,"need updating." + for x in config_protect: + x = os.path.join(target_root, x.lstrip(os.path.sep)) + try: + mymode = os.lstat(x).st_mode + except OSError: + continue + if stat.S_ISDIR(mymode): + mycommand = "cd '%s'; find . -iname '._cfg????_*'" % x + else: + mycommand = "cd '%s'; find . -maxdepth 1 -iname '._cfg????_%s'" % \ + os.path.split(x.rstrip(os.path.sep)) + a = commands.getstatusoutput(mycommand) + if a[0] != 0: + print >> sys.stderr, " " + bad("*")+ " error scanning '%s'" % x + else: + files = a[1].split() + if files: + procount += 1 + print colorize("WARN", " * IMPORTANT:"), + if stat.S_ISDIR(mymode): + print "%d config files in '%s' need updating." % \ + (len(files), x) + else: + print "config file '%s' needs updating." % x + if procount: #print " "+yellow("*")+" Type "+green("emerge --help config")+" to learn how to update config files." print " "+yellow("*")+" Type "+green("emerge --help config")+" to learn how to update config files." @@ -2484,28 +2719,43 @@ def is_valid_package_atom(x): testatom = x return portage.isvalidatom(testatom) -# general options that should be taken into account before any action -if "--debug" in myopts: - edebug=1 +def validate_merge_list(trees, mergelist): + """Validate the list to make sure all the packages are still available. + This is needed for --resume.""" + for (pkg_type, myroot, pkg_key, action) in mergelist: + if pkg_type == "binary" and \ + not trees[myroot]["bintree"].dbapi.match("="+pkg_key) or \ + pkg_type == "ebuild" and \ + not trees[myroot]["porttree"].dbapi.xmatch( + "match-all", "="+pkg_key): + print red("!!! Error: The resume list contains packages that are no longer") + print red("!!! available to be emerged. Please restart/continue") + print red("!!! the merge operation manually.") + sys.exit(1) -if myaction in ["sync","metadata"] and (not "--help" in myopts): - if "--pretend" in myopts: - print "emerge: \"sync\" actions do not support \"--pretend.\"" - sys.exit(1) +def show_blocker_docs_link(): + print + print "For more information about " + bad("Blocked Packages") + ", please refer to the following" + print "section of the Gentoo Linux x86 Handbook (architecture is irrelevant):" + print + print "http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?full=1#blocked" + print - emergelog(" === "+str(myaction)) - myportdir=portage.settings["PORTDIR"] +def action_sync(settings, trees, mtimedb, myopts, myaction): + xterm_titles = "notitles" not in settings.features + emergelog(xterm_titles, " === sync") + myportdir = settings.get("PORTDIR", None) + if not myportdir: + sys.stderr.write("!!! PORTDIR is undefined. Is /etc/make.globals missing?\n") + sys.exit(1) if myportdir[-1]=="/": myportdir=myportdir[:-1] if not os.path.exists(myportdir): print ">>>",myportdir,"not found, creating it." os.makedirs(myportdir,0755) - syncuri=string.rstrip(portage.settings["SYNC"]) + syncuri=string.rstrip(settings["SYNC"]) os.umask(0022) if myaction == "metadata": - if "--ask" in myopts: - if userquery("Are you sure?") == "No": - sys.exit(1) print "skipping sync" updatecache_flg = True tmpservertimestampfile = None @@ -2518,7 +2768,7 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): rsync_opts = [] - if portage.settings["PORTAGE_RSYNC_OPTS"] == "": + if settings["PORTAGE_RSYNC_OPTS"] == "": portage.writemsg("PORTAGE_RSYNC_OPTS empty or unset, using hardcoded defaults\n") rsync_opts.extend([ "--recursive", # Recurse directories @@ -2542,8 +2792,8 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): # The below validation is not needed when using the above hardcoded # defaults. - portage.writemsg("Using PORTAGE_RSYNC_OPTS instead of hardcoded defaults\n") - rsync_opts.extend(portage.settings["PORTAGE_RSYNC_OPTS"].split()) + portage.writemsg("Using PORTAGE_RSYNC_OPTS instead of hardcoded defaults\n", 1) + rsync_opts.extend(settings["PORTAGE_RSYNC_OPTS"].split()) for opt in ("--recursive", "--times"): if opt not in rsync_opts: @@ -2552,18 +2802,18 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): rsync_opts.append(opt) for exclude in ("distfiles", "local", "packages"): - opt = "--exclude='/%s'" % exclude + opt = "--exclude=/%s" % exclude if opt not in rsync_opts: portage.writemsg(yellow("WARNING:") + \ " adding required option %s not included in " % opt + \ - "PORTAGE_RSYNC_OPTS (override with --exclude='!')\n") + "PORTAGE_RSYNC_OPTS (can be overridden with --exclude='!')\n") rsync_opts.append(opt) - if portage.settings["RSYNC_TIMEOUT"] != "": + if settings["RSYNC_TIMEOUT"] != "": portage.writemsg("WARNING: usage of RSYNC_TIMEOUT is deprecated, " + \ "use PORTAGE_RSYNC_EXTRA_OPTS instead\n") try: - mytimeout = int(portage.settings["RSYNC_TIMEOUT"]) + mytimeout = int(settings["RSYNC_TIMEOUT"]) rsync_opts.append("--timeout=%d" % mytimeout) except ValueError, e: portage.writemsg("!!! %s\n" % str(e)) @@ -2589,36 +2839,36 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): if "--quiet" in myopts: rsync_opts.append("--quiet") # Shut up a lot else: - rsync_opts.append("--progress") # Progress meter for each file + rsync_opts.append("--verbose") # Print filelist if "--verbose" in myopts: - rsync_opts.append("--verbose") # More noise? Not really sure what + rsync_opts.append("--progress") # Progress meter for each file if "--debug" in myopts: rsync_opts.append("--checksum") # Force checksum on all files - if portage.settings["RSYNC_EXCLUDEFROM"] != "": + if settings["RSYNC_EXCLUDEFROM"] != "": portage.writemsg(yellow("WARNING:") + \ " usage of RSYNC_EXCLUDEFROM is deprecated, use " + \ "PORTAGE_RSYNC_EXTRA_OPTS instead\n") - if os.path.exists(portage.settings["RSYNC_EXCLUDEFROM"]): + if os.path.exists(settings["RSYNC_EXCLUDEFROM"]): rsync_opts.append("--exclude-from=%s" % \ - portage.settings["RSYNC_EXCLUDEFROM"]) + settings["RSYNC_EXCLUDEFROM"]) else: portage.writemsg("!!! RSYNC_EXCLUDEFROM specified," + \ " but file does not exist.\n") - if portage.settings["RSYNC_RATELIMIT"] != "": + if settings["RSYNC_RATELIMIT"] != "": portage.writemsg(yellow("WARNING:") + \ " usage of RSYNC_RATELIMIT is deprecated, use " + \ "PORTAGE_RSYNC_EXTRA_OPTS instead") rsync_opts.append("--bwlimit=%s" % \ - portage.settings["RSYNC_RATELIMIT"]) + settings["RSYNC_RATELIMIT"]) - servertimestampdir = portage.settings.depcachedir+"/" - servertimestampfile = portage.settings.depcachedir+"/timestamp.chk" - tmpservertimestampdir = portage.settings["PORTAGE_TMPDIR"]+"/" - tmpservertimestampfile = portage.settings["PORTAGE_TMPDIR"]+"/timestamp.chk" + servertimestampdir = settings.depcachedir+"/" + servertimestampfile = settings.depcachedir+"/timestamp.chk" + tmpservertimestampdir = settings["PORTAGE_TMPDIR"]+"/" + tmpservertimestampfile = settings["PORTAGE_TMPDIR"]+"/timestamp.chk" # We only use the backup if a timestamp exists in the portdir. content=None @@ -2642,29 +2892,46 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): #exitcode=0 try: - if portage.settings.has_key("RSYNC_RETRIES"): + if settings.has_key("RSYNC_RETRIES"): print yellow("WARNING:")+" usage of RSYNC_RETRIES is deprecated, use PORTAGE_RSYNC_RETRIES instead" - maxretries=int(portage.settings["RSYNC_RETRIES"]) + maxretries=int(settings["RSYNC_RETRIES"]) else: - maxretries=int(portage.settings["PORTAGE_RSYNC_RETRIES"]) + maxretries=int(settings["PORTAGE_RSYNC_RETRIES"]) except SystemExit, e: raise # Needed else can't exit except: maxretries=3 #default number of retries retries=0 - hostname, port=re.split("rsync://([^:/]*)(:[0-9]+)?", syncuri)[1:3]; - if port==None: + user_name, hostname, port = re.split( + "rsync://([^:/]+@)?([^:/]*)(:[0-9]+)?", syncuri, maxsplit=3)[1:4] + if port is None: port="" + if user_name is None: + user_name="" updatecache_flg=True - + all_rsync_opts = set(rsync_opts) + all_rsync_opts.update( + settings.get("PORTAGE_RSYNC_EXTRA_OPTS","").split()) + family = socket.AF_UNSPEC + if "-4" in all_rsync_opts or "--ipv4" in all_rsync_opts: + family = socket.AF_INET + elif socket.has_ipv6 and \ + ("-6" in all_rsync_opts or "--ipv6" in all_rsync_opts): + family = socket.AF_INET6 ips=[] while (1): if ips: del ips[0] if ips==[]: try: - ips=socket.gethostbyname_ex(hostname)[2] + for addrinfo in socket.getaddrinfo( + hostname, None, family, socket.SOCK_STREAM): + if addrinfo[0] == socket.AF_INET6: + # IPv6 addresses need to be enclosed in square brackets + ips.append("[%s]" % addrinfo[4][0]) + else: + ips.append(addrinfo[4][0]) except SystemExit, e: raise # Needed else can't exit except Exception, e: @@ -2673,7 +2940,9 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): if ips: try: - dosyncuri=string.replace(syncuri, "//"+hostname+port+"/", "//"+ips[0]+port+"/", 1) + dosyncuri = syncuri.replace( + "//" + user_name + hostname + port + "/", + "//" + user_name + ips[0] + port + "/", 1) except SystemExit, e: raise # Needed else can't exit except Exception, e: @@ -2687,19 +2956,20 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): print "Quitting." print sys.exit(0) - emergelog(">>> Starting rsync with "+dosyncuri) + emergelog(xterm_titles, ">>> Starting rsync with " + dosyncuri) if "--quiet" not in myopts: print ">>> Starting rsync with "+dosyncuri+"..." else: - emergelog(">>> Starting retry %d of %d with %s" % (retries,maxretries,dosyncuri)) + emergelog(xterm_titles, + ">>> Starting retry %d of %d with %s" % \ + (retries,maxretries,dosyncuri)) print "\n\n>>> Starting retry %d of %d with %s" % (retries,maxretries,dosyncuri) if "--quiet" not in myopts: print ">>> Checking server timestamp ..." - rsynccommand = " ".join([portage_const.EPREFIX+"/usr/bin/rsync", - portage.settings["PORTAGE_RSYNC_EXTRA_OPTS"], - " ".join(rsync_opts)]) + rsynccommand = " ".join([portage_const.EPREFIX, "/usr/bin/rsync", " ".join(rsync_opts), + settings.get("PORTAGE_RSYNC_EXTRA_OPTS","")]) if "--debug" in myopts: print rsynccommand @@ -2707,7 +2977,7 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): mycommand = " ".join([rsynccommand, dosyncuri + "/metadata/timestamp.chk", tmpservertimestampdir]) - exitcode=portage.spawn(mycommand,portage.settings,free=1) + exitcode=portage.spawn(mycommand,settings,free=1) if (exitcode==0): try: servertimestamp = time.mktime(time.strptime(portage.grabfile(tmpservertimestampfile)[0], "%a, %d %b %Y %H:%M:%S +0000")) @@ -2717,7 +2987,8 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): servertimestamp = 0 if (servertimestamp != 0) and (servertimestamp == mytimestamp): - emergelog(">>> Cancelling sync -- Already current.") + emergelog(xterm_titles, + ">>> Cancelling sync -- Already current.") print print ">>>" print ">>> Timestamps on the server and in the local repository are the same." @@ -2726,7 +2997,8 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): print sys.exit(0) elif (servertimestamp != 0) and (servertimestamp < mytimestamp): - emergelog(">>> Server out of date: %s" % dosyncuri) + emergelog(xterm_titles, + ">>> Server out of date: %s" % dosyncuri) print print ">>>" print ">>> SERVER OUT OF DATE: %s" % dosyncuri @@ -2735,7 +3007,7 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): elif (servertimestamp == 0) or (servertimestamp > mytimestamp): # actual sync mycommand=rsynccommand+" "+dosyncuri+"/ "+myportdir - exitcode=portage.spawn(mycommand,portage.settings,free=1) + exitcode=portage.spawn(mycommand,settings,free=1) if exitcode in [0,1,2,3,4,11,14,20,21]: break elif exitcode in [0,1,2,3,4,11,14,20,21]: @@ -2753,19 +3025,31 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): break if (exitcode==0): - emergelog("=== Sync completed with %s" % dosyncuri) + emergelog(xterm_titles, "=== Sync completed with %s" % dosyncuri) + # save timestamp.chk for next timestamp check. + try: + if tmpservertimestampfile is not None: + portage.movefile(tmpservertimestampfile, + servertimestampfile, mysettings=settings) + except SystemExit, e: + raise + except Exception, e: + portage.writemsg("!!! Failed to save current timestamp.\n", + noiselevel=-1) + portage.writemsg("!!! %s\n" % str(e), noiselevel=-1) + del e elif (exitcode>0): print if exitcode==1: print darkred("!!!")+green(" Rsync has reported that there is a syntax error. Please ensure") print darkred("!!!")+green(" that your SYNC statement is proper.") - print darkred("!!!")+green(" SYNC="+portage.settings["SYNC"]) + print darkred("!!!")+green(" SYNC="+settings["SYNC"]) elif exitcode==11: print darkred("!!!")+green(" Rsync has reported that there is a File IO error. Normally") print darkred("!!!")+green(" this means your disk is full, but can be caused by corruption") print darkred("!!!")+green(" on the filesystem that contains PORTDIR. Please investigate") print darkred("!!!")+green(" and try again after the problem has been fixed.") - print darkred("!!!")+green(" PORTDIR="+portage.settings["PORTDIR"]) + print darkred("!!!")+green(" PORTDIR="+settings["PORTDIR"]) elif exitcode==20: print darkred("!!!")+green(" Rsync was killed before it finished.") else: @@ -2777,7 +3061,7 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): print sys.exit(exitcode) elif syncuri[:6]=="cvs://": - if not os.path.exists("cvs"): + if not os.path.exists(portage_const.EPREFIX+"/usr/bin/cvs"): print "!!! cvs does not exist, so CVS support is disabled." print "!!! Type \"emerge dev-util/cvs\" to enable CVS support." sys.exit(1) @@ -2786,149 +3070,52 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): if not os.path.exists(myportdir+"/CVS"): #initial checkout print ">>> Starting initial cvs checkout with "+syncuri+"..." - if not portage.spawn("cd "+cvsdir+"; cvs -d "+cvsroot+" login",portage.settings,free=1): - print "!!! cvs login error; exiting." - sys.exit(1) if os.path.exists(cvsdir+"/gentoo-x86"): print "!!! existing",cvsdir+"/gentoo-x86 directory; exiting." sys.exit(1) - if not portage.spawn("cd "+cvsdir+"; cvs -z0 -d "+cvsroot+" co -P gentoo-x86",portage.settings,free=1): + if portage.spawn("cd "+cvsdir+"; cvs -z0 -d "+cvsroot+" co -P gentoo-x86",settings,free=1): print "!!! cvs checkout error; exiting." sys.exit(1) if cvsdir!=myportdir: - portage.movefile(cvsdir,portage.settings["PORTDIR"]) + portage.movefile(cvsdir,settings["PORTDIR"]) sys.exit(0) else: #cvs update print ">>> Starting cvs update with "+syncuri+"..." - sys.exit(portage.spawn("cd "+myportdir+"; cvs -z0 -q update -dP",portage.settings,free=1)) + sys.exit(portage.spawn("cd "+myportdir+"; cvs -z0 -q update -dP",settings,free=1)) else: print "!!! rsync setting: ",syncuri,"not recognized; exiting." sys.exit(1) - try: # Prevent users from affecting ebuild.sh. - os.close(sys.stdin.fileno()) - except SystemExit, e: - raise # Needed else can't exit - except: - pass - if updatecache_flg and \ myaction != "metadata" and \ - "metadata-transfer" not in portage.settings.features: + "metadata-transfer" not in settings.features: updatecache_flg = False - if os.path.exists(myportdir+"/metadata/cache") and updatecache_flg: - if "--quiet" not in myopts: - print "\n>>> Updating Portage cache: ", - os.umask(0002) - cachedir = os.path.normpath(portage.settings.depcachedir) - if cachedir in ["/", "/bin", "/dev", "/etc", "/home", - "/lib", "/opt", "/proc", "/root", "/sbin", - "/sys", "/tmp", "/usr", "/var"]: - print "!!! PORTAGE_CACHEDIR IS SET TO A PRIMARY ROOT DIRECTORY ON YOUR SYSTEM." - print "!!! This is ALMOST CERTAINLY NOT what you want: "+str(cachedir) - sys.exit(73) - if not os.path.exists(cachedir): - os.mkdir(cachedir) - - # Potentially bad - #if os.path.exists(cachedir+"/"+myportdir): - # portage.spawn("rm -Rf "+cachedir+"/"+myportdir+"/*",portage.settings,free=1) - - # save timestamp.chk for next timestamp check. - try: - if tmpservertimestampfile != None: - portage.movefile(tmpservertimestampfile, servertimestampfile) - except SystemExit, e: - raise # Needed else can't exit - except Exception, e: - print "!!! Failed to save current timestamp." - print "!!!",e - - portage.portdb.flush_cache() - - ec = portage.eclass_cache.cache(portage.portdb.porttree_root) - # kinda ugly. - # XXX: nuke the filter when mr UNUSED_0? keys are dead - cm = portage.settings.load_best_module("portdbapi.metadbmodule")(myportdir, "metadata/cache", - filter(lambda x: not x.startswith("UNUSED_0"), portage.auxdbkeys)) - - # we don't make overlay trees cache here, plus we don't trust portage.settings.categories - porttree_root = portage.portdb.porttree_root - conf = portage.config(config_profile_path=portage.settings.profile_path[:], \ - config_incrementals=portage.settings.incrementals[:]) - - conf["PORTDIR_OVERLAY"] = '' - conf.categories = portage.grabfile(os.path.join(porttree_root, "profiles", "categories")) - try: - i = conf.categories.index("virtual") - if i != -1: - conf.categories.remove(i) - except (ValueError, IndexError): - pass - - pdb = portage.portdbapi(porttree_root, conf) - - cp_all_list = pdb.cp_all() - import cache.util - - class percentage_noise_maker(cache.util.quiet_mirroring): - def __init__(self, dbapi): - self.dbapi = dbapi - self.cp_all = dbapi.cp_all() - l = len(self.cp_all) - self.call_update_min = 100000000 - self.min_cp_all = l/100.0 - self.count = 1 - self.pstr = '' - - def __iter__(self): - for x in self.cp_all: - self.count += 1 - if self.count > self.min_cp_all: - self.call_update_min = 0 - self.count = 0 - for y in self.dbapi.cp_list(x): - yield y - self.call_update_mine = 0 - - def update(self, *arg): - try: self.pstr = int(self.pstr) + 1 - except ValueError: self.pstr = 1 - sys.stdout.write("%s%i%%" % ("\b" * (len(str(self.pstr))+1), self.pstr)) - sys.stdout.flush() - self.call_update_min = 10000000 - - def finish(self, *arg): - sys.stdout.write("\b\b\b\b100%\n") - sys.stdout.flush() - + # Reload the whole config from scratch. + settings, trees, mtimedb = load_emerge_config(trees=trees) + portdb = trees[settings["ROOT"]]["porttree"].dbapi - if "--quiet" in myopts: - def quicky_cpv_generator(cp_all_list): - for x in cp_all_list: - for y in pdb.cp_list(x): - yield y - source = quicky_cpv_generator(pdb.cp_all()) - noise_maker = cache.util.quiet_mirroring() - else: - noise_maker = source = percentage_noise_maker(pdb) - cache.util.mirror_cache(source, cm, pdb.auxdb[porttree_root], eclass_cache=ec, verbose_instance=noise_maker) + if os.path.exists(myportdir+"/metadata/cache") and updatecache_flg: + action_metadata(settings, portdb, myopts) - sys.stdout.flush() + portage.global_updates( + settings, trees, mtimedb["updates"]) + mtimedb.commit() + # Reload the whole config from scratch. + settings, trees, mtimedb = load_emerge_config(trees=trees) + portdb = trees[settings["ROOT"]]["porttree"].dbapi - portage.portageexit() - reload(portage) - mybestpv=portage.portdb.xmatch("bestmatch-visible","sys-apps/portage") - mypvs=portage.best(portage.db[portage.root]["vartree"].dbapi.match("sys-apps/portage")) + mybestpv = portdb.xmatch("bestmatch-visible", "sys-apps/portage") + mypvs = portage.best( + trees[settings["ROOT"]]["vartree"].dbapi.match("sys-apps/portage")) - chk_updated_cfg_files() + chk_updated_cfg_files("/", settings.get("CONFIG_PROTECT","").split()) if myaction != "metadata": if os.access(portage.USER_CONFIG_PATH + "/bin/post_sync", os.X_OK): try: - portage.spawn(portage.USER_CONFIG_PATH + "/bin/post_sync " + dosyncuri, portage.settings, free=1) + portage.spawn(portage.USER_CONFIG_PATH + "/bin/post_sync " + dosyncuri, settings, free=1) except: print red(" * ")+bold("spawn failed of "+ portage.USER_CONFIG_PATH + "/bin/post_sync") @@ -2938,9 +3125,81 @@ if myaction in ["sync","metadata"] and (not "--help" in myopts): print red(" * ")+"that you update portage now, before any other packages are updated." print red(" * ")+"Please run 'emerge portage' and then update "+bold("ALL")+" of your" print red(" * ")+"configuration files." + print red(" * ")+"To update portage, run 'emerge portage'." print -elif myaction=="regen": - emergelog(" === regen") + +def action_metadata(settings, portdb, myopts): + portage.writemsg_stdout("\n>>> Updating Portage cache: ") + old_umask = os.umask(0002) + cachedir = os.path.normpath(settings.depcachedir) + if cachedir in ["/", "/bin", "/dev", "/etc", "/home", + "/lib", "/opt", "/proc", "/root", "/sbin", + "/sys", "/tmp", "/usr", "/var"]: + print >> sys.stderr, "!!! PORTAGE_DEPCACHEDIR IS SET TO A PRIMARY " + \ + "ROOT DIRECTORY ON YOUR SYSTEM." + print >> sys.stderr, \ + "!!! This is ALMOST CERTAINLY NOT what you want: '%s'" % cachedir + sys.exit(73) + if not os.path.exists(cachedir): + os.mkdir(cachedir) + + ec = portage.eclass_cache.cache(portdb.porttree_root) + myportdir = os.path.realpath(settings["PORTDIR"]) + cm = settings.load_best_module("portdbapi.metadbmodule")( + myportdir, "metadata/cache", portage.auxdbkeys[:]) + + import cache.util + + class percentage_noise_maker(cache.util.quiet_mirroring): + def __init__(self, dbapi): + self.dbapi = dbapi + self.cp_all = dbapi.cp_all() + l = len(self.cp_all) + self.call_update_min = 100000000 + self.min_cp_all = l/100.0 + self.count = 1 + self.pstr = '' + + def __iter__(self): + for x in self.cp_all: + self.count += 1 + if self.count > self.min_cp_all: + self.call_update_min = 0 + self.count = 0 + for y in self.dbapi.cp_list(x): + yield y + self.call_update_mine = 0 + + def update(self, *arg): + try: self.pstr = int(self.pstr) + 1 + except ValueError: self.pstr = 1 + sys.stdout.write("%s%i%%" % \ + ("\b" * (len(str(self.pstr))+1), self.pstr)) + sys.stdout.flush() + self.call_update_min = 10000000 + + def finish(self, *arg): + sys.stdout.write("\b\b\b\b100%\n") + sys.stdout.flush() + + if "--quiet" in myopts: + def quicky_cpv_generator(cp_all_list): + for x in cp_all_list: + for y in portdb.cp_list(x): + yield y + source = quicky_cpv_generator(portdb.cp_all()) + noise_maker = cache.util.quiet_mirroring() + else: + noise_maker = source = percentage_noise_maker(portdb) + cache.util.mirror_cache(source, cm, portdb.auxdb[myportdir], + eclass_cache=ec, verbose_instance=noise_maker) + + sys.stdout.flush() + os.umask(old_umask) + +def action_regen(settings, portdb): + xterm_titles = "notitles" not in settings.features + emergelog(xterm_titles, " === regen") #regenerate cache entries print "Regenerating cache entries... " try: @@ -2950,32 +3209,36 @@ elif myaction=="regen": except: pass sys.stdout.flush() - mynodes=portage.portdb.cp_all() + mynodes = portdb.cp_all() for x in mynodes: - mymatches=portage.portdb.xmatch("match-all",x) - if not "--quiet" in myopts: - print "processing",x + mymatches = portdb.xmatch("match-all",x) + portage.writemsg_stdout("processing %s\n" % x) for y in mymatches: try: - foo=portage.portdb.aux_get(y,["DEPEND"]) + foo = portdb.aux_get(y,["DEPEND"]) except SystemExit, e: # sys.exit is an exception... And consequently, we can't catch it. raise except Exception, e: print "\n error processing %(cpv)s, continuing... (%(e)s)" % {"cpv":y,"e":str(e)} print "done!" -# HELP action -elif "config"==myaction: + +def action_config(settings, trees, myopts, myfiles): if len(myfiles) != 1 or "system" in myfiles or "world" in myfiles: print red("!!! config can only take a single package atom at this time\n") sys.exit(1) if not is_valid_package_atom(myfiles[0]): - portage.writemsg("!!! '%s' is not a valid package atom.\n" % myfiles[0]) + portage.writemsg("!!! '%s' is not a valid package atom.\n" % myfiles[0], + noiselevel=-1) portage.writemsg("!!! Please check ebuild(5) for full details.\n") portage.writemsg("!!! (Did you specify a version but forget to prefix with '='?)\n") sys.exit(1) print - pkgs = portage.db[portage.root]["vartree"].dbapi.match(myfiles[0]) + try: + pkgs = trees[settings["ROOT"]]["vartree"].dbapi.match(myfiles[0]) + except ValueError, e: + # Multiple matches thrown from cpv_expand + pkgs = e.args[0] if len(pkgs) == 0: print "No packages found.\n" sys.exit(0) @@ -3010,26 +3273,43 @@ elif "config"==myaction: else: print "Configuring pkg..." print - ebuildpath = portage.db[portage.root]["vartree"].dbapi.findname(pkg) - mysettings = portage.config(clone=portage.settings) - portage.doebuild(ebuildpath,"config",portage.root,mysettings,debug=("--debug" in myopts),cleanup=True,tree="vartree") + ebuildpath = trees[settings["ROOT"]]["vartree"].dbapi.findname(pkg) + mysettings = portage.config(clone=settings) + portage.doebuild(ebuildpath, "config", settings["ROOT"], mysettings, + debug=(settings.get("PORTAGE_DEBUG", "") == 1), cleanup=True, + mydbapi=trees[settings["ROOT"]]["vartree"].dbapi, tree="vartree") print -# INFO action -elif "info"==myaction: +def action_info(settings, trees, myopts, myfiles): unameout=commands.getstatusoutput("uname -mrp")[1] - print getportageversion() - print "=================================================================" + print getportageversion(settings["PORTDIR"], settings["ROOT"], + settings.profile_path, settings["CHOST"], + trees[settings["ROOT"]]["vartree"].dbapi) + header_width = 65 + header_title = "System Settings" + if myfiles: + print header_width * "=" + print header_title.rjust(int(header_width/2 + len(header_title)/2)) + print header_width * "=" print "System uname: "+unameout - if os.path.exists(portage_const.EPREFIX+"/etc/gentoo-release"): - os.system("cat "+portage_const.EPREFIX+"/etc/gentoo-release") + gentoo_release = portage.grabfile(os.path.join( + settings["PORTAGE_CONFIGROOT"], "etc", "gentoo-release")) + if gentoo_release: + print gentoo_release[0] else: print "Unknown Host Operating System" + lastSync = portage.grabfile(os.path.join( + settings["PORTDIR"], "metadata", "timestamp.chk")) + print "Last Sync:", + if lastSync: + print lastSync[0] + else: + print "Unknown" output=commands.getstatusoutput("distcc --version") if not output[0]: print str(string.split(output[1],"\n",1)[0]), - if "distcc" in portage.features: + if "distcc" in settings.features: print "[enabled]" else: print "[disabled]" @@ -3037,20 +3317,20 @@ elif "info"==myaction: output=commands.getstatusoutput("ccache -V") if not output[0]: print str(string.split(output[1],"\n",1)[0]), - if "ccache" in portage.features: + if "ccache" in settings.features: print "[enabled]" else: print "[disabled]" myvars = ["sys-devel/autoconf", "sys-devel/automake", "virtual/os-headers", - "sys-devel/binutils","sys-devel/odcctools" ,"sys-devel/libtool", "dev-lang/python"] - myvars += portage_util.grabfile(portage.settings["PORTDIR"]+"/profiles/info_pkgs") + "sys-devel/binutils", "sys-devel/libtool", "dev-lang/python"] + myvars += portage_util.grabfile(settings["PORTDIR"]+"/profiles/info_pkgs") myvars = portage_util.unique_array(myvars) myvars.sort() for x in myvars: if portage.isvalidatom(x): - pkg_matches = portage.db["/"]["vartree"].dbapi.match(x) + pkg_matches = trees["/"]["vartree"].dbapi.match(x) pkgs = [] for y in pkg_matches: mycpv = portage.catpkgsplit(y) @@ -3066,24 +3346,24 @@ elif "info"==myaction: else: print "%-20s %s" % (x+":", "[NOT VALID]") - libtool_vers = string.join(portage.db["/"]["vartree"].dbapi.match("sys-devel/libtool"), ",") + libtool_vers = string.join(trees["/"]["vartree"].dbapi.match("sys-devel/libtool"), ",") if "--verbose" in myopts: - myvars=portage.settings.keys() + myvars=settings.keys() else: - myvars = ['DEFAULT_PATH', 'GENTOO_MIRRORS', 'CONFIG_PROTECT', 'CONFIG_PROTECT_MASK', + myvars = ['GENTOO_MIRRORS', 'CONFIG_PROTECT', 'CONFIG_PROTECT_MASK', 'PORTDIR', 'DISTDIR', 'PKGDIR', 'PORTAGE_TMPDIR', - 'PORTDIR_OVERLAY', 'PREFIX', 'USE', 'CHOST', 'CFLAGS', 'CXXFLAGS', + 'PORTDIR_OVERLAY', 'USE', 'CHOST', 'CFLAGS', 'CXXFLAGS', 'ACCEPT_KEYWORDS', 'SYNC', 'FEATURES', 'EMERGE_DEFAULT_OPTS'] - myvars.extend(portage_util.grabfile(portage.settings["PORTDIR"]+"/profiles/info_vars")) + myvars.extend(portage_util.grabfile(settings["PORTDIR"]+"/profiles/info_vars")) myvars = portage_util.unique_array(myvars) unset_vars = [] myvars.sort() for x in myvars: - if portage.settings.has_key(x): - print x+'="'+portage.settings[x]+'"' + if settings.has_key(x): + print x+'="'+settings[x]+'"' else: unset_vars.append(x) if unset_vars: @@ -3096,12 +3376,76 @@ elif "info"==myaction: if "cvs_id_string" in dir(module): print "%s: %s" % (str(x), str(module.cvs_id_string)) -# SEARCH action -elif "search"==myaction: + # See if we can find any packages installed matching the strings + # passed on the command line + mypkgs = [] + vardb = trees[settings["ROOT"]]["vartree"].dbapi + for x in myfiles: + mypkgs.extend(vardb.match(x)) + + # If some packages were found... + if mypkgs: + # Get our global settings (we only print stuff if it varies from + # the current config) + mydesiredvars = [ 'CHOST', 'CFLAGS', 'CXXFLAGS' ] + auxkeys = mydesiredvars + [ "USE", "IUSE"] + global_vals = {} + pkgsettings = portage.config(clone=settings) + + for myvar in mydesiredvars: + global_vals[myvar] = set(settings.get(myvar, "").split()) + + # Loop through each package + # Only print settings if they differ from global settings + header_printed = False + for pkg in mypkgs: + # Get all package specific variables + auxvalues = vardb.aux_get(pkg, auxkeys) + valuesmap = {} + for i in xrange(len(auxkeys)): + valuesmap[auxkeys[i]] = set(auxvalues[i].split()) + diff_values = {} + for myvar in mydesiredvars: + # If the package variable doesn't match the + # current global variable, something has changed + # so set diff_found so we know to print + if valuesmap[myvar] != global_vals[myvar]: + diff_values[myvar] = valuesmap[myvar] + valuesmap["USE"] = valuesmap["USE"].intersection(valuesmap["IUSE"]) + pkgsettings.reset() + pkgsettings.setcpv(pkg) + if valuesmap["IUSE"].intersection(pkgsettings["USE"].split()) != \ + valuesmap["USE"]: + diff_values["USE"] = valuesmap["USE"] + # If a difference was found, print the info for + # this package. + if diff_values: + + # If we have not yet printed the header, + # print it now + if not header_printed: + header_title = "Package Settings" + print header_width * "=" + print header_title.rjust(int(header_width/2 + len(header_title)/2)) + print header_width * "=" + header_printed = True + + # Print package info + print "%s was built with the following:" % pkg + for myvar in mydesiredvars + ["USE"]: + if myvar in diff_values: + mylist = list(diff_values[myvar]) + mylist.sort() + print "%s=\"%s\"" % (myvar, " ".join(mylist)) + print + +def action_search(settings, portdb, vartree, myopts, myfiles, spinner): if not myfiles: print "emerge: no search terms provided." else: - searchinstance = search() + searchinstance = search(settings, portdb, + vartree, spinner, "--searchdesc" in myopts, + "--quiet" not in myopts) for mysearch in myfiles: try: searchinstance.execute(mysearch) @@ -3109,30 +3453,37 @@ elif "search"==myaction: print "\n!!! Regular expression error in \"%s\": %s" % ( mysearch, comment ) sys.exit(1) searchinstance.output() -elif "unmerge"==myaction or "prune"==myaction or "clean"==myaction: - if 1==unmerge(myaction, myfiles): - post_emerge() -elif "depclean"==myaction: +def action_depclean(settings, trees, ldpath_mtimes, + myopts, spinner): # Kill packages that aren't explicitly merged or are required as a # dependency of another package. World file is explicit. + warn_prefix = colorize("BAD", "*** WARNING *** ") print - print red("*** WARNING ***")+" --depclean is known to be broken. It is highly recommended" - print red("*** WARNING ***")+" that "+green("`emerge --update --newuse --deep world`")+" be ran before" - print red("*** WARNING ***")+" commencing. However, using --depclean may still break link" - print red("*** WARNING ***")+" level consistency within your system. "+green("`revdep-rebuild`") - print red("*** WARNING ***")+" from app-portage/gentoolkit can help to detect breakage." - print red("*** WARNING ***") - print red("*** WARNING ***")+" Also study the list of packages to be cleaned for any" - print red("*** WARNING ***")+" obvious mistakes. Packages can be manually added to the" - print red("*** WARNING ***")+" world list by running "+green("`emerge --noreplace `")+"." - print red("*** WARNING ***") - print red("*** WARNING ***")+" "+bold("Make sure you have a backup.") - - syslist=getlist("system") - worldlist=getlist("world") - myvarlist=portage.vardbapi(portage.root).cp_all() + print warn_prefix + "Depclean may break link level dependencies. Thus, it is" + print warn_prefix + "recommended to use a tool such as " + good("`revdep-rebuild`") + " (from" + print warn_prefix + "app-portage/gentoolkit) in order to detect such breakage." + print warn_prefix + print warn_prefix + "Also study the list of packages to be cleaned for any obvious" + print warn_prefix + "mistakes. Packages that are part of the world set will always" + print warn_prefix + "be kept. They can be manually added to this set with" + print warn_prefix + good("`emerge --noreplace `") + "." + print warn_prefix + print warn_prefix + "As a safety measure, depclean will not remove any packages" + print warn_prefix + "unless *all* required dependencies have been resolved. As a" + print warn_prefix + "consequence, it is often necessary to run " + print warn_prefix + good("`emerge --update --deep world`") + " prior to depclean." + + xterm_titles = "notitles" not in settings.features + portdb = trees[settings["ROOT"]]["porttree"].dbapi + vartree = trees[settings["ROOT"]]["vartree"] + vardb = vartree.dbapi + syslist = getlist(settings, "system") + worldlist = getlist(settings, "world") + fakedb = portage.fakedbapi(settings=settings) + emptydb = portage.fakedbapi(settings=settings) + myvarlist = vardb.cpv_all() if not syslist: print "\n!!! You have no system list.", @@ -3142,95 +3493,89 @@ elif "depclean"==myaction: print "\n!!! You have no installed package database (%s)." % portage.VDB_PATH, if not (syslist and worldlist and myvarlist): - print "\n!!! Proceeding will break your installation.\n" - countdown(EMERGE_WARNING_DELAY, ">>> Depclean") + print "\n!!! Proceeding "+(syslist and myvarlist and "may" or "will") + print " break your installation.\n" + if "--pretend" not in myopts: + countdown(int(settings["EMERGE_WARNING_DELAY"]), ">>> Depclean") if not "--pretend" in myopts: #just check pretend, since --ask implies pretend - emergelog(" >>> depclean") - - mydepgraph=depgraph(myaction,myopts) + emergelog(xterm_titles, " >>> depclean") - if not ("--quiet" in myopts): + if "--quiet" not in myopts: print "\nCalculating dependencies ", - if not mydepgraph.xcreate("world"): - print "\n!!! Failed to create deptree." - sys.exit(1) - if not ("--quiet" in myopts): - print "\b\b... done!" - - if ("--usepkgonly" in myopts) and mydepgraph.missingbins: - sys.stderr.write(red("The following binaries are not available for merging...\n")) - for x in mydepgraph.missingbins: - sys.stderr.write(" "+str(x)+"\n") - sys.stderr.write("\nThese are required by '--usepkgonly' -- Terminating.\n\n") - sys.exit(1) - - alldeps=mydepgraph.digraph.allnodes() - - if not alldeps: - print "!!! You have no dependencies. Impossible. Bug." - sys.exit(1) - reallist=[] - for x in alldeps: - myparts=portage.catpkgsplit(string.split(x)[2]) - if not myparts: - sys.stderr.write( - red("!!! There appears to be a problem with the following package:\n")+ - red("!!! "+str(string.split(x)[2])+"\n\n")+ - "!!! Please ensure that blocking/conflicting packages are not merged."+ - "!!! 'emerge -p "+str(string.split(x)[2])+"\n\n") - if ("--pretend" not in myopts) and ("--ask" not in myopts): - countdown(EMERGE_WARNING_DELAY, "*** Continuing") + remaining_atoms = [(atom, 'world') for atom in worldlist if vardb.match(atom)] + remaining_atoms += [(atom, 'system') for atom in syslist if vardb.match(atom)] + unresolveable = {} + + while remaining_atoms: + atom, parent = remaining_atoms.pop() + pkgs = vardb.match(atom) + if not pkgs: + if not atom.startswith("!"): + if atom in unresolveable: + unresolveable[atom].append(parent) + else: + unresolveable[atom] = [parent] continue + # Could put slot checking here to ensure that there aren't two + # packages with the same slot... + for pkg in pkgs: + if fakedb.cpv_exists(pkg): + continue + spinner.update() + fakedb.cpv_inject(pkg) + mydb = portdb.xmatch("match-visible", "="+pkg) and portdb or vardb + if myopts.get("--with-bdeps", "y") == "y": + depstr = " ".join(mydb.aux_get(pkg, + ["DEPEND", "RDEPEND", "PDEPEND"])) + else: + depstr = " ".join(mydb.aux_get(pkg, + ["RDEPEND", "PDEPEND"])) + usedef = vardb.aux_get(pkg, ["USE"])[0].split() + success, atoms = portage.dep_check(depstr, emptydb, settings, + myuse=usedef, trees=trees, myroot=settings["ROOT"]) + if not success: + print "\n\n" + print "Error occurred while processing",pkg + print str(atoms) + return + for atom in atoms: + remaining_atoms.append((atom, pkg)) + + if "--quiet" not in myopts: + print "\b\b... done!\n" + + if unresolveable: + print "Dependencies could not be completely resolved due to" + print "the following required packages not being installed:" + print + for atom in unresolveable: + print atom, "required by", " ".join(unresolveable[atom]) + print + print "Have you forgotten to run " + good("`emerge --update --deep world`") + " prior to depclean?" + print + return - catpack=myparts[0]+"/"+myparts[1] - if catpack not in reallist: - reallist.append(catpack) - - cleanlist=[] - for x in myvarlist: - if x not in reallist: - if x not in cleanlist: - cleanlist.append(x) - - for x in syslist+worldlist: - myparts = portage.catpkgsplit(x) - if myparts: - if myparts[0][0] in ('<','>','='): - myparts[0] = myparts[0][1:] - if myparts[0][0] in ('<','>','='): - myparts[0] = myparts[0][1:] - catpack=myparts[0]+"/"+myparts[1] - else: - catpack=x - if catpack in cleanlist: - cleanlist.remove(catpack) - - #print "\n\n\nCleaning: " - #for x in cleanlist: - # print x - #print + cleanlist = [pkg for pkg in vardb.cpv_all() if not fakedb.cpv_exists(pkg)] if len(cleanlist): - unmerge("unmerge", cleanlist) + unmerge(settings, myopts, vartree, "unmerge", cleanlist, ldpath_mtimes) - print print "Packages installed: "+str(len(myvarlist)) print "Packages in world: "+str(len(worldlist)) print "Packages in system: "+str(len(syslist)) - print "Unique package names: "+str(len(reallist)) - print "Required packages: "+str(len(alldeps)) + print "Unique package names: "+str(len(myvarlist)) + print "Required packages: "+str(len(fakedb.cpv_all())) if "--pretend" in myopts: print "Number to remove: "+str(len(cleanlist)) else: print "Number removed: "+str(len(cleanlist)) - post_emerge() -# "update", "system", or just process files: -else: +def action_build(settings, trees, mtimedb, + myopts, myaction, myfiles, spinner): + ldpath_mtimes = mtimedb["ldpath"] favorites=[] - syslist=getlist("system") if ("--ask" in myopts or "--pretend" in myopts) and not "--quiet" in myopts: action = "" if "--fetchonly" in myopts or "--fetch-all-uri" in myopts: @@ -3246,33 +3591,44 @@ else: print darkgreen("These are the packages that would be %s, in order:") % action print + # validate the state of the resume data + # so that we can make assumptions later. + for k in ("resume", "resume_backup"): + if k in mtimedb: + if "mergelist" in mtimedb[k]: + if not mtimedb[k]["mergelist"]: + del mtimedb[k] + else: + del mtimedb[k] + if "--resume" in myopts and \ - ("resume" in portage.mtimedb or - "resume_backup" in portage.mtimedb): - if "resume" not in portage.mtimedb: - portage.mtimedb["resume"] = portage.mtimedb["resume_backup"] - del portage.mtimedb["resume_backup"] - portage.commit_mtimedb() - myresumeopts=portage.mtimedb["resume"]["myopts"][:] + ("resume" in mtimedb or + "resume_backup" in mtimedb): + if "resume" not in mtimedb: + mtimedb["resume"] = mtimedb["resume_backup"] + del mtimedb["resume_backup"] + mtimedb.commit() + # XXX: "myopts" is a list for backward compatibility. + myresumeopts = dict([(k,True) for k in mtimedb["resume"]["myopts"]]) for opt in ("--skipfirst", "--ask", "--tree"): - while opt in myresumeopts: - myresumeopts.remove(opt) + myresumeopts.pop(opt, None) - for myopt in myopts: + for myopt, myarg in myopts.iteritems(): if myopt not in myresumeopts: - myresumeopts.append(myopt) + myresumeopts[myopt] = myarg myopts=myresumeopts - mydepgraph=depgraph("resume",myopts) - if "--resume" not in myopts: - myopts+=["--resume"] + myparams = create_depgraph_params(myopts, myaction) + mydepgraph = depgraph(settings, trees, + myopts, myparams, spinner) else: if ("--resume" in myopts): - del myopts[myopts.index("--resume")] print darkgreen("emerge: It seems we have nothing to resume...") sys.exit(0) - mydepgraph=depgraph(myaction,myopts) + myparams = create_depgraph_params(myopts, myaction) + mydepgraph = depgraph(settings, trees, + myopts, myparams, spinner) if myaction in ["system","world"]: if not ("--quiet" in myopts): print "Calculating",myaction,"dependencies ", @@ -3283,15 +3639,14 @@ else: if not ("--quiet" in myopts): print "\b\b... done!" else: - if not myfiles: - print "emerge: please tell me what to do." - help() - sys.exit(1) - #we don't have any files to process; skip this step and exit if not ("--quiet" in myopts): print "Calculating dependencies ", sys.stdout.flush() - retval,favorites=mydepgraph.select_files(myfiles) + try: + retval, favorites = mydepgraph.select_files(myfiles) + except portage_exception.PackageNotFound, e: + portage.writemsg("\n!!! %s\n" % str(e), noiselevel=-1) + sys.exit(1) if not retval: sys.exit(1) if not ("--quiet" in myopts): @@ -3308,32 +3663,41 @@ else: if "--ask" in myopts: if "--resume" in myopts: - mydepgraph.display(portage.mtimedb["resume"]["mergelist"]) - prompt="Do you want me to resume merging these packages?" + validate_merge_list(trees, mtimedb["resume"]["mergelist"]) + mymergelist = mtimedb["resume"]["mergelist"] + if "--skipfirst" in myopts: + mymergelist = mymergelist[1:] + if len(mymergelist) == 0: + print colorize("INFORM", "emerge: It seems we have nothing to resume...") + sys.exit(0) + mydepgraph.display(mymergelist) + prompt="Would you like to resume merging these packages?" else: - mydepgraph.display(mydepgraph.altlist()) + mydepgraph.display( + mydepgraph.altlist(reversed=("--tree" in myopts))) mergecount=0 for x in mydepgraph.altlist(): - if x[3]!="nomerge": + if x[0] != "blocks" and x[3] != "nomerge": mergecount+=1 #check for blocking dependencies if x[0]=="blocks" and "--fetchonly" not in myopts and "--fetch-all-uri" not in myopts: print "\n!!! Error: The above package list contains packages which cannot be installed" print "!!! at the same time on the same system." - print + if "--quiet" not in myopts: + show_blocker_docs_link() sys.exit(1) if mergecount==0: - if portage.settings["AUTOCLEAN"] and "yes"==portage.settings["AUTOCLEAN"]: - prompt="Nothing to merge; do you want me to auto-clean packages?" + if settings["AUTOCLEAN"] and "yes"==settings["AUTOCLEAN"]: + prompt="Nothing to merge; would you like to auto-clean packages?" else: print print "Nothing to merge; quitting." print sys.exit(0) elif "--fetchonly" in myopts or "--fetch-all-uri" in myopts: - prompt="Do you want me to fetch the source files for these packages?" + prompt="Would you like to fetch the source files for these packages?" else: - prompt="Do you want me to merge these packages?" + prompt="Would you like to merge these packages?" print if userquery(prompt)=="No": print @@ -3341,39 +3705,65 @@ else: print sys.exit(0) # Don't ask again (e.g. when auto-cleaning packages after merge) - myopts.remove("--ask") + del myopts["--ask"] if ("--pretend" in myopts) and not ("--fetchonly" in myopts or "--fetch-all-uri" in myopts): if ("--resume" in myopts): - mydepgraph.display(portage.mtimedb["resume"]["mergelist"]) + validate_merge_list(trees, mtimedb["resume"]["mergelist"]) + mymergelist = mtimedb["resume"]["mergelist"] + if "--skipfirst" in myopts: + mymergelist = mymergelist[1:] + if len(mymergelist) == 0: + print colorize("INFORM", "emerge: It seems we have nothing to resume...") + sys.exit(0) + mydepgraph.display(mymergelist) else: - mydepgraph.display(mydepgraph.altlist()) + mydepgraph.display( + mydepgraph.altlist(reversed=("--tree" in myopts))) else: if ("--buildpkgonly" in myopts): - if not mydepgraph.digraph.hasallzeros(): + if not mydepgraph.digraph.hasallzeros(ignore_priority=digraph.MEDIUM): print "\n!!! --buildpkgonly requires all dependencies to be merged." print "!!! Cannot merge requested packages. Merge deps and try again.\n" sys.exit(1) if ("--resume" in myopts): - favorites=portage.mtimedb["resume"]["favorites"] - mydepgraph.merge(portage.mtimedb["resume"]["mergelist"]) + favorites=mtimedb["resume"]["favorites"] + mergetask = MergeTask(settings, trees, myopts) + if "--fetchonly" in myopts: + """ parallel-fetch uses --resume --fetchonly and we don't want + it to write the mtimedb""" + mtimedb.filename = None + time.sleep(3) # allow the parent to have first fetch + retval = mergetask.merge( + mtimedb["resume"]["mergelist"], favorites, mtimedb) + if retval != os.EX_OK: + sys.exit(retval) else: - if "resume" in portage.mtimedb and \ - "mergelist" in portage.mtimedb["resume"] and \ - len(portage.mtimedb["resume"]["mergelist"]) > 1: - portage.mtimedb["resume_backup"] = portage.mtimedb["resume"] - del portage.mtimedb["resume"] - portage.commit_mtimedb() - portage.mtimedb["resume"]={} - portage.mtimedb["resume"]["myopts"]=myopts - portage.mtimedb["resume"]["favorites"]=favorites + if "resume" in mtimedb and \ + "mergelist" in mtimedb["resume"] and \ + len(mtimedb["resume"]["mergelist"]) > 1: + mtimedb["resume_backup"] = mtimedb["resume"] + del mtimedb["resume"] + mtimedb.commit() + mtimedb["resume"]={} + # XXX: Stored as a list for backward compatibility. + mtimedb["resume"]["myopts"] = \ + [k for k in myopts if myopts[k] is True] + mtimedb["resume"]["favorites"]=favorites if ("--digest" in myopts) and not ("--fetchonly" in myopts or "--fetch-all-uri" in myopts): for pkgline in mydepgraph.altlist(): if pkgline[0]=="ebuild" and pkgline[3]=="merge": - y=portage.portdb.findname(pkgline[2]) - tmpsettings = portage.config(clone=portage.settings) - retval=portage.doebuild(y,"digest",portage.root,tmpsettings,edebug,("--pretend" in myopts),tree="porttree") + y = trees[pkgline[1]]["porttree"].dbapi.findname(pkgline[2]) + tmpsettings = portage.config(clone=settings) + edebug = 0 + if settings.get("PORTAGE_DEBUG", "") == "1": + edebug = 1 + retval = portage.doebuild( + y, "digest", settings["ROOT"], tmpsettings, edebug, + ("--pretend" in myopts), + mydbapi=trees[pkgline[1]]["porttree"].dbapi, + tree="porttree") if "--fetchonly" in myopts or "--fetch-all-uri" in myopts: pkglist = [] for pkg in mydepgraph.altlist(): @@ -3381,11 +3771,494 @@ else: pkglist.append(pkg) else: pkglist = mydepgraph.altlist() - mydepgraph.merge(pkglist) - - if portage.mtimedb.has_key("resume"): - del portage.mtimedb["resume"] - if portage.settings["AUTOCLEAN"] and "yes"==portage.settings["AUTOCLEAN"]: + mergetask = MergeTask(settings, trees, myopts) + retval = mergetask.merge(pkglist, favorites, mtimedb) + if retval != os.EX_OK: + sys.exit(retval) + + if mtimedb.has_key("resume"): + del mtimedb["resume"] + if settings["AUTOCLEAN"] and "yes"==settings["AUTOCLEAN"]: print ">>> Auto-cleaning packages..." - unmerge("clean", ["world"]) - post_emerge() + vartree = trees[settings["ROOT"]]["vartree"] + unmerge(settings, myopts, vartree, "clean", ["world"], + ldpath_mtimes) + else: + portage.writemsg_stdout(colorize("WARN", "WARNING:") + + " AUTOCLEAN is disabled. This can cause serious" + + " problems due to overlapping packages.\n") + +def multiple_actions(action1, action2): + sys.stderr.write("\n!!! Multiple actions requested... Please choose one only.\n") + sys.stderr.write("!!! '%s' or '%s'\n\n" % (action1, action2)) + sys.exit(1) + +def parse_opts(tmpcmdline): + myaction=None + myopts = {} + myfiles=[] + + global actions, options, shortmapping + + longopt_aliases = {"--cols":"--columns", "--skip-first":"--skipfirst"} + argument_options = { + "--with-bdeps": { + "help":"include unnecessary build time dependencies", + "type":"choice", + "choices":("y", "n") + } + } + + from optparse import OptionParser + parser = OptionParser() + if parser.has_option("--help"): + parser.remove_option("--help") + + for action_opt in actions: + parser.add_option("--" + action_opt, action="store_true", + dest=action_opt.replace("-", "_"), default=False) + for myopt in options: + parser.add_option(myopt, action="store_true", + dest=myopt.lstrip("--").replace("-", "_"), default=False) + for shortopt, longopt in shortmapping.iteritems(): + parser.add_option("-" + shortopt, action="store_true", + dest=longopt.lstrip("--").replace("-", "_"), default=False) + for myalias, myopt in longopt_aliases.iteritems(): + parser.add_option(myalias, action="store_true", + dest=myopt.lstrip("--").replace("-", "_"), default=False) + + for myopt, kwargs in argument_options.iteritems(): + parser.add_option(myopt, + dest=myopt.lstrip("--").replace("-", "_"), **kwargs) + + myoptions, myargs = parser.parse_args(args=tmpcmdline) + + for myopt in options: + v = getattr(myoptions, myopt.lstrip("--").replace("-", "_")) + if v: + myopts[myopt] = True + + for myopt in argument_options: + v = getattr(myoptions, myopt.lstrip("--").replace("-", "_"), None) + if v is not None: + myopts[myopt] = v + + for action_opt in actions: + v = getattr(myoptions, action_opt.replace("-", "_")) + if v: + if myaction: + multiple_actions(myaction, action_opt) + sys.exit(1) + myaction = action_opt + + for x in myargs: + if x in actions: + if x not in ["system", "world"]: + print red("*** Deprecated use of action '%s', use '--%s' instead" % (x,x)) + if myaction: + multiple_actions(myaction, x) + sys.exit(1) + myaction=x + else: + myfiles.append(x) + + return myaction, myopts, myfiles + +def validate_ebuild_environment(trees): + for myroot in trees: + mysettings = trees[myroot]["vartree"].settings + if not mysettings.get("ARCH", None): + print >> sys.stderr, bad("\a!!! ARCH is not set... " + \ + "Are you missing the '%setc/make.profile' symlink?" % \ + mysettings["PORTAGE_CONFIGROOT"]) + print >> sys.stderr, bad("\a!!! Is the symlink correct? " + \ + "Is your portage tree complete?\n") + sys.exit(9) + del myroot, mysettings + +def load_emerge_config(trees=None): + kwargs = {} + for k, envvar in (("config_root", "PORTAGE_CONFIGROOT"), ("target_root", "ROOT")): + kwargs[k] = os.environ.get(envvar, "/") + trees = portage.create_trees(trees=trees, **kwargs) + + settings = trees["/"]["vartree"].settings + + for myroot in trees: + if myroot != "/": + settings = trees[myroot]["vartree"].settings + break + + mtimedbfile = os.path.join("/", portage.CACHE_PATH.lstrip(os.path.sep), "mtimedb") + mtimedb = portage.MtimeDB(mtimedbfile) + return settings, trees, mtimedb + +def adjust_config(myopts, settings): + """Make emerge specific adjustments to the config.""" + + # To enhance usability, make some vars case insensitive by forcing them to + # lower case. + for myvar in ("AUTOCLEAN", "NOCOLOR"): + if myvar in settings: + settings[myvar] = settings[myvar].lower() + settings.backup_changes(myvar) + del myvar + + # Kill noauto as it will break merges otherwise. + if "noauto" in settings.features: + while "noauto" in settings.features: + settings.features.remove("noauto") + settings["FEATURES"] = " ".join(settings.features) + settings.backup_changes("FEATURES") + + CLEAN_DELAY = 5 + try: + CLEAN_DELAY = int(settings.get("CLEAN_DELAY", str(CLEAN_DELAY))) + except ValueError, e: + portage.writemsg("!!! %s\n" % str(e), noiselevel=-1) + portage.writemsg("!!! Unable to parse integer: CLEAN_DELAY='%s'\n" % \ + settings["CLEAN_DELAY"], noiselevel=-1) + settings["CLEAN_DELAY"] = str(CLEAN_DELAY) + settings.backup_changes("CLEAN_DELAY") + + EMERGE_WARNING_DELAY = 10 + try: + EMERGE_WARNING_DELAY = int(settings.get( + "EMERGE_WARNING_DELAY", str(EMERGE_WARNING_DELAY))) + except ValueError, e: + portage.writemsg("!!! %s\n" % str(e), noiselevel=-1) + portage.writemsg("!!! Unable to parse integer: EMERGE_WARNING_DELAY='%s'\n" % \ + settings["EMERGE_WARNING_DELAY"], noiselevel=-1) + settings["EMERGE_WARNING_DELAY"] = str(EMERGE_WARNING_DELAY) + settings.backup_changes("EMERGE_WARNING_DELAY") + + if "--quiet" in myopts: + settings["PORTAGE_QUIET"]="1" + settings.backup_changes("PORTAGE_QUIET") + + # Set so that configs will be merged regardless of remembered status + if ("--noconfmem" in myopts): + settings["NOCONFMEM"]="1" + settings.backup_changes("NOCONFMEM") + + # Set various debug markers... They should be merged somehow. + PORTAGE_DEBUG = 0 + try: + PORTAGE_DEBUG = int(settings.get("PORTAGE_DEBUG", str(PORTAGE_DEBUG))) + if PORTAGE_DEBUG not in (0, 1): + portage.writemsg("!!! Invalid value: PORTAGE_DEBUG='%i'\n" % \ + PORTAGE_DEBUG, noiselevel=-1) + portage.writemsg("!!! PORTAGE_DEBUG must be either 0 or 1\n", + noiselevel=-1) + PORTAGE_DEBUG = 0 + except ValueError, e: + portage.writemsg("!!! %s\n" % str(e), noiselevel=-1) + portage.writemsg("!!! Unable to parse integer: PORTAGE_DEBUG='%s'\n" %\ + settings["PORTAGE_DEBUG"], noiselevel=-1) + del e + if "--debug" in myopts: + PORTAGE_DEBUG = 1 + settings["PORTAGE_DEBUG"] = str(PORTAGE_DEBUG) + settings.backup_changes("PORTAGE_DEBUG") + + # Set color output + if "--nocolor" in myopts: + settings["NOCOLOR"] = "true" + settings.backup_changes("NOCOLOR") + +def emerge_main(): + # Portage needs to ensure a sane umask for the files it creates. + os.umask(022) + settings, trees, mtimedb = load_emerge_config() + portdb = trees[settings["ROOT"]]["porttree"].dbapi + portage.global_updates(settings, trees, mtimedb["updates"]) + mtimedb.commit() + # Reload the whole config from scratch. + settings, trees, mtimedb = load_emerge_config(trees=trees) + portdb = trees[settings["ROOT"]]["porttree"].dbapi + + ldpath_mtimes = mtimedb["ldpath"] + xterm_titles = "notitles" not in settings.features + + if settings.get("NOCOLOR","").lower() in ("yes","true"): + nocolor() + elif (not sys.stdout.isatty()) and \ + settings.get("NOCOLOR","").lower() != "no": + # revdep-rebuild exports NOCOLOR=false, so for now, don't allow + # NOCOLOR=false to override the isatty() check. This can be fixed + # in revdep-rebuild by using the --nocolor option when appropriate + # instead of exporting NOCOLOR. + nocolor() + + tmpcmdline = [] + if "--ignore-default-opts" not in tmpcmdline: + tmpcmdline.extend(settings["EMERGE_DEFAULT_OPTS"].split()) + tmpcmdline.extend(sys.argv[1:]) + myaction, myopts, myfiles = parse_opts(tmpcmdline) + + for myroot in trees: + mysettings = trees[myroot]["vartree"].settings + mysettings.unlock() + adjust_config(myopts, mysettings) + mysettings.lock() + del myroot, mysettings + + spinner = stdout_spinner() + if "candy" in settings.features: + spinner.update = spinner.update_scroll + + portage.deprecated_profile_check() + + try: + os.nice(int(settings.get("PORTAGE_NICENESS", "0"))) + except (OSError, ValueError), e: + portage.writemsg("!!! Failed to change nice value to '%s'\n" % \ + settings["PORTAGE_NICENESS"]) + portage.writemsg("!!! %s\n" % str(e)) + del e + + #Freeze the portdbapi for enhanced performance: + for myroot in trees: + trees[myroot]["porttree"].dbapi.freeze() + del myroot + + if "moo" in myfiles: + print """ + + Larry loves Gentoo (""" + os.uname()[0] + """) + + _______________________ +< Have you mooed today? > + ----------------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || + +""" + + if (myaction in ["world", "system"]) and myfiles: + print "emerge: please specify a package class (\"world\" or \"system\") or individual packages, but not both." + sys.exit(1) + + for x in myfiles: + ext = os.path.splitext(x)[1] + if (ext == ".ebuild" or ext == ".tbz2") and os.path.exists(os.path.abspath(x)): + print "emerging by path implies --oneshot... adding --oneshot to options." + print colorize("BAD", "\n*** emerging by path is broken and may not always work!!!\n") + break + + if ("--tree" in myopts) and ("--columns" in myopts): + print "emerge: can't specify both of \"--tree\" and \"--columns\"." + sys.exit(1) + + if ("--quiet" in myopts): + spinner.update = spinner.update_quiet + portage_util.noiselimit = -1 + + # Always create packages if FEATURES=buildpkg + # Imply --buildpkg if --buildpkgonly + if ("buildpkg" in settings.features) or ("--buildpkgonly" in myopts): + if "--buildpkg" not in myopts: + myopts["--buildpkg"] = True + + # Also allow -S to invoke search action (-sS) + if ("--searchdesc" in myopts): + if myaction and myaction != "search": + myfiles.append(myaction) + if "--search" not in myopts: + myopts["--search"] = True + myaction = "search" + + # Always try and fetch binary packages if FEATURES=getbinpkg + if ("getbinpkg" in settings.features): + myopts["--getbinpkg"] = True + + if "--skipfirst" in myopts and "--resume" not in myopts: + myopts["--resume"] = True + + if ("--getbinpkgonly" in myopts) and not ("--usepkgonly" in myopts): + myopts["--usepkgonly"] = True + + if ("--getbinpkgonly" in myopts) and not ("--getbinpkg" in myopts): + myopts["--getbinpkg"] = True + + if ("--getbinpkg" in myopts) and not ("--usepkg" in myopts): + myopts["--usepkg"] = True + + # Also allow -K to apply --usepkg/-k + if ("--usepkgonly" in myopts) and not ("--usepkg" in myopts): + myopts["--usepkg"] = True + + # Also allow -l to apply --pretend/-p, but if already in --ask mode + if ("--changelog" in myopts) and not (("--pretend" in myopts) or ("--ask" in myopts)): + print ">>> --changelog implies --pretend... adding --pretend to options." + myopts["--pretend"] = True + + # Allow -p to remove --ask + if ("--pretend" in myopts) and ("--ask" in myopts): + print ">>> --pretend disables --ask... removing --ask from options." + del myopts["--ask"] + + # forbid --ask when not in a terminal + # note: this breaks `emerge --ask | tee logfile`, but that doesn't work anyway. + if ("--ask" in myopts) and (not sys.stdin.isatty()): + portage.writemsg("!!! \"--ask\" should only be used in a terminal. Exiting.\n", + noiselevel=-1) + sys.exit(1) + + if settings.get("PORTAGE_DEBUG", "") == "1": + portage.debug=1 + if "python-trace" in settings.features: + import portage_debug + portage_debug.set_trace(True) + + if ("--resume" in myopts): + if "--tree" in myopts: + print "* --tree is currently broken with --resume. Disabling..." + del myopts["--tree"] + + # Set color output + if "--nocolor" in myopts or \ + settings["NOCOLOR"] in ("yes","true"): + nocolor() + + if not ("--quiet" in myopts): + if not sys.stdout.isatty() or ("--nospinner" in myopts): + spinner.update = spinner.update_basic + + if "--version" in myopts: + print getportageversion(settings["PORTDIR"], settings["ROOT"], + settings.profile_path, settings["CHOST"], + trees[settings["ROOT"]]["vartree"].dbapi) + sys.exit(0) + elif "--help" in myopts: + emergehelp.help(myaction, myopts, havecolor) + sys.exit(0) + + if portage.wheelgid == portage.portage_gid: + print "emerge: wheel group use is being deprecated. Please update group and passwd to" + print " include the portage user as noted above, and then use group portage." + + if "--debug" in myopts: + print "myaction", myaction + print "myopts", myopts + + if not myaction and not myfiles and "--resume" not in myopts: + emergehelp.help(myaction, myopts, havecolor) + sys.exit(1) + + # check if root user is the current user for the actions where emerge needs this + if portage.secpass < 2: + # We've already allowed "--version" and "--help" above. + if "--pretend" not in myopts and \ + myaction not in ("search","info"): + if portage.secpass >= 1: + if "--fetchonly" not in myopts and \ + "--fetch-all-uri" not in myopts and \ + myaction not in ("metadata", "regen"): + print "emerge: root access required." + sys.exit(1) + else: + print "emerge: root access required." + sys.exit(1) + + disable_emergelog = False + for x in ("--pretend", "--fetchonly", "--fetch-all-uri"): + if x in myopts: + disable_emergelog = True + break + if myaction in ("search", "info"): + disable_emergelog = True + if disable_emergelog: + """ Disable emergelog for everything except build or unmerge + operations. This helps minimize parallel emerge.log entries that can + confuse log parsers. We especially want it disabled during + parallel-fetch, which uses --resume --fetchonly.""" + global emergelog + def emergelog(*pargs, **kargs): + pass + + if not "--pretend" in myopts: + emergelog(xterm_titles, "Started emerge on: "+\ + time.strftime("%b %d, %Y %H:%M:%S", time.localtime())) + myelogstr="" + if myopts: + myelogstr=string.join(myopts, " ") + if myaction: + myelogstr+=" "+myaction + if myfiles: + myelogstr+=" "+string.join(myfiles, " ") + emergelog(xterm_titles, " *** emerge " + myelogstr) + + def emergeexitsig(signum, frame): + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + portage_util.writemsg("\n\nExiting on signal %(signal)s\n" % {"signal":signum}) + sys.exit(100+signum) + signal.signal(signal.SIGINT, emergeexitsig) + signal.signal(signal.SIGTERM, emergeexitsig) + + def emergeexit(): + """This gets out final log message in before we quit.""" + if "--pretend" not in myopts: + emergelog(xterm_titles, " *** terminating.") + if "notitles" not in settings.features: + xtermTitleReset() + portage.atexit_register(emergeexit) + + if myaction in ["sync","metadata"] and "--help" not in myopts: + if "--pretend" in myopts: + print "emerge: \"sync\" actions do not support \"--pretend.\"" + sys.exit(1) + if "sync" == myaction: + action_sync(settings, trees, mtimedb, myopts, myaction) + else: + action_metadata(settings, portdb, myopts) + elif myaction=="regen": + validate_ebuild_environment(trees) + action_regen(settings, portdb) + # HELP action + elif "config"==myaction: + if "--pretend" in myopts: + sys.stderr.write("emerge: The --config action " + \ + "does not support --pretend.\n") + return 1 + validate_ebuild_environment(trees) + action_config(settings, trees, myopts, myfiles) + + # INFO action + elif "info"==myaction: + action_info(settings, trees, myopts, myfiles) + + # SEARCH action + elif "search"==myaction: + validate_ebuild_environment(trees) + action_search(settings, portdb, trees["/"]["vartree"], + myopts, myfiles, spinner) + elif "unmerge"==myaction or "prune"==myaction or "clean"==myaction: + validate_ebuild_environment(trees) + vartree = trees[settings["ROOT"]]["vartree"] + if 1 == unmerge(settings, myopts, vartree, myaction, myfiles, + ldpath_mtimes, raise_on_missing=False): + if "--pretend" not in myopts: + post_emerge(settings, mtimedb, 0) + + elif "depclean"==myaction: + validate_ebuild_environment(trees) + action_depclean(settings, trees, ldpath_mtimes, + myopts, spinner) + if "--pretend" not in myopts: + post_emerge(settings, mtimedb, 0) + # "update", "system", or just process files: + else: + validate_ebuild_environment(trees) + action_build(settings, trees, mtimedb, + myopts, myaction, myfiles, spinner) + if "--pretend" not in myopts: + post_emerge(settings, mtimedb, 0) + +if __name__ == "__main__": + retval = emerge_main() + sys.exit(retval) diff --git a/bin/emerge-webrsync b/bin/emerge-webrsync index 91c32f5f7..bee4761f4 100755 --- a/bin/emerge-webrsync +++ b/bin/emerge-webrsync @@ -1,26 +1,22 @@ #!@BASH@ # Copyright 1999-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/Attic/emerge-webrsync,v 1.8.2.4 2005/02/26 11:22:38 carpaski Exp $ +# $Id$ # Author: Karl Trygve Kalleberg # Rewritten from the old, Perl-based emerge-webrsync script +eval $("@PORTAGE_BASE@"/bin/portageq envvar -v FEATURES FETCHCOMMAND \ + GENTOO_MIRRORS PORTAGE_INST_UID PORTAGE_INST_GID PORTAGE_NICENESS \ + PORTAGE_TMPDIR PORTDIR USERLAND) +DISTDIR="${PORTAGE_TMPDIR}/emerge-webrsync" + # If PORTAGE_NICENESS is overriden via the env then it will # still pass through the portageq call and override properly. -PORTAGE_NICENESS="$(@PORTAGE_BASE@/bin/portageq envvar PORTAGE_NICENESS)" if [ -n "${PORTAGE_NICENESS}" ]; then renice $PORTAGE_NICENESS $$ > /dev/null fi -source @PORTAGE_BASE@/bin/isolated-functions.sh || exit 1 -FEATURES="$(@PORTAGE_BASE@/bin/portageq envvar FEATURES)" -GENTOO_MIRRORS="${GENTOO_MIRRORS} $(@PORTAGE_BASE@/bin/portageq gentoo_mirrors)" -PORTDIR="$(@PORTAGE_BASE@/bin/portageq portdir)" -FETCHCOMMAND="$(@PORTAGE_BASE@/bin/portageq envvar FETCHCOMMAND)" -USERLAND="$(@PORTAGE_BASE@/bin/portageq envvar USERLAND)" -DISTDIR="$(@PORTAGE_BASE@/bin/portageq envvar PORTAGE_TMPDIR)/emerge-webrsync" -PORTAGE_INST_UID="$(@PORTAGE_BASE@/bin/portageq envvar PORTAGE_INST_UID)" -PORTAGE_INST_GID="$(@PORTAGE_BASE@/bin/portageq envvar PORTAGE_INST_GID)" +source "@PORTAGE_BASE@"/bin/isolated-functions.sh || exit 1 if [ ! -d $DISTDIR ] ; then mkdir -p $DISTDIR @@ -54,7 +50,10 @@ fi sync_local() { echo Syncing local tree... if type -p tarsync &> /dev/null; then - if ! tarsync "${FILE}" "${PORTDIR}" -v -s 1 -o ${PORTAGE_INST_UID:-0} -g ${PORTAGE_INST_GID:-0} -e /distfiles -e /packages -e /local; then + # tarsync doesn't take numeric uid/gid so we need to convert them. + local inst_user="$(python -c "import pwd; print pwd.getpwuid(int('${PORTAGE_INST_UID:-0}'))[0]")" + local inst_group="$(python -c "import grp; print grp.getgrgid(int('${PORTAGE_INST_GID:-0}'))[0]")" + if ! tarsync "${FILE}" "${PORTDIR}" -v -s 1 -o ${inst_user} -g ${inst_group} -e /distfiles -e /packages -e /local; then echo "tarsync failed; tarball is corrupt?" exit 1; fi diff --git a/bin/env-update b/bin/env-update index d5e5e04d2..bbb159127 100755 --- a/bin/env-update +++ b/bin/env-update @@ -4,7 +4,6 @@ # $Id: /var/cvsroot/gentoo-src/portage/bin/env-update,v 1.9 2004/10/04 13:56:50 vapier Exp $ import os,sys -os.environ["PORTAGE_CALLER"] = "env-update" sys.path = ["@PORTAGE_BASE@/pym"]+sys.path def usage(status): diff --git a/bin/etc-update b/bin/etc-update index 3f214b8ee..166a5f108 100755 --- a/bin/etc-update +++ b/bin/etc-update @@ -1,7 +1,7 @@ #!@BASH@ # Copyright 1999-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Id: etc-update 2221 2005-11-01 01:32:07Z vapier $ +# $Id$ # Author Brandon Low # @@ -10,10 +10,6 @@ # Leo Lipelis # Karl Trygve Kalleberg -export PORTAGE_CALLER="etc-update" - -eval $(python -c 'import portage_const;print "export EPREFIX="+portage_const.EPREFIX;print "export PORTAGE_BIN_PATH="+portage_const.PORTAGE_BIN_PATH;') - if type -p gsed >/dev/null ; then function sed() { gsed "$@"; } fi @@ -24,8 +20,10 @@ function get_config() { # First strip off comment lines, then grab the configuration # item. If there's more than one of the same configuration item, # then allow the last setting to take precedence. - cut -d'#' -f1-1 ${EPREFIX}/etc/etc-update.conf | \ - sed -ne "s/^ *$item *= *\([\"']\{0,1\}\)\(.*\)\1/\2/p" |sed -e '$p;d' + local result + result=$(cut -d'#' -f1-1 ${PORTAGE_CONFIGROOT}etc/etc-update.conf | \ + sed -ne "s/^ *$item *= *\([\"']\{0,1\}\)\(.*\)\1/\2/p" |sed -e '$p;d') + eval echo $result } function scan() { @@ -35,7 +33,9 @@ function scan() { mkdir ${TMP}/files || die "Failed mkdir command!" 1 count=0 input=0 - + local find_opts + local my_basename + # Sanity check to make sure diff exists and works if ! diff -v &>/dev/null ; then echo "ERROR: 'diff' does not seem to work, aborting" @@ -43,15 +43,24 @@ function scan() { fi for path in ${CONFIG_PROTECT} ; do - [ ! -d ${path} ] && continue + path="${ROOT}${path}" + find_opts="-iname ._cfg????_*" + if [ ! -d "${path}" ]; then + [ ! -f "${path}" ] && continue + my_basename="${path##*/}" + path="${path%/*}" + find_opts="-maxdepth 1 -iname ._cfg????_${my_basename}" + fi ofile="" - for file in `find ${path}/ -iname "._cfg????_*" | + # The below set -f turns off file name globbing in the ${find_opts} expansion. + for file in $(set -f; find ${path}/ ${find_opts} ! -iname '.*~' ! -iname '.*.bak' | sed -e "s:\(^.*/\)\(\._cfg[0-9]*_\)\(.*$\):\1\2\3\%\2\%\3:" | - sort -t'%' -k3 -k2 | LANG=POSIX LC_ALL=POSIX cut -f1 -d'%'`; do - rpath=`echo "${file/\/\///}" | sed -e "s:/[^/]*$::"` - rfile=`echo "${file/\/\///}" | sed -e "s:^.*/::"` + sort -t'%' -k3 -k2 | LANG=POSIX LC_ALL=POSIX cut -f1 -d'%'); do + rpath=$(echo "${file/\/\///}" | sed -e "s:/[^/]*$::") + rfile=$(echo "${file/\/\///}" | sed -e "s:^.*/::") for mpath in ${CONFIG_PROTECT_MASK}; do + mpath="${ROOT}${path}" if [[ "${rpath}" == "${mpath}"* ]]; then mv ${rpath}/${rfile} ${rpath}/${rfile:10} break @@ -60,22 +69,22 @@ function scan() { [ ! -f ${file} ] && continue - if [[ "${ofile:10}" != "${rfile:10}" ]] || + if [[ "${ofile:10}" != "${rfile:10}" ]] || [[ ${opath} != ${rpath} ]]; then MATCHES=0 if [[ "${EU_AUTOMERGE}" == "yes" ]]; then if [ ! -e "${rpath}/${rfile}" ] || [ ! -e "${rpath}/${rfile:10}" ]; then MATCHES=0 else - diff -Bbua ${rpath}/${rfile} ${rpath}/${rfile:10} | ${EPREFIX}/bin/grep -E '^[+-]' | ${EPREFIX}/bin/grep -E -v '^[+-][\t ]*#|^--- |^\+\+\+ ' | ${EPREFIX}/bin/grep -E -qv '^[-+][\t ]*$' + diff -Bbua ${rpath}/${rfile} ${rpath}/${rfile:10} | egrep '^[+-]' | egrep -v '^[+-][\t ]*#|^--- |^\+\+\+ ' | egrep -qv '^[-+][\t ]*$' MATCHES=$? fi - elif [[ -z `diff -Nua ${rpath}/${rfile} ${rpath}/${rfile:10}| - grep "^[+-][^+-]"|grep -v '# .Header:.*'` ]]; then + elif [[ -z $(diff -Nua ${rpath}/${rfile} ${rpath}/${rfile:10}| + grep "^[+-][^+-]"|grep -v '# .Header:.*') ]]; then MATCHES=1 fi if [[ "${MATCHES}" == "1" ]]; then - echo "Automerging trivial changes in: ${rfile:10}" + echo "Automerging trivial changes in: ${rpath}/${rfile:10}" mv ${rpath}/${rfile} ${rpath}/${rfile:10} continue else @@ -88,8 +97,8 @@ function scan() { fi fi - if [[ -z `diff -Nua ${rpath}/${rfile} ${rpath}/${ofile}| - grep "^[+-][^+-]"|grep -v '# .Header:.*'` ]]; then + if [[ -z $(diff -Nua ${rpath}/${rfile} ${rpath}/${ofile}| + grep "^[+-][^+-]"|grep -v '# .Header:.*') ]]; then mv ${rpath}/${rfile} ${rpath}/${ofile} continue else @@ -104,26 +113,21 @@ function scan() { function sel_file() { local -i isfirst=0 - until [ -f ${TMP}/files/${input} ] || [ ${input} == -1 ] || [ ${input} == -3 ]; do + until [[ -f ${TMP}/files/${input} ]] || \ + [[ ${input} == -1 ]] || \ + [[ ${input} == -3 ]] + do local numfiles=$(ls ${TMP}/files|wc -l) local numwidth=${#numfiles} for file in $(ls ${TMP}/files|sort -n); do - if (( ${isfirst} == 0 )); then + if [[ ${isfirst} == 0 ]] ; then isfirst=${file} fi numshow=$(printf "%${numwidth}i${PAR} " ${file}) - spacer=${numshow//?/ } + numupdates=$(( $(wc -l <${TMP}/files/${file}) - 1 )) echo -n "${numshow}" - if (( ${mode} == 0 )); then - spacedit=0 - for word in $(<${TMP}/files/${file}); do - if (( ${spacedit} == 1 )); then - echo -n "${spacer}" - else - spacedit=1 - fi - echo ${word} - done + if [[ ${mode} == 0 ]] ; then + echo "$(head -n1 ${TMP}/files/${file}) (${numupdates})" else head -n1 ${TMP}/files/${file} fi @@ -131,42 +135,86 @@ function sel_file() { if [ "${OVERWRITE_ALL}" == "yes" ]; then input=0 + elif [ "${DELETE_ALL}" == "yes" ]; then + input=0 else - if (( ${mode} == 0 )); then - echo "The following is the list of files which need updating, each + if [[ ${mode} == 0 ]] ; then + echo "The following is the list of files which need updating, each configuration file is followed by a list of possible replacement files." else local my_title="Please select a file to update" fi - if (( ${mode} == 0 )); then + if [[ ${mode} == 0 ]] ; then cat ${TMP}/menuitems echo "Please select a file to edit by entering the corresponding number." - echo " (don't use -3 or -5 if you're unsure what to do)" + echo " (don't use -3, -5, -7 or -9 if you're unsure what to do)" echo " (-1 to exit) (-3 to auto merge all remaining files)" - echo -n " (-5 to auto-merge AND not use 'mv -i'): " - read input + echo " (-5 to auto-merge AND not use 'mv -i')" + echo " (-7 to discard all updates)" + echo -n " (-9 to discard all updates AND not use 'rm -i'): " + input=$(read_int) else dialog --title "${title}" --menu "${my_title}" \ 0 0 0 $(echo -e "-1 Exit\n$(<${TMP}/menuitems)") \ - 2> ${TMP}/input + 2> ${TMP}/input || die "User termination!" 0 input=$(<${TMP}/input) fi - if (( ${input} == -5 )); then + if [[ ${input} == -9 ]]; then + read -p "Are you sure that you want to delete all updates (type YES):" reply + if [[ ${reply} != "YES" ]]; then + continue + else + input=-7 + export rm_opts="" + fi + fi + if [[ ${input} == -7 ]]; then + input=0 + export DELETE_ALL="yes" + fi + if [[ ${input} == -5 ]] ; then input=-3 export mv_opts="" fi - if (( ${input} == -3 )); then + if [[ ${input} == -3 ]] ; then input=0 export OVERWRITE_ALL="yes" fi fi # -3 automerge - if (( ${input} == 0 )); then + if [[ -z ${input} ]] || [[ ${input} == 0 ]] ; then input=${isfirst} fi done } +function user_special() { + if [ -r ${PORTAGE_CONFIGROOT}etc/etc-update.special ]; then + if [ -z "$1" ]; then + echo "ERROR: user_special() called without arguments" + return 1 + fi + while read pat; do + echo ${1} | grep "${pat}" > /dev/null && return 0 + done < ${PORTAGE_CONFIGROOT}etc/etc-update.special + fi + return 1 +} + +function read_int() { + # Read an integer from stdin. Continously loops until a valid integer is + # read. This is a workaround for odd behavior of bash when an attempt is + # made to store a value such as "1y" into an integer-only variable. + local my_input + while true; do + read my_input + # failed integer conversions will break a loop unless they're enclosed + # in a subshell. + echo "${my_input}" | ( declare -i x; read x) && break + done + echo ${my_input} +} + function do_file() { echo local -i my_input @@ -181,13 +229,15 @@ function do_file() { if [ "${OVERWRITE_ALL}" == "yes" ]; then my_input=0 + elif [ "${DELETE_ALL}" == "yes" ]; then + my_input=0 else for line in $(<${TMP}/files/${input}); do if (( ${fcount} > 0 )); then echo -n "${fcount}${PAR} " echo "${line}" else - if (( ${mode} == 0 )); then + if [[ ${mode} == 0 ]] ; then echo "Below are the new config files for ${line}:" else local my_title="Please select a file to process for ${line}" @@ -196,28 +246,28 @@ function do_file() { fcount=${fcount}+1 done > ${TMP}/menuitems - if (( ${mode} == 0 )); then + if [[ ${mode} == 0 ]] ; then cat ${TMP}/menuitems echo -n "Please select a file to process (-1 to exit this file): " - read my_input + my_input=$(read_int) else dialog --title "${title}" --menu "${my_title}" \ - 0 0 0 `echo -e "$(<${TMP}/menuitems)\n${fcount} Exit"` \ - 2> ${TMP}/input + 0 0 0 $(echo -e "$(<${TMP}/menuitems)\n${fcount} Exit") \ + 2> ${TMP}/input || die "User termination!" 0 my_input=$(<${TMP}/input) fi fi # OVERWRITE_ALL - if (( ${my_input} == 0 )); then + if [[ ${my_input} == 0 ]] ; then my_input=1 - elif (( ${my_input} == -1 )); then - input=0 + elif [[ ${my_input} == -1 ]] ; then + input=0 return - elif (( ${my_input} == ${fcount} )); then + elif [[ ${my_input} == ${fcount} ]] ; then break fi done - if (( ${my_input} == ${fcount} )); then + if [[ ${my_input} == ${fcount} ]] ; then break fi @@ -231,7 +281,7 @@ function do_file() { sed -e "${fcount}!p;d" ${TMP}/files/${input} > ${TMP}/files/sed mv ${TMP}/files/sed ${TMP}/files/${input} - if (( ${my_input} == -1 )); then + if [[ ${my_input} == -1 ]] ; then break fi done @@ -241,18 +291,20 @@ function do_file() { } function do_cfg() { - + local file="${1}" local ofile="${2}" local -i my_input=0 - + until (( ${my_input} == -1 )) || [ ! -f ${file} ]; do - if [ "${OVERWRITE_ALL}" == "yes" ]; then + if [[ "${OVERWRITE_ALL}" == "yes" ]] && ! user_special "${ofile}"; then my_input=1 + elif [[ "${DELETE_ALL}" == "yes" ]] && ! user_special "${ofile}"; then + my_input=2 else - showdiffcmd=$(echo "${diff_command}" | + showdiffcmd=$(echo "${diff_command}" | sed -e "s:%file1:${ofile}:" -e "s:%file2:${file}:") - + if [ "${using_editor}" == 0 ]; then ( echo "Showing differences between ${ofile} and ${file}" @@ -271,22 +323,25 @@ function do_cfg() { echo "-------------------------------------------------------------" echo fi - echo -n "1) Replace original with update + echo -n "File: ${file} +1) Replace original with update 2) Delete update, keeping original as is 3) Interactively merge original with update 4) Show differences again Please select from the menu above (-1 to ignore this update): " - read my_input + my_input=$(read_int) fi - + case ${my_input} in 1) echo "Replacing ${ofile} with ${file}" mv ${mv_opts} ${file} ${ofile} - my_input=-1 + [ -n "${OVERWRITE_ALL}" ] && my_input=-1 + [ -e "${file}" ] || touch ${ofile} continue ;; 2) echo "Deleting ${file}" rm ${rm_opts} ${file} + [ -n "${DELETE_ALL}" ] && my_input=-1 continue ;; 3) do_merge "${file}" "${ofile}" @@ -303,7 +358,7 @@ Please select from the menu above (-1 to ignore this update): " } function do_merge() { - + local file="${1}" local ofile="${2}" local mfile="${2}.merged" @@ -317,29 +372,30 @@ function do_merge() { until (( ${my_input} == -1 )); do echo "Merging ${file} and ${ofile}" - `echo "${merge_command}" | + $(echo "${merge_command}" | sed -e "s:%merged:${mfile}:g" \ -e "s:%orig:${ofile}:g" \ - -e "s:%new:${file}:g"` - until (( ${my_input} == -1 )); do + -e "s:%new:${file}:g") + until (( ${my_input} == -1 )); do echo -n "1) Replace ${ofile} with merged file 2) Show differences between merged file and original 3) Remerge original with update 4) Edit merged file 5) Return to the previous menu Please select from the menu above (-1 to exit, losing this merge): " - read my_input + my_input=$(read_int) case ${my_input} in 1) echo "Replacing ${ofile} with ${mfile}" chmod --reference=${ofile} ${mfile} mv ${mv_opts} ${mfile} ${ofile} + [ -e "${mfile}" ] || touch ${ofile} rm ${rm_opts} ${file} return 255 ;; 2) ( echo "Showing differences between ${ofile} and ${mfile}" - `echo "${diff_command}" | \ + $(echo "${diff_command}" | \ sed -e "s:%file1:${ofile}:" \ - -e "s:%file2:${mfile}:"` ) | ${pager} + -e "s:%file2:${mfile}:") ) | ${pager} continue ;; 3) break @@ -360,9 +416,15 @@ Please select from the menu above (-1 to exit, losing this merge): " } function die() { - trap "" term - trap "" kill + trap "" TERM + trap "" KILL echo "Exiting: ${1}" + + if [ ${2} -eq 0 ]; then + scan > /dev/null + [ ${count} -gt 0 ] && echo "NOTE: ${count} updates remaining" + fi + rm -rf ${TMP} exit ${2} } @@ -374,31 +436,40 @@ scriptname=$(basename $0) trap die term -export PORTAGE_TMPDIR=$(${PORTAGE_BIN_PATH}/portageq envvar PORTAGE_TMPDIR) + +eval $(@PORTAGE_BASE@/bin/portageq envvar -v CONFIG_PROTECT \ + CONFIG_PROTECT_MASK PORTAGE_CONFIGROOT PORTAGE_TMPDIR ROOT) +export PORTAGE_TMPDIR + +[ -w ${PORTAGE_CONFIGROOT}etc ] || die "Need write access to ${PORTAGE_CONFIGROOT}etc" 1 +#echo $PORTAGE_TMPDIR +#echo $CONFIG_PROTECT +#echo $CONFIG_PROTECT_MASK +#export PORTAGE_TMPDIR=$(/usr/lib/portage/bin/portageq envvar PORTAGE_TMPDIR) TMP="${PORTAGE_TMPDIR}/$$" rm -rf ${TMP} 2> /dev/null mkdir ${TMP} || die "failed mkdir command!" 1 # I need the CONFIG_PROTECT value -CONFIG_PROTECT=$(${PORTAGE_BIN_PATH}/portageq envvar CONFIG_PROTECT) -CONFIG_PROTECT_MASK=$(${PORTAGE_BIN_PATH}/portageq envvar CONFIG_PROTECT_MASK) +#CONFIG_PROTECT=$(@PORTAGE_BASE@/bin/portageq envvar CONFIG_PROTECT) +#CONFIG_PROTECT_MASK=$(@PORTAGE_BASE@/bin/portageq envvar CONFIG_PROTECT_MASK) # load etc-config's configuration -EU_AUTOMERGE=`get_config eu_automerge` -rm_opts=`get_config rm_opts` -mv_opts=`get_config mv_opts` -cp_opts=`get_config cp_opts` -pager=`get_config pager` -diff_command=`get_config diff_command` -using_editor=`get_config using_editor` -merge_command=`get_config merge_command` -declare -i mode=`get_config mode` +EU_AUTOMERGE=$(get_config eu_automerge) +rm_opts=$(get_config rm_opts) +mv_opts=$(get_config mv_opts) +cp_opts=$(get_config cp_opts) +pager=$(get_config pager) +diff_command=$(get_config diff_command) +using_editor=$(get_config using_editor) +merge_command=$(get_config merge_command) +declare -i mode=$(get_config mode) [ -z ${mode} ] && mode=0 [ -z "${pager}" ] && pager="cat" #echo "rm_opts: $rm_opts, mv_opts: $mv_opts, cp_opts: $cp_opts" -#echo "pager: $pager, diff_command: $diff_command, merge_command: $merge_command" +#echo "pager: $pager, diff_command: $diff_command, merge_command: $merge_command" if (( ${mode} == 0 )); then PAR=")" @@ -407,7 +478,7 @@ else fi declare -i count=0 -declare -i input=0 +declare input=0 declare title="Gentoolkit's etc-update tool!" scan diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh index 94ff1c983..de62d8611 100644 --- a/bin/isolated-functions.sh +++ b/bin/isolated-functions.sh @@ -2,11 +2,15 @@ # Distributed under the terms of the GNU General Public License v2 # $Header$ -# Internal logging function, don't use this in ebuilds +quiet_mode() { + [[ ${PORTAGE_QUIET} -eq 1 ]] +} + vecho() { - [[ ${PORTAGE_QUIET} == "1" ]] || echo "$@" + quiet_mode || echo "$@" } +# Internal logging function, don't use this in ebuilds elog_base() { local messagetype [ -z "${1}" -o -z "${T}" -o ! -d "${T}/logging" ] && return 1 @@ -200,14 +204,15 @@ set_colors() { ENDCOL=$'\e[A\e['${COLS}'C' # Now, ${ENDCOL} will move us to the end of the # column; irregardless of character width - GOOD=$'\e[32;01m' - WARN=$'\e[33;01m' - BAD=$'\e[31;01m' - HILITE=$'\e[36;01m' - BRACKET=$'\e[34;01m' + [ -z "${GOOD}" ] && GOOD=$'\e[32;01m' + [ -z "${WARN}" ] && WARN=$'\e[33;01m' + [ -z "${BAD}" ] && BAD=$'\e[31;01m' + [ -z "${HILITE}" ] && HILITE=$'\e[36;01m' + [ -z "${BRACKET}" ] && BRACKET=$'\e[34;01m' NORMAL=$'\e[0m' } +export -n GOOD WARN BAD HILITE BRACKET RC_ENDCOL="yes" RC_INDENTATION='' RC_DEFAULT_INDENT=2 diff --git a/bin/md5check.py b/bin/md5check.py index bdc788dd4..16157bd00 100755 --- a/bin/md5check.py +++ b/bin/md5check.py @@ -4,7 +4,6 @@ # $Id: /var/cvsroot/gentoo-src/portage/bin/md5check.py,v 1.4 2004/10/10 10:07:20 carpaski Exp $ import os,sys,string -os.environ["PORTAGE_CALLER"]="mirror" os.environ["FEATURES"]="mirror cvs" sys.path = ["@PORTAGE_BASE@/pym"]+sys.path diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh index ef8440ce8..e97b4c63e 100644 --- a/bin/misc-functions.sh +++ b/bin/misc-functions.sh @@ -17,9 +17,8 @@ shift $# source @PORTAGE_BASE@/bin/ebuild.sh install_qa_check() { - + cd "${D}" || die "cd failed" prepall - cd "${D}" declare -i UNSAFE=0 for i in $(find "${D}/" -type f -perm -2002); do @@ -61,7 +60,6 @@ install_qa_check() { vecho "QA Notice: the following files contain insecure RUNPATH's" vecho " Please file a bug about this at http://bugs.gentoo.org/" vecho " with the maintaining herd of the package." - vecho " Summary: $CATEGORY/$PN: insecure RPATH ${f}" vecho "${f}" vecho -ne '\a\n' if has stricter ${FEATURES} && ! has stricter ${RESTRICT}; then @@ -81,7 +79,8 @@ install_qa_check() { vecho " LDFLAGS='-Wl,-z,now' emerge ${PN}" vecho "${f}" vecho -ne '\a\n' - die_msg="${die_msg} setXid lazy bindings," + # Do not fail here until we have sorted out the lazy issues with security team + #die_msg="${die_msg} setXid lazy bindings," sleep 1 fi @@ -161,7 +160,7 @@ install_qa_check() { if [[ ${insecure_rpath} -eq 1 ]] ; then die "Aborting due to serious QA concerns with RUNPATH/RPATH" - elif [[ ${die_msg} != "" ]] && has stricter ${FEATURES} && ! has stricter ${RESTRICT} ; then + elif [[ -n ${die_msg} ]] && has stricter ${FEATURES} && ! has stricter ${RESTRICT} ; then die "Aborting due to QA concerns: ${die_msg}" fi @@ -225,13 +224,22 @@ install_qa_check() { if hasq multilib-strict ${FEATURES} && [ -x file -a -x find -a \ -n "${MULTILIB_STRICT_DIRS}" -a -n "${MULTILIB_STRICT_DENY}" ]; then - MULTILIB_STRICT_EXEMPT=$(echo ${MULTILIB_STRICT_EXEMPT:-"(perl5|gcc|gcc-lib|debug|portage)"} | sed -e 's:\([(|)]\):\\\1:g') + local abort=no firstrun=yes + MULTILIB_STRICT_EXEMPT=$(echo ${MULTILIB_STRICT_EXEMPT} | sed -e 's:\([(|)]\):\\\1:g') for dir in ${MULTILIB_STRICT_DIRS}; do [ -d "${D}/${dir}" ] || continue for file in $(find ${D}/${dir} -type f | grep -v "^${D}/${dir}/${MULTILIB_STRICT_EXEMPT}"); do - file ${file} | egrep -q "${MULTILIB_STRICT_DENY}" && die "File ${file} matches a file type that is not allowed in ${dir}" + if file ${file} | egrep -q "${MULTILIB_STRICT_DENY}" ; then + if [[ ${firstrun} == yes ]] ; then + echo "Files matching a file type that is not allowed:" + firstrun=no + fi + abort=yes + echo " ${file#${D}//}" + fi done done + [[ ${abort} == yes ]] && die "multilib-strict check failed!" fi } @@ -247,7 +255,7 @@ install_mask() { set -o noglob for no_inst in ${install_mask}; do set +o noglob - einfo "Removing ${no_inst}" + quiet_mode || einfo "Removing ${no_inst}" # normal stuff rm -Rf ${root}/${no_inst} >&/dev/null @@ -309,7 +317,7 @@ preinst_suid_scan() { # total suid control. if hasq suidctl $FEATURES; then sfconf=${EPREFIX}/etc/portage/suidctl.conf - vecho ">>> Preforming suid scan in ${IMAGE}" + vecho ">>> Performing suid scan in ${IMAGE}" for i in $(find ${IMAGE}/ -type f \( -perm -4000 -o -perm -2000 \) ); do if [ -s "${sfconf}" ]; then suid="$(grep ^${i/${IMAGE}/}$ ${sfconf})" @@ -377,6 +385,7 @@ dyn_package() { export SANDBOX_ON="0" tar cpvf - ./ | bzip2 -f > "${pkg_tmp}" || die "Failed to create tarball" cd .. + export PYTHONPATH=${PORTAGE_PYM_PATH:-/usr/lib/portage/pym} python -c "import xpak; t=xpak.tbz2('${pkg_tmp}'); t.recompose('${PORTAGE_BUILDDIR}/build-info')" if [ $? -ne 0 ]; then rm -f "${pkg_tmp}" diff --git a/bin/pkgmerge b/bin/pkgmerge deleted file mode 100755 index 4499599fe..000000000 --- a/bin/pkgmerge +++ /dev/null @@ -1,59 +0,0 @@ -#!@PYTHON@ -# Copyright 1999-2006 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/pkgmerge,v 1.8 2004/10/04 13:56:50 vapier Exp $ - -import sys,os,string -sys.path = ["@PORTAGE_BASE@/pym"]+sys.path - -import portage,xpak - -#build our package digraph - -def digraph_create(digraph,mykey,myprovides=None): - mytbz2=xpak.tbz2(bintree.getname(mykey)) - mydep=mytbz2.getelements("RDEPEND") - digraph.addnode(mykey,myprovides) - mycheck=roottree.depcheck(string.join(mydep," ")) - if mycheck[0]==0: - print "!!! Error: RDEPEND string formatted incorrectly:",mydep - return None - for x in mycheck[1]: - mymatch=bintree.dep_bestmatch(x) - if mymatch=="": - print "!!! Error: can't resolve dependency --",x - return None - if not digraph_create(digraph,mymatch,mykey): - return None - return 1 - -#main program loop -myvirtuals=portage.getvirtuals(portage.root) -roottree=portage.vartree(portage.root,myvirtuals) -bintree=portage.binarytree("/",myvirtuals) -pretend=0 -if len(sys.argv)>=2: - if sys.argv[1]=="--pretend": - print "These are the packages that I would merge, in order:" - pretend=1 - del sys.argv[1] - elif sys.argv[1]=="--help": - print "Usage: pkgmerge [--pretend] pkg1.tbz2 [pkg2.tbz2]..." - sys.exit(1) -for mypkg in sys.argv[1:]: - digraph=portage.digraph() - mytbz2=xpak.tbz2(mypkg) - mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.basename(mypkg)[:-5] - digraph_create(digraph,mykey) - while not digraph.empty(): - mykey=digraph.firstzero() - if not mykey: - print "!!! Error: circular dependencies" - sys.exit(1) - mytbz2=bintree.getname(mykey) - if pretend: - print mytbz2 - else: - portage.pkgmerge(mytbz2,portage.settings["ROOT"]) - digraph.delnode(mykey) - diff --git a/bin/pkgmerge.new b/bin/pkgmerge.new deleted file mode 100755 index cfab4f9d3..000000000 --- a/bin/pkgmerge.new +++ /dev/null @@ -1,84 +0,0 @@ -#!@PYTHON@ -# Copyright 1999-2006 Gentoo Foundation -# Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/pkgmerge.new,v 1.7 2004/10/04 13:56:50 vapier Exp $ - -import os,string,sys -sys.path = ["@PORTAGE_BASE@/pym"]+sys.path - -import portage,xpak - -#beautiful directed graph functions - -def dig_addnode(digraph,mykey,myprovides): - if not digraph.has_key(mykey): - if myprovides==None: - digraph[mykey]=[0,[]] - else: - digraph[mykey]=[0,[myprovides]] - digraph[myprovides][0]=digraph[myprovides][0]+1 - return - digraph[mykey]=[digraph[mykey][0],digraph[mykey][1].append(myprovides)] - -def dig_delnode(digraph,mykey): - if not digraph.has_key(mykey): - return - for x in digraph[mykey][1]: - digraph[x][0]=digraph[x][0]-1 - del digraph[mykey] - -def dig_firstzero(digraph): - for x in digraph.keys(): - if digraph[x][0]==0: - return x - return None - -#build our package digraph - -def digraph_create(digraph,mykey,myprovides=None): - mytbz2=xpak.tbz2(bintree.getname(mykey)) - mydep=mytbz2.getelements("RDEPEND") - dig_addnode(digraph,mykey,myprovides) - mycheck=roottree.depcheck(string.join(mydep," ")) - if mycheck[0]==0: - print "!!! Error: RDEPEND string formatted incorrectly:",mydep - return None - for x in mycheck[1]: - mymatch=bintree.dep_bestmatch(x) - if mymatch=="": - print "!!! Error: can't resolve dependency --",x - return None - if not digraph_create(digraph,mymatch,mykey): - return None - return 1 - -#main program loop -myvirtuals=portage.getvirtual(portage.root) -roottree=portage.vartree(portage.root,myvirtuals) -bintree=portage.binarytree("/",myvirtuals) -pretend=0 -if len(sys.argv)>=2: - if sys.argv[1]=="--pretend": - print "These are the packages that I would merge, in order:" - pretend=1 - del sys.argv[1] - elif sys.argv[1]=="--help": - print "Usage: pkgmerge [--pretend] pkg1.tbz2 [pkg2.tbz2]..." - sys.exit(1) -for mypkg in sys.argv[1:]: - digraph={} - mytbz2=xpak.tbz2(mypkg) - mykey=mytbz2.getelements("CATEGORY")[0]+"/"+mypkg[:-5] - digraph_create(digraph,mykey) - while (len(digraph)): - mykey=dig_firstzero(digraph) - if not mykey: - print "!!! Error: circular dependencies" - sys.exit(1) - mytbz2=bintree.getname(mykey) - if pretend: - print mytbz2 - else: - portage.pkgmerge(mytbz2) - dig_delnode(digraph,mykey) - diff --git a/bin/portageq b/bin/portageq index 70e5d55b9..7bb21d321 100755 --- a/bin/portageq +++ b/bin/portageq @@ -3,8 +3,23 @@ # Distributed under the terms of the GNU General Public License v2 # $Id: /var/cvsroot/gentoo-src/portage/bin/portageq,v 1.13.2.1 2005/04/12 12:23:41 jstubbs Exp $ -import sys, os -os.environ["PORTAGE_CALLER"] = "portageq" +import sys +# This block ensures that ^C interrupts are handled quietly. +try: + import signal + + def exithandler(signum, frame): + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGTERM, signal.SIG_IGN) + sys.exit(1) + + signal.signal(signal.SIGINT, exithandler) + signal.signal(signal.SIGTERM, exithandler) + +except KeyboardInterrupt: + sys.exit(1) + +import os sys.path = ["@PORTAGE_BASE@/pym"]+sys.path import types,string @@ -80,6 +95,36 @@ def mass_best_version(argv): sys.exit(1) mass_best_version.uses_root = True +def metadata(argv): + """ []+ + Returns metadata values for the specified package. + """ + if (len(argv) < 4): + print >> sys.stderr, "ERROR: insufficient parameters!" + sys.exit(2) + + root, pkgtype, pkgspec = argv[0:3] + metakeys = argv[3:] + type_map = { + "ebuild":"porttree", + "binary":"bintree", + "installed":"vartree"} + if pkgtype not in type_map: + print >> sys.stderr, "Unrecognized package type: '%s'" % pkgtype + sys.exit(1) + trees = portage.db + if os.path.realpath(root) == os.path.realpath(portage.settings["ROOT"]): + root = portage.settings["ROOT"] # contains the normalized $ROOT + try: + values = trees[root][type_map[pkgtype]].dbapi.aux_get( + pkgspec, metakeys) + for value in values: + print value + except KeyError: + print >> sys.stderr, "Package not found: '%s'" % pkgspec + sys.exit(1) + +metadata.uses_root = True def best_visible(argv): """ []+ @@ -263,9 +308,12 @@ def usage(argv): print "\nRun portageq with --help for info" def main(): - if (len(sys.argv) < 2) or ("-h" in sys.argv or "--help" in sys.argv): + if "-h" in sys.argv or "--help" in sys.argv: + usage(sys.argv) + sys.exit(os.EX_OK) + elif len(sys.argv) < 2: usage(sys.argv) - sys.exit() + sys.exit(os.EX_USAGE) cmd = sys.argv[1] try: @@ -280,7 +328,7 @@ def main(): function(sys.argv[2:]) except KeyError: usage(sys.argv) - sys.exit() + sys.exit(os.EX_USAGE) main() diff --git a/bin/prepman b/bin/prepman index 03327aeb5..6197b2211 100755 --- a/bin/prepman +++ b/bin/prepman @@ -15,7 +15,7 @@ if [[ ! -d ${z} ]] ; then fi for x in $(find "${z}"/ -type d 2>/dev/null) ; do - for y in $(find "${x}"/ -mindepth 1 -maxdepth 1 \( -type f -or -type l \) ! -name '.keep' 2>/dev/null) ; do + for y in $(find "${x}"/ -mindepth 1 -maxdepth 1 \( -type f -or -type l \) ! -name '.keep_*' 2>/dev/null) ; do if [[ -L ${y} ]] ; then # Symlink ... mylink=${y} diff --git a/bin/prepstrip b/bin/prepstrip index b45cd5643..d5cf0bc82 100755 --- a/bin/prepstrip +++ b/bin/prepstrip @@ -1,13 +1,16 @@ #!@BASH@ # Copyright 1999-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/prepstrip,v 1.23.2.3 2005/08/15 02:58:20 vapier Exp $ +# $Id$ -if [[ " ${FEATURES} " == *" nostrip "* ]] || \ - [[ " ${RESTRICT} " == *" nostrip "* ]] || \ - [[ " ${RESTRICT} " == *" strip "* ]] +source "${PORTAGE_BIN_PATH:-${EPREFIX}/usr/lib/portage/bin}"/isolated-functions.sh + +unset NOSTRIP +if hasq nostrip ${FEATURES} ${RESTRICT} || \ + hasq strip ${RESTRICT} then - exit 0 + NOSTRIP=1 + hasq installsources ${FEATURES} || exit 0 fi STRIP=${STRIP:-${CHOST}-strip} @@ -15,15 +18,27 @@ type -p -- ${STRIP} > /dev/null || STRIP=strip OBJCOPY=${OBJCOPY:-${CHOST}-objcopy} type -p -- ${OBJCOPY} > /dev/null || OBJCOPY=objcopy -PORTAGE_STRIP_FLAGS=${PORTAGE_STRIP_FLAGS:---strip-unneeded} +export PORTAGE_STRIP_FLAGS=${PORTAGE_STRIP_FLAGS:---strip-unneeded} banner=1 save_elf_debug() { local x=$1 - local y="${D}/usr/lib/debug/${x:${#D}:${#x}}.debug" + local y="${D}usr/lib/debug/${x:${#D}}.debug" + + if hasq installsources ${FEATURES} && [ -x "${EPREFIX}"/usr/bin/debugedit ] ; then + local sources_dir=/usr/src/debug/${CATEGORY}/${PF} + debugedit -b "${WORKDIR}" -d "${EPREFIX}/${sources_dir}" \ + -l "${T}"/debug.sources "${x}" + if [[ -s ${T}/debug.sources ]] ; then + [[ -d ${D}${sources_dir} ]] || mkdir -p "${D}${sources_dir}" + cat "${T}"/debug.sources | (cd "${WORKDIR}"; LANG=C sort -z -u | \ + rsync -aL0 --files-from=- "${WORKDIR}/" "${D}${sources_dir}/" ) + fi + fi - [[ " ${FEATURES} " != *" splitdebug "* ]] && return 0 + hasq splitdebug ${FEATURES} || return 0 + [[ -z "${NOSTRIP}" ]] || return 0 # dont save debug info twice. [[ ${x:7} == ".debug" ]] && return 0 @@ -31,49 +46,49 @@ save_elf_debug() { mkdir -p $(dirname "${y}") ${OBJCOPY} --only-keep-debug "${x}" "${y}" ${OBJCOPY} --add-gnu-debuglink="${y}" "${x}" - chmod a-x "${y}" + chmod a-x,o-w "${y}" - [[ " ${FEATURES} " != *" installsources "* ]] && return 0 +} - if [[ -x "${EPREFIX}/usr/bin/debugedit" ]] ; then - debugedit -b "${WORKDIR}" -d "${EPREFIX}"/usr/src/debug/${PF} -l "${T}"/debug.sources "${x}" - if [[ -s ${T}/debug.sources ]] ; then - [[ -d ${D}/usr/src/debug/${PF} ]] || mkdir -p "${D}/usr/src/debug/${PF}" - cat "${T}"/debug.sources | (cd "${WORKDIR}"; LANG=C sort -z -u | xargs -0 -- cp --parents -p --target-directory="${D}/usr/src/debug/${PF}" ) - fi +# The existance of the section .symtab tells us that a binary is stripped. +# We want to log already stripped binaries, as this may be a QA violation. +# They prevent us from getting the splitdebug data. +f="$(scanelf -yqRBF '#k%F' -k '!.symtab' "$@")" +if [[ -n "${f}" ]] ; then + vecho -e "\n\aQA Notice: pre-stripped files found:\a" + vecho "${f}" + echo "${f}" > "${T}"/scanelf-already-stripped.log +fi + +# Now we look for unstripped binaries. +for x in $(scanelf -yqRBF '#k%F' -k '.symtab' "$@") $(for y in "$@"; do find "${y}" -type f -name '*.a' -print ; done); do + if [[ ${banner} -eq 1 ]] ; then + [[ -z ${NOSTRIP} ]] && vecho "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}" + banner=0 fi -} -if [[ `type -p scanelf` ]]; then - for x in $(scanelf -yRBF%F "$@") $(for y in "$@"; do find "${y}" -type f -name '*.a' -print0 ; done); do - if [[ ${banner} -eq 1 ]] ; then - echo "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}" - banner=0 - fi - f=$(file "${x}") || continue - [[ -z ${f} ]] && continue - - # The noglob funk is to support STRIP_MASK="/*/booga" and to keep - # the for loop from expanding the globs. - # The eval echo is to support STRIP_MASK="/*/{booga,bar}" sex. - set -o noglob - stripitbaby=1 - for m in $(eval echo ${STRIP_MASK}) ; do - [[ ${x} == ${m} ]] && stripitbaby=1 && break - done - set +o noglob - - if [[ ${f} == *"current ar archive"* ]] ; then - echo " ${x:${#D}:${#x}}" - [[ ${stripitbaby} -eq 1 ]] && ${STRIP} -g "${x}" - fi - if [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* ]] ; then - echo " ${x:${#D}:${#x}}" - save_elf_debug "${x}" - [[ ${stripitbaby} -eq 1 ]] && ${STRIP} ${PORTAGE_STRIP_FLAGS} "${x}" - fi + f=$(file "${x}") || continue + [[ -z ${f} ]] && continue + + # The noglob funk is to support STRIP_MASK="/*/booga" and to keep + # the for loop from expanding the globs. + # The eval echo is to support STRIP_MASK="/*/{booga,bar}" sex. + set -o noglob + stripitbaby=1 + for m in $(eval echo ${STRIP_MASK}) ; do + [[ ${x} == ${m} ]] && stripitbaby=0 && break done -else - echo "scanelf(1) not found, skipping..." -fi - + set +o noglob + + if [[ ${f} == *"current ar archive"* ]] ; then + vecho " ${x:${#D}}" + [[ -n ${NOSTRIP} ]] && continue + [[ ${stripitbaby} -eq 1 ]] && ${STRIP} -g "${x}" + fi + if [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* ]] ; then + vecho " ${x:${#D}}" + save_elf_debug "${x}" + [[ -n ${NOSTRIP} ]] && continue + [[ ${stripitbaby} -eq 1 ]] && ${STRIP} ${PORTAGE_STRIP_FLAGS} "${x}" + fi +done diff --git a/bin/quickpkg b/bin/quickpkg index 09f48d21c..ab1589fb6 100755 --- a/bin/quickpkg +++ b/bin/quickpkg @@ -1,7 +1,7 @@ #!@BASH@ # Copyright 1999-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/quickpkg,v 1.13.2.6 2005/08/10 22:08:48 vapier Exp $ +# $Id$ # This script tries to quickly create a Gentoo binary package using the # VDB_PATH/category/pkg/* files @@ -9,14 +9,23 @@ # Resulting tbz2 file will be created in ${PKGDIR} ... # default is /usr/portage/packages/All/ -if [ "`whoami`" != "root" ] ; then +if [ "${UID}" != "0" ] ; then echo "You must run this as root" exit 1 fi +# We need to ensure a sane umask for the packages that will be created. +umask 022 + +eval $(portageq envvar -v NOCOLOR PKGDIR PORTAGE_BIN_PATH PORTAGE_NICENESS \ + PORTAGE_PYM_PATH PORTAGE_TMPDIR ROOT) +export PKGDIR PORTAGE_TMPDIR ROOT + +[ -n "${PORTAGE_NICENESS}" ] && renice $PORTAGE_NICENESS $$ > /dev/null + +# Make sure the xpak module is in PYTHONPATH +export PYTHONPATH=${PORTAGE_PYM_PATH} export PORTAGE_DB=$(portageq vdb_path) -export ROOT=$(portageq envvar ROOT) -export ROOT=${ROOT%/}/ if [ -z "$1" ] || [ $1 == "-h" ] || [ $1 == "--help" ] ; then echo "QUICKPKG ver 1.2" @@ -37,10 +46,15 @@ if [ -z "$1" ] || [ $1 == "-h" ] || [ $1 == "--help" ] ; then exit 1 fi -export PKGDIR=$(portageq envvar PKGDIR) -export PORTAGE_TMPDIR=$(portageq envvar PORTAGE_TMPDIR) - -source @PORTAGE_BASE@/sbin/functions.sh +source "${PORTAGE_BIN_PATH}/isolated-functions.sh" +case "${NOCOLOR:-false}" in + yes|true) + unset_colors + ;; + no|false) + set_colors + ;; +esac # here we make a package given a little info # $1 = package-name w/version @@ -60,6 +74,9 @@ do_pkg() { # get pkg info files mkdir -p "${MYDIR}"/temp cp "${SRCDIR}"/* "${MYDIR}"/temp/ + [ -d "${PKGDIR}"/All ] || mkdir -p "${PKGDIR}"/All + local pkg_dest="${PKGDIR}/All/${1}.tbz2" + local pkg_tmp="${PKGDIR}/All/${1}.tbz2.$$" # create filelist and a basic tbz2 gawk '{ @@ -68,19 +85,21 @@ do_pkg() { NF=NF-2 else if ($1 == "sym") NF=NF-3 - print } - }' "${SRCDIR}"/CONTENTS | cut -f2- -d" " - | sed -e 's:^/:./:' > "${MYDIR}"/filelist - tar vjcf "${MYDIR}"/bin.tar.bz2 -C "${ROOT}" --files-from="${MYDIR}"/filelist --no-recursion + print + }' "${SRCDIR}"/CONTENTS | cut -f2- -d" " - | sed -e 's:^/:./:' | \ + while read f; do + [ -d "${ROOT}/${f}" ] && [ -h "${ROOT}/${f}" ] && continue + echo "$f" + done > "${MYDIR}"/filelist + tar vjcf "${pkg_tmp}" -C "${ROOT}" --files-from="${MYDIR}"/filelist --no-recursion # join together the basic tbz2 and the pkg info files - xpak "${MYDIR}"/temp "${MYDIR}"/inf.xpak - tbz2tool join "${MYDIR}"/bin.tar.bz2 "${MYDIR}"/inf.xpak "${MYDIR}"/$1.tbz2 + python -c "import xpak; t=xpak.tbz2('${pkg_tmp}'); t.recompose('${MYDIR}/temp')" # move the final binary package to PKGDIR - [ -d "${PKGDIR}"/All ] || mkdir -p "${PKGDIR}"/All + mv -f "${pkg_tmp}" "${pkg_dest}" [ -d "${PKGDIR}/$2" ] || mkdir -p "${PKGDIR}/$2" - mv "${MYDIR}"/$1.tbz2 "${PKGDIR}"/All ( cd "${PKGDIR}/$2" && ln -s ../All/$1.tbz2 ) # cleanup again diff --git a/bin/regenworld b/bin/regenworld index f75efe16d..803de9171 100755 --- a/bin/regenworld +++ b/bin/regenworld @@ -9,7 +9,7 @@ import os import portage, string, re __candidatematcher__ = re.compile("^[0-9]+: \\*\\*\\* emerge ") -__noncandidatematcher__ = re.compile(" sync( |$)| clean( |$)| search( |$)|--oneshot| unmerge( |$)") +__noncandidatematcher__ = re.compile(" sync( |$)| clean( |$)| search( |$)|--oneshot|--fetchonly| unmerge( |$)") def issyspkg(pkgline): return (pkgline[0] == "*") diff --git a/bin/repoman b/bin/repoman index c54ffbd42..5a8a8aa0b 100755 --- a/bin/repoman +++ b/bin/repoman @@ -1,15 +1,16 @@ #!@PYTHON@ -O # Copyright 1999-2006 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Id: /var/cvsroot/gentoo-src/portage/bin/repoman,v 1.98.2.23 2005/06/18 01:00:43 vapier Exp $ +# $Id$ # Next to do: dep syntax checking in mask files # Then, check to make sure deps are satisfiable (to avoid "can't find match for" problems) # that last one is tricky because multiple profiles need to be checked. -import os,sys,shutil +import errno, os, shutil, sys +if not hasattr(__builtins__, "set"): + from sets import Set as set exename=os.path.basename(sys.argv[0]) -os.environ["PORTAGE_CALLER"]="repoman" sys.path = ["@PORTAGE_BASE@/pym"]+sys.path version="1.2" @@ -22,7 +23,10 @@ map(allowed_filename_chars_set.setdefault, map(chr, map(ord, [".", "-", "_", "+" import string,signal,re,pickle,tempfile +os.environ["PORTAGE_LEGACY_GLOBALS"] = "false" import portage +del os.environ["PORTAGE_LEGACY_GLOBALS"] + import portage_checksum import portage_const import portage_dep @@ -32,16 +36,23 @@ import time import codecs from portage_manifest import Manifest +from portage_exception import ParseError -from output import * -#bold, darkgreen, darkred, green, red, turquoise, yellow +from output import bold, darkgreen, darkred, green, nocolor, red, turquoise, yellow from commands import getstatusoutput from fileinput import input from grp import getgrnam -from stat import * +from stat import S_ISDIR, ST_CTIME, ST_GID, ST_MTIME + +# A sane umask is needed for files that portage creates. +os.umask(022) +repoman_settings = portage.config(local_config=False, + config_incrementals=portage_const.INCREMENTALS) +repoman_settings.lock() -if portage.settings["NOCOLOR"] in ("yes","true") or not sys.stdout.isatty(): +if repoman_settings.get("NOCOLOR", "").lower() in ("yes", "true") or \ + not sys.stdout.isatty(): nocolor() def warn(txt): @@ -61,7 +72,6 @@ def exithandler(signum=None,frame=None): os.kill(0,signal.SIGKILL) signal.signal(signal.SIGINT,exithandler) -REPOROOTS=["gentoo-x86","gentoo-alt/trunk/prefix"] shortmodes={"ci":"commit"} modeshelp={ "scan" : "Scan directory tree for QA issues (default)", @@ -78,7 +88,7 @@ repoman_options={ "--commitmsgfile" : "Adds a commit message from the specified file", "--help" : "Show this screen", "--ignore-arches" : "Ignore arch-specific failures (where arch != host)", -"--include-masked" : "Include masked packages in scans at category or tree level", +"--ignore-masked" : "Ignore masked packages (not allowed with commit mode)", "--pretend" : "Don't commit or fix anything; just show what would be done", "--quiet" : "Be less verbose about extraneous info", "--verbose" : "Displays every package name while checking", @@ -87,7 +97,7 @@ repoman_options={ } repoman_shortoptions={ "-h" : "--help", -"-i" : "--include-masked", +"-i" : "--ignore-masked", "-I" : "--ignore-arches", "-m" : "--commitmsg", "-M" : "--commitmsgfile", @@ -149,7 +159,8 @@ qahelp={ "ebuild.output":"A simple sourcing of the ebuild produces output; this breaks ebuild policy.", "ebuild.nesteddie":"Placing 'die' inside ( ) prints an error, but doesn't stop the ebuild.", "variable.readonly":"Assigning a readonly variable", - "IUSE.invalid":"This build has a variable in IUSE that is not in the use.desc or use.local.desc file", + "LIVEVCS.stable":"This ebuild is a live checkout from a VCS but has stable keywords.", + "IUSE.invalid":"This ebuild has a variable in IUSE that is not in the use.desc or use.local.desc file", "LICENSE.invalid":"This ebuild is listing a license that doesnt exist in portages license/ dir.", "KEYWORDS.invalid":"This ebuild contains KEYWORDS that are not listed in profiles/arch.list or for which no valid profile was found", "ebuild.nostable":"There are no ebuilds that are marked as stable for your ARCH", @@ -192,7 +203,9 @@ qawarnings=[ "metadata.bad", "virtual.versioned", "virtual.exists", -"virtual.unavailable" +"virtual.unavailable", +"usage.obsolete", +"LIVEVCS.stable" ] missingvars=["KEYWORDS","LICENSE","DESCRIPTION","HOMEPAGE","SLOT"] @@ -217,7 +230,6 @@ ven_string=ven_cat+'/'+ven_nam+'-'+ven_ver+ven_suf+ven_rev valid_ebuild_name_re=re.compile(ven_string+'$', re.I) valid_ebuild_filename_re=re.compile(ven_string+'\.ebuild$', re.I) -repoman_settings = portage.config(clone=portage.settings) verbose=0 quiet=0 @@ -349,7 +361,7 @@ if len(sys.argv)>1: elif sys.argv[x] in repoman_shortoptions.keys(): sys.argv[x] = repoman_shortoptions[sys.argv[x]] if sys.argv[x] in modes: - if mymode==None: + if mymode is None: mymode=sys.argv[x] else: err("Please specify either \""+mymode+"\" or \""+sys.argv[x]+"\", but not both.") @@ -370,7 +382,7 @@ if len(sys.argv)>1: else: err_help("\""+sys.argv[x]+"\" is not a valid mode or option.") x=x+1 -if mymode==None: +if mymode is None: mymode="scan" if mymode=="help" or ("--help" in myoptions): help(exitstatus=0) @@ -378,20 +390,21 @@ if ("--version" in myoptions): show_version() if mymode=="last" or (mymode=="lfull"): last() -if mymode=="commit" and "--include-masked" not in myoptions: - myoptions.append("--include-masked") +if mymode == "commit": + while "--ignore-masked" in myoptions: + myoptions.remove("--ignore-masked") isCvs=False myreporoot=None if os.path.isdir("CVS"): - repoman_settings["PORTDIR_OVERLAY"]="" - if "cvs" not in portage.features: + if "cvs" not in repoman_settings.features: print print print red('!!! You do not have ')+bold('FEATURES="cvs" ')+red("enabled...") print red("!!! ")+bold("Adding \"cvs\" to FEATURES") print os.environ["FEATURES"]=repoman_settings["FEATURES"]+" cvs" + isCvs = True try: isCvs=True @@ -476,13 +489,20 @@ def have_profile_dir(path, maxdepth=3): portdir=None portdir_overlay=None mydir=os.getcwd() +if "PWD" in os.environ and os.environ["PWD"] != mydir and \ + os.path.realpath(os.environ["PWD"]) == mydir: + # getcwd() returns the canonical path but that makes it hard for repoman to + # orient itself if the user has symlinks in their portage tree structure. + # We use os.environ["PWD"], if available, to get the non-canonical path of + # the current working directory (from the shell). + mydir = os.path.normpath(os.environ["PWD"]) if mydir[-1] != "/": mydir += "/" for overlay in repoman_settings["PORTDIR_OVERLAY"].split(): if overlay[-1] != "/": overlay += "/" - if mydir[:len(overlay)] == overlay: + if mydir.startswith(overlay): portdir_overlay = overlay subdir = mydir[len(overlay):] if subdir and subdir[-1] != "/": @@ -492,14 +512,15 @@ for overlay in repoman_settings["PORTDIR_OVERLAY"].split(): break if not portdir_overlay: - if (repoman_settings["PORTDIR"]+"/")[:len(mydir)] == mydir: + if (repoman_settings["PORTDIR"] + os.path.sep).startswith(mydir): portdir_overlay = repoman_settings["PORTDIR"] else: portdir_overlay = have_profile_dir(mydir) portdir = portdir_overlay if not portdir_overlay: - print darkred("Unable to determine PORTDIR.") + sys.stderr.write("Repoman is unable to determine PORTDIR or PORTDIR_OVERLAY from" + \ + " the current\nworking directory.\n") sys.exit(1) if not portdir: @@ -521,9 +542,15 @@ if quiet < 2: print "PORTDIR = \""+os.environ["PORTDIR"]+"\"" print "PORTDIR_OVERLAY = \""+os.environ["PORTDIR_OVERLAY"]+"\"" - -reload(portage) -repoman_settings = portage.config(clone=portage.settings) +# Now that PORTDIR_OVERLAY is properly overridden, create the portdb. +repoman_settings = portage.config(local_config=False, + config_incrementals=portage_const.INCREMENTALS) +trees = portage.create_trees() +trees["/"]["porttree"].settings = repoman_settings +portdb = trees["/"]["porttree"].dbapi +portdb.mysettings = repoman_settings +# dep_zapdeps looks at the vardbapi, but it shouldn't for repoman. +trees["/"]["vartree"].dbapi = portage.fakedbapi(settings=repoman_settings) if not myreporoot: myreporoot = os.path.basename(portdir_overlay) @@ -553,9 +580,6 @@ if mymode == "commit" and repolevel not in [1,2,3]: print red("***") err("Unable to identify level we're commiting from for %s" % string.join(reposplit,'/')) -if repolevel == 3 and "--include-masked" not in myoptions: - myoptions.append("--include-masked") - startdir=os.getcwd() for x in range(0,repolevel-1): @@ -570,47 +594,78 @@ if "--pretend" in myoptions: elif quiet < 1: print green("\nRepoMan scours the neighborhood...") +def parse_use_local_desc(mylines, usedict=None): + """returns a dict of the form {cpv:set(flags)}""" + if usedict is None: + usedict = {} + lineno = 0 + for l in mylines: + lineno += 1 + if not l or l.startswith("#"): + continue + mysplit = l.split(None, 1) + if not mysplit: + continue + mysplit = mysplit[0].split(":") + if len(mysplit) != 2: + raise ParseError("line %d: Malformed input: '%s'" % \ + (lineno, l.rstrip("\n"))) + usedict.setdefault(mysplit[0], set()) + usedict[mysplit[0]].add(mysplit[1]) + return usedict + # retreive local USE list luselist={} try: - mylist=portage.grabfile(portdir+"/profiles/use.local.desc") - for mypos in range(0,len(mylist)): - mysplit=mylist[mypos].split()[0] - myuse=string.split(mysplit,":") - if len(myuse)==2: - if not luselist.has_key(myuse[0]): - luselist[myuse[0]] = [] - luselist[myuse[0]].append(myuse[1]) -except SystemExit, e: - raise # Need to propogate this -except: + f = open(os.path.join(portdir, "profiles", "use.local.desc")) + parse_use_local_desc(f, luselist) + f.close() +except (IOError, OSError, ParseError), e: + print >> sys.stderr, str(e) err("Couldn't read from use.local.desc") +if portdir_overlay != portdir: + filename = os.path.join(portdir_overlay, "profiles", "use.local.desc") + if os.path.exists(filename): + try: + f = open(filename) + parse_use_local_desc(f, luselist) + f.close() + except (IOError, OSError, ParseError), e: + print >> sys.stderr, str(e) + err("Couldn't read from '%s'" % filename) + del filename + # setup a uselist from portage uselist=[] try: uselist=portage.grabfile(portdir+"/profiles/use.desc") for l in range(0,len(uselist)): uselist[l]=string.split(uselist[l])[0] + for var in string.split(repoman_settings["USE_EXPAND"]): + vardescs = portage.grabfile(portdir+"/profiles/desc/"+var.lower()+".desc") + for l in range(0, len(vardescs)): + uselist.append(var.lower() + "_" + string.split(vardescs[l])[0]) except SystemExit, e: raise # Need to propogate this except: err("Couldn't read USE flags from use.desc") # retrieve a list of current licenses in portage -liclist=portage.listdir(portdir+"/licenses") +liclist = set(portage.listdir(os.path.join(portdir, "licenses"))) if not liclist: err("Couldn't find licenses?") +if portdir_overlay != portdir: + liclist.update(portage.listdir(os.path.join(portdir_overlay, "licenses"))) # retrieve list of offical keywords -try: - kwlist=portage.grabfile(portdir+"/profiles/arch.list") -except SystemExit, e: - raise # Need to propogate this -except: - err("Couldn't read KEYWORDS from arch.list") +kwlist = set(portage.grabfile(os.path.join(portdir, "profiles", "arch.list"))) if not kwlist: - kwlist=["alpha","arm","hppa","mips","ppc","ppc-macos","sparc","x86"] + err("Couldn't read KEYWORDS from arch.list") + +if portdir_overlay != portdir: + kwlist.update(portage.grabfile( + os.path.join(portdir_overlay, "profiles", "arch.list"))) scanlist=[] if repolevel==2: @@ -620,7 +675,7 @@ if repolevel==2: caterror(catdir) mydirlist=os.listdir(startdir) for x in mydirlist: - if x=="CVS" or x==".svn": + if x == "CVS" or x.startswith("."): continue if os.path.isdir(startdir+"/"+x): scanlist.append(catdir+"/"+x) @@ -629,7 +684,7 @@ elif repolevel==1: if not os.path.isdir(startdir+"/"+x): continue for y in os.listdir(startdir+"/"+x): - if y=="CVS" or y==".svn": + if y == "CVS" or y.startswith("."): continue if os.path.isdir(startdir+"/"+x+"/"+y): scanlist.append(x+"/"+y) @@ -657,7 +712,7 @@ if os.path.exists(descfile): else: profiles[arch[0]] = [[arch[1], arch[2]]] - for x in portage.archlist: + for x in repoman_settings.archlist(): if x[0] == "~": continue if not profiles.has_key(x): @@ -674,8 +729,7 @@ else: stats={} fails={} -#objsadded records all object being added to cvs -objsadded=[] + for x in qacats: stats[x]=0 fails[x]=[] @@ -763,7 +817,6 @@ for x in scanlist: print "checking package " + x eadded=[] dadded=[] - cladded=0 catdir,pkgdir=x.split("/") checkdir=repodir+"/"+x checkdirlist=os.listdir(checkdir) @@ -819,16 +872,12 @@ for x in scanlist: myf.close() for l in myl: if l[0]!="/": - lname=l.split() - else: - lname=l[1:].split("/") - if not len(lname): continue - objsadded.append(lname[0]) - if lname[0][-7:]==".ebuild": - eadded.append(lname[0][:-7]) - if lname[0]=="ChangeLog": - cladded=1 + splitl=l[1:].split("/") + if not len(splitl): + continue + if splitl[0][-7:]==".ebuild": + eadded.append(splitl[0][:-7]) except IOError: if mymode=="commit" and isCvs: stats["CVS/Entries.IO_error"] += 1 @@ -847,14 +896,12 @@ for x in scanlist: myf.close() for l in myl: if l[0]!="/": - lname=l.split() - else: - lname=l[1:].split("/") - if not len(lname): continue - objsadded.append(lname[0]) - if lname[0][:7]=="digest-": - dadded.append(lname[0][7:]) + splitl=l[1:].split("/") + if not len(splitl): + continue + if splitl[0][:7]=="digest-": + dadded.append(splitl[0][7:]) except IOError: if mymode=="commit" and isCvs: stats["CVS/Entries.IO_error"] += 1 @@ -864,7 +911,12 @@ for x in scanlist: fails["svn.IO_error"].append(checkdir+"/files svn info") continue - mf=Manifest(checkdir, db=portage.db["/"]["porttree"].dbapi, mysettings=repoman_settings) + if mymode == "commit": + repoman_settings["O"] = checkdir + portage.digestgen( + [], repoman_settings, manifestonly=1, myportdb=portdb) + + mf = Manifest(checkdir, repoman_settings["DISTDIR"]) mydigests=mf.getTypeDigests("DIST") myfiles_all = [] @@ -894,7 +946,6 @@ for x in scanlist: vcsrm="svn rm" if "--pretend" in myoptions: print "(cd "+repodir+"/"+x+"/files; "+vcsrm+" "+y+")" - else: os.system("(cd "+repodir+"/"+x+"/files; "+vcsrm+" "+y+")") else: @@ -902,7 +953,7 @@ for x in scanlist: fails["digest.stray"].append(x+"/files/"+y) else: # We have an ebuild - myuris,myfiles = portage.db["/"]["porttree"].dbapi.getfetchlist(mykey,all=True) + myuris, myfiles = portdb.getfetchlist(mykey, all=True) myfiles_all.extend(myfiles) uri_dict = {} @@ -922,12 +973,14 @@ for x in scanlist: stats["digest.unmatch"] += 1 fails["digest.unmatch"].append(y+"::"+myf) else: - eb_name,eb_location = portage.db["/"]["porttree"].dbapi.findname2(mykey) - portage.doebuild(eb_name, "digest", "/", uri_settings, tree="porttree") + eb_name = portdb.findname2(mykey)[0] + portage.doebuild(eb_name, "digest", "/", + uri_settings, tree="porttree", + mydbapi=portdb) else: stats["digest.partial"] += 1 fails["digest.partial"].append(y+"::"+myf) - elif "assume-digests" not in portage.features: + elif "assume-digests" not in repoman_settings.features: if os.path.exists(myff): if not portage_checksum.verify_all(myff, mydigests[myf]): stats["digest.fail"] += 1 @@ -952,6 +1005,8 @@ for x in scanlist: if y == "CVS" or y == ".svn": continue for z in os.listdir(checkdir+"/files/"+y): + if z == "CVS" or z == ".svn": + continue filesdirlist.append(y+"/"+z) # current policy is no files over 20k, this is the check. elif mystat.st_size > 20480: @@ -1016,7 +1071,7 @@ for x in scanlist: stats["digest.missing"]=stats["digest.missing"]+1 fails["digest.missing"].append(x+"/files/digest-"+y) myesplit=portage.pkgsplit(y) - if myesplit==None or not valid_ebuild_name(x.split("/")[0]+"/"+y): + if myesplit is None or not valid_ebuild_name(x.split("/")[0]+"/"+y): stats["ebuild.invalidname"]=stats["ebuild.invalidname"]+1 fails["ebuild.invalidname"].append(x+"/"+y+".ebuild") continue @@ -1026,7 +1081,8 @@ for x in scanlist: fails["ebuild.namenomatch"].append(x+"/"+y+".ebuild") continue try: - myaux = dict( zip(allvars, portage.db["/"]["porttree"].dbapi.aux_get(catdir+"/"+y, allvars ) ) ) + myaux = dict( + zip(allvars, portdb.aux_get(os.path.join(catdir, y), allvars))) except KeyError: stats["ebuild.syntax"]=stats["ebuild.syntax"]+1 fails["ebuild.syntax"].append(x+"/"+y+".ebuild") @@ -1048,7 +1104,8 @@ for x in scanlist: if prov_cp != myprovide: stats["virtual.versioned"]+=1 fails["virtual.versioned"].append(x+"/"+y+".ebuild: "+myprovide) - prov_pkg = portage.dep_getkey(portage.best(portage.db["/"]["porttree"].dbapi.xmatch("match-all", prov_cp))) + prov_pkg = portage.dep_getkey( + portage.best(portdb.xmatch("match-all", prov_cp))) if prov_cp == prov_pkg: stats["virtual.exists"]+=1 fails["virtual.exists"].append(x+"/"+y+".ebuild: "+prov_cp) @@ -1070,9 +1127,21 @@ for x in scanlist: if not haskeyword: stats["KEYWORDS.stupid"] += 1 fails["KEYWORDS.stupid"].append(x+"/"+y+".ebuild") - + + """ + Ebuilds that inherit a "Live" eclasss (darcs,subversion,git,cvs,etc..) should + not be allowed to be marked stable + """ + if set(["darcs","cvs","subversion","git"]).intersection(myaux["INHERITED"].split()): + bad_stable_keywords = [keyword for keyword in myaux["KEYWORDS"].split() if not keyword.startswith("~") and not keyword.startswith("-")] + if bad_stable_keywords: + stats["LIVEVCS.stable"] += 1 + fails["LIVEVCS.stable"].append(x+"/"+y+".ebuild with stable keywords:%s " % bad_stable_keywords) + del keyword, bad_stable_keywords + if "--ignore-arches" in myoptions: - arches=[[repoman_settings["ARCH"], repoman_settings["ARCH"], portage.groups]] + arches = [[repoman_settings["ARCH"], repoman_settings["ARCH"], + repoman_settings["ACCEPT_KEYWORDS"].split()]] else: arches=[] for keyword in myaux["KEYWORDS"].split(): @@ -1088,7 +1157,7 @@ for x in scanlist: badlicsyntax = False badprovsyntax = False catpkg = catdir+"/"+y - myiuse = myaux["IUSE"].split() + portage.archlist + myiuse = myaux["IUSE"].split() + repoman_settings.archlist() type_list, badsyntax = [], [] for mytype in ("DEPEND", "RDEPEND", "PDEPEND", "LICENSE", "PROVIDE"): mydepstr = myaux[mytype] @@ -1126,7 +1195,8 @@ for x in scanlist: if mytype in ("DEPEND", "RDEPEND", "PDEPEND"): for token in filter(lambda x: not (x.endswith("?") or x.strip() in ("||", "&&", "(", ")")), mydepstr.split()): - if not "/" in token: + if not "/" in token or \ + ":" in token and myaux["EAPI"] == "0": badsyntax.append("'%s' not a valid atom" % token) type_list.extend([mytype] * (len(badsyntax) - len(type_list))) @@ -1147,7 +1217,6 @@ for x in scanlist: fails["usage.obsolete"].append("%s/%s.ebuild: not migrated to modular X" % (x, y)) for keyword,arch,groups in arches: - portage.groups=groups if not profiles.has_key(arch): # A missing profile will create an error further down @@ -1158,17 +1227,30 @@ for x in scanlist: profdir = portdir+"/profiles/"+prof[0] - portage.profiledir=profdir - - if arch_caches.has_key(prof[0]): - dep_settings, portage.portdb, portage.db["/"]["porttree"] = arch_caches[prof[0]] + if prof[0] in arch_caches: + dep_settings = arch_caches[prof[0]] else: - os.environ["ACCEPT_KEYWORDS"]="-~"+arch - dep_settings=portage.config(config_profile_path=profdir, config_incrementals=portage_const.INCREMENTALS) - portage.portdb=portage.portdbapi(portdir, dep_settings) - portage.db["/"]["porttree"]=portage.portagetree("/",dep_settings.getvirtuals("/")) - arch_caches[prof[0]]=[dep_settings, portage.portdb, portage.db["/"]["porttree"]] - + dep_settings = portage.config( + config_profile_path=profdir, + config_incrementals=portage_const.INCREMENTALS, + local_config=False) + arch_caches[prof[0]] = dep_settings + while True: + try: + # Protect ACCEPT_KEYWORDS from config.regenerate() + # (just in case) + dep_settings.incrementals.remove("ACCEPT_KEYWORDS") + except ValueError: + break + + trees["/"]["porttree"].settings = dep_settings + portdb.mysettings = dep_settings + # for package.use.mask support inside dep_check + dep_settings.setcpv("/".join((catdir, y))) + dep_settings["ACCEPT_KEYWORDS"] = " ".join(groups) + # just in case, prevent config.reset() from nuking these. + dep_settings.backup_changes("ACCEPT_KEYWORDS") + for myprovide in myaux["PROVIDE"].split(): prov_cp = portage.dep_getkey(myprovide) if prov_cp not in dep_settings.getvirtuals(): @@ -1176,9 +1258,10 @@ for x in scanlist: fails["virtual.unavailable"].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+prov_cp) if not baddepsyntax: - ismasked = (catdir+"/"+y not in portage.db["/"]["porttree"].dbapi.xmatch("list-visible",x)) + ismasked = os.path.join(catdir, y) not in \ + portdb.xmatch("list-visible", x) if ismasked: - if "--include-masked" not in myoptions: + if "--ignore-masked" in myoptions: continue #we are testing deps for a masked package; give it some lee-way suffix="masked" @@ -1189,7 +1272,7 @@ for x in scanlist: if prof[1] == "dev": suffix=suffix+"indev" - + for mytype,mypos in [["DEPEND",len(missingvars)],["RDEPEND",len(missingvars)+1],["PDEPEND",len(missingvars)+2]]: mykey=mytype+".bad"+suffix @@ -1197,7 +1280,9 @@ for x in scanlist: if not myvalue: continue try: - mydep=portage.dep_check(myvalue,portage.db["/"]["porttree"].dbapi,dep_settings,use="all",mode=matchmode) + mydep = portage.dep_check(myvalue, portdb, + dep_settings, use="all", mode=matchmode, + trees=trees) except KeyError, e: stats[mykey]=stats[mykey]+1 fails[mykey].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+repr(e[0])) @@ -1281,11 +1366,12 @@ for x in scanlist: ignore_line = re.compile(r'(^$)|(^(\t)*#)') leading_spaces = re.compile(r'^[\S\t]') trailing_whitespace = re.compile(r'.*([\S]$)') - readonly_assignment = re.compile(r'^\s*(export\s+)?(A|P|PV|PN|PR|PVR|PF|D|WORKDIR|FILESDIR|FEATURES|USE)=') + readonly_assignment = re.compile(r'^\s*(export\s+)?(A|CATEGORY|P|PV|PN|PR|PVR|PF|D|WORKDIR|FILESDIR|FEATURES|USE)=') continuation_symbol = re.compile(r'(.*[ ]+[\\][ ].*)') line_continuation_quoted = re.compile(r'(\"|\')(([\w ,:;#\[\]\.`=/|\$\^\*{}()\'-])|(\\.))*\1') line_continuation = re.compile(r'([^#]*\S)(\s+|\t)\\$') linenum=0 + previous_line = None for line in input(checkdir+"/"+y+".ebuild"): linenum += 1 # Gentoo copyright check @@ -1329,7 +1415,9 @@ for x in scanlist: fails["ebuild.minorsyn"].append(x+"/"+y+".ebuild: %s" % myerrormsg) # Readonly variable assignment check match = readonly_assignment.match(line) - if match: + # The regex can give a false positive for continued lines, + # so we check the previous line to see if it was continued. + if match and (not previous_line or not line_continuation.match(previous_line)): # invalid assignment, very bad! myerrormsg = "Readonly variable assignment to %s on line %d" % (match.group(2), linenum) stats["variable.readonly"] += 1 @@ -1350,6 +1438,8 @@ for x in scanlist: myerrormsg = "Line continuation (\"\\\") Syntax Error. Line %d" % linenum stats["ebuild.majorsyn"] +=1 fails["ebuild.majorsyn"].append(x+"/"+y+".ebuild: %s" % myerrormsg) + previous_line = line + del previous_line # Check for 'all unstable' or 'all masked' -- ACCEPT_KEYWORDS is stripped # XXX -- Needs to be implemented in dep code. Can't determine ~arch nicely. @@ -1375,7 +1465,8 @@ if not (os.stat(statsfile)[ST_GID] == getgrnam('portage')[2]): if not (os.stat(failsfile)[ST_GID] == getgrnam('portage')[2]): os.chown(failsfile,os.geteuid(),getgrnam('portage')[2]) os.chmod(failsfile,0664) -print +if quiet < 2: + print #dofail will be set to 1 if we have failed in at least one non-warning category dofail=0 #dowarn will be set to 1 if we tripped any warnings @@ -1525,48 +1616,41 @@ else: print sys.exit(1) - - myheaders=[] - mychanged=[] - mydirty=[] - mynew=[] - myupdates=[] - myremoved=[] - mymissing=None + retval=["",""] if isCvs: - if mymissing: - print "The following files are obviously missing from your cvs tree" - print "and are being fetched so we can continue:" - for x in mymissing: - print " ",x - if "--pretend" in myoptions: - print "(cvs -q up "+string.join(mymissing)+")" - retval=0 - else: - retval=os.system("cvs -q up "+string.join(mymissing)) - if retval: - print "!!! Exiting on cvs (shell) error code:",retval - sys.exit(retval) - del mymissing - retval=["",""] print "Performing a "+green("cvs -n up")+" with a little magic grep to check for updates." retval=getstatusoutput("cvs -n up 2>&1 | "+portage_const.EPREFIX+"/bin/grep -E '^[^\?] .*' | "+portage_const.EPREFIX+"/bin/grep -E -v '^. .*/digest-[^/]+|^cvs server: .* -- ignored$'") - mylines=string.split(retval[1], "\n") - - for x in mylines: - if not x: - continue - if x[0] not in "UPMAR": # Updates,Patches,Modified,Added,Removed - print red("!!! Please fix the following issues reported from cvs: ")+green("(U,P,M,A,R are ok)") - print red("!!! Note: This is a pretend/no-modify pass...") - print retval[1] - print - sys.exit(1) - elif x[0] in ["U","P"]: - myupdates+=[x[2:]] + + mylines=string.split(retval[1], "\n") + myupdates=[] + for x in mylines: + if not x: + continue + if x[0] not in "UPMAR": # Updates,Patches,Modified,Added,Removed + print red("!!! Please fix the following issues reported from cvs: ")+green("(U,P,M,A,R are ok)") + print red("!!! Note: This is a pretend/no-modify pass...") + print retval[1] + print + sys.exit(1) + elif x[0] in ["U","P"]: + myupdates+=[x[2:]] + + if myupdates: + print green("Fetching trivial updates...") + if "--pretend" in myoptions: + print "(cvs up "+string.join(myupdates)+")" + retval=0 + else: + retval=os.system("cvs up "+string.join(myupdates)) + if retval!=0: + print "!!! cvs exited with an error. Terminating." + sys.exit(retval) + if isCvs: mycvstree=cvstree.getentries("./",recursive=1) mychanged=cvstree.findchanged(mycvstree,recursive=1,basedir="./") + for manifest in [file for file in mychanged if '/Manifest' in file]: + mychanged.remove(manifest) mynew=cvstree.findnew(mycvstree,recursive=1,basedir="./") myremoved=cvstree.findremoved(mycvstree,recursive=1,basedir="./") if not (mychanged or mynew or myremoved): @@ -1576,34 +1660,42 @@ else: print "(Didn't find any changed files...)" print sys.exit(0) - if myupdates: - print green("Fetching trivial updates...") - if "--pretend" in myoptions: - print "(cvs up "+string.join(myupdates)+")" - retval=0 - else: - retval=os.system("cvs up "+string.join(myupdates)) - if retval!=0: - print "!!! cvs exited with an error. Terminating." - sys.exit(retval) - myupdates=mychanged+mynew - headerstring="'\$(Header|Id)" - headerstring+=".*\$'" - for myfile in myupdates: - myout=getstatusoutput(portage_const.EPREFIX+"/bin/grep -E -q "+headerstring+" "+myfile) - if myout[0]==0: - myheaders.append(myfile) - - print "*",green(str(len(myupdates))),"files being committed...",green(str(len(myheaders))),"have headers that will change." - print "*","Files with headers will cause the manifests to be made and recommited." + + myupdates=mychanged+mynew + myheaders=[] + mydirty=[] + headerstring="'\$(Header|Id)" + headerstring+=".*\$'" + for myfile in myupdates: + myout=getstatusoutput("egrep -q "+headerstring+" "+myfile) + if myout[0]==0: + myheaders.append(myfile) + + print "*",green(str(len(myupdates))),"files being committed...",green(str(len(myheaders))),"have headers that will change." + print "*","Files with headers will cause the manifests to be made and recommited." + if quiet == 0: print "myupdates:",myupdates print "myheaders:",myheaders print - unlinkfile=0 + unlinkfile=0 + if commitmessagefile: + try: + f = open(commitmessagefile) + commitmessage = f.read() + f.close() + del f + except (IOError, OSError), e: + if e.errno == errno.ENOENT: + portage.writemsg("!!! File Not Found: --commitmsgfile='%s'\n" % commitmessagefile) + else: + raise + # We've read the content so the file is no longer needed. + commitmessagefile = None + if not commitmessage: + print "Please enter a CVS commit message at the prompt:" # TODO svn check for UPMAR if isSvn: - myconflicts=[] print "Checking "+green("svn -u status")+" to check for updates." mylines=[ elem.strip().split() for elem in getstatusoutput("svn -u status")[1].split("\n") ][:-1] @@ -1660,7 +1752,7 @@ else: exithandler() try: commitmessage+="\n(Portage version: "+str(portage.VERSION)+"-prefix)" - except: + except AttributeError: print "Failed to insert portage version in message!" commitmessage+="\n(Portage version: Unknown)" if not commitmessagefile: @@ -1679,6 +1771,7 @@ else: print green("------------------------------------------------------------------------------") print + retval = None if "--pretend" in myoptions: if isCvs: print "(cvs -q commit -F "+commitmessagefile+")" @@ -1696,17 +1789,37 @@ else: # Setup the GPG commands def gpgsign(filename): + if "PORTAGE_GPG_KEY" not in repoman_settings: + raise portage_exception.MissingParameter("PORTAGE_GPG_KEY is unset!") + if "PORTAGE_GPG_DIR" not in repoman_settings: + if os.environ.has_key("HOME"): + repoman_settings["PORTAGE_GPG_DIR"] = os.path.join(os.environ["HOME"], ".gnupg") + if quiet < 1: + print "Automatically setting PORTAGE_GPG_DIR to",repoman_settings["PORTAGE_GPG_DIR"] + else: + raise portage_exception.MissingParameter("PORTAGE_GPG_DIR is unset!") + gpg_dir = repoman_settings["PORTAGE_GPG_DIR"] + if gpg_dir.startswith("~") and "HOME" in os.environ: + repoman_settings["PORTAGE_GPG_DIR"] = os.path.join( + os.environ["HOME"], gpg_dir[1:].lstrip(os.path.sep)) + if not os.access(repoman_settings["PORTAGE_GPG_DIR"], os.X_OK): + raise portage_exception.InvalidLocation( + "Unable to access directory: PORTAGE_GPG_DIR='%s'" % \ + repoman_settings["PORTAGE_GPG_DIR"]) gpgcmd = "gpg --sign --clearsign --yes " gpgcmd+= "--default-key "+repoman_settings["PORTAGE_GPG_KEY"] if repoman_settings.has_key("PORTAGE_GPG_DIR"): gpgcmd += " --homedir "+repoman_settings["PORTAGE_GPG_DIR"] - rValue = os.system(gpgcmd+" "+filename) - if rValue == 0: - os.rename(filename+".asc", filename) + if "--pretend" in myoptions: + print "("+gpgcmd+" "+filename+")" else: - print "!!! gpg exited with '" + str(rValue) + "' status" - return rValue + rValue = os.system(gpgcmd+" "+filename) + if rValue == os.EX_OK: + os.rename(filename+".asc", filename) + else: + raise portage_exception.PortageException("!!! gpg exited with '" + str(rValue) + "' status") + need_commit = False if myheaders or myupdates or myremoved or mynew: myfiles=myheaders+myupdates+myremoved+mynew for x in range(len(myfiles)-1, -1, -1): @@ -1715,7 +1828,8 @@ else: mydone=[] if repolevel==3: # In a package dir repoman_settings["O"]=os.getcwd() - portage.digestgen([],repoman_settings,manifestonly=1) + portage.digestgen([], repoman_settings, manifestonly=1, + myportdb=portdb) elif repolevel==2: # In a category dir for x in myfiles: xs=string.split(x,"/") @@ -1725,7 +1839,8 @@ else: continue mydone.append(xs[0]) repoman_settings["O"]=os.path.join(os.getcwd(), xs[0]) - portage.digestgen([],repoman_settings,manifestonly=1) + portage.digestgen([], repoman_settings, manifestonly=1, + myportdb=portdb) elif repolevel==1: # repo-cvsroot print green("RepoMan sez:"), "\"You're rather crazy... doing the entire repository.\"\n" for x in myfiles: @@ -1736,65 +1851,64 @@ else: continue mydone.append(string.join(xs[:2],"/")) repoman_settings["O"]=os.path.join(os.getcwd(), x[0], x[1]) - portage.digestgen([],repoman_settings,manifestonly=1) + portage.digestgen([], repoman_settings, manifestonly=1, + myportdb=portdb) else: print red("I'm confused... I don't know where I am!") sys.exit(1) - if "--pretend" in myoptions: - if isCvs: + # Force an unsigned commit when more than one Manifest needs to be signed. + if repolevel < 3 and "sign" in repoman_settings.features: + if "--pretend" in myoptions: print "(cvs -q commit -F "+commitmessagefile+")" - if isSvn: - print "(svn -q commit -F "+commitmessagefile+")" - else: - mymsg=open(commitmessagefile,"w") - mymsg.write(commitmessage) - mymsg.write(" (Unsigned Manifest commit)") - mymsg.close() - if isCvs: + else: + mymsg=open(commitmessagefile,"w") + mymsg.write(commitmessage) + mymsg.write("\n (Unsigned Manifest commit)") + mymsg.close() retval=os.system("cvs -q commit -F "+commitmessagefile) - if isSvn: - retval=os.system("svn -q commit -F "+commitmessagefile) - if retval: - print "!!! Exiting on cvs (shell) error code:",retval - sys.exit(retval) - - if "sign" in portage.features: - mydone=[] - if repolevel==3: # In a package dir - repoman_settings["O"]="./" - while(gpgsign(repoman_settings["O"]+"/Manifest")): - portage.writemsg("!!! YOU MUST sign the Manifest.\n") - portage.writemsg("!!! You can also disable this for the time being by removing FEATURES='sign'") - time.sleep(3) - elif repolevel==2: # In a category dir - for x in myfiles: - xs=string.split(x,"/") - if xs[0]==".": - xs=xs[1:] - if xs[0] in mydone: - continue - mydone.append(xs[0]) - repoman_settings["O"]="./"+xs[0] - while(gpgsign(repoman_settings["O"]+"/Manifest")): - portage.writemsg("!!! YOU MUST sign the Manifest.\n") - portage.writemsg("!!! You can also disable this for the time being by removing FEATURES='sign'") - time.sleep(3) - elif repolevel==1: # repo-cvsroot - print green("RepoMan sez:"), "\"You're rather crazy... doing the entire repository.\"\n" - for x in myfiles: - xs=string.split(x,"/") - if xs[0]==".": - xs=xs[1:] - if string.join(xs[:2],"/") in mydone: - continue - mydone.append(string.join(xs[:2],"/")) - repoman_settings["O"]="./"+string.join(xs[:2],"/") - while(gpgsign(repoman_settings["O"]+"/Manifest")): - portage.writemsg("!!! YOU MUST sign the Manifest.\n") - portage.writemsg("!!! You can also disable this for the time being by removing FEATURES='sign'") - time.sleep(3) + if retval: + print "!!! Exiting on cvs (shell) error code:",retval + sys.exit(retval) + else: + need_commit = True + signed = False + if "sign" in repoman_settings.features: + signed = True + try: + if repolevel==3: # In a package dir + repoman_settings["O"] = "." + gpgsign(os.path.join(repoman_settings["O"], "Manifest")) + elif repolevel==2: # In a category dir + mydone=[] + for x in myfiles: + xs=string.split(x,"/") + if xs[0]==".": + xs=xs[1:] + if xs[0] in mydone: + continue + mydone.append(xs[0]) + repoman_settings["O"] = os.path.join(".", xs[0]) + gpgsign(os.path.join(repoman_settings["O"], "Manifest")) + elif repolevel==1: # repo-cvsroot + print green("RepoMan sez:"), "\"You're rather crazy... doing the entire repository.\"\n" + mydone=[] + for x in myfiles: + xs=string.split(x,"/") + if xs[0]==".": + xs=xs[1:] + if string.join(xs[:2],"/") in mydone: + continue + mydone.append(string.join(xs[:2],"/")) + repoman_settings["O"] = os.path.join(".", xs[0], xs[1]) + gpgsign(os.path.join(repoman_settings["O"], "Manifest")) + except portage_exception.PortageException, e: + portage.writemsg("!!! %s\n" % str(e)) + portage.writemsg("!!! Disabled FEATURES='sign'\n") + signed = False + + if need_commit or signed: if "--pretend" in myoptions: if isCvs: print "(cvs -q commit -F "+commitmessagefile+")" @@ -1803,16 +1917,19 @@ else: else: mymsg=open(commitmessagefile,"w") mymsg.write(commitmessage) - mymsg.write("\n (Signed Manifest commit)") + if signed: + mymsg.write("\n (Signed Manifest commit)") + else: + mymsg.write("\n (Unsigned Manifest commit)") mymsg.close() if isCvs: retval=os.system("cvs -q commit -F "+commitmessagefile) if isSvn: retval=os.system("svn -q commit -F "+commitmessagefile) if retval: - print "!!! Exiting on vcs (shell) error code:",retval + print "!!! Exiting on cvs (shell) error code:",retval sys.exit(retval) - + if unlinkfile: os.unlink(commitmessagefile) print