sbt-fmpp-resolver
An Apache FreeMarker template resolver for the sbt new command.
FMPP is used as the template processing engine.
Usage
sbt-fmpp-template is compatible with sbt 0.13.13+ and sbt 1.0.
To allow a specific project to use the FmppTemplateResolver
, add the following to project/plugins.sbt
:
addSbtPlugin("com.github.jeffreyolchovy" % "sbt-fmpp-template" % "0.1.2")
To globally allow sbt to evaluate templates using this resolver, add that same line to ~/.sbt/0.13/plugins/build.sbt
.
Once the plugin has been installed for a given project (or globally), you can leverage it using sbt new
or via the fmpp
input task.
For example, given the following sbt project structure:
(root)
|-/templates # a directory containing a structured set of FreeMarker templates and/or static files
|-/includes # a directory containing FreeMarker files that can be used as libraries
|-/project
|-build.sbt
Execution of the following command will:
- Evaluate the templates found in the
templates
directory using the data given on the command line (-D ...
) - Allow templates to use any custom macros and instructions defined in the
includes
directory - Emit the generated files to the
src
directory
sbt new -S templates -O src -D "name:MyProject, organization:com.example" --freemarker-links includes:includes
If that invocation seems too intimidating, sweep your specifications into a config.fmpp
file and invoke:
sbt new -C config.fmpp
For the above example, the contents of config.fmpp
should include:
sourceRoot: templates
outputRoot: src
data: {
name: MyProject,
organization: com.example
}
freemarkerLinks: {
includes: includes
}
modes: [
ignore(config.fmpp)
]
File and directory names can be altered using FreeMarker macros. This allows for the interpolation and expansion of template file paths, if desired.
By default, empty directories will not be copied to the target destination. This behavior is configurable.
For more information refer to the FreeMarker manual and the FMPP Command-line tool documentation.
All of FMPP's command-line options are respected.
Examples
A directory of example sbt project templates is provided. It includes FreeMarker template versions of popular sbt-giter8 templates.
- scala-sbt.ftl (inspired by scala-sbt.g8)
- scala-native.ftl (inspired by scala-native.g8)
- sbt-autoplugin.ftl (inspired by sbt-autoplugin.g8)
Refer to the README.md of the examples directory for more details.
Additional example projects that utilize the FmppTemplateResolver
also exist in the plugin/src/sbt-test/sbt-fmpp-template directory.
These examples include projects that:
- Evaluate templates that exist on the local file system
- Evaluate templates from a remote git repository
- Evaluate templates from a remote git repository, referencing a given branch by name
- Evaluate templates from a remote git repository, referencing a given tag
- Evaluate templates that need to create dynamic directory paths and file names
- Evaluate templates that utilize custom user-defined macros
- Evaluate templates using a configuration file
Motivation
sbt supports the idea of pluggable template resolvers, however, only one implementation (Giter8) is provided out of the box. While I have made extensive use of Giter8 for project templates and project archetypes in the past, Giter8 is somewhat lacking in terms of raw features and extensibility. This is not necessarily a bad thing. The simplicity and limited range of Giter8 has actually been quite welcoming for the majority of my use cases, but when more power is required, there's not a whole lot you can do...
I considered forking and extending Giter8 with additional functionality, but since there already exist myriad templating solutions, it would make more sense, in my opinion, to leverage a more robust templating engine and implement any of Giter8's integral features in the new resolver or with the chosen templating solution itself.
After surveying the landscape of existing templating engines, I landed on Apache FreeMarker. FreeMarker has much more power than I anticipated -- or even desired -- and its array of features can, at times, be rather dizzying. The thing that's worth noting is how simple integration has been. The FMPP front-end has a well documented feature set and Java API.
sbt's mechanism for adding template resolvers has also proven to be rather simple. The docs explain the process clearly.
Project Structure
plugin
An sbt plugin that adds the FmppTemplateResolver
to sbt's templateResolverInfos
setting.
This plugin cross builds for sbt 0.13 and sbt 1.0. There is still a bit of noise and hackery in the build.sbt that I can't seem to simplify, notably:
See the build.sbt and the linked issues for more information on the workarounds.
plugin-buildinfo
NOT YET IMPLEMENTED
An additional plugin that requires the sbt-buildinfo plugin, as it allows users to inject contextual project information into the templates at evaluation time.
A TDD
renderer is provided for exporting BuildInfoResult
s in a format digestable by FMPP.
resolver
The template resolver implementation.
Currently, it programmatically invokes the FMPP CommandLine
front-end via FMPP's Java API.
It uses logic ported from Giter8 for resolving remote resources.
Notes
- Dynamic file and directory names can be obtained by utilizing
pp
directives inside templates - The
config.fmpp
files in the example projects present a method for automatically interpolating variables when present in file names or directory paths - FreeMarker does not include built-in string operations for Java-esque string transformations (e.g. to/from UpperCamelCase, lowerCamelCase, snake_case, etc.)
- These operations can be defined in custom macros and included as a library (or namespace, in FreeMarker lingo)
Credits
- The real heavy lifting is done by the underlying templating engine and templating "front-end" (FreeMarker and FMPP)
- For git repository integration, I've reused whatever functionality I could from Giter8
Licensed under the Apache 2.0 license
Copyright Jeffrey Olchovy, 2017