X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/c641dd5addbe3d0826c19b730d88345be15a5b3b..6d78d92dfa675156640925421f33aa0535f6f46f:/GNUmakefile?ds=inline
diff --git a/GNUmakefile b/GNUmakefile
index 495b8a4..2c77723 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -36,7 +36,6 @@ override define makefileinfo
║│ • rm │║
║│ • sed │║
║│ • sleep │║
-║│ • stat │║
║│ • test │║
║│ • touch │║
║│ • tr │║
@@ -117,7 +116,6 @@ PRINTF := printf
RM := rm
SED := sed
SLEEP := sleep
-STAT := stat
TEST := test
TOUCH := touch
TR := tr
@@ -173,7 +171,7 @@ THISDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
# By default, `find´ will ignore files which begin with a period and those which are likely to cause problems for `make´.
EXTRAFINDRULES :=
EXTRAFINDINCLUDERULES :=
-FINDRULES := ! '(' '(' -name '[.-]*' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*"{*' -o -name '*}"*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a $(EXTRAFINDRULES),)
+FINDRULES := ! '(' '(' -name '[.-]*' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a $(EXTRAFINDRULES),)
FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a $(EXTRAFINDINCLUDERULES),)
# The list of magic files to use when determining media types.
@@ -195,7 +193,10 @@ EXTRATRANSFORMS :=
TRANSFORMS := $(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/transforms/*.xslt)) $(EXTRATRANSFORMS))
# List of types which should be treated as X·M·L.
-XMLTYPES := application/xml text/xml
+#
+# Types which start with a `+´ are suffixes and match any type which
+# ends accordingly.
+XMLTYPES := application/xml text/xml +xml
ifdef GIT
ifneq ($(wildcard $(THISDIR)/.git),)
@@ -219,11 +220,11 @@ endif
#
# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive ›:
# Generates archive files from parse results.
-#
-# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:paged ›:
-# Generates paginated files from parse results.
MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:default
+# Set to a non·empty value to silence informative messages.
+QUIET :=
+
# Set to a non·empty value to print all commands as they run.
VERBOSE :=
@@ -252,18 +253,28 @@ override comma := ,
# (callable) Quote the given string for use within shell calls.
override quote = '$(subst ','"'"',$1)'
-# Outputs an `@´ to silence rules, unless `VERBOSE´ is nonempty.
-override silent := $(if $(VERBOSE),,@)
-
# (callable) Get the modified time of the provided file.
#
-# This is messy; there is no portable way of using `stat´.
+# Only the date·time information available via `ls -l´ is supported; that is, only minute precision for files newer than six months, and only day precision for files which are older.
+# This is, unfortunately, the best that can be done in pure Posix as of now.
+#
+# There is an easier implementation which provides the full date·time by archiving an empty file and then reading it back out of the archive :—
+#
+# $(shell LC_TIME=POSIX TZ=UTC0 $(TOUCH) -m -r $(callquote,$1) $(BUILDDIR)/.mtime && $(PAX) -w -x ustar $(BUILDDIR)/.mtime | $(PAX) -v -o 'listopt=%(mtime=%Y-%m-%dT%H:%M:%SZ)T')
+#
+# However, it depends on the `listopt´ option in `pax´, which (despite being specified in Posix) no implementations actually support.
#
# ☡ This variable creates a subshell every time it is computed.
-override modtime = $(shell if $(STAT) --version 2> /dev/null | $(GREP) -q GNU; then $(DATE) -u -d "@$$(TZ= $(STAT) -c '%Y' $(call quote,$1))" '+%Y-%m-%dT%H:%M:%SZ'; else TZ= $(STAT) -f '%Sm' -t '%Y-%m-%dT%H:%M:%SZ' $(call quote,$1); fi)
+override modtime = $(shell { $(DATE) -u '+%Y-%m'; LC_TIME=POSIX TZ=UTC0 ls -q -A -l $(call quote,$1) | $(SED) 's/\([^ ]* \{1,\}\)\{5\}\(\([^ ]* \{1,\}\)\{3\}\).*/\2/;s/ \([0123456789]\) / 0\1 /;s/Jan/01/;s/Feb/02/;s/Mar/03/;s/Apr/04/;s/May/05/;s/Jun/06/;s/Jul/07/;s/Aug/08/;s/Sep/09/;s/Oct/10/;s/Nov/11/;s/Dec/12/;s/\([0123456789]\{2\}\) *\([0123456789]\{2\}\) *\([0123456789]\{4\}\)/\3-\1-\2T00:00:00Z/;s/\([0123456789]\{2\}\) *\([0123456789]\{2\}\) *\([0123456789:]\{5\}\)/XXXX-\1-\2T\3:00Z/'; } | $(TR) -d ' ' | $(AWK) -F '-' 'NR==1{year=$$1;month=$$2}$$1=="XXXX"&&$$2<=month{print year"-"$$2"-"$$3}$$1=="XXXX"&&$$2>month{print year-1"-"$$2"-"$$3}NR>1&&$$1!="XXXX"')
# ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
+# Outputs an `@´ to silence rules, unless `VERBOSE´ is non·empty.
+override silent := $(if $(VERBOSE),,@)
+
+# (callable) Only perform the rule if `QUIET` is not set.
+override inform = $(if $(QUIET),,@$1)
+
# (callable) Check to see if the given directory exists and create it if not.
override ensuredirectory = if $(TEST) ! -d $(call quote,$1); then $(MKDIR) -p $(call quote,$1); fi
@@ -281,14 +292,8 @@ override xpath = $(XMLLINT) --xpath $(call quote,$1) $(call quote,$2) > /dev/nul
# (callable) Extract the value of the text nodes in the provided X·M·L document and print them to `stdout´.
override extracttext = $(PRINTF) '%s' '' | $(XSLTPROC) --nonet --novalid - $(call quote,$1)
-# (callable) List the files in the provided directory which are associated with the provided filename, one per line.
-override associatedfiles = $(LS) -1 $(call quote,$1) | $(SED) '/.*"{.*}".*/!d;s/^\(.*\)"{.*}"\(.*\)$$/\1\2|&/' | $(GREP) -F -e $(call quote,$2|) | $(SED) 's/^.*|//'
-
-# (callable) Remove files associated with the provided file.
-override removeassociatedfiles = ASSOCIATED="$$($(call associatedfiles,$(dir $1),$(notdir $1)) | $(TR) '\n' ' ')"; if $(TEST) -n "$$ASSOCIATED"; then cd $(call quote,$(dir $1)) && $(PRINTF) '%s\n' "$$ASSOCIATED" | $(SED) 's/ \{2,\}/ /g;s/^ //;s/ $$//' | $(TR) ' ' '\n' | $(xargsmultiquote) | $(XARGS) -E '' $(RM); fi
-
# (callable) Process the provided transformation result and output the result to the provided location, given the provided relative path.
-override processresultto = if $(call xpath,/*[local-name()="raw-text" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(call extracttext,$1) > $(call quote,$2); $(call removeassociatedfiles,$2); elif $(call xpath,/*[local-name()="base64-binary" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then { $(PRINTF) '%s\n' 'begin-base64 644 -'; $(call extracttext,$1) | $(TR) -d '\t\n\f\r '; $(PRINTF) '\n%s\n' '===='; } | $(UUDECODE) -o /dev/stdout > $(call quote,$2); $(call removeassociatedfiles,$2); elif $(call xpath,/*[local-name()="archive" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(MAKE) -f $(call quote,$(abspath $(THISDIR)/GNUmakefile)) $(call quote,$2) NAME=$(call quote,$3) SRC=$(call quote,$1) BUILDDIR=$(call quote,$(BUILDDIR)/archive/$3) DESTDIR=$(call quote,$(patsubst %/,%,$(dir $2))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive'; $(call removeassociatedfiles,$2); elif $(call xpath,//*[local-name()="page" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(MAKE) -f $(call quote,$(abspath $(THISDIR)/GNUmakefile)) $(call quote,$2) NAME=$(call quote,$3) SRC=$(call quote,$1) BUILDDIR=$(call quote,$(BUILDDIR)/paged/$3) DESTDIR=$(call quote,$(patsubst %/,%,$(dir $2))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:paged'; else $(XMLLINT) --nsclean $(call quote,$1) > $(call quote,$2); $(call removeassociatedfiles,$2); fi
+override processresultto = if $(call xpath,/*[local-name()="raw-text" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(call extracttext,$1) > $(call quote,$2); elif $(call xpath,/*[local-name()="base64-binary" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then { $(PRINTF) '%s\n' 'begin-base64 644 -'; $(call extracttext,$1) | $(TR) -d '\t\n\f\r '; $(PRINTF) '\n%s\n' '===='; } | $(UUDECODE) -o /dev/stdout > $(call quote,$2); elif $(call xpath,/*[local-name()="archive" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(MAKE) -f $(call quote,$(abspath $(THISDIR)/GNUmakefile)) $(call quote,$2) NAME=$(call quote,$3) SRC=$(call quote,$1) BUILDDIR=$(call quote,$(BUILDDIR)/archive/$3) DESTDIR=$(call quote,$(patsubst %/,%,$(dir $2))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive'; else $(XMLLINT) --nsclean $(call quote,$1) > $(call quote,$2); fi
# ━ § BEGIN DEFAULT MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -347,8 +352,10 @@ ifneq ($(wildcard $(BUILDDIR)/parser.xslt),)
override plaintexttypes := $(filter-out $(XMLTYPES),$(shell $(XMLLINT) --xpath '/*/*[@name="书社:parsers"]//*[namespace-uri()="http://www.w3.org/1999/xhtml" and local-name()="dd"]/text()' $(call quote,$(BUILDDIR)/parser.xslt)))
endif
-# (callable) Get all of the files (source and includes) which have the given types.
-override filesoftype = $(foreach type,$1,$(patsubst %|$(type),%,$(filter %|$(type),$(types))))
+# (callable) Get all of the files (source and includes) which have the provided types.
+#
+# If a provided type begins with a `+´, it is analysed as a suffix, and all matching types will be used.
+override filesoftype = $(foreach typeorsuffix,$1,$(foreach type,$(if $(filter +%,$(typeorsuffix)),$(sort $(foreach suffixedtype,$(filter %$(typeorsuffix),$(types)),$(lastword $(subst |, ,$(suffixedtype))))),$(typeorsuffix)),$(patsubst %|$(type),%,$(filter %|$(type),$(types)))))
# Build up collections of various file types.
override xmlfiles := $(call filesoftype,$(XMLTYPES))
@@ -502,11 +509,11 @@ install : $(call installed,$(recursivefiles) $(installablefiles)) ;
# List all source files and includes and their computed types.
list :
- @$(PRINTF) '%b' $(call quote,$(foreach file,$(sort $(sourcefiles)) $(sort $(sourceincludes)),\0033[1m$(file)\0033[22m|$(call typeoffile,$(file))|[\0033[3m$(if $(filter $(file),$(xmlfiles)),xml,$(if $(filter $(file),$(plaintextfiles)),text,asset))$(if $(filter $(file),$(sourceincludes)),|include,)\0033[23m]$(if $(call dependencies,$(file))$(call recursives,$(file)), $(strip $(foreach recursive,$(call recursives,$(file)),\0033[93;41m•|Recursive|Dependency|\0033[39;49m|$(recursive)) $(foreach dependency,$(call dependencies,$(file)),\0033[2m•|Dependency|\0033[22m|$(dependency))))$(if $(filter $(file),$(sourcefiles)), →|<\0033[4m/$(call destination,$(file))\0033[24m>,) )) | $(TR) ' |' '\n '
+ @$(PRINTF) '%b' $(call quote,$(foreach file,$(sort $(sourcefiles)) $(sort $(sourceincludes)),\0033[1m$(file)\0033[22m|$(call typeoffile,$(file))|[\0033[3m$(if $(filter $(file),$(xmlfiles)),xml,$(if $(filter $(file),$(plaintextfiles)),text,asset))$(if $(filter $(file),$(sourceincludes)),|include,)\0033[23m] Modified:|$(call modtime,$(file))$(if $(call dependencies,$(file))$(call recursives,$(file)), $(strip $(foreach recursive,$(call recursives,$(file)),\0033[93;41m•|Recursive|Dependency|\0033[39;49m|$(recursive)) $(foreach dependency,$(call dependencies,$(file)),\0033[2m•|Dependency|\0033[22m|$(dependency))))$(if $(filter $(file),$(sourcefiles)), →|<\0033[4m/$(call destination,$(file))\0033[24m>,) )) | $(TR) ' |' '\n '
# Lists out the destinations of all resulting files (relative to `DESTDIR´).
listout :
- @$(PRINTF) '⛩️📰 书社 Output: %s\n' $(call quote,$(foreach file,$(sort $(sourcefiles)),$(call destination,$(file))))
+ @$(PRINTF) '%s\n' $(call quote,$(foreach file,$(sort $(sourcefiles)),$(call destination,$(file))))
# Destroy installed files.
uninstall :
@@ -514,7 +521,7 @@ uninstall :
# Raise an error when attempting to build any files with recursive dependencies.
$(call built,$(recursivefiles)) :
- @$(PRINTF) '%b\n' $(call quote,\0033[93;41mError:\0033[39;49m `$(call unbuilt,$@)´ has recursive dependencies:\n$(subst |, ,$(subst $(space),$(newline),$(foreach recursive,$(call recursives,$(call unbuilt,$@)),•|$(recursive))))) && false
+ @$(PRINTF) '%b\n' $(call quote,\0033[93;41mError:\0033[39;49m `$(call unbuilt,$@)´ has recursive dependencies:\n$(subst |, ,$(subst $(space),$(newline),$(foreach recursive,$(call recursives,$(call unbuilt,$@)),•|$(recursive))))) >&2 && false
# Add as a prerequisite to treat the target as tho it were phony.
FORCE : ;
@@ -541,10 +548,10 @@ ifneq ($(typeupdates)$(wildcard $(BUILDDIR)/dependencies)$(wildcard $(BUILDDIR)/
$(THISDIR)/GNUmakefile :: $(BUILDDIR)/dependencies $(BUILDDIR)/destinations
$(silent)$(TOUCH) $(THISDIR)/GNUmakefile
ifeq ($(typeupdates),)
- @if $(TEST) ! -f $(call quote,$(BUILDDIR)/.update-types); then $(PRINTF) '%b\n' '\0033[1mDependency graph and output destinations updated. Restarting…\0033[22m'; fi
+ $(call inform,if $(TEST) ! -f $(call quote,$(BUILDDIR)/.update-types); then $(PRINTF) '%b\n' '\0033[1mDependency graph and output destinations updated. Restarting…\0033[22m' >&2; fi)
else
$(silent)$(RM) $(call quote,$(BUILDDIR)/.update-types)
- @$(PRINTF) '%b\n' '\0033[1mDependency graph and output destinations updated. Restarting…\0033[22m'
+ $(call inform,$(PRINTF) '%b\n' '\0033[1mDependency graph and output destinations updated. Restarting…\0033[22m' >&2)
endif
endif
@@ -557,7 +564,7 @@ ifeq ($(typeupdates),)
$(THISDIR)/GNUmakefile :: $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt
$(silent)$(TOUCH) $(THISDIR)/GNUmakefile
$(silent)$(RM) -f $(call quote,$(BUILDDIR)/dependencies) $(call quote,$(BUILDDIR)/destinations)
- @$(PRINTF) '%b\n' '\0033[1mMagic file or parsers have updated. Restarting…\0033[22m'
+ $(call inform,$(PRINTF) '%b\n' '\0033[1mMagic file or parsers have updated. Restarting…\0033[22m' >&2)
$(silent)$(SLEEP) 1
$(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types)
endif
@@ -574,18 +581,18 @@ $(call magicfile,$(MAGIC)) : $(BUILDDIR)/magic/% : $$(call magicsource,$$@)
# It must be updated if any of the files in the magic directory change.
$(BUILDDIR)/magic.mgc : $(call diffprereqs,magic,$(sort $(call magicfile,$(MAGIC))))
$(foreach outdated,$(filter-out $^,$(wildcard $(BUILDDIR)/magic/*)),$(silent)$(RM) $(call quote,$(outdated))$(newline))
- @$(PRINTF) '%s\n' 'Compiling new magic…'
+ $(call inform,$(PRINTF) '%s\n' 'Compiling new magic…' >&2)
$(silent)$(call ensuredirectory,$(dir $@))
$(silent)cd $(call quote,$(BUILDDIR)) && $(FILE) -C -m $(call quote,$(realpath $(BUILDDIR)/magic))
$(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types)
# Generate the main parser.
$(BUILDDIR)/parser.catalog : $(call diffprereqs,parsers,$(sort $(PARSERS)))
- @$(PRINTF) '%s\n' 'Generating catalog of parsers…'
+ $(call inform,$(PRINTF) '%s\n' 'Generating catalog of parsers…' >&2)
$(silent)$(XMLCATALOG) --create --noout $(call quote,$@)
$(foreach parser,$(PARSERS),$(silent){ $(call id,$(parser)); $(PRINTF) '%s\n' $(call quote,$(call fileuri,$(parser))) '--noout' $(call quote,$@); } | $(xargsmultiquote) | $(XARGS) -E '' $(XMLCATALOG) --add uri$(newline))
$(BUILDDIR)/parser.xslt : $(BUILDDIR)/parser.catalog $(THISDIR)/lib/catalog2parser.xslt
- @$(PRINTF) '%s\n' 'Generating main parser…'
+ $(call inform,$(PRINTF) '%s\n' 'Generating main parser…' >&2)
$(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2parser.xslt) $(call quote,$<)
$(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types)
@@ -594,7 +601,7 @@ $(BUILDDIR)/parser.xslt : $(BUILDDIR)/parser.catalog $(THISDIR)/lib/catalog2pars
# Even plain X·M·L files are parsed, because they may contain X·H·T·M·L `