commit b52592b
xplshn
·
2026-07-06 15:10:30 +0000 UTC
parent c9e91a9
Get rif of Make/Makefile.sh and a bunch of bullshit Signed-off-by: xplshn <anto@xplshn.com.ar>
M
Makefile
+76,
-847
1@@ -1,848 +1,77 @@
2--include scripts/mk/config.mk
3-
4-.SUFFIXES: .y .o .c
5-HDR =\
6- shared/arg.h\
7- shared/compat.h\
8- shared/config.h\
9- shared/crypt.h\
10- shared/wexec.h\
11- shared/fs.h\
12- shared/md5.h\
13- shared/queue.h\
14- shared/sha1.h\
15- shared/sha224.h\
16- shared/sha256.h\
17- shared/sha384.h\
18- shared/sha512.h\
19- shared/sha512-224.h\
20- shared/sha512-256.h\
21- shared/text.h\
22- shared/utf.h\
23- shared/util.h\
24- shared/passwd.h\
25- shared/paths.h\
26- shared/reboot.h\
27- shared/rtc.h\
28- shared/proc.h\
29- shared/tls.h
30-
31-LIBUTFOBJ =\
32- shared/libutf/fgetrune.o\
33- shared/libutf/fputrune.o\
34- shared/libutf/isalnumrune.o\
35- shared/libutf/isalpharune.o\
36- shared/libutf/isblankrune.o\
37- shared/libutf/iscntrlrune.o\
38- shared/libutf/isdigitrune.o\
39- shared/libutf/isgraphrune.o\
40- shared/libutf/isprintrune.o\
41- shared/libutf/ispunctrune.o\
42- shared/libutf/isspacerune.o\
43- shared/libutf/istitlerune.o\
44- shared/libutf/isxdigitrune.o\
45- shared/libutf/lowerrune.o\
46- shared/libutf/rune.o\
47- shared/libutf/runetype.o\
48- shared/libutf/upperrune.o\
49- shared/libutf/utf.o\
50- shared/libutf/utftorunestr.o
51-
52-LIBUTILOBJ =\
53- shared/libutil/concat.o\
54- shared/libutil/cp.o\
55- shared/libutil/crypt.o\
56- shared/libutil/confirm.o\
57- shared/libutil/diffutil.o\
58- shared/libutil/wexec.o\
59- shared/libutil/ealloc.o\
60- shared/libutil/enmasse.o\
61- shared/libutil/eprintf.o\
62- shared/libutil/eregcomp.o\
63- shared/libutil/estrtod.o\
64- shared/libutil/fnck.o\
65- shared/libutil/fshut.o\
66- shared/libutil/getlines.o\
67- shared/libutil/human.o\
68- shared/libutil/linecmp.o\
69- shared/libutil/md5.o\
70- shared/libutil/memmem.o\
71- shared/libutil/mkdirp.o\
72- shared/libutil/mode.o\
73- shared/libutil/parseoffset.o\
74- shared/libutil/putword.o\
75- shared/libutil/reallocarray.o\
76- shared/libutil/recurse.o\
77- shared/libutil/rm.o\
78- shared/libutil/sha1.o\
79- shared/libutil/sha224.o\
80- shared/libutil/sha256.o\
81- shared/libutil/sha384.o\
82- shared/libutil/sha512.o\
83- shared/libutil/sha512-224.o\
84- shared/libutil/sha512-256.o\
85- shared/libutil/strcasestr.o\
86- shared/libutil/strlcat.o\
87- shared/libutil/strlcpy.o\
88- shared/libutil/strsep.o\
89- shared/libutil/strnsubst.o\
90- shared/libutil/strtonum.o\
91- shared/libutil/writeall.o\
92- shared/libutil/unescape.o\
93- shared/libutil/agetcwd.o\
94- shared/libutil/agetline.o\
95- shared/libutil/apathmax.o\
96- shared/libutil/estrtol.o\
97- shared/libutil/estrtoul.o\
98- shared/libutil/explicit_bzero.o\
99- shared/libutil/passwd.o\
100- shared/libutil/proc.o\
101- shared/libutil/tty.o\
102- shared/libutil/fconcat.o\
103- shared/libutil/recurse_dir.o\
104- shared/libutil/sig.o\
105- shared/libutil/net.o\
106- shared/libutil/sysinfo.o\
107- shared/libutil/tls.o\
108- shared/libutil/diskutil.o
109-
110-LIBREDLINEOBJ =\
111- shared/libredline/redline.o
112-
113-LIB = shared/libredline/libredline.a shared/libutil/libutil.a shared/libutf/libutf.a
114-
115-POSIX_BIN_ALL =\
116- cmd/posix/basename\
117- cmd/posix/cal\
118- cmd/posix/cat\
119- cmd/posix/chgrp\
120- cmd/posix/chmod\
121- cmd/posix/chown\
122- cmd/posix/cksum\
123- cmd/posix/cmp\
124- cmd/posix/comm\
125- cmd/posix/cp\
126- cmd/posix/cut\
127- cmd/posix/date\
128- cmd/posix/dd\
129- cmd/posix/df\
130- cmd/posix/diff\
131- cmd/posix/patch\
132- cmd/posix/dirname\
133- cmd/posix/du\
134- cmd/posix/echo\
135- cmd/posix/ed\
136- cmd/posix/env\
137- cmd/posix/expand\
138- cmd/posix/expr\
139- cmd/posix/false\
140- cmd/posix/find\
141- cmd/posix/fold\
142- cmd/posix/getconf\
143- cmd/posix/grep\
144- cmd/posix/head\
145- cmd/posix/id\
146- cmd/posix/join\
147- cmd/posix/kill\
148- cmd/posix/link\
149- cmd/posix/ln\
150- cmd/posix/logger\
151- cmd/posix/logname\
152- cmd/posix/ls\
153- cmd/posix/mesg\
154- cmd/posix/mkdir\
155- cmd/posix/mkfifo\
156- cmd/posix/mv\
157- cmd/posix/nice\
158- cmd/posix/nl\
159- cmd/posix/nohup\
160- cmd/posix/od\
161- cmd/posix/paste\
162- cmd/posix/pathchk\
163- cmd/posix/printf\
164- cmd/posix/ps\
165- cmd/posix/pwd\
166- cmd/posix/readlink\
167- cmd/posix/renice\
168- cmd/posix/rm\
169- cmd/posix/rmdir\
170- cmd/posix/sed\
171- cmd/posix/sleep\
172- cmd/posix/sort\
173- cmd/posix/split\
174- cmd/posix/tail\
175- cmd/posix/tee\
176- cmd/posix/test\
177- cmd/posix/time\
178- cmd/posix/touch\
179- cmd/posix/tr\
180- cmd/posix/true\
181- cmd/posix/tsort\
182- cmd/posix/tty\
183- cmd/posix/uname\
184- cmd/posix/unexpand\
185- cmd/posix/uniq\
186- cmd/posix/unlink\
187- cmd/posix/uudecode\
188- cmd/posix/uuencode\
189- cmd/posix/wc\
190- cmd/posix/who\
191- cmd/posix/xargs\
192- cmd/posix/awk/awk\
193- cmd/posix/sh/sh\
194- cmd/posix/pax\
195- cmd/posix/make/make\
196- cmd/posix/bc
197-
198-LINUX_BIN_ALL =\
199- cmd/linux/blkdiscard\
200- cmd/linux/chvt\
201- cmd/linux/ctrlaltdel\
202- cmd/linux/eject\
203- cmd/linux/freeramdisk\
204- cmd/linux/fsfreeze\
205- cmd/linux/hwclock\
206- cmd/linux/insmod\
207- cmd/linux/lsmod\
208- cmd/linux/lsusb\
209- cmd/linux/modprobe\
210- cmd/linux/depmod\
211- cmd/linux/mkswap\
212- cmd/linux/mount\
213- cmd/linux/mountpoint\
214- cmd/linux/pivot_root\
215- cmd/linux/readahead\
216- cmd/linux/rmmod\
217- cmd/linux/swaplabel\
218- cmd/linux/swapoff\
219- cmd/linux/swapon\
220- cmd/linux/switch_root\
221- cmd/linux/sysctl\
222- cmd/linux/tunctl\
223- cmd/linux/umount\
224- cmd/linux/unshare\
225- cmd/linux/vtallow
226-
227-NET_BIN_ALL =\
228- cmd/net/netcat\
229- cmd/net/tftp\
230- cmd/net/wget\
231- cmd/net/ping\
232- cmd/net/sdhcp\
233- cmd/net/ifconfig\
234- cmd/net/host\
235- cmd/net/httpd\
236- cmd/net/ip
237-
238-XSI_BIN_ALL =\
239- cmd/xsi/mknod\
240- cmd/xsi/passwd\
241- cmd/xsi/su
242-
243-PSEUDO_BIN_ALL =\
244- cmd/pseudo/chroot\
245- cmd/pseudo/clear\
246- cmd/pseudo/cols\
247- cmd/pseudo/cron\
248- cmd/pseudo/flock\
249- cmd/pseudo/getty\
250- cmd/pseudo/halt\
251- cmd/pseudo/hostname\
252- cmd/pseudo/killall5\
253- cmd/pseudo/last\
254- cmd/pseudo/lastlog\
255- cmd/pseudo/login\
256- cmd/pseudo/md5sum\
257- cmd/pseudo/mktemp\
258- cmd/pseudo/nologin\
259- cmd/pseudo/pagesize\
260- cmd/pseudo/printenv\
261- cmd/pseudo/respawn\
262- cmd/pseudo/rev\
263- cmd/pseudo/seq\
264- cmd/pseudo/setsid\
265- cmd/pseudo/sha1sum\
266- cmd/pseudo/sha224sum\
267- cmd/pseudo/sha256sum\
268- cmd/pseudo/sha384sum\
269- cmd/pseudo/sha512sum\
270- cmd/pseudo/sha512-224sum\
271- cmd/pseudo/sha512-256sum\
272- cmd/pseudo/sponge\
273- cmd/pseudo/stat\
274- cmd/pseudo/tar\
275- cmd/pseudo/truncate\
276- cmd/pseudo/watch\
277- cmd/pseudo/which\
278- cmd/pseudo/whoami\
279- cmd/pseudo/xinstall\
280- cmd/pseudo/yes\
281- cmd/pseudo/base64\
282- cmd/pseudo/mkpasswd\
283- cmd/extra/b3sum\
284- cmd/extra/blkid\
285- cmd/extra/lsblk\
286- cmd/extra/fdisk\
287- cmd/extra/sync\
288- cmd/extra/yap/yap\
289- cmd/dev/ar/ar\
290- cmd/dev/as/as\
291- cmd/dev/ld/ld\
292- cmd/dev/cc/cc\
293- cmd/pseudo/dmesg\
294- cmd/pseudo/fallocate\
295- cmd/pseudo/free\
296- cmd/pseudo/pidof\
297- cmd/pseudo/pwdx\
298- cmd/pseudo/uptime\
299- cmd/pseudo/diff3
300-
301-MAKEOBJ =\
302- cmd/posix/make/defaults.o\
303- cmd/posix/make/main.o\
304- cmd/posix/make/parser.o\
305- cmd/posix/make/posix.o\
306- cmd/posix/make/rules.o
307-
308-
309-
310-BIN_basename_1 = cmd/posix/basename
311-BIN_cal_1 = cmd/posix/cal
312-BIN_cat_1 = cmd/posix/cat
313-BIN_chgrp_1 = cmd/posix/chgrp
314-BIN_chmod_1 = cmd/posix/chmod
315-BIN_chown_1 = cmd/posix/chown
316-BIN_cksum_1 = cmd/posix/cksum
317-BIN_cmp_1 = cmd/posix/cmp
318-BIN_comm_1 = cmd/posix/comm
319-BIN_cp_1 = cmd/posix/cp
320-BIN_cut_1 = cmd/posix/cut
321-BIN_date_1 = cmd/posix/date
322-BIN_dd_1 = cmd/posix/dd
323-BIN_df_1 = cmd/posix/df
324-BIN_diff_1 = cmd/posix/diff
325-BIN_patch_1 = cmd/posix/patch
326-BIN_dirname_1 = cmd/posix/dirname
327-BIN_du_1 = cmd/posix/du
328-BIN_echo_1 = cmd/posix/echo
329-BIN_ed_1 = cmd/posix/ed
330-BIN_env_1 = cmd/posix/env
331-BIN_expand_1 = cmd/posix/expand
332-BIN_expr_1 = cmd/posix/expr
333-BIN_false_1 = cmd/posix/false
334-BIN_find_1 = cmd/posix/find
335-BIN_fold_1 = cmd/posix/fold
336-BIN_getconf_1 = cmd/posix/getconf
337-BIN_grep_1 = cmd/posix/grep
338-BIN_head_1 = cmd/posix/head
339-BIN_id_1 = cmd/posix/id
340-BIN_join_1 = cmd/posix/join
341-BIN_kill_1 = cmd/posix/kill
342-BIN_link_1 = cmd/posix/link
343-BIN_ln_1 = cmd/posix/ln
344-BIN_logger_1 = cmd/posix/logger
345-BIN_logname_1 = cmd/posix/logname
346-BIN_ls_1 = cmd/posix/ls
347-BIN_mesg_1 = cmd/posix/mesg
348-BIN_mkdir_1 = cmd/posix/mkdir
349-BIN_mkfifo_1 = cmd/posix/mkfifo
350-BIN_mv_1 = cmd/posix/mv
351-BIN_nice_1 = cmd/posix/nice
352-BIN_nl_1 = cmd/posix/nl
353-BIN_nohup_1 = cmd/posix/nohup
354-BIN_od_1 = cmd/posix/od
355-BIN_paste_1 = cmd/posix/paste
356-BIN_pathchk_1 = cmd/posix/pathchk
357-BIN_printf_1 = cmd/posix/printf
358-BIN_ps_1 = cmd/posix/ps
359-BIN_pwd_1 = cmd/posix/pwd
360-BIN_readlink_1 = cmd/posix/readlink
361-BIN_renice_1 = cmd/posix/renice
362-BIN_rm_1 = cmd/posix/rm
363-BIN_rmdir_1 = cmd/posix/rmdir
364-BIN_sed_1 = cmd/posix/sed
365-BIN_sleep_1 = cmd/posix/sleep
366-BIN_sort_1 = cmd/posix/sort
367-BIN_split_1 = cmd/posix/split
368-BIN_tail_1 = cmd/posix/tail
369-BIN_tee_1 = cmd/posix/tee
370-BIN_test_1 = cmd/posix/test
371-BIN_time_1 = cmd/posix/time
372-BIN_touch_1 = cmd/posix/touch
373-BIN_tr_1 = cmd/posix/tr
374-BIN_true_1 = cmd/posix/true
375-BIN_tsort_1 = cmd/posix/tsort
376-BIN_tty_1 = cmd/posix/tty
377-BIN_uname_1 = cmd/posix/uname
378-BIN_unexpand_1 = cmd/posix/unexpand
379-BIN_uniq_1 = cmd/posix/uniq
380-BIN_unlink_1 = cmd/posix/unlink
381-BIN_uudecode_1 = cmd/posix/uudecode
382-BIN_uuencode_1 = cmd/posix/uuencode
383-BIN_wc_1 = cmd/posix/wc
384-BIN_who_1 = cmd/posix/who
385-BIN_xargs_1 = cmd/posix/xargs
386-BIN_awk_1 = cmd/posix/awk/awk
387-BIN_sh_1 = cmd/posix/sh/sh
388-BIN_pax_1 = cmd/posix/pax
389-BIN_make_1 = cmd/posix/make/make
390-
391-BIN_blkdiscard_1 = cmd/linux/blkdiscard
392-BIN_chvt_1 = cmd/linux/chvt
393-BIN_ctrlaltdel_1 = cmd/linux/ctrlaltdel
394-BIN_eject_1 = cmd/linux/eject
395-BIN_freeramdisk_1 = cmd/linux/freeramdisk
396-BIN_fsfreeze_1 = cmd/linux/fsfreeze
397-BIN_hwclock_1 = cmd/linux/hwclock
398-BIN_insmod_1 = cmd/linux/insmod
399-BIN_lsmod_1 = cmd/linux/lsmod
400-BIN_lsusb_1 = cmd/linux/lsusb
401-BIN_modprobe_1 = cmd/linux/modprobe
402-BIN_depmod_1 = cmd/linux/depmod
403-BIN_mkswap_1 = cmd/linux/mkswap
404-BIN_mount_1 = cmd/linux/mount
405-BIN_mountpoint_1 = cmd/linux/mountpoint
406-BIN_pivot_root_1 = cmd/linux/pivot_root
407-BIN_readahead_1 = cmd/linux/readahead
408-BIN_rmmod_1 = cmd/linux/rmmod
409-BIN_swaplabel_1 = cmd/linux/swaplabel
410-BIN_swapoff_1 = cmd/linux/swapoff
411-BIN_swapon_1 = cmd/linux/swapon
412-BIN_switch_root_1 = cmd/linux/switch_root
413-BIN_sysctl_1 = cmd/linux/sysctl
414-BIN_tunctl_1 = cmd/linux/tunctl
415-BIN_umount_1 = cmd/linux/umount
416-BIN_unshare_1 = cmd/linux/unshare
417-BIN_vtallow_1 = cmd/linux/vtallow
418-
419-BIN_netcat_1 = cmd/net/netcat
420-BIN_tftp_1 = cmd/net/tftp
421-BIN_wget_1 = cmd/net/wget
422-BIN_ping_1 = cmd/net/ping
423-BIN_sdhcp_1 = cmd/net/sdhcp
424-BIN_ifconfig_1 = cmd/net/ifconfig
425-BIN_host_1 = cmd/net/host
426-BIN_httpd_1 = cmd/net/httpd
427-BIN_ip_1 = cmd/net/ip
428-
429-BIN_mknod_1 = cmd/xsi/mknod
430-BIN_passwd_1 = cmd/xsi/passwd
431-BIN_su_1 = cmd/xsi/su
432-
433-BIN_chroot_1 = cmd/pseudo/chroot
434-BIN_clear_1 = cmd/pseudo/clear
435-BIN_cols_1 = cmd/pseudo/cols
436-BIN_cron_1 = cmd/pseudo/cron
437-BIN_flock_1 = cmd/pseudo/flock
438-BIN_getty_1 = cmd/pseudo/getty
439-BIN_halt_1 = cmd/pseudo/halt
440-BIN_hostname_1 = cmd/pseudo/hostname
441-BIN_killall5_1 = cmd/pseudo/killall5
442-BIN_last_1 = cmd/pseudo/last
443-BIN_lastlog_1 = cmd/pseudo/lastlog
444-BIN_login_1 = cmd/pseudo/login
445-BIN_md5sum_1 = cmd/pseudo/md5sum
446-BIN_mktemp_1 = cmd/pseudo/mktemp
447-BIN_nologin_1 = cmd/pseudo/nologin
448-BIN_pagesize_1 = cmd/pseudo/pagesize
449-BIN_printenv_1 = cmd/pseudo/printenv
450-BIN_respawn_1 = cmd/pseudo/respawn
451-BIN_rev_1 = cmd/pseudo/rev
452-BIN_seq_1 = cmd/pseudo/seq
453-BIN_setsid_1 = cmd/pseudo/setsid
454-BIN_sha1sum_1 = cmd/pseudo/sha1sum
455-BIN_sha224sum_1 = cmd/pseudo/sha224sum
456-BIN_sha256sum_1 = cmd/pseudo/sha256sum
457-BIN_sha384sum_1 = cmd/pseudo/sha384sum
458-BIN_sha512sum_1 = cmd/pseudo/sha512sum
459-BIN_sha512_224sum_1 = cmd/pseudo/sha512-224sum
460-BIN_sha512_256sum_1 = cmd/pseudo/sha512-256sum
461-BIN_sponge_1 = cmd/pseudo/sponge
462-BIN_stat_1 = cmd/pseudo/stat
463-BIN_tar_1 = cmd/pseudo/tar
464-BIN_truncate_1 = cmd/pseudo/truncate
465-BIN_watch_1 = cmd/pseudo/watch
466-BIN_which_1 = cmd/pseudo/which
467-BIN_whoami_1 = cmd/pseudo/whoami
468-BIN_xinstall_1 = cmd/pseudo/xinstall
469-BIN_yes_1 = cmd/pseudo/yes
470-BIN_base64_1 = cmd/pseudo/base64
471-BIN_mkpasswd_1 = cmd/pseudo/mkpasswd
472-BIN_bc_1 = cmd/posix/bc
473-BIN_b3sum_1 = cmd/extra/b3sum
474-BIN_blkid_1 = cmd/extra/blkid
475-BIN_lsblk_1 = cmd/extra/lsblk
476-BIN_fdisk_1 = cmd/extra/fdisk
477-BIN_sync_1 = cmd/extra/sync
478-BIN_yap_1 = cmd/extra/yap/yap
479-BIN_diff3_1 = cmd/pseudo/diff3
480-BIN_ar_1 = cmd/dev/ar/ar
481-BIN_as_1 = cmd/dev/as/as
482-BIN_ld_1 = cmd/dev/ld/ld
483-BIN_cc_1 = cmd/dev/cc/cc
484-BIN_dmesg_1 = cmd/pseudo/dmesg
485-BIN_fallocate_1 = cmd/pseudo/fallocate
486-BIN_free_1 = cmd/pseudo/free
487-BIN_pidof_1 = cmd/pseudo/pidof
488-BIN_pwdx_1 = cmd/pseudo/pwdx
489-BIN_uptime_1 = cmd/pseudo/uptime
490-
491-POSIX_BIN = $(POSIX_BIN_CONFIG)
492-
493-LINUX_BIN = $(LINUX_BIN_CONFIG)
494-
495-NET_BIN = $(NET_BIN_CONFIG)
496-
497-XSI_BIN = $(XSI_BIN_CONFIG)
498-
499-PSEUDO_BIN = $(PSEUDO_BIN_CONFIG) $(EXTRA_BIN_CONFIG) $(DEV_BIN_CONFIG)
500-
501-
502-OBJ = $(LIBUTFOBJ) $(LIBUTILOBJ) $(MAKEOBJ)
503-
504-all: $(LIB) $(POSIX_BIN) $(LINUX_BIN) $(NET_BIN) $(XSI_BIN) $(PSEUDO_BIN)
505-
506--include deps.mk
507--include scripts/mk/rules.mk
508-
509-$(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL): $(LIB)
510-
511-$(OBJ) $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL): $(HDR)
512-
513-.o:
514- $(CC) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS)
515-
516-.c.o:
517- $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
518-
519-.c:
520- $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LIB) $(LDLIBS)
521-
522-cmd/posix/bc.c: cmd/posix/bc.y
523- YACC='$(YACC)' sh scripts/genconfig.sh bc
524-
525-cmd/posix/bc: cmd/posix/bc.c
526- $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ cmd/posix/bc.c $(LIB) $(LDLIBS)
527-
528-$(MAKEOBJ): cmd/posix/make/make.h
529-
530-cmd/posix/make/make: $(MAKEOBJ) $(LIB)
531- $(CC) $(LDFLAGS) -o $@ $(MAKEOBJ) $(LIB) $(LDLIBS)
532-
533-cmd/posix/diff: cmd/posix/diff.c $(LIB)
534- $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ cmd/posix/diff.c $(LIB) $(LDLIBS) -lm
535-
536-shared/libutf/libutf.a: $(LIBUTFOBJ)
537- $(AR) $(ARFLAGS) $@ $?
538- $(RANLIB) $@
539-
540-shared/libredline/libredline.a: $(LIBREDLINEOBJ)
541- $(AR) $(ARFLAGS) $@ $?
542- $(RANLIB) $@
543-
544-shared/libutil/libutil.a: $(LIBUTILOBJ)
545- $(AR) $(ARFLAGS) $@ $?
546- $(RANLIB) $@
547-
548-cmd/posix/getconf: cmd/posix/getconf.h
549-
550-cmd/posix/getconf.h:
551- sh scripts/genconfig.sh getconf
552-
553-box: $(LIB)
554- CC='$(CC)' CPPFLAGS='$(CPPFLAGS)' CFLAGS='$(CFLAGS)' \
555- LDFLAGS='$(LDFLAGS)' LDLIBS='$(LDLIBS)' OBJCOPY='$(OBJCOPY)' \
556- scripts/mkbox
557-
558-.PHONY: man clean install regen help
559-
560-scripts/mkman/mkman: scripts/mkman/main.go scripts/mkman/page.go scripts/mkman/parse.go scripts/mkman/mdoc.go
561- cd scripts/mkman && go build -o mkman .
562-
563-man: scripts/mkman/mkman
564- find cmd -type f -name '*.c' | while read -r src; do \
565- if [ -f "$$src" ] && grep -qE '!man|\?man' "$$src"; then \
566- sub=$${src#cmd/}; \
567- cat=$${sub%%/*}; \
568- base=$${src##*/}; \
569- base=$${base%.c}; \
570- name=$${sub#*/}; \
571- if [ "$${name}" != "$${base}.c" ]; then \
572- name=$${name%%/*}; \
573- else \
574- name=$$base; \
575- fi; \
576- u_group=$$(echo "$$cat" | tr 'a-z' 'A-Z'); \
577- u_tool=$$(echo "$$name" | tr 'a-z-' 'A-Z_'); \
578- var_name="BUILD_$${u_group}_$${u_tool}"; \
579- val=""; \
580- if [ -f scripts/mk/config.kv ]; then \
581- val=$$(grep "^$${var_name}=" scripts/mk/config.kv | cut -d= -f2 | tr -d '"'); \
582- fi; \
583- if [ "$$val" != "1" ]; then \
584- continue; \
585- fi; \
586- case "$$cat" in \
587- linux|net|xsi) sec=8 ;; \
588- *) sec=1 ;; \
589- esac; \
590- mkdir -p "man/man$${sec}"; \
591- scripts/mkman/mkman -fmt mdoc -config scripts/mk/config.kv -section "$${sec}" "$$src" > "man/man$${sec}/$$name.$${sec}"; \
592- scripts/mkman/mkman -fmt txt -config scripts/mk/config.kv -section "$${sec}" "$$src" > "man/man$${sec}/$$name.$${sec}.txt"; \
593- fi; \
594- done
595-
596-regen: scripts/mk/config.mk cmd/posix/getconf.h cmd/posix/bc.c cmd/posix/awk/awkgram.tab.c cmd/posix/awk/proctab.c cmd/posix/sh/token.h cmd/posix/sh/syntax.c cmd/posix/sh/nodes.c cmd/posix/sh/builtins.c
597-
598-help:
599- @echo "Available targets:"
600- @echo " all - Build all enabled utilities"
601- @echo " clean - Clean build artifacts"
602- @echo " install - Install binaries and man pages"
603- @echo " box - Build the multicall binary (aruu-box)"
604- @echo " man - Generate manual pages"
605- @echo " regen - Regenerate build system files and config"
606- @echo " help - Show this help message"
607-
608-install: all man
609- @mkdir -p $(PREFIX)/bin $(MANPREFIX)/man1 $(MANPREFIX)/man8
610- @find cmd -type f ! -name '*.*' -perm -100 | while read -r bin; do \
611- printf " INSTALL %s/bin/%s\n" "$(PREFIX)" "$${bin##*/}"; \
612- cp "$$bin" "$(PREFIX)/bin/$${bin##*/}"; \
613- chmod 755 "$(PREFIX)/bin/$${bin##*/}"; \
614- done
615- @find man/man1 man/man8 -type f -name '*.[18]' 2>/dev/null | while read -r f; do \
616- sec=$${f%/*}; sec=$${sec##*/}; \
617- cp "$$f" "$(MANPREFIX)/$${sec}/$${f##*/}"; \
618- done
619-
620-clean:
621- @printf " CLEAN object files\n"
622- @find shared cmd -name "*.o" -exec rm -f {} +
623- @printf " CLEAN static libraries\n"
624- @rm -f $(LIB)
625- @printf " CLEAN compiled binaries\n"
626- @rm -f $(POSIX_BIN_ALL) $(LINUX_BIN_ALL) $(NET_BIN_ALL) $(XSI_BIN_ALL) $(PSEUDO_BIN_ALL)
627- @sh scripts/genconfig.sh clean
628- @printf " CLEAN build tools\n"
629- @rm -f scripts/mkman/mkman
630- @rm -f scripts/mk/config.kv
631- @rm -f scripts/mk/config.mk
632- @rm -f scripts/mk/rules.mk
633-
634- @printf " CLEAN dev artifacts\n"
635- @rm -f cmd/dev/cc/cc1 cmd/dev/cc/cpp cmd/dev/as/as cmd/dev/ld/ld cmd/dev/ar/ar shared/libaruuelf.so
636- @rm -f cmd/dev/config.h cmd/dev/cc/config.h cmd/dev/version.h
637- @printf " CLEAN misc\n"
638- @rm -rf aruu-box .box man/man1 man/man8
639- @printf " CLEAN done\n"
640-AWKOBJ =\
641- cmd/posix/awk/b.o\
642- cmd/posix/awk/main.o\
643- cmd/posix/awk/parse.o\
644- cmd/posix/awk/proctab.o\
645- cmd/posix/awk/tran.o\
646- cmd/posix/awk/lib.o\
647- cmd/posix/awk/run.o\
648- cmd/posix/awk/lex.o\
649- cmd/posix/awk/math.o\
650- cmd/posix/awk/awkgram.tab.o
651-
652-SH_GENHDRS =\
653- cmd/posix/sh/syntax.h\
654- cmd/posix/sh/nodes.h\
655- cmd/posix/sh/builtins.h\
656- cmd/posix/sh/token.h
657-
658-SHOBJ =\
659- cmd/posix/sh/alias.o\
660- cmd/posix/sh/arith_yacc.o\
661- cmd/posix/sh/arith_yylex.o\
662- cmd/posix/sh/cd.o\
663- cmd/posix/sh/echo.o\
664- cmd/posix/sh/error.o\
665- cmd/posix/sh/easter.o\
666- cmd/posix/sh/eval.o\
667- cmd/posix/sh/exec.o\
668- cmd/posix/sh/expand.o\
669- cmd/posix/sh/lineedit.o\
670- cmd/posix/sh/input.o\
671- cmd/posix/sh/jobs.o\
672- cmd/posix/sh/kill.o\
673- cmd/posix/sh/mail.o\
674- cmd/posix/sh/main.o\
675- cmd/posix/sh/memalloc.o\
676- cmd/posix/sh/miscbltin.o\
677- cmd/posix/sh/mystring.o\
678- cmd/posix/sh/options.o\
679- cmd/posix/sh/output.o\
680- cmd/posix/sh/parser.o\
681- cmd/posix/sh/printf.o\
682- cmd/posix/sh/redir.o\
683- cmd/posix/sh/show.o\
684- cmd/posix/sh/test.o\
685- cmd/posix/sh/trap.o\
686- cmd/posix/sh/var.o\
687- cmd/posix/sh/builtins.o\
688- cmd/posix/sh/nodes.o\
689- cmd/posix/sh/syntax.o
690-
691-YAPOBJ =\
692- cmd/extra/yap/ansi.o\
693- cmd/extra/yap/commands.o\
694- cmd/extra/yap/display.o\
695- cmd/extra/yap/getcomm.o\
696- cmd/extra/yap/getline.o\
697- cmd/extra/yap/help.o\
698- cmd/extra/yap/keys.o\
699- cmd/extra/yap/machine.o\
700- cmd/extra/yap/main.o\
701- cmd/extra/yap/options.o\
702- cmd/extra/yap/output.o\
703- cmd/extra/yap/pattern.o\
704- cmd/extra/yap/process.o\
705- cmd/extra/yap/prompt.o\
706- cmd/extra/yap/term.o
707-
708-cmd/posix/awk/awkgram.tab.c: cmd/posix/awk/awkgram.y cmd/posix/awk/maketab.c
709- CC='$(CC)' CFLAGS='$(CFLAGS)' YACC='$(YACC)' sh scripts/genconfig.sh awk
710-
711-cmd/posix/awk/awkgram.tab.h: cmd/posix/awk/awkgram.tab.c ;
712-cmd/posix/awk/maketab: cmd/posix/awk/awkgram.tab.c ;
713-cmd/posix/awk/proctab.c: cmd/posix/awk/awkgram.tab.c ;
714-
715-$(AWKOBJ): cmd/posix/awk/awk.h cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proto.h
716-
717-cmd/posix/awk/awk: $(AWKOBJ) $(LIB)
718- $(CC) $(LDFLAGS) -o $@ $(AWKOBJ) $(LIB) $(LDLIBS) -lm
719-
720-cmd/posix/sh/builtins.c: cmd/posix/sh/mknodes.c cmd/posix/sh/mksyntax.c cmd/posix/sh/nodetypes cmd/posix/sh/nodes.c.pat cmd/posix/sh/mkbuiltins cmd/posix/sh/builtins.def cmd/posix/sh/shell.h cmd/posix/sh/mktokens
721- CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' sh scripts/genconfig.sh sh
722-
723-cmd/posix/sh/syntax.c: cmd/posix/sh/builtins.c ;
724-cmd/posix/sh/syntax.h: cmd/posix/sh/builtins.c ;
725-cmd/posix/sh/nodes.c: cmd/posix/sh/builtins.c ;
726-cmd/posix/sh/nodes.h: cmd/posix/sh/builtins.c ;
727-cmd/posix/sh/builtins.h: cmd/posix/sh/builtins.c ;
728-cmd/posix/sh/token.h: cmd/posix/sh/builtins.c ;
729-cmd/posix/sh/mknodes: cmd/posix/sh/builtins.c ;
730-cmd/posix/sh/mksyntax: cmd/posix/sh/builtins.c ;
731-
732-$(SHOBJ): $(SH_GENHDRS)
733-
734-cmd/posix/sh/sh: $(SHOBJ) $(LIB)
735- $(CC) $(LDFLAGS) -o $@ $(SHOBJ) $(LIB) $(LDLIBS)
736-
737-cmd/extra/yap/yap: $(YAPOBJ) $(LIB)
738- $(CC) $(LDFLAGS) -o $@ $(YAPOBJ) $(LIB) $(LDLIBS) -ltinfo
739-
740-cmd/extra/diff3/diff3: cmd/extra/diff3/diff3.o cmd/posix/diff/xmalloc.o $(LIB)
741- $(CC) $(LDFLAGS) -o $@ cmd/extra/diff3/diff3.o cmd/posix/diff/xmalloc.o $(LIB) $(LDLIBS)
742-
743-cmd/net/wget: cmd/net/wget.o $(LIB)
744- $(CC) $(LDFLAGS) -o $@ cmd/net/wget.o $(LIB) $(LDLIBS) $(LDLIBS_TLS)
745-
746-cmd/dev/ar/ar: cmd/dev/ar/ar.o $(LIB)
747- $(CC) $(LDFLAGS) -o $@ cmd/dev/ar/ar.o $(LIB) $(LDLIBS)
748-
749-LD_OBJ =\
750- cmd/dev/xcutil/util.o\
751- cmd/dev/xcutil/table.o\
752- cmd/dev/xcutil/elfutil.o\
753- cmd/dev/xcutil/archive.o\
754- cmd/dev/ld/ld.o\
755- cmd/dev/ld/elfobj.o
756-
757-cmd/dev/ld/ld: $(LD_OBJ) $(LIB)
758- $(CC) $(LDFLAGS) -o $@ $(LD_OBJ) $(LIB) $(LDLIBS)
759-
760-AS_OBJ =\
761- cmd/dev/xcutil/util.o\
762- cmd/dev/xcutil/table.o\
763- cmd/dev/xcutil/elfutil.o\
764- cmd/dev/xcutil/archive.o\
765- cmd/dev/as/as.o\
766- cmd/dev/as/as_util.o\
767- cmd/dev/as/emit_elf.o\
768- cmd/dev/as/emit_macho.o\
769- cmd/dev/as/ir_asm.o\
770- cmd/dev/as/parse_asm.o\
771- cmd/dev/as/arch/x64/asm_code.o\
772- cmd/dev/as/arch/x64/ir_asm_x64.o\
773- cmd/dev/as/arch/x64/parse_x64.o
774-
775-AS_HDRS =\
776- cmd/dev/as/parse_asm.h\
777- cmd/dev/as/ir_asm.h\
778- cmd/dev/as/as_util.h\
779- cmd/dev/as/asm_code.h\
780- cmd/dev/as/arch/x64/inst.h
781-
782-cmd/dev/as/as: $(AS_OBJ) $(LIB)
783- $(CC) $(LDFLAGS) -o $@ $(AS_OBJ) $(LIB) $(LDLIBS)
784-
785-cmd/dev/cc/driver.o: cmd/dev/cc/config.h
786-
787-cmd/dev/cc/cc: cmd/dev/cc/driver.o cmd/dev/cc/util.o $(LIB) cmd/dev/cc/cc1 cmd/dev/cc/cpp cmd/dev/as/as cmd/dev/ld/ld
788- $(CC) $(LDFLAGS) -o $@ cmd/dev/cc/driver.o cmd/dev/cc/util.o $(LIB) $(LDLIBS)
789-
790-CC1_OBJ =\
791- cmd/dev/cc/attr.o\
792- cmd/dev/cc/decl.o\
793- cmd/dev/cc/eval.o\
794- cmd/dev/cc/expr.o\
795- cmd/dev/cc/init.o\
796- cmd/dev/cc/cc1.o\
797- cmd/dev/cc/map.o\
798- cmd/dev/cc/pp.o\
799- cmd/dev/cc/qbe.o\
800- cmd/dev/cc/scan.o\
801- cmd/dev/cc/scope.o\
802- cmd/dev/cc/stmt.o\
803- cmd/dev/cc/targ.o\
804- cmd/dev/cc/token.o\
805- cmd/dev/cc/tree.o\
806- cmd/dev/cc/type.o\
807- cmd/dev/cc/utf.o\
808- cmd/dev/cc/util.o
809-
810-CPP_OBJ =\
811- cmd/dev/cc/attr.o\
812- cmd/dev/cc/decl.o\
813- cmd/dev/cc/eval.o\
814- cmd/dev/cc/expr.o\
815- cmd/dev/cc/init.o\
816- cmd/dev/cc/map.o\
817- cmd/dev/cc/pp.o\
818- cmd/dev/cc/qbe.o\
819- cmd/dev/cc/scan.o\
820- cmd/dev/cc/scope.o\
821- cmd/dev/cc/stmt.o\
822- cmd/dev/cc/targ.o\
823- cmd/dev/cc/token.o\
824- cmd/dev/cc/tree.o\
825- cmd/dev/cc/type.o\
826- cmd/dev/cc/utf.o\
827- cmd/dev/cc/util.o
828-
829-cmd/dev/cc/cc1: $(CC1_OBJ) $(LIB)
830- $(CC) $(LDFLAGS) -o $@ $(CC1_OBJ) $(LIB) $(LDLIBS)
831-
832-cmd/dev/cc/cpp: cmd/dev/cc/cpp.o $(CPP_OBJ) $(LIB)
833- $(CC) $(LDFLAGS) -o $@ cmd/dev/cc/cpp.o $(CPP_OBJ) $(LIB) $(LDLIBS)
834-
835-cmd/dev/config.h: cmd/dev/configure
836- sh cmd/dev/configure
837-
838-cmd/dev/cc/config.h: cmd/dev/config.h ;
839-cmd/dev/version.h: cmd/dev/config.h ;
840-
841-$(AS_OBJ) $(LD_OBJ) $(CC1_OBJ) $(CPP_OBJ) cmd/dev/ar/ar.o cmd/dev/cc/cpp.o: cmd/dev/config.h
842-
843-scripts/mk/config.mk: build.cfg scripts/genconfig.sh
844- sh scripts/genconfig.sh
845-
846-scripts/mk/config.kv: scripts/mk/config.mk ;
847-scripts/mk/rules.mk: scripts/mk/config.mk ;
848-
849+CC ?= cc -static
850+CFLAGS ?= -std=c99 -Wall -Wextra -pedantic
851+CPPFLAGS ?= -Ishared -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L \
852+ -DFEATURE_NOFORK=0 -DFEATURE_NOEXEC=0
853+# NOEXEC cannot be on the bootstrap compile, but it can
854+# be on when one compiles aruu-box with ninqu included
855+
856+NINQU_LIB_SRC =\
857+ shared/libutil/ealloc.c\
858+ shared/libutil/eprintf.c\
859+ shared/libutil/strlcpy.c\
860+ shared/libutil/strlcat.c\
861+ shared/libutil/strtonum.c\
862+ shared/libutil/fshut.c\
863+ shared/libutil/mkdirp.c\
864+ shared/libutil/recurse_dir.c
865+
866+NINQU_SRC =\
867+ cmd/dev/ninqu/main.c\
868+ cmd/dev/ninqu/strlist.c\
869+ cmd/dev/ninqu/kv.c\
870+ cmd/dev/ninqu/sexpr.c\
871+ cmd/dev/ninqu/gate.c\
872+ cmd/dev/ninqu/registry.c\
873+ cmd/dev/ninqu/fs.c\
874+ cmd/dev/ninqu/exec.c\
875+ cmd/dev/ninqu/parse.c\
876+ cmd/dev/ninqu/expand.c\
877+ cmd/dev/ninqu/schedule.c\
878+ cmd/dev/ninqu/query.c\
879+ cmd/dev/ninqu/resolve.c
880+
881+# map make's -j to ninqu's -j flag
882+NINQU ?= cmd/dev/ninqu/ninqu
883+JOBS ?= $(filter -j%,$(MAKEFLAGS))
884+
885+# rebuilds config.set first so a build.cfg edit is picked up before
886+# ninqu compiles, ninqu also does this lazily on its own via
887+# (set-file), this just avoids the extra shell startup on every run
888+$(NINQU): $(NINQU_SRC) cmd/dev/ninqu/ninqu.h scripts/mk/config.set $(NINQU_LIB_SRC)
889+ $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(NINQU_SRC) $(NINQU_LIB_SRC)
890+
891+# FORCE has no rule, thus making any pattern
892+# that depends on it be always outdated
893+.PHONY: FORCE
894+FORCE:
895+
896+%: $(NINQU) FORCE
897+ $(NINQU) $(JOBS) $@
898+
899+clean: $(NINQU)
900+ $(NINQU) clean
901+ rm -f $(NINQU)
902+
903+scripts/mk/config.set: build.cfg scripts/genconfig.sh
904+ sh scripts/genconfig.sh config
905+
906+# stops the % pattern rule above from trying to rebuild these as if
907+# they were stale build targets
908 Makefile: ;
909+ninqu.rules: ;
910+cmd/dev/ninqu/ninqu.h: ;
911+cmd/dev/ninqu/main.c: ;
912+cmd/dev/ninqu/strlist.c: ;
913+cmd/dev/ninqu/kv.c: ;
914+cmd/dev/ninqu/sexpr.c: ;
915+cmd/dev/ninqu/gate.c: ;
916+cmd/dev/ninqu/registry.c: ;
917+cmd/dev/ninqu/fs.c: ;
918+cmd/dev/ninqu/exec.c: ;
919+cmd/dev/ninqu/parse.c: ;
920+cmd/dev/ninqu/expand.c: ;
921+cmd/dev/ninqu/schedule.c: ;
922+cmd/dev/ninqu/query.c: ;
923+cmd/dev/ninqu/resolve.c: ;
924+build.cfg: ;
925+scripts/genconfig.sh: ;
+0,
-537
1@@ -1,537 +0,0 @@
2-#!/bin/sh
3-# drives the build without needing a working make to bootstrap
4-#
5-set -e
6-
7-# per-run work directory, cleaned up on exit
8-#
9-WORKDIR="${TMPDIR:-/tmp}/aruu.$$"
10-mkdir -p "$WORKDIR"
11-trap 'rm -rf "$WORKDIR"' EXIT INT TERM HUP
12-
13-JOBS=1
14-LOAD=0
15-TARGET=all
16-
17-while [ $# -gt 0 ]; do
18- case "$1" in
19- -j) shift; JOBS="${1:?-j requires a number}" ;;
20- -j*) JOBS="${1#-j}" ;;
21- -l) shift; LOAD="${1:?-l requires a number}" ;;
22- -l*) LOAD="${1#-l}" ;;
23- -*) printf 'unknown flag: %s\n' "$1" >&2; exit 1 ;;
24- *) TARGET="$1" ;;
25- esac
26- shift
27-done
28-
29-# timestamp comparison
30-#
31-mtime() {
32- stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1" 2>/dev/null || echo 0
33-}
34-newer_than() {
35- local s t
36- [ -e "$2" ] || return 0
37- s=$(mtime "$1"); t=$(mtime "$2")
38- [ "$s" -gt "$t" ]
39-}
40-any_newer_than() {
41- local target="$1"; shift
42- for src do
43- newer_than "$src" "$target" && return 0
44- done
45- return 1
46-}
47-
48-[ -f build.cfg ] || { printf 'build.cfg not found; see the repository README\n' >&2; exit 1; }
49-
50-# generate config files if missing or build.cfg is newer
51-if [ ! -f scripts/mk/config.kv ] || newer_than build.cfg scripts/mk/config.kv; then
52- sh scripts/genconfig.sh
53-fi
54-
55-# shellcheck disable=SC1091
56-. ./build.cfg
57-
58-# load config.kv if it exists to override and provide detected TLS settings
59-if [ -f scripts/mk/config.kv ]; then
60- # shellcheck disable=SC1091
61- . ./scripts/mk/config.kv
62-fi
63-
64-# cppflags derived from build.cfg; any feature_* added there is picked up automatically
65-_feature_flags=""
66-while IFS= read -r _line; do
67- _trimmed=$(printf '%s' "$_line" | tr -d ' \t')
68- case "$_trimmed" in
69- FEATURE_*=*)
70- _key=${_trimmed%%=*}
71- eval "_val=\$$_key"
72- _feature_flags="$_feature_flags -D${_key}=${_val}"
73- ;;
74- esac
75-done <<EOF
76-$(tr ';' '\n' < build.cfg)
77-EOF
78-unset _line _key _val _trimmed
79-
80-CPPFLAGS="-Ishared -DPREFIX=\\\"\$PREFIX\\\" -D_DEFAULT_SOURCE -D_GNU_SOURCE -D_NETBSD_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64$_feature_flags $CPPFLAGS_TLS"
81-LDFLAGS="${LDFLAGS:-} $LDFLAGS_TLS"
82-TLS_LDLIBS="$LDLIBS_TLS"
83-
84-CC=${CC:-cc}
85-AR=${AR:-ar}
86-ARFLAGS=${ARFLAGS:-rc}
87-RANLIB=${RANLIB:-ranlib}
88-PREFIX=${PREFIX:-/usr/local}
89-MANPREFIX=${MANPREFIX:-$PREFIX/share/man}
90-
91-# shared headers, most objects depend on these
92-HDR=$(echo shared/*.h)
93-LIB="shared/libredline/libredline.a shared/libutil/libutil.a shared/libutf/libutf.a"
94-
95-# set by multi-file builders for generated headers, cleared after use
96-EXTRA_HDR=
97-
98-# parallel job queue
99-# objs_for must not run in a subshell, enqueue writes _qn/_queue in the current shell
100-#
101-_qn=0
102-_queue=""
103-_objs=""
104-# wraps arg in single quotes, escaping embedded ones, for safe eval
105-shquote() {
106- printf "'"
107- printf '%s' "$1" | sed "s/'/'\\\\''/g"
108- printf "'"
109-}
110-enqueue() {
111- local desc="$1" cmd="$2"
112- _qn=$((_qn + 1))
113- local s="$WORKDIR/j${_qn}.sh"
114- {
115- printf '#!/bin/sh\n'
116- printf 'printf "%%s\\n" %s\n' "$(shquote "$desc")"
117- printf 'eval %s\n' "$(shquote "$cmd")"
118- } > "$s"
119- _queue="$_queue $_qn"
120-}
121-# silently skipped on systems without /proc/loadavg
122-_wait_for_load() {
123- [ "$LOAD" -eq 0 ] && return 0
124- local avg
125- while true; do
126- avg=$(awk '{printf "%d", int($1 + 0.5)}' /proc/loadavg 2>/dev/null) || return 0
127- [ "$avg" -le "$LOAD" ] && return 0
128- sleep 1
129- done
130-}
131-drain() {
132- [ -z "$_queue" ] && return 0
133- local n
134- _wait_for_load
135- if [ "$JOBS" -le 1 ]; then
136- for n in $_queue; do
137- sh "$WORKDIR/j${n}.sh" || { printf 'error: build step failed\n' >&2; exit 1; }
138- done
139- else
140- # xargs -P (POSIX 2024), -n 1 so each path becomes its own sh invocation
141- for n in $_queue; do
142- printf '%s\n' "$WORKDIR/j${n}.sh"
143- done | xargs -P "$JOBS" -n 1 sh || { printf 'error: build step failed\n' >&2; exit 1; }
144- fi
145- _queue=""
146-}
147-
148-# compile and link routines
149-#
150-compile_c() {
151- local src="$1" obj="$2"; shift 2
152- # shellcheck disable=SC2086
153- any_newer_than "$obj" "$src" $HDR $EXTRA_HDR || return 0
154- enqueue " CC $obj" "$CC $* $CPPFLAGS $CFLAGS -o $obj -c $src"
155-}
156-link_bin() {
157- local bin="$1" objs="" libs="" sep=0; shift
158- for a do
159- if [ "$a" = "--" ]; then sep=1
160- elif [ "$sep" = 0 ]; then objs="$objs $a"
161- else libs="$libs $a"
162- fi
163- done
164- drain
165- # shellcheck disable=SC2086
166- any_newer_than "$bin" $objs || return 0
167- printf ' LD %s\n' "$bin"
168- # shellcheck disable=SC2086
169- if [ "${bin##*/}" = "wget" ]; then
170- eval "$CC $LDFLAGS -o $bin $objs $libs $LDLIBS $TLS_LDLIBS"
171- else
172- eval "$CC $LDFLAGS -o $bin $objs $libs $LDLIBS"
173- fi
174-}
175-# writes to _objs rather than stdout so the caller avoids a subshell that
176-# would discard enqueued jobs
177-objs_for() {
178- local dir="$1" skip=" $2 " flags="$3" src base
179- _objs=""
180- for src in "$dir"/*.c; do
181- [ -f "$src" ] || continue
182- base=${src##*/}
183- case "$skip" in *" $base "*) continue ;; esac
184- compile_c "$src" "${src%.c}.o" $flags
185- _objs="$_objs ${src%.c}.o"
186- done
187-}
188-
189-# static libraries
190-# both directories enqueued before the single drain so they compile in parallel
191-#
192-build_lib() {
193- local utf_objs util_objs redline_objs
194- objs_for shared/libutf "" ""
195- utf_objs="$_objs"
196- objs_for shared/libutil "" ""
197- util_objs="$_objs"
198- objs_for shared/libredline "" ""
199- redline_objs="$_objs"
200- drain
201- # shellcheck disable=SC2086
202- any_newer_than shared/libutf/libutf.a $utf_objs && {
203- printf ' AR shared/libutf/libutf.a\n'
204- $AR $ARFLAGS shared/libutf/libutf.a $utf_objs
205- $RANLIB shared/libutf/libutf.a
206- }
207- # shellcheck disable=SC2086
208- any_newer_than shared/libutil/libutil.a $util_objs && {
209- printf ' AR shared/libutil/libutil.a\n'
210- $AR $ARFLAGS shared/libutil/libutil.a $util_objs
211- $RANLIB shared/libutil/libutil.a
212- }
213- # shellcheck disable=SC2086
214- any_newer_than shared/libredline/libredline.a $redline_objs && {
215- printf ' AR shared/libredline/libredline.a\n'
216- $AR $ARFLAGS shared/libredline/libredline.a $redline_objs
217- $RANLIB shared/libredline/libredline.a
218- }
219- return 0
220-}
221-
222-# per-tool category builder
223-#
224-cfgvar() {
225- local cat="$1" base="$2"
226- printf 'BUILD_%s_%s' "$cat" "$base" | tr 'a-z-' 'A-Z_'
227-}
228-
229-# individual flag overrides group flag
230-cfg_enabled() {
231- local _var="$1" _v
232- while [ -n "$_var" ]; do
233- eval "_v=\${${_var}-UNSET}"
234- if [ "$_v" = "1" ]; then
235- return 0
236- elif [ "$_v" = "0" ]; then
237- return 1
238- fi
239- case "$_var" in
240- *_[!_]*) _var=$(printf '%s' "$_var" | sed 's/_[^_]*$//') ;;
241- *) break ;;
242- esac
243- done
244- return 1
245-}
246-
247-build_simple_tools() {
248- local cat="$1" src base var
249- for src in cmd/"$cat"/*.c; do
250- [ -f "$src" ] || continue
251- base=${src##*/}; base=${base%.c}
252- var=$(cfgvar "$cat" "$base")
253- cfg_enabled "$var" || continue
254- local bin="cmd/$cat/$base"
255- # shellcheck disable=SC2086
256- any_newer_than "$bin" "$src" $HDR $EXTRA_HDR $LIB || continue
257- if [ "$base" = "diff" ]; then
258- enqueue " CC $bin" "$CC $src $CPPFLAGS $CFLAGS $LDFLAGS -o $bin $LIB -lm $LDLIBS"
259- else
260- enqueue " CC $bin" "$CC $src $CPPFLAGS $CFLAGS $LDFLAGS -o $bin $LIB $LDLIBS"
261- fi
262- done
263- drain
264-}
265-
266-build_awk() {
267- cfg_enabled BUILD_POSIX_AWK || return 0
268- local dir=cmd/posix/awk
269- CC="$CC" CFLAGS="$CFLAGS" YACC="$YACC" sh scripts/genconfig.sh awk
270- EXTRA_HDR="$dir/awk.h $dir/awkgram.tab.h $dir/proto.h"
271- objs_for "$dir" maketab.c "-I$dir"
272- EXTRA_HDR=
273- link_bin "$dir/awk" $_objs -- $LIB -lm
274-}
275-
276-build_sh() {
277- cfg_enabled BUILD_POSIX_SH || return 0
278- local dir=cmd/posix/sh
279- CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" sh scripts/genconfig.sh sh
280- EXTRA_HDR="$dir/syntax.h $dir/nodes.h $dir/builtins.h $dir/token.h"
281- objs_for "$dir" "mknodes.c mksyntax.c" "-DSHELL -I$dir"
282- EXTRA_HDR=
283- link_bin "$dir/sh" $_objs -- $LIB
284-}
285-
286-build_make() {
287- cfg_enabled BUILD_POSIX_MAKE || return 0
288- local dir=cmd/posix/make
289- EXTRA_HDR="$dir/make.h"
290- objs_for "$dir" "" ""
291- EXTRA_HDR=
292- link_bin "$dir/make" $_objs -- $LIB
293-}
294-
295-build_bc() {
296- cfg_enabled BUILD_POSIX_BC || return 0
297- local src=cmd/posix/bc.c
298- YACC="$YACC" sh scripts/genconfig.sh bc
299- # shellcheck disable=SC2086
300- any_newer_than cmd/posix/bc "$src" $HDR $EXTRA_HDR $LIB || return 0
301- printf ' CC cmd/posix/bc\n'
302- eval "$CC $src $CPPFLAGS $CFLAGS $LDFLAGS -o cmd/posix/bc $LIB $LDLIBS"
303-}
304-
305-build_posix() {
306- cfg_enabled BUILD_POSIX_GETCONF && sh scripts/genconfig.sh getconf
307- build_simple_tools posix
308- build_awk
309- build_bc
310- build_sh
311- build_make
312-}
313-
314-# multi-file dev toolchain
315-#
316-build_ar() {
317- cfg_enabled BUILD_DEV_AR || return 0
318- local dir=cmd/dev/ar
319- objs_for "$dir" tinyar.c "-I$dir"
320- link_bin "$dir/ar" $_objs -- $LIB
321-}
322-
323-build_as() {
324- cfg_enabled BUILD_DEV_AS || return 0
325- local dir="cmd/dev/as"
326- local flags="-Icmd/dev/xcutil -I$dir -Ishared"
327- local objs=""
328- objs_for "cmd/dev/xcutil" "" "$flags"
329- objs="$objs $_objs"
330- objs_for "$dir" "" "$flags"
331- objs="$objs $_objs"
332- objs_for "$dir/arch/x64" "" "-Icmd/dev/xcutil -I$dir -I$dir/arch/x64 -Ishared"
333- objs="$objs $_objs"
334- link_bin "$dir/as" $objs -- $LIB
335-}
336-
337-build_ld() {
338- cfg_enabled BUILD_DEV_LD || return 0
339- local dir="cmd/dev/ld"
340- local flags="-Icmd/dev/xcutil -I$dir"
341- local objs=""
342- objs_for "cmd/dev/xcutil" "" "$flags"
343- objs="$objs $_objs"
344- objs_for "$dir" "" "$flags"
345- objs="$objs $_objs"
346- link_bin "$dir/ld" $objs -- $LIB
347-}
348-
349-build_cc() {
350- cfg_enabled BUILD_DEV_CC || return 0
351- local dir="cmd/dev/cc"
352- local cflags="-I$dir"
353- local common
354-
355- # compiled once, linked into both cc1 and cpp
356- objs_for "$dir" "driver.c cc1.c cpp.c" "$cflags"
357- common="$_objs"
358- drain
359-
360- compile_c "$dir/cc1.c" "$dir/cc1.o" $cflags
361- compile_c "$dir/cpp.c" "$dir/cpp.o" $cflags
362- compile_c "$dir/driver.c" "$dir/driver.o" $cflags
363- drain
364-
365- link_bin "$dir/cc1" "$dir/cc1.o" $common -- $LIB
366- link_bin "$dir/cpp" "$dir/cpp.o" $common -- $LIB
367- link_bin "$dir/cc" "$dir/driver.o" "$dir/util.o" -- $LIB
368-}
369-
370-build_dev() {
371- [ -f cmd/dev/configure ] && sh cmd/dev/configure
372- build_ar
373- build_as
374- build_ld
375- build_cc
376-}
377-
378-# manual page generation
379-#
380-man_section() {
381- case "$1" in
382- */linux/*|*/net/*|*/xsi/*) printf '8\n' ;;
383- *) printf '1\n' ;;
384- esac
385-}
386-
387-build_man_for() {
388- local var="$1" src="$2" name="$3" sec out_mdoc out_txt
389- cfg_enabled "$var" || return 0
390- [ -x scripts/mkman/mkman ] || { printf 'error: mkman not built\n' >&2; exit 1; }
391-
392- grep -qE '!man|\?man' "$src" || return 0
393-
394- sec=$(man_section "$src")
395-
396- mkdir -p "man/man${sec}"
397- out_mdoc="man/man${sec}/${name}.${sec}"
398- out_txt="man/man${sec}/${name}.${sec}.txt"
399- if any_newer_than "$out_mdoc" "$src" scripts/mk/config.kv scripts/mkman/mkman; then
400- printf ' MAN %s\n' "$out_mdoc"
401- scripts/mkman/mkman -fmt mdoc -config scripts/mk/config.kv -section "$sec" "$src" > "$out_mdoc"
402- fi
403- if any_newer_than "$out_txt" "$src" scripts/mk/config.kv scripts/mkman/mkman; then
404- printf ' MAN %s\n' "$out_txt"
405- scripts/mkman/mkman -fmt txt -config scripts/mk/config.kv -section "$sec" "$src" > "$out_txt"
406- fi
407-}
408-
409-build_man() {
410- local src cat base sub name var
411- if [ ! -x scripts/mkman/mkman ] || any_newer_than scripts/mkman/mkman scripts/mkman/main.go scripts/mkman/page.go scripts/mkman/parse.go scripts/mkman/mdoc.go; then
412- printf ' GO scripts/mkman/mkman\n'
413- (cd scripts/mkman && go build -o mkman .)
414- fi
415- find cmd -type f -name '*.c' | while read -r src; do
416- sub=${src#cmd/}
417- cat=${sub%%/*}
418- base=${src##*/}
419- base=${base%.c}
420- name=${sub#*/}
421- if [ "${name}" != "${base}.c" ]; then
422- name=${name%%/*}
423- else
424- name=$base
425- fi
426- var=$(cfgvar "$cat" "$name")
427- build_man_for "$var" "$src" "$name"
428- done
429-}
430-
431-# install and clean
432-#
433-do_install() {
434- local bin f sec
435- mkdir -p "$PREFIX/bin" "$MANPREFIX/man1" "$MANPREFIX/man8"
436- find cmd -type f ! -name '*.*' -perm -100 | while read -r bin; do
437- printf ' INSTALL %s/bin/%s\n' "$PREFIX" "${bin##*/}"
438- cp "$bin" "$PREFIX/bin/${bin##*/}"
439- chmod 755 "$PREFIX/bin/${bin##*/}"
440- done
441- find man/man1 man/man8 -type f -name '*.[18]' 2>/dev/null | while read -r f; do
442- sec=${f%/*}; sec=${sec##*/}
443- cp "$f" "$MANPREFIX/${sec}/${f##*/}"
444- done
445-}
446-
447-do_regen() {
448- # 1. regenerate config
449- sh scripts/genconfig.sh
450-
451- # 2. re-read the new config.kv
452- if [ -f scripts/mk/config.kv ]; then
453- # shellcheck disable=SC1091
454- . scripts/mk/config.kv
455- fi
456-
457- # 3. clean and generate files using scripts/genconfig.sh
458- CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" YACC="$YACC" sh scripts/genconfig.sh clean
459- CC="$CC" CFLAGS="$CFLAGS" CPPFLAGS="$CPPFLAGS" YACC="$YACC" sh scripts/genconfig.sh all
460-}
461-
462-do_clean() {
463- printf ' CLEAN object files\n'
464- find shared cmd -name '*.o' -exec rm -f {} +
465- printf ' CLEAN static libraries\n'
466- find shared -name '*.a' -exec rm -f {} +
467- printf ' CLEAN compiled binaries\n'
468- find cmd -type f ! -name '*.*' -perm -100 -exec rm -f {} +
469- sh scripts/genconfig.sh clean
470- rm -f scripts/mk/config.mk scripts/mk/config.kv scripts/mk/rules.mk shared/libutil/nofork_list.h
471- printf ' CLEAN build tools\n'
472- rm -f scripts/mkman/mkman
473- printf ' CLEAN dev artifacts\n'
474- rm -f cmd/dev/cc/cc1 cmd/dev/cc/cpp cmd/dev/as/as cmd/dev/ld/ld cmd/dev/ar/ar
475- rm -f shared/libaruuelf.so
476- rm -f cmd/dev/config.h cmd/dev/cc/config.h cmd/dev/version.h
477- printf ' CLEAN misc\n'
478- rm -rf man/man1 man/man8
479- rm -rf aruu-box .box
480- printf ' CLEAN done\n'
481-}
482-
483-case "$TARGET" in
484- all)
485- build_lib
486- build_posix
487- build_dev
488- build_simple_tools linux
489- build_simple_tools net
490- build_simple_tools xsi
491- build_simple_tools pseudo
492- build_simple_tools extra
493- ;;
494- lib) build_lib ;;
495- posix) build_lib; build_posix ;;
496- dev) build_lib; build_dev ;;
497- make) build_lib; build_make ;;
498- linux|net|xsi|pseudo|extra)
499- build_lib; build_simple_tools "$TARGET" ;;
500- regen) do_regen ;;
501- clean) do_clean ;;
502- man) build_man ;;
503- install)
504- build_lib
505- build_posix
506- build_dev
507- build_simple_tools linux
508- build_simple_tools net
509- build_simple_tools xsi
510- build_simple_tools pseudo
511- build_simple_tools extra
512- build_man
513- do_install
514- ;;
515- help)
516- printf 'usage: sh Makefile.sh [-jN] [-lN] [all|clean|install|man|lib|posix|linux|net|xsi|pseudo|extra|dev|make|regen|help]\n'
517- printf '\nTargets:\n'
518- printf ' all Build all enabled utilities\n'
519- printf ' clean Clean built objects and binaries\n'
520- printf ' install Install binaries and man pages\n'
521- printf ' man Generate man pages\n'
522- printf ' lib Build core libraries\n'
523- printf ' posix Build POSIX utilities\n'
524- printf ' linux Build Linux utilities\n'
525- printf ' net Build Network utilities\n'
526- printf ' xsi Build XSI utilities\n'
527- printf ' pseudo Build Pseudo utilities\n'
528- printf ' extra Build Extra utilities\n'
529- printf ' dev Build Dev toolchain\n'
530- printf ' make Build POSIX make\n'
531- printf ' regen Regenerate configuration and all generated source files\n'
532- printf ' help Show this help message\n'
533- ;;
534- *)
535- printf 'usage: sh Makefile.sh [-jN] [-lN] [all|clean|install|man|lib|posix|linux|net|xsi|pseudo|extra|dev|make|regen|help]\n' >&2
536- exit 1
537- ;;
538-esac
M
README
+42,
-17
1@@ -9,7 +9,8 @@ on their toybox applet equivalent.
2 The codebase has diverged a lot since the project started. And we have
3 a myriad of utilities which were written specifically for ARUU.
4 We also have greatly simplified ports of components from other systems
5-and projects, such as FreeBSD sh, minix yap (pager), onetrueawk, etc.
6+and projects, such as FreeBSD sh, minix yap (pager (thanks shrub)),
7+onetrueawk, etc.
8
9 OVERVIEW
10 ========
11@@ -19,10 +20,11 @@ aruu builds two things:
12 1. individual tool binaries (default 'make all')
13 2. aruu-box: a multi-call binary containing every tool ('make box')
14
15-The multi-call binary is built by scripts/mkbox. It uses objcopy to
16-rename each tool's 'main' symbol, generates a C dispatch table, then
17-links everything into a single statically-dispatched binary. Invoke
18-tools by symlinking aruu-box to the tool name, or by running:
19+The multi-call binary is built by ninqu's box group (BOX_OBJ, BOX_DISPATCH,
20+and BOX in ninqu.rules). Each enabled tool gets objcopied so only its
21+renamed main stays global, scripts/genbox.sh turns the resulting object
22+paths into a C dispatch table, and everything links into one binary.
23+Invoke tools by symlinking aruu-box to the tool name, or by running:
24
25 aruu-box <command> [args...]
26
27@@ -43,6 +45,23 @@ Optional:
28
29 yacc only needed if rebuilding bc from bc.y
30
31+HOW THE BUILD WORKS
32+====================
33+
34+make builds cmd/dev/ninqu/ninqu, then forwards whatever target you
35+gave it (make all, make box, make clean, ...) straight to that binary.
36+
37+ninqu reads ninqu.rules, which pulls in scripts/mk/config.set. That
38+file is generated by scripts/genconfig.sh, which reads build.cfg,
39+resolves every preset and per-tool override, and writes config.set
40+(read by ninqu), config.kv (shell-sourced by genbox.sh), and
41+scripts/mk/features.h (the resolved FEATURE_* set, pulled into every
42+compile with -include).
43+
44+To change what gets built, or see what a preset turns on or off,
45+edit or read build.cfg. That is the one file genconfig.sh expands
46+from, everything downstream of it is generated and safe to delete.
47+
48 BUILDING
49 ========
50
51@@ -58,7 +77,8 @@ Both targets can be combined:
52
53 make all box
54
55-Configuration is in config.mk. You can override compile flags via make
56+Configuration is in build.cfg. You can override compile flags via make
57+or with ninqu's -D flag (see HOW THE BUILD WORKS above).
58
59 INSTALL
60 =======
61@@ -69,14 +89,18 @@ INSTALL
62 SOURCE LAYOUT
63 =============
64
65- Makefile pdpmake-compatible build rules
66+ Makefile builds cmd/dev/ninqu/ninqu, forwards to it
67+ ninqu.rules all real build knowledge, read by ninqu
68+ build.cfg presets and per-tool overrides
69 scripts/
70- mkbox builds aruu-box from compiled tool objects
71- getconf.sh generates cmd/posix/getconf.h at build time
72+ genconfig.sh build.cfg -> config.set/config.kv/features.h
73+ genbox.sh writes aruu-box's dispatch table
74+ getconf.sh generates cmd/posix/getconf.h at build time
75+ mkbox legacy, superseded by ninqu's box group
76 shared/
77- *.h project headers (util.h, arg.h, utf.h, ...)
78- libutil/ utility library (eprintf, ealloc, recurse, ...)
79- libutf/ UTF-8/rune library
80+ *.h project headers (util.h, arg.h, utf.h, ...)
81+ libutil/ utility library (eprintf, ealloc, recurse, ...)
82+ libutf/ UTF-8/rune library
83 cmd/
84 posix/ POSIX.1-2008 tools (sh, make, grep, sed, pax, ...)
85 linux/ Linux-specific tools (mount, dmesg, insmod, ...)
86@@ -84,8 +108,9 @@ SOURCE LAYOUT
87 pseudo/ widely-used non-POSIX tools (tar, stat, seq, ...)
88 xsi/ XSI-extension tools (mknod, su, passwd)
89 extra/ third-party-flavored extras (yap, b3sum, fdisk, ...)
90- dev/ small toolchain (as, cc, ld, ar); not built by
91- default, see PRESET_UTILS in build.cfg
92+ dev/ small toolchain tooling (ninqu, in the future: as, cc, ld, ar)
93+
94+ See PRESET_UTILS in build.cfg to understand, en/dis/able the build of a set
95
96 TOOLS
97 =====
98@@ -125,9 +150,9 @@ extra:
99
100 TODOs / PENDING RELEASE
101 =======================
102-init - situation/sctl rewrite
103-troff - neatroff
104-man - unsure
105+init - vendor cowmonk's
106+troff - neatroff? create cmd/9?
107+man - unsure, this is not a fun task
108 vendor and publish my compiler toolchain (small binutils + cproc), even if incomplete - unsure
109
110 LICENSE
+19,
-14
1@@ -161,7 +161,7 @@ FEATURE_SH_HISTEDIT=1
2
3 # FEATURE_NOEXEC: when set to 1, wexec/wpopen/wsystem can execute registered
4 # tool mains directly inside the forked child process (skipping execve)
5-# This is equivalent to Busybox noexec applet support.
6+# This is equivalent to Busybox noexec applet support
7 # This applies to ALL commands contained within aruu-box, you may also
8 # specify if some specific command is not an attack vector to also be nofork
9 # via enabling FEATURE_NOFORK=1 and specifying those commands in NOFORK_LIST
10@@ -177,7 +177,7 @@ FEATURE_SH_HISTEDIT=1
11 # (like cat, echo, pwd, test) directly in the current process without spawning
12 # a subprocess (noexec+nofork). This is equivalent to Busybox nofork applet support
13 #
14-FEATURE_NOFORK=1
15+#FEATURE_NOFORK=1
16 #
17 # FEATURE_NOFORK_OVERRIDE: only meaningful when FEATURE_NOFORK=1
18 # when set to 1, ARUU_NOFORK=0 in the environment forces fork for nofork commands
19@@ -192,25 +192,30 @@ NOFORK_LIST="true false echo pwd test [ basename dirname cat clear yes"
20 #
21
22 BUILD_EXTRA_YAP=0
23+if pkg-config --exists tinfo 2>/dev/null; then
24+ BUILD_EXTRA_YAP=1
25+fi
26
27 # tls backend: set exactly one to 1 to enable tls
28 #
29 FEATURE_USE_BEARSSL=0
30-FEATURE_USE_LIBRESSL=0
31+FEATURE_USE_LIBTLS=0
32 FEATURE_USE_OPENSSL=0
33-FEATURE_USE_SSL=0
34+FEATURE_USE_SSL=1
35 #
36-if [ "$FEATURE_USE_SSL" = 1 ]; then
37- if [ "$FEATURE_USE_BEARSSL" = 0 ] && [ "$FEATURE_USE_LIBRESSL" = 0 ] && [ "$FEATURE_USE_OPENSSL" = 0 ]; then
38- if pkg-config --exists libbearssl 2>/dev/null || pkg-config --exists bearssl 2>/dev/null; then
39- FEATURE_USE_BEARSSL=1
40- elif pkg-config --exists libtls 2>/dev/null; then
41- FEATURE_USE_LIBRESSL=1
42- elif pkg-config --exists openssl 2>/dev/null; then
43- FEATURE_USE_OPENSSL=1
44- fi
45+if [ "$FEATURE_USE_SSL" = 1 ] &&
46+ [ "$FEATURE_USE_BEARSSL$FEATURE_USE_LIBTLS$FEATURE_USE_OPENSSL" = "000" ]
47+then
48+ if pkg-config --exists libbearssl 2>/dev/null || pkg-config --exists bearssl 2>/dev/null; then
49+ FEATURE_USE_BEARSSL=1
50+ elif pkg-config --exists libtls 2>/dev/null; then
51+ FEATURE_USE_LIBTLS=1
52+ elif pkg-config --exists openssl 2>/dev/null; then
53+ FEATURE_USE_OPENSSL=1
54 fi
55 fi
56+
57+unset FEATURE_USE_SSL
58 # auto-detect TLS/SSL flags based on selected backend
59 CPPFLAGS_TLS="" LDFLAGS_TLS="" LDLIBS_TLS="" PKG_NAME=""
60 if [ "$FEATURE_USE_BEARSSL" = 1 ]; then
61@@ -219,7 +224,7 @@ if [ "$FEATURE_USE_BEARSSL" = 1 ]; then
62 elif pkg-config --exists bearssl 2>/dev/null; then
63 PKG_NAME="bearssl"
64 fi
65-elif [ "$FEATURE_USE_LIBRESSL" = 1 ]; then
66+elif [ "$FEATURE_USE_LIBTLS" = 1 ]; then
67 if pkg-config --exists libtls 2>/dev/null; then
68 PKG_NAME="libtls"
69 else
+294,
-0
1@@ -0,0 +1,294 @@
2+#include "ninqu.h"
3+
4+#include <errno.h>
5+#include <libgen.h>
6+#include <limits.h>
7+#include <stdio.h>
8+#include <stdlib.h>
9+#include <string.h>
10+#include <sys/wait.h>
11+#include <unistd.h>
12+
13+int jobs_n = 1;
14+int summary_mode;
15+int failed_any;
16+
17+/* fork, chdir, redirect, exec. the child _exit on error so the
18+ * parent never sees a stale pid */
19+pid_t
20+spawn_inst(struct Inst *inst, struct StrList *argv)
21+{
22+ pid_t pid = fork();
23+ if (pid < 0)
24+ eprintf("fork:");
25+ if (pid != 0)
26+ return pid;
27+
28+ if (inst->workdir[0] && chdir(inst->workdir) != 0)
29+ eprintf("chdir %s:", inst->workdir);
30+ if (inst->redirect) {
31+ FILE *f = fopen(inst->out, "w");
32+ if (!f)
33+ eprintf("open %s:", inst->out);
34+ if (dup2(fileno(f), STDOUT_FILENO) < 0)
35+ _exit(127);
36+ fclose(f);
37+ }
38+ child_execvp(sl_argv(argv));
39+}
40+
41+/* fork a supervisor that chains each stage stdout into the next
42+ * stdin. the parent waits on the supervisor, so the whole pipeline
43+ * has one exit status */
44+pid_t
45+spawn_pipe(struct Inst *inst)
46+{
47+ pid_t pid = fork();
48+ if (pid < 0)
49+ eprintf("fork:");
50+ if (pid != 0)
51+ return pid;
52+
53+ if (inst->workdir[0] && chdir(inst->workdir) != 0)
54+ eprintf("chdir %s:", inst->workdir);
55+
56+ {
57+ int k, prev_read = -1;
58+ for (k = 0; k < inst->cmd.nstages; k++) {
59+ int p[2] = {-1, -1};
60+ pid_t sp;
61+ if (k < inst->cmd.nstages - 1) {
62+ if (pipe(p) != 0)
63+ _exit(127);
64+ }
65+ sp = fork();
66+ if (sp < 0)
67+ _exit(127);
68+ if (sp == 0) {
69+ if (prev_read >= 0) {
70+ if (dup2(prev_read, STDIN_FILENO) < 0)
71+ _exit(127);
72+ close(prev_read);
73+ }
74+ if (k < inst->cmd.nstages - 1) {
75+ close(p[0]);
76+ if (dup2(p[1], STDOUT_FILENO) < 0)
77+ _exit(127);
78+ close(p[1]);
79+ }
80+ child_execvp(sl_argv(inst->cmd.stages[k]));
81+ }
82+ if (prev_read >= 0)
83+ close(prev_read);
84+ if (k < inst->cmd.nstages - 1) {
85+ close(p[1]);
86+ prev_read = p[0];
87+ }
88+ }
89+ for (k = 0; k < inst->cmd.nstages; k++)
90+ wait(NULL);
91+ _exit(0);
92+ }
93+}
94+
95+/* mkdir -p the directory an instance writes into, so a rule (out)
96+ * can point at a path that does not exist yet */
97+void
98+mk_out_dir(const char *out)
99+{
100+ char tmp[PATH_MAX];
101+ char *dir;
102+
103+ if (!out || !out[0])
104+ return;
105+ estrlcpy(tmp, out, sizeof tmp);
106+ dir = dirname(tmp);
107+ if (strcmp(dir, ".") == 0 || strcmp(dir, "/") == 0)
108+ return;
109+ mkdirp(dir, 0777, 0777);
110+}
111+
112+/* print the rule line and flush before the child runs, so the
113+ * announcement appears before the child own output */
114+void
115+announce_inst(struct Inst *inst)
116+{
117+ mk_out_dir(inst->out);
118+ printf(" %-10s %s\n", rules[inst->rule_idx].name, inst->out[0] ? inst->out : "(phony)");
119+ fflush(stdout);
120+}
121+
122+/* run one instance synchronously. exits on failure since a missing
123+ * producer is unrecoverable */
124+void
125+run_inst_sync(int idx)
126+{
127+ struct Inst *inst = &insts[idx];
128+ pid_t pid;
129+ int status;
130+
131+ if (!inst_stale(inst))
132+ return;
133+ announce_inst(inst);
134+ pid = inst->cmd.is_pipe ? spawn_pipe(inst) : spawn_inst(inst, &inst->cmd.argv);
135+ if (waitpid(pid, &status, 0) < 0)
136+ eprintf("waitpid:");
137+ if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0))
138+ eprintf("manifest: producer failed (rule %s)\n", rules[inst->rule_idx].name);
139+}
140+
141+/* run a batch of independent instances concurrently, at most jobs_n
142+ * at a time. all instances in the batch are at the same kahn level */
143+void
144+run_batch(int *idxs, int n)
145+{
146+ int next = 0, running = 0;
147+ pid_t *pids = emalloc((size_t)(n > 0 ? n : 1) * sizeof *pids);
148+ int *slot = emalloc((size_t)(n > 0 ? n : 1) * sizeof *slot);
149+
150+ while (next < n || running > 0) {
151+ while (running < jobs_n && next < n) {
152+ struct Inst *inst = &insts[idxs[next]];
153+ pid_t pid;
154+
155+ if (!inst_stale(inst)) {
156+ next++;
157+ continue;
158+ }
159+ announce_inst(inst);
160+
161+ pid = inst->cmd.is_pipe ? spawn_pipe(inst) : spawn_inst(inst, &inst->cmd.argv);
162+ pids[running] = pid;
163+ slot[running] = idxs[next];
164+ running++;
165+ next++;
166+ }
167+ if (running > 0) {
168+ int status;
169+ pid_t done = wait(&status);
170+ int i;
171+ if (done < 0)
172+ eprintf("wait:");
173+ for (i = 0; i < running; i++) {
174+ if (pids[i] == done) {
175+ if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
176+ char *cmd = sl_join(&insts[slot[i]].cmd.argv);
177+ weprintf("rule %s failed: %s\n", rules[insts[slot[i]].rule_idx].name, cmd);
178+ free(cmd);
179+ failed_any = 1;
180+ }
181+ pids[i] = pids[running - 1];
182+ slot[i] = slot[running - 1];
183+ running--;
184+ break;
185+ }
186+ }
187+ }
188+ }
189+ free(pids);
190+ free(slot);
191+}
192+
193+/* run an instance and all its transitive deps in order, so a
194+ * producer like LIBUTIL does not archive .o files that have not
195+ * been compiled yet */
196+void
197+run_inst_with_deps(int idx)
198+{
199+ struct Inst *inst = &insts[idx];
200+ int i;
201+
202+ for (i = 0; i < inst->dep_rule_names.n; i++) {
203+ struct Rule *dep = rule_find(inst->dep_rule_names.v[i]);
204+ int k;
205+ if (!dep)
206+ continue;
207+ for (k = 0; k < dep->n_inst; k++)
208+ dep_push(inst, dep->inst_idx[k]);
209+ }
210+ for (i = 0; i < inst->n_dep; i++)
211+ run_inst_with_deps(inst->dep_inst[i]);
212+ run_inst_sync(idx);
213+}
214+
215+/* bootstrap: run the producer for path only if path does not exist.
216+ * after that the normal dependency graph takes over */
217+void
218+materialize_if_missing(const char *path)
219+{
220+ struct Rule *r;
221+
222+ if (file_exists(path))
223+ return;
224+ r = rule_find_output(path);
225+ if (!r)
226+ r = rule_find_produces(base_of(path));
227+ if (!r)
228+ return;
229+ expand_rule(r->name);
230+ if (r->n_inst > 0) {
231+ int k;
232+ for (k = 0; k < r->n_inst; k++)
233+ run_inst_with_deps(r->inst_idx[k]);
234+ }
235+}
236+
237+/* run argv synchronously, return its stdout as a string. used by
238+ * (set NAME (capture (exec ...))) at parse time */
239+char *
240+capture_argv(struct StrList *argv)
241+{
242+ int fds[2];
243+ pid_t pid;
244+ char *buf;
245+ size_t cap, len;
246+ int status;
247+
248+ if (argv->n == 0)
249+ eprintf("capture: empty argv\n");
250+ if (pipe(fds) != 0)
251+ eprintf("capture: pipe:");
252+
253+ pid = fork();
254+ if (pid < 0)
255+ eprintf("capture: fork:");
256+ if (pid == 0) {
257+ close(fds[0]);
258+ if (dup2(fds[1], STDOUT_FILENO) < 0)
259+ _exit(127);
260+ close(fds[1]);
261+ child_execvp(sl_argv(argv));
262+ }
263+ close(fds[1]);
264+
265+ cap = 4096;
266+ len = 0;
267+ buf = emalloc(cap);
268+ for (;;) {
269+ ssize_t n;
270+ if (len + 4096 > cap) {
271+ cap *= 2;
272+ buf = erealloc(buf, cap);
273+ }
274+ n = read(fds[0], buf + len, cap - len - 1);
275+ if (n < 0) {
276+ if (errno == EINTR)
277+ continue;
278+ eprintf("capture: read:");
279+ }
280+ if (n == 0)
281+ break;
282+ len += (size_t)n;
283+ }
284+ buf[len] = '\0';
285+ close(fds[0]);
286+
287+ while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
288+ buf[--len] = '\0';
289+
290+ if (waitpid(pid, &status, 0) < 0)
291+ eprintf("capture: waitpid:");
292+ if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0))
293+ eprintf("capture: command failed\n");
294+ return buf;
295+}
+355,
-0
1@@ -0,0 +1,355 @@
2+#include "ninqu.h"
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+void
9+dep_push(struct Inst *inst, int idx)
10+{
11+ int i;
12+ for (i = 0; i < inst->n_dep; i++)
13+ if (inst->dep_inst[i] == idx)
14+ return;
15+ if (inst->n_dep >= inst->cap_dep) {
16+ inst->cap_dep = inst->cap_dep ? inst->cap_dep * 2 : 4;
17+ inst->dep_inst = erealloc(inst->dep_inst, (size_t)inst->cap_dep * sizeof *inst->dep_inst);
18+ }
19+ inst->dep_inst[inst->n_dep++] = idx;
20+}
21+
22+void
23+resolve_deps(void)
24+{
25+ int i, j, k;
26+ for (i = 0; i < ninsts; i++) {
27+ struct Inst *inst = &insts[i];
28+ for (j = 0; j < inst->dep_rule_names.n; j++) {
29+ struct Rule *dep = rule_find(inst->dep_rule_names.v[j]);
30+ if (!dep)
31+ continue;
32+ for (k = 0; k < dep->n_inst; k++)
33+ dep_push(inst, dep->inst_idx[k]);
34+ }
35+ }
36+}
37+
38+int
39+inst_stale(struct Inst *inst)
40+{
41+ int i;
42+ if (inst->phony)
43+ return 1;
44+ if (!file_exists(inst->out))
45+ return 1;
46+ for (i = 0; i < inst->in.n; i++)
47+ if (mtime_of(inst->in.v[i]) > mtime_of(inst->out))
48+ return 1;
49+ for (i = 0; i < inst->stale_extra.n; i++)
50+ if (file_exists(inst->stale_extra.v[i])
51+ && mtime_of(inst->stale_extra.v[i]) > mtime_of(inst->out))
52+ return 1;
53+ return 0;
54+}
55+
56+/* a pure $(NAME) splits on whitespace so $(CFLAGS) becomes many
57+ * flags, while -I$(INCDIR) stays one token */
58+static int
59+is_pure_ref(const char *tok)
60+{
61+ size_t len;
62+ int depth;
63+ const char *p;
64+
65+ if (tok[0] != '$' || tok[1] != '(')
66+ return 0;
67+ len = strlen(tok);
68+ if (tok[len - 1] != ')')
69+ return 0;
70+ depth = 1;
71+ for (p = tok + 2; *p; p++) {
72+ if (p[0] == '$' && p[1] == '(') {
73+ depth++;
74+ p++;
75+ continue;
76+ }
77+ if (*p == ')') {
78+ depth--;
79+ if (depth == 0)
80+ return (size_t)(p - tok) == len - 1;
81+ }
82+ }
83+ return 0;
84+}
85+
86+static void
87+expand_cmd_token(struct StrList *out, const char *raw)
88+{
89+ char *exp = kv_expand(raw);
90+ if (is_pure_ref(raw)) {
91+ struct StrList pieces = {0};
92+ int j;
93+ sl_split(&pieces, exp);
94+ for (j = 0; j < pieces.n; j++)
95+ sl_push(out, pieces.v[j]);
96+ } else {
97+ sl_push(out, exp);
98+ }
99+ free(exp);
100+}
101+
102+static void
103+build_cmd(struct Inst *inst, struct Rule *r)
104+{
105+ int i;
106+ if (r->cmd.is_pipe) {
107+ inst->cmd.is_pipe = 1;
108+ inst->cmd.nstages = r->cmd.nstages;
109+ inst->cmd.stages = emalloc((size_t)r->cmd.nstages * sizeof *inst->cmd.stages);
110+ for (i = 0; i < r->cmd.nstages; i++) {
111+ int j;
112+ struct StrList *slot = emalloc(sizeof *slot);
113+ memset(slot, 0, sizeof *slot);
114+ for (j = 0; j < r->cmd.stages[i]->n; j++)
115+ expand_cmd_token(slot, r->cmd.stages[i]->v[j]);
116+ inst->cmd.stages[i] = slot;
117+ }
118+ for (i = 0; i < inst->cmd.nstages; i++) {
119+ char *joined = sl_join(inst->cmd.stages[i]);
120+ if (i > 0)
121+ sl_push(&inst->cmd.argv, "|");
122+ sl_push(&inst->cmd.argv, joined);
123+ free(joined);
124+ }
125+ } else {
126+ for (i = 0; i < r->cmd.argv.n; i++)
127+ expand_cmd_token(&inst->cmd.argv, r->cmd.argv.v[i]);
128+ }
129+}
130+
131+/* @NAME resolves to a rule ref. glob:PREFIX goes to glob_expand.
132+ * anything else is a path: if it does not exist, try to find a
133+ * producer and run it first */
134+static void
135+resolve_input_token(int inst_idx, const char *raw, int only_dep)
136+{
137+ char *exp = kv_expand(raw);
138+
139+ if (exp[0] == '@') {
140+ const char *rulename = exp + 1;
141+ struct Rule *dep;
142+ struct KvStore *saved = local_overlay;
143+ struct Inst *inst;
144+ int i;
145+
146+ /* expand_rule may set/clear local_overlay for its own loop */
147+ local_overlay = NULL;
148+ expand_rule(rulename);
149+ local_overlay = saved;
150+
151+ /* insts[] may have moved during expand_rule */
152+ inst = &insts[inst_idx];
153+ dep = rule_find(rulename);
154+ if (!dep)
155+ eprintf(
156+ "manifest: '%s' references unknown rule '@%s'\n", rules[inst->rule_idx].name, rulename
157+ );
158+ if (!sl_has(&inst->dep_rule_names, rulename))
159+ sl_push(&inst->dep_rule_names, rulename);
160+ if (!only_dep)
161+ for (i = 0; i < dep->n_inst; i++)
162+ sl_push(&inst->in, insts[dep->inst_idx[i]].out);
163+ } else if (strncmp(exp, "glob:", 5) == 0) {
164+ struct StrList matches = {0};
165+ int i;
166+ glob_expand(exp + 5, &matches);
167+ for (i = 0; i < matches.n; i++) {
168+ if (only_dep)
169+ sl_push(&insts[inst_idx].stale_extra, matches.v[i]);
170+ else
171+ sl_push(&insts[inst_idx].in, matches.v[i]);
172+ }
173+ } else {
174+ struct StrList pieces = {0};
175+ int i;
176+ sl_split(&pieces, exp);
177+ for (i = 0; i < pieces.n; i++) {
178+ const char *path = pieces.v[i];
179+ if (!file_exists(path))
180+ materialize_if_missing(path);
181+ if (only_dep)
182+ sl_push(&insts[inst_idx].stale_extra, path);
183+ else
184+ sl_push(&insts[inst_idx].in, path);
185+ }
186+ }
187+ free(exp);
188+}
189+
190+static void
191+setup_inst(struct Inst *inst, struct Rule *r)
192+{
193+ inst->phony = r->phony;
194+ inst->redirect = r->redirect;
195+ if (r->workdir[0]) {
196+ char *w = kv_expand(r->workdir);
197+ strlcpy(inst->workdir, w, sizeof inst->workdir);
198+ free(w);
199+ }
200+}
201+
202+/* resolve deps, expand (out), populate IN/OUT on the overlay, build
203+ * the cmd. caller must have local_overlay set if needed */
204+static void
205+finalize_inst(int idx, struct Rule *r, struct KvStore *ov)
206+{
207+ struct Inst *inst = &insts[idx];
208+ int k;
209+
210+ for (k = 0; k < r->in.n; k++)
211+ resolve_input_token(idx, r->in.v[k], 0);
212+ for (k = 0; k < r->extra_deps.n; k++)
213+ resolve_input_token(idx, r->extra_deps.v[k], 1);
214+
215+ inst = &insts[idx]; /* may have moved */
216+ {
217+ char *o = kv_expand(r->out);
218+ strlcpy(inst->out, o, sizeof inst->out);
219+ free(o);
220+ }
221+ kv_set_raw(ov, "IN", sl_join(&inst->in));
222+ kv_set_raw(ov, "OUT", inst->out);
223+ build_cmd(inst, r);
224+}
225+
226+void
227+expand_rule(const char *name)
228+{
229+ struct Rule *r = rule_find(name);
230+ int i;
231+
232+ if (!r)
233+ eprintf("manifest: unknown rule '%s'\n", name);
234+ if (r->expanded)
235+ return;
236+ r->expanded = 1;
237+
238+ /* a gate with $(...) is per-instance, a static gate is checked
239+ * once and if false the rule produces zero instances */
240+ if (r->gate && !gate_has_dyn(r->gate) && !gate_eval(r->gate))
241+ return;
242+
243+ if (r->require_file[0] && r->globs.n == 0) {
244+ char *rf = kv_expand(r->require_file);
245+ int ok = file_exists(rf);
246+ free(rf);
247+ if (!ok)
248+ return;
249+ }
250+
251+ if (r->globs.n > 0) {
252+ struct StrList matches = {0};
253+ int j;
254+ int r_literal = rule_is_literal(r);
255+
256+ /* run order-only @rule producers before globbing, so a codegen
257+ * step that generates sources finishes before the glob runs.
258+ * skipped in summary mode since -S must not execute */
259+ if (!summary_mode) {
260+ for (j = 0; j < r->extra_deps.n; j++) {
261+ char *tok = kv_expand(r->extra_deps.v[j]);
262+ if (tok[0] == '@') {
263+ struct Rule *dep = rule_find(tok + 1);
264+ if (dep) {
265+ expand_rule(dep->name);
266+ if (dep->n_inst > 0) {
267+ int k;
268+ for (k = 0; k < dep->n_inst; k++)
269+ run_inst_with_deps(dep->inst_idx[k]);
270+ }
271+ }
272+ }
273+ free(tok);
274+ }
275+ }
276+
277+ glob_all(r, &matches);
278+
279+ for (i = 0; i < matches.n; i++) {
280+ const char *match = matches.v[i];
281+ const char *base = base_of(match);
282+ char dir[1024], stem[512], basestem[512], ext[64], unused[64];
283+ char ident[512];
284+ struct KvStore ov;
285+ int idx;
286+ struct Inst *inst;
287+
288+ if (sl_has(&r->skip, base))
289+ continue;
290+ if (!r_literal && path_claimed_elsewhere(match, r))
291+ continue;
292+ if (r->require_marker[0] && !file_contains(match, r->require_marker))
293+ continue;
294+
295+ if (strrchr(match, '/')) {
296+ size_t dl = (size_t)(strrchr(match, '/') - match);
297+ if (dl >= sizeof dir)
298+ dl = sizeof dir - 1;
299+ memcpy(dir, match, dl);
300+ dir[dl] = '\0';
301+ } else {
302+ strlcpy(dir, ".", sizeof dir);
303+ }
304+ splitext(base, basestem, sizeof basestem, ext, sizeof ext);
305+ splitext(match, stem, sizeof stem, unused, sizeof unused);
306+ to_ident(basestem, ident, sizeof ident);
307+
308+ memset(&ov, 0, sizeof ov);
309+ kv_set_raw(&ov, "MATCH", match);
310+ kv_set_raw(&ov, "DIR", dir);
311+ kv_set_raw(&ov, "BASE", base);
312+ kv_set_raw(&ov, "EXT", ext);
313+ kv_set_raw(&ov, "STEM", stem);
314+ kv_set_raw(&ov, "BASESTEM", basestem);
315+ kv_set_raw(&ov, "IDENT", ident);
316+ local_overlay = &ov;
317+
318+ if (r->gate && gate_has_dyn(r->gate)) {
319+ struct Gate *mg = gate_materialize(r->gate);
320+ int ok = gate_eval(mg);
321+ gate_free(mg);
322+ if (!ok) {
323+ local_overlay = NULL;
324+ continue;
325+ }
326+ }
327+ if (r->require_file[0]) {
328+ char *rf = kv_expand(r->require_file);
329+ int ok = file_exists(rf);
330+ free(rf);
331+ if (!ok) {
332+ local_overlay = NULL;
333+ continue;
334+ }
335+ }
336+
337+ idx = inst_new((int)(r - rules));
338+ inst = &insts[idx];
339+ setup_inst(inst, r);
340+ finalize_inst(idx, r, &ov);
341+ local_overlay = NULL;
342+ }
343+ } else {
344+ int idx = inst_new((int)(r - rules));
345+ struct Inst *inst = &insts[idx];
346+ struct KvStore ov;
347+
348+ /* non-glob rules have no MATCH/DIR/BASE to expose, so the
349+ * overlay only carries IN/OUT for build_cmd */
350+ setup_inst(inst, r);
351+ memset(&ov, 0, sizeof ov);
352+ local_overlay = &ov;
353+ finalize_inst(idx, r, &ov);
354+ local_overlay = NULL;
355+ }
356+}
+193,
-0
1@@ -0,0 +1,193 @@
2+#include "ninqu.h"
3+
4+#include <ctype.h>
5+#include <fnmatch.h>
6+#include <glob.h>
7+#include <stdio.h>
8+#include <stdlib.h>
9+#include <string.h>
10+#include <sys/stat.h>
11+
12+long
13+mtime_of(const char *path)
14+{
15+ struct stat st;
16+ if (stat(path, &st) != 0)
17+ return -1;
18+ return (long)st.st_mtime;
19+}
20+
21+int
22+file_exists(const char *path)
23+{
24+ return mtime_of(path) >= 0;
25+}
26+
27+int
28+file_contains(const char *path, const char *needle)
29+{
30+ FILE *fp;
31+ char buf[8192];
32+ size_t n, needlen = strlen(needle), taillen = 0;
33+ char tail[256];
34+ int found = 0;
35+
36+ if (!needle[0])
37+ return 1;
38+ fp = fopen(path, "r");
39+ if (!fp)
40+ return 0;
41+ tail[0] = '\0';
42+ while (!found && (n = fread(buf, 1, sizeof buf - 1, fp)) > 0) {
43+ char joined[8192 + 256];
44+ buf[n] = '\0';
45+ snprintf(joined, sizeof joined, "%s%s", tail, buf);
46+ if (strstr(joined, needle))
47+ found = 1;
48+ taillen = strlen(buf);
49+ if (taillen > needlen)
50+ taillen = needlen - 1;
51+ snprintf(tail, sizeof tail, "%s", buf + strlen(buf) - taillen);
52+ }
53+ fclose(fp);
54+ return found;
55+}
56+
57+/* recurse_dir has no userdata slot, so the pattern and output list
58+ * travel through file-scope statics for the span of one call */
59+static const char *rg_suffix;
60+static struct StrList *rg_out;
61+
62+static void
63+rg_visit(const char *path)
64+{
65+ struct stat st;
66+
67+ if (lstat(path, &st) != 0)
68+ return;
69+ if (S_ISDIR(st.st_mode)) {
70+ recurse_dir(path, rg_visit);
71+ return;
72+ }
73+ if (S_ISREG(st.st_mode) && fnmatch(rg_suffix, base_of(path), 0) == 0)
74+ sl_push(rg_out, path);
75+}
76+
77+static void
78+recurse_glob(const char *dir, const char *suffix, struct StrList *out)
79+{
80+ rg_suffix = suffix;
81+ rg_out = out;
82+ recurse_dir(dir, rg_visit);
83+}
84+
85+/* a double-star-slash pattern recurses from the dir before it,
86+ * matching the suffix against each basename. anything else goes to
87+ * glob(3) as-is */
88+void
89+glob_expand(const char *pattern, struct StrList *out)
90+{
91+ const char *rec = strstr(pattern, "**/");
92+ if (rec) {
93+ char prefix[1024], suffix[1024];
94+ size_t plen = (size_t)(rec - pattern);
95+ if (plen >= sizeof prefix)
96+ plen = sizeof prefix - 1;
97+ memcpy(prefix, pattern, plen);
98+ prefix[plen] = '\0';
99+ if (plen > 0 && prefix[plen - 1] == '/')
100+ prefix[plen - 1] = '\0';
101+ strlcpy(suffix, rec + 3, sizeof suffix);
102+ recurse_glob(prefix[0] ? prefix : ".", suffix, out);
103+ } else {
104+ glob_t g;
105+ size_t i;
106+ if (glob(pattern, 0, NULL, &g) == 0)
107+ for (i = 0; i < g.gl_pathc; i++)
108+ sl_push(out, g.gl_pathv[i]);
109+ globfree(&g);
110+ }
111+}
112+
113+void
114+glob_all(struct Rule *r, struct StrList *out)
115+{
116+ int i;
117+ for (i = 0; i < r->globs.n; i++) {
118+ char *pat = kv_expand(r->globs.v[i]);
119+ glob_expand(pat, out);
120+ free(pat);
121+ }
122+}
123+
124+int
125+is_wildcard_pattern(const char *pattern)
126+{
127+ return strpbrk(pattern, "*?[") != NULL;
128+}
129+
130+/* true if every glob on r is a literal path. a rule built entirely
131+ * from literals is the override side of path_claimed_elsewhere */
132+int
133+rule_is_literal(struct Rule *r)
134+{
135+ int i;
136+ if (r->globs.n == 0)
137+ return 0;
138+ for (i = 0; i < r->globs.n; i++)
139+ if (is_wildcard_pattern(r->globs.v[i]))
140+ return 0;
141+ return 1;
142+}
143+
144+/* a broad wildcard rule steps aside for any file another rule names
145+ * outright, so a per-file override needs no (skip) on the broad rule */
146+int
147+path_claimed_elsewhere(const char *path, struct Rule *self)
148+{
149+ int i, j;
150+ for (i = 0; i < nrules; i++) {
151+ struct Rule *other = &rules[i];
152+ if (other == self || !rule_is_literal(other))
153+ continue;
154+ for (j = 0; j < other->globs.n; j++) {
155+ char *exp = kv_expand(other->globs.v[j]);
156+ int hit = strcmp(exp, path) == 0;
157+ free(exp);
158+ if (hit)
159+ return 1;
160+ }
161+ }
162+ return 0;
163+}
164+
165+void
166+splitext(const char *base, char *stem, size_t stemsz, char *ext, size_t extsz)
167+{
168+ const char *dot = strrchr(base, '.');
169+ if (dot && dot != base) {
170+ size_t sl = (size_t)(dot - base);
171+ if (sl >= stemsz)
172+ sl = stemsz - 1;
173+ memcpy(stem, base, sl);
174+ stem[sl] = '\0';
175+ strlcpy(ext, dot, extsz);
176+ } else {
177+ strlcpy(stem, base, stemsz);
178+ ext[0] = '\0';
179+ }
180+}
181+
182+/* a basestem like sha512-224sum has a hyphen, but a config toggle
183+ * cannot. normalize so a gate keying off BASESTEM matches the form
184+ * genconfig writes
185+ * FIXME: I do not like our current genconfig.sh
186+ */
187+void
188+to_ident(const char *s, char *out, size_t outsz)
189+{
190+ size_t i;
191+ for (i = 0; s[i] && i + 1 < outsz; i++)
192+ out[i] = (s[i] == '-' || s[i] == '.') ? '_' : s[i];
193+ out[i] = '\0';
194+}
+139,
-0
1@@ -0,0 +1,139 @@
2+#include "ninqu.h"
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+struct Gate *
9+gate_new(enum GKind kind)
10+{
11+ struct Gate *g = ecalloc(1, sizeof *g);
12+ g->kind = kind;
13+ return g;
14+}
15+
16+void
17+gate_free(struct Gate *g)
18+{
19+ int i;
20+ if (!g)
21+ return;
22+ for (i = 0; i < g->nkids; i++)
23+ gate_free(g->kids[i]);
24+ free(g->kids);
25+ free(g->var);
26+ free(g);
27+}
28+
29+struct Gate *
30+gate_parse(struct SNode *expr)
31+{
32+ const char *h;
33+ struct Gate **kids;
34+ int i, n;
35+
36+ if (!expr)
37+ return gate_new(G_TRUE);
38+
39+ if (expr->kind == S_ATOM) {
40+ struct Gate *g = gate_new(G_VAR);
41+ g->var = estrdup(expr->atom);
42+ return g;
43+ }
44+
45+ h = s_head(expr);
46+ if (!h)
47+ eprintf("manifest: empty () in gate\n");
48+
49+ if (strcmp(h, "not") == 0) {
50+ struct Gate *g;
51+ if (expr->nkids != 2)
52+ eprintf("manifest: (not X) takes one operand\n");
53+ g = gate_new(G_NOT);
54+ g->kids = emalloc(sizeof *g->kids);
55+ g->kids[0] = gate_parse(expr->kids[1]);
56+ g->nkids = 1;
57+ return g;
58+ }
59+ if (strcmp(h, "and") == 0 || strcmp(h, "or") == 0) {
60+ struct Gate *g = gate_new(h[0] == 'a' ? G_AND : G_OR);
61+ if (expr->nkids < 2)
62+ eprintf("manifest: (%s ...) needs one operand\n", h);
63+ n = expr->nkids - 1;
64+ kids = emalloc((size_t)n * sizeof *kids);
65+ for (i = 0; i < n; i++)
66+ kids[i] = gate_parse(expr->kids[i + 1]);
67+ g->kids = kids;
68+ g->nkids = n;
69+ return g;
70+ }
71+ eprintf("manifest: unknown gate operator '%s'\n", h);
72+ return NULL;
73+}
74+
75+/* true if the gate contains any $(...) that needs per-instance
76+ * expansion before it can be evaluated */
77+int
78+gate_has_dyn(struct Gate *g)
79+{
80+ int i;
81+ if (!g)
82+ return 0;
83+ if (g->kind == G_VAR)
84+ return strstr(g->var, "$(") != NULL;
85+ for (i = 0; i < g->nkids; i++)
86+ if (gate_has_dyn(g->kids[i]))
87+ return 1;
88+ return 0;
89+}
90+
91+int
92+gate_eval(struct Gate *g)
93+{
94+ int i, v;
95+ if (!g)
96+ return 1;
97+ switch (g->kind) {
98+ case G_TRUE:
99+ return 1;
100+ case G_VAR: {
101+ char *vraw = kv_get(g->var);
102+ return vraw && *vraw && strcmp(vraw, "0") != 0;
103+ }
104+ case G_NOT:
105+ return !gate_eval(g->kids[0]);
106+ case G_AND:
107+ v = 1;
108+ for (i = 0; i < g->nkids; i++)
109+ v = v && gate_eval(g->kids[i]);
110+ return v;
111+ case G_OR:
112+ v = 0;
113+ for (i = 0; i < g->nkids; i++)
114+ v = v || gate_eval(g->kids[i]);
115+ return v;
116+ }
117+ return 0;
118+}
119+
120+/* expand every $(...) so a gate like build_$(BASESTEM) resolves
121+ * against the active overlay before evaluation */
122+struct Gate *
123+gate_materialize(struct Gate *g)
124+{
125+ int i;
126+ struct Gate *out;
127+
128+ if (!g)
129+ return NULL;
130+ out = gate_new(g->kind);
131+ if (g->var)
132+ out->var = kv_expand(g->var);
133+ if (g->nkids) {
134+ out->kids = emalloc((size_t)g->nkids * sizeof *out->kids);
135+ for (i = 0; i < g->nkids; i++)
136+ out->kids[i] = gate_materialize(g->kids[i]);
137+ out->nkids = g->nkids;
138+ }
139+ return out;
140+}
+182,
-0
1@@ -0,0 +1,182 @@
2+#include "ninqu.h"
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+struct KvStore kv;
9+struct KvStore *local_overlay;
10+struct StrList kv_locked;
11+struct Map *maps;
12+int nmaps, mapscap;
13+
14+char *
15+kv_get_raw(struct KvStore *s, const char *key)
16+{
17+ int i;
18+ for (i = 0; i < s->n; i++)
19+ if (strcmp(s->v[i].key, key) == 0)
20+ return s->v[i].val;
21+ return NULL;
22+}
23+
24+void
25+kv_set_raw(struct KvStore *s, const char *key, const char *val)
26+{
27+ int i;
28+ for (i = 0; i < s->n; i++) {
29+ if (strcmp(s->v[i].key, key) == 0) {
30+ free(s->v[i].val);
31+ s->v[i].val = estrdup(val);
32+ return;
33+ }
34+ }
35+ GROW(s->v, s->n, s->cap, 16);
36+ s->v[s->n].key = estrdup(key);
37+ s->v[s->n].val = estrdup(val);
38+ s->n++;
39+}
40+
41+/* overlay beats global beats environ, so a per-match $(BASESTEM) can
42+ * shadow a (set BASESTEM ...) without clobbering it for other matches */
43+char *
44+kv_get(const char *key)
45+{
46+ char *v;
47+ if (local_overlay) {
48+ v = kv_get_raw(local_overlay, key);
49+ if (v)
50+ return v;
51+ }
52+ v = kv_get_raw(&kv, key);
53+ if (v)
54+ return v;
55+ return getenv(key);
56+}
57+
58+const char *
59+kv_get_or(const char *key, const char *def)
60+{
61+ char *v = kv_get(key);
62+ return v ? v : def;
63+}
64+
65+void
66+kv_set(const char *key, const char *val)
67+{
68+ kv_set_raw(&kv, key, val);
69+}
70+
71+/* -D on the cli locks the name so a later (set) in the file cannot
72+ * overwrite it */
73+void
74+kv_set_manifest(const char *key, const char *val)
75+{
76+ if (sl_has(&kv_locked, key))
77+ return;
78+ kv_set(key, val);
79+}
80+
81+void
82+kv_set_cli(const char *key, const char *val)
83+{
84+ kv_set(key, val);
85+ sl_push(&kv_locked, key);
86+}
87+
88+struct Map *
89+map_find(const char *name)
90+{
91+ int i;
92+ for (i = 0; i < nmaps; i++)
93+ if (strcmp(maps[i].name, name) == 0)
94+ return &maps[i];
95+ return NULL;
96+}
97+
98+struct Map *
99+map_new(const char *name)
100+{
101+ struct Map *m;
102+ GROW(maps, nmaps, mapscap, 8);
103+ m = &maps[nmaps++];
104+ memset(m, 0, sizeof *m);
105+ strlcpy(m->name, name, sizeof m->name);
106+ return m;
107+}
108+
109+/* $(NAME) and $(NAME:KEY). KEY is expanded first, the map value is
110+ * re-expanded once so $(LDLIBS_TLS) inside a map entry resolves. the
111+ * result is not recursively expanded, matching simply-expanded vars */
112+char *
113+kv_expand(const char *s)
114+{
115+ size_t cap = strlen(s) * 2 + 64, len = 0;
116+ char *out = emalloc(cap);
117+ const char *p = s;
118+
119+ while (*p) {
120+ if (p[0] == '$' && p[1] == '(') {
121+ const char *inner = p + 2;
122+ const char *close = NULL;
123+ char *val = NULL;
124+ char *expanded_val = NULL;
125+
126+ close = find_dollar_close(inner);
127+
128+ if (close) {
129+ char name[4096];
130+ size_t nl = (size_t)(close - inner);
131+ char *colon;
132+
133+ if (nl >= sizeof name)
134+ nl = sizeof name - 1;
135+ memcpy(name, inner, nl);
136+ name[nl] = '\0';
137+
138+ colon = strchr(name, ':');
139+ if (colon) {
140+ char mapname[128];
141+ char *keypart;
142+ struct Map *m;
143+ size_t mnl = (size_t)(colon - name);
144+
145+ if (mnl >= sizeof mapname)
146+ mnl = sizeof mapname - 1;
147+ memcpy(mapname, name, mnl);
148+ mapname[mnl] = '\0';
149+ m = map_find(mapname);
150+ keypart = kv_expand(colon + 1);
151+ if (m) {
152+ char *raw = kv_get_raw(&m->entries, keypart);
153+ if (raw)
154+ expanded_val = kv_expand(raw);
155+ }
156+ free(keypart);
157+ val = expanded_val;
158+ } else {
159+ val = kv_get(name);
160+ }
161+
162+ if (!val)
163+ val = "";
164+ while (len + strlen(val) + 1 >= cap) {
165+ cap *= 2;
166+ out = erealloc(out, cap);
167+ }
168+ memcpy(out + len, val, strlen(val));
169+ len += strlen(val);
170+ free(expanded_val);
171+ p = close + 1;
172+ continue;
173+ }
174+ }
175+ if (len + 2 >= cap) {
176+ cap *= 2;
177+ out = erealloc(out, cap);
178+ }
179+ out[len++] = *p++;
180+ }
181+ out[len] = '\0';
182+ return out;
183+}
+112,
-0
1@@ -0,0 +1,112 @@
2+#include "ninqu.h"
3+
4+#include <limits.h>
5+#include <stdio.h>
6+#include <stdlib.h>
7+#include <string.h>
8+#include <unistd.h>
9+
10+static void
11+usage(void)
12+{
13+ eprintf(
14+ "usage: %s [-jN] [-f manifest] [-C dir] [-Dvar=val]... [-S] [-F] [target|meta.target ...]\n"
15+ " %s query (features|groups|metas|rules|graph) [arg]\n",
16+ argv0,
17+ argv0
18+ );
19+}
20+
21+int
22+main(int argc, char *argv[])
23+{
24+ const char *manifest = "ninqu.rules";
25+ int features_mode = 0;
26+ int query_mode = 0;
27+ const char *query_sub = NULL;
28+ const char *query_arg = NULL;
29+ int i;
30+ struct StrList wanted = {0};
31+
32+ /* detect `query` before ARGBEGIN so it is not parsed as a target */
33+ if (argc >= 2 && strcmp(argv[1], "query") == 0) {
34+ query_mode = 1;
35+ if (argc < 3)
36+ usage();
37+ query_sub = argv[2];
38+ query_arg = (argc >= 4) ? argv[3] : NULL;
39+ for (i = 3; i < argc; i++) {
40+ if (argv[i][0] == '-' && argv[i][1] == 'f')
41+ manifest = argv[i][2] ? argv[i] + 2 : argv[++i];
42+ else if (argv[i][0] == '-' && argv[i][1] == 'C') {
43+ if (chdir(argv[i][2] ? argv[i] + 2 : argv[++i]) != 0)
44+ eprintf("chdir:");
45+ } else if (argv[i][0] == '-' && argv[i][1] == 'D') {
46+ char *spec = argv[i][2] ? argv[i] + 2 : argv[++i];
47+ char *eq = strchr(spec, '=');
48+ if (!eq)
49+ usage();
50+ *eq = '\0';
51+ kv_set_cli(spec, eq + 1);
52+ }
53+ }
54+ } else {
55+ ARGBEGIN
56+ {
57+ case 'j':
58+ jobs_n = (int)estrtonum(EARGF(usage()), 1, INT_MAX);
59+ break;
60+ case 'f':
61+ manifest = EARGF(usage());
62+ break;
63+ case 'C':
64+ if (chdir(EARGF(usage())) != 0)
65+ eprintf("chdir:");
66+ break;
67+ case 'D': {
68+ char *spec = EARGF(usage());
69+ char *eq = strchr(spec, '=');
70+ if (!eq)
71+ usage();
72+ *eq = '\0';
73+ kv_set_cli(spec, eq + 1);
74+ break;
75+ }
76+ case 'S':
77+ summary_mode = 1;
78+ break;
79+ case 'F':
80+ features_mode = 1;
81+ break;
82+ default:
83+ usage();
84+ }
85+ ARGEND
86+
87+ for (i = 0; i < argc; i++)
88+ sl_push(&wanted, argv[i]);
89+ }
90+
91+ load_file(manifest);
92+ resolve_all_bareword_deps();
93+
94+ if (query_mode) {
95+ do_query(query_sub, query_arg);
96+ return 0;
97+ }
98+ if (features_mode) {
99+ do_features();
100+ return 0;
101+ }
102+
103+ if (wanted.n == 0)
104+ build_default_group();
105+ else
106+ for (i = 0; i < wanted.n; i++)
107+ resolve_wanted(wanted.v[i]);
108+
109+ resolve_deps();
110+ schedule_and_run();
111+
112+ return failed_any ? 1 : 0;
113+}
+256,
-0
1@@ -0,0 +1,256 @@
2+#if !defined(_DEFAULT_SOURCE)
3+#define _DEFAULT_SOURCE
4+#endif
5+#if !defined(_POSIX_C_SOURCE)
6+#define _POSIX_C_SOURCE 200809L
7+#endif
8+
9+#include <sys/types.h>
10+
11+#include "arg.h"
12+#include "util.h"
13+
14+/* grow a struct-array when full. n/cap doubles each time */
15+#define GROW(arr, n, cap, init) \
16+ do { \
17+ if ((n) >= (cap)) { \
18+ (cap) = (cap) ? (cap) * 2 : (init); \
19+ (arr) = erealloc((arr), (size_t)(cap) * sizeof *(arr)); \
20+ } \
21+ } while (0)
22+
23+struct StrList {
24+ char **v;
25+ int n;
26+ int cap;
27+};
28+
29+void sl_push(struct StrList *sl, const char *s);
30+int sl_has(struct StrList *sl, const char *s);
31+void sl_split(struct StrList *sl, const char *s);
32+char *sl_join(struct StrList *sl);
33+char **sl_argv(struct StrList *sl);
34+const char *base_of(const char *path);
35+void child_execvp(char **av) __attribute__((noreturn));
36+
37+struct Kv {
38+ char *key;
39+ char *val;
40+};
41+
42+struct KvStore {
43+ struct Kv *v;
44+ int n, cap;
45+};
46+
47+struct Map {
48+ char name[128];
49+ struct KvStore entries;
50+};
51+
52+char *kv_get(const char *key);
53+const char *kv_get_or(const char *key, const char *def);
54+void kv_set(const char *key, const char *val);
55+void kv_set_manifest(const char *key, const char *val);
56+void kv_set_cli(const char *key, const char *val);
57+char *kv_expand(const char *s);
58+char *kv_get_raw(struct KvStore *s, const char *key);
59+void kv_set_raw(struct KvStore *s, const char *key, const char *val);
60+struct Map *map_find(const char *name);
61+struct Map *map_new(const char *name);
62+
63+extern struct KvStore kv;
64+extern struct KvStore *local_overlay;
65+extern struct StrList kv_locked;
66+extern struct Map *maps;
67+extern int nmaps, mapscap;
68+
69+enum SKind { S_ATOM, S_LIST };
70+
71+struct SNode {
72+ enum SKind kind;
73+ char *atom;
74+ struct SNode **kids;
75+ int nkids;
76+ int line;
77+};
78+
79+struct SLex {
80+ const char *src;
81+ size_t pos;
82+ int line;
83+};
84+
85+struct SNode *snode_atom(const char *s, int line);
86+struct SNode *snode_list(int line);
87+void snode_push(struct SNode *list, struct SNode *kid);
88+void snode_free(struct SNode *n);
89+struct SNode *snode_clone(struct SNode *n);
90+struct SNode *snode_parse(struct SLex *lx);
91+int s_is_list(struct SNode *n);
92+const char *s_head(struct SNode *n);
93+char *subst_tmpl(const char *s, struct StrList *params, char **vals);
94+void snode_subst(struct SNode *n, struct StrList *params, char **vals);
95+void lex_swallow_dollar(struct SLex *lx, char *buf, size_t *blen, size_t bsz);
96+void lex_string(struct SLex *lx, char *buf, size_t bsz);
97+const char *find_dollar_close(const char *inner);
98+
99+/* gate: (not X) | (and X...) | (or X...) | bare var */
100+enum GKind { G_TRUE, G_VAR, G_NOT, G_AND, G_OR };
101+
102+struct Gate {
103+ enum GKind kind;
104+ char *var;
105+ struct Gate **kids;
106+ int nkids;
107+};
108+
109+struct Gate *gate_new(enum GKind kind);
110+void gate_free(struct Gate *g);
111+struct Gate *gate_parse(struct SNode *expr);
112+int gate_has_dyn(struct Gate *g);
113+int gate_eval(struct Gate *g);
114+struct Gate *gate_materialize(struct Gate *g);
115+
116+struct Cmd {
117+ struct StrList argv;
118+ int is_pipe;
119+ struct StrList **stages;
120+ int nstages;
121+};
122+
123+struct Rule {
124+ char name[128];
125+ struct StrList globs;
126+ struct StrList skip;
127+ char require_marker[1024];
128+ char require_file[1024];
129+ struct StrList in;
130+ struct StrList extra_deps;
131+ char out[1024];
132+ struct Cmd cmd;
133+ struct Gate *gate;
134+ char workdir[1024];
135+ int phony;
136+ int redirect;
137+ char produces[128];
138+ char description[256];
139+ char group_name[128];
140+ char member_of[128];
141+ char member_alias[128];
142+ struct StrList features;
143+
144+ int *inst_idx;
145+ int n_inst, cap_inst;
146+ int expanded;
147+};
148+
149+struct Group {
150+ char name[128];
151+ struct StrList refs;
152+};
153+
154+struct Meta {
155+ char name[128];
156+ struct StrList keys;
157+ struct StrList vals;
158+};
159+
160+struct Template {
161+ char name[128];
162+ struct StrList params;
163+ struct SNode *body;
164+};
165+
166+struct Rule *rule_find(const char *name);
167+struct Rule *rule_find_output(const char *path);
168+struct Rule *rule_find_produces(const char *name);
169+struct Rule *rule_new(const char *name);
170+struct Group *group_find(const char *name);
171+struct Group *group_new(const char *name);
172+struct Meta *meta_find(const char *name);
173+struct Meta *meta_new(const char *name);
174+struct Template *template_find(const char *name);
175+struct Template *template_new(const char *name);
176+
177+extern struct Rule *rules;
178+extern int nrules, rulescap;
179+extern struct Group *groups;
180+extern int ngroups, groupscap;
181+extern struct Meta *metas;
182+extern int nmetas, metascap;
183+extern struct Template *templates;
184+extern int ntemplates, templatescap;
185+
186+struct Inst {
187+ int rule_idx;
188+ struct StrList in;
189+ struct StrList stale_extra;
190+ char out[1024];
191+ struct Cmd cmd;
192+ char workdir[1024];
193+ int phony;
194+ int redirect;
195+
196+ struct StrList dep_rule_names;
197+ int *dep_inst;
198+ int n_dep, cap_dep;
199+ int will_build;
200+};
201+
202+int inst_new(int rule_idx);
203+int inst_stale(struct Inst *inst);
204+void dep_push(struct Inst *inst, int idx);
205+void resolve_deps(void);
206+
207+extern struct Inst *insts;
208+extern int ninsts, instscap;
209+
210+long mtime_of(const char *path);
211+int file_exists(const char *path);
212+int file_contains(const char *path, const char *needle);
213+void glob_expand(const char *pattern, struct StrList *out);
214+void glob_all(struct Rule *r, struct StrList *out);
215+int is_wildcard_pattern(const char *pattern);
216+int rule_is_literal(struct Rule *r);
217+int path_claimed_elsewhere(const char *path, struct Rule *self);
218+void splitext(const char *base, char *stem, size_t stemsz, char *ext, size_t extsz);
219+void to_ident(const char *s, char *out, size_t outsz);
220+
221+pid_t spawn_inst(struct Inst *inst, struct StrList *argv);
222+pid_t spawn_pipe(struct Inst *inst);
223+void mk_out_dir(const char *out);
224+void announce_inst(struct Inst *inst);
225+void run_inst_sync(int idx);
226+void run_batch(int *idxs, int n);
227+void run_inst_with_deps(int idx);
228+void materialize_if_missing(const char *path);
229+char *capture_argv(struct StrList *argv);
230+
231+extern int jobs_n;
232+extern int summary_mode;
233+extern int failed_any;
234+
235+void load_file(const char *path);
236+void load_manifest(struct SNode *root);
237+void dispatch_top_form(struct SNode *form);
238+void parse_exec_clause(struct SNode *clause, struct Cmd *cmd);
239+void parse_shell_clause(struct SNode *clause, struct Cmd *cmd);
240+void parse_pipe_clause(struct SNode *clause, struct Cmd *cmd);
241+void meta_apply(struct Meta *m);
242+const char *first_atom(struct SNode *node, const char *msg);
243+const char *sole_atom(struct SNode *node, const char *msg);
244+
245+extern struct StrList files_read;
246+
247+void expand_rule(const char *name);
248+
249+void schedule_and_run(void);
250+
251+void do_features(void);
252+void do_query(const char *sub, const char *arg);
253+
254+void include_ref(const char *name);
255+void build_default_group(void);
256+void resolve_wanted(const char *w);
257+void resolve_all_bareword_deps(void);
+650,
-0
1@@ -0,0 +1,650 @@
2+#include "ninqu.h"
3+
4+#include <ctype.h>
5+#include <limits.h>
6+#include <stdio.h>
7+#include <stdlib.h>
8+#include <string.h>
9+
10+struct StrList files_read;
11+
12+/* second child must be an atom, more children may follow */
13+const char *
14+first_atom(struct SNode *node, const char *msg)
15+{
16+ if (node->nkids < 2 || node->kids[1]->kind != S_ATOM)
17+ eprintf("manifest: %s\n", msg);
18+ return node->kids[1]->atom;
19+}
20+
21+/* second child must be the only other child, and an atom */
22+const char *
23+sole_atom(struct SNode *node, const char *msg)
24+{
25+ if (node->nkids != 2 || node->kids[1]->kind != S_ATOM)
26+ eprintf("manifest: %s\n", msg);
27+ return node->kids[1]->atom;
28+}
29+
30+/* a dep operand is a bare atom, a (rule NAME) stored as @NAME, or
31+ * an (after X...) whose operands are order-only */
32+static void
33+parse_dep_item(struct SNode *item, struct StrList *hard, struct StrList *ord, int allow_after)
34+{
35+ const char *h;
36+
37+ if (item->kind == S_ATOM) {
38+ sl_push(hard, item->atom);
39+ return;
40+ }
41+ h = s_head(item);
42+ if (!h)
43+ eprintf("manifest: empty () in dep\n");
44+ if (strcmp(h, "rule") == 0) {
45+ char buf[256];
46+ snprintf(buf, sizeof buf, "@%s", sole_atom(item, "(rule NAME) takes one name"));
47+ sl_push(hard, buf);
48+ return;
49+ }
50+ if (strcmp(h, "after") == 0) {
51+ int i;
52+ if (!allow_after)
53+ eprintf("manifest: (after ...) not allowed here\n");
54+ for (i = 1; i < item->nkids; i++)
55+ parse_dep_item(item->kids[i], ord, ord, 0);
56+ return;
57+ }
58+ eprintf("manifest: unknown dep operand '%s'\n", h);
59+}
60+
61+static void
62+parse_dep_clause(struct SNode *clause, struct StrList *hard, struct StrList *ord)
63+{
64+ int i;
65+ for (i = 1; i < clause->nkids; i++)
66+ parse_dep_item(clause->kids[i], hard, ord, 1);
67+}
68+
69+void
70+parse_exec_clause(struct SNode *clause, struct Cmd *cmd)
71+{
72+ int i;
73+ memset(cmd, 0, sizeof *cmd);
74+ for (i = 1; i < clause->nkids; i++) {
75+ if (clause->kids[i]->kind != S_ATOM)
76+ eprintf("manifest: (exec ...) operands must be atoms\n");
77+ sl_push(&cmd->argv, clause->kids[i]->atom);
78+ }
79+ if (cmd->argv.n == 0)
80+ eprintf("manifest: (exec ...) needs one operand\n");
81+}
82+
83+/* desugar to (exec sh -c STR). sh is the posix shell, overridable
84+ * by writing (exec ...) directly */
85+void
86+parse_shell_clause(struct SNode *clause, struct Cmd *cmd)
87+{
88+ memset(cmd, 0, sizeof *cmd);
89+ sl_push(&cmd->argv, "sh");
90+ sl_push(&cmd->argv, "-c");
91+ sl_push(&cmd->argv, sole_atom(clause, "(shell \"...\") takes one string"));
92+}
93+
94+static void
95+parse_pipe_stage(struct SNode *stage, struct StrList *argv)
96+{
97+ const char *h = s_head(stage);
98+ int j;
99+
100+ if (!h)
101+ eprintf("manifest: (pipe ...) stage must be a form\n");
102+ if (strcmp(h, "exec") == 0) {
103+ for (j = 1; j < stage->nkids; j++) {
104+ if (stage->kids[j]->kind != S_ATOM)
105+ eprintf("manifest: (exec ...) operands must be atoms\n");
106+ sl_push(argv, stage->kids[j]->atom);
107+ }
108+ } else if (strcmp(h, "shell") == 0) {
109+ sl_push(argv, "sh");
110+ sl_push(argv, "-c");
111+ sl_push(argv, sole_atom(stage, "(shell \"...\") takes one string"));
112+ } else {
113+ eprintf("manifest: (pipe) stage must be exec or shell, got '%s'\n", h);
114+ }
115+ if (argv->n == 0)
116+ eprintf("manifest: (pipe) stage cannot be empty\n");
117+}
118+
119+void
120+parse_pipe_clause(struct SNode *clause, struct Cmd *cmd)
121+{
122+ int i, j;
123+ memset(cmd, 0, sizeof *cmd);
124+ cmd->is_pipe = 1;
125+ for (i = 1; i < clause->nkids; i++) {
126+ struct StrList argv = {0};
127+ struct StrList *slot;
128+
129+ parse_pipe_stage(clause->kids[i], &argv);
130+
131+ slot = emalloc(sizeof *slot);
132+ memset(slot, 0, sizeof *slot);
133+ for (j = 0; j < argv.n; j++)
134+ sl_push(slot, argv.v[j]);
135+ if (cmd->nstages == 0)
136+ cmd->stages = emalloc(8 * sizeof *cmd->stages);
137+ else if (cmd->nstages >= 8)
138+ cmd->stages = erealloc(cmd->stages, (size_t)cmd->nstages * 2 * sizeof *cmd->stages);
139+ cmd->stages[cmd->nstages++] = slot;
140+ }
141+ if (cmd->nstages < 2)
142+ eprintf("manifest: (pipe ...) needs two stages\n");
143+ for (i = 0; i < cmd->nstages; i++) {
144+ char *joined = sl_join(cmd->stages[i]);
145+ if (i > 0)
146+ sl_push(&cmd->argv, "|");
147+ sl_push(&cmd->argv, joined);
148+ free(joined);
149+ }
150+}
151+
152+static void apply_rule_clause(struct Rule *r, const char *name, struct SNode *clause);
153+
154+static void
155+process_rule_body(struct Rule *r, const char *name, struct SNode *form, int start)
156+{
157+ int i;
158+ for (i = start; i < form->nkids; i++)
159+ apply_rule_clause(r, name, form->kids[i]);
160+}
161+
162+/* (if EXPR clause...) splices its clauses in when EXPR is true.
163+ * reuses the gate grammar so there is one condition language.
164+ * a $(...) in the condition is rejected: splicing per glob match
165+ * would mean deferring cmd/dep construction, which no rule needs.
166+ * use (gate ...) on a separate rule for a per-instance condition */
167+static void
168+apply_if_clause(struct Rule *r, const char *name, struct SNode *clause)
169+{
170+ struct Gate *cond;
171+
172+ if (clause->nkids < 2)
173+ eprintf("manifest: rule %s: (if EXPR clause...) needs a condition\n", name);
174+ cond = gate_parse(clause->kids[1]);
175+ if (gate_has_dyn(cond))
176+ eprintf(
177+ "manifest: rule %s: (if ...) condition cannot reference $(...), "
178+ "only (gate ...) on the whole rule supports a per-instance condition\n",
179+ name
180+ );
181+ if (gate_eval(cond))
182+ process_rule_body(r, name, clause, 2);
183+ gate_free(cond);
184+}
185+
186+static void
187+apply_rule_clause(struct Rule *r, const char *name, struct SNode *clause)
188+{
189+ const char *h = s_head(clause);
190+ if (!h)
191+ eprintf("manifest: rule %s: empty () clause\n", name);
192+
193+ if (strcmp(h, "if") == 0) {
194+ apply_if_clause(r, name, clause);
195+ } else if (strcmp(h, "glob") == 0) {
196+ int j;
197+ for (j = 1; j < clause->nkids; j++) {
198+ if (clause->kids[j]->kind != S_ATOM)
199+ eprintf("manifest: (glob ...) operands must be atoms\n");
200+ sl_push(&r->globs, clause->kids[j]->atom);
201+ }
202+ } else if (strcmp(h, "skip") == 0) {
203+ int j;
204+ for (j = 1; j < clause->nkids; j++) {
205+ if (clause->kids[j]->kind != S_ATOM)
206+ eprintf("manifest: (skip ...) operands must be atoms\n");
207+ sl_push(&r->skip, clause->kids[j]->atom);
208+ }
209+ } else if (strcmp(h, "mark") == 0) {
210+ strlcpy(
211+ r->require_marker,
212+ sole_atom(clause, "(mark STR) takes one string"),
213+ sizeof r->require_marker
214+ );
215+ } else if (strcmp(h, "req") == 0) {
216+ strlcpy(
217+ r->require_file, sole_atom(clause, "(req PATH) takes one path"), sizeof r->require_file
218+ );
219+ } else if (strcmp(h, "gate") == 0) {
220+ if (clause->nkids != 2)
221+ eprintf("manifest: (gate EXPR) takes one expression\n");
222+ r->gate = gate_parse(clause->kids[1]);
223+ } else if (strcmp(h, "dep") == 0) {
224+ parse_dep_clause(clause, &r->in, &r->extra_deps);
225+ } else if (strcmp(h, "after") == 0) {
226+ parse_dep_clause(clause, &r->extra_deps, &r->extra_deps);
227+ } else if (strcmp(h, "out") == 0) {
228+ strlcpy(r->out, sole_atom(clause, "(out TEMPLATE) takes one template"), sizeof r->out);
229+ } else if (strcmp(h, "exec") == 0) {
230+ parse_exec_clause(clause, &r->cmd);
231+ } else if (strcmp(h, "shell") == 0) {
232+ parse_shell_clause(clause, &r->cmd);
233+ } else if (strcmp(h, "pipe") == 0) {
234+ parse_pipe_clause(clause, &r->cmd);
235+ } else if (strcmp(h, "dir") == 0) {
236+ strlcpy(r->workdir, sole_atom(clause, "(dir PATH) takes one path"), sizeof r->workdir);
237+ } else if (strcmp(h, "phony") == 0) {
238+ r->phony = 1;
239+ } else if (strcmp(h, "redirect") == 0) {
240+ r->redirect = 1;
241+ } else if (strcmp(h, "produces") == 0) {
242+ strlcpy(r->produces, sole_atom(clause, "(produces NAME) takes one name"), sizeof r->produces);
243+ } else if (strcmp(h, "description") == 0) {
244+ strlcpy(
245+ r->description,
246+ sole_atom(clause, "(description STR) takes one string"),
247+ sizeof r->description
248+ );
249+ } else if (strcmp(h, "group") == 0) {
250+ /* display-only: shows up in (query rules) output, no effect on
251+ * resolution. (member ...) below is the enforcing one. the two
252+ * are separate because several rules used (group ...) this way
253+ * before namespacing existed */
254+ strlcpy(r->group_name, sole_atom(clause, "(group NAME) takes one name"), sizeof r->group_name);
255+ } else if (strcmp(h, "member") == 0) {
256+ /* (member GROUP [ALIAS]) hides the bare name from the command
257+ * line. ALIAS defaults to the rule own name */
258+ if (clause->nkids < 2 || clause->nkids > 3 || clause->kids[1]->kind != S_ATOM)
259+ eprintf("manifest: rule %s: (member GROUP [ALIAS]) needs a group name\n", name);
260+ strlcpy(r->member_of, clause->kids[1]->atom, sizeof r->member_of);
261+ if (clause->nkids == 3) {
262+ if (clause->kids[2]->kind != S_ATOM)
263+ eprintf("manifest: rule %s: (member GROUP ALIAS) alias must be an atom\n", name);
264+ strlcpy(r->member_alias, clause->kids[2]->atom, sizeof r->member_alias);
265+ }
266+ } else if (strcmp(h, "feature") == 0) {
267+ sl_push(&r->features, sole_atom(clause, "(feature NAME) takes one name"));
268+ } else {
269+ eprintf("manifest: rule %s: unknown clause '%s'\n", name, h);
270+ }
271+}
272+
273+static void
274+parse_rule(struct SNode *form)
275+{
276+ struct Rule *r;
277+ const char *name;
278+
279+ name = first_atom(form, "(rule NAME ...) needs a name");
280+ r = rule_new(name);
281+ process_rule_body(r, name, form, 2);
282+}
283+
284+static void
285+parse_group(struct SNode *form)
286+{
287+ struct Group *g;
288+ int i;
289+ const char *name;
290+
291+ name = first_atom(form, "(group NAME ...) needs a name");
292+ g = group_new(name);
293+
294+ for (i = 2; i < form->nkids; i++) {
295+ struct SNode *ref = form->kids[i];
296+ const char *h;
297+
298+ if (ref->kind == S_ATOM) {
299+ sl_push(&g->refs, ref->atom);
300+ continue;
301+ }
302+ h = s_head(ref);
303+ if (!h)
304+ eprintf("manifest: group %s: empty () ref\n", name);
305+ if ((strcmp(h, "rule") == 0 || strcmp(h, "group") == 0) && ref->nkids == 2
306+ && ref->kids[1]->kind == S_ATOM) {
307+ sl_push(&g->refs, ref->kids[1]->atom);
308+ continue;
309+ }
310+ eprintf("manifest: group %s: unknown ref '%s'\n", name, h);
311+ }
312+}
313+
314+/* stores K/V verbatim, unexpanded. expansion happens in meta_apply
315+ * at target-resolution time, so a value picks up whatever its vars
316+ * hold then, not what they held at load time */
317+static void
318+parse_meta(struct SNode *form)
319+{
320+ struct Meta *m;
321+ int i;
322+ const char *name;
323+
324+ name = first_atom(form, "(meta NAME ...) needs a name");
325+ m = meta_new(name);
326+
327+ for (i = 2; i < form->nkids; i++) {
328+ struct SNode *clause = form->kids[i];
329+ const char *h = s_head(clause);
330+ if (!h || strcmp(h, "set") != 0)
331+ eprintf("manifest: meta %s: only (set NAME VAL) clauses are allowed\n", name);
332+ if (clause->nkids != 3 || clause->kids[1]->kind != S_ATOM || clause->kids[2]->kind != S_ATOM)
333+ eprintf("manifest: meta %s: (set NAME VAL) needs a name and an atom value\n", name);
334+ sl_push(&m->keys, clause->kids[1]->atom);
335+ sl_push(&m->vals, clause->kids[2]->atom);
336+ }
337+}
338+
339+void
340+meta_apply(struct Meta *m)
341+{
342+ int i;
343+ for (i = 0; i < m->keys.n; i++) {
344+ char *exp = kv_expand(m->vals.v[i]);
345+ kv_set_manifest(m->keys.v[i], exp);
346+ free(exp);
347+ }
348+}
349+
350+static void
351+parse_set(struct SNode *form)
352+{
353+ char buf[8192];
354+
355+ first_atom(form, "(set NAME VAL) needs a name");
356+ if (form->nkids != 3)
357+ eprintf("manifest: (set NAME VAL) takes one value\n");
358+
359+ if (form->kids[2]->kind == S_LIST) {
360+ const char *h = s_head(form->kids[2]);
361+ if (h && strcmp(h, "capture") == 0) {
362+ struct SNode *cap = form->kids[2];
363+ struct Cmd cmd;
364+ char *out;
365+
366+ if (cap->nkids != 2)
367+ eprintf("manifest: (capture (exec ...)) takes one form\n");
368+ h = s_head(cap->kids[1]);
369+ if (!h)
370+ eprintf("manifest: capture operand must be a form\n");
371+ if (strcmp(h, "exec") == 0)
372+ parse_exec_clause(cap->kids[1], &cmd);
373+ else if (strcmp(h, "shell") == 0)
374+ parse_shell_clause(cap->kids[1], &cmd);
375+ else
376+ eprintf("manifest: capture operand must be exec or shell\n");
377+ out = capture_argv(&cmd.argv);
378+ strlcpy(buf, out, sizeof buf);
379+ free(out);
380+ kv_set_manifest(form->kids[1]->atom, buf);
381+ return;
382+ }
383+ eprintf("manifest: (set NAME VAL): value must be atom or (capture ...)\n");
384+ }
385+ kv_set_manifest(form->kids[1]->atom, form->kids[2]->atom);
386+}
387+
388+/* (list) and (append) keep a space-joined string in the kv store.
389+ * a $(NAME) alone in a command splits back into argv entries */
390+static void
391+parse_list_or_append(struct SNode *form, int append)
392+{
393+ const char *name;
394+ char buf[8192];
395+ char msg[64];
396+ size_t len = 0;
397+ int i;
398+ char *prev;
399+
400+ snprintf(msg, sizeof msg, "(%s NAME tok...) needs a name", form->kids[0]->atom);
401+ name = first_atom(form, msg);
402+ buf[0] = '\0';
403+
404+ if (append) {
405+ prev = kv_get(name);
406+ if (prev)
407+ len = strlcpy(buf, prev, sizeof buf);
408+ }
409+ for (i = 2; i < form->nkids; i++) {
410+ struct SNode *tok = form->kids[i];
411+ char *exp;
412+ if (tok->kind != S_ATOM)
413+ eprintf("manifest: (%s NAME tok...): tokens must be atoms\n", form->kids[0]->atom);
414+ if (len > 0 && len + 1 < sizeof buf)
415+ buf[len++] = ' ';
416+ exp = kv_expand(tok->atom);
417+ len += strlcpy(buf + len, exp, sizeof buf - len);
418+ free(exp);
419+ }
420+ kv_set_manifest(name, buf);
421+}
422+
423+static void
424+parse_map(struct SNode *form)
425+{
426+ struct Map *m;
427+ int i;
428+ const char *name;
429+
430+ name = first_atom(form, "(map NAME (k v)...) needs a name");
431+ m = map_find(name);
432+ if (!m)
433+ m = map_new(name);
434+
435+ for (i = 2; i < form->nkids; i++) {
436+ struct SNode *pair = form->kids[i];
437+ if (!s_is_list(pair) || pair->nkids != 2 || pair->kids[0]->kind != S_ATOM
438+ || pair->kids[1]->kind != S_ATOM)
439+ eprintf("manifest: (map %s ...) entries must be (key value)\n", name);
440+ kv_set_raw(&m->entries, pair->kids[0]->atom, pair->kids[1]->atom);
441+ }
442+}
443+
444+static void
445+parse_import(struct SNode *form)
446+{
447+ char rp[PATH_MAX];
448+ const char *path;
449+
450+ path = sole_atom(form, "(import PATH) takes one path");
451+
452+ if (realpath(path, rp) && sl_has(&files_read, rp))
453+ return;
454+
455+ materialize_if_missing(path);
456+ if (!file_exists(path))
457+ eprintf("manifest: (import %s): missing and no rule produces it\n", path);
458+ load_file(path);
459+}
460+
461+static void
462+parse_set_file(struct SNode *form)
463+{
464+ const char *path;
465+ FILE *fp;
466+ char line[8192];
467+ char rp[PATH_MAX];
468+
469+ path = sole_atom(form, "(set-file PATH) takes one path");
470+
471+ materialize_if_missing(path);
472+ if (!file_exists(path))
473+ eprintf("manifest: (set-file %s): missing and no rule produces it\n", path);
474+
475+ fp = fopen(path, "r");
476+ if (!fp)
477+ eprintf("manifest: (set-file %s):", path);
478+ if (realpath(path, rp))
479+ sl_push(&files_read, rp);
480+
481+ while (fgets(line, sizeof line, fp)) {
482+ char *s = line, *eq, *end;
483+ while (isspace((unsigned char)*s))
484+ s++;
485+ if (*s == '\0' || *s == '#')
486+ continue;
487+ eq = strchr(s, '=');
488+ if (!eq)
489+ continue;
490+ *eq = '\0';
491+ end = eq - 1;
492+ while (end > s && isspace((unsigned char)*end))
493+ *end-- = '\0';
494+ eq++;
495+ while (isspace((unsigned char)*eq))
496+ eq++;
497+ end = eq + strlen(eq);
498+ while (end > eq && (isspace((unsigned char)end[-1]) || end[-1] == '\n'))
499+ *--end = '\0';
500+ kv_set_manifest(s, eq);
501+ }
502+ fclose(fp);
503+}
504+
505+/* (template NAME (PARAM...) FORM...): the body is cloned so it stays
506+ * valid regardless of when or how many times (use ...) instantiates
507+ * it */
508+static void
509+parse_template(struct SNode *form)
510+{
511+ struct Template *t;
512+ struct SNode *params;
513+ const char *name;
514+ int i;
515+
516+ name = first_atom(form, "(template NAME (PARAM...) FORM...) needs a name");
517+ if (form->nkids < 3 || form->kids[2]->kind != S_LIST)
518+ eprintf("manifest: template %s: needs a (PARAM...) list\n", name);
519+ params = form->kids[2];
520+
521+ t = template_new(name);
522+ for (i = 0; i < params->nkids; i++) {
523+ if (params->kids[i]->kind != S_ATOM)
524+ eprintf("manifest: template %s: param names must be atoms\n", name);
525+ sl_push(&t->params, params->kids[i]->atom);
526+ }
527+ t->body = snode_list(form->line);
528+ for (i = 3; i < form->nkids; i++)
529+ snode_push(t->body, snode_clone(form->kids[i]));
530+}
531+
532+/* clone the template body, substitute each $(PARAM), then dispatch
533+ * every substituted form as a normal top-level form */
534+static void
535+parse_use(struct SNode *form)
536+{
537+ struct Template *t;
538+ struct SNode *clone;
539+ const char *name;
540+ char **vals;
541+ int i, nargs;
542+
543+ name = first_atom(form, "(use NAME arg...) needs a name");
544+ t = template_find(name);
545+ if (!t)
546+ eprintf("manifest: (use %s ...): no such template\n", name);
547+
548+ nargs = form->nkids - 2;
549+ if (nargs != t->params.n)
550+ eprintf("manifest: (use %s ...): takes %d argument(s), got %d\n", name, t->params.n, nargs);
551+
552+ vals = emalloc((size_t)(nargs ? nargs : 1) * sizeof *vals);
553+ for (i = 0; i < nargs; i++) {
554+ if (form->kids[i + 2]->kind != S_ATOM)
555+ eprintf("manifest: (use %s ...): arguments must be atoms\n", name);
556+ vals[i] = form->kids[i + 2]->atom;
557+ }
558+
559+ clone = snode_clone(t->body);
560+ snode_subst(clone, &t->params, vals);
561+ for (i = 0; i < clone->nkids; i++)
562+ dispatch_top_form(clone->kids[i]);
563+ snode_free(clone);
564+ free(vals);
565+}
566+
567+void
568+dispatch_top_form(struct SNode *form)
569+{
570+ const char *h = s_head(form);
571+ if (!h)
572+ eprintf("manifest: top-level form has no head\n");
573+ if (strcmp(h, "set") == 0)
574+ parse_set(form);
575+ else if (strcmp(h, "list") == 0)
576+ parse_list_or_append(form, 0);
577+ else if (strcmp(h, "append") == 0)
578+ parse_list_or_append(form, 1);
579+ else if (strcmp(h, "map") == 0)
580+ parse_map(form);
581+ else if (strcmp(h, "rule") == 0)
582+ parse_rule(form);
583+ else if (strcmp(h, "group") == 0)
584+ parse_group(form);
585+ else if (strcmp(h, "meta") == 0)
586+ parse_meta(form);
587+ else if (strcmp(h, "import") == 0)
588+ parse_import(form);
589+ else if (strcmp(h, "set-file") == 0)
590+ parse_set_file(form);
591+ else if (strcmp(h, "template") == 0)
592+ parse_template(form);
593+ else if (strcmp(h, "use") == 0)
594+ parse_use(form);
595+ else
596+ eprintf("manifest: unknown top-level form '%s'\n", h);
597+}
598+
599+void
600+load_manifest(struct SNode *root)
601+{
602+ int i;
603+ for (i = 0; i < root->nkids; i++)
604+ dispatch_top_form(root->kids[i]);
605+}
606+
607+void
608+load_file(const char *path)
609+{
610+ FILE *fp;
611+ char *buf;
612+ size_t cap, len;
613+ char rp[PATH_MAX];
614+ struct SLex lx;
615+ struct SNode *root;
616+
617+ if (realpath(path, rp)) {
618+ if (sl_has(&files_read, rp))
619+ return;
620+ sl_push(&files_read, rp);
621+ }
622+
623+ fp = fopen(path, "r");
624+ if (!fp)
625+ eprintf("cannot open manifest %s:", path);
626+
627+ cap = 65536;
628+ len = 0;
629+ buf = emalloc(cap);
630+ for (;;) {
631+ size_t n;
632+ if (len + 65536 > cap) {
633+ cap *= 2;
634+ buf = erealloc(buf, cap);
635+ }
636+ n = fread(buf + len, 1, cap - len - 1, fp);
637+ len += n;
638+ if (n == 0)
639+ break;
640+ }
641+ buf[len] = '\0';
642+ fclose(fp);
643+
644+ lx.src = buf;
645+ lx.pos = 0;
646+ lx.line = 1;
647+ root = snode_parse(&lx);
648+ free(buf);
649+ load_manifest(root);
650+ snode_free(root);
651+}
+203,
-0
1@@ -0,0 +1,203 @@
2+#include "ninqu.h"
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+/* walk every gate tree, collect the static var names and the rules
9+ * each one gates. skips $(...) vars since those are per-instance */
10+static void
11+collect_gate_vars(struct StrList *out, struct StrList *affects, struct StrList *rules_affected)
12+{
13+ int i;
14+ for (i = 0; i < nrules; i++) {
15+ struct Rule *r = &rules[i];
16+ struct Gate *g = r->gate;
17+ struct StrList seen = {0};
18+ struct Gate **stack;
19+ int sp = 0;
20+
21+ if (!g)
22+ continue;
23+ stack = emalloc(64 * sizeof *stack);
24+ stack[sp++] = g;
25+ while (sp > 0) {
26+ struct Gate *cur = stack[--sp];
27+ int k;
28+ if (cur->kind == G_VAR) {
29+ if (strstr(cur->var, "$("))
30+ continue;
31+ if (!sl_has(&seen, cur->var)) {
32+ sl_push(&seen, cur->var);
33+ if (!sl_has(out, cur->var))
34+ sl_push(out, cur->var);
35+ sl_push(affects, cur->var);
36+ sl_push(rules_affected, r->name);
37+ }
38+ continue;
39+ }
40+ for (k = 0; k < cur->nkids; k++)
41+ stack[sp++] = cur->kids[k];
42+ }
43+ free(stack);
44+ }
45+}
46+
47+/* (feature NAME) tags, separate from gate vars. a rule can carry
48+ * more than one, so the pairing is flattened */
49+static void
50+collect_named_features(struct StrList *out, struct StrList *owns, struct StrList *rules_tagged)
51+{
52+ int i, j;
53+ for (i = 0; i < nrules; i++) {
54+ struct Rule *r = &rules[i];
55+ for (j = 0; j < r->features.n; j++) {
56+ const char *feat = r->features.v[j];
57+ if (!sl_has(out, feat))
58+ sl_push(out, feat);
59+ sl_push(owns, feat);
60+ sl_push(rules_tagged, r->name);
61+ }
62+ }
63+}
64+
65+void
66+do_features(void)
67+{
68+ struct StrList gatevars = {0}, affects = {0}, affected = {0};
69+ struct StrList featnames = {0}, feat_owns = {0}, feat_rules = {0};
70+ int i, j;
71+
72+ printf("variables (override with -Dname=value):\n");
73+ for (i = 0; i < kv.n; i++)
74+ printf(
75+ " %s=%s%s\n",
76+ kv.v[i].key,
77+ kv.v[i].val,
78+ sl_has(&kv_locked, kv.v[i].key) ? " [locked by -D]" : ""
79+ );
80+
81+ collect_gate_vars(&gatevars, &affects, &affected);
82+ printf("\nfeature gates (override with -Dname=0 or -Dname=1):\n");
83+ for (i = 0; i < gatevars.n; i++) {
84+ printf(" %s=%s\n", gatevars.v[i], kv_get_or(gatevars.v[i], ""));
85+ for (j = 0; j < affects.n; j++)
86+ if (strcmp(affects.v[j], gatevars.v[i]) == 0)
87+ printf(" gates rule: %s\n", affected.v[j]);
88+ }
89+
90+ collect_named_features(&featnames, &feat_owns, &feat_rules);
91+ if (featnames.n > 0) {
92+ printf("\nnamed features (declared with (feature NAME)):\n");
93+ for (i = 0; i < featnames.n; i++) {
94+ printf(" %s\n", featnames.v[i]);
95+ for (j = 0; j < feat_owns.n; j++)
96+ if (strcmp(feat_owns.v[j], featnames.v[i]) == 0)
97+ printf(" rule: %s\n", feat_rules.v[j]);
98+ }
99+ }
100+
101+ printf("\ngroups:\n");
102+ for (i = 0; i < ngroups; i++)
103+ printf(" %s\n", groups[i].name);
104+
105+ printf("\nrules:\n");
106+ for (i = 0; i < nrules; i++) {
107+ struct Rule *r = &rules[i];
108+ printf(" %s%s\n", r->name, r->globs.n > 0 ? " (pattern rule)" : "");
109+ if (r->description[0])
110+ printf(" # %s\n", r->description);
111+ if (r->group_name[0])
112+ printf(" group: %s\n", r->group_name);
113+ if (r->member_of[0])
114+ printf(" member: %s/%s\n", r->member_of, r->member_alias[0] ? r->member_alias : r->name);
115+ for (j = 0; j < r->features.n; j++)
116+ printf(" feature: %s\n", r->features.v[j]);
117+ }
118+}
119+
120+void
121+do_query(const char *sub, const char *arg)
122+{
123+ int i, j;
124+
125+ if (strcmp(sub, "features") == 0) {
126+ do_features();
127+ return;
128+ }
129+ if (strcmp(sub, "groups") == 0) {
130+ for (i = 0; i < ngroups; i++) {
131+ printf("%s\n", groups[i].name);
132+ for (j = 0; j < groups[i].refs.n; j++)
133+ printf(" %s\n", groups[i].refs.v[j]);
134+ }
135+ return;
136+ }
137+ if (strcmp(sub, "metas") == 0) {
138+ for (i = 0; i < nmetas; i++) {
139+ printf("%s\n", metas[i].name);
140+ for (j = 0; j < metas[i].keys.n; j++)
141+ printf(" %s=%s\n", metas[i].keys.v[j], metas[i].vals.v[j]);
142+ }
143+ return;
144+ }
145+ if (strcmp(sub, "rules") == 0) {
146+ for (i = 0; i < nrules; i++) {
147+ printf("%s", rules[i].name);
148+ if (rules[i].globs.n > 0) {
149+ printf(" glob:");
150+ for (j = 0; j < rules[i].globs.n; j++)
151+ printf(" %s", rules[i].globs.v[j]);
152+ }
153+ if (rules[i].produces[0])
154+ printf(" produces=%s", rules[i].produces);
155+ if (rules[i].group_name[0])
156+ printf(" group=%s", rules[i].group_name);
157+ if (rules[i].member_of[0])
158+ printf(
159+ " member=%s/%s",
160+ rules[i].member_of,
161+ rules[i].member_alias[0] ? rules[i].member_alias : rules[i].name
162+ );
163+ for (j = 0; j < rules[i].features.n; j++)
164+ printf(" feature=%s", rules[i].features.v[j]);
165+ if (rules[i].description[0])
166+ printf(" # %s", rules[i].description);
167+ printf("\n");
168+ }
169+ return;
170+ }
171+ if (strcmp(sub, "graph") == 0) {
172+ printf("digraph ninqu {\n");
173+ if (arg) {
174+ struct Group *g = group_find(arg);
175+ if (!g)
176+ eprintf("query graph: no such group '%s'\n", arg);
177+ for (i = 0; i < g->refs.n; i++) {
178+ struct Rule *r = rule_find(g->refs.v[i]);
179+ if (!r)
180+ continue;
181+ for (j = 0; j < r->in.n; j++)
182+ if (r->in.v[j][0] == '@')
183+ printf(" \"%s\" -> \"%s\";\n", r->in.v[j] + 1, r->name);
184+ for (j = 0; j < r->extra_deps.n; j++)
185+ if (r->extra_deps.v[j][0] == '@')
186+ printf(" \"%s\" -> \"%s\" [style=dashed];\n", r->extra_deps.v[j] + 1, r->name);
187+ }
188+ } else {
189+ for (i = 0; i < nrules; i++) {
190+ for (j = 0; j < rules[i].in.n; j++)
191+ if (rules[i].in.v[j][0] == '@')
192+ printf(" \"%s\" -> \"%s\";\n", rules[i].in.v[j] + 1, rules[i].name);
193+ for (j = 0; j < rules[i].extra_deps.n; j++)
194+ if (rules[i].extra_deps.v[j][0] == '@')
195+ printf(
196+ " \"%s\" -> \"%s\" [style=dashed];\n", rules[i].extra_deps.v[j] + 1, rules[i].name
197+ );
198+ }
199+ }
200+ printf("}\n");
201+ return;
202+ }
203+ eprintf("query: unknown subcommand '%s' (try features, groups, metas, rules, graph)\n", sub);
204+}
+167,
-0
1@@ -0,0 +1,167 @@
2+#include "ninqu.h"
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+struct Rule *rules;
9+int nrules, rulescap;
10+struct Group *groups;
11+int ngroups, groupscap;
12+struct Meta *metas;
13+int nmetas, metascap;
14+struct Template *templates;
15+int ntemplates, templatescap;
16+struct Inst *insts;
17+int ninsts, instscap;
18+
19+struct Rule *
20+rule_find(const char *name)
21+{
22+ int i;
23+ for (i = 0; i < nrules; i++)
24+ if (strcmp(rules[i].name, name) == 0)
25+ return &rules[i];
26+ return NULL;
27+}
28+
29+/* match (out) against path, expanding $(...) as needed. used to
30+ * find a producer for a file that does not exist yet */
31+struct Rule *
32+rule_find_output(const char *path)
33+{
34+ int i;
35+ for (i = 0; i < nrules; i++) {
36+ if (!rules[i].out[0])
37+ continue;
38+ if (strchr(rules[i].out, '$')) {
39+ char *exp = kv_expand(rules[i].out);
40+ int ok = strcmp(exp, path) == 0;
41+ free(exp);
42+ if (ok)
43+ return &rules[i];
44+ } else if (strcmp(rules[i].out, path) == 0) {
45+ return &rules[i];
46+ }
47+ }
48+ return NULL;
49+}
50+
51+struct Rule *
52+rule_find_produces(const char *name)
53+{
54+ int i;
55+ for (i = 0; i < nrules; i++)
56+ if (rules[i].produces[0] && strcmp(rules[i].produces, name) == 0)
57+ return &rules[i];
58+ return NULL;
59+}
60+
61+struct Rule *
62+rule_new(const char *name)
63+{
64+ struct Rule *r;
65+ if (rule_find(name))
66+ eprintf("manifest: duplicate rule '%s'\n", name);
67+ if (group_find(name))
68+ eprintf("manifest: name '%s' already a group\n", name);
69+ GROW(rules, nrules, rulescap, 16);
70+ r = &rules[nrules++];
71+ memset(r, 0, sizeof *r);
72+ strlcpy(r->name, name, sizeof r->name);
73+ return r;
74+}
75+
76+struct Group *
77+group_find(const char *name)
78+{
79+ int i;
80+ for (i = 0; i < ngroups; i++)
81+ if (strcmp(groups[i].name, name) == 0)
82+ return &groups[i];
83+ return NULL;
84+}
85+
86+struct Group *
87+group_new(const char *name)
88+{
89+ struct Group *g;
90+ if (group_find(name))
91+ eprintf("manifest: duplicate group '%s'\n", name);
92+ if (rule_find(name))
93+ eprintf("manifest: name '%s' already a rule\n", name);
94+ GROW(groups, ngroups, groupscap, 16);
95+ g = &groups[ngroups++];
96+ memset(g, 0, sizeof *g);
97+ strlcpy(g->name, name, sizeof g->name);
98+ return g;
99+}
100+
101+struct Meta *
102+meta_find(const char *name)
103+{
104+ int i;
105+ for (i = 0; i < nmetas; i++)
106+ if (strcmp(metas[i].name, name) == 0)
107+ return &metas[i];
108+ return NULL;
109+}
110+
111+struct Meta *
112+meta_new(const char *name)
113+{
114+ struct Meta *m;
115+ if (meta_find(name))
116+ eprintf("manifest: duplicate meta '%s'\n", name);
117+ if (rule_find(name))
118+ eprintf("manifest: name '%s' already a rule\n", name);
119+ if (group_find(name))
120+ eprintf("manifest: name '%s' already a group\n", name);
121+ GROW(metas, nmetas, metascap, 8);
122+ m = &metas[nmetas++];
123+ memset(m, 0, sizeof *m);
124+ strlcpy(m->name, name, sizeof m->name);
125+ return m;
126+}
127+
128+struct Template *
129+template_find(const char *name)
130+{
131+ int i;
132+ for (i = 0; i < ntemplates; i++)
133+ if (strcmp(templates[i].name, name) == 0)
134+ return &templates[i];
135+ return NULL;
136+}
137+
138+struct Template *
139+template_new(const char *name)
140+{
141+ struct Template *t;
142+ if (template_find(name))
143+ eprintf("manifest: duplicate template '%s'\n", name);
144+ GROW(templates, ntemplates, templatescap, 8);
145+ t = &templates[ntemplates++];
146+ memset(t, 0, sizeof *t);
147+ strlcpy(t->name, name, sizeof t->name);
148+ return t;
149+}
150+
151+int
152+inst_new(int rule_idx)
153+{
154+ struct Inst *in;
155+ struct Rule *r = &rules[rule_idx];
156+
157+ GROW(insts, ninsts, instscap, 32);
158+ in = &insts[ninsts];
159+ memset(in, 0, sizeof *in);
160+ in->rule_idx = rule_idx;
161+
162+ if (r->n_inst >= r->cap_inst) {
163+ r->cap_inst = r->cap_inst ? r->cap_inst * 2 : 4;
164+ r->inst_idx = erealloc(r->inst_idx, (size_t)r->cap_inst * sizeof *r->inst_idx);
165+ }
166+ r->inst_idx[r->n_inst++] = ninsts;
167+ return ninsts++;
168+}
+195,
-0
1@@ -0,0 +1,195 @@
2+#include "ninqu.h"
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+
8+/* pull in every rule a group names, recursively */
9+static struct StrList visited_groups;
10+
11+void
12+include_ref(const char *name)
13+{
14+ struct Group *g = group_find(name);
15+ int i;
16+
17+ if (g) {
18+ if (sl_has(&visited_groups, name))
19+ return;
20+ sl_push(&visited_groups, name);
21+ for (i = 0; i < g->refs.n; i++)
22+ include_ref(g->refs.v[i]);
23+ return;
24+ }
25+ if (rule_find(name)) {
26+ expand_rule(name);
27+ return;
28+ }
29+ eprintf("manifest: '%s' is neither a known group nor a known rule\n", name);
30+}
31+
32+void
33+build_default_group(void)
34+{
35+ struct Group *def = group_find("default");
36+ int i;
37+ if (!def)
38+ eprintf("no group given, and manifest defines no (group default)\n");
39+ for (i = 0; i < def->refs.n; i++)
40+ include_ref(def->refs.v[i]);
41+}
42+
43+/* match on ALIAS when the rule gave one, otherwise on the rule own
44+ * bare name, so a member with no alias is still reachable under its
45+ * existing name */
46+static struct Rule *
47+rule_find_member(const char *group, const char *alias)
48+{
49+ int i;
50+ for (i = 0; i < nrules; i++) {
51+ struct Rule *r = &rules[i];
52+ if (!r->member_of[0] || strcmp(r->member_of, group) != 0)
53+ continue;
54+ if (r->member_alias[0]) {
55+ if (strcmp(r->member_alias, alias) == 0)
56+ return r;
57+ } else if (strcmp(r->name, alias) == 0) {
58+ return r;
59+ }
60+ }
61+ return NULL;
62+}
63+
64+/* GROUP/ALIAS qualified addressing. leading segments before the
65+ * last two are ignored, so "posix/bc" and "cmd/posix/bc" resolve
66+ * the same rule. returns NULL when w has no '/', so callers can
67+ * fall through to plain group/rule resolution. a qualified path
68+ * that does not resolve is a hard error, not a silent fallthrough */
69+static struct Rule *
70+resolve_member_path(const char *w)
71+{
72+ const char *last_slash;
73+ const char *alias;
74+ const char *group_start;
75+ const char *p;
76+ char group[128];
77+ size_t glen;
78+ struct Rule *r;
79+
80+ last_slash = strrchr(w, '/');
81+ if (!last_slash)
82+ return NULL;
83+ alias = last_slash + 1;
84+ if (!*alias)
85+ eprintf("manifest: '%s' ends in '/', expected GROUP/ALIAS\n", w);
86+
87+ group_start = w;
88+ for (p = w; p < last_slash; p++)
89+ if (*p == '/')
90+ group_start = p + 1;
91+ glen = (size_t)(last_slash - group_start);
92+ if (glen == 0 || glen >= sizeof group)
93+ eprintf("manifest: '%s': bad GROUP/ALIAS path\n", w);
94+ memcpy(group, group_start, glen);
95+ group[glen] = '\0';
96+
97+ if (!group_find(group))
98+ eprintf("manifest: '%s': no such group '%s'\n", w, group);
99+ r = rule_find_member(group, alias);
100+ if (!r)
101+ eprintf("manifest: '%s' names no member of group '%s'\n", alias, group);
102+ return r;
103+}
104+
105+/* META.TARGET applies META overrides then resolves TARGET.
106+ * META alone applies META then falls back to (group default).
107+ * anything else is a plain group or rule name */
108+void
109+resolve_wanted(const char *w)
110+{
111+ const char *dot = strchr(w, '.');
112+ struct Meta *m;
113+ char meta_name[128];
114+ struct Rule *member;
115+
116+ if (strchr(w, '/')) {
117+ member = resolve_member_path(w);
118+ expand_rule(member->name);
119+ return;
120+ }
121+
122+ if (dot) {
123+ size_t len = (size_t)(dot - w);
124+ if (len >= sizeof meta_name)
125+ eprintf("manifest: meta name too long in '%s'\n", w);
126+ memcpy(meta_name, w, len);
127+ meta_name[len] = '\0';
128+ m = meta_find(meta_name);
129+ if (!m)
130+ eprintf("manifest: '%s' names no known meta-target\n", meta_name);
131+ meta_apply(m);
132+ include_ref(dot + 1);
133+ return;
134+ }
135+
136+ m = meta_find(w);
137+ if (m) {
138+ meta_apply(m);
139+ build_default_group();
140+ return;
141+ }
142+
143+ /* a rule that declared (member ...) does not resolve by bare name
144+ * from the command line, only through GROUP/ALIAS or transitively
145+ * via its owning group ref list */
146+ {
147+ struct Rule *br = rule_find(w);
148+ if (br && br->member_of[0])
149+ eprintf(
150+ "manifest: '%s' is a member of group '%s', not a top-level target; "
151+ "use '%s/%s'\n",
152+ w,
153+ br->member_of,
154+ br->member_of,
155+ br->member_alias[0] ? br->member_alias : br->name
156+ );
157+ }
158+
159+ include_ref(w);
160+}
161+
162+/* a plain token in (dep) or (after) that names an existing rule
163+ * means the same as (rule NAME). resolved once after the whole
164+ * manifest loads, so a rule declared later can still be named
165+ * earlier. a token with $, glob metachars, /, or . is left alone
166+ * so this never shadows a real file dependency */
167+static int
168+looks_like_path(const char *s)
169+{
170+ return strpbrk(s, "$*?/.") != NULL;
171+}
172+
173+static void
174+resolve_bareword_deps(struct StrList *sl)
175+{
176+ int i;
177+ for (i = 0; i < sl->n; i++) {
178+ char *tok = sl->v[i];
179+ char buf[256];
180+ if (tok[0] == '@' || looks_like_path(tok) || !rule_find(tok))
181+ continue;
182+ snprintf(buf, sizeof buf, "@%s", tok);
183+ free(sl->v[i]);
184+ sl->v[i] = estrdup(buf);
185+ }
186+}
187+
188+void
189+resolve_all_bareword_deps(void)
190+{
191+ int i;
192+ for (i = 0; i < nrules; i++) {
193+ resolve_bareword_deps(&rules[i].in);
194+ resolve_bareword_deps(&rules[i].extra_deps);
195+ }
196+}
+93,
-0
1@@ -0,0 +1,93 @@
2+#include "ninqu.h"
3+
4+#include <stdio.h>
5+#include <stdlib.h>
6+
7+/* kahn topo sort, one ready level at a time so each level
8+ * runs in parallel. failures are reported but do not stop the
9+ * batch, so one broken file does not block the rest */
10+void
11+schedule_and_run(void)
12+{
13+ int *indeg = ecalloc((size_t)ninsts, sizeof *indeg);
14+ int **dependents = emalloc((size_t)ninsts * sizeof *dependents);
15+ int *ndep = ecalloc((size_t)ninsts, sizeof *ndep);
16+ int *capdep = ecalloc((size_t)ninsts, sizeof *capdep);
17+ int *level = emalloc((size_t)ninsts * sizeof *level);
18+ int levn = 0, done_count = 0;
19+ int i, j;
20+
21+ for (i = 0; i < ninsts; i++) {
22+ indeg[i] = insts[i].n_dep;
23+ dependents[i] = NULL;
24+ }
25+ for (i = 0; i < ninsts; i++) {
26+ for (j = 0; j < insts[i].n_dep; j++) {
27+ int d = insts[i].dep_inst[j];
28+ if (ndep[d] >= capdep[d]) {
29+ capdep[d] = capdep[d] ? capdep[d] * 2 : 4;
30+ dependents[d] = erealloc(dependents[d], (size_t)capdep[d] * sizeof *dependents[d]);
31+ }
32+ dependents[d][ndep[d]++] = i;
33+ }
34+ }
35+ for (i = 0; i < ninsts; i++)
36+ if (indeg[i] == 0)
37+ level[levn++] = i;
38+
39+ while (levn > 0) {
40+ int *next_level = emalloc((size_t)ninsts * sizeof *next_level);
41+ int nextn = 0;
42+
43+ if (summary_mode) {
44+ for (i = 0; i < levn; i++) {
45+ struct Inst *inst = &insts[level[i]];
46+ int will = inst_stale(inst);
47+ int d;
48+ for (d = 0; d < inst->n_dep && !will; d++)
49+ if (insts[inst->dep_inst[d]].will_build)
50+ will = 1;
51+ inst->will_build = will;
52+ printf(
53+ "%-4s %-12s %s\n",
54+ will ? "BUILD" : "OK",
55+ rules[inst->rule_idx].name,
56+ inst->out[0] ? inst->out : "(phony)"
57+ );
58+ if (rules[inst->rule_idx].description[0])
59+ printf(" # %s\n", rules[inst->rule_idx].description);
60+ if (will) {
61+ char *cmd = sl_join(&inst->cmd.argv);
62+ printf(" cmd: %s\n", cmd);
63+ free(cmd);
64+ }
65+ }
66+ } else {
67+ run_batch(level, levn);
68+ }
69+
70+ done_count += levn;
71+ for (i = 0; i < levn; i++) {
72+ int u = level[i];
73+ for (j = 0; j < ndep[u]; j++) {
74+ int v = dependents[u][j];
75+ if (--indeg[v] == 0)
76+ next_level[nextn++] = v;
77+ }
78+ }
79+ free(level);
80+ level = next_level;
81+ levn = nextn;
82+ }
83+
84+ if (done_count < ninsts)
85+ eprintf("manifest: dependency cycle among %d instances\n", ninsts - done_count);
86+
87+ free(level);
88+ free(indeg);
89+ for (i = 0; i < ninsts; i++)
90+ free(dependents[i]);
91+ free(dependents);
92+ free(ndep);
93+ free(capdep);
94+}
+344,
-0
1@@ -0,0 +1,344 @@
2+#include "ninqu.h"
3+
4+#include <ctype.h>
5+#include <stdio.h>
6+#include <stdlib.h>
7+#include <string.h>
8+
9+/* track nesting so $(EXTRA_LIBS:$(BASESTEM)) does not stop at the
10+ * inner close paren. shared by kv_expand and subst_tmpl */
11+const char *
12+find_dollar_close(const char *inner)
13+{
14+ const char *scan = inner;
15+ int depth = 1;
16+
17+ while (*scan) {
18+ if (scan[0] == '$' && scan[1] == '(') {
19+ depth++;
20+ scan += 2;
21+ continue;
22+ }
23+ if (*scan == ')') {
24+ if (--depth == 0)
25+ return scan;
26+ }
27+ scan++;
28+ }
29+ return NULL;
30+}
31+
32+struct SNode *
33+snode_atom(const char *s, int line)
34+{
35+ struct SNode *n = ecalloc(1, sizeof *n);
36+ n->kind = S_ATOM;
37+ n->atom = estrdup(s);
38+ n->line = line;
39+ return n;
40+}
41+
42+struct SNode *
43+snode_list(int line)
44+{
45+ struct SNode *n = ecalloc(1, sizeof *n);
46+ n->kind = S_LIST;
47+ n->line = line;
48+ return n;
49+}
50+
51+void
52+snode_push(struct SNode *list, struct SNode *kid)
53+{
54+ if (list->nkids == 0) {
55+ list->kids = emalloc(8 * sizeof *list->kids);
56+ } else {
57+ int cap = 8;
58+ while (cap < list->nkids)
59+ cap *= 2;
60+ if (list->nkids == cap)
61+ list->kids = erealloc(list->kids, (size_t)cap * 2 * sizeof *list->kids);
62+ }
63+ list->kids[list->nkids++] = kid;
64+}
65+
66+void
67+snode_free(struct SNode *n)
68+{
69+ int i;
70+ if (!n)
71+ return;
72+ if (n->kind == S_LIST) {
73+ for (i = 0; i < n->nkids; i++)
74+ snode_free(n->kids[i]);
75+ free(n->kids);
76+ }
77+ free(n->atom);
78+ free(n);
79+}
80+
81+/* deep copy. a template body is parsed from a file that gets freed
82+ * once load_manifest returns, so the stored copy cannot point back
83+ * into that tree */
84+struct SNode *
85+snode_clone(struct SNode *n)
86+{
87+ struct SNode *out;
88+ int i;
89+
90+ if (!n)
91+ return NULL;
92+ if (n->kind == S_ATOM)
93+ return snode_atom(n->atom, n->line);
94+ out = snode_list(n->line);
95+ for (i = 0; i < n->nkids; i++)
96+ snode_push(out, snode_clone(n->kids[i]));
97+ return out;
98+}
99+
100+/* replace each $(PARAM) with its argument text. any other $(...) is
101+ * copied verbatim for the later kv_expand pass, so a template body
102+ * can reference $(BASESTEM) the same way a hand-written rule does */
103+char *
104+subst_tmpl(const char *s, struct StrList *params, char **vals)
105+{
106+ size_t cap = strlen(s) * 2 + 64, len = 0;
107+ char *out = emalloc(cap);
108+ const char *p = s;
109+
110+ while (*p) {
111+ if (p[0] == '$' && p[1] == '(') {
112+ const char *inner = p + 2;
113+ const char *close = find_dollar_close(inner);
114+
115+ if (close) {
116+ char name[256];
117+ size_t nl = (size_t)(close - inner);
118+ size_t tokl;
119+ int pi;
120+
121+ if (nl >= sizeof name)
122+ nl = sizeof name - 1;
123+ memcpy(name, inner, nl);
124+ name[nl] = '\0';
125+
126+ for (pi = 0; pi < params->n; pi++)
127+ if (strcmp(params->v[pi], name) == 0)
128+ break;
129+
130+ tokl = (pi < params->n) ? strlen(vals[pi]) : (size_t)(close - p) + 1;
131+ while (len + tokl + 1 >= cap) {
132+ cap *= 2;
133+ out = erealloc(out, cap);
134+ }
135+ memcpy(out + len, pi < params->n ? vals[pi] : p, tokl);
136+ len += tokl;
137+ p = close + 1;
138+ continue;
139+ }
140+ }
141+ if (len + 2 >= cap) {
142+ cap *= 2;
143+ out = erealloc(out, cap);
144+ }
145+ out[len++] = *p++;
146+ }
147+ out[len] = '\0';
148+ return out;
149+}
150+
151+void
152+snode_subst(struct SNode *n, struct StrList *params, char **vals)
153+{
154+ int i;
155+ if (!n)
156+ return;
157+ if (n->kind == S_ATOM) {
158+ char *out = subst_tmpl(n->atom, params, vals);
159+ free(n->atom);
160+ n->atom = out;
161+ return;
162+ }
163+ for (i = 0; i < n->nkids; i++)
164+ snode_subst(n->kids[i], params, vals);
165+}
166+
167+/* swallow a balanced $(...) inside a bare atom */
168+void
169+lex_swallow_dollar(struct SLex *lx, char *buf, size_t *blen, size_t bsz)
170+{
171+ int depth = 1;
172+
173+ if (*blen + 2 < bsz) {
174+ buf[(*blen)++] = '$';
175+ buf[(*blen)++] = '(';
176+ }
177+ lx->pos += 2;
178+ while (depth > 0) {
179+ int e = lx->src[lx->pos];
180+ if (e == '\0')
181+ eprintf("manifest: unterminated $(...) on line %d\n", lx->line);
182+ if (e == '\n')
183+ lx->line++;
184+ if (e == '$' && lx->src[lx->pos + 1] == '(') {
185+ depth++;
186+ if (*blen + 2 < bsz) {
187+ buf[(*blen)++] = e;
188+ buf[(*blen)++] = lx->src[lx->pos + 1];
189+ }
190+ lx->pos += 2;
191+ continue;
192+ }
193+ if (*blen < bsz - 1)
194+ buf[(*blen)++] = e;
195+ if (e == ')')
196+ depth--;
197+ lx->pos++;
198+ }
199+}
200+
201+/* \" \\ \n \t \r \$ are recognized. anything else keeps the backslash
202+ * so shell escapes like \( survive */
203+void
204+lex_string(struct SLex *lx, char *buf, size_t bsz)
205+{
206+ int line = lx->line;
207+ size_t blen = 0;
208+
209+ lx->pos++;
210+ while (lx->src[lx->pos] && lx->src[lx->pos] != '"') {
211+ int e = lx->src[lx->pos];
212+
213+ if (e == '\\' && lx->src[lx->pos + 1]) {
214+ int esc = lx->src[lx->pos + 1];
215+ switch (esc) {
216+ case 'n':
217+ e = '\n';
218+ break;
219+ case 't':
220+ e = '\t';
221+ break;
222+ case 'r':
223+ e = '\r';
224+ break;
225+ case '"':
226+ case '\\':
227+ case '$':
228+ e = esc;
229+ break;
230+ default:
231+ if (blen < bsz - 1)
232+ buf[blen++] = '\\';
233+ e = esc;
234+ break;
235+ }
236+ lx->pos += 2;
237+ } else {
238+ lx->pos++;
239+ }
240+ if (e == '\n')
241+ lx->line++;
242+ if (blen < bsz - 1)
243+ buf[blen++] = (char)e;
244+ }
245+ if (lx->src[lx->pos] != '"')
246+ eprintf("manifest: unterminated string on line %d\n", line);
247+ lx->pos++;
248+ buf[blen] = '\0';
249+}
250+
251+struct SNode *
252+snode_parse(struct SLex *lx)
253+{
254+ char buf[8192];
255+ size_t blen;
256+ int line;
257+ struct SNode *root = snode_list(lx->line);
258+ struct SNode **stack = emalloc(16 * sizeof *stack);
259+ int sp = 0, scap = 16;
260+
261+ stack[sp++] = root;
262+
263+ for (;;) {
264+ int c = lx->src[lx->pos];
265+
266+ if (c == '\0')
267+ break;
268+ if (c == '\n') {
269+ lx->line++;
270+ lx->pos++;
271+ continue;
272+ }
273+ if (isspace((unsigned char)c)) {
274+ lx->pos++;
275+ continue;
276+ }
277+ if (c == ';') {
278+ while (lx->src[lx->pos] && lx->src[lx->pos] != '\n')
279+ lx->pos++;
280+ continue;
281+ }
282+ if (c == '(') {
283+ struct SNode *lst = snode_list(lx->line);
284+ snode_push(stack[sp - 1], lst);
285+ if (sp >= scap) {
286+ scap *= 2;
287+ stack = erealloc(stack, (size_t)scap * sizeof *stack);
288+ }
289+ stack[sp++] = lst;
290+ lx->pos++;
291+ continue;
292+ }
293+ if (c == ')') {
294+ if (sp <= 1)
295+ eprintf("manifest: stray ')' on line %d\n", lx->line);
296+ sp--;
297+ lx->pos++;
298+ continue;
299+ }
300+ if (c == '"') {
301+ line = lx->line;
302+ lex_string(lx, buf, sizeof buf);
303+ snode_push(stack[sp - 1], snode_atom(buf, line));
304+ continue;
305+ }
306+
307+ /* bare atom */
308+ line = lx->line;
309+ blen = 0;
310+ while (lx->src[lx->pos]) {
311+ int d = lx->src[lx->pos];
312+ if (d == '(' || d == ')' || d == '"' || d == ';' || isspace((unsigned char)d))
313+ break;
314+ if (d == '$' && lx->src[lx->pos + 1] == '(') {
315+ lex_swallow_dollar(lx, buf, &blen, sizeof buf);
316+ continue;
317+ }
318+ if (blen < sizeof buf - 1)
319+ buf[blen++] = d;
320+ lx->pos++;
321+ }
322+ buf[blen] = '\0';
323+ snode_push(stack[sp - 1], snode_atom(buf, line));
324+ }
325+
326+ if (sp != 1)
327+ eprintf("manifest: %d unclosed '(' at end of file\n", sp - 1);
328+
329+ free(stack);
330+ return root;
331+}
332+
333+int
334+s_is_list(struct SNode *n)
335+{
336+ return n && n->kind == S_LIST;
337+}
338+
339+const char *
340+s_head(struct SNode *n)
341+{
342+ if (!s_is_list(n) || n->nkids == 0 || n->kids[0]->kind != S_ATOM)
343+ return NULL;
344+ return n->kids[0]->atom;
345+}
+103,
-0
1@@ -0,0 +1,103 @@
2+#include "ninqu.h"
3+
4+#include <ctype.h>
5+#include <errno.h>
6+#include <stdio.h>
7+#include <string.h>
8+#include <unistd.h>
9+
10+void
11+sl_push(struct StrList *sl, const char *s)
12+{
13+ GROW(sl->v, sl->n, sl->cap, 8);
14+ sl->v[sl->n++] = estrdup(s);
15+}
16+
17+int
18+sl_has(struct StrList *sl, const char *s)
19+{
20+ int i;
21+ for (i = 0; i < sl->n; i++)
22+ if (strcmp(sl->v[i], s) == 0)
23+ return 1;
24+ return 0;
25+}
26+
27+void
28+sl_split(struct StrList *sl, const char *s)
29+{
30+ const char *p = s;
31+
32+ sl->n = 0;
33+ while (*p) {
34+ const char *start;
35+ char buf[4096];
36+ size_t len;
37+
38+ while (*p && isspace((unsigned char)*p))
39+ p++;
40+ if (!*p)
41+ break;
42+ start = p;
43+ while (*p && !isspace((unsigned char)*p))
44+ p++;
45+ len = (size_t)(p - start);
46+ if (len >= sizeof buf)
47+ len = sizeof buf - 1;
48+ memcpy(buf, start, len);
49+ buf[len] = '\0';
50+ sl_push(sl, buf);
51+ }
52+}
53+
54+char *
55+sl_join(struct StrList *sl)
56+{
57+ size_t cap = 64, len = 0;
58+ char *out = emalloc(cap);
59+ int i;
60+
61+ out[0] = '\0';
62+ for (i = 0; i < sl->n; i++) {
63+ size_t addlen = strlen(sl->v[i]) + 1;
64+ while (len + addlen + 1 > cap) {
65+ cap *= 2;
66+ out = erealloc(out, cap);
67+ }
68+ if (i > 0)
69+ out[len++] = ' ';
70+ memcpy(out + len, sl->v[i], strlen(sl->v[i]));
71+ len += strlen(sl->v[i]);
72+ out[len] = '\0';
73+ }
74+ return out;
75+}
76+
77+const char *
78+base_of(const char *path)
79+{
80+ const char *p = strrchr(path, '/');
81+ return p ? p + 1 : path;
82+}
83+
84+/* null-terminate the array so execvp stops scanning */
85+char **
86+sl_argv(struct StrList *sl)
87+{
88+ char **av = emalloc((size_t)(sl->n + 1) * sizeof *av);
89+ int i;
90+ for (i = 0; i < sl->n; i++)
91+ av[i] = sl->v[i];
92+ av[sl->n] = NULL;
93+ return av;
94+}
95+
96+/* noreturn so callers do not need to guard the dead path after the
97+ * fork child calls this */
98+void
99+child_execvp(char **av)
100+{
101+ execvp(av[0], av);
102+ fprintf(stderr, "%s: exec %s: %s\n", argv0, av[0], strerror(errno));
103+ _exit(127);
104+}
+678,
-0
1@@ -0,0 +1,678 @@
2+; genconfig.sh config produces four files from build.cfg:
3+; config.set (read by ninqu), config.kv (sourced by genbox.sh),
4+; nofork_list.h (included by wexec.c), and features.h (-include'd
5+; into every compile). each gets its own rule so ninqu can find a
6+; producer for any of them
7+;
8+(rule CONFIG_SET
9+ (out "scripts/mk/config.set")
10+ (dep "build.cfg" "scripts/genconfig.sh")
11+ (exec sh scripts/genconfig.sh config))
12+
13+(rule NOFORK_LIST_H
14+ (out "shared/libutil/nofork_list.h")
15+ (dep "build.cfg" "scripts/genconfig.sh")
16+ (exec sh scripts/genconfig.sh config))
17+
18+(rule FEATURES_H
19+ (out "scripts/mk/features.h")
20+ (dep "build.cfg" "scripts/genconfig.sh")
21+ (exec sh scripts/genconfig.sh config))
22+
23+(set-file "scripts/mk/config.set")
24+
25+; static libraries: compile every .c in each lib dir then AR
26+;
27+(rule LIBUTF_OBJ
28+ (glob "shared/libutf/*.c")
29+ (dep $(MATCH))
30+ (out "$(STEM).o")
31+ (exec $(CC) $(CPPFLAGS) -Ishared/libutf $(CFLAGS) -c -o $(OUT) $(MATCH)))
32+
33+(rule LIBUTF
34+ (dep LIBUTF_OBJ)
35+ (out "shared/libutf/libutf.a")
36+ (description "utf-8 and rune handling, linked into every aruu tool")
37+ (feature core-lib)
38+ (exec $(AR) $(ARFLAGS) $(OUT) $(IN)))
39+
40+(rule LIBUTIL_OBJ
41+ (glob "shared/libutil/*.c")
42+ (dep $(MATCH))
43+ (after NOFORK_LIST_H FEATURES_H)
44+ (out "$(STEM).o")
45+ (exec $(CC) $(CPPFLAGS_LIBUTIL) -Ishared/libutil $(CFLAGS) -c -o $(OUT) $(MATCH)))
46+
47+(rule LIBUTIL
48+ (dep LIBUTIL_OBJ)
49+ (out "shared/libutil/libutil.a")
50+ (description "allocation, error reporting, and string helpers shared by every tool")
51+ (feature core-lib)
52+ (exec $(AR) $(ARFLAGS) $(OUT) $(IN)))
53+
54+(rule LIBREDLINE_OBJ
55+ (glob "shared/libredline/*.c")
56+ (dep $(MATCH))
57+ (out "$(STEM).o")
58+ (exec $(CC) $(CPPFLAGS) -Ishared/libredline $(CFLAGS) -c -o $(OUT) $(MATCH)))
59+
60+(rule LIBREDLINE
61+ (dep LIBREDLINE_OBJ)
62+ (out "shared/libredline/libredline.a")
63+ (description "line editing used by sh and any other interactive tool")
64+ (feature core-lib)
65+ (exec $(AR) $(ARFLAGS) $(OUT) $(IN)))
66+
67+(group lib LIBUTF LIBUTIL LIBREDLINE)
68+
69+; perfile: every *.c directly inside one of these dirs becomes its
70+; own binary of the same name, individually gated by
71+; build_<category>_<ident> (lowercase, hyphens and dots turned to
72+; underscores, see the $(IDENT) comment above BOX_OBJ below for why)
73+; (skip) excludes files that belong to a multi-file tool below instead
74+;
75+(map EXTRA_LIBS
76+ (diff "-lm")
77+ (wget $(LDLIBS_TLS)))
78+
79+; Every .c file in a category builds into its own binary. PERFILE captures
80+; that shared rule. Only EXTRA_AFTER differs cuz POSIX waits for GETCONF_H
81+; the rest pass "". cmd/posix/bc.c overrides this with its own yacc-based
82+; rule, so no need for PERFILE to have that as a special case
83+;
84+(template PERFILE (NAME CATEGORY DESC EXTRA_AFTER)
85+ (rule $(NAME)
86+ (glob "cmd/$(CATEGORY)/*.c")
87+ (gate build_$(CATEGORY)_$(IDENT))
88+ (dep $(MATCH))
89+ (after LIBUTIL LIBUTF $(EXTRA_AFTER))
90+ (out $(STEM))
91+ (description $(DESC))
92+ (feature perfile)
93+ (exec $(CC) $(CPPFLAGS) $(CFLAGS) -o $(OUT) $(MATCH) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS) $(EXTRA_LIBS:$(BASESTEM)))))
94+
95+(use PERFILE POSIX_BIN posix "one POSIX2024 utility per source file under cmd/posix" GETCONF_H)
96+(use PERFILE LINUX_BIN linux "Linux-only utility, one source file per binary under cmd/linux" "")
97+(use PERFILE NET_BIN net "networking utility, one source file per binary under cmd/net" "")
98+(use PERFILE XSI_BIN xsi "XSI-specified utility, one source file per binary under cmd/xsi" "")
99+(use PERFILE PSEUDO_BIN pseudo "common but undefined utility, one source file per binary under cmd/pseudo" "")
100+(use PERFILE EXTRA_BIN extra "extra utility outside POSIX/XSI scope, one source file per binary under cmd/extra" "")
101+
102+(group posix lib POSIX_BIN POSIX_BC POSIX_AWK POSIX_SH POSIX_MAKE)
103+(group linux lib LINUX_BIN)
104+(group net lib NET_BIN)
105+(group xsi lib XSI_BIN)
106+(group pseudo lib PSEUDO_BIN)
107+(group extra lib EXTRA_BIN EXTRA_YAP)
108+
109+; codegen: generate sources consumed by the build rules below. Ungated
110+; generators always run; conditional ones use (req) so they only run
111+; when their inputs exist in this checkout
112+;
113+(rule GETCONF_H
114+ (gate build_posix_getconf)
115+ (out "cmd/posix/getconf.h")
116+ (dep "scripts/getconf.sh")
117+ (exec sh scripts/genconfig.sh getconf))
118+
119+(rule POSIX_BC_C
120+ (gate build_posix_bc)
121+ (req "cmd/posix/bc.y")
122+ (out "cmd/posix/bc.c")
123+ (dep "cmd/posix/bc.y")
124+ (exec sh scripts/genconfig.sh bc))
125+
126+(rule POSIX_AWK_C
127+ (gate build_posix_awk)
128+ (out "cmd/posix/awk/awkgram.tab.c")
129+ (dep "cmd/posix/awk/awkgram.y")
130+ (exec sh scripts/genconfig.sh awk))
131+
132+(rule POSIX_SH_C
133+ (gate build_posix_sh)
134+ (out "cmd/posix/sh/syntax.c")
135+ (dep "cmd/posix/sh/mksyntax.c" "cmd/posix/sh/mknodes.c" "cmd/posix/sh/mkbuiltins" "cmd/posix/sh/mktokens")
136+ (exec sh scripts/genconfig.sh sh))
137+
138+(rule DEVCONF
139+ (req "cmd/dev/configure")
140+ (out "cmd/dev/config.h")
141+ (dep "cmd/dev/configure")
142+ (phony)
143+ (exec sh cmd/dev/configure))
144+
145+(group codegen GETCONF_H POSIX_BC_C POSIX_AWK_C POSIX_SH_C DEVCONF)
146+
147+; multi: one binary from all .c files in one or more dirs (minus skip).
148+; codegen must run first so generated sources are present before these
149+; globs collect them
150+;
151+(rule POSIX_BC_OBJ
152+ (glob "cmd/posix/bc.c")
153+ (gate build_posix_bc)
154+ (dep $(MATCH))
155+ (after POSIX_BC_C)
156+ (out "$(STEM).o")
157+ (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
158+
159+(rule POSIX_BC
160+ (gate build_posix_bc)
161+ (member posix bc)
162+ (dep POSIX_BC_OBJ)
163+ (after LIBUTIL LIBUTF)
164+ (out "cmd/posix/bc")
165+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS) -lm))
166+
167+(rule POSIX_AWK_OBJ
168+ (glob "cmd/posix/awk/*.c")
169+ (gate build_posix_awk)
170+ (skip "maketab.c")
171+ (dep $(MATCH))
172+ (after POSIX_AWK_C)
173+ (out "$(STEM).o")
174+ (exec $(CC) $(CPPFLAGS) -Icmd/posix/awk $(CFLAGS) -c -o $(OUT) $(MATCH)))
175+
176+(rule POSIX_AWK
177+ (gate build_posix_awk)
178+ (member posix awk)
179+ (dep POSIX_AWK_OBJ)
180+ (after LIBUTIL LIBUTF)
181+ (out "cmd/posix/awk/awk")
182+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS) -lm))
183+
184+(rule POSIX_SH_OBJ
185+ (glob "cmd/posix/sh/*.c")
186+ (gate build_posix_sh)
187+ (skip "mknodes.c" "mksyntax.c")
188+ (dep $(MATCH))
189+ (after POSIX_SH_C)
190+ (out "$(STEM).o")
191+ (exec $(CC) $(CPPFLAGS) -DSHELL -Icmd/posix/sh $(CFLAGS) -c -o $(OUT) $(MATCH)))
192+
193+(rule POSIX_SH
194+ (gate build_posix_sh)
195+ (member posix sh)
196+ (dep POSIX_SH_OBJ)
197+ (after LIBUTIL LIBUTF LIBREDLINE)
198+ (out "cmd/posix/sh/sh")
199+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a shared/libredline/libredline.a $(LDFLAGS) $(LDLIBS)))
200+
201+(rule POSIX_MAKE_OBJ
202+ (glob "cmd/posix/make/*.c")
203+ (gate build_posix_make)
204+ (dep $(MATCH))
205+ (out "$(STEM).o")
206+ (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
207+
208+(rule POSIX_MAKE
209+ (gate build_posix_make)
210+ (member posix make)
211+ (dep POSIX_MAKE_OBJ)
212+ (after LIBUTIL LIBUTF)
213+ (out "cmd/posix/make/make")
214+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS)))
215+
216+(rule EXTRA_YAP_OBJ
217+ (glob "cmd/extra/yap/*.c")
218+ (gate build_extra_yap)
219+ (dep $(MATCH))
220+ (out "$(STEM).o")
221+ (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
222+
223+(rule EXTRA_YAP
224+ (gate build_extra_yap)
225+ (member extra yap)
226+ (dep EXTRA_YAP_OBJ)
227+ (after LIBUTIL LIBUTF)
228+ (out "cmd/extra/yap/yap")
229+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS) -ltinfo))
230+
231+; (member GROUP ALIAS) gives a rule a qualified user-facing name
232+; (e.g. posix/bc) while keeping its internal name (POSIX_BC) for deps,
233+; gates, and other references
234+;
235+
236+; dev: the aruu toolchain. ar/as/ld are standalone and cc
237+; shares one object pool linked into three frontends
238+;
239+(rule AR_OBJ
240+ (glob "cmd/dev/ar/*.c")
241+ (gate build_dev_ar)
242+ (skip "tinyar.c")
243+ (dep $(MATCH))
244+ (out "$(STEM).o")
245+ (exec $(CC) $(CPPFLAGS) -Icmd/dev/ar $(CFLAGS) -c -o $(OUT) $(MATCH)))
246+
247+(rule AR
248+ (gate build_dev_ar)
249+ (dep AR_OBJ)
250+ (after LIBUTIL LIBUTF)
251+ (out "cmd/dev/ar/ar")
252+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS)))
253+
254+(rule XCUTIL_OBJ
255+ (glob "cmd/dev/xcutil/*.c")
256+ (gate build_dev_cc)
257+ (dep $(MATCH))
258+ (out "$(STEM).o")
259+ (exec $(CC) $(CPPFLAGS) -Icmd/dev/xcutil $(CFLAGS) -c -o $(OUT) $(MATCH)))
260+
261+(rule AS_OBJ
262+ (glob "cmd/dev/as/*.c" "cmd/dev/as/arch/x64/*.c")
263+ (gate build_dev_cc)
264+ (dep $(MATCH))
265+ (out "$(STEM).o")
266+ (exec $(CC) $(CPPFLAGS) -Icmd/dev/xcutil -Icmd/dev/as -Icmd/dev/as/arch/x64 -Ishared $(CFLAGS) -c -o $(OUT) $(MATCH)))
267+
268+(rule AS
269+ (gate build_dev_cc)
270+ (dep AS_OBJ)
271+ (after XCUTIL_OBJ LIBUTIL LIBUTF)
272+ (out "cmd/dev/as/as")
273+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS)))
274+
275+(rule LD_OBJ
276+ (glob "cmd/dev/ld/*.c")
277+ (gate build_dev_ld)
278+ (dep $(MATCH))
279+ (out "$(STEM).o")
280+ (exec $(CC) $(CPPFLAGS) -Icmd/dev/xcutil -Icmd/dev/ld $(CFLAGS) -c -o $(OUT) $(MATCH)))
281+
282+(rule LD
283+ (gate build_dev_ld)
284+ (dep LD_OBJ)
285+ (after XCUTIL_OBJ LIBUTIL LIBUTF)
286+ (out "cmd/dev/ld/ld")
287+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS)))
288+
289+; CCPOOL_OBJ globs the whole directory. The entry points below use
290+; literal globs, so the override mechanism excludes them automatically
291+(rule CCPOOL_OBJ
292+ (glob "cmd/dev/cc/*.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_OBJ
299+ (glob "cmd/dev/cc/cc1.c")
300+ (gate build_dev_cc)
301+ (dep $(MATCH))
302+ (out "$(STEM).o")
303+ (exec $(CC) $(CPPFLAGS) -Icmd/dev/cc $(CFLAGS) -c -o $(OUT) $(MATCH)))
304+
305+(rule CC1
306+ (gate build_dev_cc)
307+ (dep CC1_OBJ CCPOOL_OBJ)
308+ (after LIBUTIL LIBUTF)
309+ (out "cmd/dev/cc/cc1")
310+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS)))
311+
312+(rule CPP_OBJ
313+ (glob "cmd/dev/cc/cpp.c")
314+ (gate build_dev_cc)
315+ (dep $(MATCH))
316+ (out "$(STEM).o")
317+ (exec $(CC) $(CPPFLAGS) -Icmd/dev/cc $(CFLAGS) -c -o $(OUT) $(MATCH)))
318+
319+(rule CPP
320+ (gate build_dev_cc)
321+ (dep CPP_OBJ CCPOOL_OBJ)
322+ (after LIBUTIL LIBUTF)
323+ (out "cmd/dev/cc/cpp")
324+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS)))
325+
326+(rule CCDRV_OBJ
327+ (glob "cmd/dev/cc/driver.c" "cmd/dev/cc/util.c")
328+ (gate build_dev_cc)
329+ (dep $(MATCH))
330+ (out "$(STEM).o")
331+ (exec $(CC) $(CPPFLAGS) -Icmd/dev/cc $(CFLAGS) -c -o $(OUT) $(MATCH)))
332+
333+(rule CCDRV
334+ (gate build_dev_cc)
335+ (dep CCDRV_OBJ)
336+ (after LIBUTIL LIBUTF)
337+ (out "cmd/dev/cc/cc")
338+ (exec $(CC) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a $(LDFLAGS) $(LDLIBS)))
339+
340+; ninqu itself lives at cmd/dev/ninqu/ninqu and is built by the
341+; Makefile directly, since it has to exist before this file can be
342+; parsed. a rule here producing the same path would just race that
343+; bootstrap compile, so only the box variant below belongs to ninqu
344+;
345+; Like BOX_OBJ: rename main and hide other globals so ninqu can link
346+; into aruu-box. It is still not reentrant, so it stays out of
347+; NOFORK_LIST
348+;
349+(rule NINQU_BOX_OBJ
350+ (glob "cmd/dev/ninqu/*.c")
351+ (gate build_dev_ninqu)
352+ (dep $(MATCH))
353+ (after LIBUTIL LIBUTF LIBREDLINE NOFORK_LIST_H FEATURES_H GETCONF_H)
354+ (out ".box/cmd/dev/$(BASESTEM).o")
355+ (description "ninqu as a box builtin, main renamed for the box dispatch table")
356+ (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)")
357+ (group box))
358+
359+(group dev lib DEVCONF AR AS LD CC1 CPP CCDRV)
360+
361+; man pages: build mdoc and text pages from ?man-marked sources.
362+;
363+(rule MKMAN
364+ (dir "scripts/mkman")
365+ (dep "glob:scripts/mkman/*.go")
366+ (out "scripts/mkman/mkman")
367+ (exec go build -o mkman .))
368+
369+(rule MAN_MDOC
370+ (glob "cmd/**/*.c")
371+ (mark "?man")
372+ (dep (after MKMAN))
373+ (out "man/man$(SEC_OF:$(DIR))/$(BASESTEM).$(SEC_OF:$(DIR))")
374+ (exec scripts/mkman/mkman -fmt mdoc -config scripts/mk/config.kv -src $(MATCH))
375+ (redirect))
376+
377+(rule MAN_TXT
378+ (glob "cmd/**/*.c")
379+ (mark "?man")
380+ (dep (after MKMAN))
381+ (out "man/man$(SEC_OF:$(DIR))/$(BASESTEM).$(SEC_OF:$(DIR)).txt")
382+ (exec scripts/mkman/mkman -fmt txt -config scripts/mk/config.kv -src $(MATCH))
383+ (redirect))
384+
385+(map SEC_OF
386+ (cmd/posix 1)
387+ (cmd/linux 8)
388+ (cmd/net 8)
389+ (cmd/xsi 1)
390+ (cmd/pseudo 1)
391+ (cmd/extra 1)
392+ (cmd/dev 1))
393+
394+(group man MAN_MDOC MAN_TXT)
395+
396+(rule CLEAN
397+ (phony)
398+ (shell "
399+ printf ' CLEAN object files\n'
400+ find shared cmd -name '*.o' -exec rm -f {} +
401+
402+ printf ' CLEAN static libraries\n'
403+ find shared -name '*.a' -exec rm -f {} +
404+
405+ printf ' CLEAN compiled binaries\n'
406+ find cmd -type f ! -name '*.*' -perm -100 -exec rm -f {} +
407+
408+ sh scripts/genconfig.sh clean
409+ rm -f scripts/mk/config.mk scripts/mk/config.kv scripts/mk/rules.mk shared/libutil/nofork_list.h
410+
411+ printf ' CLEAN build tools\n'
412+ rm -f scripts/mkman/mkman
413+
414+ printf ' CLEAN dev artifacts\n'
415+ rm -f cmd/dev/cc/cc1 cmd/dev/cc/cpp cmd/dev/as/as cmd/dev/ld/ld cmd/dev/ar/ar
416+ rm -f shared/libaruuelf.so
417+ rm -f cmd/dev/config.h cmd/dev/cc/config.h cmd/dev/version.h
418+
419+ printf ' CLEAN misc\n'
420+ rm -rf man/man1 man/man8
421+ rm -rf aruu-box .box
422+
423+ printf ' CLEAN done\n'
424+ "))
425+
426+(group clean CLEAN)
427+
428+; install every built executable and generated man page
429+(rule INSTALL
430+ (phony)
431+ (after ALL_BINS MAN_MDOC MAN_TXT)
432+ (shell "
433+ mkdir -p \"$(PREFIX)/bin\" \"$(MANPREFIX)/man1\" \"$(MANPREFIX)/man8\"
434+
435+ find cmd -type f ! -name '*.*' -perm -100 | while read -r bin; do
436+ name=${bin##*/}
437+ printf ' INSTALL %s/bin/%s\n' '$(PREFIX)' \"$name\"
438+ cp \"$bin\" \"$(PREFIX)/bin/$name\"
439+ chmod 755 \"$(PREFIX)/bin/$name\"
440+ done
441+
442+ find man/man1 man/man8 -type f -name '*.[18]' 2>/dev/null | while read -r f; do
443+ sec=${f%/*}; sec=${sec##*/}
444+ name=${f##*/}
445+ cp \"$f\" \"$(MANPREFIX)/$sec/$name\"
446+ done
447+ "))
448+
449+(group install INSTALL)
450+
451+; remove exactly what install would have copied
452+(rule REMOVE
453+ (phony)
454+ (shell "
455+ find cmd -type f ! -name '*.*' -perm -100 | while read -r bin; do
456+ name=${bin##*/}
457+ printf ' REMOVE %s/bin/%s\n' '$(PREFIX)' \"$name\"
458+ rm -f \"$(PREFIX)/bin/$name\"
459+ done
460+
461+ find man/man1 man/man8 -type f -name '*.[18]' 2>/dev/null | while read -r f; do
462+ sec=${f%/*}; sec=${sec##*/}
463+ name=${f##*/}
464+ printf ' REMOVE %s/%s/%s\n' '$(MANPREFIX)' \"$sec\" \"$name\"
465+ rm -f \"$(MANPREFIX)/$sec/$name\"
466+ done
467+ "))
468+
469+(group remove REMOVE)
470+(group uninstall REMOVE)
471+
472+; box: the multi-call binary containing every enabled tool in a single executable
473+; with a dispatch table. This includes multi-file tools like POSIX_BC, POSIX_AWK,
474+; POSIX_SH, POSIX_MAKE, and EXTRA_YAP. A single-file tool has its main function
475+; renamed directly by BOX_OBJ. The multi-file tools use a partial link (ld -r)
476+; in their respective rules to combine their objects before renaming main,
477+; preventing helper symbols from colliding. This replaces scripts/mkbox
478+;
479+; Object files are stored under .box/ to mirror the cmd/ layout and prevent
480+; collisions with standalone objects. Since standalone and box objects use
481+; different symbol names, they cannot share a path
482+;
483+; Ninqu determines which tools enter the box using the gate on BOX_OBJ.
484+; BOX_DISPATCH reads the object paths passed on argv rather than consulting
485+; build.cfg or config.set directly
486+;
487+
488+; CATEGORY_OF maps a directory path to its category name, matching the toggles
489+; in build.cfg. Since BOX_OBJ globs multiple directories, its gate evaluates the
490+; category dynamically based on the match directory
491+;
492+; Gates evaluate $(IDENT) instead of $(BASESTEM) because toggle names must be
493+; valid shell variables. Genconfig.sh replaces hyphens and periods with
494+; underscores, and $(IDENT) applies the same normalization so they match
495+;
496+(map CATEGORY_OF
497+ (cmd/posix posix)
498+ (cmd/linux linux)
499+ (cmd/net net)
500+ (cmd/pseudo pseudo)
501+ (cmd/xsi xsi)
502+ (cmd/extra extra))
503+
504+; Tool basestems match their C symbols using a shell transform (tr '.-' '__'
505+; and appending _main) in both the objcopy step and in genbox.sh. A static map
506+; is unnecessary because no current tools require custom overrides, and
507+; genbox.sh only receives paths via argv
508+;
509+
510+; The match directory determines the gate and output path, allowing one rule
511+; to handle six categories. The multi-file tool bc.c inside cmd/posix is
512+; automatically excluded via the literal-glob override mechanism
513+;
514+(rule BOX_OBJ
515+ (glob "cmd/posix/*.c" "cmd/linux/*.c" "cmd/net/*.c" "cmd/pseudo/*.c"
516+ "cmd/xsi/*.c" "cmd/extra/*.c")
517+ (gate build_$(CATEGORY_OF:$(DIR))_$(IDENT))
518+ (dep $(MATCH))
519+ (after LIBUTIL LIBUTF LIBREDLINE NOFORK_LIST_H FEATURES_H GETCONF_H)
520+ (out ".box/$(DIR)/$(BASESTEM).o")
521+ (description "one object per enabled tool, main renamed for the box dispatch table")
522+ (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)")
523+ (group box))
524+
525+; Multi-file tools like POSIX_BC, POSIX_AWK, POSIX_SH, POSIX_MAKE, and
526+; EXTRA_YAP cannot use the single-file objcopy step because their global helper
527+; symbols would collide when linked into a single binary. Combining these
528+; objects first with a partial link (ld -r) resolves internal references, making
529+; it safe to isolate the renamed main function and hide remaining symbols
530+;
531+; Combined objects use the layout .box/cmd/<category>/<tool>.o so genbox.sh
532+; can parse them without structural modifications
533+;
534+(rule POSIX_BC_BOX_OBJ
535+ (glob "cmd/posix/bc.c")
536+ (gate build_posix_bc)
537+ (dep $(MATCH))
538+ (after POSIX_BC_C)
539+ (out ".box/parts/$(STEM).o")
540+ (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
541+
542+(rule POSIX_BC_BOX
543+ (gate build_posix_bc)
544+ (dep POSIX_BC_BOX_OBJ)
545+ (out ".box/cmd/posix/bc.o")
546+ (description "bc's objects combined into one relocatable object, main renamed for the box dispatch table")
547+ (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=bc_main $(OUT) && $(OBJCOPY) --keep-global-symbol=bc_main $(OUT)")
548+ (group box))
549+
550+(rule POSIX_AWK_BOX_OBJ
551+ (glob "cmd/posix/awk/*.c")
552+ (gate build_posix_awk)
553+ (skip "maketab.c")
554+ (dep $(MATCH))
555+ (after POSIX_AWK_C)
556+ (out ".box/parts/$(STEM).o")
557+ (exec $(CC) $(CPPFLAGS) -Icmd/posix/awk $(CFLAGS) -c -o $(OUT) $(MATCH)))
558+
559+(rule POSIX_AWK_BOX
560+ (gate build_posix_awk)
561+ (dep POSIX_AWK_BOX_OBJ)
562+ (out ".box/cmd/posix/awk.o")
563+ (description "awk's objects combined into one relocatable object, main renamed for the box dispatch table")
564+ (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=awk_main $(OUT) && $(OBJCOPY) --keep-global-symbol=awk_main $(OUT)")
565+ (group box))
566+
567+(rule POSIX_SH_BOX_OBJ
568+ (glob "cmd/posix/sh/*.c")
569+ (gate build_posix_sh)
570+ (skip "mknodes.c" "mksyntax.c")
571+ (dep $(MATCH))
572+ (after POSIX_SH_C)
573+ (out ".box/parts/$(STEM).o")
574+ (exec $(CC) $(CPPFLAGS) -DSHELL -Icmd/posix/sh $(CFLAGS) -c -o $(OUT) $(MATCH)))
575+
576+(rule POSIX_SH_BOX
577+ (gate build_posix_sh)
578+ (dep POSIX_SH_BOX_OBJ)
579+ (out ".box/cmd/posix/sh.o")
580+ (description "sh's objects combined into one relocatable object, main renamed for the box dispatch table")
581+ (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=sh_main $(OUT) && $(OBJCOPY) --keep-global-symbol=sh_main $(OUT)")
582+ (group box))
583+
584+(rule POSIX_MAKE_BOX_OBJ
585+ (glob "cmd/posix/make/*.c")
586+ (gate build_posix_make)
587+ (dep $(MATCH))
588+ (out ".box/parts/$(STEM).o")
589+ (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
590+
591+(rule POSIX_MAKE_BOX
592+ (gate build_posix_make)
593+ (dep POSIX_MAKE_BOX_OBJ)
594+ (out ".box/cmd/posix/make.o")
595+ (description "make's objects combined into one relocatable object, main renamed for the box dispatch table")
596+ (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=make_main $(OUT) && $(OBJCOPY) --keep-global-symbol=make_main $(OUT)")
597+ (group box))
598+
599+(rule EXTRA_YAP_BOX_OBJ
600+ (glob "cmd/extra/yap/*.c")
601+ (gate build_extra_yap)
602+ (dep $(MATCH))
603+ (out ".box/parts/$(STEM).o")
604+ (exec $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $(OUT) $(MATCH)))
605+
606+(rule EXTRA_YAP_BOX
607+ (gate build_extra_yap)
608+ (dep EXTRA_YAP_BOX_OBJ)
609+ (out ".box/cmd/extra/yap.o")
610+ (description "yap's objects combined into one relocatable object, main renamed for the box dispatch table")
611+ (shell "$(LD) -r -o $(OUT) $(IN) && $(OBJCOPY) --redefine-sym main=yap_main $(OUT) && $(OBJCOPY) --keep-global-symbol=yap_main $(OUT)")
612+ (group box))
613+
614+; Genbox.sh relies on arguments passed by ninqu, where each argument matches
615+; an output from BOX_OBJ or a custom box rule. The script derives categories
616+; and tool names directly from these paths and outputs the dispatch code
617+;
618+(rule BOX_DISPATCH
619+ (dep BOX_OBJ POSIX_BC_BOX POSIX_AWK_BOX POSIX_SH_BOX POSIX_MAKE_BOX EXTRA_YAP_BOX NINQU_BOX_OBJ)
620+ (out ".box/aruu-box.c")
621+ (description "dispatch table and main() for aruu-box, generated from the enabled tool list")
622+ (exec scripts/genbox.sh $(IN))
623+ (redirect)
624+ (group box))
625+
626+; Libm is linked unconditionally because bc requires it, and unused library
627+; references are safe. Modern toolchains omit unreferenced archive components.
628+; Yap requires terminfo, which is an optional system dependency. Linking it
629+; unconditionally breaks builds on systems without ncurses-dev when yap is
630+; disabled. Genconfig.sh exports BOX_EXTRA_LIBS as "-ltinfo" or an empty string
631+; based on whether yap is enabled
632+;
633+(rule BOX
634+ (dep BOX_DISPATCH BOX_OBJ POSIX_BC_BOX POSIX_AWK_BOX POSIX_SH_BOX POSIX_MAKE_BOX EXTRA_YAP_BOX NINQU_BOX_OBJ)
635+ (after LIBUTIL LIBUTF LIBREDLINE)
636+ (out "aruu-box")
637+ (description "single multi-call binary, one dispatch table for every enabled tool")
638+; (shell "$(CC) $(CPPFLAGS) $(CFLAGS) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a shared/libredline/libredline.a $(LDFLAGS) $(LDLIBS) -lm $(BOX_EXTRA_LIBS)")
639+ (shell "$(CC) $(CPPFLAGS) $(CFLAGS) -o $(OUT) $(IN) shared/libutil/libutil.a shared/libutf/libutf.a shared/libredline/libredline.a $(LDFLAGS) $(LDLIBS) $(BOX_EXTRA_LIBS)")
640+ (group box))
641+
642+(group box BOX)
643+
644+; -------------------------------------------------------------------
645+; top level. (group all) contains every build target except clean
646+; and box. box is separate because it produces a different binary
647+; with different .o files, and mixing it with standalone builds
648+; would cause confusion. clean is never in all because it removes
649+; what all builds
650+; -------------------------------------------------------------------
651+
652+(rule ALL_BINS
653+ (phony)
654+ (after LIBUTIL LIBUTF LIBREDLINE
655+ POSIX_BIN LINUX_BIN NET_BIN XSI_BIN
656+ PSEUDO_BIN EXTRA_BIN
657+ POSIX_BC POSIX_AWK POSIX_SH POSIX_MAKE EXTRA_YAP
658+ AR AS LD CC1 CPP CCDRV)
659+ (exec true))
660+
661+(group all ALL_BINS)
662+
663+(group default all)
664+
665+; -------------------------------------------------------------------
666+; meta-targets: apply an override, then build a normal target under
667+; it. invoke as `ninqu META.TARGET` (`ninqu debug.posix`) or bare
668+; `ninqu META` to apply the override and build (group default). the
669+; $(CFLAGS) inside each value is expanded against whatever CFLAGS
670+; already holds (the config.set default, or a -DCFLAGS=... override)
671+; at the moment the meta is applied, so these only append, they
672+; never have to repeat -std=c99 -Wall -Wextra -pedantic themselves
673+; -------------------------------------------------------------------
674+
675+(meta debug
676+ (set CFLAGS "$(CFLAGS) -g -O0"))
677+
678+(meta release
679+ (set CFLAGS "$(CFLAGS) -O2 -DNDEBUG"))
+0,
-98
1@@ -1,98 +0,0 @@
2-#!/bin/sh
3-# https://srcdump.net/shrub/shrubtools/tree/main/item/deps.html
4-#
5-# emit makefile dependencies for a list of C sources using the compiler's .d output. having a bunch of
6-# autogenerated .d files and -include's is flaky and ugly, and might not work on all make
7-# implementations. this way you can generate the deps once and ship the makefile.
8-# it takes a list of files from stdin and prints to stdout. you might also want to append
9-# you CFLAGS/CPPFLAGS if that will change the deps.
10-#
11-# example:
12-# find . -type f -name '*.c' | CPPFLAGS='-Iinclude' deps >> Makefile
13-#
14-set -eu
15-
16-cc=${CC:-cc}
17-cppflags=${CPPFLAGS:-}
18-cflags=${CFLAGS:-}
19-tmpbase=${TMPDIR:-/tmp}
20-
21-cleanup() {
22- rm -rf "$tmpdir"
23-}
24-
25-usage() {
26- printf 'usage: %s [file.c ...]\n' "${0##*/}" >&2
27- printf ' %s < file-list\n' "${0##*/}" >&2
28- exit 2
29-}
30-
31-list() {
32- if [ "$#" -gt 0 ]; then
33- for src do
34- case $src in
35- *.c) printf '%s\n' "$src" ;;
36- *) printf '%s: not a .c file: %s\n' "${0##*/}" "$src" >&2; exit 2 ;;
37- esac
38- done
39- return
40- fi
41-
42- while IFS= read -r src; do
43- [ -n "$src" ] || continue
44- case $src in
45- *.c) printf '%s\n' "$src" ;;
46- *) printf '%s: not a .c file: %s\n' "${0##*/}" "$src" >&2; exit 2 ;;
47- esac
48- done
49-}
50-
51-if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
52- usage
53-fi
54-
55-tmpdir=$(mktemp -d "$tmpbase/deps.XXXXXX")
56-trap cleanup EXIT HUP INT TERM
57-
58-count=0
59-srcs=$tmpdir/sources
60-list "$@" > "$srcs"
61-
62-if [ ! -s "$srcs" ]; then
63- printf '%s: no .c files provided\n' "${0##*/}" >&2
64- exit 1
65-fi
66-
67-printf '\n'
68-printf '#AUTOGENERATED\n'
69-
70-while IFS= read -r src; do
71- dep=$tmpdir/$count.d
72- obj=${src%.c}.o
73- "$cc" $cppflags $cflags -MMD -MF "$dep" -MT "$obj" -E -o /dev/null "$src"
74- #flatten the block with one target: dep line per prereq
75- awk '
76- BEGIN {
77- rule = ""
78- }
79- {
80- line = $0
81- sub(/\\$/, "", line)
82- rule = rule line " "
83- if ($0 !~ /\\$/) {
84- sub(/^[[:space:]]*/, "", rule)
85- split(rule, parts, ":")
86- target = parts[1]
87- deps = substr(rule, length(target) + 2)
88- n = split(deps, items, /[[:space:]]+/)
89- for (i = 1; i <= n; ++i) {
90- if (items[i] == "")
91- continue
92- print target ": " items[i]
93- }
94- rule = ""
95- }
96- }
97- ' "$dep"
98- count=$((count + 1))
99-done < "$srcs"
+186,
-0
1@@ -0,0 +1,186 @@
2+#!/bin/sh
3+# genbox.sh - generate the aruu-box dispatch table
4+#
5+# does one thing: given a list of object file paths on argv, each one
6+# of BOX_OBJ's own outputs (".box/cmd/<category>/<tool>.o"), emit the C
7+# source for aruu-box's dispatch table and main() on stdout
8+#
9+set -e
10+
11+[ $# -gt 0 ] || {
12+ printf "genbox: no object paths given\n" >&2
13+ exit 1
14+}
15+
16+# derive category, tool basestem, and C symbol from one BOX_OBJ output
17+# path. the symbol transform (tr '.-' '__', append _main) must match
18+# BOX_OBJ's own objcopy step exactly, see the CATEGORY_OF/symbol
19+# comment in ninqu.rules for why this is a shell transform and not a
20+# map
21+path_category() {
22+ rest=${1#.box/cmd/}
23+ printf "%s\n" "${rest%%/*}"
24+}
25+
26+path_tool() {
27+ rest=${1##*/}
28+ printf "%s\n" "${rest%.o}"
29+}
30+
31+path_symbol() {
32+ tool=$(path_tool "$1")
33+ printf "%s\n" "$(echo "$tool" | tr '.-' '__')_main"
34+}
35+
36+cat << 'HEAD'
37+/* this file is autogenerated by genbox.sh. do not edit */
38+
39+#include "wexec.h"
40+#include "util.h"
41+
42+#include <stdio.h>
43+#include <stdlib.h>
44+#include <string.h>
45+#include <unistd.h>
46+
47+HEAD
48+
49+# extern decls, in the order ninqu passed the objects in (BOX_OBJ's
50+# own glob order, one category at a time)
51+for obj in "$@"; do
52+ sym=$(path_symbol "$obj")
53+ echo "int ${sym}(int, char **);"
54+done
55+
56+cat << 'MID'
57+
58+struct Cmd {
59+ const char *name;
60+ int (*fn)(int, char **);
61+ const char *category;
62+};
63+
64+static struct Cmd cmds[] = {
65+MID
66+
67+categories=$(
68+ for obj in "$@"; do
69+ path_category "$obj"
70+ done | sort -u
71+)
72+
73+for category in $categories; do
74+ for obj in "$@"; do
75+ [ "$(path_category "$obj")" = "$category" ] || continue
76+ tool=$(path_tool "$obj")
77+ sym=$(path_symbol "$obj")
78+ printf '\t{ "%s", %s, "%s" },\n' "$tool" "$sym" "$category"
79+ if [ "$tool" = "test" ]; then
80+ printf '\t{ "[", %s, "%s" },\n' "$sym" "$category"
81+ fi
82+ if [ "$tool" = "xinstall" ]; then
83+ printf '\t{ "install", %s, "%s" },\n' "$sym" "$category"
84+ fi
85+ done
86+done
87+
88+cat << 'TAIL'
89+ { NULL, NULL, NULL }
90+};
91+
92+static void
93+usage(void)
94+{
95+ struct Cmd *c;
96+ const char *last;
97+ int col;
98+
99+ last = NULL;
100+ col = 0;
101+ fprintf(stderr, "usage: %s [-i path] [command [args...]]\n", argv0);
102+ for (c = cmds; c->name; c++) {
103+ if (!last || strcmp(c->category, last) != 0) {
104+ if (last)
105+ fprintf(stderr, "\n\n");
106+ last = c->category;
107+ fprintf(stderr, "%s:\n", last);
108+ col = 0;
109+ }
110+ if (col == 0) {
111+ fprintf(stderr, "\t%s", c->name);
112+ col = 8 + strlen(c->name);
113+ } else if (col + 2 + strlen(c->name) >= 78) {
114+ fprintf(stderr, ",\n\t%s", c->name);
115+ col = 8 + strlen(c->name);
116+ } else {
117+ fprintf(stderr, ", %s", c->name);
118+ col += 2 + strlen(c->name);
119+ }
120+ }
121+ if (last)
122+ fputc('\n', stderr);
123+ exit(1);
124+}
125+
126+static void
127+x_install(const char *path)
128+{
129+ struct Cmd *c;
130+ char buf[4096];
131+ int r;
132+
133+ for (c = cmds; c->name; c++) {
134+ r = snprintf(buf, sizeof buf, "%s/%s", path, c->name);
135+ if (r < 0 || r >= (int)sizeof buf)
136+ eprintf("install: '%s/%s': path too long\n", path, c->name);
137+ remove(buf);
138+ if (symlink("aruu-box", buf) < 0)
139+ eprintf("install: symlink '%s':", buf);
140+ }
141+}
142+
143+int
144+main(int argc, char *argv[])
145+{
146+ struct Cmd *c;
147+ char *s;
148+
149+ argv0 = argv[0];
150+
151+ for (c = cmds; c->name; c++)
152+ wexec_register(c->name, c->fn);
153+
154+ s = strrchr(argv[0], '/');
155+ if (s)
156+ s++;
157+ else
158+ s = argv[0];
159+
160+ if (strcmp(s, "aruu-box") == 0) {
161+ if (argc < 2)
162+ usage();
163+ argc--;
164+ argv++;
165+ if (strcmp(argv[0], "-i") == 0) {
166+ if (argc < 2)
167+ eprintf("usage: aruu-box -i path\n");
168+ x_install(argv[1]);
169+ return 0;
170+ }
171+ s = strrchr(argv[0], '/');
172+ if (s)
173+ s++;
174+ else
175+ s = argv[0];
176+ }
177+ for (c = cmds; c->name; c++) {
178+ if (strcmp(c->name, s) == 0) {
179+ argv0 = (char *)c->name;
180+ argv[0] = (char *)c->name;
181+ return c->fn(argc, argv);
182+ }
183+ }
184+ enprintf(127, "%s: not found\n", s);
185+ return 1;
186+}
187+TAIL
+272,
-332
1@@ -1,11 +1,36 @@
2 #!/bin/sh
3-# genconfig.sh - generate config.mk and config.kv from build.cfg
4-
5+# genconfig.sh generates build configuration from build.cfg
6+#
7+# outputs four files:
8+#
9+# scripts/mk/config.set ninqu reads this via (set-file). flat
10+# KEY=VAL, unquoted. per-tool BUILD_CATEGORY_TOOL
11+# toggles get a lowercase alias here, since
12+# (gate ...) in ninqu.rules only ever checks
13+# the lowercase form
14+#
15+# scripts/mk/config.kv shell-quoted KEY="VAL" format. sourced by
16+# read via the -config flag by scripts/mkman.
17+# nothing in the ninqu build itself sources
18+# this file: genbox.sh takes its input on argv
19+# only, see the BOX section of ninqu.rules
20+#
21+# shared/libutil/nofork_list.h
22+# wexec.c includes this at compile time.
23+# generated from NOFORK_LIST in build.cfg
24+#
25+# scripts/mk/features.h every resolved FEATURE_* as a #define
26+# CPPFLAGS pulls this in with -include
27+#
28+# the codegen subcommands (getconf, bc, awk, sh) are also here so
29+# ninqu.rules can call (exec sh scripts/genconfig.sh <step>) for
30+# each one. they check mtimes internally and only regenerate when
31+# their input changed
32+#
33 set -e
34
35 target=${1:-config}
36
37-# load build.cfg
38 if [ -f build.cfg ]; then
39 . ./build.cfg
40 else
41@@ -13,196 +38,275 @@ else
42 exit 1
43 fi
44
45-# helper to check if source is newer than target
46 newer_than() {
47- [ -e "$2" ] || return 0
48- local s t
49- s=$(stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1" 2>/dev/null || echo 0)
50- t=$(stat -c '%Y' "$2" 2>/dev/null || stat -f '%m' "$2" 2>/dev/null || echo 0)
51- [ "$s" -gt "$t" ]
52+ [ -e "$2" ] || return 0
53+ local s t
54+ s=$(stat -c '%Y' "$1" 2>/dev/null || stat -f '%m' "$1" 2>/dev/null || echo 0)
55+ t=$(stat -c '%Y' "$2" 2>/dev/null || stat -f '%m' "$2" 2>/dev/null || echo 0)
56+ [ "$s" -gt "$t" ]
57 }
58
59 gen_getconf() {
60- if newer_than scripts/getconf.sh cmd/posix/getconf.h; then
61- printf " GEN cmd/posix/getconf.h\n"
62- scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
63- fi
64+ if newer_than scripts/getconf.sh cmd/posix/getconf.h; then
65+ printf " GEN cmd/posix/getconf.h\n"
66+ scripts/getconf.sh > cmd/posix/getconf.h || { rm -f cmd/posix/getconf.h; exit 1; }
67+ fi
68 }
69
70 gen_bc() {
71- if newer_than cmd/posix/bc.y cmd/posix/bc.c; then
72- printf " GEN cmd/posix/bc.c\n"
73- CC=${CC:-cc}
74- YACC=${YACC:-}
75- if [ -z "$YACC" ]; then
76- if command -v yacc >/dev/null 2>&1; then
77- YACC="yacc"
78- elif command -v bison >/dev/null 2>&1; then
79- YACC="bison -y"
80- else
81- YACC="yacc"
82- fi
83- fi
84- if [ "$YACC" = "bison -y" ]; then
85- bison -o cmd/posix/bc.c cmd/posix/bc.y
86- else
87- $YACC -o cmd/posix/bc.c cmd/posix/bc.y
88- fi
89- fi
90+ if newer_than cmd/posix/bc.y cmd/posix/bc.c; then
91+ printf " GEN cmd/posix/bc.c\n"
92+ CC=${CC:-cc}
93+ YACC=${YACC:-}
94+ if [ -z "$YACC" ]; then
95+ if command -v yacc >/dev/null 2>&1; then
96+ YACC="yacc"
97+ elif command -v bison >/dev/null 2>&1; then
98+ YACC="bison -y"
99+ else
100+ YACC="yacc"
101+ fi
102+ fi
103+ if [ "$YACC" = "bison -y" ]; then
104+ bison -o cmd/posix/bc.c cmd/posix/bc.y
105+ else
106+ $YACC -o cmd/posix/bc.c cmd/posix/bc.y
107+ fi
108+ fi
109 }
110
111 gen_awk() {
112- local dir="cmd/posix/awk"
113- CC=${CC:-cc}
114- YACC=${YACC:-}
115- if [ -z "$YACC" ]; then
116- if command -v yacc >/dev/null 2>&1; then
117- YACC="yacc"
118- elif command -v bison >/dev/null 2>&1; then
119- YACC="bison -y"
120- else
121- YACC="yacc"
122- fi
123- fi
124-
125- if newer_than "$dir/awkgram.y" "$dir/awkgram.tab.c" || newer_than "$dir/awkgram.y" "$dir/awkgram.tab.h"; then
126- printf " YACC $dir/awkgram.tab.c\n"
127- if [ "$YACC" = "bison -y" ]; then
128- bison -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
129- else
130- $YACC -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
131- if [ ! -f "$dir/awkgram.tab.h" ]; then
132- if [ -f y.tab.h ]; then
133- mv y.tab.h "$dir/awkgram.tab.h"
134- elif [ -f "$dir/y.tab.h" ]; then
135- mv "$dir/y.tab.h" "$dir/awkgram.tab.h"
136+ local dir="cmd/posix/awk"
137+ CC=${CC:-cc}
138+ YACC=${YACC:-}
139+ if [ -z "$YACC" ]; then
140+ if command -v yacc >/dev/null 2>&1; then
141+ YACC="yacc"
142+ elif command -v bison >/dev/null 2>&1; then
143+ YACC="bison -y"
144+ else
145+ YACC="yacc"
146+ fi
147+ fi
148+
149+ if newer_than "$dir/awkgram.y" "$dir/awkgram.tab.c" || newer_than "$dir/awkgram.y" "$dir/awkgram.tab.h"; then
150+ printf " YACC $dir/awkgram.tab.c\n"
151+ if [ "$YACC" = "bison -y" ]; then
152+ bison -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
153+ else
154+ $YACC -d -o "$dir/awkgram.tab.c" "$dir/awkgram.y"
155+ if [ ! -f "$dir/awkgram.tab.h" ]; then
156+ if [ -f y.tab.h ]; then
157+ mv y.tab.h "$dir/awkgram.tab.h"
158+ elif [ -f "$dir/y.tab.h" ]; then
159+ mv "$dir/y.tab.h" "$dir/awkgram.tab.h"
160+ fi
161+ fi
162+ fi
163+ fi
164+
165+ if newer_than "$dir/maketab.c" "$dir/maketab" || newer_than "$dir/awkgram.tab.h" "$dir/maketab"; then
166+ printf " CC $dir/maketab\n"
167+ $CC -o "$dir/maketab" "$dir/maketab.c"
168+ fi
169+
170+ if newer_than "$dir/maketab" "$dir/proctab.c" || newer_than "$dir/awkgram.tab.h" "$dir/proctab.c"; then
171+ printf " GEN $dir/proctab.c\n"
172+ "$dir/maketab" "$dir/awkgram.tab.h" > "$dir/proctab.c"
173 fi
174- fi
175- fi
176- fi
177-
178- if newer_than "$dir/maketab.c" "$dir/maketab" || newer_than "$dir/awkgram.tab.h" "$dir/maketab"; then
179- printf " CC $dir/maketab\n"
180- $CC -o "$dir/maketab" "$dir/maketab.c"
181- fi
182-
183- if newer_than "$dir/maketab" "$dir/proctab.c" || newer_than "$dir/awkgram.tab.h" "$dir/proctab.c"; then
184- printf " GEN $dir/proctab.c\n"
185- "$dir/maketab" "$dir/awkgram.tab.h" > "$dir/proctab.c"
186- fi
187 }
188
189 gen_sh() {
190- local dir="cmd/posix/sh"
191- CC=${CC:-cc}
192-
193- if newer_than "$dir/mknodes.c" "$dir/mknodes"; then
194- printf " CC $dir/mknodes\n"
195- $CC -o "$dir/mknodes" "$dir/mknodes.c"
196- fi
197-
198- if newer_than "$dir/mksyntax.c" "$dir/mksyntax"; then
199- printf " CC $dir/mksyntax\n"
200- $CC -Ishared -I"$dir" -o "$dir/mksyntax" "$dir/mksyntax.c"
201- fi
202-
203- if newer_than "$dir/mktokens" "$dir/token.h"; then
204- printf " GEN $dir/token.h\n"
205- (cd "$dir" && sh mktokens)
206- fi
207-
208- if newer_than "$dir/mksyntax" "$dir/syntax.c" || newer_than "$dir/mksyntax" "$dir/syntax.h"; then
209- printf " GEN $dir/syntax.c\n"
210- (cd "$dir" && ./mksyntax)
211- fi
212-
213- 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
214- printf " GEN $dir/nodes.c\n"
215- (cd "$dir" && ./mknodes nodetypes nodes.c.pat)
216- fi
217-
218- 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
219- printf " GEN $dir/builtins.c\n"
220- (cd "$dir" && sh mkbuiltins .)
221- fi
222+ local dir="cmd/posix/sh"
223+ CC=${CC:-cc}
224+
225+ if newer_than "$dir/mknodes.c" "$dir/mknodes"; then
226+ printf " CC $dir/mknodes\n"
227+ $CC -o "$dir/mknodes" "$dir/mknodes.c"
228+ fi
229+
230+ if newer_than "$dir/mksyntax.c" "$dir/mksyntax"; then
231+ printf " CC $dir/mksyntax\n"
232+ $CC -Ishared -I"$dir" -o "$dir/mksyntax" "$dir/mksyntax.c"
233+ fi
234+
235+ if newer_than "$dir/mktokens" "$dir/token.h"; then
236+ printf " GEN $dir/token.h\n"
237+ (cd "$dir" && sh mktokens)
238+ fi
239+
240+ if newer_than "$dir/mksyntax" "$dir/syntax.c" || newer_than "$dir/mksyntax" "$dir/syntax.h"; then
241+ printf " GEN $dir/syntax.c\n"
242+ (cd "$dir" && ./mksyntax)
243+ fi
244+
245+ 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
246+ printf " GEN $dir/nodes.c\n"
247+ (cd "$dir" && ./mknodes nodetypes nodes.c.pat)
248+ fi
249+
250+ 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
251+ printf " GEN $dir/builtins.c\n"
252+ (cd "$dir" && sh mkbuiltins .)
253+ fi
254 }
255
256 clean() {
257- printf " CLEAN generated files\n"
258- rm -f cmd/posix/getconf.h
259- rm -f cmd/posix/bc.c cmd/posix/bc.h
260- rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proctab.c cmd/posix/awk/maketab
261- 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
262- rm -f scripts/mk/config.mk scripts/mk/config.kv scripts/mk/rules.mk shared/libutil/nofork_list.h
263+ printf " CLEAN generated files\n"
264+ rm -f cmd/posix/getconf.h
265+ rm -f cmd/posix/bc.c cmd/posix/bc.h
266+ rm -f cmd/posix/awk/awkgram.tab.c cmd/posix/awk/awkgram.tab.h cmd/posix/awk/proctab.c cmd/posix/awk/maketab
267+ 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
268+ rm -f scripts/mk/config.set scripts/mk/config.kv scripts/mk/features.h shared/libutil/nofork_list.h
269 }
270
271-# dynamic tool discovery function
272-list_tools() {
273- local catg="$1" dirs="" files="" d f b
274- [ -d "cmd/$catg" ] || return 0
275- # find all subdirectories
276- for d in "cmd/$catg"/*; do
277- if [ -d "$d" ]; then
278- dirs="$dirs ${d##*/}"
279- fi
280- done
281- # find all .c and .y files
282- for f in "cmd/$catg"/*.c "cmd/$catg"/*.y; do
283- [ -f "$f" ] || continue
284- b=${f##*/}
285- b=${b%.c}
286- b=${b%.y}
287- case "$b" in
288- # exclude intermediate files or helper generators
289- maketab|mknodes|mksyntax|y.tab|*gram.tab) continue ;;
290- esac
291- files="$files $b"
292+# config: generate config.set, config.kv, nofork_list.h
293+#
294+# config.set is what ninqu reads. config.kv is what mkbox and
295+# genbox.sh source. both contain the same data in different formats:
296+# config.kv is shell-quoted (KEY="VAL"), config.set is unquoted
297+# (KEY=VAL) since ninqu's (set-file) takes the rest of the line
298+# verbatim
299+
300+config() {
301+ mkdir -p scripts/mk
302+
303+ kv_file="scripts/mk/config.kv"
304+ set_file="scripts/mk/config.set"
305+
306+ : > "${kv_file}"
307+ : > "${set_file}"
308+
309+ emit() {
310+ local k="$1" v="$2"
311+ echo "${k}=\"${v}\"" >> "${kv_file}"
312+ echo "${k}=${v}" >> "${set_file}"
313+ }
314+
315+ emit VERSION "${VERSION}"
316+ emit PREFIX "${PREFIX}"
317+ emit MANPREFIX "${MANPREFIX:-/usr/local/share/man}"
318+ emit RANLIB "${RANLIB:-ranlib}"
319+ emit AR "${AR:-ar}"
320+ emit ARFLAGS "${ARFLAGS:-rc}"
321+ emit CC "${CC:-cc}"
322+ emit CFLAGS "${CFLAGS:--std=c99 -Wall -Wextra -pedantic}"
323+ emit LDFLAGS "${LDFLAGS:-} ${LDFLAGS_TLS:-}"
324+ emit LDLIBS "${LDLIBS:-} ${LDLIBS_TLS:-}"
325+ emit OBJCOPY "${OBJCOPY:-objcopy}"
326+ emit LD "${LD:-ld}"
327+
328+ emit CPPFLAGS_TLS "${CPPFLAGS_TLS:-}"
329+ emit LDFLAGS_TLS "${LDFLAGS_TLS:-}"
330+ emit LDLIBS_TLS "${LDLIBS_TLS:-}"
331+
332+ # these seven are only consulted per-tool below (build_CATEGORY_TOOL,
333+ # build_CATEGORY_$(BASESTEM)), the bare category toggle has no gate
334+ # anywhere in ninqu.rules, (group CATEGORY ...) already does that
335+ # job. config.kv still gets the uppercase copy since genbox.sh
336+ # and mkbox source it directly
337+ for g in BUILD_POSIX BUILD_XSI BUILD_NET BUILD_PSEUDO BUILD_EXTRA BUILD_LINUX BUILD_DEV; do
338+ eval "val=\${$g:-0}"
339+ echo "${g}=\"${val}\"" >> "${kv_file}"
340 done
341
342- # output sorted unique list of tool names
343- # shellcheck disable=SC2086
344- printf '%s %s\n' $dirs $files | tr ' ' '\n' | sort -u | grep -v '^$'
345-}
346+ # per-tool toggles: scan cmd/<category>/ for tools. a tool is
347+ # either a .c/.y file directly in the category dir (perfile)
348+ # or a subdirectory (multi-file tool). emit BUILD_<CATEGORY>_<TOOL>
349+ # plus lowercase alias for each
350+ for category in posix xsi net pseudo extra linux dev; do
351+ [ -d "cmd/$category" ] || continue
352+ u_category=$(echo "$category" | tr '[:lower:]' '[:upper:]')
353+
354+ tools=""
355+ for d in "cmd/$category"/*; do
356+ [ -d "$d" ] && tools="$tools ${d##*/}"
357+ done
358+ for f in "cmd/$category"/*.c "cmd/$category"/*.y; do
359+ [ -f "$f" ] || continue
360+ b=${f##*/}
361+ b=${b%.c}
362+ b=${b%.y}
363+ case "$b" in
364+ maketab|mknodes|mksyntax|y.tab|*gram.tab) continue ;;
365+ esac
366+ tools="$tools $b"
367+ done
368
369-write_subtarget() {
370- group="$1"
371- tool="$2"
372- u_group=$(echo "$group" | tr '[:lower:]' '[:upper:]')
373- u_tool=$(echo "$tool" | tr 'a-z-' 'A-Z_')
374- var_name="BUILD_${u_group}_${u_tool}"
375+ tools=$(echo "$tools" | tr ' ' '\n' | sort -u | grep -v '^$' || true)
376
377- eval "has_override=\${$var_name+yes}"
378- eval "val=\${$var_name:-}"
379+ for b in $tools; do
380+ u_tool=$(echo "$b" | tr 'a-z-' 'A-Z_')
381+ var_name="BUILD_${u_category}_${u_tool}"
382
383- eval "group_val=\$BUILD_${u_group}"
384+ #if [ "$category" = "dev" ] && [ "$u_tool" = "AS" ]; then
385+ # var_name="BUILD_DEV_CC"
386+ #fi
387
388- if [ "$has_override" = "yes" ]; then
389- echo "${var_name} = ${val}" >> "${mk_file}"
390- echo "${var_name}=\"${val}\"" >> "${kv_file}"
391- else
392- echo "${var_name} = \$(BUILD_${u_group})" >> "${mk_file}"
393- echo "${var_name}=\"${group_val}\"" >> "${kv_file}"
394- fi
395-}
396+ eval "has_override=\${$var_name+yes}"
397+ if [ "$has_override" = "yes" ]; then
398+ eval "val=\${$var_name}"
399+ else
400+ eval "val=\$BUILD_${u_category}"
401+ fi
402
403-gen_dir_rules() {
404- local dir="$1" flags="$2" extra_files="$3" f obj files uniq_files
405- [ -d "$dir" ] || return 0
406- files=""
407- for f in "$dir"/*.c; do
408- if [ -f "$f" ]; then
409- files="$files $f"
410- fi
411+ echo "${var_name}=\"${val}\"" >> "${kv_file}"
412+ lk=$(echo "$var_name" | tr '[:upper:]' '[:lower:]')
413+ echo "${lk}=${val}" >> "${set_file}"
414+ done
415 done
416- for f in ${extra_files}; do
417- files="$files $f"
418+
419+ # box's own link line has no per-tool composition the way a
420+ # standalone PERFILE binary's EXTRA_LIBS map does (box links
421+ # every enabled tool's object into one binary in one shot), so
422+ # a tool that needs a library nothing else in the box needs
423+ # (yap needs terminfo) can only get it by ninqu.rules reading
424+ # a value genconfig.sh already resolved, same as every other
425+ # build_* toggle. this is still data, not policy: which
426+ # library to add for which tool is decided here, not what to
427+ # do with that decision
428+ box_extra_libs=""
429+ [ "${yap_val:-0}" != "0" ] && box_extra_libs="-ltinfo"
430+ emit BOX_EXTRA_LIBS "${box_extra_libs}"
431+
432+ features=$(set | grep '^FEATURE_[A-Za-z0-9_]*=' | cut -d= -f1 | sort)
433+ for f in $features; do
434+ eval "val=\$$f"
435+ emit "$f" "$val"
436 done
437- uniq_files=$(echo "$files" | tr ' ' '\n' | sort -u | grep -v '^$')
438- for f in $uniq_files; do
439- obj="${f%.c}.o"
440- echo "${obj}: ${f}" >> "${rules_file}"
441- echo " \$(CC) \$(CPPFLAGS) ${flags} \$(CFLAGS) -o \$@ -c ${f}" >> "${rules_file}"
442+
443+ # every resolved FEATURE_* becomes a #define in one generated
444+ # header. config.h's own #ifndef guards still apply to anything
445+ # not here (there is nothing not here), and any file gets the
446+ # real values just by being compiled with -include, whether or
447+ # not it bothers to #include "config.h" itself.
448+ features_h="scripts/mk/features.h"
449+ {
450+ echo "/* autogenerated by genconfig.sh, do not edit */"
451+ for f in $features; do
452+ eval "fval=\$$f"
453+ echo "#define ${f} ${fval}"
454+ done
455+ } > "${features_h}"
456+
457+ 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:-}"
458+ cppflags_set="-Ishared ${cppflags_common}"
459+ echo "CPPFLAGS=\"${cppflags_set}\"" >> "${kv_file}"
460+ echo "CPPFLAGS=${cppflags_set}" >> "${set_file}"
461+
462+ # shared/tls.h would conflict with the system's tls.h
463+ # if we passed -Ishared to build libutil
464+ echo "CPPFLAGS_LIBUTIL=\"${cppflags_common}\"" >> "${kv_file}"
465+ echo "CPPFLAGS_LIBUTIL=${cppflags_common}" >> "${set_file}"
466+
467+ nofork_h="shared/libutil/nofork_list.h"
468+ echo "/* autogenerated by genconfig.sh */" > "${nofork_h}"
469+ echo "static const char *nofork_list[] = {" >> "${nofork_h}"
470+ for app in ${NOFORK_LIST}; do
471+ echo " \"${app}\"," >> "${nofork_h}"
472 done
473+ echo " NULL" >> "${nofork_h}"
474+ echo "};" >> "${nofork_h}"
475 }
476
477 case "$target" in
478@@ -210,181 +314,17 @@ case "$target" in
479 bc) gen_bc ;;
480 awk) gen_awk ;;
481 sh) gen_sh ;;
482- clean) clean ;;
483 all)
484 gen_getconf
485 gen_bc
486 gen_awk
487 gen_sh
488 ;;
489- config)
490- mkdir -p scripts/mk
491-
492- mk_file="scripts/mk/config.mk"
493- kv_file="scripts/mk/config.kv"
494-
495- # clear existing files
496- : > "${mk_file}"
497- : > "${kv_file}"
498-
499- # write version prefix manprefix
500- echo "VERSION = ${VERSION}" >> "${mk_file}"
501- echo "VERSION=\"${VERSION}\"" >> "${kv_file}"
502-
503- echo "PREFIX = ${PREFIX}" >> "${mk_file}"
504- echo "PREFIX=\"${PREFIX}\"" >> "${kv_file}"
505-
506- # default manprefix to prefix/share/man if empty
507- man_pref="${MANPREFIX:-/usr/local/share/man}"
508- echo "MANPREFIX = ${man_pref}" >> "${mk_file}"
509- echo "MANPREFIX=\"${man_pref}\"" >> "${kv_file}"
510-
511- # toolchain defaults
512- echo "RANLIB = ${RANLIB:-ranlib}" >> "${mk_file}"
513- echo "RANLIB=\"${RANLIB:-ranlib}\"" >> "${kv_file}"
514-
515- echo "AR = ${AR:-ar}" >> "${mk_file}"
516- echo "AR=\"${AR:-ar}\"" >> "${kv_file}"
517-
518- echo "ARFLAGS = ${ARFLAGS:-rc}" >> "${mk_file}"
519- echo "ARFLAGS=\"${ARFLAGS:-rc}\"" >> "${kv_file}"
520-
521- # build toggles
522- echo "" >> "${mk_file}"
523- echo "# build toggles" >> "${mk_file}"
524- for g in BUILD_POSIX BUILD_XSI BUILD_NET BUILD_PSEUDO BUILD_EXTRA BUILD_LINUX BUILD_DEV; do
525- eval "val=\${$g:-0}"
526- echo "$g = $val" >> "${mk_file}"
527- echo "$g=\"$val\"" >> "${kv_file}"
528- done
529-
530- # write subtargets for each category dynamically
531- for catg in posix xsi net pseudo extra linux dev; do
532- echo "" >> "${mk_file}"
533- echo "# ${catg} subtargets" >> "${mk_file}"
534- tools=$(list_tools "$catg")
535- for t in $tools; do
536- write_subtarget "$catg" "$t"
537- done
538-
539- # build the enabled list using lowercase tools
540- enabled=""
541- for t in $tools; do
542- u_group=$(echo "$catg" | tr '[:lower:]' '[:upper:]')
543- u_tool=$(echo "$t" | tr 'a-z-' 'A-Z_')
544-
545- # special cases for dev tools mapping to different variables
546- if [ "$catg" = "dev" ] && [ "$u_tool" = "AS" ]; then
547- var_name="BUILD_DEV_CC"
548- else
549- var_name="BUILD_${u_group}_${u_tool}"
550- fi
551-
552- eval "has_override=\${$var_name+yes}"
553- eval "val=\${$var_name:-}"
554- if [ "$has_override" != "yes" ]; then
555- if [ "$catg" = "dev" ] && [ "$u_tool" = "AS" ]; then
556- eval "val=\$BUILD_DEV"
557- else
558- eval "val=\$BUILD_${u_group}"
559- fi
560- fi
561-
562- if [ "$val" = "1" ]; then
563- # map to the correct path
564- case "$catg/$t" in
565- posix/awk) path="cmd/posix/awk/awk" ;;
566- posix/sh) path="cmd/posix/sh/sh" ;;
567- posix/make) path="cmd/posix/make/make" ;;
568- extra/yap) path="cmd/extra/yap/yap" ;;
569- dev/ar) path="cmd/dev/ar/ar" ;;
570- dev/as) path="cmd/dev/as/as" ;;
571- dev/ld) path="cmd/dev/ld/ld" ;;
572- dev/cc) path="cmd/dev/cc/cc" ;;
573- *) path="cmd/$catg/$t" ;;
574- esac
575- enabled="$enabled $path"
576- fi
577- done
578- u_catg=$(echo "$catg" | tr '[:lower:]' '[:upper:]')
579- # write the list of enabled binaries
580- echo "${u_catg}_BIN_CONFIG =${enabled}" >> "${mk_file}"
581- done
582-
583- # write feature flags
584- echo "" >> "${mk_file}"
585- echo "# feature flags" >> "${mk_file}"
586- features=$(set | grep '^FEATURE_[A-Za-z0-9_]*=' | cut -d= -f1 | sort)
587- for f in $features; do
588- eval "val=\$$f"
589- echo "$f = $val" >> "${mk_file}"
590- echo "$f=\"$val\"" >> "${kv_file}"
591- done
592-
593- # write TLS variables to config files
594- echo "" >> "${mk_file}"
595- echo "CPPFLAGS_TLS = ${CPPFLAGS_TLS}" >> "${mk_file}"
596- echo "CPPFLAGS_TLS=\"${CPPFLAGS_TLS}\"" >> "${kv_file}"
597-
598- echo "LDFLAGS_TLS = ${LDFLAGS_TLS}" >> "${mk_file}"
599- echo "LDFLAGS_TLS=\"${LDFLAGS_TLS}\"" >> "${kv_file}"
600-
601- echo "LDLIBS_TLS = ${LDLIBS_TLS}" >> "${mk_file}"
602- echo "LDLIBS_TLS=\"${LDLIBS_TLS}\"" >> "${kv_file}"
603-
604- # write CPPFLAGS with all feature macros
605- echo "" >> "${mk_file}"
606- echo "CPPFLAGS =\\" >> "${mk_file}"
607- echo " -Ishared\\" >> "${mk_file}"
608- echo " -DPREFIX=\\\"\\\$(PREFIX)\\\"\\" >> "${mk_file}"
609- echo " -D_DEFAULT_SOURCE\\" >> "${mk_file}"
610- echo " -D_GNU_SOURCE\\" >> "${mk_file}"
611- echo " -D_NETBSD_SOURCE\\" >> "${mk_file}"
612- echo " -D_BSD_SOURCE\\" >> "${mk_file}"
613- echo " -D_XOPEN_SOURCE=700\\" >> "${mk_file}"
614- echo " -D_FILE_OFFSET_BITS=64\\" >> "${mk_file}"
615- for f in $features; do
616- if [ "$f" = "FEATURE_USE_SSL" ]; then
617- continue
618- fi
619- echo " -D$f=\$($f)\\" >> "${mk_file}"
620- done
621- echo " \$(CPPFLAGS_TLS)" >> "${mk_file}"
622-
623- # write toolchain configuration
624- echo "" >> "${mk_file}"
625- echo "CC = ${CC:-cc}" >> "${mk_file}"
626- echo "CFLAGS = ${CFLAGS:-}" >> "${mk_file}"
627- echo "LDFLAGS = ${LDFLAGS:-} \$(LDFLAGS_TLS)" >> "${mk_file}"
628- echo "LDLIBS = ${LDLIBS:-} \$(LDLIBS_TLS)" >> "${mk_file}"
629-
630- # write compilation rules for subdirectories that need special flags or nested structures
631- rules_file="scripts/mk/rules.mk"
632- echo "# autogenerated compilation rules" > "${rules_file}"
633-
634- echo "" >> "${rules_file}"
635- gen_dir_rules "cmd/posix/awk" "-Icmd/posix/awk" "cmd/posix/awk/awkgram.tab.c cmd/posix/awk/proctab.c"
636- gen_dir_rules "cmd/posix/sh" "-DSHELL -Icmd/posix/sh" "cmd/posix/sh/syntax.c cmd/posix/sh/nodes.c cmd/posix/sh/builtins.c"
637- gen_dir_rules "cmd/extra/yap" "" ""
638- gen_dir_rules "cmd/dev/ar" "-Icmd/dev/ar" ""
639- gen_dir_rules "cmd/dev/xcutil" "-Icmd/dev/xcutil" ""
640- gen_dir_rules "cmd/dev/ld" "-Icmd/dev/xcutil -Icmd/dev/ld" ""
641- gen_dir_rules "cmd/dev/as" "-Icmd/dev/xcutil -Icmd/dev/as" ""
642- gen_dir_rules "cmd/dev/as/arch/x64" "-Icmd/dev/xcutil -Icmd/dev/as -Icmd/dev/as/arch/x64" ""
643- gen_dir_rules "cmd/dev/cc" "-Icmd/dev/cc" ""
644-
645- # generate nofork_list.h
646- nofork_h="shared/libutil/nofork_list.h"
647- echo "/* autogenerated by genconfig.sh */" > "${nofork_h}"
648- echo "static const char *nofork_list[] = {" >> "${nofork_h}"
649- for app in ${NOFORK_LIST}; do
650- echo " \"${app}\"," >> "${nofork_h}"
651- done
652- echo " NULL" >> "${nofork_h}"
653- echo "};" >> "${nofork_h}"
654- ;;
655+ config) config ;;
656+ clean) clean ;;
657 *)
658- printf "unknown target: %s\n" "$target" >&2
659+ printf "genconfig: unknown target: %s\n" "$target" >&2
660+ printf "usage: %s (config|getconf|bc|awk|sh|all|clean)\n" "$0" >&2
661 exit 1
662 ;;
663 esac
+0,
-511
1@@ -1,511 +0,0 @@
2-#!/bin/sh
3-# mkbox - build aruu-box, the aruu multi-call binary
4-#
5-# expects CC CPPFLAGS CFLAGS LDFLAGS LDLIBS OBJCOPY YACC from the environment
6-# defaults match config.kv but can be overridden
7-#
8-# run from the source root (the Makefile does this automatically)
9-
10-set -e
11-
12-ROOT=$(CDPATH= cd "$(dirname "$0")/.." && pwd)
13-
14-# load key-value config if it exists
15-if [ -f "${ROOT}/scripts/mk/config.kv" ]; then
16- . "${ROOT}/scripts/mk/config.kv"
17-fi
18-
19-: "${CC:=cc}"
20-: "${CFLAGS:=-std=c99 -Wall -Wextra -pedantic}"
21-: "${LDFLAGS:=}"
22-: "${LDLIBS:=-lcrypt -lresolv}"
23-: "${OBJCOPY:=objcopy}"
24-: "${YACC:=yacc}"
25-
26-# build feature flag string from loaded config.kv variables
27-_feature_flags=""
28-for _f in $(set | grep '^FEATURE_[A-Za-z0-9_]*=' | cut -d= -f1 | sort); do
29- eval "_v=\$$_f"
30- _feature_flags="$_feature_flags -D${_f}=${_v}"
31-done
32-unset _f _v
33-
34-CPPFLAGS="-Ishared -DPREFIX=\\\"${PREFIX:-/usr/local}\\\" -D_DEFAULT_SOURCE -D_GNU_SOURCE -D_NETBSD_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64${_feature_flags} ${CPPFLAGS_TLS:-}"
35-
36-# -lm must be included if diff or awk is built
37-if [ "${BUILD_POSIX_DIFF:-0}" = "1" ] || [ "${BUILD_POSIX_AWK:-0}" = "1" ]; then
38- case "$LDLIBS" in
39- *-lm*) ;;
40- *) LDLIBS="${LDLIBS} -lm" ;;
41- esac
42-fi
43-# -ltinfo must be included if yap is built
44-if [ "${BUILD_EXTRA_YAP:-0}" = "1" ]; then
45- case "$LDLIBS" in
46- *-ltinfo*) ;;
47- *) LDLIBS="${LDLIBS} -ltinfo" ;;
48- esac
49-fi
50-
51-BDIR="${ROOT}/.box"
52-BOX="${ROOT}/aruu-box"
53-LIBREDLINE="${ROOT}/shared/libredline/libredline.a"
54-LIBUTIL="${ROOT}/shared/libutil/libutil.a"
55-LIBUTF="${ROOT}/shared/libutf/libutf.a"
56-
57-die() {
58- printf 'mkbox: %s\n' "$*" >&2
59- exit 1
60-}
61-
62-# Clean up build directory on exit or signal termination
63-trap 'rm -rf "${BDIR}"' EXIT INT QUIT TERM
64-
65-[ -f "${LIBREDLINE}" ] || die "libredline.a not found; run 'make' first"
66-[ -f "${LIBUTIL}" ] || die "libutil.a not found; run 'make' first"
67-[ -f "${LIBUTF}" ] || die "libutf.a not found; run 'make' first"
68-
69-[ -f "${ROOT}/cmd/posix/getconf.h" ] || {
70- printf 'mkbox: generating getconf.h\n'
71- "${ROOT}/scripts/getconf.sh" > "${ROOT}/cmd/posix/getconf.h" ||
72- die "scripts/getconf.sh failed"
73-}
74-
75-rm -rf "${BDIR}"
76-mkdir -p "${BDIR}"
77-
78-OBJS=""
79-DECL_F="${BDIR}/decls.inc"
80-: > "${DECL_F}"
81-for _c in posix linux net pseudo xsi extra; do
82- : > "${BDIR}/entries_${_c}.inc"
83-done
84-unset _c
85-
86-alias_entry() {
87- printf '\t{ "%s", %s_main, "%s" },\n' "$1" "$2" "$3" \
88- >> "${BDIR}/entries_${3}.inc"
89-}
90-
91-compile_tool() {
92- src="$1"
93- name="$2"
94- fn="$3"
95- catg="$4"
96- extra="${5:-}"
97- out="${BDIR}/${fn}.o"
98- symf="${BDIR}/${fn}.syms"
99-
100- printf 'mkbox: cc %s\n' "${name}"
101- eval "${CC} ${CPPFLAGS} ${extra} ${CFLAGS} -c \"\$src\" -o \"\$out\"" ||
102- die "compile failed: ${src}"
103- printf 'main %s_main\n' "${fn}" > "${symf}"
104- ${OBJCOPY} --redefine-syms="${symf}" "${out}" ||
105- die "objcopy failed: ${name}"
106- ${OBJCOPY} --keep-global-symbol="${fn}_main" "${out}" ||
107- die "objcopy localize failed: ${name}"
108- rm -f "${symf}"
109- OBJS="${OBJS} ${out}"
110- printf 'int %s_main(int, char **);\n' "${fn}" >> "${DECL_F}"
111- printf '\t{ "%s", %s_main, "%s" },\n' "${name}" "${fn}" "${catg}" \
112- >> "${BDIR}/entries_${catg}.inc"
113-}
114-
115-compile_make() {
116- mdir="${ROOT}/cmd/posix/make"
117- symf="${BDIR}/make.syms"
118- printf 'mkbox: cc make (multi)\n'
119- make_objs=""
120- for s in defaults parser posix rules; do
121- out="${BDIR}/make_${s}.o"
122- eval "${CC} ${CPPFLAGS} ${CFLAGS} -I\"\$mdir\" -c \"\$mdir/\$s.c\" -o \"\$out\"" ||
123- die "compile failed: make/${s}.c"
124- make_objs="${make_objs} ${out}"
125- done
126- out_main="${BDIR}/make_main.o"
127- eval "${CC} ${CPPFLAGS} ${CFLAGS} -I\"\$mdir\" -c \"\$mdir/main.c\" -o \"\${out_main}\"" ||
128- die "compile failed: make/main.c"
129- make_objs="${make_objs} ${out_main}"
130-
131- # Combined incremental link to resolve internal symbols cleanly
132- combined="${BDIR}/make_combined.o"
133- ${LD:-ld} -r -o "${combined}" ${make_objs} ||
134- die "ld -r failed: make"
135-
136- printf 'main make_main\n' > "${symf}"
137- ${OBJCOPY} --redefine-syms="${symf}" "${combined}" ||
138- die "objcopy failed: make"
139- ${OBJCOPY} --keep-global-symbol=make_main "${combined}" ||
140- die "objcopy localize failed: make"
141- rm -f "${symf}" ${make_objs}
142-
143- OBJS="${OBJS} ${combined}"
144- printf 'int make_main(int, char **);\n' >> "${DECL_F}"
145- printf '\t{ "make", make_main, "posix" },\n' >> "${BDIR}/entries_posix.inc"
146-}
147-
148-
149-compile_yap() {
150- ydir="${ROOT}/cmd/extra/yap"
151- symf="${BDIR}/yap.syms"
152- yap_objs=""
153-
154- [ -d "${ydir}" ] || return 0
155- printf 'mkbox: cc yap (multi)\n'
156- for src in "${ydir}"/*.c; do
157- [ -f "${src}" ] || continue
158- s=${src##*/}
159- s=${s%.c}
160- out="${BDIR}/yap_${s}.o"
161- eval "${CC} ${CPPFLAGS} ${CFLAGS} -c \"\$src\" -o \"\$out\"" ||
162- die "compile failed: yap/${s}.c"
163- yap_objs="${yap_objs} ${out}"
164- done
165-
166- combined="${BDIR}/yap_combined.o"
167- ${LD:-ld} -r -o "${combined}" ${yap_objs} ||
168- die "ld -r failed: yap"
169-
170- printf 'main yap_main\n' > "${symf}"
171- ${OBJCOPY} --redefine-syms="${symf}" "${combined}" ||
172- die "objcopy failed: yap"
173- ${OBJCOPY} --keep-global-symbol=yap_main "${combined}" ||
174- die "objcopy localize failed: yap"
175- rm -f "${symf}" ${yap_objs}
176-
177- OBJS="${OBJS} ${combined}"
178- printf 'int yap_main(int, char **);\n' >> "${DECL_F}"
179- printf '\t{ "yap", yap_main, "extra" },\n' >> "${BDIR}/entries_extra.inc"
180-}
181-
182-compile_bc() {
183- bdir="${ROOT}/cmd/posix"
184- symf="${BDIR}/bc.syms"
185-
186- printf 'mkbox: yacc bc/bc.y\n'
187- ${YACC} -o "${bdir}/bc.c" "${bdir}/bc.y" ||
188- die "yacc failed: bc/bc.y"
189-
190- out="${BDIR}/bc.o"
191- eval "${CC} ${CPPFLAGS} ${CFLAGS} -c \"${bdir}/bc.c\" -o \"${out}\"" ||
192- die "compile failed: bc/bc.c"
193-
194- printf 'main bc_main\n' > "${symf}"
195- ${OBJCOPY} --redefine-syms="${symf}" "${out}" ||
196- die "objcopy failed: bc"
197- ${OBJCOPY} --keep-global-symbol=bc_main "${out}" ||
198- die "objcopy localize failed: bc"
199- rm -f "${symf}" "${bdir}/bc.c"
200-
201- OBJS="${OBJS} ${out}"
202- printf 'int bc_main(int, char **);\n' >> "${DECL_F}"
203- printf '\t{ "bc", bc_main, "posix" },\n' >> "${BDIR}/entries_posix.inc"
204-}
205-
206-compile_awk() {
207- adir="${ROOT}/cmd/posix/awk"
208- symf="${BDIR}/awk.syms"
209- awk_objs=""
210-
211- printf 'mkbox: yacc awk/awkgram.y\n'
212- ${YACC} -d -o "${adir}/awkgram.tab.c" "${adir}/awkgram.y" ||
213- die "yacc failed: awk/awkgram.y"
214-
215- # maketab is a host code-generator, only needs CFLAGS, no CPPFLAGS
216- printf 'mkbox: cc awk/maketab\n'
217- ${CC} ${CFLAGS} -o "${BDIR}/awk_maketab" "${adir}/maketab.c" ||
218- die "compile failed: awk/maketab.c"
219-
220- printf 'mkbox: gen awk/proctab.c\n'
221- "${BDIR}/awk_maketab" "${adir}/awkgram.tab.h" > "${adir}/proctab.c" ||
222- die "maketab failed"
223-
224- for s in b main parse proctab tran lib run lex math; do
225- out="${BDIR}/awk_${s}.o"
226- eval "${CC} ${CPPFLAGS} -I\"\$adir\" ${CFLAGS} -c \"\$adir/${s}.c\" -o \"\$out\"" ||
227- die "compile failed: awk/${s}.c"
228- awk_objs="${awk_objs} ${out}"
229- done
230- out="${BDIR}/awk_awkgram.tab.o"
231- eval "${CC} ${CPPFLAGS} -I\"\$adir\" ${CFLAGS} -c \"\$adir/awkgram.tab.c\" -o \"\$out\"" ||
232- die "compile failed: awk/awkgram.tab.c"
233- awk_objs="${awk_objs} ${out}"
234-
235- combined="${BDIR}/awk_combined.o"
236- ${LD:-ld} -r -o "${combined}" ${awk_objs} ||
237- die "ld -r failed: awk"
238-
239- printf 'main awk_main\n' > "${symf}"
240- ${OBJCOPY} --redefine-syms="${symf}" "${combined}" ||
241- die "objcopy failed: awk"
242- ${OBJCOPY} --keep-global-symbol=awk_main "${combined}" ||
243- die "objcopy localize failed: awk"
244- rm -f "${symf}" ${awk_objs}
245-
246- OBJS="${OBJS} ${combined}"
247- printf 'int awk_main(int, char **);\n' >> "${DECL_F}"
248- printf '\t{ "awk", awk_main, "posix" },\n' >> "${BDIR}/entries_posix.inc"
249-}
250-
251-compile_sh() {
252- sdir="${ROOT}/cmd/posix/sh"
253- symf="${BDIR}/sh.syms"
254- sh_objs=""
255-
256- # mknodes and mksyntax are host code-generators, not part of the shell
257- printf 'mkbox: cc sh/mknodes\n'
258- ${CC} ${CFLAGS} -o "${BDIR}/sh_mknodes" "${sdir}/mknodes.c" ||
259- die "compile failed: sh/mknodes.c"
260-
261- printf 'mkbox: cc sh/mksyntax\n'
262- ${CC} ${CPPFLAGS} -I"${sdir}" ${CFLAGS} -o "${BDIR}/sh_mksyntax" "${sdir}/mksyntax.c" ||
263- die "compile failed: sh/mksyntax.c"
264-
265- printf 'mkbox: gen sh/nodes.{c,h}\n'
266- (cd "${sdir}" && "${BDIR}/sh_mknodes" nodetypes nodes.c.pat) ||
267- die "mknodes failed"
268-
269- printf 'mkbox: gen sh/syntax.{c,h}\n'
270- (cd "${sdir}" && "${BDIR}/sh_mksyntax") ||
271- die "mksyntax failed"
272-
273- printf 'mkbox: gen sh/builtins.{c,h}\n'
274- (cd "${sdir}" && sh mkbuiltins .) ||
275- die "mkbuiltins failed"
276-
277- printf 'mkbox: gen sh/token.h\n'
278- (cd "${sdir}" && sh mktokens) ||
279- die "mktokens failed"
280-
281- for s in alias arith_yacc arith_yylex cd easter echo error eval exec \
282- expand lineedit input jobs kill mail main memalloc \
283- miscbltin mystring options output parser printf redir \
284- show test trap var builtins nodes syntax; do
285- out="${BDIR}/sh_${s}.o"
286- eval "${CC} ${CPPFLAGS} -DSHELL -I\"\$sdir\" ${CFLAGS} -c \"\$sdir/${s}.c\" -o \"\$out\"" ||
287- die "compile failed: sh/${s}.c"
288- sh_objs="${sh_objs} ${out}"
289- done
290-
291- combined="${BDIR}/sh_combined.o"
292- ${LD:-ld} -r -o "${combined}" ${sh_objs} ||
293- die "ld -r failed: sh"
294-
295- printf 'main sh_main\n' > "${symf}"
296- ${OBJCOPY} --redefine-syms="${symf}" "${combined}" ||
297- die "objcopy failed: sh"
298- ${OBJCOPY} --keep-global-symbol=sh_main "${combined}" ||
299- die "objcopy localize failed: sh"
300- rm -f "${symf}" ${sh_objs}
301-
302- OBJS="${OBJS} ${combined}"
303- printf 'int sh_main(int, char **);\n' >> "${DECL_F}"
304- printf '\t{ "sh", sh_main, "posix" },\n' >> "${BDIR}/entries_posix.inc"
305-}
306-
307-process_dir() {
308- dir="$1"
309- catg="$2"
310- extra="${3:-}"
311- for src in "${dir}"/*.c; do
312- [ -f "${src}" ] || continue
313- name=${src##*/}
314- name=${name%.c}
315-
316- # bc.c is generated by compile_bc() (see below); it lands in
317- # this same directory but must not be picked up here too, or
318- # bc_main ends up defined twice and the final link fails
319- [ "${catg}" = "posix" ] && [ "${name}" = "bc" ] && continue
320-
321- u_catg=$(printf '%s' "${catg}" | tr '[:lower:]' '[:upper:]')
322- u_name=$(printf '%s' "${name}" | tr '[:lower:].-' '[:upper:]__')
323- var_name="BUILD_${u_catg}_${u_name}"
324- eval "val=\${$var_name:-0}"
325- [ "$val" = "1" ] || continue
326-
327- fn=$(printf '%s' "${name}" | tr '.-' '__')
328- compile_tool "${src}" "${name}" "${fn}" "${catg}" "${extra}"
329- if [ "${name}" = "test" ]; then
330- alias_entry "[" "test" "${catg}"
331- fi
332- if [ "${name}" = "xinstall" ]; then
333- alias_entry "install" "xinstall" "${catg}"
334- fi
335- done
336-}
337-
338-process_dir "${ROOT}/cmd/posix" "posix"
339-[ "${BUILD_POSIX_AWK:-0}" = "1" ] && compile_awk
340-[ "${BUILD_POSIX_BC:-0}" = "1" ] && compile_bc
341-[ "${BUILD_POSIX_SH:-0}" = "1" ] && compile_sh
342-[ "${BUILD_POSIX_MAKE:-0}" = "1" ] && compile_make
343-process_dir "${ROOT}/cmd/linux" "linux"
344-process_dir "${ROOT}/cmd/net" "net"
345-process_dir "${ROOT}/cmd/pseudo" "pseudo"
346-process_dir "${ROOT}/cmd/xsi" "xsi"
347-process_dir "${ROOT}/cmd/extra" "extra"
348-[ "${BUILD_EXTRA_YAP:-0}" = "1" ] && compile_yap
349-
350-DISP="${BDIR}/aruu-box.c"
351-printf 'mkbox: generating dispatch\n'
352-
353-escaped_license=$(tr -d '\r' < "${ROOT}/LICENSE" | sed 's/\\/\\\\/g; s/"/\\"/g; s/^/\"/; s/$/\\n\"/')
354-
355-cat > "${DISP}" << DISPATCH_HEAD
356-/* this software is released into the public domain, or
357- registered under the isc license, see ./license for details
358- 2026 xplshn anto@xplshn.com.ar */
359-
360-/* routes argv0 to the appropriate tool main */
361-#include "wexec.h"
362-#include "util.h"
363-
364-#include <stdio.h>
365-#include <stdlib.h>
366-#include <string.h>
367-#include <unistd.h>
368-
369-static const char license[] =
370-${escaped_license}
371-;
372-
373-extern char *argv0;
374-
375-DISPATCH_HEAD
376-
377-cat "${DECL_F}" >> "${DISP}"
378-
379-cat >> "${DISP}" << 'DISPATCH_STRUCT'
380-
381-struct Cmd {
382- const char *name;
383- int (*fn)(int, char **);
384- const char *cat;
385-};
386-
387-static struct Cmd cmds[] = {
388-DISPATCH_STRUCT
389-
390-# sort each category alphabetically by tool name at build time;
391-# entries have the form: \t{ "name", fn_main, "cat" },
392-# splitting on " makes field 2 the tool name
393-for _c in posix linux net pseudo xsi extra; do
394- sort -t'"' -k2,2 "${BDIR}/entries_${_c}.inc"
395-done >> "${DISP}"
396-unset _c
397-
398-cat >> "${DISP}" << 'DISPATCH_TAIL'
399- { NULL, NULL, NULL }
400-};
401-
402-static void
403-usage(void)
404-{
405- struct Cmd *c;
406- const char *last;
407- int col;
408-
409- last = NULL;
410- col = 0;
411- fprintf(stderr, "usage: %s [-i path] [--license | -l] [command [args...]]\n", argv0);
412- for (c = cmds; c->name; c++) {
413- if (!last || strcmp(c->cat, last) != 0) {
414- if (last)
415- fprintf(stderr, "\n\n");
416- last = c->cat;
417- fprintf(stderr, "%s:\n", last);
418- col = 0;
419- }
420- if (col == 0) {
421- fprintf(stderr, "\t%s", c->name);
422- col = 8 + strlen(c->name);
423- } else if (col + 2 + strlen(c->name) >= 78) {
424- fprintf(stderr, ",\n\t%s", c->name);
425- col = 8 + strlen(c->name);
426- } else {
427- fprintf(stderr, ", %s", c->name);
428- col += 2 + strlen(c->name);
429- }
430- }
431- if (last)
432- fputc('\n', stderr);
433- exit(1);
434-}
435-
436-static void
437-x_install(const char *path)
438-{
439- struct Cmd *c;
440- char buf[4096];
441- int r;
442-
443- for (c = cmds; c->name; c++) {
444- r = snprintf(buf, sizeof buf, "%s/%s", path, c->name);
445- if (r < 0 || r >= (int)sizeof buf)
446- eprintf("install: '%s/%s': path too long\n", path, c->name);
447- remove(buf);
448- if (symlink("aruu-box", buf) < 0)
449- eprintf("install: symlink '%s':", buf);
450- }
451-}
452-
453-int
454-main(int argc, char *argv[])
455-{
456- struct Cmd *c;
457- char *s;
458-
459- argv0 = argv[0];
460-
461- for (c = cmds; c->name; c++)
462- wexec_register(c->name, c->fn);
463-
464- s = strrchr(argv[0], '/');
465- if (s)
466- s++;
467- else
468- s = argv[0];
469-
470- if (strcmp(s, "aruu-box") == 0) {
471- if (argc < 2)
472- usage();
473- if (strcmp(argv[1], "--license") == 0 || strcmp(argv[1], "-l") == 0) {
474- printf("%s", license);
475- return 0;
476- }
477- argc--;
478- argv++;
479- if (strcmp(argv[0], "-i") == 0) {
480- if (argc < 2)
481- eprintf("usage: aruu-box -i path\n");
482- x_install(argv[1]);
483- return 0;
484- }
485- s = strrchr(argv[0], '/');
486- if (s)
487- s++;
488- else
489- s = argv[0];
490- }
491- for (c = cmds; c->name; c++) {
492- if (strcmp(c->name, s) == 0) {
493- argv0 = (char *)c->name;
494- argv[0] = (char *)c->name;
495- return c->fn(argc, argv);
496- }
497- }
498- enprintf(127, "%s: not found\n", s);
499- return 1;
500-}
501-DISPATCH_TAIL
502-
503-printf 'mkbox: cc dispatch\n'
504-eval "${CC} ${CPPFLAGS} ${CFLAGS} -c \"\$DISP\" -o \"\${BDIR}/aruu-box.o\"" ||
505- die "compile failed: dispatch"
506-OBJS="${OBJS} ${BDIR}/aruu-box.o"
507-
508-printf 'mkbox: ld aruu-box\n'
509-eval "${CC} ${LDFLAGS} -o \"\$BOX\" \$OBJS \"\$LIBREDLINE\" \"\$LIBUTIL\" \"\$LIBUTF\" \${LDLIBS}" ||
510- die "link failed"
511-
512-printf 'mkbox: done => %s\n' "${BOX}"
1@@ -10,7 +10,7 @@
2 #include <string.h>
3 #include <unistd.h>
4
5-#if FEATURE_USE_LIBRESSL
6+#if FEATURE_USE_LIBTLS
7 #include <tls.h>
8
9 struct TlsSocket {
10@@ -298,12 +298,12 @@ tls_connect(int fd, const char *host, int check_cert, int is_tls)
11
12 if (!is_tls)
13 return s;
14-#if !FEATURE_USE_LIBRESSL && !FEATURE_USE_BEARSSL && !FEATURE_USE_OPENSSL
15+
16+ /* not every backend below uses these */
17 (void)host;
18 (void)check_cert;
19-#endif
20
21-#if FEATURE_USE_LIBRESSL
22+#if FEATURE_USE_LIBTLS
23 {
24 struct tls_config *cfg;
25
26@@ -369,7 +369,7 @@ tls_connect(int fd, const char *host, int check_cert, int is_tls)
27 }
28 #else
29 weprintf(
30- "TLS not supported, compile with FEATURE_USE_LIBRESSL or "
31+ "TLS not supported, compile with FEATURE_USE_LIBTLS or "
32 "FEATURE_USE_BEARSSL\n"
33 );
34 free(s);
35@@ -390,7 +390,7 @@ tls_read(struct TlsSocket *s, void *buf, size_t len)
36 return r;
37 }
38 }
39-#if FEATURE_USE_LIBRESSL
40+#if FEATURE_USE_LIBTLS
41 for (;;) {
42 ssize_t r = tls_read(s->ctx, buf, len);
43 if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
44@@ -415,7 +415,7 @@ tls_write(struct TlsSocket *s, const void *buf, size_t len)
45 return r;
46 }
47 }
48-#if FEATURE_USE_LIBRESSL
49+#if FEATURE_USE_LIBTLS
50 for (;;) {
51 ssize_t r = tls_write(s->ctx, buf, len);
52 if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT)
53@@ -440,7 +440,7 @@ void
54 tls_close(struct TlsSocket *s, int close_fd)
55 {
56 if (s->is_tls) {
57-#if FEATURE_USE_LIBRESSL
58+#if FEATURE_USE_LIBTLS
59 tls_close(s->ctx);
60 tls_free(s->ctx);
61 #elif FEATURE_USE_BEARSSL
1@@ -6,12 +6,12 @@
2
3 struct TlsSocket;
4
5-/* tls_connect takes an already connected socket fd and upgrades it to tls
6+/* tlssock_connect takes an already connected socket fd and upgrades it to tls
7 if check_cert is 0 certificate validation is bypassed
8 returns a new tls socket or null on error */
9 struct TlsSocket *tls_connect(int fd, const char *host, int check_cert, int is_tls);
10
11-/* standard read and write wrapping either libressl bearssl or plain fallback */
12+/* standard read and write wrapping either libtls, bearssl or plain fallback */
13 ssize_t tls_read(struct TlsSocket *s, void *buf, size_t len);
14 ssize_t tls_write(struct TlsSocket *s, const void *buf, size_t len);
15