1. Installation

1.1. Requirements

SBuild requires an working Java 6 Runtime. All other dependencies are bundled within the SBuild distribution ZIP file.

1.2. Download

You can download the latest stable SBuild distribution from the download page.

1.3. Installation

SBuild runs under all major operating systems, that have a Java 6 Runtime environment.

1.3.1. Linux

You should check, if your Linux distribution provides ready to install packages for SBuild, if so, you may want to install them in the specific way of your Linux distribution. For Gentoo Linux, you can find an ebuild for SBuild in lefous portage overlay.

If you used the installation procedure of you Linux distribution you were done here, else continue.

Unpack the ZIP file content into a directory of your choice, e.g. /opt/sbuild-0.7.7.

Make sure, the expanded file /opt/sbuild-0.7.7/bin/sbuild is executable, e.g. with chmod -x /opt/sbuild-0.7.7/bin/sbuild.

Add the binary directory (e.g. /opt/sbuild-0.7.7/bin/) to your search path: PATH=/opt/sbuild-0.7.7/bin:$PATH

1.3.2. Windows

Unpack the ZIP file content into an directory of your choice, e.g. C:\Program Files\sbuild-0.7.7. After unpacking, you should see the sub directories bin, lib and doc.

Add the environment variable SBUILD_HOME with the value of that directory, e.g. SBUILD_HOME=C:\Program Files\sbuild-0.7.7

Wasn’t there a SBUILD_HOME variable, I needed to set under Windows?

Yes, there was, but only because we had a bug in our batch script, which is fixed now, thanks to Tobias Lahn.

Add the path of the bin directory to the PATH variable: PATH=%SBUILD_HOME%\bin:%PATH%

Special Notes for Cygwin Users

If running sbuild under cygwin, you may see the following error message:

$ sbuild all
java.lang.NoClassDefFoundError: de/tototec/sbuild/runner/SBuildRunner
Caused by: java.lang.ClassNotFoundException: de.tototec.sbuild.runner.SBuildRunner
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: de.tototec.sbuild.runner.SBuildRunner.  Program will exit.
Exception in thread "main"

This is because cygwin does not execute the sbuild.bat file but the sbuild shell script. You may either call sbuild.bat directly, or delete the PATH/bin/sbuild file.

The reason is, that Java on Windows accepts the -classpath option with : (double colon) separator whereas on Linux, Mac and other system, it expects ; (semi colon) as separator. In most cygwin environments, a Java runtime environment for Windows is used. If you built your JRE for or in cygwin, you will most probably not see this issue at all.

1.4. Check the installation

Open an new terminal window and execute the command: sbuild --version

You should see something like this:

Output of sbuild --version
$ sbuild --version
SBuild 0.7.7 (c) 2011 - 2014, ToToTec GbR, Tobias Roeser

Congratulations, you have successfully finished your SBuild installation.

1.5. Troubleshooting

If you went into trouble, you may want to search the fourms or create a new thread. If you found a bug please check, if there is already a ticket for it or create a new ticket.

2. First steps on the Commandline

SBuild’s main interface is a command line tool called sbuild. It supports various options and will receive the targets, you want to build, as parameter. When invoked, it searches for a build file SBuild.scala in the current directory, reads it and executes the requested targets.

2.1. Built-in Commandline Help

SBuild supports various commandline options. You can invokde sbuild with the --help or -h option, to get a list of all supported options and parameters with short descriptions.

Output of sbuild --help
bash$ sbuild --help
SBuild 0.7.7 (c) 2011 - 2014, ToToTec GbR, Tobias Roeser

Usage: sbuild [options] [parameter]

