Skip to content

Conversation

@infotexture
Copy link
Member

Use Cursor’s claude-4-sonnet model to replace deprecated constructs from Gradle 7 and earlier versions with their Gradle 8 and 9 equivalents to quiet deprecation warnings.

@infotexture infotexture self-assigned this Nov 3, 2025
@infotexture infotexture added build Ant/Gradle build scripts & CI/CD issues dependencies Pull requests that update a dependency file labels Nov 3, 2025
@infotexture infotexture changed the base branch from feature/gradle-8-14-3 to develop November 3, 2025 20:24
@infotexture infotexture force-pushed the feature/gradle-9-compatibility branch from 55e8605 to 3d70153 Compare November 3, 2025 20:29
@infotexture infotexture marked this pull request as ready for review November 3, 2025 21:03
@infotexture infotexture force-pushed the feature/gradle-9-compatibility branch from 3d70153 to 95498de Compare November 3, 2025 21:29
Use Cursor’s `claude-4-sonnet` model to replace deprecated constructs from Gradle 7 and earlier versions with their Gradle 8 and 9 equivalents to quiet deprecation warnings.

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Roger Sheen <roger@infotexture.net>
@infotexture infotexture force-pushed the feature/gradle-9-compatibility branch from 95498de to e11be3b Compare November 3, 2025 23:13
@infotexture infotexture requested a review from jelovirt November 3, 2025 23:13
@infotexture
Copy link
Member Author

@jelovirt Asked the robots for help refactoring this, and they were able to get rid of most of the deprecation warnings.

There's one left that seems to be coming from eerohele/saxon-gradle plugin, but I'm not sure there's much we can do about that. There's ndw/saxon-gradle, but that was last updated 2 years ago, and also does things during the configuration phase, which sounds incompatible with the new configuration caching in recent Gradle versions.

The docs distribution builds complete successfully, and the site build writes commit metadata to YAML headers as before.

If this looks OK, we can use it as a basis for testing @jyjeanne's new dita-ot-gradle plug-in, but also open to suggestions if there's anything we can do better here.

@infotexture infotexture merged commit a028882 into develop Jan 7, 2026
5 checks passed
@jyjeanne
Copy link

jyjeanne commented Jan 9, 2026

Solution Implemented for Gradle 9 Compatibility

@jelovirt @infotexture , i describe a solution implemented to make the DITA-OT documentation (project docs) build fully compatible with Gradle 9 and Configuration Cache. i joined a zip archive with modified files gradle9-compatibility-solution.zip


Problems Solved

Problem 1: Deprecated exec Method

The getGitCommitHash() function used project.exec() which is deprecated and will be removed in Gradle 9.

Problem 2: Saxon-Gradle Plugin Incompatibility

The eerohele/saxon-gradle plugin (v0.9.0-beta4) is not compatible with Gradle's Configuration Cache, causing build failures when Configuration Cache is enabled.


Solution Summary

Problem Solution
Deprecated exec Replaced with providers.exec()
Saxon-gradle incompatibility Created custom XsltTransformTask in buildSrc

Implementation Details

Fix 1: Replace Deprecated exec

Location: build.gradle (lines 171-176)

Before:

def getGitCommitHash() {
    try {
        def result = new ByteArrayOutputStream()
        exec {
            workingDir = layout.projectDirectory.asFile
            commandLine 'git', 'rev-parse', 'HEAD'
            standardOutput = result
            ignoreExitValue = true
        }
        return result.toString().trim()
    } catch (Exception e) {
        logger.warn("Could not get git commit hash: ${e.message}")
        return 'unknown'
    }
}

def gitCommitHash = getGitCommitHash()

After:

// Get git commit hash using Gradle 9 compatible Provider API
def gitCommitHash = providers.exec {
    commandLine 'git', 'rev-parse', 'HEAD'
    ignoreExitValue = true
}.standardOutput.asText.map { it.trim() }.getOrElse('unknown')

Why this works:

  • providers.exec() is the Gradle 9 compatible replacement
  • Returns a Provider<ExecOutput> for lazy evaluation
  • Configuration Cache compatible
  • Cleaner error handling with .getOrElse('unknown')

Fix 2: Replace Saxon-Gradle with Custom Task

Problem: Saxon-gradle stores non-serializable objects (XmlSlurper) in task fields, breaking Configuration Cache.

Solution: Created a new XsltTransformTask in buildSrc/ that:

  • Uses abstract properties (Gradle best practice)
  • Is annotated with @CacheableTask
  • Uses proper @InputFile, @OutputFile annotations
  • Is fully Configuration Cache compatible

