Author: djspiewak
Date: Wed Jul 15 06:52:12 2009
New Revision: 794154
URL: http://svn.apache.org/viewvc?rev=794154&view=rev
Log:
Added quick start guide
Removed:
buildr/trunk/doc/quickstart.textile
Modified:
buildr/trunk/doc/quick_start.textile
Modified: buildr/trunk/doc/quick_start.textile
URL:
http://svn.apache.org/viewvc/buildr/trunk/doc/quick_start.textile?rev=794154&r1=794153&r2=794154&view=diff
==============================================================================
--- buildr/trunk/doc/quick_start.textile (original)
+++ buildr/trunk/doc/quick_start.textile Wed Jul 15 06:52:12 2009
@@ -3,4 +3,294 @@
title: Quick Start
---
+This guide is meant to be a *very* simple introduction to Buildr and its most
basic concepts. However, despite its basic level, we will still manage to
cover most of the concepts you will ever need to be productive with Buildr. We
will leave out some important things (like "sub-projects":projects.html), and
we will over-simplify some other concepts (such as "artifacts":artifacts.html).
Nevertheless, most Buildr projects never need to go beyond the techniques
contained within these pages.
+_No knowledge of Ruby is assumed._ Buildr is designed to be a very intuitive,
very easy-to-use tool. You can create buildfiles which describe incredibly
intricate projects, write custom tasks which do things far beyond Ant, and
still never need to pick up more than a smattering of Ruby syntax. With that
said, if you do know Ruby, Buildr's DSL will seem very natural and welcoming,
but Ruby expertise is by no means a prerequisite. We do however assume that
you have already "downloaded and installed":setup_guide.html Buildr and are
ready to put the tool to good use.
+
+
+(#first-project) h2. Your First Project
+
+Much like Maven, Buildr is oriented around projects and tasks. You define
your project in a concise, declarative fashion and most common tasks (such as
compilation and testing) will be made available to you "at no extra charge".
Most of the project definition is contained within the *buildfile* -- or
*Buildfile*, if you're really in love with the Make convention -- a single file
sitting at the root of your project. A project definition does not need to be
any more complicated than the following:
+
+{% highlight ruby %}
+define 'killer-app'
+{% endhighlight %}
+
+h3. Compilation
+
+Of course, this isn't really giving Buildr much information. What it can't
learn from the buildfile, Buildr will learn by inspecting your directory
structure. Java sources are expected to exist within the @src/main/java/@
directory. If Buildr finds these sources, it will automatically configure the
compilation to source that directory, depositing the compilation results in the
@target/classes/@ directory (all under the project root of course). We can run
this compilation using the following command:
+
+{% highlight sh %}
+$ buildr compile
+{% endhighlight %}
+
+This invocation runs the @compile@ task against the current project. The
current project is determined by inspecting the current working directory for a
*buildfile*. If none is found, then the parent directory will be inspected,
continuing all the way to the filesystem root if necessary. Thus, you can
"@cd@" deeply into the project directory structure and still run Buildr
commands as if you were at the root.
+
+p(tip). By default, Buildr projects assume the Java language and the
@src/main/java/@ source directory. You can also define projects in the Scala
or Groovy language (both languages support joint compilation with Java). To
use Scala, place your @.scala@ files in the @src/main/scala/@ directory and
include the following invocation at the head of your buildfile: @require
'buildr/scala'@ Similarly, Groovy expects sources in the @src/main/groovy/@
directory and necessitates @require 'buildr/groovy'@.
+
+The @compile@ task takes care of all of the messy details of configuring and
running @javac@ against the @src/main/java/@ directory. It will also detect
*any* files which exist under the @src/main/resources/@ directory. These
resources are copied verbatim to the @target/resources/@ directory as part of
the compilation task. Buildr also performs some basic change detection to
minimize compilation. If your source files haven't changed since the last
compile, then they will not be recompiled. This change detection is fairly
conservative, so if any one file changes, the entire project will be
recompiled. In practice, this isn't a problem.
+
+h3. Packaging
+
+At some point, we're going to want to wrap up our classes and produce a single
JAR file for distribution. Buildr can certainly help us with this, but we are
going to need to provide it with a little bit more information. Specifically,
we need to say the type of package to produce (e.g. @:jar@, @:war@, etc) as
well as the current version of our project. This information is placed within
the buildfile:
+
+{% highlight ruby %}
+define 'killer-app' do
+ project.version '0.1.0'
+ package :jar
+end
+{% endhighlight %}
+
+The @project.version@ attribute can be any value you like. Even non-numeric
versions are perfectly acceptable (e.g. @'ikj-0.3.1-E'@). This version,
coupled with the packaging information will be used to generate a JAR file:
@killer-app-0.1.0....@. As would be expected, this file is placed within the
@target/@ directory when the following command is run:
+
+{% highlight sh %}
+$ buildr package
+{% endhighlight %}
+
+The @package@ task depends upon the @compile@ task, so if a rebuild is
necessary prior to the creation of the JAR, Buildr will see to it.
+
+We can also chain tasks together in a single invocation. For example, we may
want to clean all of the old compilation results prior to recompiling and
generating a packaged result:
+
+{% highlight sh %}
+$ buildr clean package
+{% endhighlight %}
+
+The @clean@ task simply removes the @target/@ directory, effectively wiping
out any compilation results like class files or resources.
+
+h3. Directory Structure
+
+As you may have noticed, Buildr does have some default notions about what a
project should look like and how it should be organized. We think that these
defaults are quite nice and make for a more navigable project. However, we do
understand that not all projects are exactly alike. Buildr's
"layouts":extending.html#layouts make it possible for any project to easily
change these defaults. For example, Ant projects conventionally store Java
sources in the @src/@ directory and use the @bin/@ directory for compilation
results:
+
+{% highlight ruby %}
+ant_layout = Layout.new
+ant_layout[:source, :main, :java] = 'src'
+ant_layout[:target, :classes] = 'bin'
+
+define 'killer-app', :layout => ant_layout do
+ project.version '0.1.0'
+ package :jar
+end
+{% endhighlight %}
+
+
+(#dependencies) h2. Dependencies
+
+So far, we have seen how Buildr can automatically infer what amounts to dozens
of lines of @build.xml@ contents, all based on a buildfile and a directory
structure. However, the best is yet to come. Buildr also provides Maven-style
dependency management. In other words, you specify each dependent library
using a string descriptor and Buildr figures out how to download and configure
your classpath to use these libraries. For example, we can configure our
project to reference the "Apache Commons CLI":http://commons.apache.org/cli/
library:
+
+{% highlight ruby %}
+define 'killer-app' do
+ project.version '0.1.0'
+ compile.with 'commons-cli:commons-cli:jar:1.2'
+
+ package :jar
+end
+{% endhighlight %}
+
+If you're familiar with Maven, then this sort of dependency declaration should
look quite familiar. Well, minus the verbose XML trapings. The general format
for an artifact descriptor is *groupId*:*artifactId*:*packageType*:*version*.
The *packageType* bit is almost always @jar@, just as a general rule of thumb.
Any Maven artifacts included in this fashion will be retrieved from the
"primary Maven2 repository":http://repo1.maven.org/maven2 and installed in your
local repository (@~/.m2/repository/@).
+
+If you're not familiar with Maven, this syntax might seem a little bit
imposing. In general, the first field represents the project which produces
the library. For example, the Apache Wicket projects uses the groupId
"@org.apache.wicket@". The second field of the descriptor represents the
specific project. This is necessary as many larger projects -- such as Apache
Wicket -- publish more than one Maven artifact under a single groupId. Thus,
the main Wicket library would have a groupId and artifactId of
"@org.apache.wicket:wicket@", while the Guice integration layer would be
"@org.apache.wicket:wicket-guice@". As for the third field, you can basically
think "@jar@" here and forget about it. You will almost never need to specify
a different artifact package type. The final field is the version number. I
happen to know that the latest version of the Commons CLI library is "1.2", so
that's the version I picked. I could have just as easily chosen an older
version (e.g.
"1.1").
+
+p(tip). You can search the global repository of artifacts at sites like
"MvnRepository":http://mvnrepository.com Simply enter the name of the library
you are looking for, and the search should pull up the groupId, artifactId and
a list of available versions.
+
+Buildr uses the artifact descriptor and its "list of remote
repositories":artifacts.html#repositories to download the relevant JAR file(s)
and cache them locally. Specifically, every downloaded artifact is installed
in your local repository, by default located in the @~/.m2/repository/@
directory (yes, even on Windows). You can explicitly force Buildr to check to
make sure that all of its artifacts are current by running the @artifacts@ task:
+
+{% highlight sh %}
+$ buildr artifacts
+{% endhighlight %}
+
+Alternatively, you can simply wait until the next time you run the @compile@
task, at which point Buildr will automatically check to make sure that all of
its artifacts are current. It will also place the relevant JAR files on the
classpath of the @javac@ compiler, allowing you to use the libraries in your
project. In one line Ruby, Buildr handles everything that Ivy can do in twenty
lines of XML.
+
+Unfortunately, not all libraries are quite as simple as Commons CLI. In
particular, many libraries, such as Apache Wicket, have dependencies of their
own. While we may be able to *compile* against Apache Wicket without these
extra libraries on our classpath, we cannot actually *run* our application
without its transitive dependencies.
+
+{% highlight ruby %}
+define 'killer-app' do
+ project.version '0.1.0'
+ compile.with 'org.apache.wicket:wicket:jar:1.4-rc6'
+
+ package :jar
+end
+{% endhighlight %}
+
+This buildfile directs Buildr to download the Apache Wicket JAR file and
*only* the Apache Wicket JAR. However, Wicket depends upon
"SLF4J":http://www.slf4j.org, which will not be downloaded. There are two ways
of rectifying this situation. We can either track down all of Wicket's
dependencies, their dependencies's dependencies, and so on, adding them all to
our buildfile; or we can simply use the @transitive@ directive:
+
+{% highlight ruby %}
+define 'killer-app' do
+ project.version '0.1.0'
+ compile.with transitive('org.apache.wicket:wicket:jar:1.4-rc6')
+
+ package :jar
+end
+{% endhighlight %}
+
+The @transitive@ directive instructs Buildr to recursively traverse the entire
dependency graph, starting with Apache Wicket and passing through all of its
dependencies until a complete list of artifacts has been compiled. Buildr then
assigns that list to the @compile.with@ property, which downloads each and
every one of these artifacts, installing them into your local repository and
placing them on the classpath for the @compile@ task. This is how Maven
behaves by default.
+
+We're still not quite done though. I happen to know that Apache Wicket does
not specify a particular backend for SLF4J. Thus, despite the fact that we
have installed all of its @transitive@ dependencies, we still must explicitly
notify Buildr about one other artifact. The completed buildfile looks like the
following:
+
+{% highlight ruby %}
+define 'killer-app' do
+ project.version '0.1.0'
+ compile.with transitive('org.apache.wicket:wicket:jar:1.4-rc6'),
+ 'org.slf4j:slf4j-jdk14:jar:1.5.8'
+
+ package :jar
+end
+{% endhighlight %}
+
+The linebreak here is merely for formatting reasons, the comma (@,@) is really
all that is necessary. As you can see, the @compile.with@ property accepts a
full list of artifacts, making it possible to specify any number of
dependencies as necessary. Of course, such a long list of verbose string
descriptors could get very tiresome and messy. For this reason, it is
conventional to assign each of your dependencies to a constant (e.g. @WICKET@)
which is declared just above the project in the buildfile and passed to
@compile.with@ in a clean, easy-to-read style:
+
+{% highlight ruby %}
+WICKET = transitive('org.apache.wicket:wicket:jar:1.4-rc6')
+SLF4J = 'org.slf4j:slf4j-jdk14:jar:1.5.8'
+
+define 'killer-app' do
+ project.version '0.1.0'
+ compile.with WICKET, SLF4J
+
+ package :jar
+end
+{% endhighlight %}
+
+Unfortunate as it may seem, not all libraries are available in Maven
repositories. While most of the major libraries (e.g. Hibernate, Spring, etc)
are kept updated by intrepid volunteers, some of the more obscure frameworks
are left out in the cold. An example of one such framework is
"DBPool":http://www.snaq.net/java/DBPool, a very fast connection pooling
framework designed to integrate with JDBC. However, like most Java libraries,
DBPool does allow the download of a zip archive which contains the JAR file, as
well as some documentation and perhaps a license or two.
+
+Almost magically, we can simply instruct Buildr to get the DBPool artifact
from this URL. Buildr will treat this download just like any other artifact,
downloading it as necessary when requried by the @compile@ task. However,
unlike a normal Maven artifact, Buildr will do some extra processing once the
download is complete. It will actually dig into the downloaded archive, detect
and extract the JAR file, installing it into the local repository just like any
other artifact:
+
+{% highlight ruby %}
+DBPOOL = 'net.snaq:dbpool:jar:4.8.3'
+download artifact(DBPOOL) =>
'http://www.snaq.net/java/DBPool/DBPool_v4.8.3.zip'
+
+define 'killer-app' do
+ project.version '0.1.0'
+ compile.with DBPool
+
+ package :jar
+end
+{% endhighlight %}
+
+This is one area where Buildr's dependency management vastly excedes Maven's.
With Maven, you would have to install the DBPool dependency manually. Buildr's
auto-magical download and extraction keeps the dependency definitions
centralized within the buildfile, available to your entire team and
automatically resolved as needed by the compilation tasks.
+
+
+(#tasks) h2. Testing
+
+Buildr supports auto-magical integration with a number of mainstream testing
frameworks. For Java, this includes the ubiquitus JUnit4, as well as TestNG
and a number of others. Scala supports Specs and ScalaTest, while Groovy
supports EasyB. Configuration is as simple as placing our test sources in the
appropriate directory. In the case of JUnit4, this would be @src/test/java/@.
Once these tests are in place, we can run them using the @test@ task:
+
+{% highlight sh %}
+$ buildr test
+{% endhighlight %}
+
+Actually, we can also use the @build@ task to accomplish this same goal. The
@build@ task depends upon @test@, which in turn depends upon @comp...@. We can
even save a few keystrokes by exploiting the fact that @build@ is the default
task, chosen when no other task is specified:
+
+{% highlight sh %}
+$ buildr
+{% endhighlight %}
+
+When the @test@ task runs, it will ensure that your main sources are compiled,
as well as the tests themselves. In the case of JUnit4, test classes are
auto-detected based on which interface they extend (@TestCase@). These tests
will be invoked using the special test classpath. This classpath includes all
of the dependencies passed to @compile.with@ along with anything required for
testing. Thus, Buildr will actually go out and download JUnit 4.5 (if
necessary) and place that JAR on the classpath in order to run your tests. It
is also possible to add artifacts specifically required for testing. So, if
your tests make use of the Commons Collections library, but your main sources
do not, you can include that dependency only for the tests by using the
@test.with@ property. This functions identically to @compile.with@:
+
+{% highlight ruby %}
+define 'killer-app' do
+ project.version '0.1.0'
+
+ compile.with 'commons-cli:commons-cli:jar:1.2'
+ test.with 'commons-collections:commons-collections:jar:3.2'
+
+ package :jar
+end
+{% endhighlight %}
+
+Of course, not everyone *likes* JUnit4. As mentioned previously, Buildr
supports a number of test frameworks. It is possible to use TestNG instead of
JUnit4 by setting the @test.using@ property to @:testng@:
+
+{% highlight ruby %}
+define 'killer-app' do
+ project.version '0.1.0'
+
+ test.using :testng
+
+ package :jar
+end
+{% endhighlight %}
+
+Once again, tests are assumed to be in the @src/test/java/@ directory.
However, this time, the auto-detection is based on whether or not a class has
the @\...@test@ annotation.
+
+Note that only one test framework per-project may be used. This may seem like
an obvious restriction given that both frameworks introduced so far have used
the same directory, but other frameworks such as Specs and EasyB do not follow
the same convention. In cases of ambiguity (for example, when tests are
present in both @src/test/java/@ *and* @src/spec/scala/@), only one test
framework will be chosen, but this choice is not well-defined. When in doubt,
explicitly specify the test framework with the @test.using@ property. This
overrides Buildr's auto-detection and ensures sane behavior.
+
+Other test frameworks are documented "here":testing.html and
"here":languages.html.
+
+
+(#custom-tasks) h2. Custom Tasks
+
+p(note). Ok, I lied: this section assumes a *little* Ruby knowledge. However,
it's nothing that you couldn't pick up after spending 15 minutes with the core
library documentation.
+
+If there is one area in which Buildr excels, it is defining custom tasks.
This is something which is notoriously difficult in both Ant and Maven, often
requiring separate Java plugins and mountains of code simply to perform basic
tasks. For example, let's imagine that we wanted to define a @run@ task which
would compile and run our "killer-app" project. This is a simple matter of
invoking the @java@ command against our main class:
+
+{% highlight ruby %}
+define 'killer-app' do
+ project.version '0.1.0'
+
+ package :jar
+
+ task :run => :compile do
+ system 'java -cp target/classes org.apache.killer.Main'
+ end
+end
+{% endhighlight %}
+
+This code defines a new task, @run@, which depends upon the @compile@ task.
This task only performs a single operation: it invokes the @system@ method,
passing the relevant command as a string. Note that the @system@ method
documentation may be found
"here":http://www.ruby-doc.org/core/classes/Kernel.html#M005971. Tasks use
real Ruby (actually, the entire buildfile is real Ruby too), so if you are
familiar with that language, then you should be right at home writing custom
tasks in Buildr. We can invoke this task in the following way:
+
+{% highlight sh %}
+$ buildr killer-app:run
+{% endhighlight %}
+
+This works, but it's clumsy. The reason we had to give the "@killer-app:@"
prefix is because we defined the @run@ task *within* our project, rather than
outside of the @define@ block. However, if we define @run@ outside of the
project, then we don't really have access to the @compile@ task (which is
project-specific). The solution here is a bit of magic known as @local_t...@.
This is how tasks like @compile@ and @test@, which are technically
project-specific (think: instance methods) can be invoked without the
fully-qualified project name:
+
+{% highlight ruby %}
+local_task :run
+
+define 'killer-app' do
+ project.version '0.1.0'
+
+ package :jar
+
+ task :run => :compile do
+ system 'java -cp target/classes org.apache.killer.Main'
+ end
+end
+{% endhighlight %}
+
+Now, we can invoke @run@ exactly the way we want, with a minimum of wasted
characters:
+
+{% highlight sh %}
+$ buildr run
+{% endhighlight %}
+
+Let's make things just a little bit harder. Suppose that our project has some
dependencies. Now, these dependencies have to be included on the classpath.
On top of that, you may have noticed that the command we are passing to
@system@ includes a path (@target/classes@) which is relative to the current
working directory. Buildr doesn't introspect our strings, looking for paths,
so we have accidentally created a @run@ task which *only* works when our
current working directory is equivalent to the project root.
+
+In order to get around these dillemas, we need to use some of Buildr's API to
construct an array of paths which must be added to the Java classpath. We will
then join each of these array elements together using the system-specific path
separator ('@:@' on Mac and Unix, '@;@' on Windows) and then use this string as
the value of the @-cp@ switch on the @java@ command. This sounds very
intimidating, but it's actually not all that complicated. If you're familiar
with Ruby and its collection APIs, then this should be second nature for you.
If you're not familiar with Ruby, then just treat this as magic pixie dust and
trust that it works:
+
+{% highlight ruby %}
+WICKET = transitive('org.apache.wicket:wicket:jar:1.4-rc6')
+SLF4J = 'org.slf4j:slf4j-jdk14:jar:1.5.8'
+
+local_task :run
+
+define 'killer-app' do
+ project.version '0.1.0'
+ compile.with WICKET, SLF4J
+
+ package :jar
+
+ task :run => :compile do
+ cp = [compile.target] + compile.dependencies
+ cp_str = cp.map(&:to_s).join File::PATH_SEPARATOR
+
+ system "java -cp '#{cp_str}' org.apache.killer.Main"
+ end
+end
+{% endhighlight %}
+
+There are two bits of non-standard Ruby within this task: @compile.target@ and
@compile.dependenc...@. These are methods which are part of the Buildr API.
The former (@compile.target@) returns a string representing the canonical path
to your project's @target/classes/@ directory. This method works regardless of
what the current working directory is, and regardless of what @:layout@ the
project may be using. The latter method returns a Ruby array representing all
of the dependencies associated with the @compile@ task. Technically, this
array actually contains instances of class @ArtifactTask@, which is why we must
map over this array, invoking the @to_s@ method on each element. This
invocation transforms the artifacts into canonical paths, each pointing to a
different JAR file. The @join@ method takes that array and combines all of the
elements into a single string, using @File::PATH_SEPARATOR@ to separate the
elements.
+
+The result is stored in @cp_str@, which we include into our @system@ command
by using Ruby's @#{...}@ string syntax. This string represents the *complete*
@compile@ classpath for our project, including all transitive and immediate
dependencies, as well as the compile output directory. In short, this is
exactly what we need to pass to the @java@ command in order to ensure that our
application runs correctly. Imagine doing that with Maven!
+
+
+(#summary) h2. Summary
+
+As long as this guide was, we have barely even scratched the surface of
Buildr's true power. This was meant only to get you up and running as quickly
as possible, exploiting some of Buildr's unique features to ease your build
process. For more comprehensive documentation, start reading about "projects
in Buildr":projects.html and work your way from there.