#!/usr/bin/env bash # Copyright 1999-2023 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 # Format of /etc/env.d/binutils/: # config-TARGET: CURRENT=version for TARGET # TARGET-VER: has a TARGET and VER variable : ${ROOT:=/} [[ ${ROOT} != */ ]] && ROOT="${ROOT}/" [[ ${ROOT} != /* ]] && ROOT="${PWD%/}/${ROOT}" BROOT="@GENTOO_EPREFIX@" [[ ${BROOT} == @*@ ]] && BROOT="" : ${EPREFIX="${BROOT}"} EROOT="${ROOT%/}${EPREFIX}/" export PORTAGE_CONFIGROOT="${EROOT}" cd "${BROOT}/" trap ":" INT QUIT TSTP argv0=${0##*/} FUNCTIONS_SH="${BROOT}/lib/gentoo/functions.sh" source ${FUNCTIONS_SH} || { echo "${argv0}: Could not source ${FUNCTIONS_SH}!" 1>&2 exit 1 } esyslog() { :; } die() { eerror "${argv0}: $*"; exit 1; } umask 022 # *BSD SED does not work as-is, use GNU SED. TODO: find details. SED=$(type -P gsed) : ${SED:=$(type -P sed)} usage() { cat << USAGE_END Usage: ${HILITE}binutils-config${NORMAL} ${GOOD}[options]${NORMAL} ${BRACKET}[binutils profile]${NORMAL} ${HILITE}General Options:${NORMAL} ${GOOD}-C, --nocolor${NORMAL} Disable color output ${GOOD}-c, --get-current-profile${NORMAL} Print current profile ${GOOD}-l, --list-profiles${NORMAL} Print a list of available profiles ${GOOD}-u, --uninstall${NORMAL} Remove all signs of specified target ${GOOD}-d, --debug${NORMAL} Execute with debug output ${GOOD}-B, --get-bin-path${NORMAL} Print path where binaries of the given/current profile are located. ${GOOD}-L, --get-lib-path${NORMAL} Print path where libraries of the given/current profile are located. Profile names are of the form: ${BRACKET}-${NORMAL}, ${BRACKET}latest${NORMAL}, ${BRACKET}-latest${NORMAL}, ${BRACKET}latest${NORMAL}. For example: ${BRACKET}i686-pc-linux-gnu-2.15.92.0.2${NORMAL} For more info, please see ${HILITE}binutils-config${NORMAL}(8). USAGE_END exit ${1:-1} } # Usage: version_sorted_paths # Returns paths ordered by version from olders to newest. # We use the following hack: assume the input containst digits only in places of versions # Normalizer: # echo "hello-world-1.2.3.444.56778" | ${SED} -e 's/[0-9]\+/0000&/g' | ${SED} -e 's/0*\([0-9]\{4\}\)/\1/g' # hello-world-0001.0002.0003.0444.56778 # That way we can have 9.0 < 10.0 order. # TODO: explore how portable 'sort -V' is and try using that instead. version_sorted_paths() { local p mangled_v for p in "$@"; do # TODO: avoid -r mangled_v=$(printf "%s" "${p}" | ${SED} -e 's/[0-9]\+/0000&/g' | ${SED} -e 's/0*\([0-9]\{4\}\)/\1/g' ) printf "%s %s\n" "${mangled_v}" "${p}" done | LANG=C sort | $SED -e 's/^.* //g' } mv_if_diff() { if cmp -s "$1" "$2" ; then rm -f "$1" else mv -f "$1" "$2" fi } atomic_ln() { local target=$1 linkdir=$2 linkname=$3 linktmp linkfull linktmp="${linkdir}/.binutils-config.tmp.${linkname}" linkfull="${linkdir}/${linkname}" if [[ -d ${linkfull} ]] ; then # if linking to a dir, we need a little magic to # make it atomic since `mv -T` is not portable rm -rf "${linktmp}" mkdir -p "${linktmp}" ln -sf "${target}" "${linktmp}/${linkname}" mv "${linktmp}/${linkname}" "${linktmp}/../" rmdir "${linktmp}" else # `ln` will expand into unlink();symlink(); which # is not atomic for a small amount of time, but # `mv` is a single rename() call ln -sf "${target}" "${linktmp}" mv "${linktmp}" "${linkfull}" fi } setup_env() { unset TARGET VER LIBPATH source "${ENV_D}/${PROFILE}" if [[ -z ${TARGET} ]] ; then eerror "${PROFILE} is invalid (no \$TARGET defined) :(" return 1 fi if [[ -z ${VER} ]] ; then eerror "${PROFILE} is invalid (no \$VER defined) :(" return 1 fi # # Generate binary symlinks # BINPATH="" BINPATH_LINKS="" if [[ ${TARGET} != ${HOST} ]] ; then # # Newer paths: /usr/${HOST}/${TARGET}/... # Older paths: /usr/${TARGET}/... # if [[ -d "${EROOT}"/usr/${HOST}/${TARGET}/binutils-bin/${VER} ]] ; then BINPATH="${EPREFIX}"/usr/${HOST}/${TARGET}/binutils-bin/${VER} BINPATH_LINKS="${EPREFIX}"/usr/libexec/gcc/${TARGET} fi fi if [[ -z ${BINPATH} ]] ; then BINPATH="${EPREFIX}"/usr/${TARGET}/binutils-bin/${VER} BINPATH_LINKS="${EPREFIX}"/usr/${TARGET}/bin fi } # Lists of headers that various versions have installed. HEADERS=( ansidecl.h bfd.h bfdlink.h demangle.h dis-asm.h dyn-string.h fibheap.h hashtab.h libiberty.h objalloc.h plugin-api.h splay-tree.h symcat.h ) switch_profile() { local x ebegin "Switching to ${PROFILE}" setup_env || return 1 # Facts on binutils's private binary dir contents: # # Native directory looks like: # /usr/x86_64-pc-linux-gnu/binutils-bin/2.34 # Contents are unprefixed tools: # - ar, as, nm, ... # # Cross directory looks like: # /usr/x86_64-pc-linux-gnu/ia64-unknown-linux-gnu/binutils-bin/2.34 # Contents are also unprefixed tools: # - ar, as, nm, ... cd "${ROOT}/${BINPATH}" || exit 1 mkdir -p "${ROOT}/${BINPATH_LINKS}" "${EROOT}/usr/bin" for x in * ; do atomic_ln "${BINPATH}/${x}" "${ROOT}/${BINPATH_LINKS}" "${x}" atomic_ln "${BINPATH_LINKS}/${x}" "${EROOT}/usr/bin" "${TARGET}-${x}" if [[ ${TARGET} == ${HOST} ]] ; then if [[ ${USE_NATIVE_LINKS} == yes ]]; then atomic_ln "${TARGET}-${x}" "${EROOT}/usr/bin" "${x}" else # Remove native links if exist from previous # installations or set by user manually. binutils-config # owns these symlinks. # # TODO: cleanup symlinks not just known to this # release/configuration of binutils, but also list # all possible ones. rm -f "${EROOT}/usr/bin/${x}" fi fi done # # Generate library / ldscripts symlinks # : ${LIBPATH:=${EPREFIX}/usr/lib/binutils/${TARGET}/${VER}} cd "${ROOT}/${LIBPATH}" || exit 1 if [[ ${TARGET} == ${HOST} ]] ; then dstlib=${EROOT}/usr/${HOST}/lib else dstlib=${EROOT}/usr/${HOST}/${TARGET}/lib fi # When upgrading, we need to clean up ldscripts and libs. # Don't symlink back in the libs -- the binutils-lib package handles # these now. # TODO: Stop requiring even the ldscripts symlink. mkdir -p "${dstlib}" rm -rf "${ROOT}/${BINPATH_LINKS}"/ldscripts atomic_ln "${LIBPATH}/ldscripts" "${dstlib}" "ldscripts" find -L "${dstlib}" -xtype l -name 'lib*' -delete # Detect older binutils w/broken rpaths. #562460 # We can hardcode the "/lib" part since that's what the binutils # configure scripts have. They did not include any other path. if [[ $(scanelf -qF '%r#F' "${ROOT}/${BINPATH}/as") == */lib ]] ; then ewarn "Old cross-binutils detected; please re-emerge to fix (see bug #562460)." for x in lib* ; do atomic_ln "${LIBPATH}/${x}" "${dstlib}" "${x}" done fi # # Clean out old generated include symlinks # INCPATH=${LIBPATH}/include if [[ -d ${ROOT}/${INCPATH} ]] ; then cd "${ROOT}/${INCPATH}" || exit 1 if [[ ${HOST} != ${TARGET} ]] ; then # Clean out old path -- cannot use '-exec {} +' syntax here find . -type f -exec rm -f "${EROOT}/usr/${TARGET}/usr/include/{}" \; rmdir "${EROOT}/usr/${TARGET}/usr/include" >& /dev/null rmdir "${EROOT}/usr/${TARGET}/usr" >& /dev/null rmdir "${EROOT}/usr/${TARGET}" >& /dev/null fi fi # # Make sure proper paths get updated # local env_update_flag="--no-ldconfig" if [[ ${TARGET} == ${HOST} ]] ; then # Delete old config now that binutils-libs installs these files. # Note: This skips ldconfig update if env.d had LDPATH, but meh. # Most people have upgraded to ld.so.conf.d, and someone else will # eventually re-run ldconfig for us. x="${EROOT}"/etc/ld.so.conf.d/05binutils.conf if [[ -e ${x} ]]; then rm -f "${x}" env_update_flag="" fi DATAPATH="${EPREFIX}"/usr/share/binutils-data/${TARGET}/${VER} local e="${EROOT}"/etc/env.d/05binutils local ee="${e}.tmp" rm -f "${ee}" [[ -d ${ROOT}/${DATAPATH}/man ]] && echo "MANPATH=${DATAPATH}/man" >> "${ee}" [[ -d ${ROOT}/${DATAPATH}/info ]] && echo "INFOPATH=${DATAPATH}/info" >> "${ee}" [[ -e "${ee}" ]] && mv_if_diff "${ee}" "${e}" fi local c="${ENV_D}/config-${TARGET}" local cc="${c}.tmp" echo "CURRENT=${VER}" > "${cc}" mv_if_diff "${cc}" "${c}" eend 0 # # Regen env.d if need/can be # if [[ ${ROOT} == "/" ]] && [[ ${TARGET} == ${HOST} ]] ; then env-update ${env_update_flag} echo ewarn "Please remember to run:" echo ewarn " # . ${EPREFIX}/etc/profile" echo fi return 0 } uninstall_target() { : ${TARGET:=${UARG}} if [[ ${TARGET} == ${HOST} ]] ; then die "refusing to uninstall native binutils" fi shopt -s nullglob PROFILE="" for PROFILE in "${ENV_D}"/${TARGET}-* ; do ewarn "Removing all signs of ${PROFILE##*/}" rm -f "${ENV_D}"/${PROFILE} done if [[ -z ${PROFILE} ]] && [[ ! -e ${ENV_D}/config-${TARGET} ]] ; then die "no profiles exist for '${TARGET}'" fi rm -f "${ENV_D}"/config-${TARGET} local x for x in \ addr2line ar as c++filt dwp elf2flt elfedit flthdr gprof \ ld ld.{bfd,gold,real} \ nm objcopy objdump ranlib readelf size strings strip do x=( "${EROOT}"/usr/bin/${TARGET}-${x} "${EROOT}"/usr/{${HOST}/,}${TARGET}/bin/${x} "${EROOT}"/usr/libexec/gcc/${TARGET}/${x} ) rm -f "${x[@]}" done for x in "${HEADERS[@]}" ; do rm -f "${EROOT}"/usr/{${HOST}/,}${TARGET}/{usr/,}include/${x} done for x in bfd iberty opcodes ; do rm -f "${EROOT}"/usr/${HOST}/${TARGET}/lib/lib${x}{{-*,}.so,.a,.la} done # Delete broken symlinks local destdir="${EROOT}/usr/${HOST}/${TARGET}" rm -f "${destdir}"/lib/ldscripts find -L "${destdir}"/lib -type l -exec rm {} + rmdir \ "${destdir}"/{bin,include,lib,usr} \ "${destdir}" \ "${EROOT}"/var/db/pkg/cross-${TARGET} \ "${EROOT}"/usr/{${HOST}/,}${TARGET}/bin \ "${EROOT}"/usr/libexec/gcc/${TARGET} \ 2>/dev/null rm -f "${ENV_D}"/${TARGET}-* } set_current_profile() { if [[ ! -f ${ENV_D}/config-${TARGET} ]] ; then eerror "${argv0}: unable to locate a profile for target: ${TARGET}" return 1 fi source "${ENV_D}/config-${TARGET}" if [[ -z ${CURRENT} ]] ; then eerror "${argv0}: no binutils profile is active!" return 1 fi echo "${TARGET}-${CURRENT}" return 0 } get_current_profile() { echo "${PROFILE}" ; } get_bin_path() { setup_env || return 1 echo "${BINPATH}" } get_lib_path() { setup_env || return 1 echo "${LIBPATH}" } list_profiles() { local x i target if [[ ${ROOT} != / ]] ; then echo "Using binutils-config info in ${ROOT}" fi set -- "${ENV_D}"/* target= i=1 for x ; do # skip broken links and config files [[ -f ${x} ]] || continue [[ ${x} == */config-* ]] && continue source "${x}" if [[ ${target} != ${TARGET} ]] ; then [[ -n ${target} ]] && echo target=${TARGET} fi x=${x##*/} if [[ -e ${ENV_D}/config-${TARGET} ]] ; then source "${ENV_D}/config-${TARGET}" if [[ ${VER} == ${CURRENT} ]] ; then [[ ${TARGET} == ${HOST} ]] \ && x="${x} ${GOOD}*${NORMAL}" \ || x="${x} ${HILITE}*${NORMAL}" fi fi # We would align the [...] field like so: #printf ' [%*ss] %s\n' ${##} "${i}" "${x}" # but this breaks simple scripting: `binutils -l | awk '{print $2}'` # Or we could align the target col like so: #printf ' [%s]%*s %s\n' "${i}" $(( ${##} - ${#i} )) "" "${x}" # but i'm not sold that it looks better # So keep it simple ... only makes a diff anyways for crazy people # like me which have 100+ binutils packages installed ... echo " [$i] ${x}" ((++i)) done } set_HOST() { # Set HOST to CHOST if it isn't already set : ${HOST:=${CHOST:-$(portageq envvar CHOST)}} } ENV_D="${EROOT}etc/env.d/binutils" DEBUG="no" NEED_ACTION="yes" DOIT="switch_profile" PROFILE="current" HOST="" TARGET="" USE_NATIVE_LINKS="@USE_NATIVE_LINKS@" unset UARG select_action() { if [[ ${NEED_ACTION} != "no" ]] ; then NEED_ACTION="no" DOIT=$1 else die "one action at a time!" fi } while [[ $# -gt 0 ]] ; do x=$1 shift case ${x} in -B|--get-bin-path) select_action get_bin_path ;; -L|--get-lib-path) select_action get_lib_path ;; -c|--get-current-profile) select_action get_current_profile ;; -l|--list|--list-profiles) select_action list_profiles ;; -u|--uninstall) select_action uninstall_target ;; -C|--nocolor) ;; # nothing to do; functions.sh parsed this for us -d|--debug) DEBUG="yes" ;; -h|--help) usage 0 ;; --enable-native-links) USE_NATIVE_LINKS="yes" ;; --disable-native-links) USE_NATIVE_LINKS="no" ;; -V|--version) ver="@PV@" echo "binutils-config-${ver/@'PV'@/git}" exit 0 ;; -*) die "invalid switch! Try '--help'." ;; *) if [[ ${UARG+set} == "set" ]] ; then die "only one profile/target at a time please" fi NEED_ACTION="maybe" UARG=${x} ;; esac done [[ ${NEED_ACTION} == "yes" ]] && usage 1 [[ ${DEBUG} == "yes" ]] && set -x # All operations need to know the current HOST to figure out # what is a native target and what is a cross target set_HOST # All operations need to know the profile the user wants case ${DOIT} in switch_profile) # decode user's profile choice x=${UARG:-$(TARGET=${HOST} set_current_profile)} PROFILE="" if [[ -z $(echo ${x} | tr -d '[:digit:]') ]] ; then # User gave us a profile index number from '--list-profiles' i=1 for y in "${ENV_D}"/* ; do [[ ${y/config-} != ${y} ]] && continue if [[ -f ${y} ]] && [[ ${x} -eq ${i} ]] ; then PROFILE=${y##*/} break fi ((++i)) done fi if [[ -z ${PROFILE} ]] ; then # User gave us "latest" or "-latest". if [[ ${x} == latest ]]; then x=$(version_sorted_paths "${ENV_D}"/${HOST}-* | tail -1) elif [[ ${x} == *-latest ]]; then x=$(version_sorted_paths "${ENV_D}"/${x%-latest}-* | tail -1) fi # User gave us a full , or x=${x##*/} if [[ -f ${ENV_D}/${x} ]] ; then # Valid PROFILE=${x} else # Not a valid if [[ ! -f ${ENV_D}/config-${x} ]] ; then # Maybe they just gave us a . Infer . if [[ -f ${ENV_D}/${HOST}-${x} ]] ; then x=${HOST}-${x} else die "could not locate '$x' in '${ENV_D}/'!" fi PROFILE=${x} else # Maybe they just gave us a . Pick active profile PROFILE=$(TARGET=${x} set_current_profile) fi fi fi ;; *) # lookup current profile as the user gave us a target PROFILE=$(TARGET=${UARG:-${HOST}} set_current_profile) || exit 1 ;; esac eval ${DOIT} # vim:ts=4