You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by as...@apache.org on 2008/01/08 10:01:11 UTC

svn commit: r609910 [2/2] - in /incubator/buildr/trunk: doc/pages/artifacts.textile doc/pages/building.textile doc/pages/packaging.textile doc/pages/projects.textile doc/pages/testing.textile lib/java/packaging.rb

Modified: incubator/buildr/trunk/doc/pages/projects.textile
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/doc/pages/projects.textile?rev=609910&r1=609909&r2=609910&view=diff
==============================================================================
--- incubator/buildr/trunk/doc/pages/projects.textile (original)
+++ incubator/buildr/trunk/doc/pages/projects.textile Tue Jan  8 01:01:03 2008
@@ -2,50 +2,56 @@
 
 h2. Starting Out
 
-In Java, most projects are built the same way: compile source code, run test cases, package the code, release it.  Rinse, repeat.
+In Java, most projects are built the same way: compile source code, run test
+cases, package the code, release it.  Rinse, repeat.
 
-Feed it a project definition, and Buildr will set up all these tasks for you.  The only thing you need to do is specify the parts that are specific to your project, like the classpath dependencies, whether you're packaging a JAR or a WAR, etc.
-
-The reminder of this guide deals with what it takes to build a project.  But first, let's pick up a sample project to work with.  We'll call it _killer-app_:
+Feed it a project definition, and Buildr will set up all these tasks for you.
+The only thing you need to do is specify the parts that are specific to your
+project, like the classpath dependencies, whether you're packaging a JAR or a
+WAR, etc.
+
+The reminder of this guide deals with what it takes to build a project.  But
+first, let's pick up a sample project to work with.  We'll call it
+_killer-app_:
 
 {{{!ruby,lines
-require "buildr"
+require 'buildr'
  
-VERSION_NUMBER = "1.0"
+VERSION_NUMBER = '1.0'
  
-AXIS2 = "org.apache.axis2:axis2:jar:1.2"
-AXIOM  = group("axiom-api", "axiom-impl", "axiom-dom",
-  :under=>"org.apache.ws.commons.axiom", :version=>"1.2.4")
+AXIS2 = 'org.apache.axis2:axis2:jar:1.2'
+AXIOM  = group('axiom-api', 'axiom-impl', 'axiom-dom',
+  :under=>'org.apache.ws.commons.axiom', :version=>'1.2.4')
 AXIS_OF_WS = [AXIOM, AXIS2]
-OPENJPA = ["org.apache.openjpa:openjpa-all:jar:0.9.7",
-  "net.sourceforge.serp:serp:jar:1.12.0"]
+OPENJPA = ['org.apache.openjpa:openjpa-all:jar:0.9.7',
+  'net.sourceforge.serp:serp:jar:1.12.0']
  
-repositories.remote << "http://www.ibiblio.org/maven2/"
+repositories.remote << 'http://www.ibiblio.org/maven2/'
  
-desc "Code. Build. ??? Profit!"
-define "killer-app" do
+desc 'Code. Build. ??? Profit!'
+define 'killer-app' do
  
   project.version = VERSION_NUMBER
-  project.group = "acme"
-  manifest["Copyright"] = "Acme Inc (C) 2007"
-  compile.options.target = "1.5"
+  project.group = 'acme'
+  manifest['Copyright'] = 'Acme Inc (C) 2007'
+  compile.options.target = '1.5'
 
-  desc "Abstract classes and interfaces"
-  define "teh-api" do
+  desc 'Abstract classes and interfaces'
+  define 'teh-api' do
     package :jar
   end
 
-  desc "All those implementation details"
-  define "teh-impl" do
+  desc 'All those implementation details'
+  define 'teh-impl' do
     compile.with AXIS_OF_WS, OPENJPA
     compile { open_jpa_enhance }
     package :jar
   end
 
-  desc "What our users see"
-  define "la-web" do
+  desc 'What our users see'
+  define 'la-web' do
     test.using AXIS_OF_WS
-    package(:war).with :libs=>projects("teh-api", "teh-impl")
+    package(:war).with :libs=>projects('teh-api', 'teh-impl')
   end
 
   javadoc projects
@@ -54,20 +60,46 @@
 end
 }}}
 
-A project definition requires four pieces of information: the project name, group identifier, version number and base directory.  The project name ... do we need to explain why its necessary?  The group identifier and version number are used for packaging and deployment, we'll talk more about that in the "Packaging":packaging.html section.  The base directory lets you find files inside the project.
-
-Everything else depends on what that particular project is building.  And it all goes inside the project definition block, the piece of code that comes between @define <name> ..  do@ and @end@.
+A project definition requires four pieces of information: the project name,
+group identifier, version number and base directory.  The project name ... do
+we need to explain why its necessary?  The group identifier and version number
+are used for packaging and deployment, we'll talk more about that in the
+"Packaging":packaging.html section.  The base directory lets you find files
+inside the project.
+
+Everything else depends on what that particular project is building.  And it
+all goes inside the project definition block, the piece of code that comes
+between @define <name> ..  do@ and @end@.
 
 
 h2. The Directory Structure
 
-Buildr follows a convention we picked from years of working with Apache projects.
-
-Each project is laid out so the source files are in the @src/main/java@ directory and compile into the @target/classes@ directory.  Resource files go in the @src/main/resources@ directory, also copied over to @target/classes@.  Likewise, unit tests come from @src/test/java@ and @src/test/resources@, and end life in @target/test-classes@.
+Buildr follows a convention we picked from years of working with Apache
+projects.
 
-WAR packages pick up additional files from the aptly named @src/main/webapp@.  And most stuff, including generated source files are parked under the @target@ directory.  Test cases and such may generate reports in the, you guessed it, @reports@ directory.
-
-When projects grow big, you split them own into smaller projects by nesting projects inside each other.  Each sub-project has a sub-directory under the parent project and follows the same internal directory structure.  You can, of course, change all of that to suite your needs, but if you follow these conventions, Buildr will figure all the paths for you.
+Java projects are laid out so the source files are in the @src/main/java@
+directory and compile into the @target/classes@ directory.  Resource files go
+in the @src/main/resources@ directory, also copied over to @target/classes@.
+Likewise, unit tests come from @src/test/java@ and @src/test/resources@, and
+end life in @target/test/classes@.
+
+WAR packages pick up additional files from the aptly named @src/main/webapp@.
+And most stuff, including generated source files are parked under the @target@
+directory.  Test cases and such may generate reports in the, you guessed it,
+@reports@ directory.
+
+Other languages will use different directories, but following the same general
+conventions.  For example, Scala code compiles from the @src/main/scala@
+directory, RSpec tests are found in the @src/test/rspec@ directory, and Flash
+will compile to @target/flash@.  Throughout this document we will show examples
+using mostly Java, but you can imagine how this pattern applies to other
+languages.
+
+When projects grow big, you split them own into smaller projects by nesting
+projects inside each other.  Each sub-project has a sub-directory under the
+parent project and follows the same internal directory structure.  You can, of
+course, change all of that to suite your needs, but if you follow these
+conventions, Buildr will figure all the paths for you.
 
 Going back to the example above, the directory structure looks like this:
 
