From: Lady <redacted>
Date: Tue, 16 Jan 2024 05:07:00 +0000 (-0500)
Subject: Enable output redirection with 书社:destination
X-Git-Tag: 0.3.0
X-Git-Url: https://git.ladys.computer/Shushe/commitdiff_plain/0.3.0?ds=sidebyside;hp=63533c3d25fa8837f0d99f8984405320d8c758a6

Enable output redirection with 书社:destination

Specifying this attribute on the root element (after parsing, but
before transformation) will override the default output location. All
of the processing for this can be done at the same time as dependency
detection, as it depends on media typing but not on the dependency
tree.
---

diff --git a/GNUmakefile b/GNUmakefile
index 93a8a13..937ada5 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -318,8 +318,18 @@ override recursivefiles := $(foreach file,$(filter-out $(assetfiles),$(sourcefil
 # This is all of the non·asset, nonrecursive files.
 override compilablefiles := $(filter-out $(assetfiles) $(recursivefiles),$(sourcefiles))
 
+ifneq ($(wildcard $(BUILDDIR)/destinations),)
+# Get the output of the destination transform.
+override destinations := $(shell $(CAT) $(BUILDDIR)/destinations)
+
+# Pair source files and their destinations.
+override sourcedestinationpair := $(foreach destination,$(destinations),$(call sourcefile,$(firstword $(subst |, ,$(destination))))|$(call perdec,$(subst $(space),|,$(wordlist 2,$(words $(subst |, ,$(destination))),$(subst |, ,$(destination))))))
+
+# (callable) Get the destination for the given source files.
+override destination = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,$(sourcedestinationpair))))
+
 # Pair each source file and include with its compiled location.
-override sourcecompiledpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/public/$(call sourcepath,$(file)))
+override sourcecompiledpair := $(foreach file,$(sourcefiles) $(sourceincludes),$(file)|$(BUILDDIR)/public/$(call destination,$(file)))
 
 # (callable) Get the location of the transformed X·M·L files for the
 # given source files.
