SHELL = /bin/sh

# ━ § BASIC INFORMATION ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
override define makefileinfo
 ╭──────────────────────────────╮
╔╡ ⁌ 🪾📰 Caudex ∷ GNUmakefile ╞═══════════════════════════════╗
║╰──────────────────────────────╯                              ║
╟┬ ¶ Prerequisites ───────────────────────────────────────────┬╢
║│                                                            │║
║│ Requires G·N·U Make, at least version 3.81, and all of the │║
║│ requirements of ⛩️📰 书社, which is included as a git      │║
║│ submodule. Run `make help-shushe´ for more information.    │║
║╰────────────────────────────────────────────────────────────╯║
╟┬ ¶ Usage ───────────────────────────────────────────────────┬╢
║│                                                            │║
║│ • `make all´ (default): Compile, but do not install, all   │║
║│   files.                                                   │║
║│                                                            │║
║│ • `make clean´: Remove `BUILDDIR´.                         │║
║│                                                            │║
║│ • `make clean´: Remove `BUILDDIR´.                         │║
║│                                                            │║
║│ • `make gone´: Remove `BUILDDIR´ and installed files.      │║
║│                                                            │║
║│ • `make help´: Print this message.                         │║
║│                                                            │║
║│ • `make install´: Compile all files and install in         │║
║│   `DESTDIR´.                                               │║
║│                                                            │║
║│ • `make list´: List all recognized source files and their  │║
║│   classification (including media type and dependencies).  │║
║│                                                            │║
║│ • `make uninstall´: Remove installed files, but not        │║
║│   `BUILDDIR´.                                              │║
║│                                                            │║
║│ • `make +⟨CATEGORY⟩´: Create a new entry in ⟨CATEGORY⟩ and │║
║│   output its path.                                         │║
║│                                                            │║
║│ Set `VERBOSE=1´ to see the text of commands as they are    │║
║│ executed.                                                  │║
║│                                                            │║
║│ See `README.markdown´ for a more involved description of   │║
║│ the capabilities and configuration of this program.        │║
║╰────────────────────────────────────────────────────────────╯║
╟┬ ¶ Copyright & License ─────────────────────────────────────┬╢
║│                                                            │║
║│ Copyright © 2024 Lady [@ Lady’s 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  │║
║│ not distributed with this file, You can obtain one at      │║
║│ <http://mozilla.org/MPL/2.0/>.                             │║
║╰────────────────────────────────────────────────────────────╯║
╚══════════════════════════════════════════════════════════════╝
endef

# ━ § MAKE·FILE SETUP ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# Programs needed to run this make·file.
#
# If these are not installed on your computer, or you need to use a different implementation, you can override the appropriate variable.
#
# See also the documentation for ⛩️📰 书社, which specifies additional programs.
AWK := awk
CAT := cat
ECHO := echo
GIT := git
MKDIR := mkdir
OD := od
PRINTF := printf
RM := rm
SED := sed
TEST := test
TR := tr
XARGS := xargs
XMLCATALOG := xmlcatalog

# The directory which contains the codex entries and related metadata.
SRCDIR := entries

# The directory which contains additional assets for the codex generator.
ASSETDIR := assets

# The directory which contains includes for additional assets for the codex generator.
ASSETINCLUDEDIR := $(ASSETDIR)/includes

# The directory in which to generate temporary buildfiles.
BUILDDIR := build

# The directory into which to output files on `make´.
DESTDIR := public

# The location of this Makefile (and related ⛩️📰 书社 files), relative to the current working directory.
#
# By default, this is inferred from the variable `MAKEFILE_LIST´.
THISDIR := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))

# Extra magic files to forward to ⛩️📰 书社.
#
# Your computer probably has several already installed at `/usr/share/file/magic´.
EXTRAMAGIC :=

# Extra parsers to forward to ⛩️📰 书社.
#
# Which parsers are provided will influence which kinds of files are recognized as plaintext.
EXTRAPARSERS :=

# Extra transforms to forward to ⛩️📰 书社.
EXTRATRANSFORMS :=

# Variables to communicate to further calls to Make.
#
# By default, `MAKEOVERRIDES´ contains all variable overrides specified on the commandline.
# However, some values are specific to 🪾📰 Caudex and may have different meaning in other make·files; filter these out.
MAKEOVERRIDES := $(filter-out SRCDIR=% ASSETDIR=% BUILDDIR=% DESTDIR=% THISDIR=% EXTRAMAGIC=% EXTRAPARSERS=% EXTRATRANSFORMS=%,$(MAKEOVERRIDES))