@@ -107,22 +139,47 @@
       |__ junit          <- For all test cases
 }}}
 
-Notice the @buildfile@ at the top.  That's your project build script, the one Buildr runs.
-
-When you run the @buildr@ command, it picks up the @buildfile@ (which here we'll just call _Buildfile_) from the current directory, or it not there, from the closest parent directory.  So you can run @buildr@ form any directory inside your project, and it will always pick up the same Buildfile.  That also happens to be the base directory for the top project.  If you have any sub-projects, Buildr assumes they reflect sub-directories under their parent.
+Notice the @buildfile@ at the top.  That's your project build script, the one
+Buildr runs.
 
-And yes, you can have two top projects in the same Buildfile.  For example, you can use that to have one project that groups all the application modules (JARs, WARs, etc) and another project that groups all the distribution packages (binary, sources, javadocs).
-
-When you start with a new project you won't see the @target@ or @reports@ directories.  Buildr creates these when it needs to.  Just know that they're there.
+When you run the @buildr@ command, it picks up the @buildfile@ (which here
+we'll just call _Buildfile_) from the current directory, or it not there, from
+the closest parent directory.  So you can run @buildr@ form any directory
+inside your project, and it will always pick up the same Buildfile.  That also
+happens to be the base directory for the top project.  If you have any
+sub-projects, Buildr assumes they reflect sub-directories under their parent.
+
+And yes, you can have two top projects in the same Buildfile.  For example, you
+can use that to have one project that groups all the application modules (JARs,
+WARs, etc) and another project that groups all the distribution packages
+(binary, sources, javadocs).
+
+When you start with a new project you won't see the @target@ or @reports@
+directories.  Buildr creates these when it needs to.  Just know that they're
+there.
 
 
 h2. Naming And Finding Projects
 
-Each project has a given name, the first argument you pass when calling @define@.  The project name is just a string, but we advise to stay clear of colon (@:@) and slashes (@/@ and @\@), which could conflict with other task and file names.  Also, avoid using common Buildr task names, don't pick @compile@ or @build@ for your project name.
-
-Since each project knows its parent project, child projects and siblings, you can reference them from within the project using just the given name.  In other cases, you'll need to use the full name.  The full name is just @parent:child@.  So if you wanted to refer to _teh-impl_, you could do so with either @project("killer-app:teh-impl")@ or @project("killer-app").project("teh-impl")@.
-
-The @project@ method is convenient when you have a dependency from one project to another, e.g. using the other project in the classpath, or accessing one of its source files.  Call it with a project name and it will return that object or raise an error.  You can also call it with no arguments and it will return the project itself.  It's syntactic sugar that's useful when accessing project properties.
+Each project has a given name, the first argument you pass when calling
+@define@.  The project name is just a string, but we advise to stay clear of
+colon (@:@) and slashes (@/@ and @\@), which could conflict with other task and
+file names.  Also, avoid using common Buildr task names, don't pick @compile@
+or @build@ for your project name.
+
+Since each project knows its parent project, child projects and siblings, you
+can reference them from within the project using just the given name.  In other
+cases, you'll need to use the full name.  The full name is just @parent:child@.
+So if you wanted to refer to _teh-impl_, you could do so with either
+@project('killer-app:teh-impl')@ or
+@project('killer-app').project('teh-impl')@.
+
+The @project@ method is convenient when you have a dependency from one project
+to another, e.g. using the other project in the classpath, or accessing one of
+its source files.  Call it with a project name and it will return that object
+or raise an error.  You can also call it with no arguments and it will return
+the project itself.  It's syntactic sugar that's useful when accessing project
+properties.
 
 The @projects@ method takes a list of names and returns a list of projects.  If you call it with no arguments on a project, it returns all its sub-projects.  If you call it with no argument in any other context, it returns all the projects defined so far.
 
@@ -132,16 +189,16 @@
 puts projects.inspect
 => [project("killer-app"), project("killer-app:teh-api") ... ]
 
-puts project("killer-app").projects.inspect
+puts project('killer-app').projects.inspect
 => [project("killer-app:teh-api"), project("killer-app:teh-impl") ... ]
 
-puts project("teh-api")
+puts project('teh-api')
 => No such project teh-api
 
-puts project("killer-app:teh-api").inspect
+puts project('killer-app:teh-api').inspect
 => project("killer-app:teh-api")
 
-puts project("killer-app").project("teh-api").inspect
+puts project('killer-app').project('teh-api').inspect
 => project("killer-app:teh-api")
 }}}
 
@@ -154,8 +211,11 @@
 
 h2. Running Project Tasks
 
-Most times, you run tasks like @build@ or @package@ that operate on the current project and recursively on its sub-projects.  The "current project" is the one that uses the current working directory.  So if you're in the @la-web/src@ directory looking at source files,
-_la-web_ is the current project.  For example: 
+Most times, you run tasks like @build@ or @package@ that operate on the current
+project and recursively on its sub-projects.  The "current project" is the one
+that uses the current working directory.  So if you're in the @la-web/src@
+directory looking at source files, _la-web_ is the current project.  For
+example: 
 
 {{{!sh
 # build killer-app and all its sub-projects
@@ -170,7 +230,8 @@
 $ buildr package
 }}}
 
-You can use the project's full name to invoke one of its tasks directly, and it doesn't matter which directory you're in.  For example:
+You can use the project's full name to invoke one of its tasks directly, and it
+doesn't matter which directory you're in.  For example:
 
 {{{!sh
 # build killer-app and all its sub-projects
@@ -183,11 +244,13 @@
 $ buildr killer-app:la-web:package
 }}}
 
