master ninqu.rules
  1; genconfig.sh config produces four files from build.cfg in one
  2; invocation: config.set (read by ninqu), config.kv (read by mkman),
  3; nofork_list.h (included by wexec.c), and features.h (-included
  4; into every compile). only CONFIG_SET actually runs the script;
  5; NOFORK_LIST_H and FEATURES_H depend on its output instead of
  6; re-running genconfig.sh themselves, so ninja never schedules two
  7; invocations of the same script concurrently. running it twice in
  8; parallel raced both invocations' truncate-then-append writes to
  9; the same files, byte-interleaving their output into invalid C
 10;
 11(rule CONFIG_SET
 12  (out "scripts/mk/config.set")
 13  (dep "build.cfg" "scripts/genconfig.sh")
 14  (exec sh scripts/genconfig.sh config))
 15
 16(rule NOFORK_LIST_H
 17  (out "shared/libutil/nofork_list.h")
 18  (dep "scripts/mk/config.set")
 19  (exec true))
 20
 21(rule FEATURES_H
 22  (out "scripts/mk/features.h")
 23  (dep "scripts/mk/config.set")
 24  (exec true))
 25
 26(set-file "scripts/mk/config.set")
 27
 28; static libraries: compile every .c in each lib dir then AR
 29;
 30(rule LIBUTF_OBJ
 31  (glob "shared/libutf/*.c")
 32  (dep $(MATCH))
 33  (out "$(STEM).o")
 34  (exec $(CC) $(CPPFLAGS) -Ishared/libutf $(CFLAGS) -c -o $(OUT) $(MATCH)))
 35
 36  (rule LIBUTF
 37  (dep LIBUTF_OBJ)
 38  (out "shared/libutf/libutf.a")
 39  (description "utf-8 and rune handling, linked into every aruu tool")
 40  (feature core-lib)
 41  (exec $(AR) $(ARFLAGS) $(OUT) $(IN)))
 42
 43  (rule LIBUTIL_OBJ
 44  (glob "shared/libutil/*.c")
 45  (dep $(MATCH))
 46  (after NOFORK_LIST_H FEATURES_H)
 47  (out "$(STEM).o")
 48  (exec $(CC) $(CPPFLAGS_LIBUTIL) -Ishared/libutil $(CFLAGS) -c -o $(OUT) $(MATCH)))
 49
 50  (rule LIBUTIL
 51  (dep LIBUTIL_OBJ)
 52  (out "shared/libutil/libutil.a")
 53  (description "allocation, error reporting, and string helpers shared by every tool")
 54  (feature core-lib)
 55  (exec $(AR) $(ARFLAGS) $(OUT) $(IN)))
 56
 57  (rule LIBREDLINE_OBJ
 58  (glob "shared/libredline/*.c")
 59  (dep $(MATCH))
 60  (out "$(STEM).o")
 61  (exec $(CC) $(CPPFLAGS) -Ishared/libredline $(CFLAGS) -c -o $(OUT) $(MATCH)))
 62
 63  (rule LIBREDLINE
 64  (dep LIBREDLINE_OBJ)
 65  (out "shared/libredline/libredline.a")
 66  (description "line editing used by sh and any other interactive tool")
 67  (feature core-lib)
 68  (exec $(AR) $(ARFLAGS) $(OUT) $(IN)))
 69
 70  (rule LIBBLAKE3_OBJ
 71  (glob "shared/libblake3/*.c")
 72  (dep $(MATCH))
 73  (out "$(STEM).o")
 74  (exec $(CC) $(CPPFLAGS) -Ishared/libblake3 $(CFLAGS) -c -o $(OUT) $(MATCH)))
 75
 76  (rule LIBBLAKE3
 77  (dep LIBBLAKE3_OBJ)
 78  (out "shared/libblake3/libblake3.a")
 79  (description "the BLAKE3 hash algorithm, linked into b3sum, not embedded in it")
 80  (feature core-lib)
 81  (exec $(AR) $(ARFLAGS) $(OUT) $(IN)))
 82
 83  (group lib LIBUTF LIBUTIL LIBREDLINE LIBBLAKE3)
 84
 85  ; perfile: every *.c directly inside one of these dirs becomes its
 86  ; own binary of the same name, individually gated by
 87  ; build_<category>_<ident> (lowercase, hyphens and dots turned to
 88  ; underscores, see the $(IDENT) comment above BOX_OBJ below for why)
 89  ; (skip) excludes files that belong to a multi-file tool below instead
 90  ;
 91  ; -lcrypt and -lresolv used to be blanket LDLIBS entries, linked into
 92  ; every single utility whether it called crypt(3)/resolver functions
 93  ; or not. tracing real callers of shared/libutil/passwd.c's pw_check/
 94  ; pw_gensalt (not just direct crypt() calls) turns up exactly four:
 95  ; passwd, mkpasswd, login, su need -lcrypt; host needs -lresolv
 96  ; narrowing this here, not just for tidiness, is what makes aruu
 97  ; buildable on a host whose libc ships a static libresolv/libm but no
 98  ; static libcrypt. b3sum is the one EXTRA_BIN file with its own extra
 99  ; library dependency for a different reason: BLAKE3 used to be
