h1. Packaging

For our next trick, we're going to try and create an artifact ourselves.  We're
going to start with:

{{{!ruby
package :jar
}}}

We just told the project to create a JAR file in the @target@ directory,
including all the classes (and resources) that we previously compiled into
@target/classes@.  Or we can create a WAR file:

{{{!ruby
package :war
}}}

The easy case is always easy, but sometimes we have more complicated use cases
which we'll address through the rest of this section.

Now let's run the build, test cases and create these packages:

{{{!sh
$ buildr package
}}}

The @package@ task runs the @build@ task (remember: @compile@ and @test@) and
then runs each of the packaging tasks, creating packages in the projects'
target directories.

p(tip). The @package@ task and @package@ methods are related, but that relation is
different from other task/method pairs.  The @package@ method creates a file
task that points to the package in the @target@ directory and knows how to
create it.  It then adds itself as a prerequisite to the @package@ task.
Translation: you can create multiple packages from the same project.


h2. Specifying And Referencing Packages

Buildr supports several packaging types, and so when dealing with packages, you
have to indicate the desired package type.  The packaging type can be the first
argument, or the value of the @:type@ argument.  The following two are
equivalent:

{{{!ruby
package :jar
package :type=>:jar
}}}

If you do not specify a package type, Buildr will attempt to infer one.

In the documentation you will find a number of tasks dealing with specific
packaging types (@ZipTask@, @JarTask@, etc).  The @package@ method is a
convenience mechanism that sets up the package for you associates it with
various project life cycle tasks.

To package a particular file, use the @:file@ argument, for example:

{{{!ruby
package :zip, :file=>_('target/interesting.zip')
}}}

This returns a file task that will run as part of the project's @package@ task
(generating all packages).  It will invoke the @build@ task to generate any
necessary prerequisites, before creating the specified file.

The package type does not have to be the same as the file name extension, but
if you don't specify the package type, it will be inferred from the extension.

