X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/d4281613317ae20f8d443ec17b2f0ed5fd62581b..8f60c082aa14e189db3de44d460b76967fa6dd1b:/GNUmakefile diff --git a/GNUmakefile b/GNUmakefile index a784f21..efa5c19 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 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -36,6 +39,7 @@ override define makefileinfo ║│ • touch │║ ║│ • tr (requires support for `-d´) │║ ║│ • uuencode (requires support for `-m´ and `-r´) │║ +║│ • uudecode (requires support for `-m´ and `-r´) │║ ║│ • xargs (requires support for `-0´) │║ ║│ • xmlcatalog (provided by libxml2) │║ ║│ • xmllint (provided by libxml2) │║ @@ -109,6 +113,7 @@ STAT := stat TEST := test TOUCH := touch TR := tr +UUDECODE := uudecode UUENCODE := uuencode XARGS := xargs XMLCATALOG := xmlcatalog @@ -168,13 +173,10 @@ TRANSFORMS := $(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/transforms/*.xslt)) # List of types which should be treated as X·M·L. XMLTYPES := application/xml text/xml -# The name of the generator program. -GENERATOR := ⛩️📰 书社 - ifdef GIT ifneq ($(wildcard $(THISDIR)/.git),) # A description of the current git revision of ⛩️📰 书社. -VERSION := $(shell cd $(THISDIR); $(GIT) describe 2> /dev/null || $(GIT) rev-parse HEAD 2> /dev/null || true) +THISREV := $(shell cd $(THISDIR); $(GIT) describe 2> /dev/null || $(GIT) rev-parse HEAD 2> /dev/null || true) endif ifneq ($(wildcard .git),) @@ -214,6 +216,15 @@ override quote = '$(subst ','"'"',$1)' # Outputs an `@´ to silence rules, unless `VERBOSE´ is nonempty. override silent := $(if $(VERBOSE),,@) +# (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. +# +# 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. +# +# ☡ This variable creates at least one subshell every time it is computed. +override diffprereqs = $(if $(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastprereqs/$1) 2> /dev/null || true),,$2),$2 FORCE$(and $(shell $(call ensuredirectory,$(BUILDDIR)/lastprereqs) && $(PRINTF) '%s\n' $(call quote,$2) > $(BUILDDIR)/lastprereqs/$1),),$2) + # (callable) Escape special characters for use in sed regular expressions. override sedesc = $(subst /,[/],$(subst $$,\$$,$(subst *,\*,$(subst .,\.,$(subst [,\[,$(subst ^,\^,$(subst \,\\,$1))))))) @@ -357,14 +368,20 @@ 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 @@ -378,12 +395,25 @@ override ensuredirectory = if $(TEST) ! -d $(call quote,$1); then $(MKDIR) -p $( 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 = $(TR) '\000\013\014' '\032\011\012' < $(call quote,$1) | $(SED) "$$($(PRINTF) '%b' 's/]]>/]]]]>/g\ns/\0357\0277\0276/�/g\ns/\0357\0277\0277/�/g\n$$!s/\\r$$//g\ns/\\r/\\n/g\n$$!s/\0302\0205$$//g\ns/\0302\0205/\\n/g;s/\0342\0200\0250/\\n/g;s/[\0001-\0010]/�/g;s/[\0016-\0037]/�/g')" | $(XARGS) -0 -J %% $(PRINTF) '%b%s%s\n' '\n' + +# (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*[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. -all : $(call compiled,$(recursivefiles) $(compilablefiles)) ; +all : $(call built,$(recursivefiles) $(installablefiles)) ; # Destroy buildfiles. clean : @@ -408,8 +438,11 @@ 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 : ; # ─ ¶ Special Targets ───────────────────────────────────────────────── @@ -420,7 +453,7 @@ $(call compiled,$(recursivefiles)) : .SUFFIXES : ; # Phony rules; always consider these out·of·date. -.PHONY : all default clean gone info install list uninstall $(call compiled,$(recursivefiles)) ; +.PHONY : FORCE all default clean gone info install list 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. @@ -464,8 +497,7 @@ $(call magicfile,$(MAGIC)) : $(BUILDDIR)/magic/% : $$(call magicsource,$$@) # Generate the compiled magic file from its sources. # # It must be updated if any of the files in the magic directory change. -# It ⁜also⁜ should be updated if any of the files in the magic directory are deleted, but this isn’t tracked presently. -$(BUILDDIR)/magic.mgc : $(call magicfile,$(MAGIC)) +$(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…" $(silent)$(call ensuredirectory,$(dir $@)) @@ -473,7 +505,7 @@ $(BUILDDIR)/magic.mgc : $(call magicfile,$(MAGIC)) $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types) # Generate the main parser. -$(BUILDDIR)/parser.catalog : $(PARSERS) +$(BUILDDIR)/parser.catalog : $(call diffprereqs,parsers,$(sort $(PARSERS))) @$(ECHO) "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)) @@ -489,12 +521,12 @@ $(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 SRCTIME $$(TZ= $(STAT) -f '%Sm' -t '%Y-%m-%dT%H:%M:%SZ' $(call quote,$<)) --stringparam CKSUM $$($(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//')$(if $(GENERATOR), --stringparam GENERATOR $(call quote,$(GENERATOR)))$(if $(VERSION), --stringparam VERSION $(call quote,$(VERSION)),)$(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) -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) -) # Generate a catalog of all parsed files, for use when processing includes. # # This does not depend on actually transforming the files. -$(BUILDDIR)/catalog : $(sourcefiles) $(sourceincludes) $(typeupdates) +$(BUILDDIR)/catalog : $(call diffprereqs,sources,$(sort $(sourcefiles) $(sourceincludes))) $(typeupdates) @$(ECHO) "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)) @@ -513,7 +545,7 @@ $(BUILDDIR)/destinations : $(BUILDDIR)/catalog $(call parsed,$(filter-out $(asse $(silent)$(XSLTPROC) -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2destinations.xslt) $(call quote,$<) # Generate the main transform. -$(BUILDDIR)/transform.catalog : $(TRANSFORMS) +$(BUILDDIR)/transform.catalog : $(call diffprereqs,transforms,$(sort $(TRANSFORMS))) @$(ECHO) "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)) @@ -521,18 +553,24 @@ $(BUILDDIR)/transform.xslt : $(BUILDDIR)/transform.catalog $(THISDIR)/lib/catalo @$(ECHO) "Generating main transform…" $(silent)$(XSLTPROC) -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 PATH $(call quote,/$*) --stringparam CKSUM $$($(CKSUM) $(call quote,$(call uncompiled,$@)) | $(SED) 's/[ ].*//')$(if $(GENERATOR), --stringparam GENERATOR $(call quote,$(GENERATOR)))$(if $(VERSION), --stringparam VERSION $(call quote,$(VERSION)),)$(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) -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,$<) + +# 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 +$(call built,$(filter $(assetfiles),$(sourcefiles))) : $(BUILDDIR)/public/% : $$(call unbuilt,$$@) + @$(PRINTF) '%s\n' $(call quote,Building …) + $(silent)$(call ensuredirectory,$(dir $@)) $(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,$@)