toybox/scripts/make.sh
<<
>>
Prefs
   1#!/bin/bash
   2
   3# Grab default values for $CFLAGS and such.
   4
   5if [ ! -z "$ASAN" ]; then
   6  echo "Enabling ASan..."
   7  # Turn ASan on. Everything except -fsanitize=address is optional, but
   8  # but effectively required for useful backtraces.
   9  asan_flags="-fsanitize=address \
  10    -O1 -g -fno-omit-frame-pointer -fno-optimize-sibling-calls"
  11  CFLAGS="$asan_flags $CFLAGS"
  12  HOSTCC="$HOSTCC $asan_flags"
  13  # Ignore leaks on exit. TODO
  14  export ASAN_OPTIONS="detect_leaks=0"
  15fi
  16
  17# Centos 7 bug workaround, EOL June 30 2024.
  18DASHN=-n; wait -n 2>/dev/null; [ $? -eq 2 ] && unset DASHN
  19
  20export LANG=c
  21export LC_ALL=C
  22set -o pipefail
  23source scripts/portability.sh
  24
  25[ -z "$KCONFIG_CONFIG" ] && KCONFIG_CONFIG=.config
  26[ -z "$OUTNAME" ] && OUTNAME=toybox"${TARGET:+-$TARGET}"
  27UNSTRIPPED="generated/unstripped/$(basename "$OUTNAME")"
  28
  29# Default to running one more parallel cc instance than we have processors
  30: ${CPUS:=$(($(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null)+1))}
  31
  32# Respond to V= by echoing command lines as well as running them
  33DOTPROG=
  34do_loudly()
  35{
  36  [ ! -z "$V" ] && echo "$@" || echo -n "$DOTPROG"
  37  "$@"
  38}
  39
  40# Is anything under directory $2 newer than file $1
  41isnewer()
  42{
  43  CHECK="$1"
  44  shift
  45  [ ! -z "$(find "$@" -newer "$CHECK" 2>/dev/null || echo yes)" ]
  46}
  47
  48echo "Generate headers from toys/*/*.c..."
  49
  50mkdir -p generated/unstripped
  51
  52if isnewer generated/Config.in toys || isnewer generated/Config.in Config.in
  53then
  54  echo "Extract configuration information from toys/*.c files..."
  55  scripts/genconfig.sh
  56fi
  57
  58# Create a list of all the commands toybox can provide. Note that the first
  59# entry is out of order on purpose (the toybox multiplexer command must be the
  60# first element of the array). The rest must be sorted in alphabetical order
  61# for fast binary search.
  62
  63if isnewer generated/newtoys.h toys
  64then
  65  echo -n "generated/newtoys.h "
  66
  67  echo "USE_TOYBOX(NEWTOY(toybox, NULL, TOYFLAG_STAYROOT))" > generated/newtoys.h
  68  $SED -n -e 's/^USE_[A-Z0-9_]*(/&/p' toys/*/*.c \
  69        | $SED 's/\(.*TOY(\)\([^,]*\),\(.*\)/\2 \1\2,\3/' | sort -s -k 1,1 \
  70        | $SED 's/[^ ]* //'  >> generated/newtoys.h
  71  [ $? -ne 0 ] && exit 1
  72fi
  73
  74[ ! -z "$V" ] && echo "Which C files to build..."
  75
  76# Extract a list of toys/*/*.c files to compile from the data in $KCONFIG_CONFIG
  77# (First command names, then filenames with relevant {NEW,OLD}TOY() macro.)
  78
  79[ -d ".git" ] && GITHASH="$(git describe --tags --abbrev=12 2>/dev/null)"
  80[ ! -z "$GITHASH" ] && GITHASH="-DTOYBOX_VERSION=\"$GITHASH\""
  81TOYFILES="$($SED -n 's/^CONFIG_\([^=]*\)=.*/\1/p' "$KCONFIG_CONFIG" | xargs | tr ' [A-Z]' '|[a-z]')"
  82TOYFILES="$(egrep -l "TOY[(]($TOYFILES)[ ,]" toys/*/*.c)"
  83CFLAGS="$CFLAGS $(cat generated/cflags)"
  84BUILD="$(echo ${CROSS_COMPILE}${CC} $CFLAGS -I . $OPTIMIZE $GITHASH)"
  85LIBFILES="$(ls lib/*.c | grep -v lib/help.c)"
  86TOYFILES="lib/help.c main.c $TOYFILES"
  87
  88if [ "${TOYFILES/pending//}" != "$TOYFILES" ]
  89then
  90  echo -e "\n\033[1;31mwarning: using unfinished code from toys/pending\033[0m"
  91fi
  92
  93genbuildsh()
  94{
  95  # Write a canned build line for use on crippled build machines.
  96
  97  echo -e "#!/bin/sh\n\nPATH='\$PATH'\n\nBUILD='\$BUILD'\n\nLINK='\$LINK'\n"
  98  echo -e "FILES='$LIBFILES $TOYFILES'\n\n\$BUILD \$FILES \$LINK"
  99}
 100
 101if ! cmp -s <(genbuildsh 2>/dev/null | head -n 6 ; echo LINK="'"$LDOPTIMIZE $LDFLAGS) \
 102          <(head -n 7 generated/build.sh 2>/dev/null | $SED '7s/ -o .*//')
 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  > generated/optlibs.dat
 111  for i in util crypt m resolv selinux smack attr crypto z log iconv tls ssl
 112  do
 113    echo "int main(int argc, char *argv[]) {return 0;}" | \
 114    ${CROSS_COMPILE}${CC} $CFLAGS $LDFLAGS -xc - -o generated/libprobe $LDASNEEDED -l$i > /dev/null 2>/dev/null &&
 115    echo -l$i >> generated/optlibs.dat
 116    echo -n .
 117  done
 118  rm -f generated/libprobe
 119  echo
 120fi
 121
 122# LINK needs optlibs.dat, above
 123
 124LINK="$(echo $LDOPTIMIZE $LDFLAGS -o "$UNSTRIPPED" $LDASNEEDED $(cat generated/optlibs.dat))"
 125genbuildsh > generated/build.sh && chmod +x generated/build.sh || exit 1
 126
 127#TODO: "make $SED && make" doesn't regenerate config.h because diff .config
 128if true #isnewer generated/config.h "$KCONFIG_CONFIG"
 129then
 130  echo "Make generated/config.h from $KCONFIG_CONFIG."
 131
 132  # This long and roundabout sed invocation is to make old versions of sed
 133  # happy. New ones have '\n' so can replace one line with two without all
 134  # the branches and tedious mucking about with hyperspace.
 135  # TODO: clean this up to use modern stuff.
 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
 156fi
 157
 158if [ ! -f generated/mkflags ] || [ generated/mkflags -ot scripts/mkflags.c ]
 159then
 160  do_loudly $HOSTCC scripts/mkflags.c -o generated/mkflags || exit 1
 161fi
 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
 168if isnewer generated/flags.h toys "$KCONFIG_CONFIG"
 169then
 170  echo -n "generated/flags.h "
 171
 172  # Parse files through C preprocessor twice, once to get flags for current
 173  # .config and once to get flags for allyesconfig
 174  for I in A B
 175  do
 176    (
 177    # define macros and select header files with option string data
 178
 179    echo "#define NEWTOY(aa,bb,cc) aa $I bb"
 180    echo '#define OLDTOY(...)'
 181    if [ "$I" == A ]
 182    then
 183      cat generated/config.h
 184    else
 185      $SED '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h
 186    fi
 187    echo '#include "lib/toyflags.h"'
 188    cat generated/newtoys.h
 189
 190    # Run result through preprocessor, glue together " " gaps leftover from USE
 191    # macros, delete comment lines, print any line with a quoted optstring,
 192    # turn any non-quoted opstring (NULL or 0) into " " (because fscanf can't
 193    # handle "" with nothing in it, and mkflags uses that).
 194
 195    ) | ${CROSS_COMPILE}${CC} -E - | \
 196    $SED -n -e 's/" *"//g;/^#/d;t clear;:clear;s/"/"/p;t;s/\( [AB] \).*/\1 " "/p'
 197
 198  # Sort resulting line pairs and glue them together into triplets of
 199  #   command "flags" "allflags"
 200  # to feed into mkflags C program that outputs actual flag macros
 201  # If no pair (because command's disabled in config), use " " for flags
 202  # so allflags can define the appropriate zero macros.
 203
 204  done | sort -s | $SED -n -e 's/ A / /;t pair;h;s/\([^ ]*\).*/\1 " "/;x' \
 205    -e 'b single;:pair;h;n;:single;s/[^ ]* B //;H;g;s/\n/ /;p' | \
 206    tee generated/flags.raw | generated/mkflags > generated/flags.h || exit 1
 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    # alas basename -s isn't in posix yet.
 216    NAME="$(echo $i | $SED 's@.*/\(.*\)\.c@\1@')"
 217    DATA="$($SED -n -e '/^GLOBALS(/,/^)/b got;b;:got' \
 218            -e 's/^GLOBALS(/_data {/' \
 219            -e 's/^)/};/' -e 'p' $i)"
 220    [ ! -z "$DATA" ] && echo -e "// $i\n\nstruct $NAME$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 [ ! -f generated/mktags ] || [ 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 [ ! -f generated/config2help ] || [ 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
 281unset PENDING LNKFILES CLICK
 282DONE=0
 283COUNT=0
 284
 285for i in $LIBFILES click $TOYFILES
 286do
 287  [ "$i" == click ] && CLICK=1 && continue
 288
 289  X=${i/lib\//lib_}
 290  X=${X##*/}
 291  OUT="generated/obj/${X%%.c}.o"
 292  LNKFILES="$LNKFILES $OUT"
 293
 294  # $LIBFILES don't need to be rebuilt if older than .config, $TOYFILES do
 295  # ($TOYFILES contents can depend on CONFIG symbols, lib/*.c never should.)
 296
 297  [ "$OUT" -nt "$i" ] && [ -z "$CLICK" -o "$OUT" -nt "$KCONFIG_CONFIG" ] &&
 298    continue
 299
 300  do_loudly $BUILD -c $i -o $OUT &
 301
 302  # ratelimit to $CPUS many parallel jobs, detecting errors
 303  [ $((++COUNT)) -ge $CPUS ] && { wait $DASHN; DONE=$?; : $((--COUNT)); }
 304  [ $DONE -ne 0 ] && break
 305done
 306# wait for all background jobs, detecting errors
 307
 308while [ $((COUNT--)) -gt 0 ]
 309do
 310  wait $DASHN;
 311  DONE=$((DONE+$?))
 312done
 313[ $DONE -ne 0 ] && exit 1
 314
 315do_loudly $BUILD $LNKFILES $LINK || exit 1
 316if [ ! -z "$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# gcc 4.4's strip command is buggy, and doesn't set the executable bit on
 325# its output the way SUSv4 suggests it do so. While we're at it, make sure
 326# we don't have the "w" bit set so things like bzip2's "cp -f" install don't
 327# overwrite our binary through the symlink.
 328do_loudly chmod 555 "$OUTNAME" || exit 1
 329
 330echo
 331