Most often you will want to use the second form to generate packages that are
also artifacts.  These packages have an artifact specification, which you can
use to reference them from other projects (and buildfiles).  They are also
easier to share across projects: artifacts install themselves in the local
repository when running the @install@ task, and upload to the remote repository
when running the @upload@ task (see "Installing and
Uploading":#installing_and_uploading).

The artifact specification is based on the project name (using dashes instead
of colons), group identifier and version number, all three obtained from the
project definition.  You can specify different values using the @:id@,
@:group@, @:version@ and @:classifier@ arguments.  For example:

{{{!ruby
define 'killer-app', :version=>'1.0' do
  # Generates silly-1.0.jar
  package :jar, :id=>'silly'

  # Generates killer-app-la-web-1.x.war
  project 'la-web' do
    package :war, :version=>'1.x'
  end

  # Generates killer-app-the-api-1.0-sources.zip
  project 'teh-api' do
    package :zip, :classifier=>'sources'
  end
end
}}}

The file name is determined from the identifier, version number, classifier and
extension associated with that packaging type.

If you do not specify the packaging type, Buildr attempt to infer it from the
project definition.  In the general case it will use the default packaging
type, ZIP.  A project that compiles Java classes will default to JAR packaging;
for other languages, consult the specific documentation.

A single project can create multiple packages.  For example, a Java project may
generate a JAR package for the runtime library and another JAR containing just
the API; a ZIP file for the source code and another ZIP for the documentation.
Make sure to always call @package@ with enough information to identify the
specific package you are referencing.  Even if the project only defines a
single package, calling the @package@ method with no arguments does not
necessarily refer to that one.

You can use the @packages@ method to obtain a list of all packages defined in
the project, for example:

{{{!ruby
project('killer-app:teh-impl').packages.first
project('killer-app:teh-impl').packages.select { |pkg| pkg.type == :zip }
}}}


h2. Packaging ZIPs

ZIP is the most common form of packaging, used by default when no other
packaging type applies.  It also forms the basis for many other packaging types
(e.g. JAR and WAR).  Most of what you'll find here applies to other packaging
types.

Let's start by including additional files in the ZIP package.  We're going to
include the @target/docs@ directory and @README@ file:

{{{!ruby
package(:zip).include _('target/docs'), 'README'
}}}

The @include@ method accepts files, directories and file tasks.  You can also
use file pattern to match multiple files and directories.  File patterns
include asterisk (@*@) to match any file name or part of a file name, double
asterisk (@**@) to match directories recursively, question mark (@?@) to match
any character, square braces (@[]@) to match a set of characters, and curly
braces (@{}@) to match one of several names.

And the same way you @include@, you can also @exclude@ specific files you don't
want showing up in the ZIP.  For example, to exclude @.draft@ and @.raw@ files:

{{{!ruby
package(:zip).include('target/docs').
  exclude('target/docs/**/*.{draft,raw}')
}}}

So far we've included files under the root of the ZIP.  Let's include some
files under a given path using the @:path@ option:

{{{!ruby
package(:zip).include 'target/docs', :path=>"#{id}-#{version}"
}}}

If you need to use the @:path@ option repeatedly, consider using the @tap@
method instead.  For example:

{{{!ruby
package(:zip).path("#{id}-#{version}").tap do |path|
  path.include 'target/docs'
  path.include 'README'
end
}}}

p(tip). The @tap@ method is not part of the core library, but a very useful
extension.  It takes an object, yields to the block with that object, and then
returns that object.

p(note). To allow you to spread files across different paths, the
include/exclude patterns are specific to a path.  So in the above example, if
you want to exclude some files from the "target/docs" directory, make sure to
call @exclude@ on the path, not on the ZIP task itself.

If you need to include a file or directory under a different name, use the
@:as@ option.  For example:

{{{!ruby
package(:zip).include('corporate-logo-350x240.png', :as=>'logo.png')
}}}

You can also use @:as=>'.'@ to include all files from the given directory.  For
example:

{{{!ruby
package(:zip).include 'target/docs/*'
package(:zip).include 'target/docs', :as=>'.'
}}}

These two are almost identical.  They both include all the files from the
@target/docs@ directory, but not the directory itself.  But they operate
differently.  The first line expands to include all the files in @target/docs@.
If you don't already have files in @target/docs@, well, then it won't do
anything interesting.  Your ZIP will come up empty.  The second file includes
the directory itself, but strips the path during inclusion.  You can define it
now, create these files later, and then ZIP them all up.

For example, when @package :jar@ decides to include all the files from
@target/classes@, it's still working on the project definition, and has yet to
compile anything.  Since @target/classes@ may be empty, may not even exist, it
uses @:as=>'.'@.

If you need to get rid of all the included files, call the @clean@ method.
Some packaging types default to adding various files and directories, for
example, JAR packaging will include all the compiled classes and resources.

You can also merge two ZIP files together, expanding the content of one ZIP
into the other.  For example:

{{{!ruby
package(:zip).merge 'part1.zip', 'part2.zip'
}}}

If you need to be more selective, you can apply the include/exclude pattern to
the expanded ZIP.  For example:

{{{!ruby
# Everything but the libs
package(:zip).merge('bigbad.war').exclude('libs/**/*')
}}}


h2. Packaging JARs

JAR packages extend ZIP packages with support for Manifest files and the
META-INF directory.  They also default to include the class files found in the
@target/classes@ directory.

You can tell the JAR package to include a particular Manifest file:

{{{!ruby
package(:jar).with :manifest=>_('src/main/MANIFEST.MF')
}}}

Or generate a manifest from a hash:

{{{!ruby
package(:jar).with :manifest=>{ 'Copyright'=>'Acme Inc (C) 2007' }
}}}

You can also generate a JAR with no manifest with the value @false@, create a
manifest with several sections using an array of hashes, or create it from a
proc.

In large projects, where all the packages use the same manifest, it's easier to
set it once on the top project using the @manifest@ project property.
Sub-projects inherit the property from their parents, and the @package@ method
uses that property if you don't override it, as we do above.

For example, we can get the same result by specifying this at the top project:

{{{!ruby
manifest['Copyright'] = 'Acme Inc (C) 2007'
}}}

If you need to mix-in the project's manifest with values that only one package
uses, you can do so easily:

{{{!ruby
package(:jar).with :manifest=>manifest.merge('Main-Class'=>'com.acme.Main')
}}}

If you need to include more files in the @META-INF@ directory, you can use the
@:meta_inf@ option.  You can give it a file, or array of files.  And yes, there
is a @meta_inf@ project property you can set once to include the same set of
file in all the JARs.  It works like this:

{{{!ruby
meta_inf << file('DISCLAIMER') << file('NOTICE')
}}}

If you have a @LICENSE@ file, it's already included in the @meta_inf@ list of
files.

Other than that, @package :jar@ includes the contents of the compiler's target
directory and resources, which most often is exactly what you intend it to do.
If you want to include other files in the JAR, instead or in addition, you can
do so using the @include@ and @exclude@ methods.  If you do not want the target
directory included in your JAR, simply call the @clean@ method on it:

{{{!ruby
package(:jar).clean.include( only_these_files )
}}}


h2. Packaging WARs

Pretty much everything you know about JARs works the same way for WARs, so
let's just look at the differences.

Without much prompting, @package :war@ picks the contents of the
@src/main/webapp@ directory and places it at the root of the WAR, copies the
compiler target directory into the @WEB-INF/classes@ path, and copies any
compiled dependencies into the @WEB-INF/libs@ paths.

Again, you can use the @include@ and @exclude@ methods to change the contents
of the WAR.  There are two convenience options you can use to make the more
common changes.  If you need to include a classes directory other than the
default:

{{{!ruby
package(:war).with :classes=>_('target/additional')
}}}

If you want to include a different set of libraries other than the default:

{{{!ruby
package(:war).with :libs=>MYSQL_JDBC
}}}

Both options accept a single value or an array.  The @:classes@ option accepts
the name of a directory containing class files, initially set to
@compile.target@ and @resources.target@.  The @:libs@ option accepts artifact
specifications, file names and tasks, initially set to include everything in
@compile.dependencies@.

As you can guess, the package task has two attributes called @classes@ and
@libs@; the @with@ method merely sets their value.  If you need more precise
control over these arrays, you can always work with them directly, for example:

{{{!ruby
# Add an artifact to the existing set:
package(:war).libs += artifacts(MYSQL_JDBC)
# Remove an artifact from the existing set:
package(:war).libs -= artifacts(LOG4J)
# List all the artifacts:
puts 'Artifacts included in WAR package:'
puts package(:war).libs.map(&:to_spec)
}}}


h2. Packaging AARs

Axis2 service archives are similar to JAR's (compiled classes go into the root
of the archive) but they can embed additional libraries under /lib and include
@services.xml@ and WSDL files.

