X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/8e297e281cd40bf731273482dcf309063220dab6..53a87e096837ce277a5b90303884796627760e2a:/GNUmakefile diff --git a/GNUmakefile b/GNUmakefile index f7809ed..8f62e46 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023, 2024 Lady +# SPDX-FileCopyrightText: 2023, 2024, 2025 Lady # SPDX-License-Identifier: MPL-2.0 SHELL = /bin/sh @@ -83,7 +83,7 @@ override define makefileinfo ║╰────────────────────────────────────────────────────────────╯║ ╟┬ ¶ Copyright & License ─────────────────────────────────────┬╢ ║│ │║ -║│ Copyright © 2023–2024 Lady [@ Ladys Computer]. │║ +║│ Copyright © 2023–2025 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 │║ @@ -188,6 +188,11 @@ EXTRAFINDINCLUDERULES := FINDRULES := '!' '(' '(' -name '[.-]*' -a '!' -name '.' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a '(' $(EXTRAFINDRULES) ')',) FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a '(' $(EXTRAFINDINCLUDERULES) ')',) +# Options to use when calling Make for the first build of a two‐stage build. +# +# This can be used to override variables which are only applicable to the second build. +DATAOPTS := + # File extensions which indicate files in `SRCDIR´ which should be built as part of the first, rather than second, stage of the two‐stage `MODE´. DATAEXT := rdf @@ -197,6 +202,46 @@ DATAEXT := rdf EXTRAFINDDATARULES := FINDDATARULES := -name '.' $(foreach ext,$(DATAEXT), -o -name '$(subst ','"'"',[!.]*.$(ext))')$(if $(EXTRAFINDDATARULES), -a '(' $(EXTRAFINDDATARULES) ')',) +# A semicolon‐separated list of regular expressions which paths should be required to match when finding files. +FINDFILTERONLY := + +# A semicolon‐separated list of regular expressions for paths which should be filtered out when finding files. +FINDFILTEROUT := + +# A semicolon‐separated list of regular expressions for paths which paths should be required to match when finding includes. +# +# This is generally only useful when `SRCDIR´ and `INCLUDEDIR´ point to the same location. +# In that situation, this variable can be used to select certain files as includes, leaving the others to be recognized as sources instead. +# +# Otherwise, appropriately constructing `FINDFILTERONLY´ to look at the base directory of the files it finds should be sufficient. +FINDINCLUDEFILTERONLY := + +# A semicolon‐separated list of regular expressions for paths which should be filtered out in addition to those in `FINDFILTEROUT´ when finding includes. +# +# This is generally only useful when `SRCDIR´ and `INCLUDEDIR´ point to the same location. +# In that situation, this variable can be used to exclude certain files from being recognized as includes, which will make them recognized as sources instead. +# +# Otherwise, appropriately constructing `FINDFILTEROUT´ to look at the base directory of the files it finds should be sufficient. +FINDINCLUDEFILTEROUT := + +# If not empty, the regular expression provided by `FINDFILTERONLY´ is an extended regular expression. +FINDFILTERONLYEXTENDED := + +# If not empty, the regular expression provided by `FINDFILTEROUT´ is an extended regular expression. +# +# By default, this matches `FINDFILTERONLYEXTENDED´ +FINDFILTEROUTEXTENDED := $(FINDFILTERONLYEXTENDED) + +# If not empty, the regular expression provided by `FINDFILTEROUT´ is an extended regular expression. +# +# By default, this matches `FINDFILTERONLYEXTENDED´ +FINDINCLUDEFILTERONLYEXTENDED := $(FINDFILTERONLYEXTENDED) + +# If not empty, the regular expression provided by `FINDINCLUDEFILTEROUT´ is an extended regular expression. +# +# By default, this matches `FINDFILTEROUTEXTENDED´ +FINDINCLUDEFILTEROUTEXTENDED := $(and $(FINDFILTERONLYEXTENDED),$(FINDFILTEROUTEXTENDED),1) + # The list of magic files to use when determining media types. # # Some are provided as part of this repository, but you can add more if you need different media type detection. @@ -275,7 +320,7 @@ export LC_ALL # Posix timezone information. TZ := UTC0 -export UTC0 +export TZ # The default target for this makefile. .DEFAULT_GOAL := all @@ -311,6 +356,9 @@ override not = $(if $1,,1) # (callable) Quote the given string for use within shell calls. override quote = '$(subst ','"'"',$1)' +# (callable) Quote the given string for use defining a variable to send to a submake. +override varquote = $(subst $$,$$$$,$(call quote,$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' @@ -354,7 +402,7 @@ override notbuilding := $(and $(filter help clean,$(MAKECMDGOALS)),$(call not,$( # 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) +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 ───────────────────────────────────── @@ -385,7 +433,7 @@ override id = $(XMLLINT) --noent --nonet --xpath '/*/*[local-name()="id" and nam override extracttext = $(PRINTF) '%s' '' | $(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 $(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 +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 varquote,$3) SRC=$(call varquote,$1) BUILDDIR=$(call varquote,$(BUILDDIR)/archive/$3) DESTDIR=$(call varquote,$(patsubst %/,%,$(dir $2))) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive' $(call varquote,$2); else $(FINALIZE) $(call quote,$1) >|$(call quote,$2); fi # ━ § BEGIN DEFAULT MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ @@ -403,10 +451,10 @@ override attresc = $(subst ",",$(call xmlesc,$1)) override sedesc = $(subst /,[/],$(subst $$,\$$,$(subst *,\*,$(subst .,\.,$(subst [,\[,$(subst ^,\^,$(subst \,\\,$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)),) +sourceincludes := $(foreach dir,$(INCLUDEDIR),$(and $(dir),$(wildcard $(dir)),$(patsubst ./%,%,$(shell $(FIND) $(call quote,$(dir)) '(' $(FINDINCLUDERULES) ')' -a -type f -a -print$(and $(FINDFILTERONLY),$(space)| $(GREP)$(and $(FINDFILTERONLYEXTENDED),$(space)-E) $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDFILTERONLY)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))$(and $(FINDINCLUDEFILTERONLY),$(space)| $(GREP)$(and $(FINDINCLUDEFILTERONLYEXTENDED),$(space)-E) $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDINCLUDEFILTERONLY)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))$(and $(FINDFILTEROUT),$(space)| $(GREP)$(and $(FINDFILTEROUTEXTENDED),$(space)-E) -v $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDFILTEROUT)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))$(and $(FINDINCLUDEFILTEROUT),$(space)| $(GREP)$(and $(FINDINCLUDEFILTEROUTEXTENDED),$(space)-E) -v $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDINCLUDEFILTEROUT)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter))))))))) # (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 -a -print)))) +sourcefiles := $(filter-out $(sourceincludes),$(foreach dir,$(SRCDIR),$(and $(dir),$(wildcard $(dir)),$(patsubst ./%,%,$(shell $(FIND) $(call quote,$(dir)) '(' $(FINDRULES) ')' -a -type f -a -print$(and $(FINDFILTERONLY),$(space)| $(GREP)$(and $(FINDFILTERONLYEXTENDED),$(space)-E) $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDFILTERONLY)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))$(and $(FINDFILTEROUT),$(space)| $(GREP)$(and $(FINDFILTEROUTEXTENDED),$(space)-E) -v $(foreach filter,$(shell $(PRINTF) '%s\n' $(call quote,$(FINDFILTEROUT)) | $(TR) ' ;' '; '),-e $(call quote,$(subst ;, ,$(filter)))))))))) # Figure out the file type of each source file and source include. ifneq ($(wildcard $(BUILDDIR)/magic.mgc),) @@ -440,7 +488,7 @@ override includepath = $(or $(firstword $(foreach directory,$(INCLUDEDIR),$(if $ # (callable) Get base64 data u·r·i’s for the given files. # # ☡ 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')) +override datauri = $(foreach file,$1,data:$(call typeoffile,$(file));base64,$(shell $(UUENCODE) -m $(call quote,$(abspath $(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))))) @@ -460,23 +508,32 @@ override typeupdates := $(and $(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. +# (callable) Get the location of the metadata 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. +# (callable) Get the source files for the given metadata files. 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)))) +override sourceparsedpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes.parsed/$(call includepath,$(file)),sources.parsed/$(call sourcepath,$(file)))) # (callable) Get the location of the transformed X·M·L files for the given source files. override parsed = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourceparsedpair)))) -# (callable) Get the source files for the given parsed file. +# (callable) Get the source files for the given parsed files. override unparsed = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourceparsedpair)))) -# 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))))) +# Pair each source file and include with its parsed result location. +override parsepair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/$(if $(filter $(file),$(sourceincludes)),includes/$(call includepath,$(file)),sources/$(call sourcepath,$(file)))) + +# (callable) Get the location of the parsed results for the given source files. +override parseresult = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(parsepair)))) + +# (callable) Get the source files for the given parsed results. +override parsesource = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(parsepair)))) + +# Pair each build directory, transform, source file, or parsed file, parse result file with its file u·r·i. +override fileuripairs := $(join $(patsubst %,%|,$(BUILDDIR) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)) $(call parseresult,$(sourcefiles) $(sourceincludes))),$(call pathenc,$(foreach uriable,$(BUILDDIR) $(TRANSFORMS) $(sourcefiles) $(sourceincludes) $(call parsed,$(sourcefiles) $(sourceincludes)) $(call parseresult,$(sourcefiles) $(sourceincludes)),file://$(abspath $(uriable))))) # (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)´))) @@ -548,14 +605,14 @@ override installed = $(foreach file,$1,$(DESTDIR)/$(call destination,$(file))) # ─ ¶ Recipe Variable Definitions ───────────────────────────────────── # (callable) Sanitize and wrap the provided plaintext file in X·M·L, printing to `stdout´. -override wrapplaintext = { $(PRINTF) '%s\n%s' '' ''; } +override wrapplaintext = { $(PRINTF) '%s\n%s' '' ''; } # (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*[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}')" +override serializexml = $(SED) "$$($(PRINTF) '%b' '/]*?>//\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,$(abspath $1)) | $(SED) "$$($(PRINTF) '%b' ':a\n/^\\n*$$/{ $$d\n N\n ba\n}')" # ─ ¶ Phony Targets ─────────────────────────────────────────────────── @@ -608,12 +665,6 @@ $(THISDIR)/GNUmakefile : $(BUILDDIR)/transform.xslt # ─ ¶ Build Targets ─────────────────────────────────────────────────── -# Generate R·D·F metadata for files. -$(call metadata,$(sourcefiles) $(sourceincludes)) : % : $$(call datadata,$$@) $(THISDIR)/.metadata-format-changed-since $(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) '<书社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">$(if $(filter $<,$(assetfiles)),,)<书社vocab:hasParsedFile nfo:fileUrl="%s"/>%s' $(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 `