diff options
Diffstat (limited to 'scripts/make.sh')
-rwxr-xr-x | scripts/make.sh | 247 |
1 files changed, 135 insertions, 112 deletions
diff --git a/scripts/make.sh b/scripts/make.sh index b7bb6930..9de74192 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -1,83 +1,105 @@ #!/bin/bash # Grab default values for $CFLAGS and such. - set -o pipefail source scripts/portability.sh -# Default to running one more parallel cc instance than we have processors -: ${CPUS:=$(($(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null)+1))} +# Shell functions called by the build + +DASHN=-n +true & wait -n 2>/dev/null || { wait; unset DASHN; } +ratelimit() +{ + if [ "$#" -eq 0 ] + then + [ -z "$DASHN" ] && PIDS="$PIDS$! " + [ $((++COUNT)) -lt $CPUS ] && return 0 + fi + ((--COUNT)) + if [ -n "$DASHN" ] + then + wait -n + DONE=$(($DONE+$?)) + else + # MacOS uses an ancient version of bash which hasn't got "wait -n", and + # wait without arguments always returns 0 instead of process exit code. + # This keeps $CPUS less busy when jobs finish out of order. + wait ${PIDS%% *} + DONE=$(($DONE+$?)) + PIDS=${PIDS#* } + fi + + return $DONE +} # Respond to V= by echoing command lines as well as running them -DOTPROG= do_loudly() { - [ ! -z "$V" ] && echo "$@" || echo -n "$DOTPROG" + { [ -n "$V" ] && echo "$@" || echo -n "$DOTPROG" ; } >&2 "$@" } -# Is anything under directory $2 newer than file $1 +# Is anything under directory $2 newer than generated/$1 (or does it not exist)? isnewer() { - CHECK="$1" - shift - [ ! -z "$(find "$@" -newer "$CHECK" 2>/dev/null || echo yes)" ] + [ -e "$GENDIR/$1" ] && [ -z "$(find "${@:2}" -newer "$GENDIR/$1")" ] && + return 1 + echo -n "${DIDNEWER:-$GENDIR/{}$1" + DIDNEWER=, } -echo "Generate headers from toys/*/*.c..." - -mkdir -p "$UNSTRIPPED" - -if isnewer "$GENDIR"/Config.in toys || isnewer "$GENDIR"/Config.in Config.in -then - echo "Extract configuration information from toys/*.c files..." - scripts/genconfig.sh -fi +# Build a tool that runs on the host +hostcomp() +{ + if [ ! -f "$UNSTRIPPED"/$1 ] || [ "$UNSTRIPPED"/$1 -ot scripts/$1.c ] + then + do_loudly $HOSTCC scripts/$1.c -o "$UNSTRIPPED"/$1 || exit 1 + fi +} -# Create a list of all the commands toybox can provide. Note that the first -# entry is out of order on purpose (the toybox multiplexer command must be the -# first element of the array). The rest must be sorted in alphabetical order -# for fast binary search. +# Set/record build environment information +compflags() +{ + [ -z "$VERSION" ] && [ -d ".git" ] && [ -n "$(which git 2>/dev/null)" ] && + VERSION="-DTOYBOX_VERSION=\"$(git describe --tags --abbrev=12 2>/dev/null)\"" -if isnewer "$GENDIR"/newtoys.h toys -then - echo -n "$GENDIR/newtoys.h " + # VERSION and LIBRARIES volatile, changing either does not require a rebuild + echo '#!/bin/sh' + echo + echo "VERSION='$VERSION'" + echo "LIBRARIES='$(xargs 2>/dev/null < "$GENDIR/optlibs.dat")'" + echo "BUILD='${CROSS_COMPILE}${CC} $CFLAGS -I . $OPTIMIZE '\"\$VERSION\"" + echo "LINK='$LDOPTIMIZE $LDFLAGS '\"\$LIBRARIES\"" + echo "PATH='$PATH'" + echo "# Built from $KCONFIG_CONFIG" + echo +} - echo "USE_TOYBOX(NEWTOY(toybox, NULL, TOYFLAG_STAYROOT|TOYFLAG_NOHELP))" \ - > "$GENDIR"/newtoys.h - $SED -n -e 's/^USE_[A-Z0-9_]*(/&/p' toys/*/*.c \ - | $SED 's/\(.*TOY(\)\([^,]*\),\(.*\)/\2 \1\2,\3/' | sort -s -k 1,1 \ - | $SED 's/[^ ]* //' >> "$GENDIR"/newtoys.h - [ $? -ne 0 ] && exit 1 -fi +# Make sure rm -rf isn't gonna go funny +B="$(readlink -f "$PWD")/" A="$(readlink -f "$GENDIR")" A="${A%/}"/ +[ "$A" == "${B::${#A}}" ] && + { echo "\$GENDIR=$GENDIR cannot include \$PWD=$PWD"; exit 1; } +unset A B DOTPROG DIDNEWER -[ ! -z "$V" ] && echo "Which C files to build..." +# Force full rebuild if our compiler/linker options changed +cmp -s <(compflags|sed '5,8!d') <($SED '5,8!d' "$GENDIR"/build.sh 2>/dev/null)|| + rm -rf "$GENDIR"/* # Keep symlink, delete contents +mkdir -p "$UNSTRIPPED" "$(dirname $OUTNAME)" || exit 1 # Extract a list of toys/*/*.c files to compile from the data in $KCONFIG_CONFIG # (First command names, then filenames with relevant {NEW,OLD}TOY() macro.) -[ -d ".git" ] && [ ! -z "$(which git 2>/dev/null)" ] && - GITHASH="-DTOYBOX_VERSION=\"$(git describe --tags --abbrev=12 2>/dev/null)\"" +[ -n "$V" ] && echo -e "\nWhich C files to build..." TOYFILES="$($SED -n 's/^CONFIG_\([^=]*\)=.*/\1/p' "$KCONFIG_CONFIG" | xargs | tr ' [A-Z]' '|[a-z]')" TOYFILES="main.c $(egrep -l "TOY[(]($TOYFILES)[ ,]" toys/*/*.c | xargs)" -BUILD="$(echo ${CROSS_COMPILE}${CC} $CFLAGS -I . $OPTIMIZE $GITHASH)" if [ "${TOYFILES/pending//}" != "$TOYFILES" ] then echo -e "\n\033[1;31mwarning: using unfinished code from toys/pending\033[0m" fi -genbuildsh() -{ - # Write a canned build line for use on crippled build machines. - - LLINK="$(echo $LDOPTIMIZE $LDFLAGS $(cat "$GENDIR"/optlibs.dat))" - echo -e "#!/bin/sh\n\nPATH='$PATH'\nBUILD='$BUILD'\nLINK='$LLINK'\n" - echo -e "\$BUILD lib/*.c $TOYFILES \$LINK -o $OUTNAME" -} - -if ! cmp -s <(genbuildsh 2>/dev/null | head -n 5) \ - <(head -n 5 "$GENDIR"/build.sh 2>/dev/null | $SED '5s/ -o .*//') +# Probe library list if our compiler/linker options changed +if [ ! -e "$GENDIR"/optlibs.dat ] then echo -n "Library probe" @@ -85,25 +107,53 @@ then # compiler has no way to ignore a library that doesn't exist, so detect # and skip nonexistent libraries for it. - > "$GENDIR"/optlibs.dat + > "$GENDIR"/optlibs.new + [ -z "$V" ] && X=/dev/null || X=/dev/stderr for i in util crypt m resolv selinux smack attr crypto z log iconv tls ssl do - echo "int main(int argc, char *argv[]) {return 0;}" | \ - ${CROSS_COMPILE}${CC} $CFLAGS $LDFLAGS -xc - -o "$GENDIR"/libprobe -l$i > /dev/null 2>/dev/null && - echo -l$i >> "$GENDIR"/optlibs.dat - echo -n . + do_loudly ${CROSS_COMPILE}${CC} $CFLAGS $LDFLAGS -xc - -l$i >>$X 2>&1 \ + -o "$UNSTRIPPED"/libprobe <<<"int main(int argc,char*argv[]){return 0;}"&& + do_loudly echo -n ' '-l$i >> "$GENDIR"/optlibs.new done - rm -f "$GENDIR"/libprobe + unset X + rm -f "$UNSTRIPPED"/libprobe + mv "$GENDIR"/optlibs.{new,dat} || exit 1 echo fi -genbuildsh > "$GENDIR"/build.sh && chmod +x "$GENDIR"/build.sh || exit 1 +# Write build variables (and set them locally), then append build invocation. +compflags > "$GENDIR"/build.sh && source "$GENDIR/build.sh" && + echo -e "\$BUILD lib/*.c $TOYFILES \$LINK -o $OUTNAME" >> "$GENDIR"/build.sh&& + chmod +x "$GENDIR"/build.sh || exit 1 -#TODO: "make $SED && make" doesn't regenerate config.h because diff .config -if true #isnewer "$GENDIR"/config.h "$KCONFIG_CONFIG" +if isnewer Config.in toys || isnewer Config.in Config.in then - echo "Make $GENDIR/config.h from $KCONFIG_CONFIG." + scripts/genconfig.sh +fi + +# Does .config need dependency recalculation because toolchain changed? +A="$($SED -n '/^config .*$/h;s/default \(.\)/\1/;T;H;g;s/config \([^\n]*\)[^yn]*\(.\)/\1=\2/p' "$GENDIR"/Config.probed | sort)" +B="$(egrep "^CONFIG_($(echo "$A" | sed 's/=[yn]//' | xargs | tr ' ' '|'))=" "$KCONFIG_CONFIG" | $SED 's/^CONFIG_//' | sort)" +A="$(echo "$A" | grep -v =n)" +[ "$A" != "$B" ] && + { echo -e "\nWarning: Config.probed changed, run 'make oldconfig'" >&2; } +unset A B + +# Create a list of all the commands toybox can provide. +if isnewer newtoys.h toys +then + # The multiplexer is the first element in the array + echo "USE_TOYBOX(NEWTOY(toybox, 0, TOYFLAG_STAYROOT|TOYFLAG_NOHELP))" \ + > "$GENDIR"/newtoys.h + # Sort rest by name for binary search (copy name to front, sort, remove copy) + $SED -n 's/^\(USE_[^(]*(.*TOY(\)\([^,]*\)\(,.*\)/\2 \1\2\3/p' toys/*/*.c \ + | sort -s -k 1,1 | $SED 's/[^ ]* //' >> "$GENDIR"/newtoys.h + [ $? -ne 0 ] && exit 1 +fi +#TODO: "make $SED && make" doesn't regenerate config.h because diff .config +if true #isnewer config.h "$KCONFIG_CONFIG" +then # This long and roundabout sed invocation is to make old versions of sed # happy. New ones have '\n' so can replace one line with two without all # the branches and tedious mucking about with hyperspace. @@ -130,20 +180,14 @@ then $KCONFIG_CONFIG > "$GENDIR"/config.h || exit 1 fi -if [ ! -f "$GENDIR"/mkflags ] || [ "$GENDIR"/mkflags -ot scripts/mkflags.c ] -then - do_loudly $HOSTCC scripts/mkflags.c -o "$UNSTRIPPED"/mkflags || exit 1 -fi - # Process config.h and newtoys.h to generate FLAG_x macros. Note we must # always #define the relevant macro, even when it's disabled, because we # allow multiple NEWTOY() in the same C file. (When disabled the FLAG is 0, # so flags&0 becomes a constant 0 allowing dead code elimination.) -if isnewer "$GENDIR"/flags.h toys "$KCONFIG_CONFIG" +hostcomp mkflags +if isnewer flags.h toys "$KCONFIG_CONFIG" then - echo -n "$GENDIR/flags.h " - # Parse files through C preprocessor twice, once to get flags for current # .config and once to get flags for allyesconfig for I in A B @@ -187,18 +231,16 @@ function getglobals() { for i in toys/*/*.c do - # alas basename -s isn't in posix yet. - NAME="$(echo $i | $SED 's@.*/\(.*\)\.c@\1@')" + NAME=${i##*/} NAME=${NAME%\.c} DATA="$($SED -n -e '/^GLOBALS(/,/^)/b got;b;:got' \ -e 's/^GLOBALS(/_data {/' \ -e 's/^)/};/' -e 'p' $i)" - [ ! -z "$DATA" ] && echo -e "// $i\n\nstruct $NAME$DATA\n" + [ -n "$DATA" ] && echo -e "// $i\n\nstruct $NAME$DATA\n" done } -if isnewer "$GENDIR"/globals.h toys +if isnewer globals.h toys then - echo -n "$GENDIR/globals.h " GLOBSTRUCT="$(getglobals)" ( echo "$GLOBSTRUCT" @@ -210,53 +252,40 @@ then ) > "$GENDIR"/globals.h fi -if [ ! -f "$UNSTRIPPED"/mktags ] || [ "$UNSTRIPPED"/mktags -ot scripts/mktags.c ] -then - do_loudly $HOSTCC scripts/mktags.c -o "$UNSTRIPPED"/mktags || exit 1 -fi - -if isnewer "$GENDIR"/tags.h toys +hostcomp mktags +if isnewer tags.h toys then - echo -n "$GENDIR/tags.h " - $SED -n '/TAGGED_ARRAY(/,/^)/{s/.*TAGGED_ARRAY[(]\([^,]*\),/\1/;p}' \ toys/*/*.c lib/*.c | "$UNSTRIPPED"/mktags > "$GENDIR"/tags.h fi -if [ ! -f "$UNSTRIPPED"/config2help ] || [ "$UNSTRIPPED"/config2help -ot scripts/config2help.c ] +hostcomp config2help +if isnewer help.h "$GENDIR"/Config.in then - do_loudly $HOSTCC scripts/config2help.c -o "$UNSTRIPPED"/config2help || exit 1 -fi -if isnewer "$GENDIR"/help.h "$GENDIR"/Config.in -then - echo "$GENDIR/help.h" "$UNSTRIPPED"/config2help Config.in $KCONFIG_CONFIG > "$GENDIR"/help.h || exit 1 fi +[ -z "$DIDNEWER" ] || echo } -[ ! -z "$NOBUILD" ] && exit 0 +[ -n "$NOBUILD" ] && exit 0 -echo -n "Compile $OUTNAME" -[ ! -z "$V" ] && echo +echo "Compile $OUTNAME" DOTPROG=. -# This is a parallel version of: do_loudly $BUILD $FILES $LLINK || exit 1 +# This is a parallel version of: do_loudly $BUILD lib/*.c $TOYFILES $LINK -# Any headers newer than the oldest generated/obj file? +# Build all if oldest generated/obj file isn't newer than all header files. X="$(ls -1t "$GENDIR"/obj/* 2>/dev/null | tail -n 1)" -# TODO: redo this -if [ ! -e "$X" ] || [ ! -z "$(find toys -name "*.h" -newer "$X")" ] +if [ ! -e "$X" ] || [ -n "$(find toys -name "*.h" -newer "$X")" ] then rm -rf "$GENDIR"/obj && mkdir -p "$GENDIR"/obj || exit 1 else + # always redo toy_list[] and help_data[] rm -f "$GENDIR"/obj/main.o || exit 1 fi # build each generated/obj/*.o file in parallel -unset PENDING LNKFILES CLICK -DONE=0 -COUNT=0 - +PENDING= LNKFILES= CLICK= DONE=0 COUNT=0 for i in lib/*.c click $TOYFILES do [ "$i" == click ] && CLICK=1 && continue @@ -266,30 +295,25 @@ do OUT="$GENDIR/obj/${X%%.c}.o" LNKFILES="$LNKFILES $OUT" - # Library files don't need to be rebuilt if older than .config. - # ($TOYFILES contents can depend on CONFIG symbols, lib/*.c never should.) - + # Library files don't get rebuilt if older than .config, but commands do. [ "$OUT" -nt "$i" ] && [ -z "$CLICK" -o "$OUT" -nt "$KCONFIG_CONFIG" ] && continue do_loudly $BUILD -c $i -o $OUT & - # ratelimit to $CPUS many parallel jobs, detecting errors - [ $((++COUNT)) -ge $CPUS ] && { wait $DASHN; DONE=$?; : $((--COUNT)); } - [ $DONE -ne 0 ] && break + ratelimit || break done -# wait for all background jobs, detecting errors -while [ $((COUNT--)) -gt 0 ] +# wait for all background jobs, detecting errors +while [ "$COUNT" -gt 0 ] do - wait $DASHN; - DONE=$((DONE+$?)) + ratelimit done done [ $DONE -ne 0 ] && exit 1 UNSTRIPPED="$UNSTRIPPED/${OUTNAME/*\//}" -do_loudly $BUILD $LNKFILES $LLINK -o "$UNSTRIPPED" || exit 1 -if [ ! -z "$NOSTRIP" ] || +do_loudly $BUILD $LNKFILES $LINK -o "$UNSTRIPPED" || exit 1 +if [ -n "$NOSTRIP" ] || ! do_loudly ${CROSS_COMPILE}${STRIP} "$UNSTRIPPED" -o "$OUTNAME" then [ -z "$NOSTRIP" ] && echo "strip failed, using unstripped" @@ -297,10 +321,9 @@ then cp "$UNSTRIPPED" "$OUTNAME" || exit 1 fi -# gcc 4.4's strip command is buggy, and doesn't set the executable bit on -# its output the way SUSv4 suggests it do so. While we're at it, make sure -# we don't have the "w" bit set so things like bzip2's "cp -f" install don't -# overwrite our binary through the symlink. +# Remove write bit set so buggy installs (like bzip's) don't overwrite the +# multiplexer binary via truncate-and-write through a symlink. do_loudly chmod 555 "$OUTNAME" || exit 1 -echo +# Ensure make wrapper sees success return code +[ -z "$V" ] && echo >&2 || true |