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