X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/91f3a166f07e5f260310799a1e1687f45f3c9870..7338b131dca1dee3159488ef23b8d2545f1a5289:/GNUmakefile?ds=sidebyside diff --git a/GNUmakefile b/GNUmakefile index c601097..ca6bfcd 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023, 2024 Lady <https://www.ladys.computer/about/#lady> +# SPDX-FileCopyrightText: 2023, 2024, 2025 Lady <https://www.ladys.computer/about/#lady> # SPDX-License-Identifier: MPL-2.0 SHELL = /bin/sh @@ -259,9 +259,9 @@ endif # • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive ›: # Generates archive files from parse results. # -# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:_2stage ›: -# Two‐stage build; runs ⛩📰 书社 twice. -MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:$(if $(DATADIR),$(shell if $(TEST) -d $(call quote,$(DATADIR)); then $(PRINTF) '%s\n' '_2stage'; else $(PRINTF) '%s\n' 'default'; fi),default) +# • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:initial ›: +# The initial mode that this make·file starts in; this builds the magic file and parsers and then recursively calls into the default mode to build the files. +MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:initial # Set to a non·empty value to silence informative messages. QUIET := @@ -275,7 +275,7 @@ export LC_ALL # Posix timezone information. TZ := UTC0 -export UTC0 +export TZ # The default target for this makefile. .DEFAULT_GOAL := all @@ -311,6 +311,51 @@ override not = $(if $1,,1) # (callable) Quote the given string for use within shell calls. override quote = '$(subst ','"'"',$1)' +# The command to use for percent‐decoding. +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}' | $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/;$$!s/$$/\\/) | $(XARGS) -E '' $(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. +# +# This singly‐encodes u·r·i characters and doubly‐encodes other characters, then calls `perdec´ to decode back to a single encoding. +# 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/^[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. +# +# ☡ This variable creates a subshell every time it is computed. +override pathenc = $(subst %2F,/,$(call perenc,$1)) + +# (callable) Create a unique name for the given file, based on its absolute path. +# +# ☡ This variable creates a subshell every time it is computed. +namehash = $(shell $(PRINTF) '%s' $(call quote,$(abspath $1)) | $(CKSUM) | $(SED) 's/ .*//' | $(XARGS) -E '' $(PRINTF) '%X')-$(notdir $1) + +# The location of this make·file. +override makefile := $(abspath $(THISDIR)/GNUmakefile) + +# Non·empty if `help´ or `clean´, and no targets other than `help´ or `clean´, were specified as goals on the commandline. +override notbuilding := $(and $(filter help clean,$(MAKECMDGOALS)),$(call not,$(filter-out help clean,$(MAKECMDGOALS)))) + +# (callable) Tests 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, saves the new value. +# Returns the values plus the file in `$(BUILDDIR)/lastprereqs´, which will always be newer than the target if there was a change. +# +# 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. +# +# If `$(notbuilding)´ is non·empty, this variable produces no result to avoid unnecessary work. +# +# ☡ This variable creates at least one subshell every time it is computed. +override diffprereqs = $(if $(notbuilding),,$(and $(or $(wildcard $(BUILDDIR)/lastprereqs/$1),$(shell $(TOUCH) $(call quote,$(BUILDDIR)/lastprereqs/$1)),1),$(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastprereqs/$1) 2>>/dev/null || :),,$2),$(shell $(call ensuredirectory,$(BUILDDIR)/lastprereqs) && $(PRINTF) '%s\n' $(call quote,$2) >|$(call quote,$(BUILDDIR)/lastprereqs/$1)),)$2 $(BUILDDIR)/lastprereqs/$1) + # ─ ¶ Recipe Variable Definitions ───────────────────────────────────── # Outputs an `@´ to silence rules, unless `VERBOSE´ is non·empty. @@ -325,19 +370,22 @@ override ensuredirectory = if $(TEST) ! -d $(call quote,$1); then $(MKDIR) -p $( # 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/$$/\\/) +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/$$/'/) +override xargsmultiquote := $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;s/$$/'/) # (callable) Test if the provided xpath expression matches the provided document. override xpath = $(XMLLINT) --noent --nonet --xpath $(call quote,$1) $(call quote,$2) >>/dev/null 2>>/dev/null +# (callable) Get the identifier for the given parser or transform. +override id = $(XMLLINT) --noent --nonet --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) Extract the value of the text nodes in the provided X·M·L document and print them to `stdout´. override extracttext = $(PRINTF) '%s' '<transform xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0"><output method="text" encoding="UTF-8"/></transform>' | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite - $(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)) 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 quote,$2); else $(FINALIZE) $(call quote,$1) >|$(call quote,$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 $(makefile) 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 quote,$2); else $(FINALIZE) $(call quote,$1) >|$(call quote,$2); fi # ━ § BEGIN DEFAULT MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -345,20 +393,6 @@ ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:default) # ─ ¶ Non‐Recipe Variable Definitions ───────────────────────────────── -# Non·empty if `help´ or `clean´, and no targets other than `help´ or `clean´, were specified as goals on the commandline. -override notbuilding := $(and $(filter help clean,$(MAKECMDGOALS)),$(call not,$(filter-out help clean,$(MAKECMDGOALS)))) - -# (callable) Tests 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, saves the new value. -# Returns the values plus the file in `$(BUILDDIR)/lastprereqs´, which will always be newer than the target if there was a change. -# -# 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. -# -# If `$(notbuilding)´ is non·empty, this variable produces no result to avoid unnecessary work. -# -# ☡ This variable creates at least one subshell every time it is computed. -override diffprereqs = $(if $(notbuilding),,$(and $(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastprereqs/$1) 2>>/dev/null || :),,$2),$(shell $(call ensuredirectory,$(BUILDDIR)/lastprereqs) && $(PRINTF) '%s\n' $(call quote,$2) >|$(BUILDDIR)/lastprereqs/$1),)$2 $(BUILDDIR)/lastprereqs/$1) - # (callable) Escape special characters for use in X·M·L. override xmlesc = $(subst >,>,$(subst <,<,$(subst &,&,$1))) @@ -368,34 +402,6 @@ override attresc = $(subst ",",$(call xmlesc,$1)) # (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/%[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. -# -# ☡ 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. -# -# This singly‐encodes u·r·i characters and doubly‐encodes other characters, then calls `perdec´ to decode back to a single encoding. -# 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/^[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. -# -# ☡ This variable creates a subshell every time it is computed. -override pathenc = $(subst %2F,/,$(call perenc,$1)) - -# (callable) Create a unique name for the given file, based on its absolute path. -# -# ☡ This variable creates a subshell every time it is computed. -namehash = $(shell $(PRINTF) '%s' $(call quote,$(abspath $1)) | $(CKSUM) | $(SED) 's/ .*//' | $(XARGS) -E '' $(PRINTF) '%X')-$(notdir $1) - # (overridable) Collect all of the applicable includes from the includes directory. sourceincludes := $(if $(and $(INCLUDEDIR),$(wildcard $(INCLUDEDIR))),$(patsubst ./%,%,$(shell $(FIND) $(foreach dir,$(INCLUDEDIR),$(call quote,$(dir))) '(' $(FINDINCLUDERULES) ')' -a -type f -a -print)),) @@ -425,15 +431,6 @@ override assetfiles := $(filter-out $(xmlfiles) $(plaintextfiles),$(sourcefiles) # (callable) Get the types of the given files. override typeoffile = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(types))),application/octet-stream)) -# Pair each source magic file with its location in the build directory. -override magicpair := $(foreach magicfile,$(MAGIC),$(magicfile)|$(BUILDDIR)/magic/$(call namehash,$(magicfile))) - -# (callable) Get the source file for the given magic files. -override magicsource = $(foreach magicpath,$1,$(patsubst %|$(magicpath),%,$(firstword $(filter %|$(magicpath),$(magicpair))))) - -# (callable) Get the build file for the given magic files. -override magicfile = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(magicpair)))) - # (callable) Get the local path for the given source file. override sourcepath = $(or $(firstword $(foreach directory,$(SRCDIR),$(if $(filter .,$(directory)),$(wildcard $1),$(if $(filter $(directory)/%,$1),$(patsubst $(directory)/%,%,$1),)))),$(error Unable to get local path for source file `$1´)) @@ -478,10 +475,10 @@ 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 build directory, parser, transform, source file, or parsed file with its file u·r·i. -override fileuripairs := $(join $(patsubst %,%|,$(BUILDDIR) $(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes))),$(call pathenc,$(foreach uriable,$(BUILDDIR) $(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)),file://$(abspath $(uriable))))) +# Pair each build directory, transform, source file, or parsed file with its file u·r·i. +override fileuripairs := $(join $(patsubst %,%|,$(BUILDDIR) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes))),$(call pathenc,$(foreach uriable,$(BUILDDIR) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)),file://$(abspath $(uriable))))) -# (callable) Get the file u·r·is for the given parsers, transforms, source file or parsed files. +# (callable) Get the file u·r·is for the given transforms, source file or parsed files. override fileuri = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(fileuripairs))),$(error Unable to get file u·r·i for `$(file)´))) ifneq ($(wildcard $(BUILDDIR)/dependencies),) @@ -550,9 +547,6 @@ override installed = $(foreach file,$1,$(DESTDIR)/$(call destination,$(file))) # ─ ¶ Recipe Variable Definitions ───────────────────────────────────── -# (callable) Get the identifier for the given parser or transform. -override id = $(XMLLINT) --noent --nonet --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%s' '<?xml version="1.0"?>' '<script xmlns="http://www.w3.org/1999/xhtml" type="$(call typeoffile,$1)"><![CDATA['; $(TR) '\000\013\014' '\032\011\012' <$(call quote,$1) | $(SED) "$$($(PRINTF) '%b' 's/]]>/]]]]><!\\[CDATA\\[>/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\ns/\0342\0200\0250/\\n/g\ns/[\0001\0002\0003\0004\0005\0006\0007\0010]/�/g\ns/[\0016\0017\0020\0021\0022\0023\0024\0025\0026\0027\0031\0032\0033\0034\0035\0036\0037]/�/g')"; $(PRINTF) '%s\n' ']]></script>'; } @@ -563,25 +557,11 @@ override wrapplaintext = { $(PRINTF) '%s\n%s' '<?xml version="1.0"?>' '<script x # 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' '/<?xml[ \t]\\{1,\\}version=[\0042\0047]1.1/,$${ s/<?xml[^>]*?>/<!--<?xml version=\00421.1\0042?>-->/\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}')" -# (callable) Wraps the provided shell script in a conditional which checks whether a make restart caused by `$(BUILDDIR)/.update-types´ is imminent (i·e, it was created during the course of this run) and only executes the script if it is not. -override unlesstypeswillupdate = $(if $(call not,$(typeupdates)),if $(TEST) ! -e $(call quote,$(BUILDDIR)/.update-types); then ,)$1$(and $(call not,$(typeupdates)),; fi) - # ─ ¶ Phony Targets ─────────────────────────────────────────────────── # Compile all files, or error if any are recursive. all : $(call built,$(recursivefiles) $(installablefiles)) ; -# Destroy buildfiles. -clean : - $(if $(BUILDDIR),$(silent)$(RM) -f -R $(call quote,$(BUILDDIR)/),) - -# Destroy build directory and installed files. -gone : clean uninstall ; - -# Provide help. -help : - @$(PRINTF) '%b' '$(subst $(newline),\n,$(makefileinfo))' - # Install the compiled files into `DESTDIR´, or error if any are recursive. install : $(call installed,$(recursivefiles) $(installablefiles)) ; @@ -595,7 +575,7 @@ listout : # Destroy installed files. uninstall : - $(foreach file,$(installablefiles),$(if $(wildcard $(call installed,$(file))),$(silent)$(PRINTF) '%s\n' $(call quote,Removing </$(patsubst $(DESTDIR)/%,%,$(call installed,$(file)))>…)$(newline)$(silent)$(RM) -f $(call quote,$(call installed,$(file)))$(newline),)) + $(foreach file,$(installablefiles),$(if $(wildcard $(call installed,$(file))),$(silent)$(PRINTF) '%s\n' $(call quote,Removing </$(patsubst $(DESTDIR)/%,%,$(call installed,$(file)))>…)$(newline)$(silent)$(RM) -f -R $(call quote,$(call installed,$(file)))$(newline),)) # Raise an error when attempting to build any files with recursive dependencies. $(call built,$(recursivefiles)) : @@ -616,69 +596,39 @@ FORCE : ; .PRECIOUS : $(THISDIR)/GNUmakefile ; # Phony rules; always consider these out·of·date. -.PHONY : FORCE all clean gone help install list listout uninstall $(call built,$(recursivefiles)) ; +.PHONY : FORCE all install list listout uninstall $(call built,$(recursivefiles)) ; -ifeq ($(notbuilding),) -# Reload this make·file if the magic file, parser, or transform have changed. +# Reload this make·file if the transform has changed. # -# If the magic file or parser changed, then another restart will be required, as the dependencies and destinations will need to be regenerated. -$(THISDIR)/GNUmakefile : $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt $(BUILDDIR)/transform.xslt +# This also captures any changes to dependencies or destinations. +$(THISDIR)/GNUmakefile : $(BUILDDIR)/transform.xslt $(silent)$(TOUCH) $(THISDIR)/GNUmakefile - $(if $(filter $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt,$?),$(call inform,$(PRINTF) '%b\n' '\0033[1mMagic file or parsers have updated. Restarting…\0033[22m' >&2)$(newline)$(silent)$(SLEEP) 1,$(if $(typeupdates),$(silent)$(RM) $(call quote,$(BUILDDIR)/.update-types)$(newline),)$(call inform,$(PRINTF) '%b\n' '\0033[1mDependency graph$(comma) output destinations$(comma) or transforms updated. Restarting…\0033[22m' >&2)) -endif + $(if $(typeupdates),$(silent)$(RM) $(call quote,$(BUILDDIR)/.update-types),) + $(call inform,$(PRINTF) '%b\n' '\0033[1mDependency graph$(comma) output destinations$(comma) and transforms were updated. Restarting…\0033[22m' >&2) # ─ ¶ Build Targets ─────────────────────────────────────────────────── -# Create symbolic links from the build directory’s store of magic files to their corresponding sources. -$(call magicfile,$(MAGIC)) : $(BUILDDIR)/magic/% : $$(call magicsource,$$@) - $(silent)$(call ensuredirectory,$(dir $@)) - $(silent)$(LN) -sf $(call quote,$(realpath $<)) $(call quote,$@) - -# Generate the compiled magic file from its sources. -# -# 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)) - $(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))) - $(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 - $(call inform,$(PRINTF) '%s\n' 'Generating main parser…' >&2) - $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite $(call quote,$(THISDIR)/lib/catalog2parser.xslt) $(call quote,$<) >|$(call quote,$@) - $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types) - # Generate R·D·F metadata for files. -# -# If the types have been updated but `typeupdates´ is not active, this recipe is skipped because a restart is forthcoming. $(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(THISDIR)/.metadata-format-changed-since $(typeupdates) - $(call inform,$(call unlesstypeswillupdate,$(PRINTF) '%s\n' $(call quote,Generating metadata for `$<´…) >&2)) - $(silent)$(call unlesstypeswillupdate,$(call ensuredirectory,$(dir $@)); { if $(TEST) ! -f $(call quote,$(BUILDDIR)/.mtime); then $(PRINTF) '%b' '\n' >|$(call quote,$(BUILDDIR)/.mtime); fi; $(TOUCH) -r $(call quote,$<) $(call quote,$(BUILDDIR)/.mtime); $(DIFF) -u $(call quote,$(BUILDDIR)/.mtime) /dev/null | $(SED) '1!d;s/.* \([^ ]*\) \([^ ]*\).*$$/\1T\2Z/'; $(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//'; } | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) '<?xml version="1.0"?><书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile) xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:" rdf:about="%s" 书社vocab:path="%s" nfo:fileUrl="%s"><nie:interpretedAs>$(if $(filter $<,$(assetfiles)),<nfo:InformationElement nie:mimeType="%s"/>,<nfo:PlainTextDocument nie:mimeType="%s"/>)</nie:interpretedAs><书社vocab:hasParsedFile nfo:fileUrl="%s"/><nfo:fileLastModified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">%s</nfo:fileLastModified><nfo:hasHash nfo:hashAlgorithm="CRC32" nfo:hashValue="%s"/></书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile)>' $(call quote,$(call attresc,$(call localuri,$<))) $(call quote,$(call attresc,$(if $(filter $<,$(sourceincludes)),$(call includepath,$<),$(call sourcepath,$<)))) $(call quote,$(call attresc,$(call fileuri,$<))) $(call quote,$(call attresc,$(call typeoffile,$<))) $(call quote,$(call attresc,$(call fileuri,$(call parsed,$<)))) >|$(call quote,$@)) + $(call inform,$(PRINTF) '%s\n' $(call quote,Generating metadata for `$<´…) >&2) + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent){ if $(TEST) ! -f $(call quote,$(BUILDDIR)/.mtime); then $(PRINTF) '%b' '\n' >|$(call quote,$(BUILDDIR)/.mtime); fi; $(TOUCH) -r $(call quote,$<) $(call quote,$(BUILDDIR)/.mtime); $(DIFF) -u $(call quote,$(BUILDDIR)/.mtime) /dev/null | $(SED) '1!d;s/.* \([^ ]*\) \([^ ]*\).*$$/\1T\2Z/'; $(CKSUM) $(call quote,$<) | $(SED) 's/[ ].*//'; } | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) '<?xml version="1.0"?><书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile) xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:" rdf:about="%s" 书社vocab:path="%s" nfo:fileUrl="%s"><nie:interpretedAs>$(if $(filter $<,$(assetfiles)),<nfo:InformationElement nie:mimeType="%s"/>,<nfo:PlainTextDocument nie:mimeType="%s"/>)</nie:interpretedAs><书社vocab:hasParsedFile nfo:fileUrl="%s"/><nfo:fileLastModified rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime">%s</nfo:fileLastModified><nfo:hasHash nfo:hashAlgorithm="CRC32" nfo:hashValue="%s"/></书社vocab:$(if $(filter $<,$(sourceincludes)),IncludeFile,SourceFile)>' $(call quote,$(call attresc,$(call localuri,$<))) $(call quote,$(call attresc,$(if $(filter $<,$(sourceincludes)),$(call includepath,$<),$(call sourcepath,$<)))) $(call quote,$(call attresc,$(call fileuri,$<))) $(call quote,$(call attresc,$(call typeoffile,$<))) $(call quote,$(call attresc,$(call fileuri,$(call parsed,$<)))) >|$(call quote,$@) # Parse the files. # # Even plain X·M·L files are parsed, because they may contain X·H·T·M·L `<script>´ elements which contain other kinds of data. # Asset files are turned into H·T·M·L embeds pointing to `data:´ U·R·I’s. -# -# If the types have been updated but `typeupdates´ is not active, this recipe is skipped because a restart is forthcoming. $(call parsed,$(sourcefiles) $(sourceincludes)) : % : $$(call unparsed,$$@) $(BUILDDIR)/parser.xslt $(PARSERLIBS) $(typeupdates) - $(call inform,$(call unlesstypeswillupdate,$(PRINTF) '%s\n' $(call quote,Processing `$<´…) >&2)) - $(silent)$(call unlesstypeswillupdate,$(call ensuredirectory,$(dir $@)); $(if $(filter $<,$(assetfiles)),$(PRINTF) '%s\n' $(call quote,<?xml version="1.0"?><object xmlns="http://www.w3.org/1999/xhtml" type="$(call typeoffile,$<)" data="$(call datauri,$<)"/>) >|$(call quote,$@),$(if $(filter $<,$(plaintextfiles)),$(call wrapplaintext,$<),$(call serializexml,$<)) | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$<))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/parser.xslt) - >|$(call quote,$@))) + $(call inform,$(PRINTF) '%s\n' $(call quote,Processing `$<´…) >&2) + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(if $(filter $<,$(assetfiles)),$(PRINTF) '%s\n' $(call quote,<?xml version="1.0"?><object xmlns="http://www.w3.org/1999/xhtml" type="$(call typeoffile,$<)" data="$(call datauri,$<)"/>) >|$(call quote,$@),$(if $(filter $<,$(plaintextfiles)),$(call wrapplaintext,$<),$(call serializexml,$<)) | $(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$<))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/parser.xslt) - >|$(call quote,$@)) # Collect the metadata into a single file, and generate the dependencies and destinations files as side·effects. # # Doing this all in one step reduces the number of calls to `xsltproc´ required, but requires that it be called from the build directory (necessitating a subshell). -# -# If the types have been updated but `typeupdates´ is not active, this recipe is skipped because a restart is forthcoming. $(BUILDDIR)/dependencies $(BUILDDIR)/destinations $(BUILDDIR)/metadata : $(call diffprereqs,metadatas,$(call metadata,$(sort $(sourcefiles) $(sourceincludes)))) $(call parsed,$(filter-out $(assetfiles),$(sourcefiles) $(sourceincludes))) $(THISDIR)/lib/expandmetadata.xslt - $(call inform,$(call unlesstypeswillupdate,$(PRINTF) '%s\n' 'Compiling metadata…' >&2)) - $(silent)$(call unlesstypeswillupdate,{ $(PRINTF) '<?xml version="1.0"?><rdf:RDF xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:"><书社vocab:BuildDirectory nfo:fileUrl="%s"/>' $(call quote,$(call attresc,$(call fileuri,$(BUILDDIR)))); {$(foreach meta,$(call metadata,$(sort $(sourcefiles) $(sourceincludes))), $(CAT) $(call quote,$(meta));) } | $(SED) 's/<?xml version="1.0"?>//g'; $(PRINTF) '%s\n' '</rdf:RDF>'; } | ( $(CD) $(call quote,$(BUILDDIR)); $(XSLTPROC) --nonet --novalid --nomkdir $(call quote,$(abspath $(THISDIR)/lib/expandmetadata.xslt)) - ) | $(XMLLINT) --nonet --nsclean - >|$(call quote,$(BUILDDIR)/metadata)) + $(call inform,$(PRINTF) '%s\n' 'Compiling metadata…' >&2) + $(silent){ $(PRINTF) '<?xml version="1.0"?><rdf:RDF xmlns:nie="http://www.semanticdesktop.org/ontologies/2007/01/19/nie#" xmlns:nfo="http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:书社vocab="urn:fdc:ladys.computer:20231231:Shu1She4:vocab:"><书社vocab:BuildDirectory nfo:fileUrl="%s"/>' $(call quote,$(call attresc,$(call fileuri,$(BUILDDIR)))); {$(foreach meta,$(call metadata,$(sort $(sourcefiles) $(sourceincludes))), $(CAT) $(call quote,$(meta));) } | $(SED) 's/<?xml version="1.0"?>//g'; $(PRINTF) '%s\n' '</rdf:RDF>'; } | ( $(CD) $(call quote,$(BUILDDIR)); $(XSLTPROC) --nonet --novalid --nomkdir $(call quote,$(abspath $(THISDIR)/lib/expandmetadata.xslt)) - ) | $(XMLLINT) --nonet --nsclean - >|$(call quote,$(BUILDDIR)/metadata) # Generate the main transform. # @@ -699,13 +649,13 @@ $(BUILDDIR)/transform.xslt : $(BUILDDIR)/transform.catalog $(BUILDDIR)/metadata # • When the metadata of ⹐source files they depend on⹑ change. # # This is to reduce the number of needless regenerations of files with no substantial change. -$(call compiled,$(compilablefiles)) : $(BUILDDIR)/results/% : $$(call parsed,$$(call uncompiled,$$@)) $$(call parsed,$$(call dependencies,$$(call uncompiled,$$@))) $(BUILDDIR)/transform.catalog $(TRANSFORMLIBS) $$(call metadata,$$(call dependencies,$$(call uncompiled,$$@))) +$(call compiled,$(compilablefiles)) : $(BUILDDIR)/results/% : $$(call parsed,$$(call uncompiled,$$@)) $$(call parsed,$$(call dependencies,$$(call uncompiled,$$@))) $(BUILDDIR)/transform.catalog $(THISDIR)/lib/catalog2transform.xslt $(TRANSFORMLIBS) $$(call metadata,$$(call dependencies,$$(call uncompiled,$$@))) $(call inform,$(PRINTF) '%s\n' $(call quote,Compiling </$*>…) >&2) $(silent)$(call ensuredirectory,$(dir $@)) $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite --stringparam METADATA 'metadata' --stringparam BUILDTIME $$($(DATE) -u '+%Y-%m-%dT%H:%M:%SZ') --stringparam IDENTIFIER $(call quote,$(call localuri,$(call uncompiled,$@)))$(if $(THISREV), --stringparam THISREV $(call quote,$(THISREV)),)$(if $(SRCREV), --stringparam SRCREV $(call quote,$(SRCREV)),) $(call quote,$(BUILDDIR)/transform.xslt) $(call quote,$<) >|$(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/% +$(call built,$(compilablefiles)) : $(BUILDDIR)/public/% : $(BUILDDIR)/results/% $(THISDIR)/lib/archive2extractor.xslt $(call inform,$(PRINTF) '%s\n' $(call quote,Building </$*>…) >&2) $(silent)$(call ensuredirectory,$(dir $@)) $(silent)$(RM) -f -R $(call quote,$@) @@ -789,86 +739,152 @@ $(DESTDIR)/% : $(SRC) $(foreach file,$(archivefiles),$(BUILDDIR)/files/$(file)) $(silent)$(call ensuredirectory,$(BUILDDIR)/files) $(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) '$(if $(archivefiles),%s\n,)' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -r -w $(call quote,$(abspath $@)); else $(PRINTF) '$(if $(archivefiles),%s\n,)' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -w -x ustar >|$(call quote,$(abspath $@)); fi -# ━ § BEGIN TWO‐STEP MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# ━ § BEGIN INITIAL MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -else ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:_2stage) +else ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:initial) # ─ ¶ Non‐Recipe Variable Definitions ───────────────────────────────── -override makefile := $(abspath $(THISDIR)/GNUmakefile) +# Non·empty if this is a two‐step build. +override twostep := $(if $(DATADIR),$(call not,$(shell for dir in $(foreach dir,$(DATADIR),$(call quote,$(dir))); do if $(TEST) '!' -d "$$dir"; then $(PRINTF) '%s\n' '0'; fi; done)),) +# Pair each source magic file with its location in the build directory. +override magicpair := $(foreach magicfile,$(MAGIC),$(magicfile)|$(BUILDDIR)/magic/$(call namehash,$(magicfile))) + +# (callable) Get the source file for the given magic files. +override magicsource = $(foreach magicpath,$1,$(patsubst %|$(magicpath),%,$(firstword $(filter %|$(magicpath),$(magicpair))))) + +# (callable) Get the build file for the given magic files. +override magicfile = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(magicpair)))) + +# Pair each parser with its file u·r·i. +override fileuripairs := $(join $(patsubst %,%|,$(PARSERS)),$(call pathenc,$(foreach uriable,$(PARSERS),file://$(abspath $(uriable))))) + +# (callable) Get the file u·r·is for the given parsers. +override fileuri = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(fileuripairs))),$(error Unable to get file u·r·i for `$(file)´))) + +ifeq ($(twostep),) +# (overridable) Options to use when calling ⛩📰 书社. +shusheopts := MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default' +else # (overridable) Options to use when calling ⛩📰 书社 the first time. shushedataopts := INCLUDEDIR=$(call quote,$(DATADIR)) BUILDDIR=$(call quote,$(BUILDDIR)/data) FINDRULES=$(subst $$,$$$$,$(call quote,$(FINDRULES) -a '(' $(FINDDATARULES) ')')) FINDINCLUDERULES=$(subst $$,$$$$,$(call quote,$(FINDINCLUDERULES))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default' # (overridable) Options to use when calling ⛩📰 书社 the second time. shushesiteopts := INCLUDEDIR=$(call quote,$(INCLUDEDIR) $(BUILDDIR)/data/public) BUILDDIR=$(call quote,$(BUILDDIR)/site) FINDRULES=$(subst $$,$$$$,$(call quote,$(FINDRULES) -a '!' '(' $(FINDDATARULES) ')')) FINDINCLUDERULES=$(subst $$,$$$$,$(call quote,$(FINDINCLUDERULES))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default' +endif # ─ ¶ Recipe Variable Definitions ───────────────────────────────────── # ─ ¶ Phony Targets ─────────────────────────────────────────────────── -# Compile all files, or error if any are recursive. -all : data - @$(MAKE) -f $(call quote,$(makefile)) $(shushesiteopts) - -# Destroy buildfiles. -clean : - $(if $(BUILDDIR),$(silent)$(RM) -f -R $(call quote,$(BUILDDIR)/),) - -# Build the data and remove outdated data files. -data : $(BUILDDIR)/data.out - $(silent)$(FIND) $(call quote,$(BUILDDIR)/data/public) '!' -exec $(GREP) -F -q -x '{}' $(call quote,$<) ';' -a '(' -type d -o -print ')' | $(xargsmultiquote) | $(XARGS) -E '' $(RM) - -# Destroy build directory and installed files. -gone : clean uninstall ; - -# Install the compiled files into `DESTDIR´. -install : all +ifeq ($(twostep),) +all install list listout uninstall : $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt + @$(MAKE) -f $(call quote,$(makefile)) $@ $(shusheopts) +else +all : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data + @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts) +install uninstall : all @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts) @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts) - -# List all source files and includes and their computed types. -list : data +list : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data @$(PRINTF) '%b' $(call quote,\0033[1;7m||Stage|1:||\0033[22;27m\0033[4m||||||||||||||||||||\0033[24m ) | $(TR) ' |' '\n ' @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts) @$(PRINTF) '%b' $(call quote, \0033[1;7m||Stage|2:||\0033[22;27m\0033[4m||||||||||||||||||||\0033[24m ) | $(TR) ' |' '\n ' @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts) -listout : data +listout : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data @{ $(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts); $(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts); } | $(SED) '$$!s/$$/ /' | $(TR) -d '\n' -list1 listout1 : %1 : +list1 listout1 : %1 : $(BUILDDIR)/data/magic.mgc $(BUILDDIR)/data/parser.xslt @$(MAKE) -f $(call quote,$(makefile)) $* $(shushedataopts) -list2 listout2 : %2 : data +list2 listout2 : %2 : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data @$(MAKE) -f $(call quote,$(makefile)) $* $(shushesiteopts) +endif -# Destroy installed files. -uninstall : - @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts) +# Destroy buildfiles. +clean : + $(if $(BUILDDIR),$(silent)$(RM) -f -R $(call quote,$(BUILDDIR)/),) + +ifneq ($(twostep),) +# Build the data and remove outdated data files. +data : $(BUILDDIR)/data.out + $(silent)$(FIND) $(call quote,$(BUILDDIR)/data/public) '!' '(' -exec $(GREP) -F -q -x '{}' $(call quote,$<) ';' -a -prune ')' -a '(' -type d -o -print ')' | $(xargsmultiquote) | $(XARGS) -E '' $(RM) +endif + +# Provide help. +help : + @$(PRINTF) '%b' '$(subst $(newline),\n,$(makefileinfo))' + +# Destroy build directory and installed files. +gone : uninstall clean ; # Add as a prerequisite to treat the target as tho it were phony. FORCE : ; # ─ ¶ Special Targets ───────────────────────────────────────────────── +# Perform secondary expansion; this enables pattern rules to determine their prerequisites based on the matched pattern. +.SECONDEXPANSION : ; + # Don’t use any implicit rules. .SUFFIXES : ; # Phony rules; always consider these out·of·date. -.PHONY : FORCE all data clean gone install list listout uninstall ; +.PHONY : FORCE all clean gone install list listout uninstall$(if $(DATADIR), data list1 listout1 list2 listout2,) ; # ─ ¶ Build Targets ─────────────────────────────────────────────────── -$(BUILDDIR)/data.out : FORCE - @$(MAKE) -f $(call quote,$(makefile)) $(shushedataopts) - @$(MAKE) -s -f $(call quote,$(makefile)) listout QUIET=1 $(shushedataopts) | $(TR) ' ' '\n' | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) $(call quote,$(BUILDDIR)/data/public/%s\n) >$(call quote,$(BUILDDIR)/data.out) +# Create symbolic links from the build directory’s store of magic files to their corresponding sources. +$(call magicfile,$(MAGIC)) : $(BUILDDIR)/magic/% : $$(call magicsource,$$@) + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(LN) -sf $(call quote,$(realpath $<)) $(call quote,$@) -$(BUILDDIR)/data/% : FORCE - @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts) +# Generate the compiled magic file from its sources. +# +# 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)) + $(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) -$(BUILDDIR)/site/% : data - @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts) +# Generate the main parser. +$(BUILDDIR)/parser.catalog : $(call diffprereqs,parsers,$(sort $(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 + $(call inform,$(PRINTF) '%s\n' 'Generating main parser…' >&2) + $(silent)$(XSLTPROC) --nonet --novalid --nomkdir --nowrite $(call quote,$(THISDIR)/lib/catalog2parser.xslt) $(call quote,$<) >|$(call quote,$@) + $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types) + +ifneq ($(twostep),) +$(BUILDDIR)/data/magic.mgc $(BUILDDIR)/data/parser.xslt : $(BUILDDIR)/data/% : $(BUILDDIR)/$$* + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(LN) -sf $(call quote,$(realpath $<)) $(call quote,$@) +$(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt : $(BUILDDIR)/site/% : $(BUILDDIR)/$$* + $(silent)$(call ensuredirectory,$(dir $@)) + $(silent)$(LN) -sf $(call quote,$(realpath $<)) $(call quote,$@) +$(BUILDDIR)/data.out : $(BUILDDIR)/data/magic.mgc $(BUILDDIR)/data/parser.xslt FORCE + @$(MAKE) -f $(call quote,$(makefile)) $(shushedataopts) + @$(MAKE) -s -f $(call quote,$(makefile)) listout QUIET=1 $(shushedataopts) | $(TR) ' ' '\n' | $(xargsmultiquote) | $(XARGS) -E '' $(PRINTF) $(call quote,$(BUILDDIR)/data/public/%s\n) >$(call quote,$(BUILDDIR)/data.out) +endif -$(DESTDIR)/% : data - @$(MAKE) -f $(call quote,$(makefile)) $@ $(if $(shell $(GREP) -F -x $(call quote,$(BUILDDIR)/data/$*) $(BUILDDIR)/data.out),$(shushedataopts),$(shushesiteopts)) +ifeq ($(twostep),) +$(BUILDDIR)/lastprereqs/% : ; +$(BUILDDIR)/% : $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt FORCE + @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(shusheopts) +$(DESTDIR)/% : $(BUILDDIR)/magic.mgc $(BUILDDIR)/parser.xslt FORCE + @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(shusheopts) +else +$(BUILDDIR)/data/% : $(BUILDDIR)/data/magic.mgc $(BUILDDIR)/data/parser.xslt FORCE + @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(shushedataopts) +$(BUILDDIR)/site/% : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data + $(silent)$(call ensuredirectory,$(dir $@)) + @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(shushesiteopts) +$(DESTDIR)/% : $(BUILDDIR)/site/magic.mgc $(BUILDDIR)/site/parser.xslt data + @$(MAKE) -f $(call quote,$(makefile)) $(call quote,$@) $(if $(shell $(GREP) -F -x $(call quote,$(BUILDDIR)/data/$*) $(BUILDDIR)/data.out),$(shushedataopts),$(shushesiteopts)) +endif # ━ § END DEFINED MAKE·FILE MODES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━