Options:
  --additional-buildfile,-F FILE         Add an additional buildfile into scope.
  --buildfile,-f FILE                    The buildfile to use (default:
                                         SBuild.scala).
  --check                                Check targets for cycles and missing
                                         scheme handlers.
  --check-recursive                      Check targets of this project and all
                                         its modules for cycles and missing
                                         scheme handlers.
  --clean                                Remove all generated output and caches.
                                         This will force a new compile of the
                                         buildfile. (This will not remove output
                                         generated by buildfiles.)
  --create-stub                          Create a new minimal SBuild.scala file
                                         to start with.
  --define,-D KEY=VALUE                  Define or override properties. If VALUE
                                         is omitted it defaults to "true".
  --dependency-tree                      Show dependency tree(s) and exit.
  --execution-plan                       Show the execution plan and exit.
  --fsc                                  Use the fast scala compiler
                                         (client/server). The fsc compiler of
                                         the correct Scala version must be
                                         installed.
  --help,-h                              Show this help screen.
  --jobs,-j N                            Allow processing N targets in parallel.
                                         Use 1 to disable parallel and 0 to use
                                          threads.
  --just-clean                           Remove all generated output and caches
                                         without reading any buildfile. This
                                         will essentially remove the ".sbuild"
                                         directory in the current directory.
  --just-clean-recursive                 Remove all generated output and caches
                                         without reading any buildfile. This
                                         will essentially remove the ".sbuild"
                                         directory in the current directory and
                                         all sub-directories, no matter, if
                                         sub-directories contain SBuild projects
                                         or not.
  --keep-going,-k                        Keep going when some targets can't be
                                         made.
  --list-available-plugins               List all plugins available (used and
                                         unused) by this project.
  --list-modules                         Show a list of modules involved in this
                                         project.
  --list-plugins                         List all plugins used by this project.
  --list-targets,-l                      Show a list of targets defined in the
                                         current buildfile.
  --list-targets-recursive,-L            Show a list of targets defined in the
                                         current buildfile and all modules.
  --no-color                             Disable colored output.
  --no-fsc                               Do not try to use the fast scala
                                         compiler (client/server)
  --no-global                            Do not read global settings from /.sbuildrc.
  -q,--quiet,--no-progress               Quiet mode. Don't show progress
                                         messages with progress in percent.
                                         (This will speed up SBuild
                                         initialization.)
  --repeat SECONDS                       Repeat the requested action after the
                                         given time (in seconds), but not before
                                         the previous run was completed.
  --search-targets,--find-targets REGEX  Search targets based on a regular
                                         expression pattern.
  --verbose,-v                           Be verbose when running.
  --version                              Show SBuild version.

Parameter:
  TARGETS  The target(s) to execute (in order).

2.2. The Buildfile

The buildfile contains all instructions and target descriptions and is typically written by the developer.

If sbuild does not found any buildfile, it will stop with an error message like this one:

bash$ sbuild
Initializing project...

SBuild detected a failure in the project configuration or the build scripts.
Project: SBuild.scala
Details: Project buildfile "SBuild.scala" does not exists or is not a file.

The relevant error message tells you, that the buildfile SBuild.scala is required to proceed but was not found in the current directory.

2.2.1. Using an alternative Buildfile

To use another buildfile, you can use the --buildfile or -f option.

Using an alternative buildfile
bash$ sbuild -f MyBuild.scala

2.2.2. Creating a Buildfile stub

You you want to start a new SBuild project, you can use the --create-stub option, to let SBuild create a minial buildfile for you.

bash$ sbuild --create-stub

For an easy start you can instruct sbuild to create an minimal template file for you:

bash $ sbuild --create-stub

In case, you run sbuild --create-stub accidentally in a directory, where a buildfile already exists, SBuild will refuse to override it.

bash$ sbuild --create-stub

SBuild failed with an unexpected exception (SBuildException).
Details: File SBuild.scala already exists.

2.2.3. The Stub Buildfile

Here is the content of a Buildfile created with sbuild --create-stub.

import de.tototec.sbuild._

@version("0.7.7")
class SBuild(implicit _project: Project) {

  Target("phony:clean") exec {
    Path("target").deleteRecursive
  }

  Target("phony:hello") help "Greet me" exec {
    println("Hello you")
  }

}