-Buildr provides the following tasks that you can run on the current project, or on a specific project by prefixing them with the project's full name:
+Buildr provides the following tasks that you can run on the current project, or
+on a specific project by prefixing them with the project's full name:
 
 {{{
 clean     # Clean files generated during a build
 compile   # Compile all projects
+build     # Build the project
 upload    # Upload packages created by the project
 install   # Install packages created by the project
 javadoc   # Create the Javadocs for this project
@@ -205,104 +268,179 @@
 
 h2. Setting Project Properties
 
-We mentioned the group identifier, version number and base directory.  These are project properties.  There are a few more properties we'll cover later on.
+We mentioned the group identifier, version number and base directory.  These
+are project properties.  There are a few more properties we'll cover later on.
 
-There are two ways to set project properties.  You can pass them as a hash when you call @define@, or use accessors to set them on the project directly.  For example:
+There are two ways to set project properties.  You can pass them as a hash when
+you call @define@, or use accessors to set them on the project directly.  For
+example:
 
 {{{!ruby
-define "silly", :version=>"1.0" do
-  project.group = "acme"
+define 'silly', :version=>'1.0' do
+  project.group = 'acme'
 end
 
-puts project("silly").version
+puts project('silly').version
 => 1.0
-puts project("silly").group
+puts project('silly').group
 => acme
 }}}
 
-Project properties are inherited.  You can specify them once in the parent project, and they'll have the same value in all its sub-projects.  In the example, we only specify the version number once, for use in all sub-projects.
+Project properties are inherited.  You can specify them once in the parent
+project, and they'll have the same value in all its sub-projects.  In the
+example, we only specify the version number once, for use in all sub-projects.
 
 
 h2. Resolving Paths
 
-You can run @buildr@ from any directory in your project.  To keep tasks consistent and happy, it switches over to the Buildfile directory and executes all the tasks from there, before returning back to your working directory.  Your tasks can all rely on relative paths that start from the same directory as the Buildfile.
-
-But in practice, you'll want to use the @path_to@ method.  This method calculates a path relative to the project, a better way if you ever need to refactor your code, say turn a ad hoc task into a function you reuse.
-
-The @path_to@ method takes an array of strings and concatenates them into a path.  Absolute paths are returned as they are, relative paths are expanded relative to the project's base directory.  Slashes, if you don't already know, work very well on both Windows, Linux and OS/X.  And as a shortcut, you can use @_@.
+You can run @buildr@ from any directory in your project.  To keep tasks
+consistent and happy, it switches over to the Buildfile directory and executes
+all the tasks from there, before returning back to your working directory.
+Your tasks can all rely on relative paths that start from the same directory as
+the Buildfile.
+
+But in practice, you'll want to use the @path_to@ method.  This method
+calculates a path relative to the project, a better way if you ever need to
+refactor your code, say turn a ad hoc task into a function you reuse.
+
+The @path_to@ method takes an array of strings and concatenates them into a
+path.  Absolute paths are returned as they are, relative paths are expanded
+relative to the project's base directory.  Slashes, if you don't already know,
+work very well on both Windows, Linux and OS/X.  And as a shortcut, you can use
+@_@.
 
 For example:
 
 {{{!ruby
 # Relative to the current project
-path_to("src", "main", "java")
+path_to('src', 'main', 'java')
 
 # Exactly the same thing
-_("src/main/java")
+_('src/main/java')
 
 # Relative to the teh-impl project
-project("teh-impl")._("src/main/java")
+project('teh-impl')._('src/main/java')
 }}}
 
 
 h2. Defining The Project
 
-The project definition itself gives you a lot of pre-canned tasks to start with, but that's not enough to build a project.  You need to specify what gets built and how, which classpath dependencies to use, the packages you want to create and so forth.  You can configure existing tasks, extend them to do additional work, and create new tasks.  All that magic happens inside the project definition block.
-
-Since the project definition executes each time you run Buildr, you don't want to perform any work directly inside the project definition block.  Rather, you want to use it to specify how different build task work when you invoke them.  Here's an example to illustrate the point:
+The project definition itself gives you a lot of pre-canned tasks to start
+with, but that's not enough to build a project.  You need to specify what gets
+built and how, which dependencies to use, the packages you want to create and
+so forth.  You can configure existing tasks, extend them to do additional work,
+and create new tasks.  All that magic happens inside the project definition
+block.
+
+Since the project definition executes each time you run Buildr, you don't want
+to perform any work directly inside the project definition block.  Rather, you
+want to use it to specify how different build task work when you invoke them.
+Here's an example to illustrate the point:
 
 {{{!ruby
-define "silly" do
-  puts "Running buildr"
+define 'silly' do
+  puts 'Running buildr'
 
   build do
-    puts "Building silly"
+    puts 'Building silly'
   end
 end
 }}}
 
-Each time you run Buildr, it will execute the project definition and print out "Running buildr".  We also extend the @build@ task, and whenever we run it, it will print "Building silly".  Incidentally, @build@ is the default task, so if you run Buildr with no arguments, it will print both messages while executing the build.  If you run Buildr with a different task, say @clean@, it will only print the first message.
-
-The @define@ method gathers the project definition, but does not execute it immediately.  It executes the project definition the first time you reference that project, directly or indirectly, for example, by calling @project@ with that project's name, or calling @projects@ to return a list of all projects.  Executing a project definition will also execute all its sub-projects' definitions.  And, of course, all project definitions are executed once the Buildfile loads, so Buildr can determine how to execute each of the build tasks.
-
-If this sounds a bit complex, don't worry.  In reality, it does the right thing.  A simple rule to remember is that each project definition is executed before you need it, lazy evaluation of sort.  The reason we do that?  So you can write projects that depend on each other without worrying about their order.
+Each time you run Buildr, it will execute the project definition and print out
+"Running buildr".  We also extend the @build@ task, and whenever we run it, it
+will print "Building silly".  Incidentally, @build@ is the default task, so if
+you run Buildr with no arguments, it will print both messages while executing
+the build.  If you run Buildr with a different task, say @clean@, it will only
+print the first message.
+
+The @define@ method gathers the project definition, but does not execute it
+immediately.  It executes the project definition the first time you reference
+that project, directly or indirectly, for example, by calling @project@ with
+that project's name, or calling @projects@ to return a list of all projects.
+Executing a project definition will also execute all its sub-projects'
+definitions.  And, of course, all project definitions are executed once the
+Buildfile loads, so Buildr can determine how to execute each of the build
+tasks.
+
+If this sounds a bit complex, don't worry.  In reality, it does the right
+thing.  A simple rule to remember is that each project definition is executed
+before you need it, lazy evaluation of sort.  The reason we do that?  So you
+can write projects that depend on each other without worrying about their
+order.
+
+In our example, the _la-web_ project depends on packages created by the
+_teh-api_ and _teh-impl_ projects, the later requiring _teh-api_ to compile.
+That example is simple enough that we ended up specifying the projects in order
+of dependency.  But you don't always want to do that.  For large projects, you
+may want to group sub-projects by logical units, or sort them alphabetically
+for easier editing.
 
-In our example, the _la-web_ project depends on packages created by the _teh-api_ and _teh-impl_ projects, the later requiring _teh-api_ to compile.  That example is simple enough that we ended up specifying the projects in order of dependency.  But you don't always want to do that.  For large projects, you may want to group sub-projects by logical units, or sort them alphabetically for easier editing.
+One project can reference another ahead of its definition.  If Buildr detects a
+cyclic dependency, it will let you know.
 
-One project can reference another ahead of its definition.  If Buildr detects a cyclic dependency, it will let you know.
-
-In this example we define one project in terms of another, using the same classpath dependencies, so we only need to specify them once:
+In this example we define one project in terms of another, using the same
+dependencies, so we only need to specify them once:
 
 {{{!ruby
-define "bar" do
-  compile.with project("foo").compile.classpath
+define 'bar' do
+  compile.with project('foo').compile.dependencies
 end
 
-define "foo" do
+define 'foo' do
   compile.with ..lots of stuff..
 end
 }}}
 
 One last thing to remember.  Actually three, but they all go hand in hand.
 
-*Self is project* Each of these project definition blocks executes in the context of that project, just as if it was a method defined on the project.  So when you call the @compile@ method, you're essentially calling that method on the current project: @compile@, @self.compile@ and @project.compile@ are all the same.
-
-*Blocks are closures* The project definition is also a closure, which can reference variables from enclosing scopes.  You can use that, for example, to define constants, variables and even functions in your Buildfile, and reference them from your project definition.  As you'll see later on, in the "Artifacts":#artifacts section, it will save you a lot of work.
-
-*Projects are namespaces*
-While executing the project definition, Buildr switches the namespace to the project name.  If you define the task "do-this" inside the _teh-impl_ project, the actual task name is "killer-app:teh-impl:do-this".  Likewise, the @compile@ task is actually "killer-app:teh-impl:compile".
-
-From outside the project you can reference a task by its full name, either @task("foo:do")@ or @project("foo").task("do")@.  If you need to reference a task defined outside the project from within the project, prefix it with "rake:", for example, @task("rake:globally-defined")@.
+*Self is project* Each of these project definition blocks executes in the
+context of that project, just as if it was a method defined on the project.  So
+when you call the @compile@ method, you're essentially calling that method on
+the current project: @compile@, @self.compile@ and @project.compile@ are all
+the same.
+
+*Blocks are closures* The project definition is also a closure, which can
+reference variables from enclosing scopes.  You can use that, for example, to
+define constants, variables and even functions in your Buildfile, and reference
+them from your project definition.  As you'll see later on, in the
+"Artifacts":#artifacts section, it will save you a lot of work.
+
+*Projects are namespaces* While executing the project definition, Buildr
+switches the namespace to the project name.  If you define the task "do-this"
+inside the _teh-impl_ project, the actual task name is
+"killer-app:teh-impl:do-this".  Likewise, the @compile@ task is actually
+"killer-app:teh-impl:compile".
+
+From outside the project you can reference a task by its full name, either
+@task('foo:do')@ or @project('foo').task('do')@.  If you need to reference a
+task defined outside the project from within the project, prefix it with
+"rake:", for example, @task('rake:globally-defined')@.
 
 
 h2. Writing Your Own Tasks
 
-Of all the features Buildr provide, it doesn't have a task for making coffee.  Yet.  If you need to write your own tasks, you get all the power of Rake: you can use regular tasks, file tasks, task rules and even write your own custom task classes.  Check out the "Rake documentation":http://docs.rubyrake.org/ for more information.
-
-We mentioned projects as namespaces before.  When you call @task@ on a project, it finds or defines the task using the project namespace.  So given a project object, @task("do-this")@ will return it's "do-this" task.  If you lookup the source code for the @compile@ method, you'll find that it's little more than a shortcut for @task("compile")@.
-
-Another shortcut is the @file@ method.  When you call @file@ on a project, Buildr uses the @path_to@ method to expand relative paths using the project's base directory.  If you call @file("src")@ on _teh-impl_, it will return you a file task that points at the @teh-impl/src@ directory.
-
-In the current implementation projects are also created as tasks, although you don't invoke these tasks directly.  That's the reason for not using a project name that conflicts with an existing task name.  If you do that, you'll find quick enough, as the task will execute each time you run Buildr.
+Of all the features Buildr provide, it doesn't have a task for making coffee.
+Yet.  If you need to write your own tasks, you get all the power of Rake: you
+can use regular tasks, file tasks, task rules and even write your own custom
+task classes.  Check out the "Rake documentation":http://docs.rubyrake.org/ for
+more information.
+
+We mentioned projects as namespaces before.  When you call @task@ on a project,
+it finds or defines the task using the project namespace.  So given a project
+object, @task('do-this')@ will return it's "do-this" task.  If you lookup the
+source code for the @compile@ method, you'll find that it's little more than a
+shortcut for @task('compile')@.
+
+Another shortcut is the @file@ method.  When you call @file@ on a project,
+Buildr uses the @path_to@ method to expand relative paths using the project's
+base directory.  If you call @file('src')@ on _teh-impl_, it will return you a
+file task that points at the @teh-impl/src@ directory.
+
+In the current implementation projects are also created as tasks, although you
+don't invoke these tasks directly.  That's the reason for not using a project
+name that conflicts with an existing task name.  If you do that, you'll find
+quick enough, as the task will execute each time you run Buildr.
 
-So now that you know everything about projects and tasks, let's go and "build some code":building.html.
+So now that you know everything about projects and tasks, let's go and "build
+some code":building.html.

Modified: incubator/buildr/trunk/doc/pages/testing.textile
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/doc/pages/testing.textile?rev=609910&r1=609909&r2=609910&view=diff
==============================================================================
--- incubator/buildr/trunk/doc/pages/testing.textile (original)
+++ incubator/buildr/trunk/doc/pages/testing.textile Tue Jan  8 01:01:03 2008
@@ -1,45 +1,77 @@
 h1. Testing
 
-Untested code is broken code, so we take testing seriously.  Off the bat you get to use either JUnit or TestNG for writing unit tests and integration tests.  And you can also add your own framework, or even script tests using Ruby.  But first, let's start with the basics.
+Untested code is broken code, so we take testing seriously.  Off the bat you
+get to use either JUnit or TestNG for writing unit tests and integration tests.
+And you can also add your own framework, or even script tests using Ruby.  But
+first, let's start with the basics.
 
 
 h2. Writing Tests
 
-Each project has a @TestTask@ that you can access using the @test@ method.  @TestTask@ reflects on the fact that each project has one task responsible for getting the tests to run and acting on the results.  But in fact there are several tasks that do all the work, and a @test@ task coordinates all of that.
-
-The first two tasks to execute are @test.compile@ and @test.resources@.  They work similar to @compile@ and @resources@, but compile source files from the @src/test/java@ directory, and copy resource files from the @src/test/resources@ directory into the @target/test-classes@ directory.
-
-The @test.compile@ task will run the @compile@ task first, then use the same classpath dependencies to compile the test classes.  That much you already assumed.  It also adds the test framework (e.g. JUnit, TestNG) and JMock to the classpath.  Less work for you.
-
-If you need more classpath dependencies, the best way to add them is by calling @test.with@.  This method adds classpath dependencies to both @compile.classpath@ (for compiling) and @test.classpath@ (for running).  You can manage these two classpaths separately, but using @test.with@ is good enough in more cases.
+Each project has a @TestTask@ that you can access using the @test@ method.
+@TestTask@ reflects on the fact that each project has one task responsible for
+getting the tests to run and acting on the results.  But in fact there are
+several tasks that do all the work, and a @test@ task coordinates all of that.
+
+The first two tasks to execute are @test.compile@ and @test.resources@.  They
+work similar to @compile@ and @resources@, but uses a different set of
+directories.  For example, Java test cases compile from the @src/test/java@
+directory into the @target/test/classes@ directory, while resources are copied
+from @src/test/resources@ into @target/test/resources@.
+
+The @test.compile@ task will run the @compile@ task first, then use the same
+dependencies to compile the test classes.  That much you already assumed.  It
+also adds the test framework (e.g. JUnit, TestNG) and JMock to the dependency
+list.  Less work for you.
+
+If you need more dependencies, the best way to add them is by calling
+@test.with@.  This method adds dependencies to both @compile.dependencies@ (for
+compiling) and @test.dependencies@ (for running).  You can manage these two
+dependency lists separately, but using @test.with@ is good enough in more
+cases.
 
 Once compiled, the @test@ task runs the test cases.
 
 
 h2. Using JUnit
 
-The default test framework is JUnit 4.
-
-When you use JUnit, the classpath includes JUnit, and Buildr picks up all test classes from the project by looking for classes that either start with @Test@, end with @Test@ or end with @TestCase@.  For example, @TestMyCode@ and @LogicTest@.
-
-There are benefits to running test cases in separate VMs.  The default forking mode is @:once@, and you can change it by setting the @:fork@ option.  It accepts the following values:
-
-*@:once@* – Create one VM to run all test classes in the project, separate VMs for each project.
-
-*@:each@* – Create one VM for each test case class.  Slow but provides the best isolation between test classes.
-
-*@false@* –  Without forking, Buildr runs all test cases in a single VM.  This option runs fastest, but at the risk of running out of memory and causing test cases to interfere with each other.
+The default test framework for Java projects is JUnit 4.
 
-You can see your tests running in the console, and if any tests fail, Buildr will show a list of the failed test classes.  In addition, JUnit produces text and XML report files in the project's @reports/junit@ directory.  You can use that to get around too-much-stuff-in-my-console, or when using an automated test system.
+When you use JUnit, the dependencies includes JUnit, and Buildr picks up all
+test classes from the project by looking for classes that either start with
+@Test@, end with @Test@ or end with @TestCase@.  For example, @TestMyCode@ and
+@LogicTest@.
+
+There are benefits to running test cases in separate VMs.  The default forking
+mode is @:once@, and you can change it by setting the @:fork@ option.  It
+accepts the following values:
+
+*@:once@* – Create one VM to run all test classes in the project, separate VMs
+for each project.
+*@:each@* – Create one VM for each test case class.  Slow but provides the best
+isolation between test classes.
+*@false@* –  Without forking, Buildr runs all test cases in a single VM.  This
+option runs fastest, but at the risk of running out of memory and causing test
+cases to interfere with each other.
+
+You can see your tests running in the console, and if any tests fail, Buildr
+will show a list of the failed test classes.  In addition, JUnit produces text
+and XML report files in the project's @reports/junit@ directory.  You can use
+that to get around too-much-stuff-in-my-console, or when using an automated
+test system.
 
-In addition, you can get a consolidated XML or HTML report by running the @junit:report@ task.  For example:
+In addition, you can get a consolidated XML or HTML report by running the
+@junit:report@ task.  For example:
 
 {{{!sh
 $ buildr test junit:report test=all
 $ firefox report/junit/html/index.html
 }}}
 
-The @junit:report@ task generates a report from all tests run so far.  If you run tests in a couple of projects, it will generate a report only for these two projects.  The example above runs tests in all the projects before generating the reports.
+The @junit:report@ task generates a report from all tests run so far.  If you
+run tests in a couple of projects, it will generate a report only for these two
+projects.  The example above runs tests in all the projects before generating
+the reports.
 
 
 h2. Using TestNG
@@ -50,55 +82,82 @@
 test.using :testng
 }}}
 
