1. Start Here
    1. Welcome
    2. Download
    3. User Guide (PDF)
    4. What’s New
    5. Community Wiki
  2. Using Buildr
    1. Getting Started
    2. Projects
    3. Building
    4. Artifacts
    5. Packaging
    6. Testing
    7. Settings/Profiles
    8. Languages
    9. More Stuff
    10. Extending Buildr
    11. Recipes
  3. Reference
    1. API
    2. Rake
    3. Antwrap
    4. Troubleshooting
  4. Get Involved
    1. Mailing Lists
    2. Issues/Bugs
    3. Contributing
  5. Project Status
    1. License
    2. Changelog
    3. Specs
    4. Coverage
Google Custom Search

More Stuff

  1. Using Gems
  2. Using Java Libraries
  3. Nailgun
  4. Growl, Qube
  5. Eclipse, IDEA
  6. Cobertura, Emma, JDepend
  7. Anything Ruby Can Do

Using Gems

The purpose of the buildfile is to define your projects, and the various tasks and functions used for building them. Some of these are specific to your projects, others are more general in nature, and you may want to share them across projects.

There are several mechanisms for developing extensions and build features across projects which we cover in more details in the section Extending Buildr. Here we will talk about using extensions that are distributed in the form of RubyGems.

RubyGems provides the gem command line tool that you can use to search, install, upgrade, package and distribute gems. It installs all gems into a local repository that is shared across your builds and all other Ruby applications you may have running. You can install a gem from a local file, or download and install it from any number of remote repositories.

RubyGems is preconfigured to use the RubyForge repository. You’ll find a large number of open source Ruby libraries there, including Buildr itself and all its dependencies. RubyForge provides a free account that you can use to host your projects and distribute your gems (you can use RubyForge strictly for distribution, as we do with Buildr).

You can also set up your own private repository and use it instead or in addition to RubyForge. Use the gem sources command to add repositories, and the gem server command to run a remote repository. You can see all available options by running gem help.

If your build depends on other gems, you will want to specify these dependencies as part of your build and check that configuration into source control. That way you can have a specific environment that will guarantee repeatable builds, whether you’re building a particular version, moving between branches, or joining an existing project. Buildr will take care of installing all the necessary dependencies, which you can then manage with the gem command.

Use the build.yaml file to specify these dependencies (see Build Settings for more information), for example:

# This project requires the following gems
gems: 
  # Suppose we want to notify developers when testcases fail.
  - buildr-twitter-notifier-addon >=1
  # we test with ruby mock objects
  - mocha
  - ci_reporter

Gems contain executable code, and for that reason Buildr will not install gems without your permission. When you run a build that includes any dependencies that are not already installed on your machine, Buildr will ask for permission before installing them. On Unix-based operating systems, you will also need sudo privileges and will be asked for your password before proceeding.

Since this step requires your input, it will only happen when running Buildr interactively from the command line. In all other cases, Buildr will fail and report the missing dependencies. If you have an automated build environment, make sure to run the build once manually to install all the necessary dependencies.

When installing a gem for the first time, Buildr will automatically look for the latest available version. You can specify a particular version number, or a set of version numbers known to work with that build. You can use equality operations to specify a range of versions, for example, 1.2.3 to install only version 1.2.3, and => 1.2.3 to install version 1.2.3 or later.

You can also specify a range up to one version bump, for example, ~> 1.2.3 is the same as >= 1.2.3 < 1.3.0, and ~> 1.2 is the same as >= 1.2.0 < 2.0.0. If necessary, you can exclude a particular version number, for example, ~> 1.2.3 != 1.2.7.

Buildr will install the latest version that matches the version requirement. To keep up with newer versions, execute the gem update command periodically. You can also use gem outdated to determine which new versions are available.

Most gems include documentation that you can access in several forms. You can use the ri command line tool to find out more about a class, module or specific method. For example:

$ ri Buildr::Jetty
$ ri Buildr::Jetty.start

You can also access documentation from a Web browser by running gem server and pointing your browser to http://localhost:8808. Note that after installing a new gem, you will need to restart the gem server to see its documentation.

Using Java Libraries

Buildr runs along side a JVM, using either RJB or JRuby. The Java module allows you to access Java classes and create Java objects.

Java classes are accessed as static methods on the Java module, for example:

str = Java.java.lang.String.new('hai!')
str.toUpperCase
=> 'HAI!'
Java.java.lang.String.isInstance(str)
=> true
Java.com.sun.tools.javac.Main.compile(args)