# The location of ⛩️📰 书社.
SHUSHE := $(THISDIR)/.⛩️📰

# The name of the generator program.
GENERATOR := 🪾📰 Caudex

# A description of the current git revision of 🪾📰 Caudex.
VERSION := $(shell cd $(THISDIR); $(GIT) describe 2> /dev/null || $(GIT) rev-parse HEAD 2> /dev/null || true)

# Set to a non·empty value to print all commands as they run.
VERBOSE :=

# The default target for this makefile.
.DEFAULT_GOAL := all

# ━ § BEGIN MAKE·FILE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

# ─ ¶ Non‐Recipe Variable Definitions ─────────────────────────────────

# A variable which contains a newline, to allow the generation of multiline strings in function calls.
override define newline


endef

# (callable) Quote the given string for use within shell calls.
override quote = '$(subst ','"'"',$1)'

# Outputs an `@´ to silence rules, unless `VERBOSE´ is nonempty.
override silent := $(if $(VERBOSE),,@)

# The list of categories, determined by the set of directories in `SRCDIR´ with corresponding metadata files.
override categories := $(sort $(patsubst $(SRCDIR)/%/@,%,$(wildcard $(SRCDIR)/*/@)))

# (callable) The set of file locations for the entries of the provided categories.
override entriesforcategory = $(sort $(foreach category,$1,$(wildcard $(SRCDIR)/$(category)/???-????) $(wildcard $(SRCDIR)/$(category)/???-????,*)))

# A random W·R·M·G(·like) base⹀32 identifier.
#
# This variable returns a different value each time it is referenced.
# Use `foreach´ to save the value for repeated usage.
#
# This variable recursively calls itself to ensure there is no existing entry with the resulting identifier.
override wrmg = $(foreach attempt,$(shell $(OD) -t u4 -N 4 /dev/random | $(SED) 's/^[0-9]* *//' | $(TR) -d ' \n' | $(AWK) 'BEGIN{chars="0123456789ABCDEFGHJKMNPQRSTVWXYZ&~@=U"}{d=32^5;r=$$0%(32^6);printf("%s",substr(chars,r%37,1));while(d>0){printf("%s", substr(chars,int(r/d),1));if(d==32^4)printf("%s", "-");r%=d;d=int(d/32)}}'),$(if $(wildcard $(SRCDIR)/*/($(attempt))*),$(call wrmg),$(attempt)))

# (callable) Returns the identifiers of the provided entries.
override identifier = $(shell $(TR) ' ' '\n' <<< $(call quote,$1) | $(SED) 's/^\(.*[/]\)\{0,1\}\(.\{3\}-.\{4\}\)\(,[^/]*\)\{0,1\}$$/\2/')

# Output locations for compiled catalog files for categories.
override categorycatalogs := $(patsubst %,$(BUILDDIR)/catalogs/categories/%/index,$(categories))

# Output locations for compiled catalog files for entries.
override entrycatalogs := $(foreach entry,$(call entriesforcategory,$(categories)),$(patsubst $(SRCDIR)/%,$(BUILDDIR)/catalogs/entries/%,$(entry)))

# Output locations for all compiled catalog files.
override allcatalogs := $(BUILDDIR)/catalogs/indices/index $(BUILDDIR)/catalogs/indices/fullindex $(categorycatalogs) $(entrycatalogs)

# (overridable) Options to pass to ⛩️📰 书社.
override shusheopts := SRCDIR=$(call quote,$(if $(wildcard $(ASSETDIR)),$(ASSETDIR),) $(if $(entrycatalogs),$(BUILDDIR)/catalogs/entries,) $(BUILDDIR)/catalogs/indices) BUILDDIR=$(call quote,$(BUILDDIR)/⛩️📰) INCLUDEDIR=$(call quote,$(SRCDIR) $(if $(wildcard $(ASSETINCLUDEDIR)),$(ASSETINCLUDEDIR),) $(if $(categorycatalogs),$(BUILDDIR)/catalogs/categories,)) DESTDIR=$(call quote,$(DESTDIR)) EXTRAMAGIC=$(call quote,$(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/magic/*)) $(EXTRAMAGIC))) EXTRAPARSERS=$(call quote,$(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/parsers/*.xslt)) $(EXTRAPARSERS))) EXTRATRANSFORMS=$(call quote,$(sort $(patsubst ./%,%,$(wildcard $(THISDIR)/transforms/*.xslt)) $(EXTRATRANSFORMS))) GENERATOR=$(call quote,$(GENERATOR)) VERSION=$(call quote,$(VERSION))

# ─ ¶ Recipe Variable Definitions ─────────────────────────────────────

# (callable) Check to see if the given directory exists and create it if not.
override ensuredirectory = if $(TEST) ! -d $(call quote,$1); then $(MKDIR) -p $(call quote,$1); fi

# (callable) Test to see if the dependencies provided by the second argument matches the value in the file corresponding to the first argument in `$(BUILDDIR)/lastprereqs´.
# If not, return the third argument, plus an additional rule to save the new prerequisites.
override makeandsavedeps = $(if $(or $(if $2,,1),$(subst $(shell $(CAT) $(call quote,$(BUILDDIR)/lastdeps/$1) 2> /dev/null || true),,$2)),$3$(newline)$(silent)$(call ensuredirectory,$(BUILDDIR)/lastdeps)$(newline)$(silent)$(PRINTF) '%s\n' $(call quote,$2) > $(BUILDDIR)/lastdeps/$1,)

# (callable) Make the catalog for the provided category.
override define makecategorycatalog
@$(PRINTF) '%s\n' $(call quote,Generating catalog for category `$1´…)
$(silent)$(call ensuredirectory,$(BUILDDIR)/catalogs/categories/$1)
$(silent)$(XMLCATALOG) --create --noout $(call quote,$(BUILDDIR)/catalogs/categories/$1/index)
$(silent)$(XMLCATALOG) --add uri '?' 'about:category' --noout $(call quote,$(BUILDDIR)/catalogs/categories/$1/index)
$(silent)$(XMLCATALOG) --add uri '@' $(call quote,$1/@) --noout $(call quote,$(BUILDDIR)/catalogs/categories/$1/index)
$(foreach entry,$(call entriesforcategory,$1),$(silent)$(XMLCATALOG) --add uri $(call quote,$(call identifier,$(entry))) $(call quote,$(patsubst $(SRCDIR)/%,%,$(entry))) --noout $(call quote,$(BUILDDIR)/catalogs/categories/$1/index)$(newline))
endef

# (callable) Make the catalog for the dynamic or static index.
override define makeindexcatalog
@$(PRINTF) '%s\n' 'Generating $(if $1,static,dynamic) catalog index…'
$(silent)$(call ensuredirectory,$(BUILDDIR)/catalogs/indices)
$(silent)$(XMLCATALOG) --create --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)
$(silent)$(XMLCATALOG) --add uri '.' '$(if $1,standalone,index).xhtml' --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)
$(silent)$(XMLCATALOG) --add uri '?' 'about:$(if $1,full,)index' --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)
$(silent)$(XMLCATALOG) --add uri '@' '@' --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)
$(foreach category,$(categories),$(silent)$(XMLCATALOG) --add uri $(call quote,$(category)) $(call quote,$(category)/index) --noout $(call quote,$(BUILDDIR)/catalogs/indices/$(if $1,full,)index)$(newline))
endef

# ─ ¶ ⛩️📰 书社 Targets ──────────────────────────────────────────────

# Targets to forward to ⛩️📰 书社.
all install list uninstall : $(SHUSHE)/GNUmakefile catalogs
	$(silent)$(MAKE) -f $(call quote,$<) $(call quote,$@) $(shusheopts)
%-shushe : $(SHUSHE)/GNUmakefile FORCE
	$(silent)$(MAKE) -f $(call quote,$<) $(call quote,$*) $(shusheopts)
build/⛩️📰/% : $(SHUSHE)/GNUmakefile catalogs FORCE
	$(SILENT)$(MAKE) -f $(call quote,$<) $(call quote,$@) $(shusheopts)
public/% : $(SHUSHE)/GNUmakefile catalogs FORCE
	$(SILENT)$(MAKE) -f $(call quote,$<) $(call quote,$@) $(shusheopts)

# ─ ¶ Phony Targets ───────────────────────────────────────────────────

# Generate all catalogs, then remove any which no longer have corresponding source files.
catalogs : $(allcatalogs)
	@$(PRINTF) '%s\n' 'Removing any outdated catalogs…'
	$(foreach deletedcategory,$(filter-out $(categorycatalogs),$(wildcard $(BUILDDIR)/catalogs/categories/*/index)),@$(PRINTF) '%s\n' $(call quote,Removing `$(patsubst $(BUILDDIR)/catalogs/categories/%/index,%,$(deletedcategory))´…)$(newline)$(silent)$(RM) -rf $(call quote,./$(dir $(deletedcategory)))$(newline)$(silent)$(RM) -rf $(call quote,./$(patsubst $(BUILDDIR)/catalogs/categories/%,$(BUILDDIR)/catalogs/entries/%,$(dir $(deletedcategory))))$(newline))
	$(foreach deletedentry,$(filter-out $(entrycatalogs),$(wildcard $(BUILDDIR)/catalogs/entries/*/*)),@$(PRINTF) '%s\n' $(call quote,Removing `$(patsubst $(BUILDDIR)/catalogs/entries/%,%,$(deletedentry))´…)$(newline)$(silent)$(RM) $(call quote,./$(deletedentry))$(newline))

# Destroy buildfiles.
clean:
	$(if $(BUILDDIR),$(silent)$(RM) -rf $(call quote,$(BUILDDIR)/),)

# Provide help.
help :
	@$(PRINTF) '%b' '$(subst $(newline),\n,$(makefileinfo))'

# Destroy build directory and installed files.
gone : clean uninstall ;

# List the i·d’s of all recognized entries.
listids :
	@$(TR) '| ' ' \n' <<< '$(strip $(foreach category,$(categories),\0033[1m$(category)\0033[22m: $(patsubst %,•|%,$(call identifier,$(call entriesforcategory,$(category)))) ))' | $(XARGS) -0 $(PRINTF) '%b'

# Output a random unused identifier.
wrmg :
	@$(ECHO) $(wrmg)

# Create a new entry in the provided category and output its path.
+% : FORCE
	$(silent)$(call ensuredirectory,$(SRCDIR)/$*)
	$(if $(wildcard $(SRCDIR)/$*/@),,$(silent)$(PRINTF) '%b' '%%\nCATEGORY : $*\n%%\n' > '$(SRCDIR)/$*/@')
	$(foreach identifier,$(wrmg),@$(PRINTF) '%b' '%%\nENTRY : $(identifier)\n%%\n\n' > '$(SRCDIR)/$*/$(identifier)'$(newline)@$(ECHO) '$(SRCDIR)/$*/$(identifier)')

# ─ ¶ 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 : ;

# Treat targets with this as a prerequisite as though they were phony.
#
# Mainly useful in pattern rules.
FORCE : ;

# Phony rules; always consider these out·of·date.
.PHONY : FORCE all build catalogs clean gone help install list listids uninstall wrmg ;

# ─ ¶ Build Targets ───────────────────────────────────────────────────

# Initialize the ⛩️📰 书社 repository if it is empty.
$(SHUSHE)/GNUmakefile : $(THISDIR)/%/GNUmakefile :
	@$(PRINTF) '%s\n' $(call quote,Initializing git submodule at `$*´)
	$(silent)cd $(THISDIR) && $(GIT) submodule update --init $(call quote,$*)

# Touch parsers and transforms if the files in `lib/´ have changed.
$(PARSERS) $(TRANSFORMS) : $(wildcard lib/*.xslt)
	$(silent)$(TOUCH) $(call quote,$@)

# Create an empty codex metadata file if none exists.
$(SRCDIR)/@ :
	$(silent)$(PRINTF) '%b' '%%\nCODEX :\n%%\n' > $(call quote,$@)

# Create category catalogs.
$(categorycatalogs) : $(BUILDDIR)/catalogs/categories/%/index : FORCE
	$(call makeandsavedeps,$*,$(call entriesforcategory,$*),$(call makecategorycatalog,$*))

# Create entry catalogs.
$(entrycatalogs) : $(BUILDDIR)/catalogs/entries/% :
	@$(PRINTF) '%s\n' $(call quote,Generating catalog for document `$(call identifier,$*)´…)
	$(silent)$(call ensuredirectory,$(dir $@))
	$(silent)$(XMLCATALOG) --create --noout $(call quote,$@)
	$(silent)$(XMLCATALOG) --add uri '.' '$(call identifier,$*).xhtml' --noout $(call quote,$@)
	$(silent)$(XMLCATALOG) --add uri '?' 'about:entry' --noout $(call quote,$@)
	$(silent)$(XMLCATALOG) --add uri '@' $(call quote,$*) --noout $(call quote,$@)

$(BUILDDIR)/catalogs/indices/index : FORCE | $(SRCDIR)/@
	$(call makeandsavedeps,index,$(categories),$(call makeindexcatalog,))

$(BUILDDIR)/catalogs/indices/fullindex : FORCE | $(SRCDIR)/@
	$(call makeandsavedeps,fullindex,$(categories),$(call makeindexcatalog,1))