master xplshn/aruu / scripts / genconfig.sh
  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