-Like all other options you can set with @test.using@, it affects the projects and all its sub-projects, so you only need to do this once at the top-most project to use TestNG throughout.  You can also mix TestNG and JUnit by setting different projects to use different frameworks, but you can't mix both frameworks in the same project.  (And yes, @test.using :junit@ will switch a project back to using JUnit)
-
-TestNG works much like JUnit, it gets included in the classpath, Buildr picks test classes that start or end with @Test@, and generates test reports in the @reports/testng@ directory.  At the moment we don't have consolidated HTML reports for TestNG.
+Like all other options you can set with @test.using@, it affects the projects
+and all its sub-projects, so you only need to do this once at the top-most
+project to use TestNG throughout.  You can also mix TestNG and JUnit by setting
+different projects to use different frameworks, but you can't mix both
+frameworks in the same project.  (And yes, @test.using :junit@ will switch a
+project back to using JUnit)
+
+TestNG works much like JUnit, it gets included in the dependency list, Buildr
+picks test classes that start or end with @Test@, and generates test reports in
+the @reports/testng@ directory.  At the moment we don't have consolidated HTML
+reports for TestNG.
 
 
 h2. Excluding Tests and Ignoring Failures
 
-If you have a lot of test cases that are failing or just hanging there collecting dusts, you can tell Buildr to ignore them.  You can either tell Buildr to only run specific tests, for example:
+If you have a lot of test cases that are failing or just hanging there
+collecting dusts, you can tell Buildr to ignore them.  You can either tell
+Buildr to only run specific tests, for example:
 
 {{{!ruby
-test.include "com.acme.tests.passing.*"
+test.include 'com.acme.tests.passing.*'
 }}}
 
 Or tell it to exclude specific tests, for example:
 
 {{{!ruby
-test.exclude "*FailingTest", "*FailingWorseTest"
+test.exclude '*FailingTest', '*FailingWorseTest'
 }}}
 
