jeffreyolchovy / sbt-fmpp-resolver

An Apache FreeMarker template resolver for the sbt new command

Github

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.

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:

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 BuildInfoResults 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