{{{!ruby
package(:aar).with(:libs=>'log4j:log4j:jar:1.1')
package(:aar).with(:services_xml=>_('target/services.xml'), :wsdls=>_('target/*.wsdl'))
}}}

The @libs@ attribute is a list of .jar artifacts to be included in the archive
under /lib.  The default is no artifacts; compile dependencies are not included
by default.

The @services_xml@ attribute points to an Axis2 services configuration file
called @services.xml@ that will be placed in the @META-INF@ directory inside
the archive.  The default behavior is to point to the @services.xml@ file in
the project's @src/main/axis2@ directory.  In the second example above we set
it explicitly.

The @wsdls@ attribute is a collection of file names or glob patterns for WSDL
files that get included in the @META-INF@ directory.  In the second example we
include WSDL files from the @target@ directory, presumably created by an
earlier build task.  In addition, AAR packaging will include all files ending
with @.wsdl@ from the @src/main/axis2@ directory.

If you already have WSDL files in the @src/main/axis2@ directory but would like
to perform some filtering, for example, to set the HTTP port number, consider
ignoring the originals and including only the filtered files, for example:

{{{!ruby
# Host name depends on environment.
host = ENV['ENV'] == 'test' ? 'test.host' : 'ws.example.com'
filter.from('src/main/axis2').into('target').
  include('services.xml', '*.wsdl').using('http_port'=>'8080', 'http_host'=>host)
package(:aar).wsdls.clear
package(:aar).with(:services_xml=>_('target/services.xml'), :wsdls=>_('target/*.wsdl'))
}}}


h2.  Packaging EARs

EAR packaging is slightly different from JAR/WAR packaging.  It's main purpose
is to package components together, and so it includes special methods for
handling component inclusion that take care to update application.xml and the
component's classpath.

EAR packages support four component types:

|_. Argument  |_. Component                     |
| @:war@      | J2EE Web Application (WAR).     |
| @:ejb@      | Enterprise Java Bean (JAR).     |
| @:jar@      | J2EE Application Client (JAR).  |
| @:lib@      | Shared library (JAR).           |

This example shows two ways for adding components built by other projects:

{{{!ruby
package(:ear) << project('coolWebService').package(:war)
package(:ear).add project('commonLib') # By default, the JAR package
}}}

Adding a WAR package assumes it's a WAR component and treats it as such, but
JAR packages can be any of three component types, so by default they are all
treated as shared libraries.  If you want to add an EJB or Application Client
component, you need to say so explicitly, either passing @:type=>package@, or by
passing the component type in the @:type@ option.

Here are three examples:

{{{!ruby
# Assumed to be a shared library.
package(:ear).add 'org.springframework:spring:jar:2.6'
# Component type mapped to package.
package(:ear).add :ejb=>project('beanery')
# Adding component with specific package type.
package(:ear).add project('client'), :type=>:jar
}}}

By default, WAR components are all added under the @/war@ path, and likewise,
EJB components are added under the @/ejb@ path, shared libraries under @/lib@
and Application Client components under @/jar@.

If you want to place components in different locations you can do so using the
@:path@ option, or by specifying a different mapping between component types
and their destination directory.  The following two examples are equivalent:

{{{!ruby
# Specify once per component.
package(:ear).add project('coolWebService').package(:war), :path=>'coolServices'
# Configure once and apply to all added components.
package(:ear).dirs[:war] = 'coolServices'
package(:ear) << project('coolWebService').package(:war)
}}}

EAR packages include an @application.xml@ file in the @META-INF@ directory that
describes the application and its components.  This file is created for you
during packaging, by referencing all the components added to the EAR.  There
are a couple of things you will typically want to change.