100  ; implemented inline in b3sum.c itself, which meant anything else
101  ; that wanted the same algorithm (ports/pkgtools/libpkg, for hashing
102  ; package archives) had no real library to link against and carried
103  ; its own separate copy instead, with no way to notice the two had
104  ; drifted until one of them turned out to have a bug the other one
105  ; didn't. shared/libblake3 exists so there is exactly one copy
106  (map EXTRA_LIBS
107  (diff "-lm")
108  (wget $(LDLIBS_TLS))
109  (passwd "-lcrypt")
110  (mkpasswd "-lcrypt")
111  (login "-lcrypt")
112  (su "-lcrypt")
113  (host "-lresolv")
114  (b3sum "-Lshared/libblake3 -lblake3"))
115
116  ; every .c file in a category builds into its own binary. (PERFILE)
117  ; only EXTRA_AFTER differs cuz POSIX waits for GETCONF_H, the rest pass ""
118  ; cmd/posix/bc.c overrides this with its own yacc-based rule, so no need for
119  ; PERFILE to have that as a special case
120  ;
121  (template PERFILE (NAME CATEGORY DESC EXTRA_AFTER)
122  (rule $(NAME)
123    (glob "cmd/$(CATEGORY)/*.c")
124  (gate build_$(CATEGORY)_$(IDENT))
125  (dep $(MATCH) LIBUTIL LIBUTF LIBBLAKE3)
126  (after $(EXTRA_AFTER))
127  (out $(STEM))
128  (description $(DESC))
129  (feature perfile)
130  (exec $(CC) $(CPPFLAGS) $(CFLAGS) -o $(OUT) $(IN) $(LDFLAGS) $(LDLIBS) $(EXTRA_LIBS:$(BASESTEM)))))
131
132  (use PERFILE POSIX_BIN posix "one POSIX2024 utility per source file under cmd/posix" GETCONF_H)
133  (use PERFILE LINUX_BIN linux "linux-only utility, one source file per binary under cmd/linux" "")
134  (use PERFILE NET_BIN net "networking utility, one source file per binary under cmd/net" "")
135  (use PERFILE XSI_BIN xsi "xsi-specified utility, one source file per binary under cmd/xsi" "")
136  (use PERFILE PSEUDO_BIN pseudo "common but undefined utility, one source file per binary under cmd/pseudo" "")
137  (use PERFILE EXTRA_BIN extra "extra utility outside POSIX/XSI scope, one source file per binary under cmd/extra" "")
138
139  (group posix lib POSIX_BIN POSIX_BC POSIX_AWK POSIX_SH POSIX_MAKE)
140  (group linux lib LINUX_BIN)
141  (group net lib NET_BIN)
142  (group xsi lib XSI_BIN)
143  (group pseudo lib PSEUDO_BIN)
144  (group extra lib EXTRA_BIN EXTRA_YAP)
145
146  ; codegen: generate sources consumed by the build rules below. ungated
147  ; ones always run. conditional ones use (req) so they only run when
148  ; their inputs exists in this checkout
149  ;
150  (rule GETCONF_H
151  (gate build_posix_getconf)
152  (out "cmd/posix/getconf.h")
153  (dep "scripts/getconf.sh")
154  (exec sh scripts/genconfig.sh getconf))
155
156  (rule POSIX_BC_C
157  (gate build_posix_bc)
158  (req "cmd/posix/bc.y")
159  (out "cmd/posix/bc.c")
160  (dep "cmd/posix/bc.y")
161  (exec sh scripts/genconfig.sh bc))
162
163  (rule POSIX_AWK_C
164  (gate build_posix_awk)
165  (out "cmd/posix/awk/awkgram.tab.c")
166  (dep "cmd/posix/awk/awkgram.y")
167  (exec sh scripts/genconfig.sh awk))
168
169  (rule POSIX_SH_C
170  (gate build_posix_sh)
171  (out "cmd/posix/sh/syntax.c")
172  (dep "cmd/posix/sh/mksyntax.c" "cmd/posix/sh/mknodes.c" "cmd/posix/sh/mkbuiltins" "cmd/posix/sh/mktokens")
173  (exec sh scripts/genconfig.sh sh))
174
175  (rule DEVCONF
176  (req "cmd/dev/configure")
177  (out "cmd/dev/config.h")
178  (dep "cmd/dev/configure")
179  (phony)
180  (exec sh cmd/dev/configure))
181
182  (group codegen GETCONF_H POSIX_BC_C POSIX_AWK_C POSIX_SH_C DEVCONF)
183
184  ; multi: one binary from all .c files in one or more dirs (minus skip)
185  ; codegen must run first so generated sources are present before these
186  ; globs collect them
187  ;
188  (rule POSIX_BC_OBJ
189  (glob "cmd/posix/bc.c")
190  (gate build_posix_bc)
191  (dep $(MATCH))
192  (after POSIX_BC_C)
193  (out "$(STEM).o")
194  (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
195
196  (rule POSIX_BC
197  (gate build_posix_bc)
198  (member posix bc)
199  (dep POSIX_BC_OBJ LIBUTIL LIBUTF)
200  (out "cmd/posix/bc")
201  (exec $(CC) -o $(OUT) $(IN) $(LDFLAGS) $(LDLIBS) -lm))
202
203  (rule POSIX_AWK_OBJ
204  (glob "cmd/posix/awk/*.c")
205  (gate build_posix_awk)
206  (skip "maketab.c")
207  (dep $(MATCH))
208  (after POSIX_AWK_C)
209  (out "$(STEM).o")
210  (exec $(CC) $(CPPFLAGS) -Icmd/posix/awk $(CFLAGS) -c -o $(OUT) $(MATCH)))
211
212  (rule POSIX_AWK
213  (gate build_posix_awk)
214  (member posix awk)
215  (dep POSIX_AWK_OBJ LIBUTIL LIBUTF)
216  (out "cmd/posix/awk/awk")
217  (exec $(CC) -o $(OUT) $(IN) $(LDFLAGS) $(LDLIBS) -lm))
218
219  (rule POSIX_SH_OBJ
220  (glob "cmd/posix/sh/*.c")
221  (gate build_posix_sh)
222  (skip "mknodes.c" "mksyntax.c")
223  (dep $(MATCH))
224  (after POSIX_SH_C)
225  (out "$(STEM).o")
226  (exec $(CC) $(CPPFLAGS) -DSHELL -Icmd/posix/sh $(CFLAGS) -c -o $(OUT) $(MATCH)))
227
228  (rule POSIX_SH
229  (gate build_posix_sh)
230  (member posix sh)
231  (dep POSIX_SH_OBJ LIBUTIL LIBUTF LIBREDLINE)
232  (out "cmd/posix/sh/sh")
233  (exec $(CC) -o $(OUT) $(IN) $(LDFLAGS) $(LDLIBS)))
234
235  (rule POSIX_MAKE_OBJ
236  (glob "cmd/posix/make/*.c")
237  (gate build_posix_make)
238  (dep $(MATCH))
239  (out "$(STEM).o")
240  (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
241
242  (rule POSIX_MAKE
243  (gate build_posix_make)
244  (member posix make)
245  (dep POSIX_MAKE_OBJ LIBUTIL LIBUTF)
246  (out "cmd/posix/make/make")
247  (exec $(CC) -o $(OUT) $(IN) $(LDFLAGS) $(LDLIBS)))
248
249  (rule EXTRA_YAP_OBJ
250  (glob "cmd/extra/yap/*.c")
251  (gate build_extra_yap)
252  (dep $(MATCH))
253  (out "$(STEM).o")
254  (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
255
256  (rule EXTRA_YAP
257  (gate build_extra_yap)
258  (member extra yap)
259  (dep EXTRA_YAP_OBJ LIBUTIL LIBUTF)
260  (out "cmd/extra/yap/yap")
261  (exec $(CC) -o $(OUT) $(IN) $(LDFLAGS) $(LDLIBS) -ltinfo))
262
263  ; (member GROUP ALIAS) gives a rule a qualified user-facing name
264  ; (e.g. posix/bc) while keeping its internal name (POSIX_BC) for deps,
265  ; gates, and other references
266  ;
267
268  ; dev: the aruu toolchain. ar/as/ld are standalone and cc
269  ; shares one object pool linked into three frontends
270  ;
271  (rule AR_OBJ
272  (glob "cmd/dev/ar/*.c")
273  (gate build_dev_ar)
274  (skip "tinyar.c")
275  (dep $(MATCH))
276  (out "$(STEM).o")
277  (exec $(CC) $(CPPFLAGS) -Icmd/dev/ar $(CFLAGS) -c -o $(OUT) $(MATCH)))
278
279  (rule AR
280  (gate build_dev_ar)
281  (dep AR_OBJ LIBUTIL LIBUTF)
282  ; static: these tools run inside whatever rootfs they target via
283  ; nac, not just the host that built them, and never need the
284  ; project-wide LDLIBS default (openssl, unused by any of them)
285  (out "cmd/dev/ar/ar")
286  (exec $(CC) -static -o $(OUT) $(IN) $(LDFLAGS)))
287
288  (rule TCUTIL_OBJ
289  (glob "shared/libtcutil/*.c" "shared/libtcutil/elf/*.c" "shared/libtcutil/ar/*.c")
290  (gate build_dev_cc)
291  (dep $(MATCH))
292  (out "$(STEM).o")
293  (exec $(CC) $(CPPFLAGS) -Ishared/libtcutil -Ishared/libtcutil/elf -Ishared/libtcutil/ar -Icmd/dev $(CFLAGS) -c -o $(OUT) $(MATCH)))
294
295  (rule AS_OBJ
296  (glob "cmd/dev/as/*.c" "cmd/dev/as/arch/x64/*.c")
297  (gate build_dev_cc)
298  (dep $(MATCH))
299  (out "$(STEM).o")
300  (exec $(CC) $(CPPFLAGS) -Ishared/libtcutil -Ishared/libtcutil/elf -Ishared/libtcutil/ar -Icmd/dev -Icmd/dev/as -Icmd/dev/as/arch/x64 -Ishared $(CFLAGS) -c -o $(OUT) $(MATCH)))
301
302  (rule AS
303  (gate build_dev_cc)
304  (dep AS_OBJ TCUTIL_OBJ LIBUTIL LIBUTF)
305  (out "cmd/dev/as/as")
306  (exec $(CC) -static -o $(OUT) $(IN) $(LDFLAGS)))
307
308  (rule LD_OBJ
309  (glob "cmd/dev/ld/*.c" "cmd/dev/ld/arch/*/*.c")
310  (gate build_dev_ld)
311  (dep $(MATCH))
312  (out "$(STEM).o")
313  (exec $(CC) $(CPPFLAGS) -Icmd/dev/ld $(CFLAGS) -c -o $(OUT) $(MATCH)))
314
315  (rule LD
316  (gate build_dev_ld)
317  (dep LD_OBJ LIBUTIL LIBUTF)
318  (out "cmd/dev/ld/ld")
319  (exec $(CC) -static -o $(OUT) $(IN) $(LDFLAGS)))
320
321  ; CCPOOL_OBJ globs the whole directory. the entry points below use
322  ; literal globs, so the override excludes them automatically
323  (rule CCPOOL_OBJ
324  (glob "cmd/dev/cc/*.c")
325  (gate build_dev_cc)
326  (dep $(MATCH))
327  (out "$(STEM).o")
328  (exec $(CC) $(CPPFLAGS) -Icmd/dev/cc $(CFLAGS) -c -o $(OUT) $(MATCH)))
329
330  (rule CC1_OBJ
331  (glob "cmd/dev/cc/cc1.c")
332  (gate build_dev_cc)
333  (dep $(MATCH))
334  (out "$(STEM).o")
335  (exec $(CC) $(CPPFLAGS) -Icmd/dev/cc $(CFLAGS) -c -o $(OUT) $(MATCH)))
336
337  ; util.c is its own rule, not folded into CCPOOL_OBJ's wildcard or
338  ; CCDRV_OBJ's own glob: cc1, cpp, and cc (the driver) all call its
339  ; helpers (fatal, xmalloc, arrayadd, ...), but only cc has its own
340  ; main(), so driver.c cannot be a shared link input the other two
341  ; binaries pull in too
342  (rule UTIL_OBJ
343  (glob "cmd/dev/cc/util.c")
344  (gate build_dev_cc)
345  (dep $(MATCH))
346  (out "$(STEM).o")
347  (exec $(CC) $(CPPFLAGS) -Icmd/dev/cc $(CFLAGS) -c -o $(OUT) $(MATCH)))
348
349  (rule CC1
350  (gate build_dev_cc)
351  (dep CC1_OBJ CCPOOL_OBJ UTIL_OBJ LIBUTIL LIBUTF)
352  (out "cmd/dev/cc/cc1")
353  (exec $(CC) -static -o $(OUT) $(IN) $(LDFLAGS)))
354
355  (rule CPP_OBJ
356  (glob "cmd/dev/cc/cpp.c")
357  (gate build_dev_cc)
358  (dep $(MATCH))
359  (out "$(STEM).o")
360  (exec $(CC) $(CPPFLAGS) -Icmd/dev/cc $(CFLAGS) -c -o $(OUT) $(MATCH)))
361
362  (rule CPP
363  (gate build_dev_cc)
364  (dep CPP_OBJ CCPOOL_OBJ UTIL_OBJ LIBUTIL LIBUTF)
365  (out "cmd/dev/cc/cpp")
366  (exec $(CC) -static -o $(OUT) $(IN) $(LDFLAGS)))
367
368  (rule CCDRV_OBJ
369  (glob "cmd/dev/cc/driver.c")
370  (gate build_dev_cc)
371  (dep $(MATCH))
372  (out "$(STEM).o")
373  (exec $(CC) $(CPPFLAGS) -Icmd/dev/cc $(CFLAGS) -c -o $(OUT) $(MATCH)))
374
375  (rule CCDRV
376  (gate build_dev_cc)
377  (dep CCDRV_OBJ UTIL_OBJ LIBUTIL LIBUTF)
378  (out "cmd/dev/cc/cc")
379  (exec $(CC) -static -o $(OUT) $(IN) $(LDFLAGS)))
380
381  ; like BOX_OBJ: rename main and hide other globals so ninqu can link
382  ; into aruu-box. it is still not reentrant, so it stays out of
383  ; NOFORK_LIST
384  ;
385  (rule NINQU_BOX_OBJ
386  (glob "cmd/dev/ninqu/*.c")
387  (gate build_dev_ninqu)
388  (dep $(MATCH))
389  (after LIBUTIL LIBUTF LIBREDLINE NOFORK_LIST_H FEATURES_H GETCONF_H)
390  (out ".box/cmd/dev/$(BASESTEM).o")
391  (description "ninqu as a box builtin, main renamed for the box dispatch table")
392  (shell "$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH) && sym=`echo $(BASESTEM) | tr '.-' '__'`_main && $(OBJCOPY) --redefine-sym main=$sym $(OUT) && $(OBJCOPY) --keep-global-symbol=$sym $(OUT)")
393  (group box))
394
395  (group dev lib DEVCONF AR AS LD CC1 CPP CCDRV)
396
397  ; man pages: build mdoc and text pages from ?man-marked sources
398  ;
399  (rule MKMAN
400  (dir "scripts/mkman")
401  (dep "glob:scripts/mkman/*.go")
402  (out "scripts/mkman/mkman")
403  (exec go build -o mkman .))
404
405  (rule MAN_MDOC
406  (glob "cmd/**/*.c")
407  (mark "?man")
408  (dep (after MKMAN))
409  (out "man/man$(SEC_OF:$(DIR))/$(BASESTEM).$(SEC_OF:$(DIR))")
410  (exec scripts/mkman/mkman -fmt mdoc -config scripts/mk/config.kv $(MATCH))
411  (redirect))
412
413  (rule MAN_TXT
414  (glob "cmd/**/*.c")
415  (mark "?man")
416  (dep (after MKMAN))
417  (out "man/man$(SEC_OF:$(DIR))/$(BASESTEM).$(SEC_OF:$(DIR)).txt")
418  (exec scripts/mkman/mkman -fmt txt -config scripts/mk/config.kv $(MATCH))
419  (redirect))
420
421  (map SEC_OF
422  (cmd/posix 1)
423  (cmd/linux 8)
424  (cmd/net 8)
425  (cmd/xsi 1)
426  (cmd/pseudo 1)
427  (cmd/extra 1)
428  (cmd/dev 1))
429
430  (group man MAN_MDOC MAN_TXT)
431
432  (rule CLEAN
433  (phony)
434  (shell "
435  printf ' CLEAN object files\n'
436  find shared cmd -name '*.o' -exec rm -f {} +
437
438  printf ' CLEAN static libraries\n'
439  find shared -name '*.a' -exec rm -f {} +
440
441  printf ' CLEAN compiled binaries\n'
442  find cmd -type f ! -name '*.*' -perm -100 -exec rm -f {} +
443
444  sh scripts/genconfig.sh clean
445  rm -f scripts/mk/config.mk scripts/mk/config.kv scripts/mk/rules.mk shared/libutil/nofork_list.h
446
447  printf ' CLEAN build tools\n'
448  rm -f scripts/mkman/mkman
449
450  printf ' CLEAN dev artifacts\n'
451  rm -f cmd/dev/cc/cc1 cmd/dev/cc/cpp cmd/dev/as/as cmd/dev/ld/ld cmd/dev/ar/ar
452  rm -f shared/libaruuelf.so
453  rm -f cmd/dev/config.h cmd/dev/cc/config.h cmd/dev/version.h
454
455  printf ' CLEAN misc\n'
456  rm -rf man/man1 man/man8
457  rm -rf aruu-box .box
458  rm -rf .ninja_log .ninja_deps
459
460  printf ' CLEAN done\n'
461  "))
462
463  (group clean CLEAN)
464
465  ; install every built executable and generated man page, plus
466  ; shared/libutil itself as a real linkable library: other MICE
467  ; components (ports/pkgtools, nac) should link against this, not carry
468  ; their own copy of ealloc/eprintf/wexec/etc alongside it
469  (rule INSTALL
470  (phony)
471  (after ALL_BINS LIBUTIL MAN_MDOC MAN_TXT)
472  (shell "
473  d=\"${DESTDIR:-}$(PREFIX)\"
474  m=\"${DESTDIR:-}$(MANPREFIX)\"
475  mkdir -p \"$d/bin\" \"$m/man1\" \"$m/man8\" \"$d/lib\" \"$d/include/aruu\"
476
477  find cmd -type f ! -name '*.*' -perm -100 | while read -r bin; do
478  name=${bin##*/}
479      printf '  INSTALL %s/bin/%s\n' '$(PREFIX)' \"$name\"
480      cp \"$bin\" \"$d/bin/$name\"
481      chmod 755 \"$d/bin/$name\"
482    done
483
484    find man/man1 man/man8 -type f -name '*.[18]' 2>/dev/null | while read -r f; do
485      sec=${f%/*}; sec=${sec##*/}
486      name=${f##*/}
487      cp \"$f\" \"$m/$sec/$name\"
488    done
489
490    printf '  INSTALL %s/lib/libaruu.a\n' '$(PREFIX)'
491    cp shared/libutil/libutil.a \"$d/lib/libaruu.a\"
492    chmod 644 \"$d/lib/libaruu.a\"
493    for h in shared/util.h shared/wexec.h shared/arg.h shared/compat.h; do
494      printf '  INSTALL %s/include/aruu/%s\n' '$(PREFIX)' \"${h##*/}\"
495      cp \"$h\" \"$d/include/aruu/${h##*/}\"
496      chmod 644 \"$d/include/aruu/${h##*/}\"
497    done
498  "))
499
500(group install INSTALL)
501
502; same body as INSTALL, minus the dev (compiler toolchain) category:
503; what a MICE port needs to put a shell and coreutils on a rootfs,
504; not a hard dependency on the separate, not-yet-portable dev build
505(rule INSTALL_BASE
506  (phony)
507  (after POSIX_BIN LINUX_BIN NET_BIN XSI_BIN PSEUDO_BIN EXTRA_BIN
508         POSIX_BC POSIX_AWK POSIX_SH POSIX_MAKE EXTRA_YAP
509         LIBUTIL MAN_MDOC MAN_TXT)
510  (shell "
511    d=\"${DESTDIR:-}$(PREFIX)\"
512    m=\"${DESTDIR:-}$(MANPREFIX)\"
513    mkdir -p \"$d/bin\" \"$m/man1\" \"$m/man8\" \"$d/lib\" \"$d/include/aruu\"
514
515    find cmd -type f ! -name '*.*' -perm -100 ! -path 'cmd/dev/*' | while read -r bin; do
516  name=${bin##*/}
517      printf '  INSTALL %s/bin/%s\n' '$(PREFIX)' \"$name\"
518      cp \"$bin\" \"$d/bin/$name\"
519      chmod 755 \"$d/bin/$name\"
520    done
521
522    find man/man1 man/man8 -type f -name '*.[18]' 2>/dev/null | while read -r f; do
523      sec=${f%/*}; sec=${sec##*/}
524      name=${f##*/}
525      cp \"$f\" \"$m/$sec/$name\"
526    done
527
528    printf '  INSTALL %s/lib/libaruu.a\n' '$(PREFIX)'
529    cp shared/libutil/libutil.a \"$d/lib/libaruu.a\"
530    chmod 644 \"$d/lib/libaruu.a\"
531    for h in shared/util.h shared/wexec.h shared/arg.h shared/compat.h; do
532      printf '  INSTALL %s/include/aruu/%s\n' '$(PREFIX)' \"${h##*/}\"
533      cp \"$h\" \"$d/include/aruu/${h##*/}\"
534      chmod 644 \"$d/include/aruu/${h##*/}\"
535    done
536  "))
537
538(group install-base INSTALL_BASE)
539
540; remove exactly what install would have copied
541(rule REMOVE
542  (phony)
543  (shell "
544    find cmd -type f ! -name '*.*' -perm -100 | while read -r bin; do
545      name=${bin##*/}
546      printf '  REMOVE %s/bin/%s\n' '$(PREFIX)' \"$name\"
547      rm -f \"$(PREFIX)/bin/$name\"
548    done
549
550    find man/man1 man/man8 -type f -name '*.[18]' 2>/dev/null | while read -r f; do
551      sec=${f%/*}; sec=${sec##*/}
552      name=${f##*/}
553      printf '  REMOVE %s/%s/%s\n' '$(MANPREFIX)' \"$sec\" \"$name\"
554      rm -f \"$(MANPREFIX)/$sec/$name\"
555    done
556
557    printf '  REMOVE %s/lib/libaruu.a\n' '$(PREFIX)'
558    rm -f \"$(PREFIX)/lib/libaruu.a\"
559    for h in util.h wexec.h arg.h compat.h; do
560      printf '  REMOVE %s/include/aruu/%s\n' '$(PREFIX)' \"$h\"
561      rm -f \"$(PREFIX)/include/aruu/$h\"
562    done
563    rmdir \"$(PREFIX)/include/aruu\" 2>/dev/null || :
564  "))
565
566(group remove REMOVE)
567(group uninstall REMOVE)
568
569(rule FMT
570  (phony)
571  (shell "
572    printf '  FMT cmd and shared (skipping dir tools)\n'
573    find cmd shared -type f \( -name '*.c' -o -name '*.h' \) \
574      ! -path 'cmd/posix/awk/*' \
575      ! -path 'cmd/posix/sh/*' \
576      ! -path 'cmd/posix/make/*' \
577      ! -path 'cmd/extra/yap/*' \
578  ! -path 'cmd/posix/bc.c' \
579  -print | xargs -r clang-format -i
580  "))
581
582  (rule FMT_ALL
583  (phony)
584  (shell "
585  printf ' FMT cmd and shared (including dir tools)\n'
586  find cmd shared -type f \( -name '*.c' -o -name '*.h' \) -print \
587  | xargs -r clang-format -i
588  "))
589
590  (group fmt FMT)
591  (group fmt-all FMT_ALL)
592
593  ; box: one executable containing every enabled tool, dispatched through
594  ; a table. a single-file tool just gets its main renamed by BOX_OBJ
595  ; multi-file tools (POSIX_BC, POSIX_AWK, POSIX_SH, POSIX_MAKE, EXTRA_YAP)
596  ; go through a partial link (ld -r) first, merging their objects into
597  ; one before main gets renamed and helper symbols hidden. replaces
598  ; scripts/mkbox
599  ;
600  ; object files live under .box/, mirroring the cmd/ layout
601  ;
602  ; ninqu determines which tools enter the box using the gate on BOX_OBJ
603  ; BOX_DISPATCH reads the object paths passed on argv rather than consulting
604  ; build.cfg or config.set directly
605  ;
606
607  ; CATEGORY_OF maps a directory path to its category name, matching the toggles
608  ; in build.cfg. since BOX_OBJ globs multiple directories, its gate evaluates the
609  ; category dynamically based on the match directory
610  ;
611  ; gates evaluate $(IDENT) instead of $(BASESTEM) because toggle names must be
612  ; valid shell variables. genconfig.sh replaces hyphens and periods with
613  ; underscores, and $(IDENT) applies the same normalization so they match
614  ;
615  (map CATEGORY_OF
616  (cmd/posix posix)
617  (cmd/linux linux)
618  (cmd/net net)
619  (cmd/pseudo pseudo)
620  (cmd/xsi xsi)
621  (cmd/extra extra))
622
623  ; tool basestems match their c symbols using a shell transform (tr '.-' '__'
624  ; and appending _main) in both the objcopy step and in genbox.sh. a static map
625  ; is unnecessary because no current tools require custom overrides, and
626  ; genbox.sh only receives paths via argv
627  ;
628
629  ; the match directory picks the gate and output path, so one rule covers
630  ; all six categories. bc.c inside cmd/posix is excluded automatically by
631  ; the literal-glob override mechanism
632  ;
633  (rule BOX_OBJ
634  (glob "cmd/posix/*.c" "cmd/linux/*.c" "cmd/net/*.c" "cmd/pseudo/*.c"
635        "cmd/xsi/*.c" "cmd/extra/*.c")
636  (gate build_$(CATEGORY_OF:$(DIR))_$(IDENT))
637  (dep $(MATCH))
638  (after LIBUTIL LIBUTF LIBREDLINE NOFORK_LIST_H FEATURES_H GETCONF_H)
639  (out ".box/$(DIR)/$(BASESTEM).o")
640  (description "one object per enabled tool, main renamed for the box dispatch table")
641  (shell "$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH) && sym=`echo $(BASESTEM) | tr '.-' '__'`_main && $(OBJCOPY) --redefine-sym main=$sym $(OUT) && $(OBJCOPY) --keep-global-symbol=$sym $(OUT)")
642  (group box))
643
644  ; POSIX_BC, POSIX_AWK, POSIX_SH, POSIX_MAKE, and EXTRA_YAP cant go through
645  ; the single-file objcopy step, their global helper symbols would collide
646  ; once linked into one binary. a partial link (ld -r) resolves those
647  ; internal references first, then main gets renamed and the rest hidden
648  ;
649  ; combined objects use the layout .box/cmd/<category>/<tool>.o so genbox.sh
650  ; can parse them without structural modifications
651  ;
652  (rule POSIX_BC_BOX_OBJ
653  (glob "cmd/posix/bc.c")
654  (gate build_posix_bc)
655  (dep $(MATCH))
656  (after POSIX_BC_C)
657  (out ".box/parts/$(STEM).o")
658  (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
659
660  (rule POSIX_BC_BOX
661  (gate build_posix_bc)
662  (dep POSIX_BC_BOX_OBJ)
663  (out ".box/cmd/posix/bc.o")
664  (description "bc's objects combined into one relocatable object, main renamed for the box dispatch table")
665  (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=bc_main $(OUT) && $(OBJCOPY) --keep-global-symbol=bc_main $(OUT)")
666  (group box))
667
668  (rule POSIX_AWK_BOX_OBJ
669  (glob "cmd/posix/awk/*.c")
670  (gate build_posix_awk)
671  (skip "maketab.c")
672  (dep $(MATCH))
673  (after POSIX_AWK_C)
674  (out ".box/parts/$(STEM).o")
675  (exec $(CC) $(CPPFLAGS) -Icmd/posix/awk $(CFLAGS) -c -o $(OUT) $(MATCH)))
676
677  (rule POSIX_AWK_BOX
678  (gate build_posix_awk)
679  (dep POSIX_AWK_BOX_OBJ)
680  (out ".box/cmd/posix/awk.o")
681  (description "awk's objects combined into one relocatable object, main renamed for the box dispatch table")
682  (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=awk_main $(OUT) && $(OBJCOPY) --keep-global-symbol=awk_main $(OUT)")
683  (group box))
684
685  (rule POSIX_SH_BOX_OBJ
686  (glob "cmd/posix/sh/*.c")
687  (gate build_posix_sh)
688  (skip "mknodes.c" "mksyntax.c")
689  (dep $(MATCH))
690  (after POSIX_SH_C)
691  (out ".box/parts/$(STEM).o")
692  (exec $(CC) $(CPPFLAGS) -DSHELL -Icmd/posix/sh $(CFLAGS) -c -o $(OUT) $(MATCH)))
693
694  (rule POSIX_SH_BOX
695  (gate build_posix_sh)
696  (dep POSIX_SH_BOX_OBJ)
697  (out ".box/cmd/posix/sh.o")
698  (description "sh's objects combined into one relocatable object, main renamed for the box dispatch table")
699  (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=sh_main $(OUT) && $(OBJCOPY) --keep-global-symbol=sh_main $(OUT)")
700  (group box))
701
702  (rule POSIX_MAKE_BOX_OBJ
703  (glob "cmd/posix/make/*.c")
704  (gate build_posix_make)
705  (dep $(MATCH))
706  (out ".box/parts/$(STEM).o")
707  (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
708
709  (rule POSIX_MAKE_BOX
710  (gate build_posix_make)
711  (dep POSIX_MAKE_BOX_OBJ)
712  (out ".box/cmd/posix/make.o")
713  (description "make's objects combined into one relocatable object, main renamed for the box dispatch table")
714  (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=make_main $(OUT) && $(OBJCOPY) --keep-global-symbol=make_main $(OUT)")
715  (group box))
716
717  (rule EXTRA_YAP_BOX_OBJ
718  (glob "cmd/extra/yap/*.c")
719  (gate build_extra_yap)
720  (dep $(MATCH))
721  (out ".box/parts/$(STEM).o")
722  (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
723
724  (rule EXTRA_YAP_BOX
725  (gate build_extra_yap)
726  (dep EXTRA_YAP_BOX_OBJ)
727  (out ".box/cmd/extra/yap.o")
728  (description "yap's objects combined into one relocatable object, main renamed for the box dispatch table")
729  (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=yap_main $(OUT) && $(OBJCOPY) --keep-global-symbol=yap_main $(OUT)")
730  (group box))
731
732  ; genbox.sh relies on arguments passed by ninqu, where each argument matches
733  ; an output from BOX_OBJ or a custom box rule. the script derives categories
734  ; and tool names directly from these paths and outputs the dispatch code
735  ;
736  (rule BOX_DISPATCH
737  (dep BOX_OBJ POSIX_BC_BOX POSIX_AWK_BOX POSIX_SH_BOX POSIX_MAKE_BOX EXTRA_YAP_BOX NINQU_BOX_OBJ)
738  (out ".box/aruu-box.c")
739  (description "dispatch table and main() for aruu-box, generated from the enabled tool list")
740  (exec scripts/genbox.sh $(IN))
741  (redirect)
742  (group box))
743
744  ; libm is linked unconditionally because bc requires it, and unused library
745  ; references are safe. modern toolchains omit unreferenced archive components
746  ; yap requires terminfo, which is an optional system dependency. linking it
747  ; unconditionally breaks builds on systems without ncurses-dev when yap is
748  ; disabled. genconfig.sh exports BOX_EXTRA_LIBS as "-ltinfo" or an empty string
749  ; based on whether yap is enabled
750  ;
751  (rule BOX
752  (dep BOX_DISPATCH BOX_OBJ POSIX_BC_BOX POSIX_AWK_BOX POSIX_SH_BOX POSIX_MAKE_BOX EXTRA_YAP_BOX NINQU_BOX_OBJ LIBUTIL LIBUTF LIBREDLINE)
753  (out "aruu-box")
754  (description "single multi-call binary, one dispatch table for every enabled tool")
755  (shell "$(CC) $(CPPFLAGS) $(CFLAGS) -o $(OUT) $(IN) $(LDFLAGS) $(LDLIBS) $(BOX_EXTRA_LIBS)")
756  (group box))
757
758  (group box BOX)
759
760  ; -------------------------------------------------------------------
761  ; top level. (group all) contains every build target except clean
762  ; and box. box is separate because it produces a different binary
763  ; with different .o files, and mixing it with standalone builds
764  ; would cause confusion. clean is never in all because it removes
765  ; what all builds
766  ; -------------------------------------------------------------------
767
768  (rule ALL_BINS
769  (phony)
770  (after LIBUTIL LIBUTF LIBREDLINE
771  POSIX_BIN LINUX_BIN NET_BIN XSI_BIN
772  PSEUDO_BIN EXTRA_BIN
773  POSIX_BC POSIX_AWK POSIX_SH POSIX_MAKE EXTRA_YAP
774  AR AS LD CC1 CPP CCDRV)
775  (exec true))
776
777  (group all ALL_BINS)
778
779  (group default all)
780
781  ; meta-targets: apply an override, then build a normal target under
782  ; it. invoke as `ninqu META.TARGET` (`ninqu debug.posix`) or bare
783  ; `ninqu META` to apply the override and build (group default)
784  ;
785  (meta debug
786  (set CFLAGS "$(CFLAGS) -g -O0"))
787
788  (meta release
789  (set CFLAGS "$(CFLAGS) -O2 -DNDEBUG"))