| package com.example.android.samples.build |
| |
| import freemarker.cache.FileTemplateLoader |
| import freemarker.cache.MultiTemplateLoader |
| import freemarker.cache.TemplateLoader |
| import freemarker.template.Configuration |
| import freemarker.template.DefaultObjectWrapper |
| import freemarker.template.Template |
| import org.gradle.api.GradleException |
| import org.gradle.api.file.FileVisitDetails |
| import org.gradle.api.tasks.InputDirectory |
| import org.gradle.api.tasks.OutputDirectory |
| import org.gradle.api.tasks.SourceTask |
| import org.gradle.api.tasks.TaskAction |
| |
| |
| class ApplyTemplates extends SourceTask { |
| /** |
| * Freemarker context object |
| */ |
| def Configuration cfg = new freemarker.template.Configuration() |
| |
| /** |
| * The root directory for output files. All output file paths |
| * are assumed to be relative to this root. |
| */ |
| @OutputDirectory |
| public outputDir = project.projectDir |
| |
| /** |
| * Include directory. The templates in this directory will not be |
| * processed directly, but will be accessible to other templates |
| * via the <#include> directive. |
| */ |
| def include = project.file("$project.projectDir/templates/include") |
| |
| /** |
| * List of file extensions that indicate a file to be processed, rather |
| * than simply copied. |
| */ |
| def extensionsToProcess = ['ftl'] |
| |
| /** |
| * List of file extensions that should be completely ignored by this |
| * task. File extensions that appear in neither this list nor the list |
| * specified by {@link #extensionsToProcess} are copied into the destination |
| * without processing. |
| */ |
| def extensionsToIgnore = ['ftli'] |
| |
| /** |
| * A String -> String closure that transforms a (relative) input path into a |
| * (relative) output path. This closure is responsible for any alterations to |
| * the output path, including pathname substitution and extension removal. |
| */ |
| Closure<String> filenameTransform |
| |
| /** |
| * The hash which will be passed to the freemarker template engine. This hash |
| * is used by the freemarker script as input data. |
| * The hash should contain a key named "meta". The template processor will add |
| * processing data to this key. |
| */ |
| def parameters |
| |
| /** |
| * The main action for this task. Visits each file in the source directories and |
| * either processes, copies, or ignores it. The action taken for each file depends |
| * on the contents of {@link #extensionsToProcess} and {@link #extensionsToIgnore}. |
| */ |
| @TaskAction |
| def applyTemplate() { |
| // Create a list of Freemarker template loaders based on the |
| // source tree(s) of this task. The loader list establishes a virtual |
| // file system for freemarker templates; the template language can |
| // load files, and each load request will have its path resolved |
| // against this set of loaders. |
| println "Gathering template load locations:" |
| def List loaders = [] |
| source.asFileTrees.each { |
| src -> |
| println " ${src.dir}" |
| loaders.add(0, new FileTemplateLoader(project.file(src.dir))) |
| } |
| |
| // Add the include path(s) to the list of loaders. |
| println "Gathering template include locations:" |
| include = project.fileTree(include) |
| include.asFileTrees.each { |
| inc -> |
| println " ${inc.dir}" |
| loaders.add(0, new FileTemplateLoader(project.file(inc.dir))) |
| } |
| // Add the loaders to the freemarker config |
| cfg.setTemplateLoader(new MultiTemplateLoader(loaders.toArray(new TemplateLoader[1]))) |
| |
| // Set the wrapper that will be used to convert the template parameters hash into |
| // the internal freemarker data model. The default wrapper is capable of handling a |
| // mix of POJOs/POGOs and XML nodes, so we'll use that. |
| cfg.setObjectWrapper(new DefaultObjectWrapper()) |
| |
| // This is very much like setting the target SDK level in Android. |
| cfg.setIncompatibleEnhancements("2.3.20") |
| |
| // Add an implicit <#include 'common.ftl' to the top of every file. |
| // TODO: should probably be a parameter instead of hardcoded like this. |
| cfg.addAutoInclude('common.ftl') |
| |
| // Visit every file in the source tree(s) |
| def processTree = source.getAsFileTree() |
| processTree.visit { |
| FileVisitDetails input -> |
| def inputFile = input.getRelativePath().toString() |
| def outputFile = input.getRelativePath().getFile(project.file(outputDir)) |
| // Get the input and output files, and make sure the output path exists |
| def renamedOutput = filenameTransform(outputFile.toString()) |
| outputFile = project.file(renamedOutput) |
| |
| if (input.directory){ |
| // create the output directory. This probably will have already been |
| // created as part of processing the files *in* the directory, but |
| // do it here anyway to support empty directories. |
| outputFile.mkdirs() |
| } else { |
| // We may or may not see the directory before we see the files |
| // in that directory, so create it here |
| outputFile.parentFile.mkdirs() |
| |
| // Check the input file extension against the process/ignore list |
| def extension = "NONE" |
| def extensionPattern = ~/.*\.(\w*)$/ |
| def extensionMatch = extensionPattern.matcher(inputFile) |
| if (extensionMatch.matches()) { |
| extension = extensionMatch[0][1] |
| } |
| // If the extension is in the process list, put the input through freemarker |
| if (extensionsToProcess.contains(extension)){ |
| print '[freemarker] PROCESS: ' |
| println "$inputFile -> $outputFile" |
| |
| try { |
| def Template tpl = this.cfg.getTemplate(inputFile) |
| def FileWriter out = new FileWriter(outputFile) |
| |
| // Add the output file path to parameters.meta so that the freemarker |
| // script can access it. |
| parameters.meta.put("outputFile", "${outputFile}") |
| tpl.process(parameters, out) |
| } catch (e) { |
| println e.message |
| throw new GradleException("Error processing ${inputFile}: ${e.message}") |
| } |
| } else if (!extensionsToIgnore.contains(extension)) { |
| // if it's not processed and not ignored, then it must be copied. |
| print '[freemarker] COPY: ' |
| println "$inputFile -> $outputFile" |
| input.copyTo(outputFile); |
| } |
| } |
| } |
| } |
| } |