xmlxsltxproc

Force sequencing in XProc when a p:for-each only has side effects?


I’m using XProc 3.0 with XML Calabash 3. I have a pipeline where:

  1. I iterate over a directory of XML files
  2. For each file, I run an XSLT trasnformation and store a modified version back to disk
  3. After that, I run p:xinclude on a different document which depends on the files that were fixed and stored in step (2)
<p:declare-step xmlns:p="http://www.w3.org/ns/xproc" xmlns:c="http://www.w3.org/ns/xproc-step" version="3.0" name="generateDocumentation">
    
    <p:directory-list name="dl" path="../includes" include-filter=".+\.xml$"
        exclude-filter=".+fixed\.xml$" max-depth="unbounded"/>
    <p:make-absolute-uris match="@name"/>
    
    <p:for-each name="fix-includes">
        <!-- iterate over each c:file from the directory listing -->
        <p:with-input select="//c:file"/>
        <!-- in this iteration, the document is a single c:file -->
        <p:variable name="in" select="string(/*/@name)"/>
        <p:variable name="out" select="replace($in, '\.xml$', '.fixed.xml', 'i')"/>
        <p:load href="{$in}" content-type="application/xml"/>
        <p:xslt>
            <p:with-input port="stylesheet" href="../stylesheets/fix-includes.xsl"/>
        </p:xslt>
        <p:store href="{$out}"/>
    </p:for-each>

    <p:xinclude name="include">
        <p:with-input port="source">
            <p:document href="index.xml" content-type="application/xml"/>
        </p:with-input>
    </p:xinclude>

    <!-- EDIT: I have further steps here which need the result of <p:xinclude> -->

</p:declare-step>

The problem: Calabash lets p:xinclude start before the p:for-each has finished writing files, leading to missing-file errors in p:xinclude. I understand why this happens (no data dependency, the loop does not feed any data into the later step so its p:store steps look like side effects), but I can't figure out an idiomatic XProc 3 solution for this.

The question: How can I make sure that all the iterations of p:for-each complete, including all thep:store operations, before running a later step (p:xinclude) when there is no natural document flow between them?

Many thanks in advance!


Solution

  • I have tried my suggestion from the comment (to wrap the p:xinclude in a p:if that checks a file count taken by a p:count "after" the p:for-each):

    <p:declare-step xmlns:p="http://www.w3.org/ns/xproc" xmlns:c="http://www.w3.org/ns/xproc-step" version="3.0" name="generateDocumentation">
        
        <p:output port="result"/>
        
        <p:directory-list name="dl" path="includes" include-filter=".+\.xml$"
            exclude-filter=".+fixed\.xml$" max-depth="unbounded"/>
        <p:make-absolute-uris match="@name"/>
        
        <p:variable name="file-count" select="count(//c:file)"/>
        
        <p:for-each name="fix-includes">
            <!-- iterate over each c:file from the directory listing -->
            <p:with-input select="//c:file"/>
            <!-- in this iteration, the document is a single c:file -->
            <p:variable name="in" select="string(/*/@name)"/>
            <p:variable name="out" select="replace($in, '\.xml$', '.fixed.xml', 'i')"/>
            <p:load href="{$in}" content-type="application/xml"/>
            <p:xslt>
                <p:with-input port="stylesheet" href="fix-includes.xsl"/>
            </p:xslt>
            <p:store href="{$out}" message="p:store {current-dateTime()}"/>
        </p:for-each>
        
        <p:count/>
        
        <p:group name="do-include">
            <p:output port="result"></p:output>
            
            <p:if test=". = $file-count">
                
                <p:xinclude name="include">
                    <p:with-input port="source">
                        <p:document href="index.xml" content-type="application/xml"/>
                    </p:with-input>
                </p:xinclude>
                
                <p:identity message="Inside if {current-dateTime()}"/>
                <!-- EDIT: I have further steps which need the result of <p:xinclude> -->
                
            </p:if>
    
        </p:group>
        
       <!-- You can't use outputs from <xi:include> directly, but you can use 
            <p:pipe step="do-include" port="result"/> instead. -->
        
    </p:declare-step>
    

    That seems to make sure the p:xinclude is run after the p:for-each, output is e.g.

    p:store 2025-12-28T19:37:59.5199281+01:00
    p:store 2025-12-28T19:37:59.5403036+01:00
    p:store 2025-12-28T19:37:59.5510977+01:00
    p:store 2025-12-28T19:37:59.563198+01:00
    p:store 2025-12-28T19:37:59.5737237+01:00
    Inside if 2025-12-28T19:37:59.58695+01:00
    <root xmlns:xi="http://www.w3.org/2001/XInclude">...