-Note that we're always using the package qualified class name, and you can use star (@*@) to substitute for any set of characters.
+Note that we're always using the package qualified class name, and you can use
+star (@*@) to substitute for any set of characters.
 
-When tests fail, Buildr fails the @test@ task.  This is usually a good thing, but you can also tell Buildr to ignore failures by resetting the @:fail_on_failure@ option:
+When tests fail, Buildr fails the @test@ task.  This is usually a good thing,
+but you can also tell Buildr to ignore failures by resetting the
+@:fail_on_failure@ option:
 
 {{{!ruby
 test.using :fail_on_failure=>false
 }}}
 
-Besides giving you a free pass to ignore failures, you can use it for other causes, for example, to be somewhat forgiving:
+Besides giving you a free pass to ignore failures, you can use it for other
+causes, for example, to be somewhat forgiving:
 
 {{{!ruby
 test do
-  fail "More than 3 tests failed!" if test.failed_tests.size > 3
+  fail 'More than 3 tests failed!' if test.failed_tests.size > 3
 end
 }}}
 
-The @failed_tests@ collection holds the names of all classes with failed tests.  And there's @classes@, which holds the names of all test classes.  Ruby arithmetic allows you to get the name of all passed test classes with a simple @test.classes – test.failed_tests@.  We'll let you imagine creative use for these two.
+The @failed_tests@ collection holds the names of all classes with failed tests.
+And there's @classes@, which holds the names of all test classes.  Ruby
+arithmetic allows you to get the name of all passed test classes with a simple
+@test.classes – test.failed_tests@.  We'll let you imagine creative use for
+these two.
 
 
 h2. Running Tests
 
