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