X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/8f60c082aa14e189db3de44d460b76967fa6dd1b..a17c5cb8e0cef1d871cdf0b476d00831ce07b6e7:/GNUmakefile diff --git a/GNUmakefile b/GNUmakefile index efa5c19..da2912d 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -25,11 +25,13 @@ override define makefileinfo ║│ • echo │║ ║│ • file │║ ║│ • find │║ +║│ • grep │║ ║│ • git (optional) │║ ║│ • ln │║ ║│ • mkdir (requires support for `-p´) │║ ║│ • mv │║ ║│ • od (requires support for `-t x1´) │║ +║│ • pax (only when generating archives) │║ ║│ • printf │║ ║│ • rm │║ ║│ • sed │║ @@ -101,10 +103,12 @@ ECHO := echo FILE := file FIND := find GIT := git +GREP := grep LN := ln MKDIR := mkdir MV := mv OD := od +PAX := pax PRINTF := printf RM := rm SED := sed @@ -120,6 +124,16 @@ 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. @@ -134,14 +148,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´. @@ -185,13 +205,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 ───────────────────────────────── @@ -216,6 +248,33 @@ 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´. +# +# ☡ This variable creates a subshell every time it is computed. +override modtime = $(shell if $(STAT) --version 2> /dev/null | $(GREP) -q GNU; then $(DATE) -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) + +# ─ ¶ 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) 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 $(RM) -f $(call quote,$2); $(call extracttext,$1) > $(call quote,$2); elif $(call xpath,/*[local-name()="base64-binary" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(RM) -f $(call quote,$2); $(call extracttext,$1) | $(TR) -d '\t\n\f\r ' | $(UUDECODE) -m -r > $(call quote,$2); elif $(call xpath,/*[local-name()="archive" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(RM) -f $(call quote,$2); $(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 $(RM) -f $(call quote,$2); $(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. @@ -259,7 +318,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. @@ -388,9 +447,6 @@ 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)))) @@ -404,12 +460,6 @@ override wrapplaintext = $(TR) '\000\013\014' '\032\011\012' < $(call quote,$1) # 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*[1-8BCEFbcef];/\0302\0221/g\n s/&\0043x0*1[0-9A-Fa-f];/\0302\0221/g\n s/&\00430*[1-8];/\0302\0221/g\n s/&\00430*1[124-9];/\0302\0221/g\n s/&\00430*2[0-9];/\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}')" -# (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) - $(call quote,$1) - # ─ ¶ Phony Targets ─────────────────────────────────────────────────── # Compile all files, or error if any are recursive. @@ -511,7 +561,7 @@ $(BUILDDIR)/parser.catalog : $(call diffprereqs,parsers,$(sort $(PARSERS))) $(foreach parser,$(PARSERS),$(silent)( $(call id,$(parser)) ) | $(XARGS) -I %% $(XMLCATALOG) --add uri %% $(call quote,$(call fileuri,$(parser))) --noout $(call quote,$@)$(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,$<) + $(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. @@ -521,7 +571,7 @@ $(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,$<),$(call serializexml,$<)) | $(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 $$(TZ= $(DATE) '+%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. # @@ -534,7 +584,7 @@ $(BUILDDIR)/catalog : $(call diffprereqs,sources,$(sort $(sourcefiles) $(sourcei # 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,$<) + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2dependencies.xslt) $(call quote,$<) # Generate a catalog of destinations for files. # @@ -542,7 +592,7 @@ $(BUILDDIR)/dependencies : $(BUILDDIR)/catalog $(call parsed,$(plaintextfiles) $ # 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,$<) + $(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))) @@ -551,19 +601,19 @@ $(BUILDDIR)/transform.catalog : $(call diffprereqs,transforms,$(sort $(TRANSFORM $(foreach transform,$(TRANSFORMS),$(silent)( $(call id,$(transform)) ) | $(XARGS) -I %% $(XMLCATALOG) --add uri %% $(call quote,$(call fileuri,$(transform))) --noout $(call quote,$@)$(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,$<) + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2transform.xslt) $(call quote,$<) # 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) -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,$<) + $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) --stringparam CATALOG 'catalog' --stringparam BUILDTIME $$(TZ= $(DATE) '+%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)if $(call xpath,/*[local-name()="raw-text" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$<); then $(RM) -f $(call quote,$@); $(call extracttext,$<) > $(call quote,$@); elif $(call xpath,/*[local-name()="base64-binary" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$<); then $(RM) -f $(call quote,$@); $(call extracttext,$<) | $(TR) -d '\t\n\f\r ' | $(UUDECODE) -m -r > $(call quote,$@); else $(LN) -s -f $(call quote,$(subst $(space),,$(foreach component,$(subst /, ,$*),../))results/$*) $(call quote,$@); fi + $(silent)$(call processresultto,$<,$@,$*) $(call built,$(filter $(assetfiles),$(sourcefiles))) : $(BUILDDIR)/public/% : $$(call unbuilt,$$@) @$(PRINTF) '%s\n' $(call quote,Building …) $(silent)$(call ensuredirectory,$(dir $@)) @@ -574,3 +624,63 @@ $(call installed,$(recursivefiles) $(installablefiles)) : $(DESTDIR)/% : $(BUILD @$(PRINTF) '%s\n' $(call quote,Installing …) $(silent)$(call ensuredirectory,$(dir $@)) $(silent)$(CP) $(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)$(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)/% : $(foreach file,$(archivefiles),$(BUILDDIR)/files/$(file)) + @$(PRINTF) '%s\n' $(call quote,Archiving …) + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)cd $(call quote,$(BUILDDIR)/files); $(PRINTF) '%s\n' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -w > $(call quote,$(abspath $@)) + +# ━ § END DEFINED MAKE·FILE MODES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +else +$(error Unrecognized MODE: $(MODE)) +endif