Home Code Other Software

Table of Contents

Introduction

sbt-editsource is a text substitution plugin for SBT 0.11.x and greater. In a way, it’s a poor man’s sed(1), for SBT. It provides the ability to apply line-by-line substitutions to a source text file, producing an edited output file. It supports two kinds of edits:

Each of these capabilities is explained in more detail, below.

This plugin only works with SBT 0.11.x or better. If you’re using SBT 0.7, there’s an older version (with fewer features and a different variable syntax) here.

Getting the Plugin

First, within your SBT project, create project/plugins/build.sbt (if it doesn’t already exist) and add the following:

addSbtPlugin("org.clapper" % "sbt-editsource" % "0.6.5")

Next, in your main project build.sbt file, add:

seq(EditSource.settings: _*)

Now the plugin and its settings are available to your SBT builds.

Settings and Tasks

The plugin provides the following new settings and tasks.

Note: sbt-editsource uses predefined SBT settings, where possible (e.g., sources). Where sbt-editsource defines its own settings, those settings are in an EditSource namespace, to avoid import clashes with identically named settings from other plugins. The pattern for accessing settings in this plugin is:

EditSource.settingName in EditSource.Config <<= ...

Task access is similar.

Settings

The plugin provides the following new settings.


sources


The source files to be edited. For instance, suppose you want to edit all files under “src” ending in “.txt”. To do so, use:

sources in EditSource.Config <++= baseDirectory.map(d => (d / "src" ** "*.txt").get)

If you also want to apply the edits to all files ending in “.md”, use either:

sources in EditSource.Config <++= baseDirectory.map(d => (d / "src" ** "*.txt").get)

sources in EditSource.Config <++= baseDirectory.map(d => (d / "src" ** "*.md").get)

or, more succinctly:

sources in EditSource.Config <++= baseDirectory.map { dir =>
    (dir / "src" ** "*.txt").get ++
    (dir / "src" ** "*.md").get
}

targetDirectory


The directory to which to write the edited versions of the source files. For example:

EditSource.targetDirectory in EditSource.Config <<= baseDirectory(_ / "target")

See also editFlatten, below.


flatten


If EditSource.flatten is true, then the edited files will all be placed directly in EditSource.targetDirectory; if there are name clashes, then some files will be overwritten. If EditSource.flatten is false, then the partial path to each source file is preserved in the target directory.

An example will help clarify. Consider the following file tree:

Directory tree

Let’s assume you’re editing all the files ending in “.md”, into the target directory.

sources in EditSource.Config <++= baseDirectory(
  d => (d / "src" ** "*.md").get
)

EditSource.targetDirectory in EditSource.Config <<= baseDirectory(_ / "target")

If you also set:

EditSource.flatten in EditSource.Config := true

the edit operation will put all the edited versions of all three files directly in the target directory.

If, instead, you set:

EditSource.flatten in EditSource.Config := false

you’ll end up with the following edited versions:


variables


EditSource.variables is a sequence of (variableName, value) pairs. For instance, the following two lines define a ${projectName} variable that substitutes the name of the project, and a ${author} variable:

name := "my-project"

EditSource.variables in EditSource.Config <+= name {name => ("projectName", name)}

EditSource.variables in EditSource.Config += ("author", "Brian Clapper")

Inside a source file to be edited, variable references are of the form ${varname}, as in the Unix shell. A shortened $varname is also support. The ${} long form also supports a default syntax: ${varname?default}. If the reference variable has no value, then the default value is supplied, instead. (The ?default syntax is not supported for the short form reference.)

With the above definitions in place, when the source files are edited, any reference to ${projectName} is replaced with “my-project”, and any reference to ${author} is replaced with “Brian Clapper”.

You can define any number of variables. If the edit logic encounters a variable that isn’t defined, it simply replaces the variable reference with an empty string (like bash does).

In addition to the variables you define in your build file, the sbt-editsource also honors the following special variable prefixes:


substitutions


EditSource.substitutions is a sequence of regular expression substitutions, of the form:

sub(regex, replacement)
sub(regex, replacement, flags)

There’s only one (optional) flag right now:

Additional flags can be specified inline, as indicated in the documentation for the java.util.regex.Pattern class.

For example, to replace the first occurrence of the word “test” in each line with “TEST”, without regard to case, you might use:

EditSource.substitutions in EditSource.Config += sub("""(?i)\btest\b""".r, "TEST")

The ”(?i)” is the embedded option sequence that tells the regular expression parser to use case-blind comparison.

Similarly, to replace all occurrences of the word “test”, with regard to case, you might use:

EditSource.substitutions in EditSource.Config += sub("""\btest\b""".r, "TEST", SubAll)

You can specify multiple substitutions, of course:

EditSource.substitutions in EditSource.Config ++= Seq(
    sub("""^#""".r, "//"),
    sub("""\b(?i)simple build tool\b""".r, "Scalable Build Tool", SubAll)
)

Also, regular expression capturing groups are supported, so you can use more complex regular expression substitutions like this:

// Remove everything up to, but not including the word "foo", but save
// the "foo" and everything after.

sub("""^.*(foo.*)$""".r, "$1")

NOTE: Regular expression substitutions are run after variable substitutions.

Tasks

The plugin provides two new SBT tasks.

Restrictions

Change log

The change log for all releases is here.

Author

Brian M. Clapper, bmc@clapper.org

Copyright and License

This software is copyright © 2010-2011 Brian M. Clapper and is released under a BSD License.

Patches

I gladly accept patches from their original authors. Feel free to email patches to me or to fork the GitHub repository and send me a pull request. Along with any patch you send: