Savant Build Tool: Dependencies & Build Targets

Brian Pontarelli

Savant Build Files: Define Dependencies & Build Targets

In this post we'll cover Savant build files and how to define your dependencies and build targets. First, let's set up a simple build file for a new project called HelloWorld. Here is how we define this project and its version:

project(group: "org.inversoft.savant.example", name: "HelloWorld", version: "1.0", licenses: ["ApacheV2_0"]) {
}

To define a project you need a group, name, version and a license. You can ignore the licenses definition for now, we'll cover that in a future post. The group name is usually a reverse DNS name (notice that the org is first not last) that identifies the owners of the project. The name is the project's formal name and the version is the project’s Semantic Version.

Next we can define the other libraries and frameworks that our project depends on. We place the dependencies inside the project definition and break them into groups. It looks like this:

project(group: "org.inversoft.savant.example", name: "HelloWorld", version: "1.0", licenses: ["ApacheV2_0"]) {
  dependencies {
    group(name: "compile") {
      dependency(id: "org.apache.commons:commons-collections:3.1")
    }
    group(name: "test-compile") {
      dependency(id: "org.testng:testng:6.8.7")
    }
  }
}

These dependency directives tell Savant that in order to compile our source code Savant needs to include Commons Collections version 3.1. Likewise, in order to compile our test source code, Savant will need to include TestNG version 6.8.7.

Savant always uses a shorthand notation for dependencies. This notation is in this form:

::

There are a couple other formats for this notation, but this form is the most commonly used.

Savant's dependency groups are completely freeform and we could have named the groups above compile-time and test-compile-time instead. However, there are a few standard names that are used by Savant plugins. These are:

  • compile - Used during compilation
  • provided - User during compilation, but not at runtime because these dependencies are provided by an external source (i.e. a servlet container)
  • compile-optional - Used during compilation, but not included at runtime because they are optional
  • runtime - Used at runtime and not included during compilation (used mostly for API implementations like implementors of the SLF4J logging API)
  • test-compile - Used during compilation of test source code
  • test-runtime - Used to run the tests

Next, let's add some build targets to our project. The first target we need to add is a compile target that will compile all of our source code. Compiling everything is not a trivial task and writing the Groovy to accomplish that sounds tedious. Savant uses plugins to provide reusable features like compiling source code. In this case, we will use the Java plugin since our project is a Java project.

Plugins are dependencies just like the project dependencies we added above. However, they aren't included in the same way. Instead, we simply define the plugin dependency in the loadPlugin directive. Here is how we include that plugin in our build file:

java = loadPlugin(id: "org.savantbuild.plugin:java:0.2.0")

This loads the Java plugin version 0.2.0 and the plugin reference is assigned to a variable named java. Now that we have loaded the plugin, we need to configure it. The Java plugin requires us to define the JDK we are using to compile. We'll start by setting the java version using the settings of the plugin like this:

java.settings.javaVersion = "1.8"

Next, we need to create a configuration file in our home directory that tells Savant location of JDK 1.8 on our computer. Forcing this configuration file to be in the users home directory allows you to have Java in different locations on different computers. We need to create the file ~/.savant/plugins/org.savantbuild.plugin.java.properties and add this configuration:

1.8=/Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home

This tells Savant where JDK 1.8 lives on my computer. The path to the JDK may be different on your computer.

Now that the Java plugin is configured, we can use it in our build target like this:

target(name: "compile", description: "Compiles everything") {
  java.compile()
}

This will tell Savant that we have a build target named compile and that it will execute the Java plugin's compile() method. This method on the Java plugin executes the javac command using the path we configured above. It will include the dependencies we configured in our build file on the classpath when executing the javac command.

Finally, we can execute our build from the command line like this:

$ sb compile

This is a brief overview of how Savant build files are written. We've demonstrated the creation of the Savant project definition, defined two new dependency groups, loaded and configured the Java plugin, requested the Java plugin compile our project within a compile target and successfully executed the Savant build command to run the compile target.

You can learn more about Savant at the Github project page here:

https://github.com/inversoft/savant-core/wiki

In our next blog post, we will dive into more detail about Savant Dependency Management concepts.

Tags:
Savant