The classpath attribute allows Buildr to add JARs and directories to the classpath, for example, we use it to load Ant and various Ant tasks, code generators, test frameworks, and so forth.

When using an artifact specification, Buildr will automatically download and install the artifact before adding it to the classpath.

For example, Ant is loaded as follows:

Java.classpath << 'org.apache.ant:ant:jar:1.7.0'

Artifacts can only be downloaded after the Buildfile has loaded, giving it a chance to specify which remote repositories to use, so adding to classpath does not by itself load any libraries. You must call Java.load before accessing any Java classes to give Buildr a chance to load the libraries specified in the classpath.

When building an extension, make sure to follow these rules:

  1. Add to the classpath when the extension is loaded (i.e. in module or class definition), so the first call to Java.load anywhere in the code will include the libraries you specify.
  2. Call Java.load once before accessing any Java classes, allowing Buildr to set up the classpath.
  3. Only call Java.load when invoked, otherwise you may end up loading the JVM with a partial classpath, or before all remote repositories are listed.
  4. Check on a clean build with empty local repository.

Nailgun

Nailgun is a client, protocol, and server for running Java programs from the command line without incurring the JVM startup overhead. Nailgun integration is available only when running Buildr within JRuby.

Buildr provides a custom nailgun server, allowing you to start a single JVM and let buildr create a queue of runtimes. These JRuby runtimes can be cached (indexed by buildfile path) and are automatically reloaded when the buildfile has been modified. Runtime caching allows you to execute tasks without spending time creating the buildr environment.

Start the BuildrServer by executing

$ jruby -S buildr -rbuildr/nailgun nailgun:start

Server output will display a message when it becomes ready, you will also see messages when the JRuby runtimes are being created, or when a new buildr environment is being loaded on them. After the runtime queues have been populated, you can start calling buildr as you normally do, by invoking the $NAILGUN_HOME/ng binary:

# on another terminal, change directory to a project.
# if this project is the same nailgun:start was invoked on, it's 
# runtime has been cached, so no loading is performed unless 
# the buildfile has been modified. otherwise the buildfile 
# will be loaded on a previously loaded fresh-buildr runtime
# and it will be cached.
cd /some/buildr/project
ng nailgun:help                      # display nailgun help
ng nailgun:tasks                     # display overview of ng tasks
ng clean compile                # just invoke those two tasks

Some nailgun tasks have been provided to manage the cached runtimes, to get an overview of them execute the nailgun:tasks task.

Be sure to read the nailgun help by executing the nailgun:help task.

Growl, Qube

For OS X users, Buildr supports Growl out of the box to send "completed and “failed” notifications to the user.

For other platforms or if you want to notify the user differently, Buildr offers two extension points:

Here is an example using these extension points to send notifications using Qube:

# Send notifications using Qube 
notify = lambda do |type, title, message|
  param = case type
    when 'completed'; '-i'
    when 'failed'; '-e'
    else '-i'
  end
  system "qube #{param} #{title.inspect} #{message.inspect}"
end
  
Buildr.application.on_completion do |title, message| 
  notify['completed', title, message]
end
Buildr.application.on_failure do |title, message, ex| 
  notify['failed', title, message] 
end

You can place this code inside buildr.rb in your home directory.

Eclipse, IDEA

If you’re using Eclipse, you can generate .classpath and .project from your Buildfile and use them to create a project in your workspace:

$ buildr eclipse

The eclipse task will generate a .classpath and .project file for each of projects (and sub-project) that compiles source code. It will not generate files for other projects, for examples, projects you use strictly for packaging a distribution, or creating command line scripts, etc.

If you add a new project, change the dependencies, or make any other change to your Buildfile, just run the eclipse task again to re-generate the Eclipse project files.

If you prefer IntelliJ IDEA, you can always:

$ buildr idea

It will generate a .iml file for every project (or subproject) and a .ipr that you can directly open for the root project. To allow IntelliJ Idea to resolve external dependencies properly, you will need to add a M2_REPO variable pointing to your Maven2 repository directory (Settings / Path Variables).

If you’re using IDEA 7 or later, use the buildr idea7x task instead. This task creates the proper .ipr and .iml files for IDEA version 7. It includes the -7x suffix in the generated files, so you can use the idea and idea7x tasks side by side on the same project.

