6. Build

Overview

Fantom comes bundled with its own standard build engine to promote consistency and make sharing code easy. Build scripts are normal Fantom scripts which subclass from BuildScript. Characteristics of build scripts:

The build toolkit is designed to provide a consistent way to organize build scripts - it doesn't provide a comprehensive library of everything you might need to perform a build. But by predefining common scripts such as BuildPod it reduces most scripts down to purely declarative information. When you do need the power of custom code, you can implement it cleanly as normal Fantom code.

Example

Let's create a build script called "buildtest.fan":

using build

class Build : BuildScript
{
  @Target { help = "Compile everything" }
  Void compile() { log.info("Compile away!") }

  @Target { help = "Clean it all up" }
  Void clean() { log.info("Clean away!") }
}

The script above defines two targets: compile and clean. The targets are annotated with the @Target facet. Set the help field to a short summary. To print the usage of this script use "-?":

C:\dev\fan\src>buildtest -?
usage:
  build [options] <target>*
options:
  -? -help       Print usage summary
  -v             Verbose debug logging
  -dumpEnv       Debug dump of script env
targets:
  compile*       Compile everything
  clean          Clean it all up

Note that the "compile" target is marked with an asterisk indicating it is the default target because it is the first one declared. If we invoke the script with no arguments the "compile" target is executed:

C:\dev\fan\src>buildtest
Compile away!
BUILD SUCCESS [3ms]!

Or we can pass one or more targets as arguments:

C:\dev\fan\src>buildtest clean
Clean away!
BUILD SUCCESS [2ms]!

C:\dev\fan\src>buildtest clean compile
Clean away!
Compile away!
BUILD SUCCESS [2ms]!

Note: see Setup to configure your environment to run scripts using just the script filename.

BuildScript

The BuildScript is the base class for all build scripts. It provides many useful slots you will find handy:

Lifecycle

A build script lifecycle is composed of these steps:

  1. Base class constructor is run and will immediately setup the environment variables such as log and scriptFile.
  2. Your subclass constructor is run, which is where you can configure your own scripts fields.
  3. The targets callback is invoked to get the list of targets - most often you will let the default implementation build this list by searching for methods which implement the @Target facet.
  4. Command line parsed to find all the specified targets, if no targets specified then we use the first target from the targets method..
  5. Each target method is invoked in the order specified
  6. If an exception is raised, the script fails and returns -1, otherwise 0 is returned.

Problems during the script should be reported via the BuildScript.log. If an error is encountered which should terminate the script, then throw a FatalBuildErr via this pattern:

throw fatal("I just can't go on!")

BuildPod

BuildPod is the base class for build scripts which build a Fantom pod. The BuildPod script defines a bunch of fields to be filled in. Plus it predefines several targets ready to use:

By convention pod source directories are organized as follows:

foo/
  build.fan
  fan/
    ClassAlpha.fan
    ClassBeta.fan
  java/
    ClassAlphaPeer.java
  js/
    ClassAlphaPeer.js
  dotnet/
    ClassAlphaPeer.cs
  test/
    TestClassAlpha.fan
    TestClassBeta.fan
  res/
    icon.png
    resource-file.txt

If you don't have tests or native code, then those directories aren't included. The build script for our directory structure above would look like:

// build.fan
using build
class Build : BuildPod
{
  new make()
  {
    podName    = "foo"
    summary    = "Description of foo pod.  Foo is good."
    depends    = ["sys 1.0+", "bar 1.1+"]
    srcDirs    = [`fan/`, `test/`]
    javaDirs   = [`java/`]
    jsDirs     = [`js/`]
    dotnetDirs = [`dotnet/`]
    resDirs    = [`res/`]
  }
}

Note that all the directories are specified as Uris relative to the script directory. If you don't have native code, you can omit the javaDirs, dotnetDirs, and jsDirs settings. If you don't have resource files you can omit resDirs. See HelloWorld for a simpler example.

Typically the pod's version is set by the build script's version field. By default this is set to the config property buildVersion configured in "etc/build/config.props".