This Buildfile contains the following information:

  • It requires SBuild 0.7.7 or newer

  • It contains two targets clean and hello

  • Both targets are phony, which means, they do not produce a single file but constitute some tasks, and both contain some custom actions.

You can customize the result of sbuild --create-stub by providing your own stub buildfile. To do this, you have to create a directory stub in the installation directory of SBuild (${SBUILD_HOME}) and place the template buildfile in that directory. If SBuild is run with the --create-stub, it will first search in that stub directory if it will find a file with the same name as the expected buildfile, namely SBuild.scala.

You can also provide more that one stub files with different names. To select a specific stub file, e.g. Setup.scala, run SBuild with sbuild --create-stub -f Setup.scala. Now SBuild will first search for a file named Setup.scala in the ${SBUILD_HOME}/sub directory. If one is found, this will be used as stub file, else the built-in default will be used. Finally, you will find the newly created stub file in the current directory.

2.3. Running SBuild

To execute one or more targets of a project, simple give the desired targets as parameters.

E.g. to execute the clean and the hello targets of the just created buildfile above, you will run sbuild clean hello. Following is the output of that command:

bash$ sbuild clean hello
Initializing project...
Compiling build script: /tmp/test/SBuild.scala...   (1)
[0%] Executing...
[0%] Executing target: clean   (2)
[50%] Finished target: clean after 4 msec
[50%] Executing target: hello
[50%] Greet me
Hello you
[100%] Finished target: hello after 0 msec
[100%] Execution finished. SBuild init time: 3,904 msec, Execution time: 57 msec   (3)
1 If SBuild never run before or if it detects, that the buildfile has changed, it will compile the buildfile, thus the output Compiling build script: ....
2 After compilation of the buildfile, it will execute the required targets and print what it is actual doing paired with a handy progress report.
3 At the end, you will see the Execution finished message and some little statistics.

In any subsequent run, compilation of the buildscript is not needed again and SBuild the execution of the requested targets start almost instantly.

bash$ sbuild clean hello
Initializing project...
[0%] Executing...
[0%] Executing target: clean
[50%] Finished target: clean after 2 msec
[50%] Executing target: hello
[50%] Greet me
Hello you
[100%] Finished target: hello after 0 msec
[100%] Execution finished. SBuild init time: 164 msec, Execution time: 55 msec

2.4. Built-in project exploration

SBuild has some handy options, which let you easily explore a project. Most of these options also have a "recursive" variant, which includes also all modules (sub projects) into the output.

The most frequently used options are:

--list-targets, -l

Show a list of targets defined in the current buildfile.

--list-targets-recursive

Show a list of targets defined in the current buildfile and all modules.

--list-modules

Show a list of modules involved in this project.

--list-plugins

List all plugins used by this project.

--list-available-plugins

List all plugins available (used and unused) by this project.

2.5. Project validation

TBD

2.6. Parallel execution

By default, SBuild will execute targets in parallel, to utilize the resources of modern multi-core hardware more efficiently. You can customize the number of simultaneously used threads with the --jobs or -j commandline option. With -j 1, you can disable the parallelization entirely and will also reduce the output slightly.

To use as much threads as your CPU has cores, you can use -j 0 (which is also the default) to instruct SBuild to auto-detect the used thread pool size.

To make a custom jobs setting permanent, you can add it to the ${HOME}/.sbuildrc file.

${HOME}/.sbuildrc
jobs=4

2.7. Failing the Build

When SBuild detects an execution failure in a target, it will interrupt all other parallel executing targets, print a error message with some details about the initially failed target and quit.

Sometimes, it is desirable to fail the build as late as possible and not stop at the moment the first target fails. Of course, the build can not be completed successfully, but some other targets may complete. In such scenarios, you can use the --keep-going or -k commandline option. When specified, SBuild tries to complete as much targets as possible before failing with a descriptive error message indicating which targets failed and which could not be completed because of unsatisfied dependencies.

2.8. Repetitive tasks

TBD

3. Writing Buildfiles

3.1. The Buildfile - the only true source

