lib/java/packaging.rb in buildr-0.18.0 vs lib/java/packaging.rb in buildr-0.19.0
- old
+ new
@@ -1,37 +1,74 @@
+require "core/project"
+require "java/artifact"
+require "java/java"
+require "java/compile"
+require "tasks/zip"
+
+
module Buildr
module Java
+ # This module includes comming packaging classes. Each one is also reflected by
+ # a packaging method. Generally, you would want to use the packaging method from
+ # 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.
+ # * 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
+
+ # 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)
+ def initialize(*args) #:nodoc:
super
@manifest = true
+ @meta_inf = []
end
- def []=(key, value)
+ def []=(key, value) #:nodoc:
if key.to_sym == :manifest
self.manifest = value
elsif key.to_sym == :meta_inf
self.meta_inf = value
else
super key, value
end
value
end
+ def prerequisites() #:nodoc:
+ super + [ String === manifest ? file(manifest) : nil ].compact +
+ meta_inf.map { |file| String === file ? file(file) : file }
+ end
+
protected
- def create(zip)
+ def create(zip) #:nodoc:
zip.mkdir "META-INF"
- meta_inf.each { |file| zip.add "META-INF/#{File.basename(file)}", file } if meta_inf
+ meta_inf.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.write MANIFEST_HEADER
if manifest
case manifest
@@ -54,18 +91,28 @@
super zip
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")
class WarTask < JarTask
- def []=(key, value)
+ def []=(key, value) #:nodoc:
case key.to_sym
when :libs
self.include artifacts(value), :path=>"WEB-INF/lib"
when :classes
- self.include value, :path=>"WEB-INF/classes"
+ self.include value, :path=>"WEB-INF/classes", :as=>"."
else
super key, value
end
value
end
@@ -74,158 +121,243 @@
end
end
- # Create a JAR from all the pre-requisites.
- #
- # For example:
- # jar "lib-1.0.jar"=>["target/classes", "MANIFEST,MF"]
- def jar(file)
- Packaging::JarTask.define_task(file)
- end
-
class Project
- # Group used for packaging. Inherited from parent project.
- inherited_attr :group
+ # Group used for packaging. Inherited from parent project. Defaults to the top-level project name.
+ attr_accessor :group
+ inherited_attr(:group) { |project| project.name }
# Version used for packaging. Inherited from parent project.
+ attr_accessor :version
inherited_attr :version
- # Manifest used for packaging. Inherited from parent project.
+ # Manifest used for packaging. Inherited from parent project. The default value
+ # is a hash that includes the Build-By, Build-Jdk and Implementation-Version values.
+ # The later is taken from the project's version number.
+ attr_accessor :manifest
inherited_attr :manifest do |project|
manifest = { "Build-By"=>ENV['USER'], "Build-Jdk"=>Java.version }
manifest["Implementation-Version"] = project.version if project.version
manifest
end
- # Files to always include in the package META-INF directory.
- inherited_attr :meta_inf do |project|
- meta_inf = ["DISCLAIMER", "LICENSE", "NOTICE"].map { |f| path_to(f) if File.exist?(path_to(f)) }.compact
+ # Files to always include in the package META-INF directory. The default value include
+ # the LICENSE file if one exists in the project's base directory.
+ attr_accessor :meta_inf
+ inherited_attr(:meta_inf) do |project|
+ license = project.file("LICENSE")
+ File.exist?(license.to_s) ? [license] : []
end
- # The project ID is the project name, and for a sub-project the
- # parent project ID followed by the project name, separated with a
- # hyphen. For example, "foo" and "foo-bar".
+ # The project's identifier. Same as the project name, with colons replaced by dashes.
+ # The ID for project foo:bar is foo-bar.
+ attr_reader :id
def id()
name.gsub(":", "-")
end
- inherited_attr :webapp_src_dir do File.join(src_dir, "main", "webapp") end
- def package(*args)
- if Hash === args.last
- options = args.pop.dup
- else
- options = {}
- end
- options[:type] = args.shift.to_s if Symbol === args.first
- fail "No packaging type specified" unless options[:type]
+ # :call-seq:
+ # package(type, options?) => 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 following options are supported by all package types:
+ # * :file_name -- The package file name. By default it uses the artifact identifier,
+ # version number and file type to create a file name (id-version.type).
+ # * :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.
+ #
+ # The JAR packager adds 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 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
+ # end
+ # package :zip, :classifier=>"sources", :include=>"."
+ # 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.
+ #
+ # 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
+ #
+ # 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.
+ def package(type, options = nil)
+ options = options.nil? ? {} : options.dup
+ options[:id] ||= self.id
options[:group] ||= self.group
options[:version] ||= self.version
- options[:id] ||= self.id
- if String === args.first
- file = args.shift
- else
- file = options.delete(:file) || path_to(:target_dir, Artifact.hash_to_file_name(options))
- end
- fail "One argument too many; expecting at most type, file name, and hash of options" unless args.empty?
+ options[:type] = type
+ file_name = options[:file_name] || path_to("target", Artifact.hash_to_file_name(options))
- packager = method("package_as_#{options[:type]}") rescue
- fail("Do not know how to create a package of type #{options[:type]}")
- package = packager.call(file, options) or fail("Do not know how to create a package of type #{options[:type]}")
+ packager = method("package_as_#{type}") rescue
+ fail("Do not know how to create a package of type #{:type}")
+ packager.call(file_name, options) do |package|
+ # Make it an artifact using the specifications, and tell it how to create a POM.
+ package.extend ActsAsArtifact
+ package.send :apply_spec, options
+ package.pom.enhance do |pom|
+ 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 options[:group]
+ xml.artifactId options[:id]
+ xml.version options[:version]
+ xml.classifier options[:classifier] if options[:classifier]
+ end
+ end
+ end
- task "package"=>package
- package.enhance [ task("build")]
+ # Make sure the package task creates it, and it invokes the build task first.
+ task "package"=>package
+ package.enhance [task("build")]
- task "install"=>(file(repositories.locate(package)=>package) { |task|
- mkpath File.dirname(task.name), :verbose=>false
- cp package.name, task.name
- })
- task "install"=>package.pom
-
- task "uninstall" do |task|
- verbose(Rake.application.options.trace) do
- [ package, package.pom ].map { |artifact| repositories.locate(artifact) }.
- each { |file| rm file if File.exist?(file) }
+ # 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
+ # POM artifact (created by calling artifact on its spec) is already mapped to its right
+ # place in the local repository, so we only need to invoke it.
+ installed = file(repositories.locate(package)=>package) { |task|
+ verbose(Rake.application.options.trace || false) do
+ mkpath File.dirname(task.name), :verbose=>false
+ cp package.name, task.name
+ end
+ puts "Installed #{task.name}" if verbose
+ }
+ task "install"=>[installed, package.pom]
+ task "uninstall" do |task|
+ verbose(Rake.application.options.trace || false) do
+ [ installed, package.pom ].map(&:to_s).each { |file| rm file if File.exist?(file) }
+ end
end
- end
+ task("deploy") { deploy(installed, package.pom) }
- task("deploy") { deploy(package, package.pom) }
-
- packages << package
- Artifact.register package, package.pom
- package
+ # 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, package.pom
+ end
end
+ # :call-seq:
+ # packages() => tasks
+ #
+ # Returns all packages created by this project. A project may create any number of packages.
+ #
+ # This method is used whenever you pass a project to Buildr#artifact or any other method
+ # that accepts artifact specifications and projects. You can use it to list all packages
+ # created by the project. If you want to return a specific package, it is often more
+ # convenient to call #package with the type.
def packages()
@packages ||= []
end
protected
- def package_as_jar(file, options)
- unless Rake::Task.task_defined?(file)
- returning(Java::Packaging::JarTask.define_task(file)) do |task|
- package_extend task, options
- task.enhance [path_to(:java_target_dir)]
- task.include path_to(:java_target_dir, "*")
- task.manifest = manifest.merge("Implementation-Title"=>self.comment)
- task.meta_inf = meta_inf
+ def package_as_jar(file_name, options) #:nodoc:
+ unless Rake::Task.task_defined?(file_name)
+ Java::Packaging::JarTask.define_task(file_name).tap do |jar|
+ jar.manifest = options[:manifest] || manifest.merge("Implementation-Title"=>self.comment)
+ jar.meta_inf = options[:meta_inf] || meta_inf
+ if options[:include]
+ jar.include options[:include]
+ else
+ # Can only decide on this once we're done configuring the compile task.
+ enhance { jar.include compile.target, :as=>"." }
+ end
+ yield jar
end
end
- returning(Java::Packaging::JarTask.define_task(file)) do |task|
- task.include options[:include] if options[:include]
- task.manifest = options[:manifest] if options[:manifest]
- end
+ file(file_name)
end
- def package_as_war(file, options)
- unless Rake::Task.task_defined?(file)
- returning(Java::Packaging::WarTask.define_task(file)) do |task|
- package_extend task, options
- task.include path_to(:webapp_src_dir, "*")
- task.with :classes=>path_to(:java_target_dir, "**")
- task.manifest = manifest.merge("Implementation-Title"=>self.comment)
- task.meta_inf = meta_inf
+ def package_as_war(file_name, options) #:nodoc:
+ unless Rake::Task.task_defined?(file_name)
+ Java::Packaging::WarTask.define_task(file_name).tap do |war|
+ war.manifest = options[:manifest] || manifest.merge("Implementation-Title"=>self.comment)
+ war.meta_inf = options[:meta_inf] || meta_inf
+ # Add libraries in WEB-INF lib, and classes in WEB-INF classes
+ if options[:classes]
+ 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? }
+ end
+ if options[:libs]
+ 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 }
+ end
+ # Add included files, or the webapp directory.
+ if options[:include]
+ war.include options[:include]
+ elsif File.exist?(path_to("src/main/webapp"))
+ war.include path_to("src/main/webapp"), :as=>"."
+ end
+ yield war
end
end
- # Add anything we find in webapp sources directory.
- returning(Java::Packaging::WarTask.define_task(file)) do |task|
- task.include options[:include] if options[:include]
- # Add libraries in WEB-INF lib, and classes in WEB-INF classes
- task.with :libs=>options[:libs].collect if options[:libs]
- task.with :classes=>options[:classes] if options[:classes]
- task.manifest = options[:manifest] if options[:manifest]
- end
+ file(file_name)
end
- def package_as_zip(file, options)
- unless Rake::Task.task_defined?(file)
- returning(ZipTask.define_task(file)) do |task|
- package_extend task, options
- task.include path_to(:java_target_dir, "*")
- end
- end
- returning(ZipTask.define_task(file)) do |task|
- task.include options[:include] if options[:include]
- end
- end
-
- def package_extend(task, spec)
- task.extend ActsAsArtifact
- task.apply_spec spec
- task.pom.enhance do |task|
- mkpath File.dirname(task.name), :verbose=>false
- File.open(task.name, "w") do |file|
- xml = Builder::XmlMarkup.new(:target=>file, :indent=>2)
- xml.instruct!
- xml.project do
- xml.modelVersion "4.0.0."
- xml.groupId spec[:group]
- xml.artifactId spec[:id]
- xml.version spec[:version]
- xml.classifier spec[:classifier] if spec[:classifier]
+ def package_as_zip(file_name, options) #:nodoc:
+ unless Rake::Task.task_defined?(file_name)
+ ZipTask.define_task(file_name).tap do |zip|
+ if options[:include]
+ zip.include options[:include]
end
+ yield zip
end
end
+ file(file_name)
end
end