Description
@infotexture it describes a possible solution implemented to make the DITA-OT documentation 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:
- Delete
buildSrc/ directory
- Restore
build.gradle from git:
git checkout HEAD -- build.gradle
- Note: Configuration Cache will not work with saxon-gradle
References
Description
@infotexture it describes a possible solution implemented to make the DITA-OT documentation 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
execMethodThe
getGitCommitHash()function usedproject.exec()which is deprecated and will be removed in Gradle 9.Problem 2: Saxon-Gradle Plugin Incompatibility
The
eerohele/saxon-gradleplugin (v0.9.0-beta4) is not compatible with Gradle's Configuration Cache, causing build failures when Configuration Cache is enabled.Solution Summary
execproviders.exec()XsltTransformTaskin buildSrcImplementation Details
Fix 1: Replace Deprecated
execLocation:
build.gradle(lines 171-176)Before:
After:
Why this works:
providers.exec()is the Gradle 9 compatible replacementProvider<ExecOutput>for lazy evaluation.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
XsltTransformTaskinbuildSrc/that:@CacheableTask@InputFile,@OutputFileannotationsNew Files Created
File:
buildSrc/build.gradleplugins { 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.groovyMigration 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')becomesparameters(['key': 'value'])(explicit Map).Files Changed
build.gradlebuildSrc/build.gradlebuildSrc/src/main/groovy/XsltTransformTask.groovyVerification Results
No Deprecation Warnings
Configuration Cache Works
All XSLT Tasks Work
Full Build Works
Benefits
@CacheableTaskTask Comparison
@CacheableTask@InputFile/@OutputFileUsage Guide
Basic XSLT Transformation
With Parameters
With Additional Output Directory
For stylesheets that generate multiple files:
Rollback Instructions
If needed, to revert to saxon-gradle:
buildSrc/directorybuild.gradlefrom git:References