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