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