X-Git-Url: https://git.ladys.computer/Shushe/blobdiff_plain/84e79eb9ea63d28dfa93dfe7d3acd457905cc53e..f731d563c57d467911e966511245764fb111d653:/GNUmakefile diff --git a/GNUmakefile b/GNUmakefile index c7f615d..95fcbf4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -10,11 +10,10 @@ override define makefileinfo ║╰────────────────────────────╯ ║ ╟┬ ¶ Prerequisites ───────────────────────────────────────────┬╢ ║│ │║ -║│ Requires G·N·U Make, at least version 3.81, a version of │║ -║│ uuencode with base64 support, and the various programs │║ -║│ offered by libxml2 and libxslt. Beyond this, only programs │║ -║│ required by Posix are required, altho there is a chance of │║ -║│ version incompatibilities. The full list of program │║ +║│ Requires G·N·U Make, at least version 3.81, the Fine Free │║ +║│ File Command, and the various programs packaged with │║ +║│ libxml2 and libxslt. Beyond this, only programs specified │║ +║│ by Posix are required. The full list of program │║ ║│ requirements is as follows :— │║ ║│ │║ ║│ • awk │║ @@ -23,16 +22,17 @@ override define makefileinfo ║│ • cksum │║ ║│ • cp │║ ║│ • date │║ -║│ • diff │║ -║│ • file │║ +║│ • diff (with -u) │║ +║│ • file (specifically the Fine Free File Command) │║ ║│ • find │║ ║│ • grep │║ ║│ • git (optional) │║ ║│ • ln │║ +║│ • make (specifically G·N·U Make, version 3.81 or later) │║ ║│ • mkdir │║ ║│ • mv │║ ║│ • od │║ -║│ • pax (only when generating archives) │║ +║│ • pax (ustar format, when generating archives) │║ ║│ • printf │║ ║│ • rm │║ ║│ • sed │║ @@ -83,7 +83,7 @@ override define makefileinfo ║╰────────────────────────────────────────────────────────────╯║ ╟┬ ¶ Copyright & License ─────────────────────────────────────┬╢ ║│ │║ -║│ Copyright © 2023–2024 Lady [@ Ladys 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,11 +149,21 @@ 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. +# +# This variable is also used by the two‐stage `MODE´, where it gives the include directory for the second stage. INCLUDEDIR := $(foreach dir,$(SRCDIR),$(dir)/includes) +# The directory which contains the data files. +# +# Only used in the two‐stage `MODE´, and defaults to that `MODE´ if this directory exists. +# This directory is used as the include directory in the first stage. +# +# Multiple directories can be given so long as files with the same name do not exist in each. +DATADIR := + # The directory in which to generate temporary buildfiles. # -# This variable is also used by the archiving `MODE´. +# This variable is also used by the archiving and two‐stage `MODE´s. BUILDDIR := build # The directory into which to output files on `make install´. @@ -165,17 +175,28 @@ DESTDIR := public # # By default, this is inferred from the variable `MAKEFILE_LIST´. # -# This variable is also used by the archiving `MODE´. +# This variable is also used by the archiving and two‐stage `MODE´s. THISDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) # Configuration of `find´. # # By default, `find´ will ignore files which begin with a period and those which are likely to cause problems for `make´. +# +# These variables are also used by the two‐stage `MODE´. EXTRAFINDRULES := EXTRAFINDINCLUDERULES := -FINDRULES := ! '(' '(' -name '[.-]*' -a ! -name '.' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a '(' $(EXTRAFINDRULES) ')',) +FINDRULES := '!' '(' '(' -name '[.-]*' -a '!' -name '.' -o -name '*[][*?:|$$%\#\\; ]*' -o -name '*[)]' ')' -a -prune ')'$(if $(EXTRAFINDRULES), -a '(' $(EXTRAFINDRULES) ')',) FINDINCLUDERULES := $(FINDRULES)$(if $(EXTRAFINDINCLUDERULES), -a '(' $(EXTRAFINDINCLUDERULES) ')',) +# 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 + +# Rules for identifying files in `SRCDIR´ which should be built as part of the first, rather than second, stage of the two‐stage `MODE´. +# +# By default, this just constructs rules from `DATAEXT´. +EXTRAFINDDATARULES := +FINDDATARULES := -name '.' $(foreach ext,$(DATAEXT), -o -name '$(subst ','"'"',[!.]*.$(ext))')$(if $(EXTRAFINDDATARULES), -a '(' $(EXTRAFINDDATARULES) ')',) + # 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. @@ -233,11 +254,14 @@ endif # The following modes are available :— # # • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:default ›: -# The default mode; typical Shushe behaviours. +# The default mode; typical ⛩📰 书社 behaviours. # # • ‹ urn:fdc:ladys.computer:20231231:Shu1She4:mode:archive ›: # Generates archive files from parse results. -MODE := urn:fdc:ladys.computer:20231231:Shu1She4:mode:default +# +# • ‹ 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) # Set to a non·empty value to silence informative messages. QUIET := @@ -324,16 +348,16 @@ ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:default) # 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) 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. +# (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),,$(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)) +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))) @@ -589,10 +613,10 @@ FORCE : ; .SUFFIXES : ; # Don’t delete these files even if Make is stopped in the process of rebuilding them. -.PRECIOUS : GNUmakefile ; +.PRECIOUS : $(THISDIR)/GNUmakefile ; # Phony rules; always consider these out·of·date. -.PHONY : FORCE all default clean gone info install list listout uninstall $(call built,$(recursivefiles)) ; +.PHONY : FORCE all clean gone help install list listout uninstall $(call built,$(recursivefiles)) ; ifeq ($(notbuilding),) # Reload this make·file if the magic file, parser, dependencies, or destinations have changed. @@ -707,6 +731,12 @@ endif # ─ ¶ Special Targets ───────────────────────────────────────────────── +# Don’t use any implicit rules. +.SUFFIXES : ; + +# Don’t delete these files even if Make is stopped in the process of rebuilding them. +.PRECIOUS : $(THISDIR)/GNUmakefile ; + # Reload this make·file if the archive components have changed. $(THISDIR)/GNUmakefile : $(BUILDDIR)/index $(silent)$(TOUCH) $(THISDIR)/GNUmakefile @@ -722,10 +752,13 @@ $(BUILDDIR)/extractor.xslt : $(SRC) $(THISDIR)/lib/archive2extractor.xslt # Use the extractor transform to extract the archive source file out into its components. # # This target sleeps for 1 second to ensure the resulting index will always be newer than this make·file. +# +# If the archive is empty, the index will not be written by `xsltproc´ and will need to be manually created. $(BUILDDIR)/index : $(BUILDDIR)/extractor.xslt $(SRC) $(call inform,$(PRINTF) '%s\n' $(call quote,Extracting components for …) >&2) $(silent)$(SLEEP) 1 $(silent)$(XSLTPROC) --nonet --novalid -o $(call quote,$@) --writesubtree $(call quote,$(dir $@)) $(call quote,$<) $(call quote,$(SRC)) + $(silent)if $(TEST) ! -e $(call quote,$@); then $(TOUCH) $(call quote,$@); fi # All archive components are extracted alongside the index. $(foreach file,$(archivefiles),$(BUILDDIR)/extracted/$(file)) : $(BUILDDIR)/index ; @@ -744,7 +777,93 @@ $(DESTDIR)/% : $(SRC) $(foreach file,$(archivefiles),$(BUILDDIR)/files/$(file)) $(call inform,$(PRINTF) '%s\n' $(call quote,Archiving …) >&2) $(silent)$(call ensuredirectory,$(dir $@)) $(silent)$(RM) -f -R $(call quote,$@) - $(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) '%s\n' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -r -w $(call quote,$(abspath $@)); else $(PRINTF) '%s\n' $(foreach file,$(archivefiles),$(call quote,$(file))) | $(PAX) -w -x ustar >|$(call quote,$(abspath $@)); fi + $(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 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +else ifeq ($(MODE),urn:fdc:ladys.computer:20231231:Shu1She4:mode:_2stage) + +# ─ ¶ Non‐Recipe Variable Definitions ───────────────────────────────── + +override makefile := $(abspath $(THISDIR)/GNUmakefile) + +# (overridable) Options to use when calling ⛩📰 书社 the first time. +shushedataopts := INCLUDEDIR=$(call quote,$(DATADIR)) BUILDDIR=$(call quote,$(BUILDDIR)/stage1) DESTDIR=$(call quote,$(BUILDDIR)/data) FINDRULES=$(call quote,$(FINDRULES) -a '(' $(FINDDATARULES) ')') FINDINCLUDERULES=$(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) BUILDDIR=$(call quote,$(BUILDDIR)/stage2) FINDRULES=$(call quote,$(FINDRULES) -a '!' '(' $(FINDDATARULES) ')') FINDINCLUDERULES=$(call quote,$(FINDINCLUDERULES)) MODE='urn:fdc:ladys.computer:20231231:Shu1She4:mode:default' + +# ─ ¶ 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 + @$(MAKE) -f $(call quote,$(makefile)) install $(shushedataopts) + $(silent)$(FIND) $(call quote,$(BUILDDIR)/data) '!' -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 + @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts) + @$(MAKE) -f $(call quote,$(makefile)) $(foreach dest,$(patsubst $(BUILDDIR)/data/%,$(DESTDIR)/%,$(shell $(CAT) $(call quote,$(BUILDDIR)/data.out))),$(call quote,$(dest))) + +# List all source files and includes and their computed types. +list : 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 + @{ $(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts); $(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts); } | $(SED) '$$!s/$$/ /' | $(TR) -d '\n' +list1 listout1 : %1 : + @$(MAKE) -f $(call quote,$(makefile)) $* $(shushedataopts) +list2 listout2 : %2 : data + @$(MAKE) -f $(call quote,$(makefile)) $* $(shushesiteopts) + +# Destroy installed files. +uninstall : + @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts) + +# Add as a prerequisite to treat the target as tho it were phony. +FORCE : ; + +# ─ ¶ Special Targets ───────────────────────────────────────────────── + +# 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 ; + +# ─ ¶ 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/%s\n) >$(call quote,$(BUILDDIR)/data.out) + +$(BUILDDIR)/stage1/% : FORCE + @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts) + +$(BUILDDIR)/data/% : FORCE + @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushedataopts) + +$(BUILDDIR)/stage2/% : data + @$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts) + +$(DESTDIR)/% : data + $(if $(shell $(GREP) -F -x $(call quote,$(BUILDDIR)/data/$*) $(BUILDDIR)/data.out),$(call inform $(PRINTF) '%s\n' $(call quote,Copying over …) >&2)$(newline),$(CP) $(call quote,$(BUILDDIR)/data/$*) $(call quote,$@),@$(MAKE) -f $(call quote,$(makefile)) $@ $(shushesiteopts)) # ━ § END DEFINED MAKE·FILE MODES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━