SBuild buildfiles are normal Scala source files. As such, they can be viewed and edited as any other text file with any text editor. Although an editor providing syntax highlighting and other Scala specific candy might be helpful.

The buildfile is the only place, where SBuild looks for description of your build!

To change the build and the SBuild behavior, you have thus only the option to change the buildfile or to make use of the various commandline options of sbuild.

3.2. Buildfile Preprocessing

SBuild buildfiles are Scala source files and must be compiled before SBuild can use them. This preprocessing is automatically done by SBuild, without any interaction from your site. Of course, the Scala compiler has no notion of SBuild plugins, libraries and includes, thus the buildfile must be preprocessed. SBuild will extract and interpret specific annotations before the buildfile is compiled and executed.

These are the special annotation, which are typically placed just before the build class SBuild.

@version

Specified the minimal SBuild version that is required for this buildfile. This annotation should be present in each buildfile. It will help detect version mismatches which otherwise might result in strange compile or runtime errors, which are comparatively hard to understand.

Example: @version("0.7.2")

If in doubt, use the version you are currently using.

@classpath

Additional plugins and libraries that should be used by this buildfile have to be declared here. Each given entry will be interpreted as a potentially plugin (see chapter Plugins), unless it is prefixed with raw:. If the entry is a plugin, SBuild will automatically scan it’s manifest and load it’s dependencies. All default SchemeHandler can be used here.

Example: @classpath("mvn:org.sbuild:org.sbuild.plugins.sbuildplugin:0.3.0", "mvn:org.apache.ant:ant:1.8.4")

@include

Here you can refer to additional Scala source files. These files will be compiled by SBuild with the same classpath as the buildfile. The compiled classes will be added to the classpath and can be directly used. All default SchemeHandler can be used here.

Example: @include("CommonSettings.scala")

3.3. SBuild Core API

The Core API of SBuild allows you to

  • create targets,

  • declare actions that should take place when a target gets executed,

  • define dependencies between targets,

  • interact with the environment,

  • run file operations

With a knowledge of the Core API, you have everything you need to write simple to complex buildfiles in an imperative style.

Please don’t stop here. With the use of Plugins you will be able to write much more concise and readable build script in a declarative fashion.

3.3.1. Targets

Targets are the central unit of work in SBuild. Each target has at least a name. This name is what you use on the SBuild commandline.

Targets can be of two kinds:

  • file targets - Targets, that are associated with a local file.

  • phony targets - Targets, not associated to any file representing some kind of action.

A buildfile with two targets, file.txt and action
import de.tototec.sbuild._

@version("0.7.7")
class SBuild(implicit _project: Project) {

  Target("file.txt") (1)

  Target("phony:action") (2)

}
1 A file target associated with the file file.txt in the project directory. The prefix file: is optional and was omitted.
2 A phony target with the name action. As it is not associated with any file it must be prefixed with phony:.

File-Targets represent a file in the local file system. If SBuild is requested to execute a file target (without dependencies), it will first check if that file already exists, and only if not, the target is run.

Phony-Targets represents some kind of action not resulting in a single local file. If SBuild is requested to execute a phony target, the target will always run. Of course, there are various mechanism to make that logic more clever, e.g. by making targets cacheable.

3.3.2. Executions

A plain target as seen above does not do anything when executed. That can be changed by assigning an execution block to a target.

A buildfile with two targets, file and action, both having an execution block
import de.tototec.sbuild._

@version("0.7.7")
class SBuild(implicit _project: Project) {

  Target("file.txt") exec { (1)
    import java.io._
    val target = Path("file.txt") (2)
    println(s"Created file: ${target}") (3)
    val ps = new PrintStream(target)
    ps.println("file content") (4)
    ps.close()
  }

