1 #!/bin/bash 2 3 # Grab default values for $CFLAGS and such. 4 5 export LANG=c 6 export LC_ALL=C 7 set -o pipefail 8 source ./configure 9 10 [ -z "$KCONFIG_CONFIG" ] && KCONFIG_CONFIG=.config 11 [ -z "$OUTNAME" ] && OUTNAME=toybox 12 UNSTRIPPED="generated/unstripped/$(basename "$OUTNAME")" 13 14 # Since each cc invocation is short, launch half again as many processes 15 # as we have processors so they don't exit faster than we can start them. 16 [ -z "$CPUS" ] && 17 CPUS=$((($(echo /sys/devices/system/cpu/cpu[0-9]* | wc -w)*3)/2)) 18 19 if [ -z "$SED" ] 20 then 21 [ ! -z "$(which gsed 2>/dev/null)" ] && SED=gsed || SED=sed 22 fi 23 24 # Respond to V= by echoing command lines as well as running them 25 DOTPROG= 26 do_loudly() 27 { 28 [ ! -z "$V" ] && echo "$@" || echo -n "$DOTPROG" 29 "$@" 30 } 31 32 # Is anything under directory $2 newer than file $1 33 isnewer() 34 { 35 CHECK="$1" 36 shift 37 [ ! -z "$(find "$@" -newer "$CHECK" 2>/dev/null || echo yes)" ] 38 } 39 40 echo "Generate headers from toys/*/*.c..." 41 42 mkdir -p generated/unstripped 43 44 if isnewer generated/Config.in toys 45 then 46 echo "Extract configuration information from toys/*.c files..." 47 scripts/genconfig.sh 48 fi 49 50 # Create a list of all the commands toybox can provide. Note that the first 51 # entry is out of order on purpose (the toybox multiplexer command must be the 52 # first element of the array). The rest must be sorted in alphabetical order 53 # for fast binary search. 54 55 if isnewer generated/newtoys.h toys 56 then 57 echo -n "generated/newtoys.h " 58 59 echo "USE_TOYBOX(NEWTOY(toybox, NULL, TOYFLAG_STAYROOT))" > generated/newtoys.h 60 $SED -n -e 's/^USE_[A-Z0-9_]*(/&/p' toys/*/*.c \ 61 | $SED 's/\(.*TOY(\)\([^,]*\),\(.*\)/\2 \1\2,\3/' | sort -s -k 1,1 \ 62 | $SED 's/[^ ]* //' >> generated/newtoys.h 63 [ $? -ne 0 ] && exit 1 64 fi 65 66 [ ! -z "$V" ] && echo "Which C files to build..." 67 68 # Extract a list of toys/*/*.c files to compile from the data in $KCONFIG_CONFIG 69 # (First command names, then filenames with relevant {NEW,OLD}TOY() macro.) 70 71 GITHASH="$(git describe --tags --abbrev=12 2>/dev/null)" 72 [ ! -z "$GITHASH" ] && GITHASH="-DTOYBOX_VERSION=\"$GITHASH\"" 73 TOYFILES="$($SED -n 's/^CONFIG_\([^=]*\)=.*/\1/p' "$KCONFIG_CONFIG" | xargs | tr ' [A-Z]' '|[a-z]')" 74 TOYFILES="$(egrep -l "TOY[(]($TOYFILES)[ ,]" toys/*/*.c)" 75 CFLAGS="$CFLAGS $(cat generated/cflags)" 76 BUILD="$(echo ${CROSS_COMPILE}${CC} $CFLAGS -I . $OPTIMIZE $GITHASH)" 77 LIBFILES="$(ls lib/*.c | grep -v lib/help.c)" 78 TOYFILES="lib/help.c main.c $TOYFILES" 79 80 if [ "${TOYFILES/pending//}" != "$TOYFILES" ] 81 then 82 echo -e "\n\033[1;31mwarning: using unfinished code from toys/pending\033[0m" 83 fi 84 85 genbuildsh() 86 { 87 # Write a canned build line for use on crippled build machines. 88 89 echo "#!/bin/sh" 90 echo 91 echo "BUILD='$BUILD'" 92 echo 93 echo "FILES='$LIBFILES $TOYFILES'" 94 echo 95 echo "LINK='$LINK'" 96 echo 97 echo 98 echo '$BUILD $FILES $LINK' 99 } 100 101 if ! cmp -s <(genbuildsh | head -n 3) \ 102 <(head -n 3 generated/build.sh 2>/dev/null) 103 then 104 echo -n "Library probe" 105 106 # We trust --as-needed to remove each library if we don't use any symbols 107 # out of it, this loop is because the compiler has no way to ignore a library 108 # that doesn't exist, so we have to detect and skip nonexistent libraries 109 # for it. 110 111 > generated/optlibs.dat 112 for i in util crypt m resolv selinux smack attr rt crypto 113 do 114 echo "int main(int argc, char *argv[]) {return 0;}" | \ 115 ${CROSS_COMPILE}${CC} $CFLAGS -xc - -o generated/libprobe -Wl,--as-needed -l$i > /dev/null 2>/dev/null && 116 echo -l$i >> generated/optlibs.dat 117 echo -n . 118 done 119 rm -f generated/libprobe 120 echo 121 fi 122 123 # LINK needs optlibs.dat, above 124 125 LINK="$(echo $LDOPTIMIZE $LDFLAGS -o "$UNSTRIPPED" -Wl,--as-needed $(cat generated/optlibs.dat))" 126 genbuildsh > generated/build.sh && chmod +x generated/build.sh || exit 1 127 128 #TODO: "make $SED && make" doesn't regenerate config.h because diff .config 129 if true #isnewer generated/config.h "$KCONFIG_CONFIG" 130 then 131 echo "Make generated/config.h from $KCONFIG_CONFIG." 132 133 # This long and roundabout sed invocation is to make old versions of sed 134 # happy. New ones have '\n' so can replace one line with two without all 135 # the branches and tedious mucking about with hold space. 136 137 $SED -n \ 138 -e 's/^# CONFIG_\(.*\) is not set.*/\1/' \ 139 -e 't notset' \ 140 -e 's/^CONFIG_\(.*\)=y.*/\1/' \ 141 -e 't isset' \ 142 -e 's/^CONFIG_\([^=]*\)=\(.*\)/#define CFG_\1 \2/p' \ 143 -e 'd' \ 144 -e ':notset' \ 145 -e 'h' \ 146 -e 's/.*/#define CFG_& 0/p' \ 147 -e 'g' \ 148 -e 's/.*/#define USE_&(...)/p' \ 149 -e 'd' \ 150 -e ':isset' \ 151 -e 'h' \ 152 -e 's/.*/#define CFG_& 1/p' \ 153 -e 'g' \ 154 -e 's/.*/#define USE_&(...) __VA_ARGS__/p' \ 155 $KCONFIG_CONFIG > generated/config.h || exit 1 156 fi 157 158 if [ generated/mkflags -ot scripts/mkflags.c ] 159 then 160 do_loudly $HOSTCC scripts/mkflags.c -o generated/mkflags || exit 1 161 fi 162 163 # Process config.h and newtoys.h to generate FLAG_x macros. Note we must 164 # always #define the relevant macro, even when it's disabled, because we 165 # allow multiple NEWTOY() in the same C file. (When disabled the FLAG is 0, 166 # so flags&0 becomes a constant 0 allowing dead code elimination.) 167 168 make_flagsh() 169 { 170 # Parse files through C preprocessor twice, once to get flags for current 171 # .config and once to get flags for allyesconfig 172 for I in A B 173 do 174 ( 175 # define macros and select header files with option string data 176 177 echo "#define NEWTOY(aa,bb,cc) aa $I bb" 178 echo '#define OLDTOY(...)' 179 if [ "$I" == A ] 180 then 181 cat generated/config.h 182 else 183 $SED '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h 184 fi 185 echo '#include "lib/toyflags.h"' 186 cat generated/newtoys.h 187 188 # Run result through preprocessor, glue together " " gaps leftover from USE 189 # macros, delete comment lines, print any line with a quoted optstring, 190 # turn any non-quoted opstring (NULL or 0) into " " (because fscanf can't 191 # handle "" with nothing in it, and mkflags uses that). 192 193 ) | ${CROSS_COMPILE}${CC} -E - | \ 194 $SED -n -e 's/" *"//g;/^#/d;t clear;:clear;s/"/"/p;t;s/\( [AB] \).*/\1 " "/p' 195 196 # Sort resulting line pairs and glue them together into triplets of 197 # command "flags" "allflags" 198 # to feed into mkflags C program that outputs actual flag macros 199 # If no pair (because command's disabled in config), use " " for flags 200 # so allflags can define the appropriate zero macros. 201 202 done | sort -s | $SED -n -e 's/ A / /;t pair;h;s/\([^ ]*\).*/\1 " "/;x' \ 203 -e 'b single;:pair;h;n;:single;s/[^ ]* B //;H;g;s/\n/ /;p' | \ 204 tee generated/flags.raw | generated/mkflags > generated/flags.h || exit 1 205 } 206 207 if isnewer generated/flags.h toys "$KCONFIG_CONFIG" 208 then 209 echo -n "generated/flags.h " 210 make_flagsh 211 fi 212 213 # Extract global structure definitions and flag definitions from toys/*/*.c 214 215 function getglobals() 216 { 217 for i in toys/*/*.c 218 do 219 NAME="$(echo $i | $SED 's@.*/\(.*\)\.c@\1@')" 220 DATA="$($SED -n -e '/^GLOBALS(/,/^)/b got;b;:got' \ 221 -e 's/^GLOBALS(/struct '"$NAME"'_data {/' \ 222 -e 's/^)/};/' -e 'p' $i)" 223 224 [ ! -z "$DATA" ] && echo -e "// $i\n\n$DATA\n" 225 done 226 } 227 228 if isnewer generated/globals.h toys 229 then 230 echo -n "generated/globals.h " 231 GLOBSTRUCT="$(getglobals)" 232 ( 233 echo "$GLOBSTRUCT" 234 echo 235 echo "extern union global_union {" 236 echo "$GLOBSTRUCT" | \ 237 $SED -n 's/struct \(.*\)_data {/ struct \1_data \1;/p' 238 echo "} this;" 239 ) > generated/globals.h 240 fi 241 242 if [ generated/mktags -ot scripts/mktags.c ] 243 then 244 do_loudly $HOSTCC scripts/mktags.c -o generated/mktags || exit 1 245 fi 246 247 if isnewer generated/tags.h toys 248 then 249 echo -n "generated/tags.h " 250 251 $SED -n '/TAGGED_ARRAY(/,/^)/{s/.*TAGGED_ARRAY[(]\([^,]*\),/\1/;p}' \ 252 toys/*/*.c lib/*.c | generated/mktags > generated/tags.h 253 fi 254 255 if [ generated/config2help -ot scripts/config2help.c ] 256 then 257 do_loudly $HOSTCC scripts/config2help.c -I . lib/xwrap.c lib/llist.c \ 258 lib/lib.c lib/portability.c -o generated/config2help || exit 1 259 fi 260 if isnewer generated/help.h generated/Config.in 261 then 262 echo "generated/help.h" 263 generated/config2help Config.in $KCONFIG_CONFIG > generated/help.h || exit 1 264 fi 265 266 [ ! -z "$NOBUILD" ] && exit 0 267 268 echo -n "Compile toybox" 269 [ ! -z "$V" ] && echo 270 DOTPROG=. 271 272 # This is a parallel version of: do_loudly $BUILD $FILES $LINK || exit 1 273 274 # Any headers newer than the oldest generated/obj file? 275 X="$(ls -1t generated/obj/* 2>/dev/null | tail -n 1)" 276 # TODO: redo this 277 if [ ! -e "$X" ] || [ ! -z "$(find toys -name "*.h" -newer "$X")" ] 278 then 279 rm -rf generated/obj && mkdir -p generated/obj || exit 1 280 else 281 rm -f generated/obj/{main,lib_help}.o || exit 1 282 fi 283 284 # build each generated/obj/*.o file in parallel 285 286 PENDING= 287 LNKFILES= 288 DONE=0 289 COUNT=0 290 CLICK= 291 292 for i in $LIBFILES click $TOYFILES 293 do 294 [ "$i" == click ] && CLICK=1 && continue 295 296 X=${i/lib\//lib_} 297 X=${X##*/} 298 OUT="generated/obj/${X%%.c}.o" 299 LNKFILES="$LNKFILES $OUT" 300 301 # $LIBFILES doesn't need to be rebuilt if newer than .config, $TOYFILES does 302 303 [ "$OUT" -nt "$i" ] && [ -z "$CLICK" -o "$OUT" -nt "$KCONFIG_CONFIG" ] && 304 continue 305 306 do_loudly $BUILD -c $i -o $OUT & 307 PENDING="$PENDING $!" 308 COUNT=$(($COUNT+1)) 309 310 # ratelimit to $CPUS many parallel jobs, detecting errors 311 312 for j in $PENDING 313 do 314 [ "$COUNT" -lt "$CPUS" ] && break; 315 316 wait $j 317 DONE=$(($DONE+$?)) 318 COUNT=$(($COUNT-1)) 319 PENDING="${PENDING## $j}" 320 done 321 [ $DONE -ne 0 ] && break 322 done 323 324 # wait for all background jobs, detecting errors 325 326 for i in $PENDING 327 do 328 wait $i 329 DONE=$(($DONE+$?)) 330 done 331 332 [ $DONE -ne 0 ] && exit 1 333 334 do_loudly $BUILD $LNKFILES $LINK || exit 1 335 if [ ! -z "$NOSTRIP" ] || 336 ! do_loudly ${CROSS_COMPILE}strip "$UNSTRIPPED" -o "$OUTNAME" 337 then 338 echo "strip failed, using unstripped" && cp "$UNSTRIPPED" "$OUTNAME" || 339 exit 1 340 fi 341 342 # gcc 4.4's strip command is buggy, and doesn't set the executable bit on 343 # its output the way SUSv4 suggests it do so. While we're at it, make sure 344 # we don't have the "w" bit set so things like bzip2's "cp -f" install don't 345 # overwrite our binary through the symlink. 346 do_loudly chmod 555 "$OUTNAME" || exit 1 347 348 echo 349