1#!/bin/bash 2 3# Grab default values for $CFLAGS and such. 4set -o pipefail 5source scripts/portability.sh 6 7# Shell functions called by the build 8 9DASHN=-n 10true & wait -n 2>/dev/null || { wait; unset DASHN; } 11ratelimit() 12{ 13 if [ "$#" -eq 0 ] 14 then 15 [ -z "$DASHN" ] && PIDS="$PIDS$! " 16 [ $((++COUNT)) -lt $CPUS ] && return 0 17 fi 18 ((--COUNT)) 19 if [ -n "$DASHN" ] 20 then 21 wait -n 22 DONE=$(($DONE+$?)) 23 else 24 # MacOS uses an ancient version of bash which hasn't got "wait -n", and 25 # wait without arguments always returns 0 instead of process exit code. 26 # This keeps $CPUS less busy when jobs finish out of order. 27 wait ${PIDS%% *} 28 DONE=$(($DONE+$?)) 29 PIDS=${PIDS#* } 30 fi 31 32 return $DONE 33} 34 35# Respond to V= by echoing command lines as well as running them 36do_loudly() 37{ 38 { [ -n "$V" ] && echo "$@" || echo -n "$DOTPROG" ; } >&2 39 "$@" 40} 41 42# Is anything under directory $2 newer than generated/$1 (or does it not exist)? 43isnewer() 44{ 45 [ -e "$GENDIR/$1" ] && [ -z "$(find "${@:2}" -newer "$GENDIR/$1")" ] && 46 return 1 47 echo -n "${DIDNEWER:-$GENDIR/{}$1" 48 DIDNEWER=, 49} 50 51# Build a tool that runs on the host 52hostcomp() 53{ 54 if [ ! -f "$UNSTRIPPED"/$1 ] || [ "$UNSTRIPPED"/$1 -ot scripts/$1.c ] 55 then 56 do_loudly $HOSTCC scripts/$1.c -o "$UNSTRIPPED"/$1 || exit 1 57 fi 58} 59 60# Set/record build environment information 61compflags() 62{ 63 [ -z "$VERSION" ] && [ -d ".git" ] && [ -n "$(which git 2>/dev/null)" ] && 64 VERSION="-DTOYBOX_VERSION=\"$(git describe --tags --abbrev=12 2>/dev/null)\"" 65 66 # VERSION and LIBRARIES volatile, changing either does not require a rebuild 67 echo '#!/bin/sh' 68 echo 69 echo "VERSION='$VERSION'" 70 echo "LIBRARIES='$(xargs 2>/dev/null < "$GENDIR/optlibs.dat")'" 71 echo "BUILD='${CROSS_COMPILE}${CC} $CFLAGS -I . $OPTIMIZE '\"\$VERSION\"" 72 echo "LINK='$LDOPTIMIZE $LDFLAGS '\"\$LIBRARIES\"" 73 echo "PATH='$PATH'" 74 echo "# Built from $KCONFIG_CONFIG" 75 echo 76} 77 78# Make sure rm -rf isn't gonna go funny 79B="$(readlink -f "$PWD")/" A="$(readlink -f "$GENDIR")" A="${A%/}"/ 80[ "$A" == "${B::${#A}}" ] && 81 { echo "\$GENDIR=$GENDIR cannot include \$PWD=$PWD"; exit 1; } 82unset A B DOTPROG DIDNEWER 83 84# Force full rebuild if our compiler/linker options changed 85cmp -s <(compflags|sed '5,8!d') <($SED '5,8!d' "$GENDIR"/build.sh 2>/dev/null)|| 86 rm -rf "$GENDIR"/* # Keep symlink, delete contents 87mkdir -p "$UNSTRIPPED" "$(dirname $OUTNAME)" || exit 1 88 89# Extract a list of toys/*/*.c files to compile from the data in $KCONFIG_CONFIG 90# (First command names, then filenames with relevant {NEW,OLD}TOY() macro.) 91 92[ -n "$V" ] && echo -e "\nWhich C files to build..." 93TOYFILES="$($SED -n 's/^CONFIG_\([^=]*\)=.*/\1/p' "$KCONFIG_CONFIG" | xargs | tr ' [A-Z]' '|[a-z]')" 94TOYFILES="main.c $(egrep -l "TOY[(]($TOYFILES)[ ,]" toys/*/*.c | xargs)" 95 96if [ "${TOYFILES/pending//}" != "$TOYFILES" ] 97then 98 echo -e "\n\033[1;31mwarning: using unfinished code from toys/pending\033[0m" 99fi 100 101# Probe library list if our compiler/linker options changed 102if [ ! -e "$GENDIR"/optlibs.dat ] 103then 104 echo -n "Library probe" 105 106 # --as-needed removes libraries we don't use any symbols out of, but the 107 # compiler has no way to ignore a library that doesn't exist, so detect 108 # and skip nonexistent libraries for it. 109 110 > "$GENDIR"/optlibs.new 111 [ -z "$V" ] && X=/dev/null || X=/dev/stderr 112 for i in util crypt m resolv selinux smack attr crypto z log iconv tls ssl 113 do 114 do_loudly ${CROSS_COMPILE}${CC} $CFLAGS $LDFLAGS -xc - -l$i >>$X 2>&1 \ 115 -o "$UNSTRIPPED"/libprobe <<<"int main(int argc,char*argv[]){return 0;}"&& 116 do_loudly echo -n ' '-l$i >> "$GENDIR"/optlibs.new 117 done 118 unset X 119 rm -f "$UNSTRIPPED"/libprobe 120 mv "$GENDIR"/optlibs.{new,dat} || exit 1 121 echo 122fi 123 124# Write build variables (and set them locally), then append build invocation. 125compflags > "$GENDIR"/build.sh && source "$GENDIR/build.sh" && 126 echo -e "\$BUILD lib/*.c $TOYFILES \$LINK -o $OUTNAME" >> "$GENDIR"/build.sh&& 127 chmod +x "$GENDIR"/build.sh || exit 1 128 129if isnewer Config.in toys || isnewer Config.in Config.in 130then 131 scripts/genconfig.sh 132fi 133 134# Does .config need dependency recalculation because toolchain changed? 135A="$($SED -n '/^config .*$/h;s/default \(.\)/\1/;T;H;g;s/config \([^\n]*\)[^yn]*\(.\)/\1=\2/p' "$GENDIR"/Config.probed | sort)" 136B="$(egrep "^CONFIG_($(echo "$A" | sed 's/=[yn]//' | xargs | tr ' ' '|'))=" "$KCONFIG_CONFIG" | $SED 's/^CONFIG_//' | sort)" 137A="$(echo "$A" | grep -v =n)" 138[ "$A" != "$B" ] && 139 { echo -e "\nWarning: Config.probed changed, run 'make oldconfig'" >&2; } 140unset A B 141 142# Create a list of all the commands toybox can provide. 143if isnewer newtoys.h toys 144then 145 # The multiplexer is the first element in the array 146 echo "USE_TOYBOX(NEWTOY(toybox, 0, TOYFLAG_STAYROOT|TOYFLAG_NOHELP))" \ 147 > "$GENDIR"/newtoys.h 148 # Sort rest by name for binary search (copy name to front, sort, remove copy) 149 $SED -n 's/^\(USE_[^(]*(.*TOY(\)\([^,]*\)\(,.*\)/\2 \1\2\3/p' toys/*/*.c \ 150 | sort -s -k 1,1 | $SED 's/[^ ]* //' >> "$GENDIR"/newtoys.h 151 [ $? -ne 0 ] && exit 1 152fi 153 154#TODO: "make $SED && make" doesn't regenerate config.h because diff .config 155if true #isnewer config.h "$KCONFIG_CONFIG" 156then 157 # This long and roundabout sed invocation is to make old versions of sed 158 # happy. New ones have '\n' so can replace one line with two without all 159 # the branches and tedious mucking about with hyperspace. 160 # TODO: clean this up to use modern stuff. 161 162 $SED -n \ 163 -e 's/^# CONFIG_\(.*\) is not set.*/\1/' \ 164 -e 't notset' \ 165 -e 's/^CONFIG_\(.*\)=y.*/\1/' \ 166 -e 't isset' \ 167 -e 's/^CONFIG_\([^=]*\)=\(.*\)/#define CFG_\1 \2/p' \ 168 -e 'd' \ 169 -e ':notset' \ 170 -e 'h' \ 171 -e 's/.*/#define CFG_& 0/p' \ 172 -e 'g' \ 173 -e 's/.*/#define USE_&(...)/p' \ 174 -e 'd' \ 175 -e ':isset' \ 176 -e 'h' \ 177 -e 's/.*/#define CFG_& 1/p' \ 178 -e 'g' \ 179 -e 's/.*/#define USE_&(...) __VA_ARGS__/p' \ 180 $KCONFIG_CONFIG > "$GENDIR"/config.h || exit 1 181fi 182 183# Process config.h and newtoys.h to generate FLAG_x macros. Note we must 184# always #define the relevant macro, even when it's disabled, because we 185# allow multiple NEWTOY() in the same C file. (When disabled the FLAG is 0, 186# so flags&0 becomes a constant 0 allowing dead code elimination.) 187 188hostcomp mkflags 189if isnewer flags.h toys "$KCONFIG_CONFIG" 190then 191 # Parse files through C preprocessor twice, once to get flags for current 192 # .config and once to get flags for allyesconfig 193 for I in A B 194 do 195 ( 196 # define macros and select header files with option string data 197 198 echo "#define NEWTOY(aa,bb,cc) aa $I bb" 199 echo '#define OLDTOY(...)' 200 if [ "$I" == A ] 201 then 202 cat "$GENDIR"/config.h 203 else 204 $SED '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' "$GENDIR"/config.h 205 fi 206 echo '#include "lib/toyflags.h"' 207 cat "$GENDIR"/newtoys.h 208 209 # Run result through preprocessor, glue together " " gaps leftover from USE 210 # macros, delete comment lines, print any line with a quoted optstring, 211 # turn any non-quoted opstring (NULL or 0) into " " (because fscanf can't 212 # handle "" with nothing in it, and mkflags uses that). 213 214 ) | ${CROSS_COMPILE}${CC} -E - | \ 215 $SED -n -e 's/" *"//g;/^#/d;t clear;:clear;s/"/"/p;t;s/\( [AB] \).*/\1 " "/p' 216 217 # Sort resulting line pairs and glue them together into triplets of 218 # command "flags" "allflags" 219 # to feed into mkflags C program that outputs actual flag macros 220 # If no pair (because command's disabled in config), use " " for flags 221 # so allflags can define the appropriate zero macros. 222 223 done | sort -s | $SED -n -e 's/ A / /;t pair;h;s/\([^ ]*\).*/\1 " "/;x' \ 224 -e 'b single;:pair;h;n;:single;s/[^ ]* B //;H;g;s/\n/ /;p' | \ 225 tee "$GENDIR"/flags.raw | "$UNSTRIPPED"/mkflags > "$GENDIR"/flags.h || exit 1 226fi 227 228# Extract global structure definitions and flag definitions from toys/*/*.c 229 230function getglobals() 231{ 232 for i in toys/*/*.c 233 do 234 NAME=${i##*/} NAME=${NAME%\.c} 235 DATA="$($SED -n -e '/^GLOBALS(/,/^)/b got;b;:got' \ 236 -e 's/^GLOBALS(/_data {/' \ 237 -e 's/^)/};/' -e 'p' $i)" 238 [ -n "$DATA" ] && echo -e "// $i\n\nstruct $NAME$DATA\n" 239 done 240} 241 242if isnewer globals.h toys 243then 244 GLOBSTRUCT="$(getglobals)" 245 ( 246 echo "$GLOBSTRUCT" 247 echo 248 echo "extern union global_union {" 249 echo "$GLOBSTRUCT" | \ 250 $SED -n 's/struct \(.*\)_data {/ struct \1_data \1;/p' 251 echo "} this;" 252 ) > "$GENDIR"/globals.h 253fi 254 255hostcomp mktags 256if isnewer tags.h toys 257then 258 $SED -n '/TAGGED_ARRAY(/,/^)/{s/.*TAGGED_ARRAY[(]\([^,]*\),/\1/;p}' \ 259 toys/*/*.c lib/*.c | "$UNSTRIPPED"/mktags > "$GENDIR"/tags.h 260fi 261 262hostcomp config2help 263if isnewer help.h "$GENDIR"/Config.in 264then 265 "$UNSTRIPPED"/config2help Config.in $KCONFIG_CONFIG > "$GENDIR"/help.h || exit 1 266fi 267[ -z "$DIDNEWER" ] || echo } 268 269[ -n "$NOBUILD" ] && exit 0 270 271echo "Compile $OUTNAME" 272DOTPROG=. 273 274# This is a parallel version of: do_loudly $BUILD lib/*.c $TOYFILES $LINK 275 276# Build all if oldest generated/obj file isn't newer than all header files. 277X="$(ls -1t "$GENDIR"/obj/* 2>/dev/null | tail -n 1)" 278if [ ! -e "$X" ] || [ -n "$(find toys -name "*.h" -newer "$X")" ] 279then 280 rm -rf "$GENDIR"/obj && mkdir -p "$GENDIR"/obj || exit 1 281else 282 # always redo toy_list[] and help_data[] 283 rm -f "$GENDIR"/obj/main.o || exit 1 284fi 285 286# build each generated/obj/*.o file in parallel 287 288PENDING= LNKFILES= CLICK= DONE=0 COUNT=0 289for i in lib/*.c click $TOYFILES 290do 291 [ "$i" == click ] && CLICK=1 && continue 292 293 X=${i/lib\//lib_} 294 X=${X##*/} 295 OUT="$GENDIR/obj/${X%%.c}.o" 296 LNKFILES="$LNKFILES $OUT" 297 298 # Library files don't get rebuilt if older than .config, but commands do. 299 [ "$OUT" -nt "$i" ] && [ -z "$CLICK" -o "$OUT" -nt "$KCONFIG_CONFIG" ] && 300 continue 301 302 do_loudly $BUILD -c $i -o $OUT & 303 304 ratelimit || break 305done 306 307# wait for all background jobs, detecting errors 308while [ "$COUNT" -gt 0 ] 309do 310 ratelimit done 311done 312[ $DONE -ne 0 ] && exit 1 313 314UNSTRIPPED="$UNSTRIPPED/${OUTNAME/*\//}" 315do_loudly $BUILD $LNKFILES $LINK -o "$UNSTRIPPED" || exit 1 316if [ -n "$NOSTRIP" ] || 317 ! do_loudly ${CROSS_COMPILE}${STRIP} "$UNSTRIPPED" -o "$OUTNAME" 318then 319 [ -z "$NOSTRIP" ] && echo "strip failed, using unstripped" 320 rm -f "$OUTNAME" && 321 cp "$UNSTRIPPED" "$OUTNAME" || exit 1 322fi 323 324# Remove write bit set so buggy installs (like bzip's) don't overwrite the 325# multiplexer binary via truncate-and-write through a symlink. 326do_loudly chmod 555 "$OUTNAME" || exit 1 327 328# Ensure make wrapper sees success return code 329[ -z "$V" ] && echo >&2 || true 330