make.sh 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #!/bin/bash
  2. # Grab default values for $CFLAGS and such.
  3. set -o pipefail
  4. source scripts/portability.sh
  5. # Shell functions called by the build
  6. # Respond to V= by echoing command lines as well as running them
  7. do_loudly()
  8. {
  9. { [ -n "$V" ] && echo "$@" || echo -n "$DOTPROG" ; } >&2
  10. "$@"
  11. }
  12. # Is anything under directory $2 newer than generated/$1 (or does it not exist)?
  13. isnewer()
  14. {
  15. [ -e "$GENDIR/$1" ] && [ -z "$(find "${@:2}" -newer "$GENDIR/$1")" ] &&
  16. return 1
  17. echo -n "${DIDNEWER:-$GENDIR/{}$1"
  18. DIDNEWER=,
  19. }
  20. # Build a tool that runs on the host
  21. hostcomp()
  22. {
  23. if [ ! -f "$UNSTRIPPED"/$1 ] || [ "$UNSTRIPPED"/$1 -ot scripts/$1.c ]
  24. then
  25. do_loudly $HOSTCC scripts/$1.c -o "$UNSTRIPPED"/$1 || exit 1
  26. fi
  27. }
  28. # Set/record build environment information
  29. compflags()
  30. {
  31. [ -z "$VERSION" ] && [ -d ".git" ] && [ -n "$(which git 2>/dev/null)" ] &&
  32. VERSION="-DTOYBOX_VERSION=\"$(git describe --tags --abbrev=12 2>/dev/null)\""
  33. # VERSION and LIBRARIES volatile, changing either does not require a rebuild
  34. echo '#!/bin/sh'
  35. echo
  36. echo "VERSION='$VERSION'"
  37. echo "LIBRARIES='$(xargs 2>/dev/null < "$GENDIR/optlibs.dat")'"
  38. echo "BUILD='${CROSS_COMPILE}${CC} $CFLAGS -I . $OPTIMIZE '\"\$VERSION\""
  39. echo "LINK='$LDOPTIMIZE $LDFLAGS '\"\$LIBRARIES\""
  40. echo "PATH='$PATH'"
  41. echo "# Built from $KCONFIG_CONFIG"
  42. echo
  43. }
  44. # Make sure rm -rf isn't gonna go funny
  45. B="$(readlink -f "$PWD")/" A="$(readlink -f "$GENDIR")" A="${A%/}"/
  46. [ "$A" == "${B::${#A}}" ] &&
  47. { echo "\$GENDIR=$GENDIR cannot include \$PWD=$PWD"; exit 1; }
  48. unset A B DOTPROG DIDNEWER
  49. # Force full rebuild if our compiler/linker options changed
  50. cmp -s <(compflags|sed '5,8!d') <($SED '5,8!d' "$GENDIR"/build.sh 2>/dev/null)||
  51. rm -rf "$GENDIR"/* # Keep symlink, delete contents
  52. mkdir -p "$UNSTRIPPED" "$(dirname $OUTNAME)" || exit 1
  53. # Extract a list of toys/*/*.c files to compile from the data in $KCONFIG_CONFIG
  54. # (First command names, then filenames with relevant {NEW,OLD}TOY() macro.)
  55. [ -n "$V" ] && echo -e "\nWhich C files to build..."
  56. TOYFILES="$($SED -n 's/^CONFIG_\([^=]*\)=.*/\1/p' "$KCONFIG_CONFIG" | xargs | tr ' [A-Z]' '|[a-z]')"
  57. TOYFILES="main.c $(egrep -l "TOY[(]($TOYFILES)[ ,]" toys/*/*.c | xargs)"
  58. if [ "${TOYFILES/pending//}" != "$TOYFILES" ]
  59. then
  60. echo -e "\n\033[1;31mwarning: using unfinished code from toys/pending\033[0m"
  61. fi
  62. # Probe library list if our compiler/linker options changed
  63. if [ ! -e "$GENDIR"/optlibs.dat ]
  64. then
  65. echo -n "Library probe"
  66. # --as-needed removes libraries we don't use any symbols out of, but the
  67. # compiler has no way to ignore a library that doesn't exist, so detect
  68. # and skip nonexistent libraries for it.
  69. > "$GENDIR"/optlibs.new
  70. [ -z "$V" ] && X=/dev/null || X=/dev/stderr
  71. for i in util crypt m resolv selinux smack attr crypto z log iconv tls ssl
  72. do
  73. do_loudly ${CROSS_COMPILE}${CC} $CFLAGS $LDFLAGS -xc - -l$i >>$X 2>&1 \
  74. -o "$UNSTRIPPED"/libprobe <<<"int main(int argc,char*argv[]){return 0;}"&&
  75. do_loudly echo -n ' '-l$i >> "$GENDIR"/optlibs.new
  76. done
  77. unset X
  78. rm -f "$UNSTRIPPED"/libprobe
  79. mv "$GENDIR"/optlibs.{new,dat} || exit 1
  80. echo
  81. fi
  82. # Write build variables (and set them locally), then append build invocation.
  83. compflags > "$GENDIR"/build.sh && source "$GENDIR/build.sh" &&
  84. echo -e "\$BUILD lib/*.c $TOYFILES \$LINK -o $OUTNAME" >> "$GENDIR"/build.sh&&
  85. chmod +x "$GENDIR"/build.sh || exit 1
  86. if isnewer Config.in toys || isnewer Config.in Config.in
  87. then
  88. scripts/genconfig.sh
  89. fi
  90. # Does .config need dependency recalculation because toolchain changed?
  91. A="$($SED -n '/^config .*$/h;s/default \(.\)/\1/;T;H;g;s/config \([^\n]*\)[^yn]*\(.\)/\1=\2/p' "$GENDIR"/Config.probed | sort)"
  92. B="$(egrep "^CONFIG_($(echo "$A" | sed 's/=[yn]//' | xargs | tr ' ' '|'))=" "$KCONFIG_CONFIG" | $SED 's/^CONFIG_//' | sort)"
  93. A="$(echo "$A" | grep -v =n)"
  94. [ "$A" != "$B" ] &&
  95. { echo -e "\nWarning: Config.probed changed, run 'make oldconfig'" >&2; }
  96. unset A B
  97. # Create a list of all the commands toybox can provide.
  98. if isnewer newtoys.h toys
  99. then
  100. # The multiplexer is the first element in the array
  101. echo "USE_TOYBOX(NEWTOY(toybox, 0, TOYFLAG_STAYROOT|TOYFLAG_NOHELP))" \
  102. > "$GENDIR"/newtoys.h
  103. # Sort rest by name for binary search (copy name to front, sort, remove copy)
  104. $SED -n 's/^\(USE_[^(]*(.*TOY(\)\([^,]*\)\(,.*\)/\2 \1\2\3/p' toys/*/*.c \
  105. | sort -s -k 1,1 | $SED 's/[^ ]* //' >> "$GENDIR"/newtoys.h
  106. [ $? -ne 0 ] && exit 1
  107. fi
  108. #TODO: "make $SED && make" doesn't regenerate config.h because diff .config
  109. if true #isnewer config.h "$KCONFIG_CONFIG"
  110. then
  111. # This long and roundabout sed invocation is to make old versions of sed
  112. # happy. New ones have '\n' so can replace one line with two without all
  113. # the branches and tedious mucking about with hyperspace.
  114. # TODO: clean this up to use modern stuff.
  115. $SED -n \
  116. -e 's/^# CONFIG_\(.*\) is not set.*/\1/' \
  117. -e 't notset' \
  118. -e 's/^CONFIG_\(.*\)=y.*/\1/' \
  119. -e 't isset' \
  120. -e 's/^CONFIG_\([^=]*\)=\(.*\)/#define CFG_\1 \2/p' \
  121. -e 'd' \
  122. -e ':notset' \
  123. -e 'h' \
  124. -e 's/.*/#define CFG_& 0/p' \
  125. -e 'g' \
  126. -e 's/.*/#define USE_&(...)/p' \
  127. -e 'd' \
  128. -e ':isset' \
  129. -e 'h' \
  130. -e 's/.*/#define CFG_& 1/p' \
  131. -e 'g' \
  132. -e 's/.*/#define USE_&(...) __VA_ARGS__/p' \
  133. $KCONFIG_CONFIG > "$GENDIR"/config.h || exit 1
  134. fi
  135. # Process config.h and newtoys.h to generate FLAG_x macros. Note we must
  136. # always #define the relevant macro, even when it's disabled, because we
  137. # allow multiple NEWTOY() in the same C file. (When disabled the FLAG is 0,
  138. # so flags&0 becomes a constant 0 allowing dead code elimination.)
  139. hostcomp mkflags
  140. if isnewer flags.h toys "$KCONFIG_CONFIG"
  141. then
  142. # Parse files through C preprocessor twice, once to get flags for current
  143. # .config and once to get flags for allyesconfig
  144. for I in A B
  145. do
  146. (
  147. # define macros and select header files with option string data
  148. echo "#define NEWTOY(aa,bb,cc) aa $I bb"
  149. echo '#define OLDTOY(...)'
  150. if [ "$I" == A ]
  151. then
  152. cat "$GENDIR"/config.h
  153. else
  154. $SED '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' "$GENDIR"/config.h
  155. fi
  156. echo '#include "lib/toyflags.h"'
  157. cat "$GENDIR"/newtoys.h
  158. # Run result through preprocessor, glue together " " gaps leftover from USE
  159. # macros, delete comment lines, print any line with a quoted optstring,
  160. # turn any non-quoted opstring (NULL or 0) into " " (because fscanf can't
  161. # handle "" with nothing in it, and mkflags uses that).
  162. ) | ${CROSS_COMPILE}${CC} -E - | \
  163. $SED -n -e 's/" *"//g;/^#/d;t clear;:clear;s/"/"/p;t;s/\( [AB] \).*/\1 " "/p'
  164. # Sort resulting line pairs and glue them together into triplets of
  165. # command "flags" "allflags"
  166. # to feed into mkflags C program that outputs actual flag macros
  167. # If no pair (because command's disabled in config), use " " for flags
  168. # so allflags can define the appropriate zero macros.
  169. done | sort -s | $SED -n -e 's/ A / /;t pair;h;s/\([^ ]*\).*/\1 " "/;x' \
  170. -e 'b single;:pair;h;n;:single;s/[^ ]* B //;H;g;s/\n/ /;p' | \
  171. tee "$GENDIR"/flags.raw | "$UNSTRIPPED"/mkflags > "$GENDIR"/flags.h || exit 1
  172. fi
  173. # Extract global structure definitions and flag definitions from toys/*/*.c
  174. function getglobals()
  175. {
  176. for i in toys/*/*.c
  177. do
  178. # alas basename -s isn't in posix yet.
  179. NAME="$(echo $i | $SED 's@.*/\(.*\)\.c@\1@')"
  180. DATA="$($SED -n -e '/^GLOBALS(/,/^)/b got;b;:got' \
  181. -e 's/^GLOBALS(/_data {/' \
  182. -e 's/^)/};/' -e 'p' $i)"
  183. [ -n "$DATA" ] && echo -e "// $i\n\nstruct $NAME$DATA\n"
  184. done
  185. }
  186. if isnewer globals.h toys
  187. then
  188. GLOBSTRUCT="$(getglobals)"
  189. (
  190. echo "$GLOBSTRUCT"
  191. echo
  192. echo "extern union global_union {"
  193. echo "$GLOBSTRUCT" | \
  194. $SED -n 's/struct \(.*\)_data {/ struct \1_data \1;/p'
  195. echo "} this;"
  196. ) > "$GENDIR"/globals.h
  197. fi
  198. hostcomp mktags
  199. if isnewer tags.h toys
  200. then
  201. $SED -n '/TAGGED_ARRAY(/,/^)/{s/.*TAGGED_ARRAY[(]\([^,]*\),/\1/;p}' \
  202. toys/*/*.c lib/*.c | "$UNSTRIPPED"/mktags > "$GENDIR"/tags.h
  203. fi
  204. hostcomp config2help
  205. if isnewer help.h "$GENDIR"/Config.in
  206. then
  207. "$UNSTRIPPED"/config2help Config.in $KCONFIG_CONFIG > "$GENDIR"/help.h || exit 1
  208. fi
  209. [ -z "$DIDNEWER" ] || echo }
  210. [ -n "$NOBUILD" ] && exit 0
  211. echo "Compile $OUTNAME"
  212. DOTPROG=.
  213. # This is a parallel version of: do_loudly $BUILD lib/*.c $TOYFILES $LINK
  214. # Build all if oldest generated/obj file isn't newer than all header files.
  215. X="$(ls -1t "$GENDIR"/obj/* 2>/dev/null | tail -n 1)"
  216. if [ ! -e "$X" ] || [ -n "$(find toys -name "*.h" -newer "$X")" ]
  217. then
  218. rm -rf "$GENDIR"/obj && mkdir -p "$GENDIR"/obj || exit 1
  219. else
  220. # always redo toy_list[] and help_data[]
  221. rm -f "$GENDIR"/obj/main.o || exit 1
  222. fi
  223. # build each generated/obj/*.o file in parallel
  224. PENDING= LNKFILES= CLICK= DONE=0 COUNT=0
  225. for i in lib/*.c click $TOYFILES
  226. do
  227. [ "$i" == click ] && CLICK=1 && continue
  228. X=${i/lib\//lib_}
  229. X=${X##*/}
  230. OUT="$GENDIR/obj/${X%%.c}.o"
  231. LNKFILES="$LNKFILES $OUT"
  232. # Library files don't get rebuilt if older than .config, but commands do.
  233. [ "$OUT" -nt "$i" ] && [ -z "$CLICK" -o "$OUT" -nt "$KCONFIG_CONFIG" ] &&
  234. continue
  235. do_loudly $BUILD -c $i -o $OUT &
  236. # ratelimit to $CPUS many parallel jobs, detecting errors
  237. [ $((++COUNT)) -ge $CPUS ] && { wait $DASHN; DONE=$?; : $((--COUNT)); }
  238. [ $DONE -ne 0 ] && break
  239. done
  240. # wait for all background jobs, detecting errors
  241. while [ $((COUNT--)) -gt 0 ]
  242. do
  243. wait $DASHN;
  244. DONE=$((DONE+$?))
  245. done
  246. [ $DONE -ne 0 ] && exit 1
  247. UNSTRIPPED="$UNSTRIPPED/${OUTNAME/*\//}"
  248. do_loudly $BUILD $LNKFILES $LINK -o "$UNSTRIPPED" || exit 1
  249. if [ -n "$NOSTRIP" ] ||
  250. ! do_loudly ${CROSS_COMPILE}${STRIP} "$UNSTRIPPED" -o "$OUTNAME"
  251. then
  252. [ -z "$NOSTRIP" ] && echo "strip failed, using unstripped"
  253. rm -f "$OUTNAME" &&
  254. cp "$UNSTRIPPED" "$OUTNAME" || exit 1
  255. fi
  256. # Remove write bit set so buggy installs (like bzip's) don't overwrite the
  257. # multiplexer binary via truncate-and-write through a symlink.
  258. do_loudly chmod 555 "$OUTNAME" || exit 1
  259. # Ensure make wrapper sees success return code
  260. [ -z "$V" ] && echo >&2 || true