build: declarative subdir recursion, no for-loops/FORCE
Replace the shell `for dir` loops and the FORCE pattern-rule hack with a uniform per-directory PHONY target scheme: each (phase, dir) is a target "<phase>@<dir>" that recurses into <dir> with that phase's goal via $(submake), and each phase aggregate (depend, modules, libs, techs, install, install-tcl, clean, mains, tcllibrary) is just the list of its per-dir targets as prerequisites. Inter-phase ordering moves from the aggregate onto the per-dir targets (module@/lib@/tech@ each require `depend`; main@ requires modules+libs), so the dependency graph is correct under `make -jN`, not only in a left-to-right serial build. '@' is a safe namespace char (never in a real target name). readline/ still takes the -f path inside $(submake). The `depend` rule is left as real file targets (<dir>/Depend) so Depend is still regenerated incrementally, not on every build. clean-mains loses its one-iteration shell loop too. The one cross-module edge (libdatabase.a needs tiles/utils modules first) is preserved by prerequisite order for serial builds here; its explicit -j edge is fixed in the following commit. Verified serial rc=0, 375 files, identical installs in-tree and out-of-tree; --without-tcl builds readline + magic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2b4fcf5968
commit
e84e353a40
117
Makefile.in
117
Makefile.in
|
|
@ -53,15 +53,31 @@ defs.mak:
|
||||||
config:
|
config:
|
||||||
${MAGICSRC}/configure
|
${MAGICSRC}/configure
|
||||||
|
|
||||||
tcllibrary: ${DATABASE_H} modules
|
# ---------------------------------------------------------------------------
|
||||||
@echo --- making Tcl shared libraries
|
# Declarative subdir recursion.
|
||||||
for dir in ${PROGRAMS}; do \
|
#
|
||||||
$(call submake,$$dir,tcl-main) || exit 1; done
|
# Each (phase, dir) pair is a PHONY target "<phase>@<dir>" whose recipe recurses
|
||||||
|
# into <dir> with that phase's goal via $(submake) (so readline/ still takes the
|
||||||
|
# -f path). A phase aggregate (depend, modules, libs, ...) is then just a list
|
||||||
|
# of its per-dir targets as prerequisites -- no shell `for` loops, no FORCE
|
||||||
|
# pattern-rule hack. Ordering between phases is expressed as prerequisites on the
|
||||||
|
# per-dir targets (e.g. every module@ waits for `depend`), so the graph is correct
|
||||||
|
# under `make -jN`, not just left-to-right in a serial build. '@' never appears
|
||||||
|
# in a real target name, so it is a safe namespace separator.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
mains: ${DATABASE_H} modules libs
|
# main@<prog> / tcl-main@<prog>: link the final program(s) (PROGRAMS is "magic").
|
||||||
@echo --- making main programs
|
MAIN_TARGETS := $(addprefix main@,${PROGRAMS})
|
||||||
for dir in ${PROGRAMS}; do \
|
TCLMAIN_TARGETS := $(addprefix tcl-main@,${PROGRAMS})
|
||||||
$(call submake,$$dir,main) || exit 1; done
|
.PHONY: $(MAIN_TARGETS) $(TCLMAIN_TARGETS)
|
||||||
|
$(MAIN_TARGETS): main@%: ${DATABASE_H} modules libs
|
||||||
|
@$(call submake,$*,main)
|
||||||
|
$(TCLMAIN_TARGETS): tcl-main@%: ${DATABASE_H} modules
|
||||||
|
@$(call submake,$*,tcl-main)
|
||||||
|
|
||||||
|
.PHONY: tcllibrary mains
|
||||||
|
tcllibrary: $(TCLMAIN_TARGETS)
|
||||||
|
mains: $(MAIN_TARGETS)
|
||||||
|
|
||||||
# makedbh is the one generated helper script; it lives in the *build* tree.
|
# makedbh is the one generated helper script; it lives in the *build* tree.
|
||||||
# ${DATABASE_H} is $(builddir)/database/database.h in-tree, or
|
# ${DATABASE_H} is $(builddir)/database/database.h in-tree, or
|
||||||
|
|
@ -79,24 +95,26 @@ ${DATABASE_H}: ${MAGICSRC}/database/database.h.in
|
||||||
.PHONY: prepare
|
.PHONY: prepare
|
||||||
prepare: ${DATABASE_H}
|
prepare: ${DATABASE_H}
|
||||||
|
|
||||||
# tiles xyz => tiles/libtiles.o xyz/libxyz.o
|
# module@<dir> builds <dir>/lib<dir>.o; lib@<dir> builds <dir>/lib<dir>.a.
|
||||||
MODULES_SUBDIR := $(shell for i in ${MODULES}; do echo "$${i}/lib$${i}.o"; done)
|
# Every module@/lib@ waits for the whole `depend` phase first (it used to be a
|
||||||
# tiles xyz => tiles/libtiles.a xyz/libxyz.a
|
# prereq of the `modules`/`libs` aggregates; moving it onto each per-dir target
|
||||||
LIBS_SUBDIR := $(shell for i in ${MODULES}; do echo "$${i}/lib$${i}.a"; done)
|
# keeps depend before compilation even under -j).
|
||||||
|
MODULE_TARGETS := $(addprefix module@,${MODULES})
|
||||||
.PHONY: FORCE
|
LIB_TARGETS := $(addprefix lib@,${MODULES})
|
||||||
${MODULES_SUBDIR}: FORCE
|
.PHONY: $(MODULE_TARGETS) $(LIB_TARGETS)
|
||||||
@$(call submake,$(patsubst %/,%,$(dir $@)),module)
|
$(MODULE_TARGETS): module@%: depend
|
||||||
|
@$(call submake,$*,module)
|
||||||
|
$(LIB_TARGETS): lib@%: depend
|
||||||
|
@$(call submake,$*,lib)
|
||||||
|
|
||||||
.PHONY: modules
|
.PHONY: modules
|
||||||
modules: ${DATABASE_H} depend ${MODULES_SUBDIR}
|
modules: $(MODULE_TARGETS)
|
||||||
|
|
||||||
${LIBS_SUBDIR}: FORCE
|
|
||||||
@$(call submake,$(patsubst %/,%,$(dir $@)),lib)
|
|
||||||
|
|
||||||
# Force the tiles/utils modules to exist first for libdatabase.a
|
|
||||||
.PHONY: libs
|
.PHONY: libs
|
||||||
libs: ${DATABASE_H} depend tiles/libtiles.o utils/libutils.o ${LIBS_SUBDIR}
|
# libdatabase.a needs the tiles/utils module objects first. Listing them before
|
||||||
|
# $(LIB_TARGETS) orders a serial build correctly; the explicit per-target -j edge
|
||||||
|
# is added in a later commit.
|
||||||
|
libs: module@tiles module@utils $(LIB_TARGETS)
|
||||||
|
|
||||||
#
|
#
|
||||||
# extcheck - utility tool
|
# extcheck - utility tool
|
||||||
|
|
@ -120,11 +138,14 @@ ${SUBDIRS_DEPEND}: ${DATABASE_H}
|
||||||
.PHONY: depend
|
.PHONY: depend
|
||||||
depend: defs.mak ${SUBDIRS_DEPEND}
|
depend: defs.mak ${SUBDIRS_DEPEND}
|
||||||
|
|
||||||
|
# tech@<dir>: build a technology dir (goal "all").
|
||||||
|
TECH_TARGETS := $(addprefix tech@,${TECHS})
|
||||||
|
.PHONY: $(TECH_TARGETS)
|
||||||
|
$(TECH_TARGETS): tech@%: depend
|
||||||
|
@$(call submake,$*,all)
|
||||||
|
|
||||||
.PHONY: techs
|
.PHONY: techs
|
||||||
techs: depend
|
techs: $(TECH_TARGETS)
|
||||||
@echo --- making techs
|
|
||||||
for dir in ${TECHS}; do \
|
|
||||||
$(call submake,$$dir,all) || exit 1; done
|
|
||||||
|
|
||||||
# Regenerate the PostScript documentation from its .tex sources (only reached
|
# Regenerate the PostScript documentation from its .tex sources (only reached
|
||||||
# from "all" when configure found latex+dvips; see DOCS_TARGET).
|
# from "all" when configure found latex+dvips; see DOCS_TARGET).
|
||||||
|
|
@ -141,11 +162,13 @@ install-magic:
|
||||||
@echo --- installing runtime files to $(DESTDIR)${INSTALL_LIBDIR}
|
@echo --- installing runtime files to $(DESTDIR)${INSTALL_LIBDIR}
|
||||||
@${MAKE} install-real
|
@${MAKE} install-real
|
||||||
|
|
||||||
install-real: install-dirs
|
# install@<dir>: install one subdir's artifacts (CAD dirs, then the programs).
|
||||||
for dir in ${INSTALL_CAD_DIRS}; do \
|
INSTALL_TARGETS := $(addprefix install@,${INSTALL_CAD_DIRS} ${PROGRAMS})
|
||||||
$(call submake,$$dir,install); done
|
.PHONY: $(INSTALL_TARGETS)
|
||||||
for dir in ${PROGRAMS}; do \
|
$(INSTALL_TARGETS): install@%: install-dirs
|
||||||
$(call submake,$$dir,install); done
|
@$(call submake,$*,install)
|
||||||
|
|
||||||
|
install-real: $(INSTALL_TARGETS)
|
||||||
|
|
||||||
install-tcl-dirs:
|
install-tcl-dirs:
|
||||||
${SCRIPTS}/mkdirs $(DESTDIR)${INSTALL_BINDIR} \
|
${SCRIPTS}/mkdirs $(DESTDIR)${INSTALL_BINDIR} \
|
||||||
|
|
@ -162,17 +185,26 @@ install-tcl:
|
||||||
@echo --- installing runtime files to $(DESTDIR)${INSTALL_LIBDIR}
|
@echo --- installing runtime files to $(DESTDIR)${INSTALL_LIBDIR}
|
||||||
@${MAKE} install-tcl-real
|
@${MAKE} install-tcl-real
|
||||||
|
|
||||||
install-tcl-real: install-tcl-dirs
|
# install-tcl@<dir>: install one subdir's Tcl-flavoured artifacts.
|
||||||
for dir in ${INSTALL_CAD_DIRS} ${PROGRAMS}; do \
|
INSTALLTCL_TARGETS := $(addprefix install-tcl@,${INSTALL_CAD_DIRS} ${PROGRAMS})
|
||||||
$(call submake,$$dir,install-tcl); done
|
.PHONY: $(INSTALLTCL_TARGETS)
|
||||||
|
$(INSTALLTCL_TARGETS): install-tcl@%: install-tcl-dirs
|
||||||
|
@$(call submake,$*,install-tcl)
|
||||||
|
|
||||||
clean:
|
install-tcl-real: $(INSTALLTCL_TARGETS)
|
||||||
@# clean must not create dirs: skip absent ones. Recurse natively;
|
|
||||||
@# readline/ is the one unconverted module, so it uses -f (mirrors $(submake)).
|
# clean@<dir>: clean one subdir if it exists. Must not create dirs (skip absent
|
||||||
for dir in ${SUBDIRS_FILTERED} ${TECHS} ${BUNDLED_MODULES}; do \
|
# ones) and must not fail the build (|| true). readline/ uses the -f path
|
||||||
test -d $$dir || continue; \
|
# (mirrors $(submake); it is the one unconverted module).
|
||||||
if test "x$$dir" = xreadline; then ${MAKE} -C $$dir -f ${MAGICSRC}/$$dir/Makefile clean; \
|
CLEAN_TARGETS := $(addprefix clean@,$(sort ${SUBDIRS_FILTERED} ${TECHS} ${BUNDLED_MODULES}))
|
||||||
else ${MAKE} -C $$dir clean; fi || true; done
|
.PHONY: $(CLEAN_TARGETS)
|
||||||
|
$(CLEAN_TARGETS): clean@%:
|
||||||
|
@test -d $* || exit 0; \
|
||||||
|
if test "x$*" = xreadline; then ${MAKE} -C $* -f ${MAGICSRC}/$*/Makefile clean; \
|
||||||
|
else ${MAKE} -C $* clean; fi || true
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean: $(CLEAN_TARGETS)
|
||||||
${RM} *.tmp */*.tmp *.sav */*.sav *.log TAGS tags
|
${RM} *.tmp */*.tmp *.sav */*.sav *.log TAGS tags
|
||||||
|
|
||||||
distclean:
|
distclean:
|
||||||
|
|
@ -199,8 +231,7 @@ dist:
|
||||||
magic-${VERSION}
|
magic-${VERSION}
|
||||||
|
|
||||||
clean-mains:
|
clean-mains:
|
||||||
for dir in ${PROGRAMS}; do \
|
${RM} $(foreach p,${PROGRAMS},$p/$p)
|
||||||
${RM} $$dir/$$dir; done
|
|
||||||
|
|
||||||
tags:
|
tags:
|
||||||
${RM} tags
|
${RM} tags
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue