• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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