X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/b96a5ac986977275066876a3383966f1f4e24d30..6773582e45aab385c96638fe9e90d811132a1005:/GNUmakefile?ds=sidebyside diff --git a/GNUmakefile b/GNUmakefile index 8417d13..2cb311f 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 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -16,16 +19,20 @@ override define makefileinfo ║│ │║ ║│ • awk │║ ║│ • cat │║ +║│ • cksum │║ ║│ • cp │║ ║│ • date │║ ║│ • echo │║ ║│ • file │║ ║│ • find │║ +║│ • grep │║ ║│ • git (optional) │║ ║│ • ln │║ +║│ • ls │║ ║│ • mkdir (requires support for `-p´) │║ ║│ • mv │║ ║│ • od (requires support for `-t x1´) │║ +║│ • pax (only when generating archives) │║ ║│ • printf │║ ║│ • rm │║ ║│ • sed │║ @@ -35,6 +42,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) │║ @@ -61,6 +69,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´. │║ ║│ │║ @@ -89,16 +101,20 @@ endef # If these are not installed on your computer, or you need to use a different implementation, you can override the appropriate variable. AWK := awk 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 @@ -107,12 +123,23 @@ 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. @@ -127,14 +154,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´. @@ -142,7 +175,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 '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a $(EXTRAFINDRULES),) +FINDRULES := ! '(' '(' -name '[.-]*' -o -name '*[][*?:|$$%\#\\; ]*' -o -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. @@ -166,13 +199,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),) @@ -181,13 +211,28 @@ 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. +# +# • ‹ 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 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 ───────────────────────────────── @@ -212,13 +257,57 @@ 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) 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" | $(TR) ' ' '\n' | $(SED) $(call quote,/^$$/d;s/'/'"'"'/g;s/^/'/;s/$$/'/) | $(TR) '\n' ' ' | $(XARGS) $(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 $(call extracttext,$1) | $(TR) -d '\t\n\f\r ' | $(XARGS) -0 $(PRINTF) 'begin-base64 644 -\n%s\n====\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 + +# ━ § 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. +# +# 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))))))) # 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}/%/{sub("%","0x");cmd="$(XARGS) $(PRINTF) \"%04o\"";printf "%s","\\";printf "%s",$$0|cmd;close(cmd)}' | $(XARGS) -0 $(PRINTF) '%b' +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' # (callable) Percent‐decode the given strings. +# +# ☡ This variable creates a subshell every time it is computed. override perdec = $(shell $(PRINTF) '%s\0450A' $(foreach encoded,$1,$(call quote,$(encoded))) | $(perdeccmd)) # (callable) Percent‐encode the given strings. @@ -227,9 +316,13 @@ override perdec = $(shell $(PRINTF) '%s\0450A' $(foreach encoded,$1,$(call quote # The encoding assumes the input is a “u·r·i component”; e·g that the resulting string should only contain `pchar´ (but can contain any `sub-delim´). # # 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)) # (callable) Percent‐encode each component in the given paths. +# +# ☡ This variable creates a subshell every time it is computed. override pathenc = $(subst %2F,/,$(call perenc,$1)) # (overridable) Collect all of the applicable includes from the includes directory. @@ -240,7 +333,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. @@ -259,9 +352,6 @@ override assetfiles := $(filter-out $(xmlfiles) $(plaintextfiles),$(sourcefiles) # (callable) Get the types of the given files. override typeoffile = $(patsubst $(foreach file,$1,$(file)|%),%,$(filter $(foreach file,$1,$(file)|%),$(types))) -# (callable) Get the identifier for the given parser or transform. -override id = $(or $(shell $(XMLLINT) --xpath '/*/*[local-name()="id" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"]/text()[1]' $1 2> /dev/null),about:shushe?$(or $2,unknown)=$(call pathenc,$(basename $(notdir $1)))) - # Pair each source magic file with its location in the build directory. override magicpair := $(foreach magicfile,$(MAGIC),$(magicfile)|$(BUILDDIR)/magic/$(notdir $(magicfile))) @@ -278,7 +368,9 @@ override sourcepath = $(firstword $(foreach directory,$(SRCDIR),$(if $(filter $( override includepath = $(firstword $(foreach directory,$(INCLUDEDIR),$(if $(filter $(directory)/%,$1),$(patsubst $(directory)/%,%,$1),))) # (callable) Get base64 data u·r·i’s for the given files. -override datauri = $(foreach file,$1,data:$(call typeoffile,$(file));base64,$(shell $(UUENCODE) -m -r $(call quote,$(file)) _ | tr -d ' \n')) +# +# ☡ This variable creates a subshell every time it is computed. +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))))) @@ -304,6 +396,12 @@ 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 parser, transform, or parsed file with its file u·r·i. +override fileuripairs := $(join $(patsubst %,%|,$(PARSERS) $(TRANSFORMS) $(call parsed,$(sourcefiles) $(sourceincludes))),$(call pathenc,$(foreach uriable,$(PARSERS) $(TRANSFORMS) $(call parsed,$(sourcefiles) $(sourceincludes)),file://$(abspath $(uriable))))) + +# (callable) Get the file u·r·is for the given parsers, transforms, or parsed files. +override fileuri = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(fileuripairs)))) + ifneq ($(wildcard $(BUILDDIR)/dependencies),) # Pair each file with a list of dependencies for it. override dependenciesforfile := $(foreach file,$(filter-out $(assetfiles),$(sourcefiles) $(sourceincludes)),$(file)|$(subst $(space),|,$(shell $(CAT) $(call quote,$(BUILDDIR)/dependencies) | $(SED) $(call quote,/^$(call sedesc,$(call localuri,$(file)))$$/$(comma)/^[^ ]/!d;/^ /!d;s/^ //)))) @@ -344,30 +442,43 @@ 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 = $(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 $(PRINTF) '%b%s]]>\n' '\n