* *display-name* -- The application's display name defaults to the project's
identifier.  You can change that by setting the @display_name@ attribute.

* *context-root* -- WAR components specify a context root, based on the package
identifier, for example, "cool-web-1.0.war" will have the context root
"cool-web".  To specify a different context root, add the WAR package with the
@context_root@ option.

Again, by example:

{{{!ruby
package(:ear).display_name = 'MyCoolWebService'
package(:ear).add project('coolWebService').package(:war), :context-root=>'coolness'
}}}

If you need to disable the context root (e.g. for Portlets), set @context_root@
to @false@.


h2.  Packaging Tars and GZipped Tars

Everything you know about working with ZIP files translates to Tar files, the
two tasks are identical in more respect, so here we'll just go over the
differences.

{{{!ruby
package(:tar).include _('target/docs'), 'README'
package(:tgz).include _('target/docs'), 'README'
}}}

The first line creates a Tar archive with the extension @.tar@, the second
creates a GZipped Tar archive with the extension @.tgz@.

In addition to packaging that includes the archive in the list of
installed/released files, you can use the method @tar@ to create a @TarTask@.
This task is similar to @ZipTask@, and introduces the @gzip@ attribute, which
you can use to tell it whether to create a regular file, or GZip it.  By
default the attribute it set to true (GZip) if the file name ends with either
@.gz@ or @.tgz@.


h2. Installing and Uploading

You can bring in the artifacts you need from remote repositories and install
them in the local repositories.  Other projects have the same expectation, that
your packages be their artifacts.

So let's create these packages and install them in the local repository where
other projects can access them:

{{{!sh
$ buildr install
}}}

If you changes your mind you can always:

{{{!sh
$ buildr uninstall
}}}

That works between projects you build on the same machine.  Now let's share
these artifacts with other developers through a remote repository:

{{{!sh
$ buildr upload
}}}

Of course, you'll need to tell Buildr about the release server:

{{{!ruby
repositories.release_to = 'sftp://john:secret@release/usr/share/repo'
}}}

This example uses the SFTP protocol.  In addition, you can use the HTTP
protocol -- Buildr supports HTTP and HTTPS, Basic Authentication and uploads
using PUT -- or point to a directory on your file system.

The URL in this example contains the release server ("release"), path to
repository ("user/share/repo") and username/password for access.  The way SFTP
works, you specify the path on the release server, and give the user
permissions to create directories and files inside the repository.  The file
system path is different from the path you use to download these artifacts
through an HTTP server, and starts at the root, not the user's home directory.

Of course, you'll want to specify the release server URL in the Buildfile, but
leave the username/password settings private in your local @buildr.rb@ file.
Let's break up the release server settings:

{{{!ruby
# build.rb, loaded first
repositories.release_to[:username] = 'john'
repositories.release_to[:password] = 'secret'

# Buildfile, loaded next
repositories.release_to[:url] = 'sftp://release/usr/share/repo'
}}}

The @upload@ task takes care of uploading all the packages created by your
project, along with their associated POM files and MD5/SHA1 signatures (Buildr
creates these for you).

If you need to upload other files, you can always extend the @upload@ task and
use @repositories.release_to@ in combination with @URI.upload@.  You can also
extend it to upload to different servers, for example, to publish the
documentation and test coverage reports to your site:

{{{!ruby
# We'll let some other task decide how to create 'docs'
task 'deploy'=>'docs' do
  uri = URI("sftp://#{username}:#{password}@var/www/docs")
  uri.upload file('docs')
end
}}}


h2. Packaging Sources and JavaDocs

IDEs can take advantage of source packages to help you debug and trace through
compiled code.  We'll start with a simple example:

{{{!ruby
package :sources
}}}

This one creates a ZIP package with the classifier "sources" that will contain
all the source directories in that project, typically @src/main/java@, but also
other sources generated from Apt, JavaCC, XMLBeans and friends.

You can also generate a ZIP package with the classifier "javadoc" that contains
the JavaDoc documentation for the project.  It uses the same set of
documentation files generated by the project's @javadoc@ task, so you can use
it in combination with the @javadoc@ method.  For example:

{{{!ruby
package :javadoc
javadoc :windowtitle=>'Buggy but Works'
}}}

By default Buildr picks the project's description for the window title.

You can also tell Buildr to automatically create sources and JavaDoc packages
in all the sub-projects that have any source files to package or document.
Just add either or both of these methods in the top-level project:

{{{!ruby
package_with_sources
package_with_javadoc
}}}

You can also tell it to be selective using the @:only@ and @:except@ options.
For example:

{{{!ruby
package_with_javadoc :except=>'la-web'
}}}

We packaged the code, but will it actually work? Let's see "what the tests
say":testing.html.
