#!/bin/echo "This file is sourced, not run" # This file contains generic functions, presumably reusable in other contexts. # Create a blank directory at first argument, deleting existing contents if any proc blank_tempdir { # sanity test: never rm -rf something we don't own. test -z $1 && dienow touch -c $1 || dienow # Delete old directory, create new one. test -z $NO_CLEANUP && rm -rf $1 mkdir -p $1 || dienow } # output the sha1sum of a file proc sha1file { sha1sum /dev/null @ARGV | tail -n +2 | awk '{print $1}' } # dienow() is an exit function that works properly even from a subshell. # (actually_dienow is run in the parent shell via signal handler.) proc actually_dienow { echo -e "\n\e[31mExiting due to errors ($ARCH_NAME $STAGE_NAME $PACKAGE)\e[0m" >&2 exit 1 } trap actually_dienow SIGUSR1 setvar TOPSHELL = ""$$ proc dienow { kill -USR1 $TOPSHELL exit 1 } # Turn a bunch of output lines into a much quieter series of periods, # roughly one per screenfull proc dotprogress { setvar x = '0' while read i { setvar x = $[$x + 1] if [[ "$x" -eq 25 ]] { setvar x = '0' echo -n . } } echo } # Announce an action to the world proc announce { # Write a line to the log file with easily greppable header echo "=== $1 ($ARCH_NAME $STAGE_NAME)" # Set the title bar of the current xterm test -z $NO_TITLE_BAR && echo -en "\033]2;$ARCH_NAME $STAGE_NAME $1\007" } # Filter out unnecessary noise, keeping just lines starting with "===" proc maybe_quiet { test -z $FORK && cat || grep "^===" } # Run a command background if FORK is set, in foreground otherwise proc maybe_fork { test -z $BUILD_VERBOSE || echo "$[join(ARGV)]" if test -z $FORK { eval "$[join(ARGV)]" } else { eval "$[join(ARGV)]" & } } # Kill a process and all its decendants proc killtree { local KIDS="" while [ $# -ne 0 ] { setvar KIDS = ""$KIDS $(pgrep -P$1)"" shift } setvar KIDS = "$(echo -n $KIDS)" if test ! -z $KIDS { # Depth first kill avoids reparent_to_init hiding stuff. killtree $KIDS kill $KIDS 2>/dev/null } } # Search a colon-separated path for files matching a pattern. # Arguments are 1) path to search, 2) pattern, 3) command to run on each file. # During command, $DIR/$FILE points to file found. proc path_search { # For each each $PATH element, loop through each file in that directory, # and create a symlink to the wrapper with that name. In the case of # duplicates, keep the first one. echo $1 | sed 's/:/\n/g' | while read DIR { find "$DIR/" -maxdepth 1 -mindepth 1 -name $2 | sed 's@.*/@@' | \ while read FILE { eval $3 # Output is verbose. Pipe it to dotprogress. echo $FILE } } } # Abort if we haven't got a prerequisite in the $PATH proc check_prerequisite { if test -z $(which "$1") { test -z $FAIL_QUIET && echo No $1 in '$PATH'. >&2 dienow } } # Search through all the files under a directory and collapse together # identical files into hardlinks proc collapse_hardlinks { setvar SHA1LIST = """" find $1 -type f | while read FILE { echo $FILE setvar SHA1 = $(sha1file "$FILE") setvar MATCH = $(echo "$SHA1LIST" | grep "^$SHA1") if test -z $MATCH { # Yes, the quote spanning two lines here is intentional setvar SHA1LIST = ""$SHA1LIST $SHA1 $FILE"" } else { setvar FILE2 = "$(echo "$MATCH" | sed 's/[^ ]* //')" cmp -s $FILE $FILE2 || continue ln -f $FILE $FILE2 || dienow } } } # Check if $1 is in the comma separated list $2 proc is_in_list { test $2 == all || test ! -z $(echo ,"$2", | grep ,"$1",) }