X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/655dfa89c0782dd1252cdca50ef4182e4dc8cf95..44ad50cac2e0fb39ba3cf8b3d5bdf3840e47dc34:/GNUmakefile diff --git a/GNUmakefile b/GNUmakefile index 3db4e3c..d91dd30 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,7 +6,7 @@ SHELL = /bin/sh # ━ § BASIC INFORMATION ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ override define makefileinfo ╭────────────────────────────╮ -╔╡ ⁌ ⛩️📰 书社 ∷ GNUmakefile ╞═════════════════════════════════╗ +╔╡ ⁌ ⛩📰 书社 ∷ GNUmakefile ╞═════════════════════════════════╗ ║╰────────────────────────────╯ ║ ╟┬ ¶ Prerequisites ───────────────────────────────────────────┬╢ ║│ │║ @@ -83,7 +83,7 @@ override define makefileinfo ║╰────────────────────────────────────────────────────────────╯║ ╟┬ ¶ Copyright & License ─────────────────────────────────────┬╢ ║│ │║ -║│ Copyright © 2023–2024 Lady [@ Lady’s Computer]. │║ +║│ Copyright © 2023–2024 Lady [@ Ladys Computer]. │║ ║│ │║ ║│ This Source Code Form is subject to the terms of the │║ ║│ Mozilla Public License, v 2.0. If a copy of the M·P·L was │║ @@ -149,7 +149,7 @@ SRCDIR := sources # Multiple directories can be given so long as files with the same name do not exist in each. # # These can be inside of `SRCDIR´ directories if desired. -INCLUDEDIR := $(SRCDIR)/includes +INCLUDEDIR := $(foreach dir,$(SRCDIR),$(dir)/includes) # The directory in which to generate temporary buildfiles. # @@ -161,7 +161,7 @@ BUILDDIR := build # 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. +# The location of this Makefile (and related ⛩📰 书社 files), relative to the current working directory. # # By default, this is inferred from the variable `MAKEFILE_LIST´. # @@ -173,8 +173,8 @@ 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 '[.-]*' -a ! -name '.' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a $(EXTRAFINDRULES),) -FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a $(EXTRAFINDINCLUDERULES),) +FINDRULES := ! '(' '(' -name '[.-]*' -a ! -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. # @@ -200,9 +200,12 @@ TRANSFORMS := $(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/transforms/*.xslt)) # ends accordingly. XMLTYPES := application/xml text/xml +xml +# A program to run on basic X·M·L files after they are transformed. +FINALIZE := $(XMLLINT) --nonet --nsclean + ifdef GIT ifneq ($(wildcard $(THISDIR)/.git),) -# A description of the current git revision of ⛩️📰 书社. +# A description of the current git revision of ⛩📰 书社. THISREV := $(shell $(CD) $(THISDIR); $(GIT) describe 2>>/dev/null || $(GIT) rev-parse HEAD 2>>/dev/null || :) endif @@ -230,6 +233,14 @@ QUIET := # Set to a non·empty value to print all commands as they run. VERBOSE := +# Posix locale information. +LC_ALL := C +export LC_ALL + +# Posix timezone information. +TZ := UTC0 +export UTC0 + # The default target for this makefile. .DEFAULT_GOAL := all @@ -261,16 +272,6 @@ override comma := , # (callable) Quote the given string for use within shell calls. override quote = '$(subst ','"'"',$1)' -# (callable) Get the modified time of the provided file. -# -# This touches a file containing only a newline and then diffs it with `/dev/null´ and extracts the timestamp from the diff. -# Interestingly, on Macintosh, the format of `diff -u´ is only Posixy (including a fractional component and timezone) when `COMMAND_MODE=legacy´; however, the timezone will always be zero and the fractional component is ignorable, so it’s not necessary to worry about that here. -# -# The diff will always have an exit status of 1, but this is ignored by piping into Sed. -# -# ☡ This variable creates a subshell every time it is computed. -override modtime = $(shell if $(TEST) ! -f $(call quote,$(BUILDDIR)/.mtime); then $(PRINTF) '%b' '\n' > $(call quote,$(BUILDDIR)/.mtime); fi; $(TOUCH) -r $(call quote,$1) $(call quote,$(BUILDDIR)/.mtime); TZ=UTC0 $(DIFF) -u $(call quote,$(BUILDDIR)/.mtime) /dev/null | $(SED) '1!d;s/.* \([^ ]*\) \([^ ]*\).*$$/\1T\2Z/') - # ─ ¶ Recipe Variable Definitions ───────────────────────────────────── # Outputs an `@´ to silence rules, unless `VERBOSE´ is non·empty. @@ -291,13 +292,13 @@ override xargsquote = $(SED) $(call quote,s/'/'"'"'/g;s/^/'/;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) --xpath $(call quote,$1) $(call quote,$2) >>/dev/null 2>>/dev/null +override xpath = $(XMLLINT) --noent --nonet --xpath $(call quote,$1) $(call quote,$2) >>/dev/null 2>>/dev/null # (callable) Extract the value of the text nodes in the provided X·M·L document and print them to `stdout´. override extracttext = $(PRINTF) '%s' '' | $(XSLTPROC) --nonet --novalid - $(call quote,$1) # (callable) Process the provided transformation result and output the result to the provided location, given the provided relative path. -override processresultto = if $(call xpath,/*[local-name()="raw-text" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(call extracttext,$1) >|$(call quote,$2); elif $(call xpath,/*[local-name()="base64-binary" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then { $(PRINTF) '%s\n' 'begin-base64 644 -'; $(call extracttext,$1) | $(TR) -d '\t\n\f\r '; $(PRINTF) '\n%s\n' '===='; } | $(UUDECODE) -o /dev/stdout >|$(call quote,$2); elif $(call xpath,/*[local-name()="archive" and namespace-uri()="urn:fdc:ladys.computer:20231231:Shu1She4"],$1); then $(MAKE) -f $(call quote,$(abspath $(THISDIR)/GNUmakefile)) 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 $(XMLLINT) --nsclean $(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 $(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 # ━ § BEGIN DEFAULT MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -314,6 +315,12 @@ ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:default) # ☡ 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 || :),,$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 X·M·L. +override xmlesc = $(subst >,>,$(subst <,<,$(subst &,&,$1))) + +# (callable) Escape special characters for use in X·M·L attributes. +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))))))) @@ -340,11 +347,16 @@ override perenc = $(shell $(PRINTF) '%s\n' $(foreach unencoded,$1,$(call quote,$ # ☡ 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))) '(' $(FINDRULES) ')' -a -type f)),) +sourceincludes := $(if $(and $(INCLUDEDIR),$(wildcard $(INCLUDEDIR))),$(patsubst ./%,%,$(shell $(FIND) $(foreach dir,$(INCLUDEDIR),$(call quote,$(dir))) '(' $(FINDINCLUDERULES) ')' -a -type f -a -print)),) # (overridable) Collect all of the applicable source files from the source directory, removing any which are also includes. -sourcefiles := $(if $(and $(SRCDIR),$(wildcard $(SRCDIR))),$(filter-out $(sourceincludes),$(patsubst ./%,%,$(shell $(FIND) $(foreach dir,$(SRCDIR),$(call quote,$(dir))) '(' $(FINDRULES) ')' -a -type f)))) +sourcefiles := $(if $(and $(SRCDIR),$(wildcard $(SRCDIR))),$(filter-out $(sourceincludes),$(patsubst ./%,%,$(shell $(FIND) $(foreach dir,$(SRCDIR),$(call quote,$(dir))) '(' $(FINDRULES) ')' -a -type f -a -print)))) # Figure out the file type of each source file and source include. ifneq ($(wildcard $(BUILDDIR)/magic.mgc),) @@ -353,7 +365,7 @@ endif # Get the list of supported plaintext file types from the parser. ifneq ($(wildcard $(BUILDDIR)/parser.xslt),) -override plaintexttypes := $(filter-out $(XMLTYPES),$(shell $(XMLLINT) --xpath '/*/*[@name="书社:parsers"]//*[namespace-uri()="http://www.w3.org/1999/xhtml" and local-name()="dd"]/text()' $(call quote,$(BUILDDIR)/parser.xslt))) +override plaintexttypes := $(filter-out $(XMLTYPES),$(shell $(XMLLINT) --noent --nonet --xpath '/*/*[@name="书社:parsers"]//*[namespace-uri()="http://www.w3.org/1999/xhtml" and local-name()="dd"]/text()' $(call quote,$(BUILDDIR)/parser.xslt))) endif # (callable) Get all of the files (source and includes) which have the provided types. @@ -370,7 +382,7 @@ override assetfiles := $(filter-out $(xmlfiles) $(plaintextfiles),$(sourcefiles) override typeoffile = $(foreach file,$1,$(or $(patsubst $(file)|%,%,$(filter $(file)|%,$(types))),$(error Unable to get type of file `$(file)´))) # Pair each source magic file with its location in the build directory. -override magicpair := $(foreach magicfile,$(MAGIC),$(magicfile)|$(BUILDDIR)/magic/$(notdir $(magicfile))) +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))))) @@ -404,6 +416,15 @@ override sourcefile = $(foreach local,$1,$(patsubst %|$(local),%,$(filter %|$(lo # This ensures that file classifications are up·to·date immediately after the reload. override typeupdates := $(if $(wildcard $(BUILDDIR)/.update-types),FORCE,) +# Pair each source file and include with its metadata location. +override sourcemetadatapair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes.metadata/$(call includepath,$(file)),sources.metadata/$(call sourcepath,$(file)))) + +# (callable) Get the location of the transformed X·M·L files for the given source files. +override metadata = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourcemetadatapair)))) + +# (callable) Get the source files for the given parsed file. +override datadata = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourcemetadatapair)))) + # Pair each source file and include with its parsed location. override sourceparsedpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes/$(call includepath,$(file)),sources/$(call sourcepath,$(file)))) @@ -413,15 +434,15 @@ 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))))) +# Pair each parser, transform, source file, parsed file, or metadata compilation with its file u·r·i. +override fileuripairs := $(join $(patsubst %,%|,$(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)) $(BUILDDIR)/metadata),$(call pathenc,$(foreach uriable,$(PARSERS) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)) $(BUILDDIR)/metadata,file://$(abspath $(uriable))))) -# (callable) Get the file u·r·is for the given parsers, transforms, or parsed files. +# (callable) Get the file u·r·is for the given parsers, transforms, parsed files, or metadata compilations. 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),) # 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/^ //)))) +override dependenciesforfile := $(foreach file,$(filter-out $(assetfiles),$(sourcefiles) $(sourceincludes)),$(file)|$(subst $(space),|,$(shell $(SED) $(call quote,/^$(call sedesc,$(call localuri,$(file)))$$/$(comma)/^[^ ]/!d;/^ /!d;s/^ //) <$(call quote,$(BUILDDIR)/dependencies)))) # (callable) Get the list of dependency leiris for the given source files. # @@ -486,7 +507,7 @@ endif # ─ ¶ Recipe Variable Definitions ───────────────────────────────────── # (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)))) +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' '' ''; } @@ -519,7 +540,7 @@ install : $(call installed,$(recursivefiles) $(installablefiles)) ; # List all source files and includes and their computed types. list : - @$(PRINTF) '%b' $(call quote,$(foreach file,$(sort $(sourcefiles)) $(sort $(sourceincludes)),\0033[1m$(file)\0033[22m|$(call typeoffile,$(file))|[\0033[3m$(if $(filter $(file),$(xmlfiles)),xml,$(if $(filter $(file),$(plaintextfiles)),text,asset))$(if $(filter $(file),$(sourceincludes)),|include,)\0033[23m] Modified:|$(call modtime,$(file))$(if $(call dependencies,$(file))$(call recursives,$(file)), $(strip $(foreach recursive,$(call recursives,$(file)),\0033[93;41m•|Recursive|Dependency|\0033[39;49m|$(recursive)) $(foreach dependency,$(call dependencies,$(file)),\0033[2m•|Dependency|\0033[22m|$(dependency))))$(if $(filter $(file),$(sourcefiles)), →|<\0033[4m/$(call destination,$(file))\0033[24m>,) )) | $(TR) ' |' '\n ' + @$(PRINTF) '%b' $(call quote,$(foreach file,$(sort $(sourcefiles)) $(sort $(sourceincludes)),\0033[1m$(file)\0033[22m|$(call typeoffile,$(file))|[\0033[3m$(if $(filter $(file),$(xmlfiles)),xml,$(if $(filter $(file),$(plaintextfiles)),text,asset))$(if $(filter $(file),$(sourceincludes)),|include,)\0033[23m]$(if $(call dependencies,$(file))$(call recursives,$(file)), $(strip $(foreach recursive,$(call recursives,$(file)),\0033[93;41m•|Recursive|Dependency|\0033[39;49m|$(recursive)) $(foreach dependency,$(call dependencies,$(file)),\0033[2m•|Dependency|\0033[22m|$(dependency))))$(if $(filter $(file),$(sourcefiles)), →|<\0033[4m/$(call destination,$(file))\0033[24m>,) )) | $(TR) ' |' '\n ' # Lists out the destinations of all resulting files (relative to `DESTDIR´). listout : @@ -609,6 +630,12 @@ $(BUILDDIR)/parser.xslt : $(BUILDDIR)/parser.catalog $(THISDIR)/lib/catalog2pars $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2parser.xslt) $(call quote,$<) $(silent)$(TOUCH) $(call quote,$(BUILDDIR)/.update-types) +# Generate R·D·F metadata for files. +$(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(typeupdates) + $(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) '$(if $(filter $<,$(assetfiles)),,)%s' $(call quote,$(call attresc,$(call localuri,$<))) $(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 `