Also, check out the Buildr plugin for IDEA (IDEA 7 and later). Once installed, open your project with IDEA. If IDEA finds that you have Buildr installed and finds a buildfile in the project’s directory, it will show all the tasks available for that project. To run a task, double-click it. When the task completes, IDEA will show the results in the Buildr Output window.

Cobertura, Emma, JDepend

You can use Cobertura or Emma to instrument your code, run the tests and create a test coverage report in either HTML or XML format.

There are two tasks for each tool, both of which generate a test coverage report in the reports/cobertura (respectively reports/emma) directory. For example:

$ buildr test cobertura:html

As you can guess, the other tasks are cobertura:xml, emma:html and emma:xml.

If you want to generate a test coverage report only for a specific project, you can do so by using the project name as prefix to the tasks.

$ buildr subModule:cobertura:html

Each project can specify which classes to include or exclude from cobertura instrumentation by giving a class-name regexp to the cobertura.include or cobertura.exclude methods:

define 'someModule' do 
  cobertura.include 'some.package.*'
  cobertura.include /some.(foo|bar).*/
  cobertura.exclude 'some.foo.util.SimpleUtil'
  cobertura.exclude /*.Const(ants)?/i
end

Emma has include and exclude methods too, but they take glob patterns instead of regexps.

You can use JDepend on to generate design quality metrics. There are three tasks this time, the eye candy one:

$ buildr jdepend:swing

The other two tasks are jdepend:text and jdepend:xml.

We want Buildr to load fast, and not everyone cares for these tasks, so we don’t include them by default. If you want to use one of them, you need to require it explicitly. The proper way to do it in Ruby:

require 'buildr/cobertura'
require 'buildr/emma'
require 'buildr/jdepend'

You may want to add those to the Buildfile. Alternatively, you can use these tasks for all your projects without modifying the Buildfile. One convenient method is to add these lines to the buildr.rb file in your home directory.

Another option is to require it from the command line (--require or -r), for example:

$ buildr --require buildr/jdepend jdepend:swing
$ buildr -rbuildr/cobertura cobertura:html

Anything Ruby Can Do

Buildr is Ruby code. That’s an implementation detail for some, but a useful features for others. You can use Ruby to keep your build scripts simple and DRY, tackle ad hoc tasks and write reusable features without the complexity of “plugins”.

We already showed you one example where Ruby could help. You can use Ruby to manage dependency by setting constants and reusing them, grouping related dependencies into arrays and structures.

You can use Ruby to perform ad hoc tasks. For example, Buildr doesn’t have any pre-canned task for setting file permissions. But Ruby has a method for that, so it’s just a matter of writing a task:

bins = file('target/bin'=>FileList[_('src/main/dist/bin/*')]) do |task|
  mkpath task.name
  cp task.prerequisites, task.name
  chmod 0755, FileList[task.name + '/*.sh'], :verbose=>false
end

You can use functions to keep your code simple. For example, in the ODE project we create two binary distributions, both of which contain a common set of files, and one additional file unique to each distribution. We use a method to define the common distribution:

def distro(project, id)
  project.package(:zip, :id=>id).path("#{id}-#{version}").tap do |zip|
    zip.include meta_inf + ['RELEASE_NOTES', 'README'].map { |f| path_to(f) }
    zip.path('examples').include project.path_to(:source, :examples), :as=>'.'
    zip.merge project('ode:tools-bin').package(:zip)
    zip.path('lib').include artifacts(COMMONS.logging, COMMONS.codec,
      COMMONS.httpclient, COMMONS.pool, COMMONS.collections, JAXEN, SAXON,
      LOG4J, WSDL4J, XALAN, XERCES)
    project('ode').projects('utils', 'tools', 'bpel-compiler', 'bpel-api',
      'bpel-obj', 'bpel-schemas').map(&:packages).flatten.each do |pkg|
        zip.include(pkg.to_s, :as=>"#{pkg.id}.#{pkg.type}", :path=>'lib')
      end
    yield zip
  end
end

And then use it in the project definition:

define 'distro-axis2' do
  parent.distro(self, "#{parent.id}-war") { |zip|
    zip.include project('ode:axis2-war').package(:war), :as=>'ode.war' }
end

Ruby’s functional style and blocks make some task extremely easy. For example, let’s say we wanted to count how many source files we have, and total number of lines:

sources = projects.map { |prj| prj.compile.sources.
  map { |src| FileList["#{src}/**/*.java"] } }.flatten
puts "There are #{source.size} source files"
lines = sources.inject(0) { |lines, src| lines += File.readlines(src).size }
puts "That contain #{lines} lines"