lib/java/packaging.rb in buildr-1.1.3 vs lib/java/packaging.rb in buildr-1.2.0
- old
+ new
@@ -1,9 +1,10 @@
require "core/project"
require "java/artifact"
require "java/java"
require "java/compile"
+require "java/test"
require "tasks/zip"
module Buildr
module Java
@@ -13,61 +14,43 @@
# the project definition, since it does all the heavy lifting.
module Packaging
MANIFEST_HEADER = "Manifest-Version: 1.0\nCreated-By: Buildr\n"
- # Extends the ZipTask to create a JAR file.
- #
- # This task supports two additional attributes: manifest and meta-inf.
- #
- # The manifest attribute specifies how to create the MANIFEST.MF file.
- # * A hash of manifest properties (name/value pairs).
- # * An array of hashes, one for each section of the manifest.
- # * A string providing the name of an existing manifest file.
- # * A file task can be used the same way.
- # * Proc or method called to return the contents of the manifest file.
- # * False to not generate a manifest file.
- #
- # The meta-inf attribute lists one or more files that should be copied into
- # the META-INF directory.
- #
- # For example:
- # package(:jar).using(:manifest=>"src/MANIFEST.MF")
- # package(:jar).meta_inf << file("README")
- class JarTask < ZipTask
+ # Adds support for MANIFEST.MF and other META-INF files.
+ module WithManifest
+ class << self
+ protected
+ def included(mod)
+ mod.alias_method_chain :initialize, :manifest
+ mod.alias_method_chain :invoke_prerequisites, :manifest
+ mod.alias_method_chain :create, :manifest
+ end
+ end
+
# Specifies how to create the manifest file.
attr_accessor :manifest
# Specifies files to include in the META-INF directory.
attr_accessor :meta_inf
- def initialize(*args) #:nodoc:
- super
- @manifest = true
- @meta_inf = []
- end
+ private
- def []=(key, value) #:nodoc:
- if key.to_sym == :manifest
- self.manifest = value
- elsif key.to_sym == :meta_inf
- self.meta_inf = [value].flatten
- else
- super key, value
- end
- value
+ def invoke_prerequisites_with_manifest()
+ prerequisites << file(manifest.to_s) if String === manifest || Rake::Task === manifest
+ [meta_inf].flatten.each { |file| prerequisites << file(file.to_s) }
+ invoke_prerequisites_without_manifest
end
- def prerequisites() #:nodoc:
- super + [ String === manifest ? file(manifest) : nil ].compact +
- [meta_inf].flatten.map { |file| String === file ? file(file) : file }
+ def initialize_with_manifest(*args)
+ @manifest = false
+ @meta_inf = []
+ initialize_without_manifest *args
end
- protected
-
- def create(zip) #:nodoc:
+ def create_with_manifest(zip) #:nodoc:
[meta_inf].flatten.map(&:to_s).uniq.each { |file| zip.add "META-INF/#{File.basename(file)}", file }
unless manifest == false
zip.file.open("META-INF/MANIFEST.MF", "w") do |output|
output << MANIFEST_HEADER
if manifest
@@ -78,51 +61,118 @@
output << manifest.reject { |section| section.empty? }.map { |section|
section.map { |pair| pair.join(": ") }.sort.join("\n").concat("\n")
}.join("\n") << "\n"
when Proc, Method
output << manifest.call
- when String, Task
+ when String, Rake::Task
output << File.read(manifest.to_s)
end
end
end
end
- super zip
+ create_without_manifest zip
end
end
+ class ::Buildr::ZipTask
+ include WithManifest
+ end
+
+
+ # Extends the ZipTask to create a JAR file.
+ #
+ # This task supports two additional attributes: manifest and meta-inf.
+ #
+ # The manifest attribute specifies how to create the MANIFEST.MF file.
+ # * A hash of manifest properties (name/value pairs).
+ # * An array of hashes, one for each section of the manifest.
+ # * A string providing the name of an existing manifest file.
+ # * A file task can be used the same way.
+ # * Proc or method called to return the contents of the manifest file.
+ # * False to not generate a manifest file.
+ #
+ # The meta-inf attribute lists one or more files that should be copied into
+ # the META-INF directory.
+ #
+ # For example:
+ # package(:jar).with(:manifest=>"src/MANIFEST.MF")
+ # package(:jar).meta_inf << file("README")
+ class JarTask < ZipTask
+
+ def initialize(*args) #:nodoc:
+ super
+ end
+
+ # :call-seq:
+ # with(options) => self
+ #
+ # Additional
+ # Pass options to the task. Returns self. ZipTask itself does not support any options,
+ # but other tasks (e.g. JarTask, WarTask) do.
+ #
+ # For example:
+ # package(:jar).with(:manifest=>"MANIFEST_MF")
+ def with(*args)
+ super args.pop if Hash === args.last
+ include :from=>args
+ self
+ end
+
+ end
+
# Extends the JarTask to create a WAR file.
#
# Supports all the same options as JarTask, in additon to these two options:
# * :libs -- An array of files, tasks, artifact specifications, etc that will be added
# to the WEB-INF/lib directory.
# * :classes -- A directory containing class files for inclusion in the WEB-INF/classes
# directory.
#
# For example:
- # package(:war).using(:libs=>"log4j:log4j:jar:1.1")
+ # package(:war).with(:libs=>"log4j:log4j:jar:1.1")
class WarTask < JarTask
- def []=(key, value) #:nodoc:
- case key.to_sym
- when :libs
- self.include Buildr.artifacts(value), :path=>"WEB-INF/lib"
- when :classes
- self.include value, :path=>"WEB-INF/classes", :as=>"."
- else
- super key, value
- end
- value
+ # Directories with class files to include under WEB-INF/classes.
+ attr_accessor :classes
+
+ # Artifacts to include under WEB-INF/libs.
+ attr_accessor :libs
+
+ def initialize(*args) #:nodoc:
+ super
+ @classes = []
+ @libs = []
end
+ def invoke_prerequisites() #:nodoc:
+ @classes.to_a.flatten.each { |classes| path("WEB-INF/classes").include classes, :as=>"." }
+ path("WEB-INF/lib").include Buildr.artifacts(@libs.to_a.flatten)
+ super
+ end
+
+ def libs=(value) #:nodoc:
+ @libs |= Buildr.artifacts(value)
+ end
+
+ def classes=(value) #:nodoc:
+ @classes |= [value].flatten.map { |dir| file(dir.to_s) }
+ end
+
end
end
end
+ Project.on_define do |project|
+ # Need to run buildr before package, since package is often used as a dependency by tasks that
+ # expect build to happen.
+ task "package"=>task("build")
+ end
+
+
class Project
# Options accepted by #package method for all package types.
PACKAGE_OPTIONS = [:group, :id, :version, :type, :classifier]
@@ -159,111 +209,90 @@
license = project.file("LICENSE")
File.exist?(license.to_s) ? [license] : []
end
# :call-seq:
- # package(type, options?) => task
+ # package(type, spec?) => task
#
# Defines and returns a package created by this project.
#
# The first argument declares the package type. For example, :jar to create a JAR file.
- # The second argument provides additional options used when defining the package.
+ # The package is an artifact that takes its artifact specification from the project.
+ # You can override the artifact specification by passing various options in the second
+ # argument, for example:
+ # package(:zip, :classifier=>"sources")
#
- # The following options are supported by all package types:
- # * :id -- The artifact identifier. By default, uses the project's #id property.
- # * :group -- The group identifier. By default, uses the project's #group property.
- # * :version -- The version number. By default, uses the project's #version property.
- # * :classifier -- Artifact classifier. By default, the artifact has no classifier.
+ # Packages that are ZIP files provides various ways to include additional files, directories,
+ # and even merge ZIPs together. Have a look at ZipTask for more information. In case you're
+ # wondering, JAR and WAR packages are ZIP files.
#
- # The JAR packager adds the following options:
+ # You can also enhance a JAR package using the ZipTask#with method that accepts the following options:
# * :manifest -- Specifies how to create the MANIFEST.MF. By default, uses the project's
# #manifest property.
# * :meta_inf -- Specifies files to be included in the META-INF directory. By default,
# uses the project's #meta-inf property.
- # * :include -- List of files and directories to include in the JAR. By default,
- # includes the contents of the target/classes directory.
#
- # The WAR packager adds the following options:
- # * :manifest -- See JAR.
- # * :meta_inf -- See JAR.
- # * :classes -- Directories of class files to include in WEB-INF/classes. By default,
- # includes the contents of the target/classes directory.
- # * :libs -- Artifacts and files to include in WEB-INF/libs. By default, includes the
- # compile classpath.
- # * :include -- List of files and directories to include in the JAR. By default,
- # includes the contents of the src/main/webapp directory.
+ # The WAR package supports the same options and adds a few more:
+ # * :classes -- Directories of class files to include in WEB-INF/classes. Includes the compile
+ # target directory by default.
+ # * :libs -- Artifacts and files to include in WEB-INF/libs. Includes the compile classpath
+ # dependencies by default.
#
- # The ZIP packager adds the following options:
- # * :include -- List of file and directories to include in the ZIP.
- #
- # In addition, you can always enhance the package task directly, e.g. by calling the
- # ZipTask#include and ZipTask#with methods.
- #
# For example:
# define "project" do
# define "beans" do
# package :jar
# end
# define "webapp" do
# compile.with project("beans")
- # package :war
+ # package(:war).with :libs=>MYSQL_JDBC
# end
- # package :zip, :classifier=>"sources", :include=>"."
+ # package(:zip, :classifier=>"sources").include path_to(".")
# end
#
- # The first time you call package, it defines the new task. Afterwards, it only uses some of the
- # options (file_name, id, etc) to find an existing package task and return it.
+ # Two other packaging types are:
+ # * package :sources -- Creates a ZIP file with the source code and classifier "sources", for use by IDEs.
+ # * package :javadoc -- Creates a ZIP file with the Javadocs and classifier "javadoc". You can use the
+ # javadoc method to further customize it.
#
- # For example:
- # # Create a new package that includes an existing package.
- # package(:war).include project("foo:bar").package(:jar)
- #
# A package is also an artifact. The following tasks operate on packages created by the project:
- # rake deploy # Deploy packages created by the project
- # rake install # Install packages created by the project
- # rake package # Create packages
- # rake uninstall # Remove previously installed packages
+ # buildr upload # Upload packages created by the project
+ # buildr install # Install packages created by the project
+ # buildr package # Create packages
+ # buildr uninstall # Remove previously installed packages
#
# If you want to add additional packaging types, implement a method with the name package_as_[type]
- # that accepts two arguments, the file name and a hash of options. The method must yield to the
- # block with the package only when first called to define the package, and must return the package
- # from each call.
+ # that accepts two arguments, the file name and a hash of options. You can change the options and
+ # file name, e.g. to add a classifier or change the file type. Your method may be called multiple times,
+ # and must return the same file task on each call.
def package(type = :jar, options = nil)
options = options.nil? ? {} : options.dup
options[:id] ||= self.id
options[:group] ||= self.group
options[:version] ||= self.version
options[:type] = type
file_name = path_to(:target, Artifact.hash_to_file_name(options))
packager = method("package_as_#{type}") rescue
fail("Don't know how to create a package of type #{type}")
- packager.call(file_name, options) do |package|
+ package = packager.call(file_name, options) { warn_deprecated "Yielding from package_as_ no longer necessary." }
+ unless packages.include?(package)
# Make it an artifact using the specifications, and tell it how to create a POM.
package.extend ActsAsArtifact
package.send :apply_spec, Hash[*Artifact::ARTIFACT_ATTRIBUTES.map { |k| [ k,options[k]] }.flatten]
# Another task to create the POM file.
pom_spec = package.to_spec_hash.merge(:type=>:pom)
pom = file(Buildr.repositories.locate(pom_spec))
pom.extend ActsAsArtifact
pom.send :apply_spec, pom_spec
pom.enhance do
mkpath File.dirname(pom.name), :verbose=>false
- File.open(pom.name, "w") do |file|
- xml = Builder::XmlMarkup.new(:target=>file, :indent=>2)
- xml.instruct!
- xml.project do
- xml.modelVersion "4.0.0"
- xml.groupId pom.group
- xml.artifactId pom.id
- xml.version pom.version
- xml.classifier pom.classifier if pom.classifier
- end
- end
+ File.open(pom.name, "w") { |file| file.write pom.pom_xml }
end
- # Make sure the package task creates it, and it invokes the build task first.
+ # We already run build before package, but we also need to do so if the package itself is
+ # used as a dependency, before we get to run the package task.
task "package"=>package
package.enhance [task("build")]
# Install the artifact along with its POM. Since the artifact (package task) is created
# in the target directory, we need to copy it into the local repository. However, the
@@ -280,19 +309,20 @@
task "uninstall" do |task|
verbose(Rake.application.options.trace || false) do
[ installed, pom ].map(&:to_s).each { |file| rm file if File.exist?(file) }
end
end
- task("deploy") { deploy(package, pom) }
+ task("upload") { package.pom.invoke ; package.pom.upload ; package.upload }
# Add the package to the list of packages created by this project, and
# register it as an artifact. The later is required so if we look up the spec
# we find the package in the project's target directory, instead of finding it
# in the local repository and attempting to install it.
packages << package
Artifact.register package, pom
end
+ package
end
# :call-seq:
# packages() => tasks
#
@@ -310,19 +340,23 @@
def package_as_jar(file_name, options) #:nodoc:
unless Rake::Task.task_defined?(file_name)
rake_check_options options, *PACKAGE_OPTIONS + [:manifest, :meta_inf, :include]
Java::Packaging::JarTask.define_task(file_name).tap do |jar|
- jar.manifest = options.has_key?(:manifest) ? options[:manifest] : manifest
- jar.meta_inf = options[:meta_inf] || meta_inf
+ jar.with :manifest=>manifest, :meta_inf=>meta_inf
+ [:manifest, :meta_inf].each do |option|
+ if options.has_key?(option)
+ warn_deprecated "The :#{option} option in package(:jar) is deprecated, please use package(:jar).with(:#{option}=>) instead."
+ jar.with option=>options[option]
+ end
+ end
if options[:include]
+ warn_deprecated "The :include option in package(:jar) is deprecated, please use package(:jar).include(files) instead."
jar.include options[:include]
else
- # Can only decide on this once we're done configuring the compile task.
- enhance { jar.include compile.target, :as=>"." }
+ jar.with compile.target unless compile.sources.empty?
end
- yield jar
end
else
rake_check_options options, *PACKAGE_OPTIONS
end
file(file_name)
@@ -330,32 +364,37 @@
def package_as_war(file_name, options) #:nodoc:
unless Rake::Task.task_defined?(file_name)
rake_check_options options, *PACKAGE_OPTIONS + [:manifest, :meta_inf, :classes, :libs, :include]
Java::Packaging::WarTask.define_task(file_name).tap do |war|
- war.manifest = options.has_key?(:manifest) ? options[:manifest] : manifest
- war.meta_inf = options[:meta_inf] || meta_inf
+ war.with :manifest=>manifest, :meta_inf=>meta_inf
+ [:manifest, :meta_inf].each do |option|
+ if options.has_key?(option)
+ warn_deprecated "The :#{option} option in package :war is deprecated, please use package(:war).with(:#{option}=>) instead."
+ war.with option=>options[option]
+ end
+ end
# Add libraries in WEB-INF lib, and classes in WEB-INF classes
- if options[:classes]
+ if options.has_key?(:classes)
+ warn_deprecated "The :classes option in package(:war) is deprecated, please use package(:war).with(:classes=>) instead."
war.with :classes=>options[:classes]
else
- # Can only decide on this once we're done configuring the compile task.
- enhance { war.with :classes=>compile.target unless compile.sources.empty? }
+ war.with :classes=>compile.target unless compile.sources.empty?
end
- if options[:libs]
+ if options.has_key?(:libs)
+ warn_deprecated "The :libs option in package(:war) is deprecated, please use package(:war).with(:libs=>) instead."
war.with :libs=>options[:libs].collect
else
- # Can only decide on this once we're done configuring the compile task.
- enhance { war.with :libs=>compile.classpath }
+ war.with :libs=>compile.classpath
end
# Add included files, or the webapp directory.
- if options[:include]
+ if options.has_key?(:include)
+ warn_deprecated "The :include option in package(:war) is deprecated, please use package(:war).include(files) instead."
war.include options[:include]
- elsif File.exist?(path_to("src/main/webapp"))
- war.include path_to("src/main/webapp"), :as=>"."
+ else
+ path_to("src/main/webapp").tap { |path| war.with path if File.exist?(path) }
end
- yield war
end
else
rake_check_options options, *PACKAGE_OPTIONS
end
file(file_name)
@@ -364,17 +403,96 @@
def package_as_zip(file_name, options) #:nodoc:
unless Rake::Task.task_defined?(file_name)
rake_check_options options, *PACKAGE_OPTIONS + [:include]
ZipTask.define_task(file_name).tap do |zip|
if options[:include]
+ warn_deprecated "The :include option in package(:zip) is deprecated, please use package(:zip).include(files) instead."
zip.include options[:include]
end
- yield zip
end
else
rake_check_options options, *PACKAGE_OPTIONS
end
file(file_name)
+ end
+
+ def package_as_sources(file_name, options) #:nodoc:
+ rake_check_options options, *PACKAGE_OPTIONS
+ options.merge!(:type=>:zip, :classifier=>"sources")
+ file_name = path_to(:target, Artifact.hash_to_file_name(options))
+ ZipTask.define_task(file_name).tap { |zip| zip.include :from=>compile.sources } unless Rake::Task.task_defined?(file_name)
+ file(file_name)
+ end
+
+ def package_as_javadoc(file_name, options) #:nodoc:
+ rake_check_options options, *PACKAGE_OPTIONS
+ options.merge!(:type=>:zip, :classifier=>"javadoc")
+ file_name = path_to(:target, Artifact.hash_to_file_name(options))
+ unless Rake::Task.task_defined?(file_name)
+ ZipTask.define_task(file_name).tap { |zip| zip.include :from=>javadoc.target }
+ javadoc.options[:windowtitle] ||= project.comment || project.name
+ end
+ file(file_name)
+ end
+
+ end
+
+ class Project
+
+ # :call-seq:
+ # package_with_sources(options?)
+ #
+ # Call this when you want the project (and all its sub-projects) to create a source distribution.
+ # You can use the source distribution in an IDE when debugging.
+ #
+ # A source distribution is a ZIP package with the classifier "sources", which includes all the
+ # sources used by the compile task.
+ #
+ # Packages use the project's manifest and meta_inf properties, which you can override by passing
+ # different values (e.g. false to exclude the manifest) in the options.
+ #
+ # To create source distributions only for specific projects, use the :only and :except options,
+ # for example:
+ # package_with_sources :only=>["foo:bar", "foo:baz"]
+ #
+ # (Same as calling package :sources on each project/sub-project that has source directories.)
+ def package_with_sources(options = nil)
+ options ||= {}
+ enhance do
+ selected = options[:only] ? projects(options[:only]) :
+ options[:except] ? ([self] + projects - projects(options[:except])) :
+ [self] + projects
+ selected.reject { |project| project.compile.sources.empty? }.
+ each { |project| project.package(:sources) }
+ end
+ end
+
+ # :call-seq:
+ # package_with_javadoc(options?)
+ #
+ # Call this when you want the project (and all its sub-projects) to create a JavaDoc distribution.
+ # You can use the JavaDoc distribution in an IDE when coding against the API.
+ #
+ # A JavaDoc distribution is a ZIP package with the classifier "javadoc", which includes all the
+ # sources used by the compile task.
+ #
+ # Packages use the project's manifest and meta_inf properties, which you can override by passing
+ # different values (e.g. false to exclude the manifest) in the options.
+ #
+ # To create JavaDoc distributions only for specific projects, use the :only and :except options,
+ # for example:
+ # package_with_javadoc :only=>["foo:bar", "foo:baz"]
+ #
+ # (Same as calling package :javadoc on each project/sub-project that has source directories.)
+ def package_with_javadoc(options = nil)
+ options ||= {}
+ enhance do
+ selected = options[:only] ? projects(options[:only]) :
+ options[:except] ? ([self] + projects - projects(options[:except])) :
+ [self] + projects
+ selected.reject { |project| project.compile.sources.empty? }.
+ each { |project| project.package(:javadoc) }
+ end
end
end
end