-It's a good idea to run tests every time you change the source code, so we wired the @build@ task to run the @test@ task at the end of the build.  And conveniently enough, the @build@ task is the default task, so another way to build changes in your code and run your test cases:
+It's a good idea to run tests every time you change the source code, so we
+wired the @build@ task to run the @test@ task at the end of the build.  And
+conveniently enough, the @build@ task is the default task, so another way to
+build changes in your code and run your test cases:
 
 {{{!sh
 $ buildr
 }}}
 
-That only works with the local @build@ task and any local task that depends on it, like @package@, @install@ and @upload@.  Each project also has its own @build@ task that does not invoke the @test@ task, so @buildr build@ will run the tests cases, but @buildr foo:build@ will not.
-
-While it's a good idea to always run your test cases, it's not always possible.  There are two ways you can get @build@ to not run the @test@ task.  You can set the environment variable @test@ to @no@ (but @skip@ and @off@ will also work).  You can do that when running Buildr:
+That only works with the local @build@ task and any local task that depends on
+it, like @package@, @install@ and @upload@.  Each project also has its own
+@build@ task that does not invoke the @test@ task, so @buildr build@ will run
+the tests cases, but @buildr foo:build@ will not.
+
+While it's a good idea to always run your test cases, it's not always possible.
+There are two ways you can get @build@ to not run the @test@ task.  You can set
+the environment variable @test@ to @no@ (but @skip@ and @off@ will also work).
+You can do that when running Buildr:
 
 {{{!sh
 $ buildr test=no
@@ -111,43 +170,70 @@
 $ buildr
 }}}
 
-
-If you're feeling really adventurous, you can also disable tests from your Buildfile or @buildr.rb@ file, by setting @options.test = false@. We didn't say it's a good idea, we're just giving you the option.
-
-The @test@ task is just smart enough to run all the test cases it finds, but will accept include/exclude patterns.  Often enough you're only working on one broken test case and you only want to run that one test case.  Better than changing your Buildfile, you can run the @test@ task with a pattern.  For example:
+If you're feeling really adventurous, you can also disable tests from your
+Buildfile or @buildr.rb@ file, by setting @options.test = false@. We didn't say
+it's a good idea, we're just giving you the option.
+
+The @test@ task is just smart enough to run all the test cases it finds, but
+will accept include/exclude patterns.  Often enough you're only working on one
+broken test case and you only want to run that one test case.  Better than
+changing your Buildfile, you can run the @test@ task with a pattern.  For
+example:
 
 {{{!sh
 $ buildr test:KillerAppTest
 }}}
 
-Buildr will then only run tests that contain the value @KillerAppTest@.  It uses pattern matching, so @test:Foo@ will run @com.acme.FooTest@ and @com.acme.FooBarTest@.  You can use a class name, or a package name to run all tests in that package, or any such combination.  In fact, you can specify several patterns separated with commas.  For example:
+Buildr will then only run tests that contain the value @KillerAppTest@.  It
+uses pattern matching, so @test:Foo@ will run @com.acme.FooTest@ and
+@com.acme.FooBarTest@.  You can use a class name, or a package name to run all
+tests in that package, or any such combination.  In fact, you can specify
+several patterns separated with commas.  For example:
 
 {{{!sh
 $ buildr test:FooTest,BarTest
 }}}
 
-As you probably noticed, Buildr will stop your build at the first test that fails.  We think it's a good idea, except when it's not.  If you're using a continuous build system, you'll want a report of all the failed tests without stopping at the first failure.  To make that happen, set the environment variable @test@ to "all", or the Buildr @options.test@ option to @:all@.  For example:
+As you probably noticed, Buildr will stop your build at the first test that
+fails.  We think it's a good idea, except when it's not.  If you're using a
+continuous build system, you'll want a report of all the failed tests without
+stopping at the first failure.  To make that happen, set the environment
+variable @test@ to "all", or the Buildr @options.test@ option to @:all@.  For
+example:
 
 {{{!sh
 $ buildr package test=all
 }}}
 
