--- eclass/eutils.eclass 2006/03/10 23:24:21 1.228 +++ eclass/eutils.eclass 2011/09/29 02:32:20 1.365 @@ -1,39 +1,47 @@ -# Copyright 1999-2005 Gentoo Foundation +# Copyright 1999-2011 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 -# $Header: /var/cvsroot/gentoo-x86/eclass/eutils.eclass,v 1.228 2006/03/10 23:24:21 vapier Exp $ -# -# Author: Martin Schlemmer -# -# This eclass is for general purpose functions that most ebuilds -# have to implement themselves. +# $Header: /var/cvsroot/gentoo-x86/eclass/eutils.eclass,v 1.365 2011/09/29 02:32:20 vapier Exp $ + +# @ECLASS: eutils.eclass +# @MAINTAINER: +# base-system@gentoo.org +# @BLURB: many extra (but common) functions that are used in ebuilds +# @DESCRIPTION: +# The eutils eclass contains a suite of functions that complement +# the ones that ebuild.sh already contain. The idea is that the functions +# are not required in all ebuilds but enough utilize them to have a common +# home rather than having multiple ebuilds implementing the same thing. # -# NB: If you add anything, please comment it! +# Due to the nature of this eclass, some functions may have maintainers +# different from the overall eclass! inherit multilib portability -DEPEND="!bootstrap? ( sys-devel/patch )" -# sys-apps/shadow is needed for useradd, etc, bug #94745. - DESCRIPTION="Based on the ${ECLASS} eclass" -# Wait for the supplied number of seconds. If no argument is supplied, defaults -# to five seconds. If the EPAUSE_IGNORE env var is set, don't wait. If we're not -# outputting to a terminal, don't wait. For compatability purposes, the argument -# must be an integer greater than zero. -# Bug 62950, Ciaran McCreesh (05 Sep 2004) +if has "${EAPI:-0}" 0 1 2; then + +# @FUNCTION: epause +# @USAGE: [seconds] +# @DESCRIPTION: +# Sleep for the specified number of seconds (default of 5 seconds). Useful when +# printing a message the user should probably be reading and often used in +# conjunction with the ebeep function. If the EPAUSE_IGNORE env var is set, +# don't wait at all. Defined in EAPIs 0 1 and 2. epause() { - if [ -z "$EPAUSE_IGNORE" ] && [ -t 1 ] ; then - sleep ${1:-5} - fi + [[ -z ${EPAUSE_IGNORE} ]] && sleep ${1:-5} } -# Beep the specified number of times (defaults to five). If our output -# is not a terminal, don't beep. If the EBEEP_IGNORE env var is set, -# don't beep. -# Bug 62950, Ciaran McCreesh (05 Sep 2004) +# @FUNCTION: ebeep +# @USAGE: [number of beeps] +# @DESCRIPTION: +# Issue the specified number of beeps (default of 5 beeps). Useful when +# printing a message the user should probably be reading and often used in +# conjunction with the epause function. If the EBEEP_IGNORE env var is set, +# don't beep at all. Defined in EAPIs 0 1 and 2. ebeep() { local n - if [ -z "$EBEEP_IGNORE" ] && [ -t 1 ] ; then + if [[ -z ${EBEEP_IGNORE} ]] ; then for ((n=1 ; n <= ${1:-5} ; n++)) ; do echo -ne "\a" sleep 0.1 &>/dev/null ; sleep 0,1 &>/dev/null @@ -43,334 +51,456 @@ fi } -# This function generate linker scripts in /usr/lib for dynamic -# libs in /lib. This is to fix linking problems when you have -# the .so in /lib, and the .a in /usr/lib. What happens is that -# in some cases when linking dynamic, the .a in /usr/lib is used -# instead of the .so in /lib due to gcc/libtool tweaking ld's -# library search path. This cause many builds to fail. -# See bug #4411 for more info. -# -# To use, simply call: -# -# gen_usr_ldscript libfoo.so -# -# Note that you should in general use the unversioned name of -# the library, as ldconfig should usually update it correctly -# to point to the latest version of the library present. -# -# (26 Oct 2002) -# -gen_usr_ldscript() { - local lib libdir=$(get_libdir) - # Just make sure it exists - dodir /usr/${libdir} - - for lib in "${@}" ; do - cat > "${D}/usr/${libdir}/${lib}" <<-END_LDSCRIPT - /* GNU ld script - Since Gentoo has critical dynamic libraries - in /lib, and the static versions in /usr/lib, - we need to have a "fake" dynamic lib in /usr/lib, - otherwise we run into linking problems. - - See bug http://bugs.gentoo.org/4411 for more info. - */ - GROUP ( /${libdir}/${lib} ) - END_LDSCRIPT - fperms a+x "/usr/${libdir}/${lib}" || die "could not change perms on ${lib}" - done +else + +ebeep() { + ewarn "QA Notice: ebeep is not defined in EAPI=${EAPI}, please file a bug at http://bugs.gentoo.org" +} + +epause() { + ewarn "QA Notice: epause is not defined in EAPI=${EAPI}, please file a bug at http://bugs.gentoo.org" +} + +fi + +# @FUNCTION: eqawarn +# @USAGE: [message] +# @DESCRIPTION: +# Proxy to ewarn for package managers that don't provide eqawarn and use the PM +# implementation if available. Reuses PORTAGE_ELOG_CLASSES as set by the dev +# profile. +if ! declare -F eqawarn >/dev/null ; then + eqawarn() { + has qa ${PORTAGE_ELOG_CLASSES} && ewarn "$@" + } +fi + +# @FUNCTION: ecvs_clean +# @USAGE: [list of dirs] +# @DESCRIPTION: +# Remove CVS directories recursiveley. Useful when a source tarball contains +# internal CVS directories. Defaults to $PWD. +ecvs_clean() { + [[ -z $* ]] && set -- . + find "$@" -type d -name 'CVS' -prune -print0 | xargs -0 rm -rf + find "$@" -type f -name '.cvs*' -print0 | xargs -0 rm -rf +} + +# @FUNCTION: esvn_clean +# @USAGE: [list of dirs] +# @DESCRIPTION: +# Remove .svn directories recursiveley. Useful when a source tarball contains +# internal Subversion directories. Defaults to $PWD. +esvn_clean() { + [[ -z $* ]] && set -- . + find "$@" -type d -name '.svn' -prune -print0 | xargs -0 rm -rf +} + +# @FUNCTION: eshopts_push +# @USAGE: [options to `set` or `shopt`] +# @DESCRIPTION: +# Often times code will want to enable a shell option to change code behavior. +# Since changing shell options can easily break other pieces of code (which +# assume the default state), eshopts_push is used to (1) push the current shell +# options onto a stack and (2) pass the specified arguments to set. +# +# If the first argument is '-s' or '-u', we assume you want to call `shopt` +# rather than `set` as there are some options only available via that. +# +# A common example is to disable shell globbing so that special meaning/care +# may be used with variables/arguments to custom functions. That would be: +# @CODE +# eshopts_push -o noglob +# for x in ${foo} ; do +# if ...some check... ; then +# eshopts_pop +# return 0 +# fi +# done +# eshopts_pop +# @CODE +eshopts_push() { + # have to assume __ESHOPTS_SAVE__ isn't screwed with + # as a `declare -a` here will reset its value + local i=${#__ESHOPTS_SAVE__[@]} + if [[ $1 == -[su] ]] ; then + __ESHOPTS_SAVE__[$i]=$(shopt -p) + [[ $# -eq 0 ]] && return 0 + shopt "$@" || die "eshopts_push: bad options to shopt: $*" + else + __ESHOPTS_SAVE__[$i]=$- + [[ $# -eq 0 ]] && return 0 + set "$@" || die "eshopts_push: bad options to set: $*" + fi } +# @FUNCTION: eshopts_pop +# @USAGE: +# @DESCRIPTION: +# Restore the shell options to the state saved with the corresponding +# eshopts_push call. See that function for more details. +eshopts_pop() { + [[ $# -ne 0 ]] && die "eshopts_pop takes no arguments" + local i=$(( ${#__ESHOPTS_SAVE__[@]} - 1 )) + [[ ${i} -eq -1 ]] && die "eshopts_{push,pop}: unbalanced pair" + local s=${__ESHOPTS_SAVE__[$i]} + unset __ESHOPTS_SAVE__[$i] + if [[ ${s} == "shopt -"* ]] ; then + eval "${s}" || die "eshopts_pop: sanity: invalid shopt options: ${s}" + else + set +$- || die "eshopts_pop: sanity: invalid shell settings: $-" + set -${s} || die "eshopts_pop: sanity: unable to restore saved shell settings: ${s}" + fi +} -# Default directory where patches are located +# @VARIABLE: EPATCH_SOURCE +# @DESCRIPTION: +# Default directory to search for patches. EPATCH_SOURCE="${WORKDIR}/patch" -# Default extension for patches +# @VARIABLE: EPATCH_SUFFIX +# @DESCRIPTION: +# Default extension for patches (do not prefix the period yourself). EPATCH_SUFFIX="patch.bz2" -# Default options for patch -# Set -g0 to keep RCS, ClearCase, Perforce and SCCS happy. Bug #24571 -# Set --no-backup-if-mismatch so we don't leave '.orig' files behind. -EPATCH_OPTS="-g0 --no-backup-if-mismatch" -# List of patches not to apply. Not this is only file names, -# and not the full path .. +# @VARIABLE: EPATCH_OPTS +# @DESCRIPTION: +# Default options for patch: +# @CODE +# -g0 - keep RCS, ClearCase, Perforce and SCCS happy #24571 +# --no-backup-if-mismatch - do not leave .orig files behind +# -E - automatically remove empty files +# @CODE +EPATCH_OPTS="-g0 -E --no-backup-if-mismatch" +# @VARIABLE: EPATCH_EXCLUDE +# @DESCRIPTION: +# List of patches not to apply. Note this is only file names, +# and not the full path. Globs accepted. EPATCH_EXCLUDE="" +# @VARIABLE: EPATCH_SINGLE_MSG +# @DESCRIPTION: # Change the printed message for a single patch. EPATCH_SINGLE_MSG="" +# @VARIABLE: EPATCH_MULTI_MSG +# @DESCRIPTION: # Change the printed message for multiple patches. EPATCH_MULTI_MSG="Applying various patches (bugfixes/updates) ..." -# Force applying bulk patches even if not following the style: -# -# ??_${ARCH}_foo.${EPATCH_SUFFIX} -# +# @VARIABLE: EPATCH_FORCE +# @DESCRIPTION: +# Only require patches to match EPATCH_SUFFIX rather than the extended +# arch naming style. EPATCH_FORCE="no" -# This function is for bulk patching, or in theory for just one -# or two patches. -# -# It should work with .bz2, .gz, .zip and plain text patches. -# Currently all patches should be the same format. -# -# You do not have to specify '-p' option to patch, as it will -# try with -p0 to -p5 until it succeed, or fail at -p5. -# -# Above EPATCH_* variables can be used to control various defaults, -# bug they should be left as is to ensure an ebuild can rely on -# them for. -# -# Patches are applied in current directory. -# -# Bulk Patches should preferibly have the form of: -# -# ??_${ARCH}_foo.${EPATCH_SUFFIX} +# @FUNCTION: epatch +# @USAGE: [patches] [dirs of patches] +# @DESCRIPTION: +# epatch is designed to greatly simplify the application of patches. It can +# process patch files directly, or directories of patches. The patches may be +# compressed (bzip/gzip/etc...) or plain text. You generally need not specify +# the -p option as epatch will automatically attempt -p0 to -p5 until things +# apply successfully. +# +# If you do not specify any options, then epatch will default to the directory +# specified by EPATCH_SOURCE. +# +# When processing directories, epatch will apply all patches that match: +# @CODE +# if ${EPATCH_FORCE} != "yes" +# ??_${ARCH}_foo.${EPATCH_SUFFIX} +# else +# *.${EPATCH_SUFFIX} +# @CODE +# The leading ?? are typically numbers used to force consistent patch ordering. +# The arch field is used to apply patches only for the host architecture with +# the special value of "all" means apply for everyone. Note that using values +# other than "all" is highly discouraged -- you should apply patches all the +# time and let architecture details be detected at configure/compile time. # -# For example: -# -# 01_all_misc-fix.patch.bz2 -# 02_sparc_another-fix.patch.bz2 -# -# This ensures that there are a set order, and you can have ARCH -# specific patches. -# -# If you however give an argument to epatch(), it will treat it as a -# single patch that need to be applied if its a file. If on the other -# hand its a directory, it will set EPATCH_SOURCE to this. -# -# (10 Nov 2002) +# If EPATCH_SUFFIX is empty, then no period before it is implied when searching +# for patches to apply. # +# Refer to the other EPATCH_xxx variables for more customization of behavior. epatch() { _epatch_draw_line() { - # this func produces a lot of pointless noise when debugging is turned on ... - local is_debug=0 - [[ $- == *x* ]] && is_debug=1 && set +x - - local i=0 str_length="" str_out="" - - # Handle calls that do not have args, or wc not being installed ... - if [[ -z $1 ]] || ! type -p wc >/dev/null ; then - str_length=65 - else - str_length=$(echo -n "$*" | wc -m) - fi - - while ((i++ < ${str_length})) ; do - str_out="${str_out}=" - done - echo ${str_out} - - [[ ${is_debug} -eq 1 ]] && set -x - return 0 + # create a line of same length as input string + [[ -z $1 ]] && set "$(printf "%65s" '')" + echo "${1//?/=}" } - _epatch_assert() { local _pipestatus=${PIPESTATUS[*]}; [[ ${_pipestatus// /} -eq 0 ]] ; } - local PIPE_CMD="" - local STDERR_TARGET="${T}/$$.out" - local PATCH_TARGET="${T}/$$.patch" - local PATCH_SUFFIX="" - local SINGLE_PATCH="no" - local x="" unset P4CONFIG P4PORT P4USER # keep perforce at bay #56402 - if [ "$#" -gt 1 ] - then - local m="" + # Let the rest of the code process one user arg at a time -- + # each arg may expand into multiple patches, and each arg may + # need to start off with the default global EPATCH_xxx values + if [[ $# -gt 1 ]] ; then + local m for m in "$@" ; do epatch "${m}" done return 0 fi - if [ -n "$1" -a -f "$1" ] - then - SINGLE_PATCH="yes" + local SINGLE_PATCH="no" + # no args means process ${EPATCH_SOURCE} + [[ $# -eq 0 ]] && set -- "${EPATCH_SOURCE}" - local EPATCH_SOURCE="$1" - local EPATCH_SUFFIX="${1##*\.}" + if [[ -f $1 ]] ; then + SINGLE_PATCH="yes" + set -- "$1" + # Use the suffix from the single patch (localize it); the code + # below will find the suffix for us + local EPATCH_SUFFIX=$1 + + elif [[ -d $1 ]] ; then + # Some people like to make dirs of patches w/out suffixes (vim) + set -- "$1"/*${EPATCH_SUFFIX:+."${EPATCH_SUFFIX}"} + + elif [[ -f ${EPATCH_SOURCE}/$1 ]] ; then + # Re-use EPATCH_SOURCE as a search dir + epatch "${EPATCH_SOURCE}/$1" + return $? - elif [ -n "$1" -a -d "$1" ] - then - # Allow no extension if EPATCH_FORCE=yes ... used by vim for example ... - if [ "${EPATCH_FORCE}" = "yes" ] && [ -z "${EPATCH_SUFFIX}" ] - then - local EPATCH_SOURCE="$1/*" - else - local EPATCH_SOURCE="$1/*.${EPATCH_SUFFIX}" - fi else - if [ ! -d ${EPATCH_SOURCE} ] || [ -n "$1" ] - then - if [ -n "$1" -a "${EPATCH_SOURCE}" = "${WORKDIR}/patch" ] - then - EPATCH_SOURCE="$1" - fi - - echo - eerror "Cannot find \$EPATCH_SOURCE! Value for \$EPATCH_SOURCE is:" - eerror - eerror " ${EPATCH_SOURCE}" - eerror " ( ${EPATCH_SOURCE##*/} )" - echo - die "Cannot find \$EPATCH_SOURCE!" - fi - - local EPATCH_SOURCE="${EPATCH_SOURCE}/*.${EPATCH_SUFFIX}" + # sanity check ... if it isn't a dir or file, wtf man ? + [[ $# -ne 0 ]] && EPATCH_SOURCE=$1 + echo + eerror "Cannot find \$EPATCH_SOURCE! Value for \$EPATCH_SOURCE is:" + eerror + eerror " ${EPATCH_SOURCE}" + eerror " ( ${EPATCH_SOURCE##*/} )" + echo + die "Cannot find \$EPATCH_SOURCE!" fi + local PIPE_CMD case ${EPATCH_SUFFIX##*\.} in - bz2) - PIPE_CMD="bzip2 -dc" - PATCH_SUFFIX="bz2" - ;; - gz|Z|z) - PIPE_CMD="gzip -dc" - PATCH_SUFFIX="gz" - ;; - ZIP|zip) - PIPE_CMD="unzip -p" - PATCH_SUFFIX="zip" - ;; - *) - PIPE_CMD="cat" - PATCH_SUFFIX="patch" - ;; + xz) PIPE_CMD="xz -dc" ;; + lzma) PIPE_CMD="lzma -dc" ;; + bz2) PIPE_CMD="bzip2 -dc" ;; + gz|Z|z) PIPE_CMD="gzip -dc" ;; + ZIP|zip) PIPE_CMD="unzip -p" ;; + *) ;; esac - if [ "${SINGLE_PATCH}" = "no" ] - then - einfo "${EPATCH_MULTI_MSG}" - fi - for x in ${EPATCH_SOURCE} - do - # New ARCH dependant patch naming scheme ... - # - # ???_arch_foo.patch - # - if [ -f ${x} ] && \ - ([ "${SINGLE_PATCH}" = "yes" -o "${x/_all_}" != "${x}" -o "${x/_${ARCH}_}" != "${x}" ] || \ - [ "${EPATCH_FORCE}" = "yes" ]) + [[ ${SINGLE_PATCH} == "no" ]] && einfo "${EPATCH_MULTI_MSG}" + + local x + for x in "$@" ; do + # If the patch dir given contains subdirs, or our EPATCH_SUFFIX + # didn't match anything, ignore continue on + [[ ! -f ${x} ]] && continue + + local patchname=${x##*/} + + # Apply single patches, or forced sets of patches, or + # patches with ARCH dependant names. + # ???_arch_foo.patch + # Else, skip this input altogether + local a=${patchname#*_} # strip the ???_ + a=${a%%_*} # strip the _foo.patch + if ! [[ ${SINGLE_PATCH} == "yes" || \ + ${EPATCH_FORCE} == "yes" || \ + ${a} == all || \ + ${a} == ${ARCH} ]] then - local count=0 - local popts="${EPATCH_OPTS}" - local patchname=${x##*/} + continue + fi - if [ -n "${EPATCH_EXCLUDE}" ] - then - if [ "${EPATCH_EXCLUDE/${patchname}}" != "${EPATCH_EXCLUDE}" ] - then - continue + # Let people filter things dynamically + if [[ -n ${EPATCH_EXCLUDE} ]] ; then + # let people use globs in the exclude + eshopts_push -o noglob + + local ex + for ex in ${EPATCH_EXCLUDE} ; do + if [[ ${patchname} == ${ex} ]] ; then + eshopts_pop + continue 2 fi - fi + done - if [ "${SINGLE_PATCH}" = "yes" ] - then - if [ -n "${EPATCH_SINGLE_MSG}" ] - then - einfo "${EPATCH_SINGLE_MSG}" - else - einfo "Applying ${patchname} ..." - fi + eshopts_pop + fi + + if [[ ${SINGLE_PATCH} == "yes" ]] ; then + if [[ -n ${EPATCH_SINGLE_MSG} ]] ; then + einfo "${EPATCH_SINGLE_MSG}" else - einfo " ${patchname} ..." + einfo "Applying ${patchname} ..." fi + else + einfo " ${patchname} ..." + fi - echo "***** ${patchname} *****" > ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} + # most of the time, there will only be one run per unique name, + # but if there are more, make sure we get unique log filenames + local STDERR_TARGET="${T}/${patchname}.out" + if [[ -e ${STDERR_TARGET} ]] ; then + STDERR_TARGET="${T}/${patchname}-$$.out" + fi - # Allow for prefix to differ ... im lazy, so shoot me :/ - while [ "${count}" -lt 5 ] - do - # Generate some useful debug info ... - _epatch_draw_line "***** ${patchname} *****" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - - if [ "${PATCH_SUFFIX}" != "patch" ] - then - echo -n "PIPE_COMMAND: " >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo "${PIPE_CMD} ${x} > ${PATCH_TARGET}" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - else - PATCH_TARGET="${x}" - fi + printf "***** %s *****\n\n" "${patchname}" > "${STDERR_TARGET}" - echo -n "PATCH COMMAND: " >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo "patch -p${count} ${popts} < ${PATCH_TARGET}" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} + # Decompress the patch if need be + local count=0 + local PATCH_TARGET + if [[ -n ${PIPE_CMD} ]] ; then + PATCH_TARGET="${T}/$$.patch" + echo "PIPE_COMMAND: ${PIPE_CMD} ${x} > ${PATCH_TARGET}" >> "${STDERR_TARGET}" - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - _epatch_draw_line "***** ${patchname} *****" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} + if ! (${PIPE_CMD} "${x}" > "${PATCH_TARGET}") >> "${STDERR_TARGET}" 2>&1 ; then + echo + eerror "Could not extract patch!" + #die "Could not extract patch!" + count=5 + break + fi + else + PATCH_TARGET=${x} + fi - if [ "${PATCH_SUFFIX}" != "patch" ] - then - if ! (${PIPE_CMD} ${x} > ${PATCH_TARGET}) >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} 2>&1 - then - echo - eerror "Could not extract patch!" - #die "Could not extract patch!" - count=5 - break - fi + # Check for absolute paths in patches. If sandbox is disabled, + # people could (accidently) patch files in the root filesystem. + # Or trigger other unpleasantries #237667. So disallow -p0 on + # such patches. + local abs_paths=$(egrep -n '^[-+]{3} /' "${PATCH_TARGET}" | awk '$2 != "/dev/null" { print }') + if [[ -n ${abs_paths} ]] ; then + count=1 + printf "NOTE: skipping -p0 due to absolute paths in patch:\n%s\n" "${abs_paths}" >> "${STDERR_TARGET}" + fi + # Similar reason, but with relative paths. + local rel_paths=$(egrep -n '^[-+]{3} [^ ]*[.][.]/' "${PATCH_TARGET}") + if [[ -n ${rel_paths} ]] ; then + eqawarn "QA Notice: Your patch uses relative paths '../'." + eqawarn " In the future this will cause a failure." + eqawarn "${rel_paths}" + fi + + # Dynamically detect the correct -p# ... i'm lazy, so shoot me :/ + while [[ ${count} -lt 5 ]] ; do + # Generate some useful debug info ... + ( + _epatch_draw_line "***** ${patchname} *****" + echo + echo "PATCH COMMAND: patch -p${count} ${EPATCH_OPTS} < '${PATCH_TARGET}'" + echo + _epatch_draw_line "***** ${patchname} *****" + patch -p${count} ${EPATCH_OPTS} --dry-run -f < "${PATCH_TARGET}" 2>&1 + ret=$? + echo + echo "patch program exited with status ${ret}" + exit ${ret} + ) >> "${STDERR_TARGET}" + + if [ $? -eq 0 ] ; then + ( + _epatch_draw_line "***** ${patchname} *****" + echo + echo "ACTUALLY APPLYING ${patchname} ..." + echo + _epatch_draw_line "***** ${patchname} *****" + patch -p${count} ${EPATCH_OPTS} < "${PATCH_TARGET}" 2>&1 + ret=$? + echo + echo "patch program exited with status ${ret}" + exit ${ret} + ) >> "${STDERR_TARGET}" + + if [ $? -ne 0 ] ; then + echo + eerror "A dry-run of patch command succeeded, but actually" + eerror "applying the patch failed!" + #die "Real world sux compared to the dreamworld!" + count=5 fi + break + fi - if (cat ${PATCH_TARGET} | patch -p${count} ${popts} --dry-run -f ; _epatch_assert) >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} 2>&1 - then - _epatch_draw_line "***** ${patchname} *****" > ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - echo "ACTUALLY APPLYING ${patchname} ..." >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - echo >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - _epatch_draw_line "***** ${patchname} *****" >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - - cat ${PATCH_TARGET} | patch -p${count} ${popts} >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real 2>&1 - _epatch_assert - - if [ "$?" -ne 0 ] - then - cat ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real >> ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} - echo - eerror "A dry-run of patch command succeeded, but actually" - eerror "applying the patch failed!" - #die "Real world sux compared to the dreamworld!" - count=5 - fi - - rm -f ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}.real - - break - fi + : $(( count++ )) + done - count=$((count + 1)) - done + # if we had to decompress the patch, delete the temp one + if [[ -n ${PIPE_CMD} ]] ; then + rm -f "${PATCH_TARGET}" + fi - if [ "${PATCH_SUFFIX}" != "patch" ] - then - rm -f ${PATCH_TARGET} - fi + if [[ ${count} -ge 5 ]] ; then + echo + eerror "Failed Patch: ${patchname} !" + eerror " ( ${PATCH_TARGET} )" + eerror + eerror "Include in your bugreport the contents of:" + eerror + eerror " ${STDERR_TARGET}" + echo + die "Failed Patch: ${patchname}!" + fi - if [ "${count}" -eq 5 ] - then - echo - eerror "Failed Patch: ${patchname} !" - eerror " ( ${PATCH_TARGET} )" - eerror - eerror "Include in your bugreport the contents of:" - eerror - eerror " ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/}" - echo - die "Failed Patch: ${patchname}!" - fi + # if everything worked, delete the patch log + rm -f "${STDERR_TARGET}" + eend 0 + done - rm -f ${STDERR_TARGET%/*}/${patchname}-${STDERR_TARGET##*/} + [[ ${SINGLE_PATCH} == "no" ]] && einfo "Done with patching" + : # everything worked +} - eend 0 +# @FUNCTION: epatch_user +# @USAGE: +# @DESCRIPTION: +# Applies user-provided patches to the source tree. The patches are +# taken from /etc/portage/patches///, where the first +# of these three directories to exist will be the one to use, ignoring +# any more general directories which might exist as well. +# +# User patches are intended for quick testing of patches without ebuild +# modifications, as well as for permanent customizations a user might +# desire. Obviously, there can be no official support for arbitrarily +# patched ebuilds. So whenever a build log in a bug report mentions that +# user patches were applied, the user should be asked to reproduce the +# problem without these. +# +# Not all ebuilds do call this function, so placing patches in the +# stated directory might or might not work, depending on the package and +# the eclasses it inherits and uses. It is safe to call the function +# repeatedly, so it is always possible to add a call at the ebuild +# level. The first call is the time when the patches will be +# applied. +# +# Ideally, this function should be called after gentoo-specific patches +# have been applied, so that their code can be modified as well, but +# before calls to e.g. eautoreconf, as the user patches might affect +# autotool input files as well. +epatch_user() { + [[ $# -ne 0 ]] && die "epatch_user takes no options" + + # Allow multiple calls to this function; ignore all but the first + local applied="${T}/epatch_user.applied" + [[ -e ${applied} ]] && return 2 + + # don't clobber any EPATCH vars that the parent might want + local EPATCH_SOURCE check base=${PORTAGE_CONFIGROOT%/}/etc/portage/patches + for check in {${CATEGORY}/${PF},${CATEGORY}/${P},${CATEGORY}/${PN}}; do + EPATCH_SOURCE=${base}/${CTARGET}/${check} + [[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${base}/${CHOST}/${check} + [[ -r ${EPATCH_SOURCE} ]] || EPATCH_SOURCE=${base}/${check} + if [[ -d ${EPATCH_SOURCE} ]] ; then + EPATCH_SOURCE=${EPATCH_SOURCE} \ + EPATCH_SUFFIX="patch" \ + EPATCH_FORCE="yes" \ + EPATCH_MULTI_MSG="Applying user patches from ${EPATCH_SOURCE} ..." \ + epatch + echo "${EPATCH_SOURCE}" > "${applied}" + return 0 fi done - if [ "${SINGLE_PATCH}" = "no" ] - then - einfo "Done with patching" - fi + echo "none" > "${applied}" + return 1 } +# @FUNCTION: emktemp +# @USAGE: [temp dir] +# @DESCRIPTION: # Cheap replacement for when debianutils (and thus mktemp) -# does not exist on the users system -# vapier@gentoo.org -# -# Takes just 1 optional parameter (the directory to create tmpfile in) +# does not exist on the users system. emktemp() { local exe="touch" [[ $1 == -d ]] && exe="mkdir" && shift @@ -382,7 +512,8 @@ || topdir=${T} fi - if [[ -z $(type -p mktemp) ]] ; then + if ! type -P mktemp > /dev/null ; then + # system lacks `mktemp` so we have to fake it local tmp=/ while [[ -e ${tmp} ]] ; do tmp=${topdir}/tmp.${RANDOM}.${RANDOM}.${RANDOM} @@ -390,36 +521,54 @@ ${exe} "${tmp}" || ${exe} -p "${tmp}" echo "${tmp}" else + # the args here will give slightly wierd names on BSD, + # but should produce a usable file on all userlands if [[ ${exe} == "touch" ]] ; then - [[ ${USERLAND} == "GNU" ]] \ - && mktemp -p "${topdir}" \ - || TMPDIR="${topdir}" mktemp -t tmp + TMPDIR="${topdir}" mktemp -t tmp.XXXXXXXXXX else - [[ ${USERLAND} == "GNU" ]] \ - && mktemp -d "${topdir}" \ - || TMPDIR="${topdir}" mktemp -dt tmp + TMPDIR="${topdir}" mktemp -dt tmp.XXXXXXXXXX fi fi } -# Small wrapper for getent (Linux), nidump (Mac OS X), +# @FUNCTION: egetent +# @USAGE: +# @MAINTAINER: +# base-system@gentoo.org (Linux) +# Joe Jezak (OS X) +# usata@gentoo.org (OS X) +# Aaron Walker (FreeBSD) +# @DESCRIPTION: +# Small wrapper for getent (Linux), +# nidump (< Mac OS X 10.5), dscl (Mac OS X 10.5), # and pw (FreeBSD) used in enewuser()/enewgroup() -# Joe Jezak and usata@gentoo.org -# FBSD stuff: Aaron Walker -# -# egetent(database, key) egetent() { case ${CHOST} in - *-darwin*) + *-darwin[678]) case "$2" in *[!0-9]*) # Non numeric - nidump $1 . | awk -F":" "{ if (\$1 ~ /^$2$/) {print \$0;exit;} }" + nidump $1 . | awk -F":" "{ if (\$1 ~ /^$2\$/) {print \$0;exit;} }" ;; *) # Numeric nidump $1 . | awk -F":" "{ if (\$3 == $2) {print \$0;exit;} }" ;; esac ;; + *-darwin*) + local mytype=$1 + [[ "passwd" == $mytype ]] && mytype="Users" + [[ "group" == $mytype ]] && mytype="Groups" + case "$2" in + *[!0-9]*) # Non numeric + dscl . -read /$mytype/$2 2>/dev/null |grep RecordName + ;; + *) # Numeric + local mykey="UniqueID" + [[ $mytype == "Groups" ]] && mykey="PrimaryGroupID" + dscl . -search /$mytype $mykey $2 2>/dev/null + ;; + esac + ;; *-freebsd*|*-dragonfly*) local opts action="user" [[ $1 == "passwd" ]] || action="group" @@ -441,20 +590,22 @@ esac } -# Simplify/standardize adding users to the system -# vapier@gentoo.org -# -# enewuser(username, uid, shell, homedir, groups, extra options) -# -# Default values if you do not specify any: -# username: REQUIRED ! -# uid: next available (see useradd(8)) -# note: pass -1 to get default behavior -# shell: /bin/false -# homedir: /dev/null -# groups: none -# extra: comment of 'added by portage for ${PN}' +# @FUNCTION: enewuser +# @USAGE: [uid] [shell] [homedir] [groups] [params] +# @DESCRIPTION: +# Same as enewgroup, you are not required to understand how to properly add +# a user to the system. The only required parameter is the username. +# Default uid is (pass -1 for this) next available, default shell is +# /bin/false, default homedir is /dev/null, there are no default groups, +# and default params sets the comment as 'added by portage for ${PN}'. enewuser() { + case ${EBUILD_PHASE} in + unpack|compile|test|install) + eerror "'enewuser()' called from '${EBUILD_PHASE}()' which is not a pkg_* function." + eerror "Package fails at QA and at life. Please file a bug." + die "Bad package! enewuser is only for use in pkg_* functions!" + esac + # get the username local euser=$1; shift if [[ -z ${euser} ]] ; then @@ -463,7 +614,7 @@ fi # lets see if the username already exists - if [[ ${euser} == $(egetent passwd "${euser}" | cut -d: -f1) ]] ; then + if [[ -n $(egetent passwd "${euser}") ]] ; then return 0 fi einfo "Adding user '${euser}' to your system ..." @@ -473,9 +624,9 @@ # handle uid local euid=$1; shift - if [[ ! -z ${euid} ]] && [[ ${euid} != "-1" ]] ; then + if [[ -n ${euid} && ${euid} != -1 ]] ; then if [[ ${euid} -gt 0 ]] ; then - if [[ ! -z $(egetent passwd ${euid}) ]] ; then + if [[ -n $(egetent passwd ${euid}) ]] ; then euid="next" fi else @@ -486,7 +637,7 @@ euid="next" fi if [[ ${euid} == "next" ]] ; then - for euid in $(seq 101 999) ; do + for ((euid = 101; euid <= 999; euid++)); do [[ -z $(egetent passwd ${euid}) ]] && break done fi @@ -510,8 +661,13 @@ done if [[ ${shell} == "/dev/null" ]] ; then - eerror "Unable to identify the shell to use" - die "Unable to identify the shell to use" + eerror "Unable to identify the shell to use, proceeding with userland default." + case ${USERLAND} in + GNU) shell="/bin/false" ;; + BSD) shell="/sbin/nologin" ;; + Darwin) shell="/usr/sbin/nologin" ;; + *) die "Unable to identify the default shell for userland ${USERLAND}" + esac fi eshell=${shell} @@ -619,12 +775,14 @@ *) if [[ -z $@ ]] ; then - useradd ${opts} ${euser} \ + useradd -r ${opts} \ -c "added by portage for ${PN}" \ + ${euser} \ || die "enewuser failed" else einfo " - Extra: $@" - useradd ${opts} ${euser} "$@" \ + useradd -r ${opts} "$@" \ + ${euser} \ || die "enewuser failed" fi ;; @@ -640,16 +798,21 @@ export SANDBOX_ON=${oldsandbox} } -# Simplify/standardize adding groups to the system -# vapier@gentoo.org -# -# enewgroup(group, gid) -# -# Default values if you do not specify any: -# groupname: REQUIRED ! -# gid: next available (see groupadd(8)) -# extra: none +# @FUNCTION: enewgroup +# @USAGE: [gid] +# @DESCRIPTION: +# This function does not require you to understand how to properly add a +# group to the system. Just give it a group name to add and enewgroup will +# do the rest. You may specify the gid for the group or allow the group to +# allocate the next available one. enewgroup() { + case ${EBUILD_PHASE} in + unpack|compile|test|install) + eerror "'enewgroup()' called from '${EBUILD_PHASE}()' which is not a pkg_* function." + eerror "Package fails at QA and at life. Please file a bug." + die "Bad package! enewgroup is only for use in pkg_* functions!" + esac + # get the group local egroup="$1"; shift if [ -z "${egroup}" ] @@ -659,8 +822,7 @@ fi # see if group already exists - if [ "${egroup}" == "`egetent group \"${egroup}\" | cut -d: -f1`" ] - then + if [[ -n $(egetent group "${egroup}") ]]; then return 0 fi einfo "Adding group '${egroup}' to your system ..." @@ -713,8 +875,8 @@ # If we need the next available case ${egid} in *[!0-9]*) # Non numeric - for egid in $(seq 101 999); do - [ -z "`egetent group ${egid}`" ] && break + for ((egid = 101; egid <= 999; egid++)); do + [[ -z $(egetent group ${egid}) ]] && break done esac dscl . create /groups/${egroup} gid ${egid} @@ -724,8 +886,8 @@ *-freebsd*|*-dragonfly*) case ${egid} in *[!0-9]*) # Non numeric - for egid in $(seq 101 999); do - [ -z "`egetent group ${egid}`" ] && break + for ((egid = 101; egid <= 999; egid++)); do + [[ -z $(egetent group ${egid}) ]] && break done esac pw groupadd ${egroup} -g ${egid} || die "enewgroup failed" @@ -734,61 +896,54 @@ *-netbsd*) case ${egid} in *[!0-9]*) # Non numeric - for egid in $(seq 101 999); do - [ -z "`egetent group ${egid}`" ] && break + for ((egid = 101; egid <= 999; egid++)); do + [[ -z $(egetent group ${egid}) ]] && break done esac groupadd -g ${egid} ${egroup} || die "enewgroup failed" ;; *) - groupadd ${opts} ${egroup} || die "enewgroup failed" + # We specify -r so that we get a GID in the system range from login.defs + groupadd -r ${opts} ${egroup} || die "enewgroup failed" ;; esac export SANDBOX_ON="${oldsandbox}" } -# Simple script to replace 'dos2unix' binaries -# vapier@gentoo.org -# -# edos2unix(file, ...) +# @FUNCTION: edos2unix +# @USAGE: [more files ...] +# @DESCRIPTION: +# A handy replacement for dos2unix, recode, fixdos, etc... This allows you +# to remove all of these text utilities from DEPEND variables because this +# is a script based solution. Just give it a list of files to convert and +# they will all be changed from the DOS CRLF format to the UNIX LF format. edos2unix() { - for f in "$@" - do - cp "${f}" ${T}/edos2unix - sed 's/\r$//' ${T}/edos2unix > "${f}" - done + echo "$@" | xargs sed -i 's/\r$//' } - -############################################################## -# START: Handle .desktop files and menu entries # -# maybe this should be separated into a new eclass some time # -# lanius@gentoo.org # -############################################################## - # Make a desktop file ! # Great for making those icons in kde/gnome startmenu ! -# Amaze your friends ! Get the women ! Join today ! +# Amaze your friends ! Get the women ! Join today ! # -# make_desktop_entry(, [name], [icon], [type], [path]) +# make_desktop_entry(, [name], [icon], [type], [fields]) # -# binary: what binary does the app run with ? +# binary: what command does the app run with ? # name: the name that will show up in the menu # icon: give your little like a pretty little icon ... # this can be relative (to /usr/share/pixmaps) or # a full path to an icon -# type: what kind of application is this ? for categories: -# http://www.freedesktop.org/Standards/desktop-entry-spec -# path: if your app needs to startup in a specific dir +# type: what kind of application is this ? for categories: +# http://standards.freedesktop.org/menu-spec/latest/apa.html +# fields: extra fields to append to the desktop file; a printf string make_desktop_entry() { - [[ -z $1 ]] && eerror "make_desktop_entry: You must specify the executable" && return 1 + [[ -z $1 ]] && die "make_desktop_entry: You must specify the executable" local exec=${1} local name=${2:-${PN}} - local icon=${3:-${PN}.png} + local icon=${3:-${PN}} local type=${4} - local path=${5} + local fields=${5} if [[ -z ${type} ]] ; then local catmaj=${CATEGORY%%-*} @@ -796,17 +951,22 @@ case ${catmaj} in app) case ${catmin} in - admin) type=System;; - cdr) type=DiscBurning;; - dicts) type=Dictionary;; - editors) type=TextEditor;; - emacs) type=TextEditor;; - emulation) type=Emulator;; - laptop) type=HardwareSettings;; - office) type=Office;; - vim) type=TextEditor;; - xemacs) type=TextEditor;; - *) type=;; + accessibility) type=Accessibility;; + admin) type=System;; + antivirus) type=System;; + arch) type=Archiving;; + backup) type=Archiving;; + cdr) type=DiscBurning;; + dicts) type=Dictionary;; + doc) type=Documentation;; + editors) type=TextEditor;; + emacs) type=TextEditor;; + emulation) type=Emulator;; + laptop) type=HardwareSettings;; + office) type=Office;; + pda) type=PDA;; + vim) type=TextEditor;; + xemacs) type=TextEditor;; esac ;; @@ -816,36 +976,48 @@ games) case ${catmin} in - action) type=ActionGame;; + action|fps) type=ActionGame;; arcade) type=ArcadeGame;; board) type=BoardGame;; - kid) type=KidsGame;; emulation) type=Emulator;; + kids) type=KidsGame;; puzzle) type=LogicGame;; - rpg) type=RolePlaying;; roguelike) type=RolePlaying;; + rpg) type=RolePlaying;; simulation) type=Simulation;; sports) type=SportsGame;; strategy) type=StrategyGame;; - *) type=;; esac type="Game;${type}" ;; + gnome) + type="Gnome;GTK" + ;; + + kde) + type="KDE;Qt" + ;; + mail) type="Network;Email" ;; media) case ${catmin} in - gfx) type=Graphics;; - radio) type=Tuner;; - sound) type=Audio;; - tv) type=TV;; - video) type=Video;; - *) type=;; + gfx) + type=Graphics + ;; + *) + case ${catmin} in + radio) type=Tuner;; + sound) type=Audio;; + tv) type=TV;; + video) type=Video;; + esac + type="AudioVideo;${type}" + ;; esac - type="AudioVideo;${type}" ;; net) @@ -858,30 +1030,35 @@ news) type=News;; nntp) type=News;; p2p) type=FileTransfer;; - *) type=;; + voip) type=Telephony;; esac type="Network;${type}" ;; sci) case ${catmin} in - astro*) type=Astronomy;; - bio*) type=Biology;; - calc*) type=Calculator;; - chem*) type=Chemistry;; - geo*) type=Geology;; - math*) type=Math;; - *) type=;; + astro*) type=Astronomy;; + bio*) type=Biology;; + calc*) type=Calculator;; + chem*) type=Chemistry;; + elec*) type=Electronics;; + geo*) type=Geology;; + math*) type=Math;; + physics) type=Physics;; + visual*) type=DataVisualization;; esac - type="Science;${type}" + type="Education;Science;${type}" + ;; + + sys) + type="System" ;; www) case ${catmin} in client) type=WebBrowser;; - *) type=;; esac - type="Network" + type="Network;${type}" ;; *) @@ -894,94 +1071,189 @@ else local desktop_name="${PN}-${SLOT}" fi - local desktop=${T}/${exec%% *}-${desktop_name}.desktop + local desktop="${T}/$(echo ${exec} | sed 's:[[:space:]/:]:_:g')-${desktop_name}.desktop" + #local desktop=${T}/${exec%% *:-${desktop_name}}.desktop -echo "[Desktop Entry] -Encoding=UTF-8 -Version=0.9.2 -Name=${name} -Type=Application -Comment=${DESCRIPTION} -Exec=${exec} -Path=${path} -Icon=${icon} -Categories=Application;${type};" > "${desktop}" + # Don't append another ";" when a valid category value is provided. + type=${type%;}${type:+;} + + eshopts_push -s extglob + if [[ -n ${icon} && ${icon} != /* ]] && [[ ${icon} == *.xpm || ${icon} == *.png || ${icon} == *.svg ]]; then + ewarn "As described in the Icon Theme Specification, icon file extensions are not" + ewarn "allowed in .desktop files if the value is not an absolute path." + icon=${icon%.@(xpm|png|svg)} + fi + eshopts_pop + + cat <<-EOF > "${desktop}" + [Desktop Entry] + Name=${name} + Type=Application + Comment=${DESCRIPTION} + Exec=${exec} + TryExec=${exec%% *} + Icon=${icon} + Categories=${type} + EOF + + if [[ ${fields:-=} != *=* ]] ; then + # 5th arg used to be value to Path= + ewarn "make_desktop_entry: update your 5th arg to read Path=${fields}" + fields="Path=${fields}" + fi + [[ -n ${fields} ]] && printf '%b\n' "${fields}" >> "${desktop}" ( # wrap the env here so that the 'insinto' call # doesn't corrupt the env of the caller insinto /usr/share/applications doins "${desktop}" - ) + ) || die "installing desktop file failed" } -# Make a GDM/KDM Session file -# -# make_desktop_entry(, <command>) -# title: File to execute to start the Window Manager -# command: Name of the Window Manager +# @FUNCTION: validate_desktop_entries +# @USAGE: [directories] +# @MAINTAINER: +# Carsten Lohrke <carlo@gentoo.org> +# @DESCRIPTION: +# Validate desktop entries using desktop-file-utils +validate_desktop_entries() { + if [[ -x /usr/bin/desktop-file-validate ]] ; then + einfo "Checking desktop entry validity" + local directories="" + for d in /usr/share/applications $@ ; do + [[ -d ${D}${d} ]] && directories="${directories} ${D}${d}" + done + if [[ -n ${directories} ]] ; then + for FILE in $(find ${directories} -name "*\.desktop" \ + -not -path '*.hidden*' | sort -u 2>/dev/null) + do + local temp=$(desktop-file-validate ${FILE} | grep -v "warning:" | \ + sed -e "s|error: ||" -e "s|${FILE}:|--|g" ) + [[ -n $temp ]] && elog ${temp/--/${FILE/${D}/}:} + done + fi + echo "" + else + einfo "Passing desktop entry validity check. Install dev-util/desktop-file-utils, if you want to help to improve Gentoo." + fi +} +# @FUNCTION: make_session_desktop +# @USAGE: <title> <command> [command args...] +# @DESCRIPTION: +# Make a GDM/KDM Session file. The title is the file to execute to start the +# Window Manager. The command is the name of the Window Manager. +# +# You can set the name of the file via the ${wm} variable. make_session_desktop() { - [[ -z $1 ]] && eerror "make_session_desktop: You must specify the title" && return 1 - [[ -z $2 ]] && eerror "make_session_desktop: You must specify the command" && return 1 + [[ -z $1 ]] && eerror "$0: You must specify the title" && return 1 + [[ -z $2 ]] && eerror "$0: You must specify the command" && return 1 local title=$1 local command=$2 - local desktop=${T}/${wm}.desktop + local desktop=${T}/${wm:-${PN}}.desktop + shift 2 -echo "[Desktop Entry] -Encoding=UTF-8 -Name=${title} -Comment=This session logs you into ${title} -Exec=${command} -TryExec=${command} -Type=Application" > "${desktop}" + cat <<-EOF > "${desktop}" + [Desktop Entry] + Name=${title} + Comment=This session logs you into ${title} + Exec=${command} $* + TryExec=${command} + Type=XSession + EOF + ( + # wrap the env here so that the 'insinto' call + # doesn't corrupt the env of the caller insinto /usr/share/xsessions doins "${desktop}" + ) } +# @FUNCTION: domenu +# @USAGE: <menus> +# @DESCRIPTION: +# Install the list of .desktop menu files into the appropriate directory +# (/usr/share/applications). domenu() { - local i j + ( + # wrap the env here so that the 'insinto' call + # doesn't corrupt the env of the caller + local i j ret=0 insinto /usr/share/applications for i in "$@" ; do if [[ -f ${i} ]] ; then doins "${i}" + ((ret+=$?)) elif [[ -d ${i} ]] ; then for j in "${i}"/*.desktop ; do doins "${j}" + ((ret+=$?)) done + else + ((++ret)) fi done + exit ${ret} + ) } + +# @FUNCTION: newmenu +# @USAGE: <menu> <newname> +# @DESCRIPTION: +# Like all other new* functions, install the specified menu as newname. newmenu() { + ( + # wrap the env here so that the 'insinto' call + # doesn't corrupt the env of the caller insinto /usr/share/applications - newins "$1" "$2" + newins "$@" + ) } +# @FUNCTION: doicon +# @USAGE: <list of icons> +# @DESCRIPTION: +# Install the list of icons into the icon directory (/usr/share/pixmaps). +# This is useful in conjunction with creating desktop/menu files. doicon() { - local i j + ( + # wrap the env here so that the 'insinto' call + # doesn't corrupt the env of the caller + local i j ret insinto /usr/share/pixmaps for i in "$@" ; do if [[ -f ${i} ]] ; then doins "${i}" + ((ret+=$?)) elif [[ -d ${i} ]] ; then for j in "${i}"/*.png ; do doins "${j}" + ((ret+=$?)) done + else + ((++ret)) fi done + exit ${ret} + ) } + +# @FUNCTION: newicon +# @USAGE: <icon> <newname> +# @DESCRIPTION: +# Like all other new* functions, install the specified icon as newname. newicon() { + ( + # wrap the env here so that the 'insinto' call + # doesn't corrupt the env of the caller insinto /usr/share/pixmaps - newins "$1" "$2" + newins "$@" + ) } -############################################################## -# END: Handle .desktop files and menu entries # -############################################################## - - # for internal use only (unpack_pdv and unpack_makeself) find_unpackable_file() { local src=$1 @@ -1000,28 +1272,40 @@ echo "${src}" } +# @FUNCTION: unpack_pdv +# @USAGE: <file to unpack> <size of off_t> +# @DESCRIPTION: # Unpack those pesky pdv generated files ... # They're self-unpacking programs with the binary package stuffed in # the middle of the archive. Valve seems to use it a lot ... too bad # it seems to like to segfault a lot :(. So lets take it apart ourselves. # -# Usage: unpack_pdv [file to unpack] [size of off_t] -# - you have to specify the off_t size ... i have no idea how to extract that -# information out of the binary executable myself. basically you pass in -# the size of the off_t type (in bytes) on the machine that built the pdv -# archive. one way to determine this is by running the following commands: -# strings <pdv archive> | grep lseek -# strace -elseek <pdv archive> -# basically look for the first lseek command (we do the strings/grep because -# sometimes the function call is _llseek or something) and steal the 2nd -# parameter. here is an example: -# root@vapier 0 pdv_unpack # strings hldsupdatetool.bin | grep lseek -# lseek -# root@vapier 0 pdv_unpack # strace -elseek ./hldsupdatetool.bin -# lseek(3, -4, SEEK_END) = 2981250 -# thus we would pass in the value of '4' as the second parameter. +# You have to specify the off_t size ... I have no idea how to extract that +# information out of the binary executable myself. Basically you pass in +# the size of the off_t type (in bytes) on the machine that built the pdv +# archive. +# +# One way to determine this is by running the following commands: +# +# @CODE +# strings <pdv archive> | grep lseek +# strace -elseek <pdv archive> +# @CODE +# +# Basically look for the first lseek command (we do the strings/grep because +# sometimes the function call is _llseek or something) and steal the 2nd +# parameter. Here is an example: +# +# @CODE +# vapier@vapier 0 pdv_unpack # strings hldsupdatetool.bin | grep lseek +# lseek +# vapier@vapier 0 pdv_unpack # strace -elseek ./hldsupdatetool.bin +# lseek(3, -4, SEEK_END) = 2981250 +# @CODE +# +# Thus we would pass in the value of '4' as the second parameter. unpack_pdv() { - local src=$(find_unpackable_file $1) + local src=$(find_unpackable_file "$1") local sizeoff_t=$2 [[ -z ${src} ]] && die "Could not locate source for '$1'" @@ -1029,31 +1313,31 @@ local shrtsrc=$(basename "${src}") echo ">>> Unpacking ${shrtsrc} to ${PWD}" - local metaskip=`tail -c ${sizeoff_t} ${src} | hexdump -e \"%i\"` - local tailskip=`tail -c $((${sizeoff_t}*2)) ${src} | head -c ${sizeoff_t} | hexdump -e \"%i\"` + local metaskip=$(tail -c ${sizeoff_t} "${src}" | hexdump -e \"%i\") + local tailskip=$(tail -c $((${sizeoff_t}*2)) "${src}" | head -c ${sizeoff_t} | hexdump -e \"%i\") # grab metadata for debug reasons - local metafile="$(emktemp)" - tail -c +$((${metaskip}+1)) ${src} > ${metafile} + local metafile=$(emktemp) + tail -c +$((${metaskip}+1)) "${src}" > "${metafile}" # rip out the final file name from the metadata - local datafile="`tail -c +$((${metaskip}+1)) ${src} | strings | head -n 1`" - datafile="`basename ${datafile}`" + local datafile=$(tail -c +$((${metaskip}+1)) "${src}" | strings | head -n 1) + datafile=$(basename "${datafile}") # now lets uncompress/untar the file if need be - local tmpfile="$(emktemp)" + local tmpfile=$(emktemp) tail -c +$((${tailskip}+1)) ${src} 2>/dev/null | head -c 512 > ${tmpfile} - local iscompressed="`file -b ${tmpfile}`" - if [ "${iscompressed:0:8}" == "compress" ] ; then + local iscompressed=$(file -b "${tmpfile}") + if [[ ${iscompressed:0:8} == "compress" ]] ; then iscompressed=1 mv ${tmpfile}{,.Z} gunzip ${tmpfile} else iscompressed=0 fi - local istar="`file -b ${tmpfile}`" - if [ "${istar:0:9}" == "POSIX tar" ] ; then + local istar=$(file -b "${tmpfile}") + if [[ ${istar:0:9} == "POSIX tar" ]] ; then istar=1 else istar=0 @@ -1091,15 +1375,17 @@ #assert "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')" } +# @FUNCTION: unpack_makeself +# @USAGE: [file to unpack] [offset] [tail|dd] +# @DESCRIPTION: # Unpack those pesky makeself generated files ... # They're shell scripts with the binary package tagged onto # the end of the archive. Loki utilized the format as does # many other game companies. # -# Usage: unpack_makeself [file to unpack] [offset] [tail|dd] -# - If the file is not specified then unpack will utilize ${A}. -# - If the offset is not specified then we will attempt to extract -# the proper offset from the script itself. +# If the file is not specified, then ${A} is used. If the +# offset is not specified then we will attempt to extract +# the proper offset from the script itself. unpack_makeself() { local src_input=${1:-${A}} local src=$(find_unpackable_file "${src_input}") @@ -1111,11 +1397,11 @@ local shrtsrc=$(basename "${src}") echo ">>> Unpacking ${shrtsrc} to ${PWD}" if [[ -z ${skip} ]] ; then - local ver=$(grep -a '#.*Makeself' "${src}" | awk '{print $NF}') + local ver=$(grep -m1 -a '#.*Makeself' "${src}" | awk '{print $NF}') local skip=0 exe=tail case ${ver} in - 1.5.*) # tested 1.5.{3,4,5} ... guessing 1.5.x series is same + 1.5.*|1.6.0-nv) # tested 1.5.{3,4,5} ... guessing 1.5.x series is same skip=$(grep -a ^skip= "${src}" | cut -d= -f2) ;; 2.0|2.0.1) @@ -1123,15 +1409,15 @@ ;; 2.1.1) skip=$(grep -a ^offset= "${src}" | awk '{print $2}' | cut -b2-) - let skip="skip + 1" + (( skip++ )) ;; 2.1.2) skip=$(grep -a ^offset= "${src}" | awk '{print $3}' | head -n 1) - let skip="skip + 1" + (( skip++ )) ;; 2.1.3) skip=`grep -a ^offset= "${src}" | awk '{print $3}'` - let skip="skip + 1" + (( skip++ )) ;; 2.1.4|2.1.5) skip=$(grep -a offset=.*head.*wc "${src}" | awk '{print $3}' | head -n 1) @@ -1150,16 +1436,16 @@ fi case ${exe} in tail) exe="tail -n +${skip} '${src}'";; - dd) exe="dd ibs=${skip} skip=1 obs=1024 conv=sync if='${src}'";; + dd) exe="dd ibs=${skip} skip=1 if='${src}'";; *) die "makeself cant handle exe '${exe}'" esac # lets grab the first few bytes of the file to figure out what kind of archive it is - local tmpfile="$(emktemp)" + local filetype tmpfile=$(emktemp) eval ${exe} 2>/dev/null | head -c 512 > "${tmpfile}" - local filetype="$(file -b "${tmpfile}")" + filetype=$(file -b "${tmpfile}") || die case ${filetype} in - *tar\ archive) + *tar\ archive*) eval ${exe} | tar --no-same-owner -xf - ;; bzip2*) @@ -1179,10 +1465,11 @@ assert "failure unpacking (${filetype}) makeself ${shrtsrc} ('${ver}' +${skip})" } -# Display a license for user to accept. -# -# Usage: check_license [license] -# - If the file is not specified then ${LICENSE} is used. +# @FUNCTION: check_license +# @USAGE: [license] +# @DESCRIPTION: +# Display a license for user to accept. If no license is +# specified, then ${LICENSE} is used. check_license() { local lic=$1 if [ -z "${lic}" ] ; then @@ -1196,32 +1483,31 @@ lic="${lic}" fi fi - [ ! -f "${lic}" ] && die "Could not find requested license ${lic}" local l="`basename ${lic}`" # here is where we check for the licenses the user already # accepted ... if we don't find a match, we make the user accept - local shopts=$- local alic - set -o noglob #so that bash doesn't expand "*" + eshopts_push -o noglob # so that bash doesn't expand "*" for alic in ${ACCEPT_LICENSE} ; do - if [[ ${alic} == * || ${alic} == ${l} ]]; then - set +o noglob; set -${shopts} #reset old shell opts + if [[ ${alic} == ${l} ]]; then + eshopts_pop return 0 fi done - set +o noglob; set -$shopts #reset old shell opts + eshopts_pop + [ ! -f "${lic}" ] && die "Could not find requested license ${lic}" - local licmsg="$(emktemp)" - cat << EOF > ${licmsg} -********************************************************** -The following license outlines the terms of use of this -package. You MUST accept this license for installation to -continue. When you are done viewing, hit 'q'. If you -CTRL+C out of this, the install will not run! -********************************************************** + local licmsg=$(emktemp) + cat <<-EOF > ${licmsg} + ********************************************************** + The following license outlines the terms of use of this + package. You MUST accept this license for installation to + continue. When you are done viewing, hit 'q'. If you + CTRL+C out of this, the install will not run! + ********************************************************** -EOF + EOF cat ${lic} >> ${licmsg} ${PAGER:-less} ${licmsg} || die "Could not execute pager (${PAGER}) to accept ${lic}" einfon "Do you accept the terms of this license (${l})? [yes/no] " @@ -1238,24 +1524,28 @@ esac } +# @FUNCTION: cdrom_get_cds +# @USAGE: <file on cd1> [file on cd2] [file on cd3] [...] +# @DESCRIPTION: # Aquire cd(s) for those lovely cd-based emerges. Yes, this violates # the whole 'non-interactive' policy, but damnit I want CD support ! # -# with these cdrom functions we handle all the user interaction and -# standardize everything. all you have to do is call cdrom_get_cds() +# With these cdrom functions we handle all the user interaction and +# standardize everything. All you have to do is call cdrom_get_cds() # and when the function returns, you can assume that the cd has been # found at CDROM_ROOT. # -# normally the cdrom functions will refer to the cds as 'cd #1', 'cd #2', -# etc... if you want to give the cds better names, then just export -# the CDROM_NAME_X variables before calling cdrom_get_cds(). -# -# for those multi cd ebuilds, see the cdrom_load_next_cd() below. -# -# Usage: cdrom_get_cds <file on cd1> [file on cd2] [file on cd3] [...] -# - this will attempt to locate a cd based upon a file that is on -# the cd ... the more files you give this function, the more cds -# the cdrom functions will handle +# The function will attempt to locate a cd based upon a file that is on +# the cd. The more files you give this function, the more cds +# the cdrom functions will handle. +# +# Normally the cdrom functions will refer to the cds as 'cd #1', 'cd #2', +# etc... If you want to give the cds better names, then just export +# the appropriate CDROM_NAME variable before calling cdrom_get_cds(). +# Use CDROM_NAME for one cd, or CDROM_NAME_# for multiple cds. You can +# also use the CDROM_NAME_SET bash array. +# +# For those multi cd ebuilds, see the cdrom_load_next_cd() function. cdrom_get_cds() { # first we figure out how many cds we're dealing with by # the # of files they gave us @@ -1289,7 +1579,7 @@ export CDROM_SET=-1 for f in ${CDROM_CHECK_1//:/ } ; do ((++CDROM_SET)) - [[ -e ${CD_ROOT}/${f} ]] && break + [[ -e ${CDROM_ROOT}/${f} ]] && break done export CDROM_MATCH=${f} return @@ -1309,6 +1599,15 @@ einfo "export CD_ROOT=/mnt/cdrom" echo else + if [[ -n ${CDROM_NAME_SET} ]] ; then + # Translate the CDROM_NAME_SET array into CDROM_NAME_# + cdcnt=0 + while [[ ${cdcnt} -lt ${CDROM_TOTAL_CDS} ]] ; do + ((++cdcnt)) + export CDROM_NAME_${cdcnt}="${CDROM_NAME_SET[$((${cdcnt}-1))]}" + done + fi + einfo "This package will need access to ${CDROM_TOTAL_CDS} cds." cdcnt=0 while [[ ${cdcnt} -lt ${CDROM_TOTAL_CDS} ]] ; do @@ -1342,10 +1641,14 @@ cdrom_load_next_cd } -# this is only used when you need access to more than one cd. -# when you have finished using the first cd, just call this function. -# when it returns, CDROM_ROOT will be pointing to the second cd. -# remember, you can only go forward in the cd chain, you can't go back. +# @FUNCTION: cdrom_load_next_cd +# @DESCRIPTION: +# Some packages are so big they come on multiple CDs. When you're done reading +# files off a CD and want access to the next one, just call this function. +# Again, all the messy details of user interaction are taken care of for you. +# Once this returns, just read the variable CDROM_ROOT for the location of the +# mounted CD. Note that you can only go forward in the CD list, so make sure +# you only call this function when you're done using the current CD. cdrom_load_next_cd() { local var ((++CDROM_CURRENT_CD)) @@ -1372,7 +1675,7 @@ # (2) the user hits CTRL+C _cdrom_locate_file_on_cd() { local mline="" - local showedmsg=0 + local showedmsg=0 showjolietmsg=0 while [[ -z ${CDROM_ROOT} ]] ; do local i=0 @@ -1385,15 +1688,19 @@ local dir=$(dirname ${cdset[${i}]}) local file=$(basename ${cdset[${i}]}) - for mline in $(mount | gawk '/(iso|cdrom|fs=cdfss)/ {print $3}') ; do - [[ -d ${mline}/${dir} ]] || continue - if [[ -n $(find ${mline}/${dir} -maxdepth 1 -iname ${file}) ]] ; then - export CDROM_ROOT=${mline} - export CDROM_SET=${i} - export CDROM_MATCH=${cdset[${i}]} - return - fi - done + local point= node= fs= foo= + while read point node fs foo ; do + [[ " cd9660 iso9660 udf " != *" ${fs} "* ]] && \ + ! [[ ${fs} == "subfs" && ",${opts}," == *",fs=cdfss,"* ]] \ + && continue + point=${point//\040/ } + [[ ! -d ${point}/${dir} ]] && continue + [[ -z $(find "${point}/${dir}" -maxdepth 1 -iname "${file}") ]] && continue + export CDROM_ROOT=${point} + export CDROM_SET=${i} + export CDROM_MATCH=${cdset[${i}]} + return + done <<< "$(get_mounts)" ((++i)) done @@ -1419,31 +1726,34 @@ einfo "Press return to scan for the cd again" einfo "or hit CTRL+C to abort the emerge." echo - einfo "If you are having trouble with the detection" - einfo "of your CD, it is possible that you do not have" - einfo "Joliet support enabled in your kernel. Please" - einfo "check that CONFIG_JOLIET is enabled in your kernel." + if [[ ${showjolietmsg} -eq 0 ]] ; then + showjolietmsg=1 + else + ewarn "If you are having trouble with the detection" + ewarn "of your CD, it is possible that you do not have" + ewarn "Joliet support enabled in your kernel. Please" + ewarn "check that CONFIG_JOLIET is enabled in your kernel." + ebeep 5 + fi read || die "something is screwed with your system" done } +# @FUNCTION: strip-linguas +# @USAGE: [<allow LINGUAS>|<-i|-u> <directories of .po files>] +# @DESCRIPTION: # Make sure that LINGUAS only contains languages that -# a package can support -# -# usage: strip-linguas <allow LINGUAS> -# strip-linguas -i <directories of .po files> -# strip-linguas -u <directories of .po files> -# -# The first form allows you to specify a list of LINGUAS. -# The -i builds a list of po files found in all the -# directories and uses the intersection of the lists. -# The -u builds a list of po files found in all the -# directories and uses the union of the lists. +# a package can support. The first form allows you to +# specify a list of LINGUAS. The -i builds a list of po +# files found in all the directories and uses the +# intersection of the lists. The -u builds a list of po +# files found in all the directories and uses the union +# of the lists. strip-linguas() { - local ls newls + local ls newls nols if [[ $1 == "-i" ]] || [[ $1 == "-u" ]] ; then local op=$1; shift - ls=" $(find "$1" -name '*.po' -exec basename {} \;) "; shift + ls=$(find "$1" -name '*.po' -exec basename {} .po ';'); shift local d f for d in "$@" ; do if [[ ${op} == "-u" ]] ; then @@ -1451,141 +1761,179 @@ else newls="" fi - for f in $(find "$d" -name '*.po' -exec basename {} \;) ; do + for f in $(find "$d" -name '*.po' -exec basename {} .po ';') ; do if [[ ${op} == "-i" ]] ; then - [[ ${ls/ ${f} /} != ${ls} ]] && newls="${newls} ${f}" + has ${f} ${ls} && newls="${newls} ${f}" else - [[ ${ls/ ${f} /} == ${ls} ]] && newls="${newls} ${f}" + has ${f} ${ls} || newls="${newls} ${f}" fi done ls=${newls} done - ls=${ls//.po} else - ls=$@ + ls="$@" fi - ls=" ${ls} " + nols="" newls="" for f in ${LINGUAS} ; do - if [[ ${ls/ ${f} /} != ${ls} ]] ; then + if has ${f} ${ls} ; then newls="${newls} ${f}" else - ewarn "Sorry, but ${PN} does not support the ${f} LINGUA" + nols="${nols} ${f}" fi done - if [[ -z ${newls} ]] ; then - export LINGUAS="" - else - export LINGUAS=${newls:1} - fi -} - -# moved from kernel.eclass since they are generally useful outside of -# kernel.eclass -iggy (20041002) - -# the following functions are useful in kernel module ebuilds, etc. -# for an example see ivtv or drbd ebuilds - -# set's ARCH to match what the kernel expects -set_arch_to_kernel() { - i=10 - while ((i--)) ; do - ewarn "PLEASE UPDATE TO YOUR PACKAGE TO USE linux-info.eclass" - done - export EUTILS_ECLASS_PORTAGE_ARCH="${ARCH}" - case ${ARCH} in - x86) export ARCH="i386";; - amd64) export ARCH="x86_64";; - hppa) export ARCH="parisc";; - mips) export ARCH="mips";; - sparc) export ARCH="$(tc-arch-kernel)";; # Yeah this is ugly, but it's even WORSE if you don't do this. linux-info.eclass's set_arch_to_kernel is fixed, but won't get used over this one! - *) export ARCH="${ARCH}";; - esac + [[ -n ${nols} ]] \ + && ewarn "Sorry, but ${PN} does not support the LINGUAS:" ${nols} + export LINGUAS=${newls:1} } -# set's ARCH back to what portage expects -set_arch_to_portage() { - i=10 - while ((i--)) ; do - ewarn "PLEASE UPDATE TO YOUR PACKAGE TO USE linux-info.eclass" - done - export ARCH="${EUTILS_ECLASS_PORTAGE_ARCH}" -} - -# Jeremy Huddleston <eradicator@gentoo.org>: -# preserve_old_lib /path/to/libblah.so.0 -# preserve_old_lib_notify /path/to/libblah.so.0 -# -# These functions are useful when a lib in your package changes --library. Such -# an example might be from libogg.so.0 to libogg.so.1. Removing libogg.so.0 +# @FUNCTION: preserve_old_lib +# @USAGE: <libs to preserve> [more libs] +# @DESCRIPTION: +# These functions are useful when a lib in your package changes ABI SONAME. +# An example might be from libogg.so.0 to libogg.so.1. Removing libogg.so.0 # would break packages that link against it. Most people get around this # by using the portage SLOT mechanism, but that is not always a relevant -# solution, so instead you can add the following to your ebuilds: -# -# src_install() { -# ... -# preserve_old_lib /usr/$(get_libdir)/libogg.so.0 -# ... -# } -# -# pkg_postinst() { -# ... -# preserve_old_lib_notify /usr/$(get_libdir)/libogg.so.0 -# ... -# } - +# solution, so instead you can call this from pkg_preinst. See also the +# preserve_old_lib_notify function. preserve_old_lib() { - LIB=$1 - - if [ -n "${LIB}" -a -f "${ROOT}${LIB}" ]; then - SONAME=`basename ${LIB}` - DIRNAME=`dirname ${LIB}` - - dodir ${DIRNAME} - cp ${ROOT}${LIB} ${D}${DIRNAME} - touch ${D}${LIB} - fi + if [[ ${EBUILD_PHASE} != "preinst" ]] ; then + eerror "preserve_old_lib() must be called from pkg_preinst() only" + die "Invalid preserve_old_lib() usage" + fi + [[ -z $1 ]] && die "Usage: preserve_old_lib <library to preserve> [more libraries to preserve]" + + # let portage worry about it + has preserve-libs ${FEATURES} && return 0 + + local lib dir + for lib in "$@" ; do + [[ -e ${ROOT}/${lib} ]] || continue + dir=${lib%/*} + dodir ${dir} || die "dodir ${dir} failed" + cp "${ROOT}"/${lib} "${D}"/${lib} || die "cp ${lib} failed" + touch "${D}"/${lib} + done } +# @FUNCTION: preserve_old_lib_notify +# @USAGE: <libs to notify> [more libs] +# @DESCRIPTION: +# Spit helpful messages about the libraries preserved by preserve_old_lib. preserve_old_lib_notify() { - LIB=$1 - - if [ -n "${LIB}" -a -f "${ROOT}${LIB}" ]; then - SONAME=`basename ${LIB}` - - ewarn "An old version of an installed library was detected on your system." - ewarn "In order to avoid breaking packages that link against it, this older version" - ewarn "is not being removed. In order to make full use of this newer version," - ewarn "you will need to execute the following command:" - ewarn " revdep-rebuild --library ${SONAME}" + if [[ ${EBUILD_PHASE} != "postinst" ]] ; then + eerror "preserve_old_lib_notify() must be called from pkg_postinst() only" + die "Invalid preserve_old_lib_notify() usage" + fi + + # let portage worry about it + has preserve-libs ${FEATURES} && return 0 + + local lib notice=0 + for lib in "$@" ; do + [[ -e ${ROOT}/${lib} ]] || continue + if [[ ${notice} -eq 0 ]] ; then + notice=1 + ewarn "Old versions of installed libraries were detected on your system." + ewarn "In order to avoid breaking packages that depend on these old libs," + ewarn "the libraries are not being removed. You need to run revdep-rebuild" + ewarn "in order to remove these old dependencies. If you do not have this" + ewarn "helper program, simply emerge the 'gentoolkit' package." + ewarn + fi + # temp hack for #348634 #357225 + [[ ${PN} == "mpfr" ]] && lib=${lib##*/} + ewarn " # revdep-rebuild --library '${lib}'" + done + if [[ ${notice} -eq 1 ]] ; then ewarn - ewarn "After doing that, you can safely remove ${LIB}" - ewarn "Note: 'emerge gentoolkit' to get revdep-rebuild" + ewarn "Once you've finished running revdep-rebuild, it should be safe to" + ewarn "delete the old libraries. Here is a copy & paste for the lazy:" + for lib in "$@" ; do + ewarn " # rm '${lib}'" + done fi } -# Hack for people to figure out if a package was built with -# certain USE flags +# @FUNCTION: built_with_use +# @USAGE: [--hidden] [--missing <action>] [-a|-o] <DEPEND ATOM> <List of USE flags> +# @DESCRIPTION: +# +# Deprecated: Use EAPI 2 use deps in DEPEND|RDEPEND and with has_version calls. +# +# A temporary hack until portage properly supports DEPENDing on USE +# flags being enabled in packages. This will check to see if the specified +# DEPEND atom was built with the specified list of USE flags. The +# --missing option controls the behavior if called on a package that does +# not actually support the defined USE flags (aka listed in IUSE). +# The default is to abort (call die). The -a and -o flags control +# the requirements of the USE flags. They correspond to "and" and "or" +# logic. So the -a flag means all listed USE flags must be enabled +# while the -o flag means at least one of the listed IUSE flags must be +# enabled. The --hidden option is really for internal use only as it +# means the USE flag we're checking is hidden expanded, so it won't be found +# in IUSE like normal USE flags. # -# Usage: built_with_use [-a|-o] <DEPEND ATOM> <List of USE flags> -# ex: built_with_use xchat gtk2 -# -# Flags: -a all USE flags should be utilized -# -o at least one USE flag should be utilized -# Note: the default flag is '-a' +# Remember that this function isn't terribly intelligent so order of optional +# flags matter. built_with_use() { + local hidden="no" + if [[ $1 == "--hidden" ]] ; then + hidden="yes" + shift + fi + + local missing_action="die" + if [[ $1 == "--missing" ]] ; then + missing_action=$2 + shift ; shift + case ${missing_action} in + true|false|die) ;; + *) die "unknown action '${missing_action}'";; + esac + fi + local opt=$1 [[ ${opt:0:1} = "-" ]] && shift || opt="-a" local PKG=$(best_version $1) + [[ -z ${PKG} ]] && die "Unable to resolve $1 to an installed package" shift local USEFILE=${ROOT}/var/db/pkg/${PKG}/USE + local IUSEFILE=${ROOT}/var/db/pkg/${PKG}/IUSE - # if the USE file doesnt exist, assume the $PKG is either - # injected or package.provided - [[ ! -e ${USEFILE} ]] && return 0 + # if the IUSE file doesn't exist, the read will error out, we need to handle + # this gracefully + if [[ ! -e ${USEFILE} ]] || [[ ! -e ${IUSEFILE} && ${hidden} == "no" ]] ; then + case ${missing_action} in + true) return 0;; + false) return 1;; + die) die "Unable to determine what USE flags $PKG was built with";; + esac + fi + + if [[ ${hidden} == "no" ]] ; then + local IUSE_BUILT=( $(<"${IUSEFILE}") ) + # Don't check USE_EXPAND #147237 + local expand + for expand in $(echo ${USE_EXPAND} | tr '[:upper:]' '[:lower:]') ; do + if [[ $1 == ${expand}_* ]] ; then + expand="" + break + fi + done + if [[ -n ${expand} ]] ; then + if ! has $1 ${IUSE_BUILT[@]#[-+]} ; then + case ${missing_action} in + true) return 0;; + false) return 1;; + die) die "$PKG does not actually support the $1 USE flag!";; + esac + fi + fi + fi local USE_BUILT=$(<${USEFILE}) while [[ $# -gt 0 ]] ; do @@ -1599,51 +1947,31 @@ [[ ${opt} = "-a" ]] } -# Many configure scripts wrongly bail when a C++ compiler -# could not be detected. #73450 +# @FUNCTION: epunt_cxx +# @USAGE: [dir to scan] +# @DESCRIPTION: +# Many configure scripts wrongly bail when a C++ compiler could not be +# detected. If dir is not specified, then it defaults to ${S}. +# +# http://bugs.gentoo.org/73450 epunt_cxx() { local dir=$1 [[ -z ${dir} ]] && dir=${S} ebegin "Removing useless C++ checks" local f - for f in $(find ${dir} -name configure) ; do - patch -p0 "${f}" "${PORTDIR}/eclass/ELT-patches/nocxx/nocxx.patch" > /dev/null + find "${dir}" -name configure | while read f ; do + patch --no-backup-if-mismatch -p0 "${f}" "${PORTDIR}/eclass/ELT-patches/nocxx/nocxx.patch" > /dev/null done eend 0 } -# dopamd <file> [more files] -# -# Install pam auth config file in /etc/pam.d -dopamd() { - [[ -z $1 ]] && die "dopamd requires at least one argument" - - use pam || return 0 - - INSDESTTREE=/etc/pam.d \ - doins "$@" || die "failed to install $@" -} -# newpamd <old name> <new name> -# -# Install pam file <old name> as <new name> in /etc/pam.d -newpamd() { - [[ $# -ne 2 ]] && die "newpamd requires two arguements" - - use pam || return 0 - - INSDESTTREE=/etc/pam.d \ - newins "$1" "$2" || die "failed to install $1 as $2" -} - -# make a wrapper script ... -# NOTE: this was originally games_make_wrapper, but I noticed other places where -# this could be used, so I have moved it here and made it not games-specific -# -- wolf31o2 -# $1 == wrapper name -# $2 == binary to run -# $3 == directory to chdir before running binary -# $4 == extra LD_LIBRARY_PATH's (make it : delimited) -# $5 == path for wrapper +# @FUNCTION: make_wrapper +# @USAGE: <wrapper> <target> [chdir] [libpaths] [installpath] +# @DESCRIPTION: +# Create a shell wrapper script named wrapper in installpath +# (defaults to the bindir) to execute target (default of wrapper) by +# first optionally setting LD_LIBRARY_PATH to the colon-delimited +# libpaths followed by optionally changing directory to chdir. make_wrapper() { local wrapper=$1 bin=$2 chdir=$3 libdir=$4 path=$5 local tmpwrapper=$(emktemp) @@ -1663,9 +1991,64 @@ EOF chmod go+rx "${tmpwrapper}" if [[ -n ${path} ]] ; then + ( exeinto "${path}" newexe "${tmpwrapper}" "${wrapper}" + ) || die else - newbin "${tmpwrapper}" "${wrapper}" + newbin "${tmpwrapper}" "${wrapper}" || die fi } + +# @FUNCTION: path_exists +# @USAGE: [-a|-o] <paths> +# @DESCRIPTION: +# Check if the specified paths exist. Works for all types of paths +# (files/dirs/etc...). The -a and -o flags control the requirements +# of the paths. They correspond to "and" and "or" logic. So the -a +# flag means all the paths must exist while the -o flag means at least +# one of the paths must exist. The default behavior is "and". If no +# paths are specified, then the return value is "false". +path_exists() { + local opt=$1 + [[ ${opt} == -[ao] ]] && shift || opt="-a" + + # no paths -> return false + # same behavior as: [[ -e "" ]] + [[ $# -eq 0 ]] && return 1 + + local p r=0 + for p in "$@" ; do + [[ -e ${p} ]] + : $(( r += $? )) + done + + case ${opt} in + -a) return $(( r != 0 )) ;; + -o) return $(( r == $# )) ;; + esac +} + +# @FUNCTION: in_iuse +# @USAGE: <flag> +# @DESCRIPTION: +# Determines whether the given flag is in IUSE. Strips IUSE default prefixes +# as necessary. +# +# Note that this function should not be used in the global scope. +in_iuse() { + debug-print-function ${FUNCNAME} "${@}" + [[ ${#} -eq 1 ]] || die "Invalid args to ${FUNCNAME}()" + + local flag=${1} + local liuse=( ${IUSE} ) + + has "${flag}" "${liuse[@]#[+-]}" +} + +# @FUNCTION: usex +# @USAGE: <USE flag> [true output] [false output] [true suffix] [false suffix] +# @DESCRIPTION: +# If USE flag is set, echo [true output][true suffix] (defaults to "yes"), +# otherwise echo [false output][false suffix] (defaults to "no"). +usex() { use "$1" && echo "${2-yes}$4" || echo "${3-no}$5" ; } #382963