X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/c00fbad125caaac8be191801ca47a6971007fbad..cb45fc856ed6f9e7eaf6db12a93372026decd080:/GNUmakefile?ds=sidebyside diff --git a/GNUmakefile b/GNUmakefile index 1697903..7765095 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2023, 2024 Lady +# SPDX-License-Identifier: MPL-2.0 + SHELL = /bin/sh # ━ § BASIC INFORMATION ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -19,24 +22,26 @@ override define makefileinfo ║│ • cksum │║ ║│ • cp │║ ║│ • date │║ -║│ • echo │║ ║│ • file │║ ║│ • find │║ +║│ • grep │║ ║│ • git (optional) │║ ║│ • ln │║ -║│ • mkdir (requires support for `-p´) │║ +║│ • ls │║ +║│ • mkdir │║ ║│ • mv │║ -║│ • od (requires support for `-t x1´) │║ +║│ • od │║ +║│ • pax (only when generating archives) │║ ║│ • printf │║ ║│ • rm │║ ║│ • sed │║ ║│ • sleep │║ -║│ • stat │║ ║│ • test │║ ║│ • touch │║ -║│ • tr (requires support for `-d´) │║ -║│ • uuencode (requires support for `-m´ and `-r´) │║ -║│ • xargs (requires support for `-0´) │║ +║│ • tr │║ +║│ • uuencode │║ +║│ • uudecode │║ +║│ • xargs │║ ║│ • xmlcatalog (provided by libxml2) │║ ║│ • xmllint (provided by libxml2) │║ ║│ • xsltproc (provided by libxslt) │║ @@ -62,6 +67,10 @@ override define makefileinfo ║│ • `make list´: List all recognized source files and their │║ ║│ classification (including media type and dependencies). │║ ║│ │║ +║│ • `make listout´: List out the destinations of all │║ +║│ resulting files (relative to `DESTDIR´), e·g for │║ +║│ processing by another script. │║ +║│ │║ ║│ • `make uninstall´: Remove installed files, but not │║ ║│ `BUILDDIR´. │║ ║│ │║ @@ -93,28 +102,40 @@ CAT := cat CKSUM := cksum CP := cp DATE := date -ECHO := echo FILE := file FIND := find GIT := git +GREP := grep LN := ln +LS := ls MKDIR := mkdir MV := mv OD := od +PAX := pax PRINTF := printf RM := rm SED := sed SLEEP := sleep -STAT := stat TEST := test TOUCH := touch TR := tr +UUDECODE := uudecode UUENCODE := uuencode XARGS := xargs XMLCATALOG := xmlcatalog XMLLINT := xmllint XSLTPROC := xsltproc +# This variable is not used in normal execution. +# +# In the archiving `MODE´, it provides the X·M·L file from which the archive should be constructed. +SRC := + +# This variable is not used in normal execution. +# +# In the archiving `MODE´, it provides a user‐friendly name for the archive. +NAME := + # The directory which contains the source files. # # Multiple directories can be given so long as files with the same name do not exist in each. @@ -129,14 +150,20 @@ SRCDIR := sources INCLUDEDIR := sources/includes # The directory in which to generate temporary buildfiles. +# +# This variable is also used by the archiving `MODE´. BUILDDIR := build # The directory into which to output files on `make install´. +# +# This variable is also used by the archiving `MODE´. DESTDIR := public # The location of this Makefile (and related ⛩️📰 书社 files), relative to the current working directory. # # By default, this is inferred from the variable `MAKEFILE_LIST´. +# +# This variable is also used by the archiving `MODE´. THISDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) # Configuration of `find´. @@ -166,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),) @@ -180,13 +210,25 @@ SRCREV := $(shell $(GIT) describe 2> /dev/null || $(GIT) rev-parse HEAD 2> /dev/ endif endif +# The mode in which to run this make·file. +# Modes essentially allow for the combination of multiple conceptual make·files into a single source. +# +# The following modes are available :— +# +# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:default ›: +# The default mode; typical Shushe behaviours. +# +# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive ›: +# Generates archive files from parse results. +MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:default + # Set to a non·empty value to print all commands as they run. VERBOSE := # The default target for this makefile. .DEFAULT_GOAL := all -# ━ § BEGIN MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# ━ § BEGIN SHARED MAKE·FILE CONSTRUCTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ─ ¶ Non‐Recipe Variable Definitions ───────────────────────────────── @@ -211,6 +253,48 @@ 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. +# +# 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 { $(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 ───────────────────────────────────── + +# (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 + +# Quote standard input in such a way that piping it to xargs will result in it being processed as a single argument. +# +# If standard input ends in a newline, it is stripped; all other newlines are preserved. +override xargsquote = $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/;$$!s/$$/\\/) + +# Quote standard input in such a way that piping it to xargs will result in each line being processed as a single argument. +override xargsmultiquote = $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/) + +# (callable) Test if the provided xpath expression matches the provided document. +override xpath = $(XMLLINT) --xpath $(call quote,$1) $(call quote,$2) > /dev/null 2> /dev/null + +# (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) 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); 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 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:default) + +# ─ ¶ Non‐Recipe Variable Definitions ───────────────────────────────── + # (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. @@ -224,7 +308,7 @@ override diffprereqs = $(if $(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/last override sedesc = $(subst /,[/],$(subst $$,\$$,$(subst *,\*,$(subst .,\.,$(subst [,\[,$(subst ^,\^,$(subst \,\\,$1))))))) # The command to use for percent‐decoding. -override perdeccmd := $(SED) 's/|/%7C/g;s/[\]/%5C/g;s/%[0-9A-Fa-f]\{2\}/|&|/g' | $(TR) '|' '\n' | $(SED) '/^%[0-9A-Fa-f]\{2\}$$/!s/%/|%25|/' | $(TR) '|' '\n' | $(AWK) '$$0!~/%/{printf "%s",$$0}/%/{d="0123456789ABCDEF";v=substr(toupper($$0),2,2);printf "\\%04o",(index(d,substr(v,1,1))-1)*16+index(d,substr(v,2,1))-1}' | $(XARGS) -0 $(PRINTF) '%b' +override perdeccmd := $(SED) 's/|/%7C/g;s/[\]/%5C/g;s/%[0123456789ABCDEFabcdef]\{2\}/|&|/g' | $(TR) '|' '\n' | $(SED) '/^%[0123456789ABCDEFabcdef]\{2\}$$/!s/%/|%25|/' | $(TR) '|' '\n' | $(AWK) '$$0!~/%/{printf "%s",$$0}/%/{d="0123456789ABCDEF";v=substr(toupper($$0),2,2);printf "\\%04o",(index(d,substr(v,1,1))-1)*16+index(d,substr(v,2,1))-1}' | $(xargsquote) | $(XARGS) -E '' $(PRINTF) '%b' # (callable) Percent‐decode the given strings. # @@ -239,7 +323,7 @@ override perdec = $(shell $(PRINTF) '%s\0450A' $(foreach encoded,$1,$(call quote # It is assumed that the given strings do not contain newlines. # # ☡ This variable creates a subshell every time it is computed. -override perenc = $(shell $(PRINTF) '%s\n' $(foreach unencoded,$1,$(call quote,$(unencoded))) | $(OD) -t x1 | $(SED) 's/^[0-9]*//;s/0[Aa]/%&/g;s/2[146-9A-Ea-e]/%&/g;s/3[0-9AaBbDd]/%&/g;s/40/%&/g;s/[46][1-9A-Fa-f]/%&/g;s/[57][0-9Aa]/%&/g;s/5[Ff]/%&/g;s/7[Ee]/%&/g;s/[ ][0-9A-Fa-f]\{2\}/%25&/g' | $(TR) -d ' \n' | $(TR) 'abcdef' 'ABCDEF' | $(perdeccmd)) +override perenc = $(shell $(PRINTF) '%s\n' $(foreach unencoded,$1,$(call quote,$(unencoded))) | $(OD) -t x1 | $(SED) 's/^[0123456789]*//;s/0[Aa]/%&/g;s/2[146789ABCDEabcde]/%&/g;s/3[0123456789AaBbDd]/%&/g;s/40/%&/g;s/[46][123456789ABCDEFabcdef]/%&/g;s/[57][0123456789Aa]/%&/g;s/5[Ff]/%&/g;s/7[Ee]/%&/g;s/[ ][0123456789ABCDEFabcdef]\{2\}/%25&/g' | $(TR) -d ' \n' | $(TR) 'abcdef' 'ABCDEF' | $(perdeccmd)) # (callable) Percent‐encode each component in the given paths. # @@ -254,7 +338,7 @@ sourcefiles := $(filter-out $(sourceincludes),$(shell $(FIND) $(foreach dir,$(SR # Figure out the file type of each source file and source include. ifneq ($(wildcard $(BUILDDIR)/magic.mgc),) -override types := $(shell $(SED) 's/^ *//;s/ *$$//;s/ {2,}/ /g' <<< $(call quote,$(sourcefiles) $(sourceincludes)) | $(TR) ' ' '\n' | $(FILE) -m $(call quote,$(BUILDDIR)/magic.mgc) --mime-type --separator '|' --files-from - | $(SED) 's/| */|/g') +override types := $(shell $(PRINTF) '%s\n' $(call quote,$(sourcefiles) $(sourceincludes)) | $(SED) 's/^ *//;s/ *$$//;s/ {2,}/ /g' | $(TR) ' ' '\n' | $(FILE) -m $(call quote,$(BUILDDIR)/magic.mgc) --mime-type --separator '|' --files-from - | $(SED) 's/| */|/g') endif # Get the list of supported plaintext file types from the parser. @@ -262,8 +346,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)) @@ -291,7 +377,7 @@ override includepath = $(firstword $(foreach directory,$(INCLUDEDIR),$(if $(filt # (callable) Get base64 data u·r·i’s for the given files. # # ☡ This variable creates a subshell every time it is computed. -override datauri = $(foreach file,$1,data:$(call typeoffile,$(file));base64,$(shell $(UUENCODE) -m -r $(call quote,$(file)) _ | tr -d ' \n')) +override datauri = $(foreach file,$1,data:$(call typeoffile,$(file));base64,$(shell $(UUENCODE) -m $(call quote,$(file)) _ | $(SED) '2,$$!d;$$d' | $(TR) -d ' \n')) # Pair each source file and include with its local u·r·i. override sourcelocalpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|about:shushe?$(if $(filter $(file),$(sourceincludes)),include=$(call pathenc,$(call includepath,$(file))),source=$(call pathenc,$(call sourcepath,$(file))))) @@ -363,37 +449,47 @@ override sourcedestinationpair := $(foreach destination,$(destinations),$(call s override destination = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourcedestinationpair)))) # Pair each source file with its compiled location. -override sourcecompiledpair := $(foreach file,$(sourcefiles),$(file)|$(BUILDDIR)/public/$(call destination,$(file))) +override sourcecompiledpair := $(foreach file,$(sourcefiles),$(file)|$(BUILDDIR)/results/$(call destination,$(file))) # (callable) Get the location of the transformed X·M·L files for the given source files. override compiled = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourcecompiledpair)))) -# (callable) Get the source files for the given compiled file. +# (callable) Get the location of the source files for the given compiled file. override uncompiled = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourcecompiledpair)))) +# (callable) Get the location of the final built files for the given source files. +override built = $(foreach file,$1,$(patsubst $(BUILDDIR)/results/%,$(BUILDDIR)/public/%,$(call compiled,$(file)))) + +# (callable) Get the location of the source files for the given built files. +override unbuilt = $(foreach file,$1,$(call uncompiled,$(patsubst $(BUILDDIR)/public/%,$(BUILDDIR)/results/%,$(file)))) + # (callable) Get the installed locations for the given source files. override installed = $(foreach file,$1,$(DESTDIR)/$(call destination,$(file))) endif # ─ ¶ Recipe Variable Definitions ───────────────────────────────────── -# (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 - # (callable) Get the identifier for the given parser or transform. override id = $(XMLLINT) --xpath '/*/*[local-name()="id" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"]/text()[1]' $(call quote,$1) 2> /dev/null || $(PRINTF) '%s\n' $(call quote,about:shushe?$(or $2,unknown)=$(call pathenc,$(basename $(notdir $1)))) # (callable) Sanitize and wrap the provided plaintext file in X·M·L, printing to `stdout´. -override wrapplaintext = $(PRINTF) '%s\n' "$$($(PRINTF) '%b' '\n')" +override wrapplaintext = { $(PRINTF) '%s\n%s' '' ''; } + +# (callable) Check if the provided X·M·L file is X·M·L 1.1, and if so, coerce to X·M·L 1.0 as best as possible, printing the result (or the original file contents) to `stdout´. +# +# The X·M·L declaration will be dropped and character escapes for C0 control codes will be replaced with a literal `U+0091 PRIVATE USE ONE´, which is invalid in X·M·L 1.1, but valid X·M·L 1.0 (making the replacement obvious). +# +# This isn’t a perfect substitution (it makes some assumptions about the format of the underlying X·M·L), but it should be workable for most sensible, welformed files. +override serializexml = $(SED) "$$($(PRINTF) '%b' '/]*?>//\n s/&\0043x0*[12345678BCEFbcef];/\0302\0221/g\n s/&\0043x0*1[0123456789ABCDEFabcdef];/\0302\0221/g\n s/&\00430*[12345678];/\0302\0221/g\n s/&\00430*1[12456789];/\0302\0221/g\n s/&\00430*2[0123456789];/\0302\0221/g\n s/&\00430*3[01];/\0302\0221/g\n}')" < $(call quote,$1) | $(SED) "$$($(PRINTF) '%b' ':a\n/^\\n*$$/{ $$d\n N\n ba\n}')" # ─ ¶ Phony Targets ─────────────────────────────────────────────────── # Compile all files, or error if any are recursive. -all : $(call compiled,$(recursivefiles) $(compilablefiles)) ; +all : $(call built,$(recursivefiles) $(installablefiles)) ; # Destroy buildfiles. clean : - $(if $(BUILDDIR),$(silent)$(RM) -rf $(call quote,$(BUILDDIR)/),) + $(if $(BUILDDIR),$(silent)$(RM) -f -R $(call quote,$(BUILDDIR)/),) # Destroy build directory and installed files. gone : clean uninstall ; @@ -407,15 +503,19 @@ 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)))) # Destroy installed files. uninstall : $(foreach file,$(installablefiles),$(if $(wildcard $(call installed,$(file))),$(silent)$(PRINTF) '%s\n' $(call quote,Removing …)$(newline)$(silent)$(RM) -f $(call quote,$(call installed,$(file)))$(newline),)) # Raise an error when attempting to build any files with recursive dependencies. -$(call compiled,$(recursivefiles)) : - @$(PRINTF) '%b\n' $(call quote,\0033[93;41mError:\0033[39;49m `$(call uncompiled,$@)´ has recursive dependencies:\n$(subst |, ,$(subst $(space),$(newline),$(foreach recursive,$(call recursives,$(call uncompiled,$@)),•|$(recursive))))) && false +$(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 # Add as a prerequisite to treat the target as tho it were phony. FORCE : ; @@ -429,7 +529,7 @@ FORCE : ; .SUFFIXES : ; # Phony rules; always consider these out·of·date. -.PHONY : FORCE all default clean gone info install list uninstall $(call compiled,$(recursivefiles)) ; +.PHONY : FORCE all default clean gone info install list listout uninstall $(call built,$(recursivefiles)) ; ifneq ($(typeupdates)$(wildcard $(BUILDDIR)/dependencies)$(wildcard $(BUILDDIR)/destinations),) # Reload this make·file if the dependency graph or output destinations have changed. @@ -444,7 +544,7 @@ $(THISDIR)/GNUmakefile :: $(BUILDDIR)/dependencies $(BUILDDIR)/destinations 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 else - $(silent)$(RM) $(BUILDDIR)/.update-types + $(silent)$(RM) $(call quote,$(BUILDDIR)/.update-types) @$(PRINTF) '%b\n' '\0033[1mDependency graph and output destinations updated. Restarting…\0033[22m' endif endif @@ -475,19 +575,19 @@ $(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)) - @$(ECHO) "Compiling new magic…" + @$(PRINTF) '%s\n' 'Compiling new magic…' $(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))) - @$(ECHO) "Generating catalog of parsers…" + @$(PRINTF) '%s\n' 'Generating catalog of parsers…' $(silent)$(XMLCATALOG) --create --noout $(call quote,$@) - $(foreach parser,$(PARSERS),$(silent)( $(call id,$(parser)) ) | $(XARGS) -I %% $(XMLCATALOG) --add uri %% $(call quote,$(call fileuri,$(parser))) --noout $(call quote,$@)$(newline)) + $(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 - @$(ECHO) "Generating main parser…" - $(silent)$(XSLTPROC) -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2parser.xslt) $(call quote,$<) + @$(PRINTF) '%s\n' 'Generating main parser…' + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2parser.xslt) $(call quote,$<) $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types) # Parse the files. @@ -497,50 +597,123 @@ $(BUILDDIR)/parser.xslt : $(BUILDDIR)/parser.catalog $(THISDIR)/lib/catalog2pars $(call parsed,$(sourcefiles) $(sourceincludes)) : % : $$(call unparsed,$$@) $(BUILDDIR)/parser.xslt $(typeupdates) @$(PRINTF) '%s\n' $(call quote,Processing `$<´…) $(silent)$(call ensuredirectory,$(dir $@)) - $(silent)$(if $(filter $<,$(assetfiles)),$(PRINTF) '%s\n' $(call quote,) > $(call quote,$@),$(if $(filter $<,$(plaintextfiles)),$(call wrapplaintext,$<),$(CAT) $(call quote,$<)) | $(XSLTPROC) -o $(call quote,$@) --stringparam BUILDTIME $$(TZ= $(DATE) '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$<)) --stringparam SRCTIME $$(TZ= $(STAT) -f '%Sm' -t '%Y-%m-%dT%H:%M:%SZ' $(call quote,$<)) --stringparam CKSUM $$($(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//')$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/parser.xslt) -) + $(silent)$(if $(filter $<,$(assetfiles)),$(PRINTF) '%s\n' $(call quote,) > $(call quote,$@),$(if $(filter $<,$(plaintextfiles)),$(call wrapplaintext,$<),$(call serializexml,$<)) | $(XSLTPROC) --nonet --novalid -o $(call quote,$@) --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$<)) --stringparam SRCTIME '$(call modtime,$<)' --stringparam CKSUM $$($(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//')$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/parser.xslt) -) # Generate a catalog of all parsed files, for use when processing includes. # # This does not depend on actually transforming the files. $(BUILDDIR)/catalog : $(call diffprereqs,sources,$(sort $(sourcefiles) $(sourceincludes))) $(typeupdates) - @$(ECHO) "Generating catalog of parsed files…" + @$(PRINTF) '%s\n' 'Generating catalog of parsed files…' $(silent)$(XMLCATALOG) --create --noout $(call quote,$@) $(foreach source,$(sourcefiles) $(sourceincludes),$(silent)$(XMLCATALOG) --add uri $(call quote,$(call localuri,$(source))) $(call quote,$(call fileuri,$(call parsed,$(source)))#$(if $(filter $(source),$(assetfiles)),asset,xml)) --noout $(call quote,$@)$(newline)) # Build a list of dependencies for each parsed file. $(BUILDDIR)/dependencies : $(BUILDDIR)/catalog $(call parsed,$(plaintextfiles) $(xmlfiles)) $(THISDIR)/lib/catalog2dependencies.xslt - @$(ECHO) "Identifying dependencies…" - $(silent)$(XSLTPROC) -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2dependencies.xslt) $(call quote,$<) + @$(PRINTF) '%s\n' 'Identifying dependencies…' + $(silent)$(TOUCH) $(call quote,$@) + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2dependencies.xslt) $(call quote,$<) # Generate a catalog of destinations for files. # # This depends on parsing non·asset source files, but not assets or includes. # It does not require knowing the dependencies. $(BUILDDIR)/destinations : $(BUILDDIR)/catalog $(call parsed,$(filter-out $(assetfiles),$(sourcefiles))) $(THISDIR)/lib/catalog2destinations.xslt - @$(ECHO) "Identifying output destinations…" - $(silent)$(XSLTPROC) -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2destinations.xslt) $(call quote,$<) + @$(PRINTF) '%s\n' 'Identifying output destinations…' + $(silent)$(TOUCH) $(call quote,$@) + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2destinations.xslt) $(call quote,$<) # Generate the main transform. $(BUILDDIR)/transform.catalog : $(call diffprereqs,transforms,$(sort $(TRANSFORMS))) - @$(ECHO) "Generating catalog of transforms…" + @$(PRINTF) '%s\n' 'Generating catalog of transforms…' $(silent)$(XMLCATALOG) --create --noout $(call quote,$@) - $(foreach transform,$(TRANSFORMS),$(silent)( $(call id,$(transform)) ) | $(XARGS) -I %% $(XMLCATALOG) --add uri %% $(call quote,$(call fileuri,$(transform))) --noout $(call quote,$@)$(newline)) + $(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 $(THISDIR)/lib/catalog2transform.xslt - @$(ECHO) "Generating main transform…" - $(silent)$(XSLTPROC) -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2transform.xslt) $(call quote,$<) + @$(PRINTF) '%s\n' 'Generating main transform…' + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2transform.xslt) $(call quote,$<) -# Generate the output files using the dependencies as necessary. -$(call compiled,$(compilablefiles)) : $(BUILDDIR)/public/% : $$(call parsed,$$(call uncompiled,$$@)) $(BUILDDIR)/transform.xslt $$(call parsed,$$(call dependencies,$$(call uncompiled,$$@))) - $(silent)$(call ensuredirectory,$(dir $@)) - @$(PRINTF) '%s\n' $(call quote,Compiling …) - $(silent)$(XSLTPROC) -o $(call quote,$@) --stringparam CATALOG 'catalog' --stringparam BUILDTIME $$(TZ= $(DATE) '+%Y-%m-%dT%H:%M:%SZ') --stringparam SRCTIME $$(TZ= $(STAT) -f '%Sm' -t '%Y-%m-%dT%H:%M:%SZ' $(call quote,$(call uncompiled,$@))) --stringparam IDENTIFIER $(call quote,$(call localuri,$(call uncompiled,$@))) --stringparam PATH $(call quote,/$*) --stringparam CKSUM $$($(CKSUM) $(call quote,$(call uncompiled,$@)) | $(SED) 's/[ ].*//')$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/transform.xslt) $(call quote,$<) -$(call compiled,$(filter $(assetfiles),$(sourcefiles))) : $(BUILDDIR)/public/% : $$(call uncompiled,$$@) +# Compile the result files using the dependencies as necessary. +$(call compiled,$(compilablefiles)) : $(BUILDDIR)/results/% : $$(call parsed,$$(call uncompiled,$$@)) $(BUILDDIR)/transform.xslt $$(call parsed,$$(call dependencies,$$(call uncompiled,$$@))) @$(PRINTF) '%s\n' $(call quote,Compiling …) $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) --stringparam CATALOG 'catalog' --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam SRCTIME '$(call modtime,$(call uncompiled,$@))' --stringparam IDENTIFIER $(call quote,$(call localuri,$(call uncompiled,$@))) --stringparam PATH $(call quote,/$*) --stringparam CKSUM $$($(CKSUM) $(call quote,$(call uncompiled,$@)) | $(SED) 's/[ ].*//')$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/transform.xslt) $(call quote,$<) + +# Create the final files from the compiled results (or error in the case of recursive ones). +$(call built,$(compilablefiles)) : $(BUILDDIR)/public/% : $(BUILDDIR)/results/% + @$(PRINTF) '%s\n' $(call quote,Building …) + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(RM) -f -R $(call quote,$@) + $(silent)$(call processresultto,$<,$@,$*) +$(call built,$(filter $(assetfiles),$(sourcefiles))) : $(BUILDDIR)/public/% : $$(call unbuilt,$$@) + @$(PRINTF) '%s\n' $(call quote,Building …) + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(RM) -f -R $(call quote,$@) $(silent)$(CP) $(call quote,$<) $(call quote,$@) # Install compiled files (or error in the case of recursive ones). -$(call installed,$(filter $(assetfiles),$(sourcefiles)) $(recursivefiles) $(compilablefiles)) : $(DESTDIR)/% : $(BUILDDIR)/public/% +$(call installed,$(recursivefiles) $(installablefiles)) : $(DESTDIR)/% : $(BUILDDIR)/public/% @$(PRINTF) '%s\n' $(call quote,Installing …) $(silent)$(call ensuredirectory,$(dir $@)) - $(silent)$(CP) $(call quote,$<) $(call quote,$@) + $(silent)$(RM) -f -R $(call quote,$@) + $(silent)$(CP) -R $(call quote,$<) $(call quote,$@) + +# ━ § BEGIN ARCHIVE MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +else ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive) + +# ─ ¶ Non‐Recipe Variable Definitions ───────────────────────────────── + +# Get the list of files in the archive. +ifneq ($(wildcard $(BUILDDIR)/index),) +override archivefiles := $(patsubst ./extracted/%,%,$(shell $(CAT) $(call quote,$(BUILDDIR)/index))) +endif + +# ─ ¶ Recipe Variable Definitions ───────────────────────────────────── + +# ─ ¶ Phony Targets ─────────────────────────────────────────────────── + +# ─ ¶ Special Targets ───────────────────────────────────────────────── + +# Reload this make·file if the archive components have changed. +$(THISDIR)/GNUmakefile : $(BUILDDIR)/index + $(silent)$(TOUCH) $(THISDIR)/GNUmakefile + @$(PRINTF) '%b\n' $(call quote,\0033[1mArchive components for updated. Restarting…\0033[22m) + +# ─ ¶ Build Targets ─────────────────────────────────────────────────── + +# Build the extractor transform for extracting the contents of the archive source file. +$(BUILDDIR)/extractor.xslt : $(SRC) $(THISDIR)/lib/archive2extractor.xslt + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/archive2extractor.xslt) $(call quote,$<) + +# 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. +$(BUILDDIR)/index : $(BUILDDIR)/extractor.xslt $(SRC) + @$(PRINTF) '%s\n' $(call quote,Extracting components for …) + $(silent)$(SLEEP) 1 + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) --writesubtree $(call quote,$(dir $@)) $(call quote,$<) $(call quote,$(SRC)) + +# All archive components are extracted alongside the index. +$(foreach file,$(archivefiles),$(BUILDDIR)/extracted/$(file)) : $(BUILDDIR)/index ; + +# Process each extractor component into its final form. +$(foreach file,$(archivefiles),$(BUILDDIR)/files/$(file)) : $(BUILDDIR)/files/% : $(BUILDDIR)/extracted/% + @$(PRINTF) '%s\n' $(call quote,Building …) + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(RM) -f -R $(call quote,$@) + $(silent)$(call processresultto,$<,$@,$*) + +# Archive all components in the file to the destination. +# +# This is a match‐anything target, with the assumption that this make·file is being called recursively from the default mode. +$(DESTDIR)/% : $(SRC) $(foreach file,$(archivefiles),$(BUILDDIR)/files/$(file)) + @$(PRINTF) '%s\n' $(call quote,Archiving …) + $(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 + +# ━ § END DEFINED MAKE·FILE MODES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +else +$(error Unrecognized MODE: $(MODE)) +endif