  Target("phony:action") exec { (5)
    println("Action executed") (6)
  }

}
1 A definition of a file target file.txt with an execution block.
2 Here you see a way to use Path to construct a java.io.File relative to the project directory. This is not the most appropriate way in this situation, as you will see soon in the "Target Context" section.
3 The string "Created file: file.txt" will be printed to standard output stream.
4 The string file content is written into the file file.txt.
5 A definition of a phony target with an execution block.
6 The string "Action executed" will be printed to standard output stream.

The example above shows, how targets with execution blocks can be created.

3.3.3. Dependencies

Targets can also have dependencies.

TBD

3.3.4. Target Context

TBD

3.3.5. File operations

TBD

TBD

3.4. Using Plugins

TBD

3.5. Multi-Project Setups

TBD

3.6. Working with Include-Files

TBD

4. Advanced topics

4.1. Understanding SchemeHandler’s

TBD

4.2. Writing Plugins

4.2.1. Concept

The Plugin concept was introduced in SBuild 0.7.0 and further refined in 0.7.1 and 0.7.2.

SBuild Users deal mostly with the Plugin API to access and configure Plugin instances. Implementers of Plugins implement the `Plugin trait to create and apply plugin instances to the current project.

Finally, SBuild plugins need to be packaged as JAR files and need provided special entries in the JAR Manifest.

4.2.2. Plugin API

The Plugin API is defined by the Plugin object and the PluginHandle trait.

The Plugin object is used to activate a plugin instance and can be also use to query for some properies of a plugin, e.g. it’s version or whether a concrete instance is active or not.

Once activated through one of the Plugin.apply methods, the PluginHandle trait provides various methods to work with Plugin instances.

4.2.3. Implementing a Plugin

An plugin implementation is essentially a class which extends the Plugin trait.

This is the Plugin trait (see Scaladoc):

package de.tototec.sbuild

trait Plugin[T] {  (1)
  def create(name: String): T  (2)
  def applyToProject(instances: Seq[(String, T)])  (3)
}
1 The type parameter T represents the type of the plugin instance.
2 Create a new plugin instance with the name name. "" is a valid instance name and represent the default instance.
3 Apply the plugin’s functionality to the project. This is called at the end of the project initialization, especially after the buildfile was processed.

The class can have either a default constructor or a one-arg constructor of type Project. The later is also the preferred way to get a instance of the current project and is especially required if you want to contribute targets or scheme handler to the project.

By mixing in additional traits, plugin can make use of additional plugin features.

4.2.4. Plugin with Dependencies

Plugins that depend on other plugins need to mix-in the PluginWithDependencies trait. SBuild will ensure, that such a plugin will be applied to the project before their dependencies are applied. This way, it is guaranteed, that configuration changed made while the application can take effect.

This is the PluginWithDependencies trait (see Scaladoc):

package de.tototec.sbuild

trait PluginWithDependencies { self: Plugin[_] =>
  def dependsOn: Seq[PluginDependency]
}

Currently, two kind of PluginDependency's can be used:

  • PluginDependency.Basic - This is just the reference to another plugin instance class.

  • PluginDependency.Versioned - This is a reference to another plugin instance with additional version constraints.

4.2.5. Getting notified when a Plugin configuration changes

Plugins that want to be notified whenever they get (re-)configured need to mix-in the PluginConfigureAware trait.

This is the PluginConfigureAware trait (see Scaladoc):

package de.tototec.sbuild

trait PluginConfigureAware[T] { self: Plugin[T] =>
  def configured(name: String, instance: T)
}

TBD

4.2.6. Packaging Plugins

The plugin implementation needs to be packaged into a JAR file together with a proper JAR Manifest, which need some of the following supported entries.

  • SBuild-Plugin - Used to declare one or more plugins. This entry is mandatory!

  • SBuild-Classpath - Used to add additional dependencies (as part of the private class space). All default SBuild schemes (file, http, mvn) are supported.

  • SBuild-ExportPackage - Used to export some of the plugins private packages. If this is not present, all packages of that plugin will be exported.

4.2.7. Conventions

TBD

4.2.8. Plugin Classloading

TBD

5. Vision

5.1. The unimplemented parts

5.2. The eco system