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