#!/bin/sh - # # portup # # Copyright (C) 2006-2011 Oliver Fromme (Germany) # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # Author contacts: # # ------------------------ # ======== Manual ======== # ------------------------ # # The "portup" tool is a Helper script for updating ports or # packages, to be used with the FreeBSD Ports Collection. # You can update either from source (/usr/ports) or via # precompiled binary packages (usually *.tbz files). # This script does not require any other software beyond # the FreeBSD base system. # # The portup tool is intended to be used by people who know # what they're doing, and who don't like other tools like # portupgrade or portmanager (for whatever reason). # # It does not automatically rebuild ports that depend on the # current one (but it shouldn't break them either, because # libraries are saved). It is suggested that you look at the # output of "pkg_info -R " and decide yourself what to do. # As usual, you should know what you are doing, no matter what # kind of tools you use. ;-) # # Usage: portup [-bdHhLlPpy] { | :: [...]} # # There are two possibilities to select ports for updating: # # -1- Specify the name of one or more installed ports: # # portup foo-2.0 bar-3.4 # # -2- You can omit the version number: # # portup foo bar # # If multiple ports are specified, all of them will be updated # in the order of the command line. As soon as one of them # fails, the reminaing ones are skipped. After all ports have # been handled, an indication is printed if there were any # failures. If so, then a detailed list of ports is printed, # indicating which of them succeeded, which failed, and which # were skipped because of preceding failures. # # The "-l" option will display the most recent comments from # the commit log of this port. No update is done. # # Normally, this script requests you to press to begin # the actual update. The "-y" flag suppresses that, so it # does not request confirmation. Might be useful for batch- # mode operation when called from other scripts, or when you # want to update a lot of ports without interaction. # Note that the "-y" flag also suppresses the safety confirmation # when the names of the old and updated ports differ, which is # usually caused by changes to the $PKGNAMESUFFIX setting of # the port. # # If the "-b" option is present, the script will beep when it # waits for the key to be pressed. This is useful if # you run the script for multiple ports without watching the # terminal all the time. (It beeps four times, then waits a # minute, then beeps again, and so on, until you press .) # Note that "-b" is ignored if "-y" is present. # # The "-p" option causes a package to be created (the same as # doing "make package") in addition to the normal update. # # The "-d" option de-installs the existing port before trying # to build the new port. This is often required when using the # "::" syntax (see below) because of conflicts between the two # ports involved. Beware: If building the new port fails, the # system will miss both ports! (The default behaviour -- when # "-d" is not used -- is to build the new port first, then # remove the existing port, finally install the newly built # port.) Note that the "-d" option is ignored when the "-P" # option is used, because nothing needs to be built. # # The "-P" option prevents use of the ports collection. # Instead, a binary package (*.tbz) in the current directory # will be used to replace the installed package. In this # mode, /usr/ports does not have to exist at all. This option # cannot be combined with -p. Note that the "-P" option is # particularly useful to update packages inside a chroot or # jail that doesn't contain a proper build environment. It # must contain the package tools (at least pkg_add, pkg_info # and pkg_delete) and their libraries, the package database # (/var/db/pkg) and the portup tool itself, of course. # # The "-L" option prevents the saving of old libraries. # Normally, if the update installs new shared libraries, the # old ones are preserved and moved to the "compat" directory, # so old binaries that are linked against the old libraries # will continue to work. The "-L" option prevents this. # # If you need to update a port to a different port while # retaining the dependencies, append a double colon and the # new port origin. For example: # # portup python25::lang/python26 # # That command would update the existing python25-* port to # the current python26 port. All other packages that have # a dependency on the old port will get their dependency # adjusted to the new port. If you use the -P option (i.e. # don't use /usr/ports), then the second part after the # double colon must be the file name of the new package, # which must be located in the current directory, like this: # # portup -P python25::python26-2.6.2.tbz # # When using the "::" syntax, you often have to use the "-d" # flag, too, because of conflicts between the two ports # involved. # # The -w option enables split-screen display using the # window(1) utility. The top part of the screen (about 20% # of available rows) contains a summary of the current # build progress, while the bottom part of the screen # contains the usual port build output. Repeat the -w # option to increase the verbosity of the progress display. # In FreeBSD up to version 7.x, the window(1) utility is # included in the base system. If you have FreeBSD 8 or # newer, you need to install the misc/window port in order # to be able to use the -w option. # # Finally, there are two online help texts available: # The -h option will display a short list of the options, # this should fit on most screens without scrolling. # The -H option will display a longer description (i.e. # this text). # # The portup tool recognizes the PORTSDIR and PKG_DBDIR # environment variables if they are set. Of course, the # usual default values are used if the variables are not set: # /usr/ports and /var/db/pkg, respectively. # # # TODO: # # Prevent saving old libs if they don't originate from # /usr/local/lib ($PREFIX/lib). This is necessary for # compat ports and linux compatibility ports. When this # is implemented, the -L option can probably be dropped. # # Add an option to include any dependant ports in the update # (or rebuild) process. # # Sort ports according to the dependency graph. This is # currently implemented in a separate script called # "portorder". # # Take variables from /etc/make.conf (check if this should # override the environment). Applies to: # PORTSDIR, PKG_DBDIR, PREFIX. # export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin if [ -d /usr/X11R6/bin ]; then PATH="${PATH}:/usr/X11R6/bin" fi if [ -d /usr/X11R6/sbin ]; then PATH="${PATH}:/usr/X11R6/sbin" fi LIBSAVEDIR="/usr/local/lib/compat/pkg" ME="${0##*/}" NEWLINE=" " if [ -t 2 ]; then TTY=2 elif [ -t 1 ]; then TTY=1 else TTY="" fi if [ -n "$TTY" ]; then export BOLD_ON="$(tput md)" if [ -z "$BOLD_ON" ]; then # If bold is not supported, use reverse. export BOLD_ON="$(tput mr)" fi export BOLD_OFF="$(tput me)" export MSG_DELAY="3" else export BOLD_ON="" export BOLD_OFF="" export MSG_DELAY="" fi Message() { if [ "x$1" = "x-b" ]; then shift if [ -z "$*" ]; then echo "" >&${TTY:-2} elif [ -n "$TTY" ]; then echo "${ME}: ${BOLD_ON}$*${BOLD_OFF}" >&$TTY else echo "${ME}: $*$" >&2 fi if $MULTIPORTS && [ -n "$MSG_DELAY" -a "$MSG_DELAY" != "0" ]; then sleep $MSG_DELAY fi elif [ -z "$*" ]; then echo "" >&${TTY:-2} else echo "${ME}: $*" >&${TTY:-2} fi } Die() { Message "$@" exit 1 } Die_With_Hint() { if [ -n "$*" ]; then Message "$@" fi Die "Type \"$ME -h\" for short help or \"$ME -H\" for detailed help." } match() { case "$1" in $2) return 0 ;; *) return 1 ;; esac } Get_Screen_Size() { read win_rows win_cols <<-daer $( stty -a | awk '{ if (!rows && / row/) { rows = $0 sub(/ *row.*$/, "", rows) sub(/^.*[^0-9]/, "", rows) } if (!cols && / col/) { cols = $0 sub(/ *col.*$/, "", cols) sub(/^.*[^0-9]/, "", cols) } if (rows && cols) { print rows, cols exit } }' ) daer if match "${win_rows}:${win_cols}" ':*|*:|*[!0-9:]*|*:*:*'; then Message "Cannot determine number of rows and columns." Die -b "Unknown screen size, please retry without the -w option." fi } html_entity() { sed 's/<//g;s/"/"/g;s/&/\&/g' } Get_confirmation() { local OUT if ! $CONFIRM || [ ! -t 0 -o -z "$TTY" ]; then return fi MSG_DELAY="" Message -b "$1" echo -n "Please press to continue or to abort. " >&$TTY if $DO_BEEP; then TIMEOUT=1 REPEAT=4 i=1 while :; do printf '\a' >&$TTY if read -t $TIMEOUT dummy ; then break fi i=$(( $i + 1 )) if [ $i -eq $REPEAT ]; then TIMEOUT=60 elif [ $i -gt $REPEAT ]; then TIMEOUT=1 i=1 fi done else read dummy fi } Help_short() { cat <<-tac >&2 Usage: $ME [-bdHhLlPpy] { | :: [...]} Options: -b Beep when waiting for the key. -d Delete old port *before* building the new one. -H Display longer help. -h Display short help (this one). -L Do not save old libraries to the compat directory. -l Display latest commit log entry and exit. -P Use binary packages instead of /usr/ports. -p Run "make package" in addition to the normal update. -w Use split-screen display. Requires the window(1) utility. -y Do not request confirmation. Arguments: is the name of an installed port, with or without version. is a path relative to /usr/ports. is the name of a binary package file (use with the -P option). Environment variables used: \$PORTSDIR (default: /usr/ports) \$PKG_DBDIR (default: /var/db/pkg) tac exit 1 } Help_long() { sed '1d;/^$/,$d;s/^#/ /' "$0" >&2 exit 1 } if [ "x${PORTSDIR:=/usr/ports}" != "x/usr/ports" ]; then Message -b "WARNING: \$PORTSDIR is set to $PORTSDIR" fi PORTSDIR="${PORTSDIR%/}" # Do not put "/" at the end of directories!!! export PORTSDIR if [ "x${PKG_DBDIR:=/var/db/pkg}" != "x/var/db/pkg" ]; then Message -b "WARNING: \$PKG_DBDIR is set to $PKG_DBDIR" fi PKG_DBDIR="${PKG_DBDIR%/}" # Do not put "/" at the end of directories!!! export PKG_DBDIR # # Parse command line options. # # We intentionally do *not* use getopt(1) # because of certain shortcomings. # DO_BEEP=false RMFIRST=false NO_SAVE=false CVS_LOG=false NOPORTS=false PACKAGE=false SWINDOW=0 CONFIRM=true BRKFAIL=true # No command line option for this one. NOCLEAN=false # No command line option for this one. OPT_ACTION_b='DO_BEEP=true' OPT_ACTION_d='RMFIRST=true' OPT_ACTION_H='Help_long' OPT_ACTION_h='Help_short' OPT_ACTION_L='NO_SAVE=true' OPT_ACTION_l='CVS_LOG=true' OPT_ACTION_P='NOPORTS=true' OPT_ACTION_p='PACKAGE=true' OPT_ACTION_w='SWINDOW=$(( SWINDOW + 1 ))' OPT_ACTION_y='CONFIRM=false' # Save options and arguments in case we need to spawn window(1). # Note that this works because valid arguments don't contain spaces. if [ -n "${_PORTUP_ARGS}" ]; then set -f set -- ${_PORTUP_ARGS} set +f else export _PORTUP_ARGS="$*" fi while [ $# -gt 0 ]; do OPT="$1" if match "$OPT" "[!-]*"; then break fi while [ ${#OPT} -gt 1 ]; do OPT="${OPT#?}" if match "$OPT" "-"; then break fi if ! match "$OPT" "[A-Za-z0-9]*"; then Die_With_Hint "Options syntax error." fi REST="${OPT#?}" CHAR="${OPT%$REST}" eval ACTION=\"\$OPT_ACTION_"$CHAR"\" if [ -n "$ACTION" ]; then eval "$ACTION" || exit 1 else Die_With_Hint "Unknown option '$CHAR'." fi done shift if match "$OPT" "-"; then break fi done if [ $# -lt 1 ]; then Help_short fi if $NOPORTS; then if $NOCLEAN || $PACKAGE; then Die -b "The -P option cannot be used with -n or -p." fi fi if [ $SWINDOW -gt 0 ]; then Get_Screen_Size fi Print_CVS_log() { # # If a CVS directory exists, assume that a ports tree # has been checked-out locally and try to get the # information from there. Otherwise try to get it # via anoncvs (requires network access). Finally, # if anoncvs fails for some reason, try to get the # log from the cvsweb interface (requires network # access, too). # if ! $NOPORTS && [ -f CVS/Entries -a -f CVS/Repository -a -f CVS/Root ]; then read CVS_RT < CVS/Root if match "$CVS_RT" "*:/*" || [ -d "$CVS_RT" ]; then cvs -R log -N -r. Makefile | sed -n '/^revision/,$p' return fi fi # # The following piece of code was used to retrieve the # CVS log from an anonymous CVS server (anoncvs). # However, the few anoncvs servers turned out to be # somewhat unreliable. I guess that's because not many # people are using them, so the respective admins don't # care much about them, and problems aren't noticed # very quickly. Also, this piece of code displays only # the latest commit message, while the cvsweb code below # displays all commit messages after the version that's # currently installed. # # For the above reasons, the anoncvs code is disabled, # so it doesn't unnecessarily complicate things. # ##ANONCVS="freebsdanoncvs@anoncvs.freebsd.org:/home/ncvs" ##ANONCVS="freebsd@mirror.hanse.de:/var/freebsd/cvs" ##ANONCVS="anoncvs@anoncvs1.freebsd.org:/home/ncvs" #ANONCVS="" # #if [ -n "$ANONCVS" ]; then # TMPDIR=$(mktemp -d /tmp/portup.tmp.$$.XXXXXX) || { Message -b "mktemp failed."; return 1; } # mkdir $TMPDIR/CVS # echo "ports/$PDIR" > $TMPDIR/CVS/Repository # echo "/Makefile/1.1///" > $TMPDIR/CVS/Entries # cd $TMPDIR # LOG=$(cvs -R -f -d "$ANONCVS" log -N -r. Makefile | sed -n '/^revision/,$p') # cd .. # rm -rf $TMPDIR #else # LOG="" #fi # #if [ -n "$LOG" ]; then # echo "$LOG" #fi # # Web access might require a proxy. In that case, # assume that the user has configured a proxy in # /etc/make.conf. # # Caution -- This is heavy shell voodoo magic! :-) # FETCH_BEFORE_ARGS="" if [ -f /etc/make.conf ]; then ORIG_IFS="$IFS" IFS="$NEWLINE" { read FETCH_ENV read FETCH_BEFORE_ARGS read HTTP_PROXY read http_proxy } <<-daer $(make \ -V FETCH_ENV \ -V FETCH_BEFORE_ARGS \ -V HTTP_PROXY \ -V http_proxy \ -f /dev/null ) daer IFS="$ORIG_IFS" test -n "$HTTP_PROXY" && export HTTP_PROXY test -n "$http_proxy" && export http_proxy test -n "$FETCH_ENV" && export $FETCH_ENV fi CVSWEB="http://www.freebsd.org/cgi/cvsweb.cgi/ports/$PDIR/Makefile" if ! PKG_MTIME=$(stat -f "%m" "$PKG_DBDIR/$PKG/+CONTENTS"); then Message "File not found: $PKG_DBDIR/$PKG/+CONTENTS" PKG_MTIME=$(date +%s) fi # We need to parse the date/time stamp with POSIX locale. unset LC_ALL LANG export LC_TIME=C IN_LOG=false HAVE_ONE=false fetch $FETCH_BEFORE_ARGS -q -o - "$CVSWEB" | ( IFS="$NEWLINE"; while read LINE; do case "$LINE" in *Revision*[0-9].[0-9]*""*) IN_LOG=true URLS="" # Note that $WHEN is in UTC, but local time is # more useful to us, so we convert $REV_MTIME # to a local date/time string. LWHEN=$(date -r $REV_MTIME +"%F %T") HEAD="---- ${NEW_ORIGIN:-$PDIR}/Makefile $REV - "$LWHEN" ------------------------" while [ ${#HEAD} -gt 79 ]; do HEAD="${HEAD%?}" done echo "${BOLD_ON}$HEAD${BOLD_OFF}" ;; *""*) IN_LOG=false HAVE_ONE=true if [ -n "$URLS" ]; then echo "" echo "URLs:" ORIG_IFS2="$IFS" IFS="<" set -- ${URLS#*<} while [ $# -gt 0 ]; do if match "$1" "a href=\"*"; then URL="${1#a href=\"}" URL="${URL%%\"*}" echo "$URL" fi shift done IFS="$ORIG_IFS2" fi ;; *) if $IN_LOG; then echo "$LINE" if match "$LINE" "*]*>//g' \ | html_entity } Portup() { # # This is the main update function for one port. # It takes exactly one argument (the package to be updated). # local i # Find the appropriate port's directory and change to it # (unless the -P option is in effect, i.e. $NOPORTS). if match "$1" '*::*'; then NEW_ORIGIN="${1##*::}" if $NOPORTS; then if [ ! -f "$NEW_ORIGIN" ]; then Message "Argument: \"$1\"" Message -b "Package file \"$NEW_ORIGIN\" not found." return 1 fi else NEW_ORIGIN="${NEW_ORIGIN#$PORTSDIR/}" if [ ! -d "$PORTSDIR/$NEW_ORIGIN" ]; then Message "Argument: \"$1\"" Message -b "New port origin \"$NEW_ORIGIN\" not found." return 1 fi fi PKG="${1%::*}" else NEW_ORIGIN="" PKG="$1" fi PKG="${PKG%.t[bgx]z}" if [ ! -d "$PKG_DBDIR/$PKG" ]; then # # Maybe the version number is missing or wrong ... # set -- $(cd $PKG_DBDIR ; for i in "${PKG}-"*; do case "$i" in "${PKG}-"*-*|*"*") ;; *) echo "$i" ;; esac done) if [ $# -lt 1 ]; then set -- $(cd $PKG_DBDIR ; for i in "${PKG%-*}-"*; do case "$i" in "${PKG%-*}-"*-*|*"*") ;; *) echo "$i" ;; esac done) if [ $# -lt 1 ]; then Message -b "No package matching \"$PKG\" is currently installed." return 1 elif [ $# -gt 1 ]; then Message -b "Multiple packages matching \"$PKG\"." return 1 fi elif [ $# -gt 1 ]; then Message -b "Multiple packages matching \"${PKG}\"." return 1 fi PKG="$1" fi OLD_ORIGIN=$(pkg_info -qo "$PKG") if [ -n "$NEW_ORIGIN" ]; then PDIR="$NEW_ORIGIN" else PDIR="$OLD_ORIGIN" fi if ! $NOPORTS; then APDIR="$PORTSDIR/$PDIR" if [ -z "$PDIR" -o ! -d "$APDIR" ]; then Message "Argument: \"$1\"" Message -b "Cannot find ports directory for package \"$PKG\"." return 1 fi cd "$APDIR" || { Message "Argument: \"$1\"" Message -b "Port directory $APDIR does not exist." return 1 } fi # # We are now in the base directory of a port (/usr/ports/cat/foo), # (unless the -P option is in effect, i.e. $NOPORTS is true), # and $PDIR contains the part relative to /usr/ports ("cat/foo"). # $PKG contains the package name to be updated. # # If the "::" syntax has been used to cross-update to a different # port, the $NEW_ORIGIN contains the new port's origin relative to # /usr/ports. If the -P option is present, $NEW_ORIGIN contains # the new package's filename. When doing a normal update (no "::"), # $NEW_ORIGIN is empty. # # If the -l option was specified, simply get the comment from the # most recent CVS commit and display it, then exit. # if $CVS_LOG; then Print_CVS_log return 0 fi # # Find the package that this port was installed from. # PI_DIR=$( cd $PKG_DBDIR echo */+CONTENTS \ | xargs grep -l "comment ORIGIN:${OLD_ORIGIN}\$" ) if [ -z "$PI_DIR" ]; then Message -b "Error: The port $OLD_ORIGIN is currently not installed." return 1 elif [ $(echo "$PI_DIR" | wc -l) -gt 1 ]; then Message "Error: Multiple ports have $OLD_ORIGIN as their origin:" echo "$PI_DIR" | sed 's+/.*$++;s/^/ - /' >&${TTY:-2} Message -b "Error: Unexpected inconsistency of the package database." return 1 fi P_OLD="${PI_DIR%/+CONTENTS}" P_OLD="${P_OLD##*/}" if $NOPORTS; then if [ -n "$NEW_ORIGIN" ]; then set -- "$NEW_ORIGIN" else P_NEW="${PKG%-*}" set -- $(for i in "$P_NEW"-*.t[bgx]z; do case "$i" in "$P_NEW"-*-*|*"*".t[bgx]z) ;; *) echo "$i" ;; esac done) if [ $# -lt 1 ]; then Message -b "No package file matching \"${P_NEW}*.t[bgx]z\" found." return 1 elif [ $# -gt 1 ]; then Message -b "Multiple package files matching \"${P_NEW}*.t[bgx]z\"." return 1 fi fi P_NEW="${1%.t[bgx]z}" else P_NEW=$(make describe | cut -f1 -d'|') fi if [ -z "$P_NEW" ]; then Message -b "Error: Cannot determine the version name of port ${PDIR}." return 1 fi # # Now $P_OLD and $P_NEW contain the package names of the # currently installed (old) and the new port, respectively, # e.g. "foo-1.0" and "foo-1.1". # if [ "x$P_NEW" = "x$P_OLD" ]; then REBUILD=true # Same version, just rebuild it. else REBUILD=false # New version, i.e. update. fi PORT_NAME="${P_OLD%-*}" PNEW_NAME="${P_NEW%-*}" if [ -z "$NEW_ORIGIN" -a "x$PORT_NAME" != "x$PNEW_NAME" ]; then Message -b "WARNING: The port name seems to have changed." Message "Old name: \"$PORT_NAME\" ($P_OLD)" Message "New name: \"$PNEW_NAME\" ($P_NEW)" if $CONFIRM; then if [ -t 2 -a -t 0 ]; then Get_confirmation "Are you sure you want to do that?" else Message -b "For safety reasons the update won't be done automatically." Message -b 'Use -y to enforce it, or use \"::\" syntax (see docs).' return 1 fi fi NEW_ORIGIN="$OLD_ORIGIN" # Behave as if "::" syntax was used. fi OLD_VERS="${P_OLD##*-}" NEW_VERS="${P_NEW##*-}" if [ -s "$PKG_DBDIR/$P_OLD/+REQUIRED_BY" ]; then REQ_BY_FILE="$PKG_DBDIR/$P_OLD/+REQUIRED_BY" HAVE_REQ_BY=true elif [ -s "./+REQUIRED_BY" ]; then REQ_BY_FILE="./+REQUIRED_BY" HAVE_REQ_BY=true else if pkg_info $P_OLD | grep -q "^Information for ${P_OLD}:\$"; then if pkg_info -R $P_OLD | fgrep -xvq -e "" -e "Information for ${P_OLD}:"; then Message -b "Error: Cannot locate required_by file of ${P_OLD}!" return 1 fi else Message -b "Error: Cannot get information for installed port ${P_OLD}." return 1 fi HAVE_REQ_BY=false fi echo "" >&${TTY:-2} if $REBUILD; then if $NOPORTS; then MESSAG="Refreshing package \"$PORT_NAME\" (version $NEW_VERS)." WTITLE="Refreshing pkg $PORT_NAME $NEW_VERS" else MESSAG="Rebuilding port \"$PORT_NAME\" (version $NEW_VERS)." WTITLE="Rebuilding port $PORT_NAME $NEW_VERS" fi elif [ -z "$NEW_ORIGIN" ]; then if $NOPORTS; then MESSAG="Updating package \"$PORT_NAME\" from $OLD_VERS to $NEW_VERS." WTITLE="Updating pkg $PORT_NAME $OLD_VERS --> $NEW_VERS" else MESSAG="Updating port \"$PORT_NAME\" from $OLD_VERS to $NEW_VERS." WTITLE="Updating port $PORT_NAME $OLD_VERS --> $NEW_VERS" fi else MESSAG="Updating from \"${PORT_NAME}-$OLD_VERS\" to \"${PNEW_NAME}-$NEW_VERS\"." WTITLE="$PORT_NAME $OLD_VERS --> $PNEW_NAME $NEW_VERS" fi printf '^\b%s\n' "$MESSAG" >&${TTY:-2} if $MULTIPORTS; then # Update window title. printf "\033]2;%s\a" "$WTITLE" >&${TTY:-2} fi if $HAVE_REQ_BY; then echo "It is required by the following packages:" >&${TTY:-2} echo "" >&${TTY:-2} sort "$REQ_BY_FILE" | sed 's/^/ /' >&${TTY:-2} else echo "It is not required by any other installed packages." >&${TTY:-2} fi # # From this point on we need to be root. # test "$(id -nu)" = root || { Message -b "Sorry, you must be root to do this." ; return 1; } Get_confirmation "" echo "" >&${TTY:-2} # # Save all libraries from the old package, so we can save # them to the compat directory later if necessary. # if [ -d "portup-libs" ]; then Message "WARNING: Directory portup-libs already exists; removing it first." rm -rf portup-libs || return 1 fi mkdir portup-libs || { Message -b "Cannot create temporary directory \"portup-libs\"."; return 1; } pkg_info -L $P_OLD | grep '/lib/.*\.so\.' | xargs -J % cp -p % portup-libs if [ $(ls -a portup-libs | wc -l) -le 2 ]; then rmdir portup-libs || return 1 fi # # Save the information from the old "REQUIRED_BY" file. # if [ -f "$PKG_DBDIR/$P_OLD/+REQUIRED_BY" ]; then mv "$PKG_DBDIR/$P_OLD/+REQUIRED_BY" . || { Message -b "Cannot save the old package information to the current directory."; return 1; } fi # # Build the port! # if ! $NOPORTS && ! $RMFIRST ; then make || return 1 fi # # Unfortunately, replacing the package is not an atomic operation. # There isn't anything we can do about that, so lets just hope it # won't take too long, and nothing tries to use the package in between. # if ! $NOPORTS && [ -z "$NEW_ORIGIN" ]; then make deinstall || { Message -b "Cannot deinstall old package ($PDIR)."; return 1; } else pkg_delete -f "$P_OLD" || { Message -b "Cannot pkg_delete old package \"$P_OLD\"."; return 1; } fi if ! $NOPORTS && $RMFIRST ; then make || return 1 fi if $NOPORTS; then pkg_add "${P_NEW}".t[bgx]z || { Message -b "Cannot install new package \"${P_NEW}.t[bgx]z\""; return 1; } else make install || { Message -b "Cannot install new port ($PDIR)."; return 1; } fi # # OK, done. Now make a package if requested. # if $PACKAGE; then make package || return 1 fi # # If any libraries have been updated, move the old ones # to the compat directory. # if [ -d portup-libs ]; then if ! pkg_info $P_NEW | grep -q "^Information for ${P_NEW}:\$"; then Message -b "Error: Cannot get information for new port." return 1 fi ( echo "" >&${TTY:-2} cd portup-libs for LIB in *.so.*; do PAT=$(echo "$LIB" | sed 's/[[\\.*+]/\\&/g') if ! pkg_info -L $P_NEW | egrep -q "/lib(/|/.*/)${PAT}\$"; then if $NO_SAVE; then Message -b "$LIB was updated, but saving of old libraries is disabled." else Message "Saving library $LIB to compat." if [ ! -d "$LIBSAVEDIR" ]; then mkdir -p "$LIBSAVEDIR" || return 1 fi mv $LIB "$LIBSAVEDIR" || return 1 fi else # DEBUG: Message "Library $LIB is kept by port." fi done echo "" >&${TTY:-2} ) rm -rf portup-libs else Message "Note: This port does not provide shared libraries." fi if [ ! -d "$PKG_DBDIR/$P_NEW" ]; then Message -b "Error: New pkg directory doesn't exist." return 1 fi # # Restore the information from the "REQUIRED_BY" file. # if $HAVE_REQ_BY; then mv ./+REQUIRED_BY "$PKG_DBDIR/$P_NEW" || return 1 fi # # Update the dependency information of ports that depend on this one. # if ! $REBUILD; then pkg_info -R "$P_NEW" \ | egrep '^[^ ]+$' \ | while read P; do Message "Updating dependency information for ${P}." PARCON="$PKG_DBDIR/$P/+CONTENTS" if [ ! -d "${PARCON%/*}" ]; then Message -b "WARNING: No such port registered!" continue fi if [ ! -f "$PARCON" ]; then Message -b "WARNING: Port has no +CONTENTS file!" continue fi if fgrep -qx "@pkgdep $P_OLD" "$PARCON"; then rm -f "$PARCON".new awk '{ if ($0 == "@pkgdep '"$P_OLD"'") print "@pkgdep '"$P_NEW"'" else print $0 }' "$PARCON" > "$PARCON".new if [ ! -f "$PARCON".new ]; then Message -b "WARNING: Cannot create new +CONTENTS file!" continue fi if [ $? -eq 0 -a -s "$PARCON".new ] && \ [ $(wc -l < "$PARCON".new) -eq $(wc -l < "$PARCON") ] && \ ! diff -q "$PARCON".new "$PARCON" >/dev/null ; then mv -f "$PARCON".new "$PARCON" \ || Message -b "WARNING: Cannot replace +CONTENTS file!" else Message -b "WARNING: Cannot generate new +CONTENTS file!" rm -f "$PARCON".new fi else Message -b "WARNING: $P_OLD is not recorded as dependecy!" fi done fi # ! $REBUILD # # Finally clean up (unless we were instructed not to). # if ! $NOPORTS && ! $NOCLEAN; then make clean fi Message -b "Successfully done." # # End of the "Portup" function. # } Portup_All() { # # Call the "Portup" function for every command line argument. # If there's more than one, manage lists of failed and successfully # built ports. # if [ $# -le 1 ]; then # Just one port/package, no need to manage lists. MULTIPORTS=false Portup "$1" else # Two or more ports/packages. MULTIPORTS=true PORTS_GOOD="" PORTS_FAIL="" PORTS_NONE="" BREAK=false for i in "$@"; do if $BREAK; then PORTS_NONE="$PORTS_NONE $i" else if Portup "$i"; then PORTS_GOOD="$PORTS_GOOD $i" else PORTS_FAIL="$PORTS_FAIL $i" if $BRKFAIL; then BREAK=true fi fi fi done echo "" >&${TTY:-2} if [ -z "$PORTS_FAIL" ]; then Message -b "All ports successfully done." exit 0 else echo "The following ports were updated successfully:" >&${TTY:-2} if [ -z "$PORTS_GOOD" ]; then echo " - none -" >&${TTY:-2} else for i in $PORTS_GOOD; do echo " $i" >&${TTY:-2} done fi echo "The following ports failed to update:" >&${TTY:-2} for i in $PORTS_FAIL; do echo " $i" >&${TTY:-2} done if [ -n "$PORTS_NONE" ]; then echo "The following ports were skipped because preceding ones failed:" >&${TTY:-2} for i in $PORTS_NONE; do echo " $i" >&${TTY:-2} done fi Die -b "One or more ports failed to update." fi exit 0 # Shouldn't be reached. fi } if [ "${_PORTUP_ACTIVE}" != "${_PORTUP_ACTIVE#[1-9]}" ]; then # # OK, we're running as a shell process inside window(1). # Next we need to find out whether we're running in the # progress window (that's the one at the top) or in the # build window (the bottom one). To do that, we simply # look at the number of rows we have. # export SHELL=/bin/sh if [ $win_rows -eq ${_PORTUP_ACTIVE} ]; then # # We're in the progress window. Just keep # reading lines from the FIFO and display # them appropriately. # awk ' BEGIN { #tc_home = ENVIRON["TC_HOME"] print "" } { print # fflush() } ' "$WINDOW_FIFO" elif [ -z "$_PORTUP_SCRIPT" ]; then # # We're in the build window. Run the actual # build commands and filter the output while # feeding the FIFO. # # When done (or interrupted), send a SIGHUP # to our parent which is the window(1) process. # my_pid=$$ parent=$(ps -alx | awk '($2=="'$my_pid'"){print $3; exit}') Cancel() { echo "C" > "${WINDOW_FIFO%/*}/exitcode" kill -HUP $parent sleep 0.5 exit 0 } trap Cancel 1 2 3 15 awk ' BEGIN { fifo = "'"$WINDOW_FIFO"'" swin = "'"$SWINDOW"'" } { if (/^\^\b[A-Z][a-z]+ing /) print substr($0, 3) > fifo if (swin > 1 && /^===> [A-Z][a-z]+ing for / \ && $2 ~ /^(Config|Build|Inst)/) print > fifo } ' < "$SCRIPT_FIFO" & export _PORTUP_SCRIPT=YES # Arguments are passed via $_PORTUP_ARGS. script -qt0 "$SCRIPT_FIFO" "$0" echo "$?" >> "${WINDOW_FIFO%/*}/exitcode" sleep 0.1 trap 1 2 3 15 kill -HUP $parent else # Running within /usr/bin/script. Portup_All "$@" exit $? fi # # We cannot exit right away here, because window(1) # prompts for a command when a process exits. We # do not want that. Therefore we wait in a small # loop. We will either get killed when window(1) # terminates, or -- just to be safe -- we exit when # the FIFO doesn't exist any longer. # # Actually the time window is very small, so the # loop body probably won't be executed more than # once. Therefore this kind of "polling" isn't as # bad as it might look. ;-) # while :; do if [ ! -e "$WINDOW_FIFO" ]; then exit 0 fi sleep 1 done fi if [ $SWINDOW -gt 0 ]; then # # Window mode (split-srceen) requested: # Run the window(1) utility, restarting the script inside. # if ! type window >/dev/null 2>&1; then Message "Hint: The window tool can be installed from /usr/ports/misc/window." Die -b "Can't find the window tool (required by the -w option)." fi # # First check the terminal size, then create the FIFO # for communication between the slave processes. # if [ $win_rows -lt 24 ]; then Die -b "Terminal is too small, need at least 24 rows!" fi prog_rows=$(( win_rows / 5 )) if [ $prog_rows -lt 4 ]; then prog_rows=4 fi fifo_dir=$(mktemp -d "/tmp/${ME}.tmp.$$.XXXXXX") Cleanup() { rm -rf "$fifo_dir" exit 1 } trap Cleanup 1 2 3 15 export WINDOW_FIFO="$fifo_dir/wfifo" export SCRIPT_FIFO="$fifo_dir/sfifo" mkfifo "$WINDOW_FIFO" "$SCRIPT_FIFO" # # Now we're ready for action. # unset _PORTUP_SCRIPT export _PORTUP_ACTIVE=$prog_rows SHELL="$0" window -t -f -c "cursormodes(0);window(0,0,$prog_rows);window($(( prog_rows + 1 )),0)" exit_code=$(head -1 "$fifo_dir/exitcode") # # Clean up and exit. # rm -rf "$fifo_dir" if [ "$exit_code" = C ]; then echo "${ME} cancelled." exit_code=1 #elif [ "${exit_code:-0}" = 0 ]; then # echo "${ME} finished successfully." #else # echo "${ME} terminated with exit code ${exit_code}." fi exit ${exit_code:-0} else # # Normal mode (not split-screen). # Portup_All "$@" fi #--