1#!/bin/sh
2# genconfig.sh generates build configuration from build.cfg
3#
4# outputs four files:
5#
6# scripts/mk/config.set ninqu reads this via (set-file). flat
7# KEY=VAL, unquoted. per-tool BUILD_CATEGORY_TOOL
8# toggles get a lowercase alias here, since
9# (gate ...) in ninqu.rules only ever checks
10# the lowercase form
11#
12# scripts/mk/config.kv shell-quoted KEY="VAL" format. sourced by
13# read via the -config flag by scripts/mkman.
14# nothing in the ninqu build itself sources
15# this file: genbox.sh takes its input on argv
16# only, see the BOX section of ninqu.rules
17#
18# shared/libutil/nofork_list.h
19# wexec.c includes this at compile time.
20# generated from NOFORK_LIST in build.cfg
21#
22# scripts/mk/features.h every resolved FEATURE_* as a #define
23# CPPFLAGS pulls this in with -include
24#
25# the codegen subcommands (getconf, bc, awk, sh) are also here so
26# ninqu.rules can call (exec sh scripts/genconfig.sh <step>) for
27# each one. they check mtimes internally and only regenerate when
28# their input changed
29#
30set -e
31
32target=${1:-config}
33
34if [ -f build.cfg ]; then
35 . ./build.cfg
36else
37 echo "genconfig: build.cfg not found" >&2
38 exit 1
39fi
40
41newer_than() {
42 [ -e "$2" ] || return 0
43 local s t
44 s=$(stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1" 2>/dev/null || echo 0)
45 t=$(stat -c '%Y' "$2" 2>/dev/null || stat -f '%m' "$2" 2>/dev/null || echo 0)
46 [ "$s" -gt "$t" ]
47}
48
49gen_getconf() {
50 if newer_than scripts/getconf.sh cmd/posix/getconf.h; then
51 printf " GEN cmd/posix/getconf.h\n"
52 scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
53 fi
54}
55
56gen_bc() {
57 if newer_than cmd/posix/bc.y cmd/posix/bc.c; then
58 printf " GEN cmd/posix/bc.c\n"
59 CC=${CC:-cc}
60 YACC=${YACC:-}
61 if [ -z "$YACC" ]; then
62 if command -v yacc >/dev/null 2>&1; then
63 YACC="yacc"
64 elif command -v bison >/dev/null 2>&1; then
65 YACC="bison -y"
66 else
67 YACC="yacc"
68 fi
69 fi
70 if [ "$YACC" = "bison -y" ]; then
71 bison -o cmd/posix/bc.c cmd/posix/bc.y
72 else
73 $YACC -o cmd/posix/bc.c cmd/posix/bc.y
74 fi
75 fi
76}
77
78gen_awk() {
79 local dir="cmd/posix/awk"
80 CC=${CC:-cc}
81 YACC=${YACC:-}
82 if [ -z "$YACC" ]; then
83 if command -v yacc >/dev/null 2>&1; then
84 YACC="yacc"
85 elif command -v bison >/dev/null 2>&1; then
86 YACC="bison -y"
87 else
88 YACC="yacc"
89 fi
90 fi
91
92 if newer_than "$dir/awkgram.y" "$dir/awkgram.tab.c" || newer_than "$dir/awkgram.y" "$dir/awkgram.tab.h"; then
93 printf " YACC $dir/awkgram.tab.c\n"
94 if [ "$YACC" = "bison -y" ]; then
95 bison -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
96 else
97 $YACC -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
98 if [ ! -f "$dir/awkgram.tab.h" ]; then
99 if [ -f y.tab.h ]; then
100 mv y.tab.h "$dir/awkgram.tab.h"
101 elif [ -f "$dir/y.tab.h" ]; then
102 mv "$dir/y.tab.h" "$dir/awkgram.tab.h"
103 fi
104 fi
105 fi
106 fi
107
108 if newer_than "$dir/maketab.c" "$dir/maketab" || newer_than "$dir/awkgram.tab.h" "$dir/maketab"; then
109 printf " CC $dir/maketab\n"
110 $CC -o "$dir/maketab" "$dir/maketab.c"
111 fi
112
113 if newer_than "$dir/maketab" "$dir/proctab.c" || newer_than "$dir/awkgram.tab.h" "$dir/proctab.c"; then
114 printf " GEN $dir/proctab.c\n"
115 "$dir/maketab" "$dir/awkgram.tab.h" > "$dir/proctab.c"
116 fi
117}
118
119gen_sh() {
120 local dir="cmd/posix/sh"
121 CC=${CC:-cc}
122
123 if newer_than "$dir/mknodes.c" "$dir/mknodes"; then
124 printf " CC $dir/mknodes\n"
125 $CC -o "$dir/mknodes" "$dir/mknodes.c"
126 fi
127
128 if newer_than "$dir/mksyntax.c" "$dir/mksyntax"; then
129 printf " CC $dir/mksyntax\n"
130 $CC -Ishared -I"$dir" -o "$dir/mksyntax" "$dir/mksyntax.c"
131 fi
132
133 if newer_than "$dir/mktokens" "$dir/token.h"; then
134 printf " GEN $dir/token.h\n"
135 (cd "$dir" && sh mktokens)
136 fi
137
138 if newer_than "$dir/mksyntax" "$dir/syntax.c" || newer_than "$dir/mksyntax" "$dir/syntax.h"; then
139 printf " GEN $dir/syntax.c\n"
140 (cd "$dir" && ./mksyntax)
141 fi
142
143 if newer_than "$dir/mknodes" "$dir/nodes.c" || newer_than "$dir/nodetypes" "$dir/nodes.c" || newer_than "$dir/nodes.c.pat" "$dir/nodes.c"; then
144 printf " GEN $dir/nodes.c\n"
145 (cd "$dir" && ./mknodes nodetypes nodes.c.pat)
146 fi
147
148 if newer_than "$dir/mkbuiltins" "$dir/builtins.c" || newer_than "$dir/builtins.def" "$dir/builtins.c" || newer_than "$dir/shell.h" "$dir/builtins.c" || [ ! -f "$dir/builtins.c" ]; then
149 printf " GEN $dir/builtins.c\n"
150 (cd "$dir" && sh mkbuiltins .)
151 fi
152}
153
154clean() {
155 printf " CLEAN generated files\n"
156 rm -f cmd/posix/getconf.h
157 rm -f cmd/posix/bc.c cmd/posix/bc.h
158 rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proctab.c cmd/posix/awk/maketab
159 rm -f cmd/posix/sh/mknodes cmd/posix/sh/mksyntax cmd/posix/sh/token.h cmd/posix/sh/syntax.c cmd/posix/sh/syntax.h cmd/posix/sh/nodes.c cmd/posix/sh/nodes.h cmd/posix/sh/builtins.c cmd/posix/sh/builtins.h
160 rm -f scripts/mk/config.set scripts/mk/config.kv scripts/mk/features.h shared/libutil/nofork_list.h
161 rm -f scripts/mk/ninja-bin.cache
162}
163
164# config: generate config.set, config.kv, nofork_list.h
165#
166# config.set is what ninqu reads. config.kv is what mkbox and
167# genbox.sh source. both contain the same data in different formats:
168# config.kv is shell-quoted (KEY="VAL"), config.set is unquoted
169# (KEY=VAL) since ninqu's (set-file) takes the rest of the line
170# verbatim
171
172config() {
173 mkdir -p scripts/mk
174
175 kv_file="scripts/mk/config.kv"
176 set_file="scripts/mk/config.set"
177
178 : > "${kv_file}"
179 : > "${set_file}"
180
181 emit() {
182 local k="$1" v="$2"
183 echo "${k}=\"${v}\"" >> "${kv_file}"
184 echo "${k}=${v}" >> "${set_file}"
185 }
186
187 emit VERSION "${VERSION}"
188 emit PREFIX "${PREFIX}"
189 emit MANPREFIX "${MANPREFIX:-/usr/local/share/man}"
190 emit RANLIB "${RANLIB:-ranlib}"
191 emit AR "${AR:-ar}"
192 emit ARFLAGS "${ARFLAGS:-rc}"
193 emit CC "${CC:-cc}"
194 emit CFLAGS "${CFLAGS:--std=c99 -Wall -Wextra -pedantic}"
195 emit LDFLAGS "${LDFLAGS:-} ${LDFLAGS_TLS:-}"
196 emit LDLIBS "${LDLIBS:-} ${LDLIBS_TLS:-}"
197 emit OBJCOPY "${OBJCOPY:-objcopy}"
198 emit LD "${LD:-ld}"
199
200 emit CPPFLAGS_TLS "${CPPFLAGS_TLS:-}"
201 emit LDFLAGS_TLS "${LDFLAGS_TLS:-}"
202 emit LDLIBS_TLS "${LDLIBS_TLS:-}"
203
204 # For when ninqu builds itself, otherwise it won't have this value
205 emit NINJA_TRY "${NINJA_TRY:-samu,ninja}"
206
207 # these seven are only consulted per-tool below (build_CATEGORY_TOOL,
208 # build_CATEGORY_$(BASESTEM)).
209 for g in BUILD_POSIX BUILD_XSI BUILD_NET BUILD_PSEUDO BUILD_EXTRA BUILD_LINUX BUILD_DEV; do
210 eval "val=\${$g:-0}"
211 echo "${g}=\"${val}\"" >> "${kv_file}"
212 done
213
214 # per-tool toggles: scan cmd/<category>/ for tools. a tool is
215 # either a .c/.y file directly in the category dir (perfile)
216 # or a subdirectory (multi-file tool). emit BUILD_<CATEGORY>_<TOOL>
217 # plus lowercase alias for each
218 for category in posix xsi net pseudo extra linux dev; do
219 [ -d "cmd/$category" ] || continue
220 u_category=$(echo "$category" | tr '[:lower:]' '[:upper:]')
221
222 tools=""
223 for d in "cmd/$category"/*; do
224 [ -d "$d" ] && tools="$tools ${d##*/}"
225 done
226 for f in "cmd/$category"/*.c "cmd/$category"/*.y; do
227 [ -f "$f" ] || continue
228 b=${f##*/}
229 b=${b%.c}
230 b=${b%.y}
231 case "$b" in
232 maketab|mknodes|mksyntax|y.tab|*gram.tab) continue ;;
233 esac
234 tools="$tools $b"
235 done
236
237 tools=$(echo "$tools" | tr ' ' '\n' | sort -u | grep -v '^$' || true)
238
239 for b in $tools; do
240 u_tool=$(echo "$b" | tr 'a-z-' 'A-Z_')
241 var_name="BUILD_${u_category}_${u_tool}"
242
243 #if [ "$category" = "dev" ] && [ "$u_tool" = "AS" ]; then
244 # var_name="BUILD_DEV_CC"
245 #fi
246
247 eval "has_override=\${$var_name+yes}"
248 if [ "$has_override" = "yes" ]; then
249 eval "val=\${$var_name}"
250 else
251 eval "val=\$BUILD_${u_category}"
252 fi
253
254 echo "${var_name}=\"${val}\"" >> "${kv_file}"
255 lk=$(echo "$var_name" | tr '[:upper:]' '[:lower:]')
256 echo "${lk}=${val}" >> "${set_file}"
257 done
258 done
259
260 # box's own link line has no per-tool composition the way a
261 # standalone PERFILE binary's EXTRA_LIBS map does (box links
262 # every enabled tool's object into one binary in one shot), so
263 # a tool that needs a library nothing else in the box needs
264 # (yap needs terminfo) can only get it by ninqu.rules reading
265 # a value genconfig.sh already resolved, same as every other
266 # build_* toggle. this is still data, not policy: which
267 # library to add for which tool is decided here, not what to
268 # do with that decision
269 box_extra_libs=""
270 [ "${yap_val:-0}" != "0" ] && box_extra_libs="-ltinfo"
271 emit BOX_EXTRA_LIBS "${box_extra_libs}"
272
273 features=$(set | grep '^FEATURE_[A-Za-z0-9_]*=' | cut -d= -f1 | sort)
274 for f in $features; do
275 eval "val=\$$f"
276 emit "$f" "$val"
277 done
278
279 # every resolved FEATURE_* becomes a #define in one generated
280 # header. config.h's own #ifndef guards still apply to anything
281 # not here (there is nothing not here), and any file gets the
282 # real values just by being compiled with -include, whether or
283 # not it bothers to #include "config.h" itself.
284 features_h="scripts/mk/features.h"
285 {
286 echo "/* autogenerated by genconfig.sh, do not edit */"
287 for f in $features; do
288 eval "fval=\$$f"
289 echo "#define ${f} ${fval}"
290 done
291 } > "${features_h}"
292
293 cppflags_common="-DPREFIX=\"${PREFIX}\" -D_DEFAULT_SOURCE -D_GNU_SOURCE -D_NETBSD_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64 -include ${features_h} ${CPPFLAGS_TLS:-}"
294 cppflags_set="-Ishared ${cppflags_common}"
295 echo "CPPFLAGS=\"${cppflags_set}\"" >> "${kv_file}"
296 echo "CPPFLAGS=${cppflags_set}" >> "${set_file}"
297
298 # shared/tls.h would conflict with the system's tls.h
299 # if we passed -Ishared to build libutil
300 echo "CPPFLAGS_LIBUTIL=\"${cppflags_common}\"" >> "${kv_file}"
301 echo "CPPFLAGS_LIBUTIL=${cppflags_common}" >> "${set_file}"
302
303 nofork_h="shared/libutil/nofork_list.h"
304 echo "/* autogenerated by genconfig.sh */" > "${nofork_h}"
305 echo "static const char *nofork_list[] = {" >> "${nofork_h}"
306 for app in ${NOFORK_LIST}; do
307 echo " \"${app}\"," >> "${nofork_h}"
308 done
309 echo " NULL" >> "${nofork_h}"
310 echo "};" >> "${nofork_h}"
311}
312
313case "$target" in
314 getconf) gen_getconf ;;
315 bc) gen_bc ;;
316 awk) gen_awk ;;
317 sh) gen_sh ;;
318 all)
319 gen_getconf
320 gen_bc
321 gen_awk
322 gen_sh
323 ;;
324 config) config ;;
325 clean) clean ;;
326 *)
327 printf "genconfig: unknown target: %s\n" "$target" >&2
328 printf "usage: %s (config|getconf|bc|awk|sh|all|clean)\n" "$0" >&2
329 exit 1
330 ;;
331esac