X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/dfa704e1220e80795f3e47907a4f1d800a6897c5..e5503365cff290808176bec63f9227e5761dee67:/GNUmakefile?ds=sidebyside
diff --git a/GNUmakefile b/GNUmakefile
index be4ec71..c654ddb 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -10,11 +10,10 @@ override define makefileinfo
║╰────────────────────────────╯ ║
╟┬ ¶ Prerequisites ───────────────────────────────────────────┬╢
║│ │║
-║│ Requires G·N·U Make, at least version 3.81, a version of │║
-║│ uuencode with base64 support, and the various programs │║
-║│ offered by libxml2 and libxslt. Beyond this, only programs │║
-║│ required by Posix are required, altho there is a chance of │║
-║│ version incompatibilities. The full list of program │║
+║│ Requires G·N·U Make, at least version 3.81, the Fine Free │║
+║│ File Command, and the various programs packaged with │║
+║│ libxml2 and libxslt. Beyond this, only programs specified │║
+║│ by Posix are required. The full list of program │║
║│ requirements is as follows :— │║
║│ │║
║│ • awk │║
@@ -23,16 +22,17 @@ override define makefileinfo
║│ • cksum │║
║│ • cp │║
║│ • date │║
-║│ • diff │║
-║│ • file │║
+║│ • diff (with -u) │║
+║│ • file (specifically the Fine Free File Command) │║
║│ • find │║
║│ • grep │║
║│ • git (optional) │║
║│ • ln │║
+║│ • make (specifically G·N·U Make, version 3.81 or later) │║
║│ • mkdir │║
║│ • mv │║
║│ • od │║
-║│ • pax (only when generating archives) │║
+║│ • pax (ustar format, when generating archives) │║
║│ • printf │║
║│ • rm │║
║│ • sed │║
@@ -83,7 +83,7 @@ override define makefileinfo
║╰────────────────────────────────────────────────────────────╯║
╟┬ ¶ Copyright & License ─────────────────────────────────────┬╢
║│ │║
-║│ Copyright © 2023–2024 Lady [@ Ladys Computer]. │║
+║│ Copyright © 2023–2024 Lady [@ Ladys Computer]. │║
║│ │║
║│ This Source Code Form is subject to the terms of the │║
║│ Mozilla Public License, v 2.0. If a copy of the M·P·L was │║
@@ -149,11 +149,21 @@ SRCDIR := sources
# Multiple directories can be given so long as files with the same name do not exist in each.
#
# These can be inside of `SRCDIR´ directories if desired.
+#
+# This variable is also used by the two‐stage `MODE´, where it gives the include directory for the second stage.
INCLUDEDIR := $(foreach dir,$(SRCDIR),$(dir)/includes)
+# The directory which contains the data files.
+#
+# Only used in the two‐stage `MODE´, and defaults to that `MODE´ if this directory exists.
+# This directory is used as the include directory in the first stage.
+#
+# Multiple directories can be given so long as files with the same name do not exist in each.
+DATADIR :=
+
# The directory in which to generate temporary buildfiles.
#
-# This variable is also used by the archiving `MODE´.
+# This variable is also used by the archiving and two‐stage `MODE´s.
BUILDDIR := build
# The directory into which to output files on `make install´.
@@ -165,17 +175,28 @@ DESTDIR := public
#
# By default, this is inferred from the variable `MAKEFILE_LIST´.
#
-# This variable is also used by the archiving `MODE´.
+# This variable is also used by the archiving and two‐stage `MODE´s.
THISDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
# Configuration of `find´.
#
# By default, `find´ will ignore files which begin with a period and those which are likely to cause problems for `make´.
+#
+# These variables are also used by the two‐stage `MODE´.
EXTRAFINDRULES :=
EXTRAFINDINCLUDERULES :=
-FINDRULES := ! '(' '(' -name '[.-]*' -a ! -name '.' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a '(' $(EXTRAFINDRULES) ')',)
+FINDRULES := '!' '(' '(' -name '[.-]*' -a '!' -name '.' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a '(' $(EXTRAFINDRULES) ')',)
FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a '(' $(EXTRAFINDINCLUDERULES) ')',)
+# File extensions which indicate files in `SRCDIR´ which should be built as part of the first, rather than second, stage of the two‐stage `MODE´.
+DATAEXT := rdf
+
+# Rules for identifying files in `SRCDIR´ which should be built as part of the first, rather than second, stage of the two‐stage `MODE´.
+#
+# By default, this just constructs rules from `DATAEXT´.
+EXTRAFINDDATARULES :=
+FINDDATARULES := -name '.' $(foreach ext,$(DATAEXT), -o -name '$(subst ','"'"',[!.]*.$(ext))')$(if $(EXTRAFINDDATARULES), -a '(' $(EXTRAFINDDATARULES) ')',)
+
# The list of magic files to use when determining media types.
#
# Some are provided as part of this repository, but you can add more if you need different media type detection.
@@ -184,12 +205,24 @@ FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a '(' $(EXTRAFIN
EXTRAMAGIC :=
MAGIC := $(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/magic/*)) $(EXTRAMAGIC))
+# The list of depedencies for parsers.
+#
+# When these files change, the assumption is that the result of the parsers will change as well, even if the parsers themselves have not.
+EXTRAPARSERLIBS :=
+PARSERLIBS := $(sort $(THISDIR)/lib/split.xslt $(EXTRAPARSERLIBS))
+
# The list of parsers for plaintext file types.
#
# Which parsers are provided will influence which kinds of files are recognized as plaintext.
EXTRAPARSERS :=
PARSERS := $(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/parsers/*.xslt)) $(EXTRAPARSERS))
+# The list of depedencies for transforms.
+#
+# When these files change, the assumption is that the result of the transforms will change as well, even if the transforms themselves have not.
+EXTRATRANSFORMLIBS :=
+TRANSFORMLIBS := $(sort $(THISDIR)/lib/serialize.xslt $(EXTRATRANSFORMLIBS))
+
# The list of transforms.
EXTRATRANSFORMS :=
TRANSFORMS := $(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/transforms/*.xslt)) $(EXTRATRANSFORMS))
@@ -221,11 +254,14 @@ endif
# The following modes are available :—
#
# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:default ›:
-# The default mode; typical Shushe behaviours.
+# The default mode; typical ⛩📰 书社 behaviours.
#
# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive ›:
# Generates archive files from parse results.
-MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:default
+#
+# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:_2stage ›:
+# Two‐stage build; runs ⛩📰 书社 twice.
+MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:$(if $(DATADIR),$(shell if $(TEST) -d $(call quote,$(DATADIR)); then $(PRINTF) '%s\n' '_2stage'; else $(PRINTF) '%s\n' 'default'; fi),default)
# Set to a non·empty value to silence informative messages.
QUIET :=
@@ -312,16 +348,16 @@ ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:default)
# Non·empty if `help´ or `clean´, and no targets other than `help´ or `clean´, were specified as goals on the commandline.
override notbuilding := $(and $(filter help clean,$(MAKECMDGOALS)),$(call not,$(filter-out help clean,$(MAKECMDGOALS))))
-# (callable) Test to see if the prerequisites provided by the second argument matches the value in the file corresponding to the first argument in `$(BUILDDIR)/lastprereqs´.
-# If not, save the new value and then add FORCE.
-# Return them regardless.
+# (callable) Tests to see if the prerequisites provided by the second argument matches the value in the file corresponding to the first argument in `$(BUILDDIR)/lastprereqs´.
+# If not, saves the new value.
+# Returns the values plus the file in `$(BUILDDIR)/lastprereqs´, which will always be newer than the target if there was a change.
#
# Calling this variable is useful when a given target should be updated whenever its list of prerequisites changes in addition to whenever there is a change to one of its prerequisites.
#
# If `$(notbuilding)´ is non·empty, this variable produces no result to avoid unnecessary work.
#
# ☡ This variable creates at least one subshell every time it is computed.
-override diffprereqs = $(if $(notbuilding),,$(if $(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastprereqs/$1) 2>>/dev/null || :),,$2),$2 FORCE$(and $(shell $(call ensuredirectory,$(BUILDDIR)/lastprereqs) && $(PRINTF) '%s\n' $(call quote,$2) >|$(BUILDDIR)/lastprereqs/$1),),$2))
+override diffprereqs = $(if $(notbuilding),,$(and $(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastprereqs/$1) 2>>/dev/null || :),,$2),$(shell $(call ensuredirectory,$(BUILDDIR)/lastprereqs) && $(PRINTF) '%s\n' $(call quote,$2) >|$(BUILDDIR)/lastprereqs/$1),)$2 $(BUILDDIR)/lastprereqs/$1)
# (callable) Escape special characters for use in X·M·L.
override xmlesc = $(subst >,>,$(subst <,<,$(subst &,&,$1)))
@@ -442,10 +478,10 @@ override parsed = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(
# (callable) Get the source files for the given parsed file.
override unparsed = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourceparsedpair))))
-# Pair each build directory, parser, transform, or source file with its file u·r·i.
-override fileuripairs := $(join $(patsubst %,%|,$(BUILDDIR) $(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes)),$(call pathenc,$(foreach uriable,$(BUILDDIR) $(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes),file://$(abspath $(uriable)))))
+# Pair each build directory, parser, transform, source file, or parsed file with its file u·r·i.
+override fileuripairs := $(join $(patsubst %,%|,$(BUILDDIR) $(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes))),$(call pathenc,$(foreach uriable,$(BUILDDIR) $(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)),file://$(abspath $(uriable)))))
-# (callable) Get the file u·r·is for the given parsers, transforms, parsed files, or metadata compilations.
+# (callable) Get the file u·r·is for the given parsers, transforms, source file or parsed files.
override fileuri = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(fileuripairs))),$(error Unable to get file u·r·i for `$(file)´)))
ifneq ($(wildcard $(BUILDDIR)/dependencies),)
@@ -577,10 +613,10 @@ FORCE : ;
.SUFFIXES : ;
# Don’t delete these files even if Make is stopped in the process of rebuilding them.
-.PRECIOUS : GNUmakefile ;
+.PRECIOUS : $(THISDIR)/GNUmakefile ;
# Phony rules; always consider these out·of·date.
-.PHONY : FORCE all default clean gone info install list listout uninstall $(call built,$(recursivefiles)) ;
+.PHONY : FORCE all clean gone help install list listout uninstall $(call built,$(recursivefiles)) ;
ifeq ($(notbuilding),)
# Reload this make·file if the magic file, parser, dependencies, or destinations have changed.
@@ -620,12 +656,10 @@ $(BUILDDIR)/parser.xslt : $(BUILDDIR)/parser.catalog $(THISDIR)/lib/catalog2pars
# Generate R·D·F metadata for files.
#
-# This “depends” on `$(THISDIR)/lib/expandmetadata.xslt´ not because it is an actual dependency, but because a change to that file strongly suggests a change to these rules has occurred as well.
-#
# If the types have been updated but `typeupdates´ is not active, this recipe is skipped because a restart is forthcoming.
-$(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(THISDIR)/lib/expandmetadata.xslt $(typeupdates)
+$(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(THISDIR)/.metadata-format-changed-since $(typeupdates)
$(call inform,$(call unlesstypeswillupdate,$(PRINTF) '%s\n' $(call quote,Generating metadata for `$<´…) >&2))
- $(silent)$(call unlesstypeswillupdate,$(call ensuredirectory,$(dir $@)); { if $(TEST) ! -f $(call quote,$(BUILDDIR)/.mtime); then $(PRINTF) '%b' '\n' >|$(call quote,$(BUILDDIR)/.mtime); fi; $(TOUCH) -r $(call quote,$<) $(call quote,$(BUILDDIR)/.mtime); $(DIFF) -u $(call quote,$(BUILDDIR)/.mtime) /dev/null | $(SED) '1!d;s/.* \([^ ]*\) \([^ ]*\).*$$/\1T\2Z/'; $(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//'; } | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) '<书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile) xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:" rdf:about="%s" 书社vocab:path="%s">$(if $(filter $<,$(assetfiles)),,)%s书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile)>' $(call quote,$(call attresc,$(call localuri,$<))) $(call quote,$(call attresc,$(if $(filter $<,$(sourceincludes)),$(call includepath,$<),$(call sourcepath,$<)))) $(call quote,$(call attresc,$(call typeoffile,$<))) >|$(call quote,$@))
+ $(silent)$(call unlesstypeswillupdate,$(call ensuredirectory,$(dir $@)); { if $(TEST) ! -f $(call quote,$(BUILDDIR)/.mtime); then $(PRINTF) '%b' '\n' >|$(call quote,$(BUILDDIR)/.mtime); fi; $(TOUCH) -r $(call quote,$<) $(call quote,$(BUILDDIR)/.mtime); $(DIFF) -u $(call quote,$(BUILDDIR)/.mtime) /dev/null | $(SED) '1!d;s/.* \([^ ]*\) \([^ ]*\).*$$/\1T\2Z/'; $(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//'; } | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) '<书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile) xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:" rdf:about="%s" 书社vocab:path="%s" nfo:fileUrl="%s">$(if $(filter $<,$(assetfiles)),,)<书社vocab:hasParsedFile nfo:fileUrl="%s"/>%s书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile)>' $(call quote,$(call attresc,$(call localuri,$<))) $(call quote,$(call attresc,$(if $(filter $<,$(sourceincludes)),$(call includepath,$<),$(call sourcepath,$<)))) $(call quote,$(call attresc,$(call fileuri,$<))) $(call quote,$(call attresc,$(call typeoffile,$<))) $(call quote,$(call attresc,$(call fileuri,$(call parsed,$<)))) >|$(call quote,$@))
# Parse the files.
#
@@ -633,7 +667,7 @@ $(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(
# Asset files are turned into H·T·M·L embeds pointing to `data:´ U·R·I’s.
#
# If the types have been updated but `typeupdates´ is not active, this recipe is skipped because a restart is forthcoming.
-$(call parsed,$(sourcefiles) $(sourceincludes)) : % : $$(call unparsed,$$@) $(BUILDDIR)/parser.xslt $(typeupdates)
+$(call parsed,$(sourcefiles) $(sourceincludes)) : % : $$(call unparsed,$$@) $(BUILDDIR)/parser.xslt $(PARSERLIBS) $(typeupdates)
$(call inform,$(call unlesstypeswillupdate,$(PRINTF) '%s\n' $(call quote,Processing `$<´…) >&2))
$(silent)$(call unlesstypeswillupdate,$(call ensuredirectory,$(dir $@)); $(if $(filter $<,$(assetfiles)),$(PRINTF) '%s\n' $(call quote,) >|$(call quote,$@),$(if $(filter $<,$(plaintextfiles)),$(call wrapplaintext,$<),$(call serializexml,$<)) | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$<))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/parser.xslt) - >|$(call quote,$@)))
@@ -651,12 +685,12 @@ $(BUILDDIR)/transform.catalog : $(call diffprereqs,transforms,$(sort $(TRANSFORM
$(call inform,$(PRINTF) '%s\n' 'Generating catalog of transforms…' >&2)
$(silent)$(XMLCATALOG) --create --noout $(call quote,$@)
$(foreach transform,$(TRANSFORMS),$(silent){ $(call id,$(transform)); $(PRINTF) '%s\n' $(call quote,$(call fileuri,$(transform))) '--noout' $(call quote,$@); } | $(xargsmultiquote) | $(XARGS) -E '' $(XMLCATALOG) --add uri$(newline))
-$(BUILDDIR)/transform.xslt : $(BUILDDIR)/transform.catalog $(BUILDDIR)/metadata $(THISDIR)/lib/catalog2transform.xslt
+$(BUILDDIR)/transform.xslt : $(BUILDDIR)/transform.catalog $(BUILDDIR)/metadata $(THISDIR)/lib/literally.xslt $(THISDIR)/lib/catalog2transform.xslt
$(call inform,$(PRINTF) '%s\n' 'Generating main transform…' >&2)
$(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam METADATA $(call quote,$(call fileuri,$(BUILDDIR))/metadata) $(call quote,$(THISDIR)/lib/catalog2transform.xslt) $(call quote,$<) >|$(call quote,$@)
# Compile the result files using the dependencies as necessary.
-$(call compiled,$(compilablefiles)) : $(BUILDDIR)/results/% : $$(call parsed,$$(call uncompiled,$$@)) $$(call parsed,$$(call dependencies,$$(call uncompiled,$$@))) $(BUILDDIR)/transform.xslt $(BUILDDIR)/metadata
+$(call compiled,$(compilablefiles)) : $(BUILDDIR)/results/% : $$(call parsed,$$(call uncompiled,$$@)) $$(call parsed,$$(call dependencies,$$(call uncompiled,$$@))) $(BUILDDIR)/transform.xslt $(TRANSFORMLIBS) $(BUILDDIR)/metadata
$(call inform,$(PRINTF) '%s\n' $(call quote,Compiling $*>…) >&2)
$(silent)$(call ensuredirectory,$(dir $@))
$(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam METADATA 'metadata' --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$(call uncompiled,$@)))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/transform.xslt) $(call quote,$<) >|$(call quote,$@)
@@ -697,6 +731,12 @@ endif
# ─ ¶ Special Targets ─────────────────────────────────────────────────
+# Don’t use any implicit rules.
+.SUFFIXES : ;
+
+# Don’t delete these files even if Make is stopped in the process of rebuilding them.
+.PRECIOUS : $(THISDIR)/GNUmakefile ;
+
# Reload this make·file if the archive components have changed.
$(THISDIR)/GNUmakefile : $(BUILDDIR)/index
$(silent)$(TOUCH) $(THISDIR)/GNUmakefile
@@ -712,10 +752,13 @@ $(BUILDDIR)/extractor.xslt : $(SRC) $(THISDIR)/lib/archive2extractor.xslt
# Use the extractor transform to extract the archive source file out into its components.
#
# This target sleeps for 1 second to ensure the resulting index will always be newer than this make·file.
+#
+# If the archive is empty, the index will not be written by `xsltproc´ and will need to be manually created.
$(BUILDDIR)/index : $(BUILDDIR)/extractor.xslt $(SRC)
$(call inform,$(PRINTF) '%s\n' $(call quote,Extracting components for $(NAME)>…) >&2)
$(silent)$(SLEEP) 1
$(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) --writesubtree $(call quote,$(dir $@)) $(call quote,$<) $(call quote,$(SRC))
+ $(silent)if $(TEST) ! -e $(call quote,$@); then $(TOUCH) $(call quote,$@); fi
# All archive components are extracted alongside the index.
$(foreach file,$(archivefiles),$(BUILDDIR)/extracted/$(file)) : $(BUILDDIR)/index ;
@@ -734,7 +777,84 @@ $(DESTDIR)/% : $(SRC) $(foreach file,$(archivefiles),$(BUILDDIR)/files/$(file))
$(call inform,$(PRINTF) '%s\n' $(call quote,Archiving $(NAME)>…) >&2)
$(silent)$(call ensuredirectory,$(dir $@))
$(silent)$(RM) -f -R $(call quote,$@)
- $(silent)$(CD) $(call quote,$(BUILDDIR)/files); if $(call xpath,/*/@*[local-name()="expanded" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$(abspath $<)); then $(MKDIR) -p $(call quote,$(abspath $@)); $(PRINTF) '%s\n' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -r -w $(call quote,$(abspath $@)); else $(PRINTF) '%s\n' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -w -x ustar >|$(call quote,$(abspath $@)); fi
+ $(silent)$(call ensuredirectory,$(BUILDDIR)/files)
+ $(silent)$(CD) $(call quote,$(BUILDDIR)/files); if $(call xpath,/*/@*[local-name()="expanded" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$(abspath $<)); then $(MKDIR) -p $(call quote,$(abspath $@)); $(PRINTF) '$(if $(archivefiles),%s\n,)' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -r -w $(call quote,$(abspath $@)); else $(PRINTF) '$(if $(archivefiles),%s\n,)' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -w -x ustar >|$(call quote,$(abspath $@)); fi
+
+# ━ § BEGIN TWO‐STEP MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+else ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:_2stage)
+
+# ─ ¶ Non‐Recipe Variable Definitions ─────────────────────────────────
+
+override makefile := $(abspath $(THISDIR)/GNUmakefile)
+
+# (overridable) Options to use when calling ⛩📰 书社 the first time.
+shushedataopts := INCLUDEDIR=$(call quote,$(DATADIR)) BUILDDIR=$(call quote,$(BUILDDIR)/stage1) DESTDIR=$(call quote,$(BUILDDIR)/data) FINDRULES=$(call quote,$(FINDRULES) -a '(' $(FINDDATARULES) ')') FINDINCLUDERULES=$(call quote,$(FINDINCLUDERULES)) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default'
+
+# (overridable) Options to use when calling ⛩📰 书社 the second time.
+shushesiteopts := INCLUDEDIR=$(call quote,$(INCLUDEDIR) $(BUILDDIR)/data) BUILDDIR=$(call quote,$(BUILDDIR)/stage2) FINDRULES=$(call quote,$(FINDRULES) -a '!' '(' $(FINDDATARULES) ')') FINDINCLUDERULES=$(call quote,$(FINDINCLUDERULES)) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default'
+
+# ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
+
+# ─ ¶ Phony Targets ───────────────────────────────────────────────────
+
+# Compile all files, or error if any are recursive.
+all : data
+ @$(MAKE) -f $(call quote,$(makefile)) $(shushesiteopts)
+
+# Destroy buildfiles.
+clean :
+ $(if $(BUILDDIR),$(silent)$(RM) -f -R $(call quote,$(BUILDDIR)/),)
+
+# Build the data and remove outdated data files.
+data : $(BUILDDIR)/data.out
+ @$(MAKE) -f $(call quote,$(makefile)) install $(shushedataopts)
+ $(silent)$(FIND) $(call quote,$(BUILDDIR)/data) '!' -exec $(GREP) -F -q -x '{}' $(call quote,$<) ';' -a '(' -type d -o -print ')' | $(xargsmultiquote) | $(XARGS) -E '' $(RM)
+
+# Destroy build directory and installed files.
+gone : clean uninstall ;
+
+# Install the compiled files into `DESTDIR´.
+install : all
+ @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
+ @$(MAKE) -f $(call quote,$(makefile)) $(foreach dest,$(patsubst $(BUILDDIR)/data/%,$(DESTDIR)/%,$(shell $(CAT) $(call quote,$(BUILDDIR)/data.out))),$(call quote,$(dest)))
+
+# List all source files and includes and their computed types.
+list listout : data
+ @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
+
+# Destroy installed files.
+uninstall :
+ @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
+
+# Add as a prerequisite to treat the target as tho it were phony.
+FORCE : ;
+
+# ─ ¶ Special Targets ─────────────────────────────────────────────────
+
+# Don’t use any implicit rules.
+.SUFFIXES : ;
+
+# Phony rules; always consider these out·of·date.
+.PHONY : FORCE all data clean gone install list listout uninstall ;
+
+# ─ ¶ Build Targets ───────────────────────────────────────────────────
+
+$(BUILDDIR)/data.out : FORCE
+ @$(MAKE) -f $(call quote,$(makefile)) $(shushedataopts)
+ @$(MAKE) -s -f $(call quote,$(makefile)) listout QUIET=1 $(shushedataopts) | $(TR) ' ' '\n' | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) $(call quote,$(BUILDDIR)/data/%s\n) >$(call quote,$(BUILDDIR)/data.out)
+
+$(BUILDDIR)/stage1/% : FORCE
+ @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts)
+
+$(BUILDDIR)/data/% : FORCE
+ @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts)
+
+$(BUILDDIR)/stage2/% : data
+ @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)
+
+$(DESTDIR)/% : data
+ $(if $(shell $(GREP) -F -x $(call quote,$(BUILDDIR)/data/$*) $(BUILDDIR)/data.out),$(call inform $(PRINTF) '%s\n' $(call quote,Copying over $*>…) >&2)$(newline),$(CP) $(call quote,$(BUILDDIR)/data/$*) $(call quote,$@),@$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts))
# ━ § END DEFINED MAKE·FILE MODES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━