Replace token in file before building, but keep token in sources

You only need to replace @VERSION@ tokens before releasing your software to the public. Here I defined a task compileForRelease that accomplishes it:

import org.apache.tools.ant.filters.ReplaceTokens

task sourcesForRelease(type: Copy) {
    from 'src/main/java'
    into 'build/adjustedSrc'
    filter(ReplaceTokens, tokens: [VERSION: '2.3.1'])
}

task compileForRelease(type: JavaCompile, dependsOn: sourcesForRelease) {
    source = sourcesForRelease.destinationDir
    classpath = sourceSets.main.compileClasspath
    destinationDir = file('build/adjustedClasses')
}

I don't recommend messing with standard tasks defined by the Java plugin because that would add unnecessary overhead to each and every build.


I found existing answers somewhat unsatisfying, so here is my solution:

import org.apache.tools.ant.filters.ReplaceTokens

task processSource(type: Sync) {
    from sourceSets.main.java
    inputs.property 'version', version
    filter(ReplaceTokens, tokens: [VERSION: version])
    into "$buildDir/src"
}

compileJava {
    source = processSource.outputs
}

This addresses various concerns as follows:

  1. Unlike @Opal's answer, the main source remains unmolested; instead it is staged with modifications to $buildDir/src by the processSource task, which mirrors the standard processResources.
  2. Unlike @Gregory Stachowiak's answer, sourceSets.main.java.srcDirs remains the default value and there is no sleight of hand in specifying a location that does not (yet) exist
  3. Unlike @Raffaele's answer, there is no separate task set for release vs other builds. I disagree that separating them is desirable; I think the added complexity is not worth it unless you have measured any performance hit and found it to be unacceptable. Before going with @Raffaele's solution I would even for instance prefer to limit the scope of the filter with include/exclude patterns.
  4. Task dependencies are implicitly defined via outputs.
  5. All locations are taken from defaults and nothing is stringly typed. The only magic value here is src, the directory under $buildDir where the processed source files are put.
  6. (Edit: added 2019/1/12) Other answers do not properly handle situations where only the version has changed. Changing the version should, by itself, invalidate the task output. This is accomplished via inputs.property.
  7. (Edit 2019/5/20) Uses Sync rather than Copy so that files deleted from source are deleted from the filtered source as well (thanks, @Earthcomputer).

Tags:

Java

Gradle