-We're using @package@ and not @build@ above.  When using a continuous build system, you want to make sure that packages are created, contain the right files, and also run the integration tests.
+We're using @package@ and not @build@ above.  When using a continuous build
+system, you want to make sure that packages are created, contain the right
+files, and also run the integration tests.
 
 
 h2. Integration Tests
 
-So far we talked about unit tests.  Unit tests are run in isolation on the specific project they test, in an isolated environment, generally with minimal setup and teardown.  You get a sense of that when we told you test cases run after the @build@ task, and include JMock in the classpath.
-
-In contrast, integration tests are run with a number of components, in an environment that resembles production, often with more complicates setup and teardown procedures.  In this section we'll talk about the differences between running unit and integration tests.
-
-You write integration tests much the same way as you write unit tests, using @test.compile@ and @test.resources@.  However, you need to tell Buildr that your test cases will execute during integration test.  To do so, add the following line in your project definition:
+So far we talked about unit tests.  Unit tests are run in isolation on the
+specific project they test, in an isolated environment, generally with minimal
+setup and teardown.  You get a sense of that when we told you test cases run
+after the @build@ task, and include JMock in the dependency list.
+
+In contrast, integration tests are run with a number of components, in an
+environment that resembles production, often with more complicates setup and
+teardown procedures.  In this section we'll talk about the differences between
+running unit and integration tests.
+
+You write integration tests much the same way as you write unit tests, using
+@test.compile@ and @test.resources@.  However, you need to tell Buildr that
+your test cases will execute during integration test.  To do so, add the
+following line in your project definition:
 
 {{{!ruby
 test.using :integration
 }}}
 
-Typically you'll use unit tests in projects that create internal modules, such as JARs, and integration tests in projects that create components, such as WARs and EARs.  You only need to use the @:integration@ option with the later.
+Typically you'll use unit tests in projects that create internal modules, such
+as JARs, and integration tests in projects that create components, such as WARs
+and EARs.  You only need to use the @:integration@ option with the later.
 
 To run integration tests on the current project:
 
@@ -161,63 +247,86 @@
 $ buildr integration:ClientTest
 }}}
 
-If you run the @package@ task (or any task that depends on it, like @install@ and @upload@), Buildr will first run the @build@ task and all its unit tests, and then create the packages and run the integration tests.  That gives you full coverage for your tests and ready to release packages.  As with unit tests, you can set the environment variable @test@ to "no" to skip integration tests, or "all" to ignore failures.
+If you run the @package@ task (or any task that depends on it, like @install@
+and @upload@), Buildr will first run the @build@ task and all its unit tests,
+and then create the packages and run the integration tests.  That gives you
+full coverage for your tests and ready to release packages.  As with unit
+tests, you can set the environment variable @test@ to "no" to skip integration
+tests, or "all" to ignore failures.
 
 
 h2. Using Setup and Teardown
 
-Some tests need you to setup an environment before they run, and tear it down afterwards.  The test frameworks (JUnit, TestNG) allow you to do that for each test.  Buildr provides two additional mechanisms for dealing with more complicated setup and teardown procedures.
-
-Integration tests run a setup task before the tests, and a teardown task afterwards.  You can use this task to setup a Web server for testing your Web components, or a database server for testing persistence.  You can access either task by calling @integration.setup@ and @integration.teardown@.  For example:
+Some tests need you to setup an environment before they run, and tear it down
+afterwards.  The test frameworks (JUnit, TestNG) allow you to do that for each
+test.  Buildr provides two additional mechanisms for dealing with more
+complicated setup and teardown procedures.
+
+Integration tests run a setup task before the tests, and a teardown task
+afterwards.  You can use this task to setup a Web server for testing your Web
+components, or a database server for testing persistence.  You can access
+either task by calling @integration.setup@ and @integration.teardown@.  For
+example:
 
 {{{!ruby
 integration.setup { server.start ; server.deploy }
 integration.teardown { server.stop }
 }}}
 
-Depending on your build, you may want to enhance the setup/teardown tasks from within a project, for example, to populate the database with data used by that project's test, or from outside the project definition, for example, to start and stop the Web server.
-
-Likewise, each project has its own setup and teardown tasks that are run before and after tests for that specific project.  You can access these tasks using @test.setup@ and @test.teardown@.
-
-
-h2. Testing Using Ruby
-
-You can use the @test@ task to run other kinds of tests beyond Java unit tests.  For example, you can use RJB to test Java code from within Ruby, and write test cases using Test::Unit or RSpec[2].  Other interesting options include "RFuzz":http://rfuzz.rubyforge.org/ for testing your Web server with random inputs, or Web UI testing with "Selenium":http://www.openqa.org/selenium/ and "Watir":http://wtr.rubyforge.org/.
+Depending on your build, you may want to enhance the setup/teardown tasks from
+within a project, for example, to populate the database with data used by that
+project's test, or from outside the project definition, for example, to start
+and stop the Web server.
+
+Likewise, each project has its own setup and teardown tasks that are run before
+and after tests for that specific project.  You can access these tasks using
+@test.setup@ and @test.teardown@.
 
 
 h2. Testing Your Build
 
-So you got the build running and all the tests pass, binaries are shipping when you find out some glaring omissions.  The license file is empty, the localized messages for Japanese are missing, the CSS files are not where you expect them to be.  The fact is, some errors slip by unit and integration tests.  So how do we make sure the same mistake doesn't happen again?
-
-Each project has a @check@ task that runs just after packaging.  You can use this task to verify that your build created the files you wanted it to create.  And to make it extremely convenient, we introduced the notion of expectations.
-
-You use the @check@ method to express and expectation.  Buildr will then run all these expectations against your project, and fail at the first expectation that doesn't match.  An expectation says three things.  Let's look at a few examples:
+So you got the build running and all the tests pass, binaries are shipping when
+you find out some glaring omissions.  The license file is empty, the localized
+messages for Japanese are missing, the CSS files are not where you expect them
+to be.  The fact is, some errors slip by unit and integration tests.  So how do
+we make sure the same mistake doesn't happen again?
+
+Each project has a @check@ task that runs just after packaging.  You can use
+this task to verify that your build created the files you wanted it to create.
+And to make it extremely convenient, we introduced the notion of expectations.
+
+You use the @check@ method to express and expectation.  Buildr will then run
+all these expectations against your project, and fail at the first expectation
+that doesn't match.  An expectation says three things.  Let's look at a few
+examples:
 
 {{{!ruby
-check package(:war), "should exist" do
+check package(:war), 'should exist' do
   it.should exist
 end
-check package(:war), "should contain a manifest" do
-  it.should contain("META-INF/MANIFEST.MF")
+check package(:war), 'should contain a manifest' do
+  it.should contain('META-INF/MANIFEST.MF')
 end
-check package(:war).path("WEB-INF"), "should contain files" do
+check package(:war).path('WEB-INF'), 'should contain files' do
   it.should_not be_empty
 end
-check package(:war).path("WEB-INF/classes"), "should contain classes" do 
-  it.should contain("**/*.class")
+check package(:war).path('WEB-INF/classes'), 'should contain classes' do 
+  it.should contain('**/*.class')
 end
-check package(:war).entry("META-INF/MANIFEST"), "should have license" do
+check package(:war).entry('META-INF/MANIFEST'), 'should have license' do
   it.should contain(/Copyright (C) 2007/)
 end
-check file("target/classes"), "should contain class files" do
-  it.should contain("**/*.class")
+check file('target/classes'), 'should contain class files' do
+  it.should contain('**/*.class')
 end
-check file("target/classes/killerapp/Code.class"), "should exist" do
+check file('target/classes/killerapp/Code.class'), 'should exist' do
   it.should exist
 end
 }}}
 
