]> Lady’s Gitweb - Shushe/commitdiff
Support “soft” dependencies
authorLady <redacted>
Wed, 24 Dec 2025 19:27:53 +0000 (14:27 -0500)
committerLady <redacted>
Wed, 24 Dec 2025 19:29:25 +0000 (14:29 -0500)
From the perspective of Make, these are ordinary dependencies, but they
aren’t expanded and so are allowed to be recursive.

.metadata-format-changed-since
GNUmakefile
README.markdown
lib/expandmetadata.xslt
transforms/expansion.xslt

index 36e448cb04f68706a11e03db61e91473aa7d8f26..224e40d0ec24238cb2a46a92be8bd1b074153ea9 100644 (file)
@@ -4,7 +4,7 @@ SPDX-License-Identifier: CC0-1.0
 The following hash indicates a commit in which the metadata format
 generated by ⛩📰 书社 was different than it currently is :⁠—
 
-d64a8d34c7638243f90421664268766df75a42d7
+1ba6bb315cf3e1c7120acff6c34149acf65f0120
 
 The purpose of this file is to serve as a trackable dependency which
 will prompt a rebuild of metadata when the generation mechanism
index a3624ccd944425e9185ab93f66f6ea3e78256037..a8838375e568bc8b58a7e96e7b2c3e738e687e4c 100644 (file)
@@ -545,6 +545,7 @@ override dependenciesforfile := $(foreach file,$(filter-out $(assetfiles),$(sour
 # (callable) Get the list of dependency leiris for the given source files.
 #
 # Recursive dependencies are marked with a leading `-´.
+# Soft dependencies are marked with a leading `?´.
 override dependencyuris = $(foreach file,$1,$(subst |, ,$(patsubst $(file)|%,%,$(filter $(file)|%,$(dependenciesforfile)))))
 
 # (callable) Get the list of recursive dependencies for the given source files.
@@ -555,7 +556,7 @@ override recursives = $(foreach uri,$(filter -%,$(call dependencyuris,$1)),$(cal
 # (callable) Get the list of (nonrecursive) dependencies for the given source files.
 #
 # If the file cannot have dependencies (e·g is an asset file), the resulting value will be the empty string.
-override dependencies = $(foreach uri,$(filter-out -%,$(call dependencyuris,$1)),$(call sourcefile,$(uri)))
+override dependencies = $(foreach uri,$(filter-out -%,$(call dependencyuris,$1)),$(call sourcefile,$(if $(filter ?%,$(uri)),$(patsubst ?%,%,$(uri)),$(uri))))
 endif
 
 # Collect all files with recursive dependencies.
@@ -624,7 +625,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]$(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)), $(foreach dest,$(call destination,$(file)),→|<\0033[4m/$(dest)\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 dependencyuris,$(file)), $(strip $(foreach dep,$(call dependencyuris,$(file)),$(if $(filter -%,$(dep)),\0033[93;41m•|Recursive|Dependency|\0033[39;49m|$(call sourcefile,$(patsubst -%,%,$(dep))),$(if $(filter ?%,$(dep)),\0033[2m•|Soft|Dependency|\0033[22m|$(call sourcefile,$(patsubst ?%,%,$(dep))),\0033[2m•|Dependency|\0033[22m|$(call sourcefile,$(dep)))))))$(if $(filter $(file),$(sourcefiles)), $(foreach dest,$(call destination,$(file)),→|<\0033[4m/$(dest)\0033[24m>),) )) | $(TR) ' |' '\n '
 
 # Lists out the destinations of all resulting files (relative to `DESTDIR´).
 listout :
index 4671064e2238f8d323ca98ac44295464917ffa96..be400b9e2b9a4c9d9e24830dc5d53977e6fe7b0e 100644 (file)
@@ -575,7 +575,8 @@ The file is retransformed each time, with the value of the `DESTINATION` global
 ## Embedding
 
 Documents can be embedded in other documents using a `<书社:link>`
-  element with `@xlink:show="embed"`.
+  element with `@xlink:show="embed"` and an `@xlink:actuate` which is
+  absent or `"none"`.
 The `@xlink:href`s of these elements should have the format
   `about:shushe?source=<path>`, where `<path>` provides the path to the
   file within `SRCDIR`.
@@ -583,6 +584,8 @@ Includes, which do not generate outputs of their own but may still be
   freely embedded, instead use the format
   `about:shushe?include=<path>`, where `<path>` provides the path
   within `INCLUDEDIR`.
+If `<path>` indicates a directory and ends with a slash (`/`),
+  everything within that directory will be embedded.
 
 Embeds are replaced with the parsed contents of a file, unless the file
   is an asset, in which case an `<html:object>` element is produced
@@ -622,6 +625,40 @@ These include :⁠—
   These attributes are used to scope any nested `<html:meta>` elements
     with `@itemprop` attributes to their containing documents.
 
+## Soft Dependencies
+
+When a file depends only on the metadata of another file, and not its
+  contents, it can be added as a soft dependency rather than an embed.
+Soft dependencies are indicated using a `<书社:link>` element with an
+  `@xlink:show` of `"other"`, `"none"`, or absent, and an
+  `@xlink:actuate` which is absent or `"none"`.
+A change to a soft dependency requires a file to be rebuilt, but no
+  embedding occurs automatically.
+Because there is no automatic embedding, soft dependencies are allowed
+  to be recursive.
+
+The `@xlink:href`s of soft dependency `<书社:link>`s are processed in
+  exactly the same fashion as embeds, described above.
+
+If the value of `@xlink:show` is `"other"`, the soft dependency is
+  transitive.
+Any dependencies of the indicated file which have a `@name` which
+  matches that of the referencing `<书社:link>` element will also be
+  treated as soft dependencies.
+If no `@name` is given, it is treated as the empty string.
+
+When a document is embedded directly, all of its soft dependencies are
+  also treated as soft dependencies of the embedding object.
+However, a document is embedded in a transitive soft dependency, the
+  embed is treated exactly as tho it were itself a transitive soft
+  dependency.
+That means it must have a matching `@name` to be included, and
+  like·wise for any embeds or soft dependencies it contains.
+
+If the value of `@xlink:show` is `"none"` or absent, the soft
+  dependency is not transitive and its own dependencies are not
+  checked.
+
 ## Transforms
 
 Transforms are used to convert X·M·L files into their final output,
index 423924a3d35eb889c8ab4bd46e46a937e6d54c02..727d58a084bc4d9cb7f06969bd4db0dcb50189f0 100644 (file)
@@ -26,53 +26,126 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
 >
        <variable name="builddir" select="//书社vocab:BuildDirectory/@nfo:fileUrl"/>
        <variable name="files" select="//书社vocab:SourceFile|//书社vocab:IncludeFile"/>
-       <template name="书社:collect-dependency-paths">
-               <param name="path" select="/.."/>
-               <variable name="parent" select="string($path/*[last()])"/>
-               <variable name="children-fragment">
-                       <for-each select="$files[@rdf:about=$parent and nie:interpretedAs/nfo:PlainTextDocument]">
-                               <variable name="uri" select="书社vocab:hasParsedFile/@nfo:fileUrl"/>
-                               <for-each select="document($uri)//书社:link[@xlink:show='embed']">
-                                       <variable name="identifier" select="string(@xlink:href)"/>
-                                       <variable name="is-dir" select="substring($identifier, string-length($identifier))='/'"/>
-                                       <for-each select="$files/@rdf:about[string(.)=$identifier or $is-dir and starts-with(., $identifier)]">
-                                               <sort select="." data-type="text" lang="zxx" case-order="upper-first"/>
+       <variable name="stated-dependencies-fragment">
+               <for-each select="$files[nie:interpretedAs/nfo:PlainTextDocument]">
+                       <variable name="uri" select="书社vocab:hasParsedFile/@nfo:fileUrl"/>
+                       <书社:Dependencies for="{@rdf:about}">
+                               <for-each select="document($uri)//书社:link">
+                                       <variable name="spaced-show">
                                                <choose>
-                                                       <when test="$path/*[string(.)=string(current())]">
-                                                               <书社:recursive-dependency>
-                                                                       <value-of select="."/>
-                                                               </书社:recursive-dependency>
+                                                       <when test="@xlink:show">
+                                                               <text> </text>
+                                                               <value-of select="@xlink:show"/>
+                                                               <text> </text>
                                                        </when>
                                                        <otherwise>
-                                                               <书社:dependency>
-                                                                       <value-of select="."/>
-                                                               </书社:dependency>
+                                                               <text> none </text>
                                                        </otherwise>
                                                </choose>
-                                       </for-each>
+                                       </variable>
+                                       <variable name="actuate">
+                                               <choose>
+                                                       <when test="@xlink:actuate">
+                                                               <value-of select="@xlink:actuate"/>
+                                                       </when>
+                                                       <otherwise>
+                                                               <text>none</text><!-- not the true default in all cases, but in the ones that matter -->
+                                                       </otherwise>
+                                               </choose>
+                                       </variable>
+                                       <variable name="name" select="string(@name)"/>
+                                       <if test="contains(' embed other none ', $spaced-show) and $actuate='none'">
+                                               <variable name="identifier" select="string(@xlink:href)"/>
+                                               <variable name="is-dir" select="substring($identifier, string-length($identifier))='/'"/>
+                                               <for-each select="$files/@rdf:about[string(.)=$identifier or $is-dir and starts-with(., $identifier)]">
+                                                       <sort select="." data-type="text" lang="zxx" case-order="upper-first"/>
+                                                       <choose>
+                                                               <when test="$spaced-show=' embed '">
+                                                                       <书社:dependency name="{$name}">
+                                                                               <value-of select="."/>
+                                                                       </书社:dependency>
+                                                               </when>
+                                                               <when test="$spaced-show=' other '">
+                                                                       <书社:transitive-soft-dependency name="{$name}">
+                                                                               <value-of select="."/>
+                                                                       </书社:transitive-soft-dependency>
+                                                               </when>
+                                                               <otherwise>
+                                                                       <书社:soft-dependency name="{$name}">
+                                                                               <value-of select="."/>
+                                                                       </书社:soft-dependency>
+                                                               </otherwise>
+                                                       </choose>
+                                               </for-each>
+                                       </if>
                                </for-each>
+                       </书社:Dependencies>
+               </for-each>
+       </variable>
+       <variable name="stated-dependencies" select="exsl:node-set($stated-dependencies-fragment)/*"/>
+       <template name="书社:collect-dependency-paths">
+               <param name="path" select="/.."/>
+               <variable name="parent" select="$path/*[last()]"/>
+               <variable name="children-fragment">
+                       <for-each select="$stated-dependencies[@for=string($parent)]/*">
+                               <choose>
+                                       <when test="$path/*[string()=current()]">
+                                               <if test="self::书社:dependency">
+                                                       <书社:recursive-dependency name="{@name}">
+                                                               <value-of select="."/>
+                                                       </书社:recursive-dependency>
+                                               </if>
+                                       </when>
+                                       <otherwise>
+                                               <copy-of select="."/>
+                                       </otherwise>
+                               </choose>
                        </for-each>
                </variable>
-               <variable name="children" select="exsl:node-set($children-fragment)"/>
+               <variable name="children" select="exsl:node-set($children-fragment)/*"/>
+               <variable name="child-paths-fragment">
+                       <for-each select="$children">
+                               <choose>
+                                       <when test="self::书社:recursive-dependency">
+                                               <书社:dependency-path>
+                                                       <copy-of select="$path/*[string()=current()][1]/preceding-sibling::*"/>
+                                                       <copy-of select="."/><!-- replace first instance with recursive dependency -->
+                                               </书社:dependency-path>
+                                       </when>
+                                       <when test="$parent/self::书社:transitive-soft-dependency and string(@name)!=string($parent/@name)"/>
+                                       <when test="self::书社:soft-dependency">
+                                               <书社:dependency-path>
+                                                       <copy-of select="$path"/>
+                                                       <copy-of select="."/>
+                                               </书社:dependency-path>
+                                       </when>
+                                       <otherwise>
+                                               <variable name="path-fragment">
+                                                       <copy-of select="$path"/>
+                                                       <choose>
+                                                               <when test="self::书社:dependency and $path/书社:transitive-soft-dependency">
+                                                                       <书社:transitive-soft-dependency name="{@name}">
+                                                                               <value-of select="."/>
+                                                                       </书社:transitive-soft-dependency>
+                                                               </when>
+                                                               <otherwise>
+                                                                       <copy-of select="."/>
+                                                               </otherwise>
+                                                       </choose>
+                                               </variable>
+                                               <call-template name="书社:collect-dependency-paths">
+                                                       <with-param name="path" select="exsl:node-set($path-fragment)"/>
+                                               </call-template><!-- a little inefficient in the case of reflexive dependencies, as the path will be traversed once in each direction, but improving on this is a hard problem in X·S·L·T -->
+                                       </otherwise>
+                               </choose>
+                       </for-each>
+               </variable>
+               <variable name="child-paths" select="exsl:node-set($child-paths-fragment)/*"/>
                <choose>
-                       <when test="$children/*">
-                               <for-each select="$children/书社:recursive-dependency">
-                                       <书社:dependency-path>
-                                               <copy-of select="$path[string(.)=string(current())]/preceding-sibling::*"/>
-                                               <copy-of select="."/>
-                                       </书社:dependency-path>
-                               </for-each>
-                               <for-each select="$children/书社:dependency">
-                                       <variable name="path-fragment">
-                                               <copy-of select="$path"/>
-                                               <copy-of select="."/>
-                                       </variable>
-                                       <call-template name="书社:collect-dependency-paths">
-                                               <with-param name="path" select="exsl:node-set($path-fragment)"/>
-                                       </call-template>
-                               </for-each>
+                       <when test="$child-paths">
+                               <copy-of select="$child-paths"/>
                        </when>
-                       <otherwise>
+                       <otherwise><!-- fallback result is just the input path -->
                                <书社:dependency-path>
                                        <copy-of select="$path"/>
                                </书社:dependency-path>
@@ -108,10 +181,6 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                </for-each>
                        </if>
                </variable>
-               <variable name="dependencies-fragment">
-                       <apply-templates select="." mode="书社:dependencies"/>
-               </variable>
-               <variable name="dependencies" select="exsl:node-set($dependencies-fragment)"/>
                <variable name="destinations" select="exsl:node-set($destinations-fragment)/node()"/>
                <variable name="bad-destinations-fragment">
                        <for-each select="$destinations">
@@ -125,12 +194,7 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                <copy>
                        <apply-templates select="@*"/>
                        <apply-templates select="node()"/>
-                       <for-each select="$dependencies/书社:dependency">
-                               <书社vocab:hasDependencyOn rdf:resource="{.}"/>
-                       </for-each>
-                       <for-each select="$dependencies/书社:recursive-dependency">
-                               <书社vocab:hasRecursiveDependencyOn rdf:resource="{.}"/>
-                       </for-each>
+                       <apply-templates select="." mode="书社:dependencies"/>
                        <choose>
                                <when test="not($destinations)">
                                        <element name="书社vocab:destination">
@@ -164,18 +228,9 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                </copy>
        </template>
        <template match="//书社vocab:IncludeFile[nie:interpretedAs/nfo:PlainTextDocument]" priority="1">
-               <variable name="dependencies-fragment">
-                       <apply-templates select="." mode="书社:dependencies"/>
-               </variable>
-               <variable name="dependencies" select="exsl:node-set($dependencies-fragment)"/>
                <copy>
                        <apply-templates select="@*|node()"/>
-                       <for-each select="$dependencies/书社:dependency">
-                               <书社vocab:hasDependencyOn rdf:resource="{.}"/>
-                       </for-each>
-                       <for-each select="$dependencies/书社:recursive-dependency">
-                               <书社vocab:hasRecursiveDependencyOn rdf:resource="{.}"/>
-                       </for-each>
+                       <apply-templates select="." mode="书社:dependencies"/>
                </copy>
        </template>
        <template match="书社vocab:hasParsedFileWithMetadata" priority="1"/>
@@ -190,8 +245,12 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
                                <value-of select="@rdf:about"/>
                                <text>&#xA;</text>
                                <for-each select="书社vocab:hasRecursiveDependencyOn/@rdf:resource">
-                                       <text>&#x9;</text>
-                                       <text>-</text>
+                                       <text>&#x9;-</text>
+                                       <value-of select="."/>
+                                       <text>&#xA;</text>
+                               </for-each>
+                               <for-each select="书社vocab:hasSoftDependencyOn/@rdf:resource">
+                                       <text>&#x9;?</text>
                                        <value-of select="."/>
                                        <text>&#xA;</text>
                                </for-each>
@@ -224,18 +283,41 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
        <template match="书社vocab:SourceFile|书社vocab:IncludeFile" mode="书社:dependencies">
                <if test="nie:interpretedAs/nfo:PlainTextDocument">
                        <variable name="path-fragment">
-                               <书社:dependency-root>
+                               <element name="书社:dependency-root">
                                        <value-of select="@rdf:about"/>
-                               </书社:dependency-root>
+                               </element>
                        </variable>
                        <variable name="paths-fragment">
                                <call-template name="书社:collect-dependency-paths">
                                        <with-param name="path" select="exsl:node-set($path-fragment)"/>
                                </call-template>
                        </variable>
-                       <for-each select="exsl:node-set($paths-fragment)/*/*[not(self::书社:dependency-root)]">
-                               <if test="not(preceding::*[string(.)=string(current())])">
-                                       <copy-of select="."/>
+                       <variable name="paths-document" select="exsl:node-set($paths-fragment)"/>
+                       <for-each select="$paths-document//书社:recursive-dependency">
+                               <if test="not(preceding::*[string()=string(current())])">
+                                       <element name="书社vocab:hasRecursiveDependencyOn">
+                                               <attribute name="rdf:resource">
+                                                       <value-of select="."/>
+                                               </attribute>
+                                       </element>
+                               </if>
+                       </for-each>
+                       <for-each select="$paths-document//书社:dependency">
+                               <if test="not((preceding::*|//书社:recursive-dependency)[string()=string(current())])">
+                                       <element name="书社vocab:hasDependencyOn">
+                                               <attribute name="rdf:resource">
+                                                       <value-of select="."/>
+                                               </attribute>
+                                       </element>
+                               </if>
+                       </for-each>
+                       <for-each select="$paths-document//*[self::书社:soft-dependency or self::书社:transitive-soft-dependency]">
+                               <if test="not((preceding::*|//书社:recursive-dependency|//书社:dependency)[string()=string(current())])">
+                                       <element name="书社vocab:hasSoftDependencyOn">
+                                               <attribute name="rdf:resource">
+                                                       <value-of select="."/>
+                                               </attribute>
+                                       </element>
                                </if>
                        </for-each>
                </if>
index 8c0dd69df7a44adc34f5b49a6e9d24c27816f872..c27fdbfaf3553b895121017909c544d5e1be78f2 100644 (file)
@@ -29,6 +29,7 @@ If a copy of the M·P·L was not distributed with this file, You can obtain one
        version="1.0"
 >
        <书社:id>urn:fdc:ladys.computer:20231231:Shu1She4:expansion.xslt</书社:id>
+       <template match="书社:link[(not(@xlink:show) or @xlink:show='none' or @xlink:show='other') and (not(@xlink:actuate) or @xlink:actuate='none')]" mode="书社:expand" priority="1"/>
        <template match="书社:link[@xlink:show='embed' and (not(@xlink:actuate) or @xlink:actuate='none')]" mode="书社:expand" priority="1">
                <variable name="link" select="."/>
                <variable name="identifier" select="string(@xlink:href)"/>
This page took 0.312589 seconds and 4 git commands to generate.