@@ -329,7 +339,8 @@ override compiled = $(foreach file,$1,$(patsubst $(file)|%,%,$(filter $(file)|%,
 override uncompiled = $(foreach file,$1,$(patsubst %|$(file),%,$(filter %|$(file),$(sourcecompiledpair))))
 
 # (callable) Get the installed locations for the given source files.
-override installed = $(foreach file,$1,$(DESTDIR)/$(call sourcepath,$(file)))
+override installed = $(foreach file,$1,$(DESTDIR)/$(call destination,$(file)))
+endif
 
 # ─ ¶ Recipe Variable Definitions ─────────────────────────────────────
 
@@ -363,7 +374,7 @@ install: $(call installed,$(recursivefiles) $(compilablefiles) $(filter $(source
 
 # List all source files and includes and their computed types.
 list:
-	$(silent)$(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)))) )) | $(TR) ' |' '\n '
+	$(silent)$(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 '
 
 # Raise an error when attempting to build any files with recursive
 # dependencies.
@@ -391,10 +402,10 @@ ifneq ($(wildcard $(BUILDDIR)/.update-types)$(wildcard $(BUILDDIR)/dependencies)
 #
 # This recipe only exists after types have been updated or when the
 # dependency graph already exists.
-$(THISDIR)/GNUmakefile:: $(BUILDDIR)/dependencies
+$(THISDIR)/GNUmakefile:: $(BUILDDIR)/dependencies $(BUILDDIR)/destinations
 	$(silent)$(TOUCH) $(THISDIR)/GNUmakefile
 	$(silent)$(RM) -f $(BUILDDIR)/.update-types
-	@$(PRINTF) '%b\n' '\0033[1mDependency graph updated. Restarting…\0033[22m'
+	@$(PRINTF) '%b\n' '\0033[1mDependency graph and output destinations updated. Restarting…\0033[22m'
 endif
 
 ifeq ($(wildcard $(BUILDDIR)/.update-types),)
@@ -457,6 +468,13 @@ $(BUILDDIR)/dependencies: $(BUILDDIR)/catalog $(call parsed,$(plaintextfiles) $(
 	@$(ECHO) "Identifying dependencies…"
 	$(silent)$(XSLTPROC) -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2dependencies.xslt) $(call quote,$<)
 
+# Generate a catalog of destinations for files. This depends on parsing
+# non·asset source files, but not assets or includes. It does not
+# require knowing the dependencies.
+$(BUILDDIR)/destinations: $(BUILDDIR)/catalog $(call parsed,$(filter-out $(assetfiles),$(sourcefiles))) $(THISDIR)/lib/catalog2destinations.xslt
+	@$(ECHO) "Identifying output destinations…"
+	$(silent)$(XSLTPROC) -o $(call quote,$@) $(call quote,$(THISDIR)/lib/catalog2destinations.xslt) $(call quote,$<)
+
 # Generate the main transform.
 $(BUILDDIR)/transform.catalog: $(TRANSFORMS)
 	@$(ECHO) "Generating catalog of transforms…"
diff --git a/README.markdown b/README.markdown
index 7e9b74b..de82209 100644
--- a/README.markdown
+++ b/README.markdown
@@ -304,6 +304,15 @@ Embedding takes place after parsing but before transformation, so
   and update them accordingly; it will signal an error if the
   dependencies are recursive.
 
+## Output Redirection
+
+By default, ⛩️📰 书社 installs files to the same location in `DESTDIR`
+  as they were placed in their `SRCDIR`.
+This behaviour can be customized by setting the `@书社:destination`
+  attribute on the root element, whose value can give a different path.
+This attribute is read after parsing, but before transformation (where
+  it is silently dropped).
+
 ## Transforms
 
 Transforms are used to convert X·M·L files into their final output,
diff --git a/lib/catalog2destinations.xslt b/lib/catalog2destinations.xslt
new file mode 100644
index 0000000..cadb0c3
--- /dev/null
+++ b/lib/catalog2destinations.xslt
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!--
+⁌ ⛩️📰 书社 ∷ lib/catalog2destinations.xslt
+
+© 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 <https://mozilla.org/MPL/2.0/>.
+-->
+<transform
+	xmlns="http://www.w3.org/1999/XSL/Transform"
+	xmlns:catalog="urn:oasis:names:tc:entity:xmlns:xml:catalog"
+	xmlns:书社="urn:fdc:ladys.computer:20231231:Shu1She4"
+	version="1.0"
+>
+	<template match="/">
+		<for-each select="//catalog:uri[starts-with(@name, 'about:shushe?source=')]">
+			<choose>
+				<when test="substring-after(@uri, '#')='xml'">
+					<variable name="destination" select="document(substring-before(@uri, '#'), .)/*/@书社:destination"/>
+					<value-of select="@name"/>
+					<text>|</text>
+					<choose>
+						<when test="$destination">
+							<value-of select="$destination"/>
+						</when>
+						<otherwise>
+							<value-of select="substring-after(@name, 'about:shushe?source=')"/>
+						</otherwise>
+					</choose>
+					<text>&#xA;</text>
+				</when>
+				<otherwise>
+					<value-of select="@name"/>
+					<text>|</text>
+					<value-of select="substring-after(@name, 'about:shushe?source=')"/>
+					<text>&#xA;</text>
+				</otherwise>
+			</choose>
+		</for-each>
+	</template>
+	<output method="text" encoding="UTF-8"/>
+</transform>
diff --git a/lib/catalog2transform.xslt b/lib/catalog2transform.xslt
index 3339062..2be9218 100644
--- a/lib/catalog2transform.xslt
+++ b/lib/catalog2transform.xslt
@@ -58,7 +58,7 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
 					</xslt:otherwise>
 				</xslt:choose>
 			</xslt:template>
-			<xslt:template match="/*/*//@书社:disable-output-wrapping" priority="0"/>
+			<xslt:template match="/*/*//@书社:disable-output-wrapping|//@书社:destination" priority="0"/>
 			<xslt:template match="@*|node()" priority="-1">
 				<xslt:copy>
 					<xslt:apply-templates select="@*|node()"/>