New Files Created

File: buildSrc/build.gradle

plugins {
    id 'groovy'
}

repositories {
    mavenCentral()
}

dependencies {
    // Saxon-HE for XSLT transformations
    implementation 'net.sf.saxon:Saxon-HE:10.6'

    // Gradle API
    implementation gradleApi()
}

File: buildSrc/src/main/groovy/XsltTransformTask.groovy

import org.gradle.api.DefaultTask
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.tasks.*

import net.sf.saxon.s9api.Processor
import net.sf.saxon.s9api.QName
import net.sf.saxon.s9api.XdmAtomicValue
import net.sf.saxon.s9api.Serializer

import javax.xml.transform.stream.StreamSource

/**
 * Gradle 9 compatible XSLT transformation task using Saxon-HE.
 * Replaces eerohele/saxon-gradle which is not Configuration Cache compatible.
 */
@CacheableTask
abstract class XsltTransformTask extends DefaultTask {

    @InputFile
    @PathSensitive(PathSensitivity.RELATIVE)
    abstract RegularFileProperty getInputFile()

    @InputFile
    @PathSensitive(PathSensitivity.RELATIVE)
    abstract RegularFileProperty getStylesheetFile()

    @OutputFile
    abstract RegularFileProperty getOutputFile()

    @Input
    @Optional
    abstract MapProperty<String, String> getParameters()

    @OutputDirectory
    @Optional
    abstract DirectoryProperty getAdditionalOutputDir()

    @TaskAction
    void transform() {
        def inputPath = inputFile.get().asFile
        def stylesheetPath = stylesheetFile.get().asFile
        def outputPath = outputFile.get().asFile

        logger.info("XSLT Transform: ${inputPath.name} -> ${outputPath.name}")

        // Ensure output directory exists
        outputPath.parentFile?.mkdirs()

        // Create Saxon processor and execute transformation
        def processor = new Processor(false)
        def compiler = processor.newXsltCompiler()
        def executable = compiler.compile(new StreamSource(stylesheetPath))
        def transformer = executable.load30()

        // Set parameters if provided
        if (parameters.isPresent() && !parameters.get().isEmpty()) {
            parameters.get().each { name, value ->
                transformer.setStylesheetParameters(
                    Collections.singletonMap(new QName(name), new XdmAtomicValue(value))
                )
            }
        }

        // Execute transformation
        transformer.transform(
            new StreamSource(inputPath),
            processor.newSerializer(outputPath)
        )

        logger.lifecycle("Generated: ${outputPath.name}")
    }

    // Backward-compatible DSL methods
    void input(Object path) { inputFile.set(project.file(path)) }
    void output(Object path) { outputFile.set(project.file(path)) }
    void stylesheet(Object path) { stylesheetFile.set(project.file(path)) }
    void parameters(Map<String, String> params) { parameters.putAll(params) }
}

Migration in build.gradle

Before:

plugins {
    id 'io.github.jyjeanne.dita-ot-gradle' version '2.3.2'
    id 'com.github.eerohele.saxon-gradle' version '0.9.0-beta4'
}

configurations {
    saxon
}

dependencies {
    saxon 'net.sf.saxon:Saxon-HE:10.6'
}

import com.github.eerohele.SaxonXsltTask

task messages(type: SaxonXsltTask) {
    input "${configDir}/messages.xml"
    output "${projectDirPath}/topics/error-messages.xml"
    stylesheet "${projectDirPath}/resources/messages.xsl"
}

task params(type: SaxonXsltTask) {
    input "${configDir}/plugins.xml"
    output "${projectDirPath}/parameters/all-parameters.dita"
    stylesheet "${projectDirPath}/resources/params.xsl"
    parameters('output-dir.url': toURI('parameters'))
    outputs.dir "${projectDirPath}/parameters"
}

After:

plugins {
    id 'io.github.jyjeanne.dita-ot-gradle' version '2.3.2'
    // Removed: saxon-gradle - not Configuration Cache compatible
}

// XsltTransformTask is provided by buildSrc

task messages(type: XsltTransformTask) {
    input "${configDir}/messages.xml"
    output "${projectDirPath}/topics/error-messages.xml"
    stylesheet "${projectDirPath}/resources/messages.xsl"
}

task params(type: XsltTransformTask) {
    input "${configDir}/plugins.xml"
    output "${projectDirPath}/parameters/all-parameters.dita"
    stylesheet "${projectDirPath}/resources/params.xsl"
    parameters(['output-dir.url': toURI('parameters')])
    outputs.dir "${projectDirPath}/parameters"
}

