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