-The first argument is the subject, or the project if you skip the first argument.  The second argument is the description, optional, but we recommend using it.  The method @it@ returns the subject.
+The first argument is the subject, or the project if you skip the first
+argument.  The second argument is the description, optional, but we recommend
+using it.  The method @it@ returns the subject.
 
 You can also write the first expectation like this:
 
@@ -227,27 +336,49 @@
 end
 }}}
 
-We recommend using the subject and description, they make your build easier to read and maintain, and produce better error messages.
+We recommend using the subject and description, they make your build easier to
+read and maintain, and produce better error messages.
 
-There are two methods you can call on just about any object, called @should@ and @should_not@.  Each method takes an argument, a matcher, and executes that matcher.  If the matcher returns false, @should@ fails.  You can figure out what @should_not@ does in the same case.
+There are two methods you can call on just about any object, called @should@
+and @should_not@.  Each method takes an argument, a matcher, and executes that
+matcher.  If the matcher returns false, @should@ fails.  You can figure out
+what @should_not@ does in the same case.
 
 Buildr provides the following matchers:
 
 *exist* – Given a file task checks that the file (or directory) exists.
-
 *empty* – Given a file task checks that the file (or directory) is empty.
+*contain* – Given a file task referencing a file, checks its contents, using
+string or regular expression.  For a file task referencing a directory, checks
+that it contains the specified files; global patterns using @*@ and @**@ are
+allowed.
+
+All these matchers operate against a file task.  If you run them against a
+ZipTask (including JAR, WAR, etc) they can also check the contents of the ZIP
+file.  And as you can see in the examples above, you can also run them against
+a path in a ZIP file, checking its contents as if it was a directory, or
+against an entry in a ZIP file, checking the content of that file.
+
+*Note:* The @package@ method returns a package task based on packaging type,
+identifier, group, version and classifier.  The last four are inferred, but if
+you create a package with different specifications (for example, you specify a
+classifier) your checks must call @package@ with the same qualifying arguments
+to return the very same package task.
+
+Buildr expectations are based on RSpec.  Check the "RSpec
+documentation":http://rspec.rubyforge.org/ if want to see all the supported
+matchers, or want to write your own.
+
+We're almost done, but there's "a few more things":more_stuff.html you'll want
+to learn.
+
+
+fn1.  When Buildr sees @foo=bar@ on the command line, it sets the environment
+variable @foo@ to the value "bar".  Environment variables are available from
+the @ENV@ global variable and are case sensitive.  You can use that to other
+effects, for example, you can use an environment variable called build with
+values like "dev", "stage", "deploy" to change how your build works in
+different settings.
 
-*contain* – Given a file task referencing a file, checks its contents, using string or regular expression.  For a file task referencing a directory, checks that it contains the specified files; global patterns using @*@ and @**@ are allowed.
-
-All these matchers operate against a file task.  If you run them against a ZipTask (including JAR, WAR, etc) they can also check the contents of the ZIP file.  And as you can see in the examples above, you can also run them against a path in a ZIP file, checking its contents as if it was a directory, or against an entry in a ZIP file, checking the content of that file.
-
-*Note:* The @package@ method returns a package task based on packaging type, identifier, group, version and classifier.  The last four are inferred, but if you create a package with different specifications (for example, you specify a classifier) your checks must call @package@ with the same qualifying arguments to return the very same package task.
-
-Buildr expectations are based on RSpec.  Check the "RSpec documentation":http://rspec.rubyforge.org/ if want to see all the supported matchers, or want to write your own.
-
-We're almost done, but there's "a few more things":more_stuff.html you'll want to learn.
-
-
-fn1.  When Buildr sees @foo=bar@ on the command line, it sets the environment variable @foo@ to the value "bar".  Environment variables are available from the @ENV@ global variable and are case sensitive.  You can use that to other effects, for example, you can use an environment variable called build with values like "dev", "stage", "deploy" to change how your build works in different settings.
-
-fn2. "RSpec":http://rspec.rubyforge.org/ is the behavior-driven development framework we use to test Buildr itself. 
+fn2. "RSpec":http://rspec.rubyforge.org/ is the behavior-driven development
+framework we use to test Buildr itself. 

Modified: incubator/buildr/trunk/lib/java/packaging.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/java/packaging.rb?rev=609910&r1=609909&r2=609910&view=diff
==============================================================================
--- incubator/buildr/trunk/lib/java/packaging.rb (original)
+++ incubator/buildr/trunk/lib/java/packaging.rb Tue Jan  8 01:01:03 2008
@@ -300,8 +300,10 @@
         Java::Packaging::WarTask.define_task(file_name).tap do |war|
           war.with :manifest=>manifest, :meta_inf=>meta_inf
           # Add libraries in WEB-INF lib, and classes in WEB-INF classes
-          war.with :classes=>compile.target unless compile.sources.empty?
-          war.with :classes=>resources.target unless resources.sources.empty?
+          classes = []
+          classes << compile.target unless compile.sources.empty?
+          classes << resources.target unless resources.sources.empty?
+          war.with :classes=>classes
           war.with :libs=>compile.dependencies
           # Add included files, or the webapp directory.
           webapp = path_to(:source, :main, :webapp)