Note: The only syntax change is parameters('key': 'value') becomes parameters(['key': 'value']) (explicit Map).


Files Changed

File Type Description
build.gradle Modified Removed saxon-gradle, fixed exec, use XsltTransformTask
buildSrc/build.gradle New Build configuration for custom task
buildSrc/src/main/groovy/XsltTransformTask.groovy New Gradle 9 compatible XSLT task

Verification Results

No Deprecation Warnings

$ ./gradlew help --warning-mode all
BUILD SUCCESSFUL in 11s

Configuration Cache Works

# First run - stores cache
$ ./gradlew messages --configuration-cache
Configuration cache entry stored.
BUILD SUCCESSFUL

# Second run - reuses cache
$ ./gradlew messages --configuration-cache
Reusing configuration cache.
Configuration cache entry reused.
BUILD SUCCESSFUL

All XSLT Tasks Work

$ ./gradlew messages params extensionPoints generatePropertiesTemplate

> Task :messages
Generated: error-messages.xml

> Task :params
Generated: all-parameters.dita

> Task :extensionPoints
Generated: all-extension-points.dita

> Task :generatePropertiesTemplate
Generated: template.properties

BUILD SUCCESSFUL

Full Build Works

$ ./gradlew clean html --warning-mode all

> Task :html
DITA-OT Transformation Report
Status:           SUCCESS
Output size:      7.58 MB
Duration:         72.08s

BUILD SUCCESSFUL in 1m 24s

Benefits

Aspect Before After
Deprecation warnings 1 0
Configuration Cache Not compatible Fully compatible
External plugin dependencies saxon-gradle None
Task caching Limited Full @CacheableTask
Saxon version control Plugin-defined Project-controlled
Maintenance Depends on plugin updates Self-contained

Task Comparison

Feature SaxonXsltTask (old) XsltTransformTask (new)
Configuration Cache No Yes
@CacheableTask No Yes
Abstract properties No Yes
@InputFile / @OutputFile Partial Full
Serializable No (XmlSlurper) Yes
Saxon version Plugin bundled Project controlled

Usage Guide

Basic XSLT Transformation

task myTransform(type: XsltTransformTask) {
    input 'src/input.xml'
    output 'build/output.xml'
    stylesheet 'src/transform.xsl'
}

With Parameters

task myTransform(type: XsltTransformTask) {
    input 'src/input.xml'
    output 'build/output.xml'
    stylesheet 'src/transform.xsl'
    parameters([
        'param1': 'value1',
        'param2': 'value2'
    ])
}

With Additional Output Directory

For stylesheets that generate multiple files:

task myTransform(type: XsltTransformTask) {
    input 'src/input.xml'
    output 'build/main-output.xml'
    stylesheet 'src/multi-output.xsl'
    parameters(['output-dir.url': file('build/additional').toURI().toString()])
    outputs.dir 'build/additional'
}

Rollback Instructions

If needed, to revert to saxon-gradle:

  1. Delete buildSrc/ directory
  2. Restore build.gradle from git:
    git checkout HEAD -- build.gradle
  3. Note: Configuration Cache will not work with saxon-gradle

References

@jyjeanne
Copy link

jyjeanne commented Jan 9, 2026

@infotexture @jelovirt i check implemented solution on windows OS with Dita OT 4.3.5, gradle 9 and i have this result :

PS C:\Users\jjeanne\Documents\Perso\Projects\docs> .\gradlew.bat -PditaHome='C:\dita-ot\dita-ot-4.3.5' clean pdf

> Task :pdf
Starting DITA-OT transformation

???????????????????????????????????????????????????????
DITA-OT Transformation Report
???????????????????????????????????????????????????????
Status:           SUCCESS
DITA-OT version: 4.3.5
Files processed:  1
Formats:          pdf
Output size:      7,58 MB
Duration:         74,66s
???????????????????????????????????????????????????????

BUILD SUCCESSFUL in 1m 16s
9 actionable tasks: 1 executed, 8 up-to-date
PS C:\Users\jjeanne\Documents\Perso\Projects\docs> .\gradlew.bat -PditaHome='C:\dita-ot\dita-ot-4.3.5' clean pdf

BUILD SUCCESSFUL in 1s
9 actionable tasks: 1 executed, 8 up-to-date

You see improvment only on second time execution using new cache feature. Best regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Ant/Gradle build scripts & CI/CD issues dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants