TL;DR This article shows a solution for overcoming the issue GRADLE-3122. You can jump straight to the implementation at the bottom of this post.
In my current project we have a need to generate a set of files for each environment, using templates. As this is a
Gradle project, this requirement is easily accomplished with a CopyTask
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
The processTemplates
task above will copy all files from src/templates
to build/output/<envName>
. All files whose
name ends with .template
will be processed and tokens (ex: ${variable}
) will be replaced by their values from the
env/<envName>.properties
file. Adding the properties file as an input for the task is important (inputs.file
method
call), so when you change it, the task will be re-executed on the next build.
Simple, right?
Not so fast, Johnny…
This worked fine until we had one template that was really big (120KB) and we found out about issue GRADLE-3122. Gradle uses Groovy’s SimpleTemplateEngine, that can only process files up to 64KB!
Newest versions of Groovy (2.4+) include StreamingTemplateEngine that does not have this limit, but the most recent version of Gradle (2.5 as of this post) still uses Groovy 2.3…
One way to overcome this would be to use Ant’s ReplaceTokens
filter, simply by changing our templates to use Ant’s
token syntax (ex: @variable@
) and changing the line expand(env)
to filter(ReplaceTokens, tokens: env)
But because we need to use logic in our templates (if’s and loops), we had to come up with a different approach. The solutions available were too simple for our needs or too complicated to implement in a clear way, making them unsuitable for our project. So we decided to roll…
Our own solution
Finally we decided to implement a simple template processor using FreeMarker. We select this awesome template engine for its feature set, IDE support and excelent OSS reputation, although the solution bellow could be adapted to be used with any other template engine (Velocity, JMustache, etc…)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
|
Note that we now use the .ftl
file extension for our templates, to enable support in our
IDE of choice. To use the processor, you have to put it under the
buildSrc project.
This is a special “module” in your project that is a simple way to organize build logic in your build scripts. It is
all automatically handled by Gradle. You’ll also need a small build.gradle
just for declaring the dependencies for
FreeMarker and Apache Commons IO (used for the copyFile
method):
1 2 3 4 5 6 |
|
To have this code available to your main build script, add this two files in your project, in the following paths:
- buildSrc/build.gradle
- buildSrc/src/main/groovy/your/package/TemplateProcessor.groovy
The last step is to actually use it in our processTemplates
task:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Not hard, eh? Note that this task is not a CopyTask
anymore, so we now need to specify its inputs and outputs.
My plan is to convert this code into a proper Gradle plugin. But for now: That’s all, folks!