lib/core/artifact.rb in buildr-0.15.0 vs lib/core/artifact.rb in buildr-0.16.0

- old
+ new

@@ -1,27 +1,29 @@ module Buildr # This module gives you a way to access the individual properties of an - # artifact: id, group, type and version. It can also return the artifact - # specification. + # artifact: id, group, type, classifier and version. It also provides other + # methods commonly used on an artifact, specifically #to_hash and #to_spec. module ActsAsArtifact + ARTIFACT_ATTRIBUTES = [:group, :id, :type, :classifier, :version] + class << self def included(mod) mod.extend self end end - # The artifact id. + # The artifact identifier. attr_reader :id - # The group id. + # The group identifier. attr_reader :group # The file type. attr_reader :type # The version number. attr_reader :version - # Optional classifier. + # Optional artifact classifier. attr_reader :classifier # Returns the artifact specification as a hash. def to_spec_hash() base = { :group=>group, :id=>id, :type=>type, :version=>version } @@ -29,140 +31,141 @@ end alias_method :to_hash, :to_spec_hash # Returns the artifact specification, in the structure: # <group>:<artifact>:<type>:<version> + # or + # <group>:<artifact>:<type>:<classifier><:<version> def to_spec() classifier.blank? ? "#{group}:#{id}:#{type}:#{version}" : "#{group}:#{id}:#{type}:#{classifier}:#{version}" end # Apply specification to this artifact. def apply_spec(spec) - case spec - when String - @group, @id, @type, @version, *rest = spec.split(":") - unless rest.empty? - @classifier, @version = @version, rest.shift - fail "Expecting project:id:type:version or project:id:type:classifier:version, found #{spec}" unless rest.empty? - end - when Hash - [:group, :id, :type, :version, :classifier].each { |key| instance_variable_set("@#{key}", spec[key]) } - @type ||= DEFAULT_FILE_TYPE - else - raise ArgumentError, "Expecting a string or a hash" - end + spec = Artifact.to_hash(spec) + ARTIFACT_ATTRIBUTES.each { |key| instance_variable_set("@#{key}", spec[key]) } self end + # Convenience method that returns a POM artifact. def pom() return self if type.to_s == "pom" artifact(:group=>group, :id=>id, :version=>version, :type=>"pom", :classifier=>classifier) end end - # Use the artifact and artifacts method to create artifact tasks. + # The Artifact task maps to an artifact file in the local repository + # and knows how to download the file from a remote repository. + # + # The task will only download the file if it does not exist. You can + # enhance the task to create the artifact yourself. class Artifact < Rake::FileCreationTask # The default file type for artifacts, if not specified. DEFAULT_FILE_TYPE = "jar" include ActsAsArtifact class << self - # Lookup an artifact task based on its specification. + # Lookup a previously registered artifact task based on the + # artifact specification (string or hash). def lookup(spec) @artifacts ||= {} - @artifacts[hash_to_spec(spec)] + @artifacts[to_spec(spec)] end - # Register a task as an artifact. Returns the task when calling - # #artifacts. For example, a project will use this to register the - # packages it creates. - def register(task) + # Register an artifact task(s) for later lookup (see #lookup). + def register(*tasks) @artifacts ||= {} - if task.respond_to?(:to_hash) - @artifacts[hash_to_spec(task.to_hash)] = task - else - fail "Can only call with an artifact task" - end + fail "You can only register an artifact task, strings and hashes are just not good enough" unless + tasks.all? { |task| task.respond_to?(:to_spec) && task.respond_to?(:invoke) } + tasks.each { |task| @artifacts[task.to_spec] = task } + tasks end # Turn a spec into a hash. This method accepts a string, hash or any object - # that responds to the method to_spec. There are several reasons you'll want - # to use this method: - # * You can pass it anything that could possibly be a spec, and get a hash. - # * It will check that your spec includes the group identifier, artifact - # identifier and version number, and set the file type, if not specified. + # that responds to the method to_spec. There are several reasons to use this + # method: + # * You can pass anything that could possibly be a spec, and get a hash. + # * It will check that the spec includes the group identifier, artifact + # identifier and version number and set the file type, if missing. # * It will always return a new specs hash. - # * Calling to_s on the hash will return a spec string. # # :nodoc: - def spec_to_hash(spec) - return spec_to_hash(spec.to_spec) if spec.respond_to?(:to_spec) - if String === spec - group, id, type, version, *rest = spec.split(":") - unless rest.empty? - classifier, version = version, rest.shift - fail "Expecting project:id:type:version or project:id:type:classifier:version, found #{spec}" unless rest.empty? - end - fail "Missing file type for #{spec}" if type.blank? - spec_to_hash :group=>group, :id=>id, :type=>type, :version=>version, :classifier=>classifier - elsif Hash === spec - spec = spec.clone + def to_hash(spec) + if spec.respond_to?(:to_spec) + to_hash spec.to_spec + elsif Hash === spec + # Sanitize the hash and check it's valid. + spec = ARTIFACT_ATTRIBUTES.inject({}) { |h, k| h[k] = spec[k] ; h } fail "Missing group identifier for #{spec.inspect}" if spec[:group].blank? fail "Missing artifact identifier for #{spec.inspect}" if spec[:id].blank? fail "Missing version for #{spec.inspect}" if spec[:version].blank? spec[:type] = DEFAULT_FILE_TYPE if spec[:type].blank? spec + elsif String === spec + group, id, type, version, *rest = spec.split(":") + unless rest.empty? + # Optional classifier comes before version. + classifier, version = version, rest.shift + fail "Expecting <project:id:type:version> or <project:id:type:classifier:version>, found <#{spec}>" unless rest.empty? + end + to_hash :group=>group, :id=>id, :type=>type, :version=>version, :classifier=>classifier else - fail "Spec must be a string, hash or object that responds to to_spec" + fail "Expecting a String, Hash or object that responds to to_spec" end end - # Convert a hash back to a spec string. - def hash_to_spec(hash) + # Convert a hash back to a spec string. This method accepts + # a string, hash or any object that responds to to_spec. + # :nodoc: + def to_spec(hash) + hash = to_hash(hash) unless Hash === hash version = ":#{hash[:version]}" unless hash[:version].blank? classifier = ":#{hash[:classifier]}" unless hash[:classifier].blank? "#{hash[:group]}:#{hash[:id]}:#{hash[:type] || DEFAULT_FILE_TYPE}#{classifier}#{version}" end # Convert a hash to a file name. + # :nodoc: def hash_to_file_name(hash) version = "-#{hash[:version]}" unless hash[:version].blank? classifier = "-#{hash[:classifier]}" unless hash[:classifier].blank? "#{hash[:id]}#{version}#{classifier}.#{hash[:type] || DEFAULT_FILE_TYPE}" end end - def initialize(*args) + def execute() + # Default behavior: download the artifact from one of the remote + # repositories if the file does not exist. But this default behavior + # is counter useful if the artifact knows how to build itself + # (e.g. download from a different location), so don't perform it + # if the task found a different way to create the artifact. super - enhance do |task| - # Download the artifact form one of the remote repositories if the - # file does not exist in the local repository, and no other behavior - # specified. If this task has been enhanced, delegate to the other - # enhancers. We don't know if this is the first or other enhancement, - # but we only need to know it's not the only one. - if @actions.size == 1 - repositories.download(to_spec) - end + unless Rake.application.options.dryrun || File.exist?(name) + repositories.download(to_spec) end end end - # Singleton object for specifying the local, remote and release repositories. + # Holds the path to the local repository, URLs for remote repositories, and + # settings for the deployment repository. class Repositories include Singleton # Returns the path to the local repository. # # The default path is .m2/repository relative to the home directory. + # You can change the location of the local repository by using a symbol + # link or by setting a different path. If you set a different path, do it + # in the buildr.rb file instead of the Rakefile. def local() @local ||= ENV["local_repo"] || File.join(ENV["HOME"], ".m2", "repository") end # Sets the path to the local repository. @@ -174,11 +177,11 @@ # # For example: # locate :group=>"log4j", :id=>"log4j", :version=>"1.1" # => ~/.m2/repository/log4j/log4j/1.1/log4j-1.1.jar def locate(spec) - spec = Artifact.spec_to_hash(spec) unless Hash === spec + spec = Artifact.to_hash(spec) File.join(local, spec[:group].split("."), spec[:id], spec[:version], Artifact.hash_to_file_name(spec)) end # Returns a hash of all the remote repositories. The key is the repository # identifier, and the value is the repository base URL. @@ -208,14 +211,14 @@ # Attempts to download the artifact from one of the remote repositories # and store it in the local repository. Returns the path if downloaded, # otherwise raises an exception. def download(spec) - spec = Artifact.spec_to_hash(spec) unless Hash === spec + spec = Artifact.to_hash(spec) unless Hash === spec path = locate(spec) - puts "Downloading #{Artifact.hash_to_spec(spec)}" if Rake.application.options.trace + puts "Downloading #{Artifact.to_spec(spec)}" if Rake.application.options.trace return path if remote.any? do |repo_id, repo_url| begin rel_path = spec[:group].gsub(".", "/") + "/#{spec[:id]}/#{spec[:version]}/#{Artifact.hash_to_file_name(spec)}" Transports.perform URI.parse(repo_url.to_s) do |http| @@ -230,31 +233,42 @@ rescue Exception=>error warn error if Rake.application.options.trace false end end - fail "Failed to download #{Artifact.hash_to_spec(spec)}, tried the following repositories:\n#{repositories.remote.values.join("\n")}" + fail "Failed to download #{Artifact.to_spec(spec)}, tried the following repositories:\n#{repositories.remote.values.join("\n")}" end - # Specifies deployment target for all artifacts generated by this project. + # Specifies the deployment repository. Accepts a hash with the different + # repository settings (e.g. url, username, password). Anything else is + # interepted as the URL. # # For example: - # repositories.deploy_to = "sftp://example.com/var/www/maven/" + # repositories.deploy_to = { :url=>"sftp://example.com/var/www/maven/", + # :username="john", :password=>"secret" } # or: # repositories.deploy_to = "sftp://john:secret@example.com/var/www/maven/" def deploy_to=(options) options = { :url=>options } unless Hash === options @deploy_to = options end + # Returns the current deployment repository configuration. This is a more + # convenient way to specify deployment in the Rakefile, and override it + # locally. For example: + # # Rakefile + # repositories.deploy_to[:url] ||= "sftp://example.com" + # # buildr.rb + # repositories.deploy_to[:url] = "sftp://acme.org" def deploy_to() - @deploy_to || {} + @deploy_to ||= {} end end - # Returns a global object for setting local and remote repositories. + + # Returns a global object for setting local, remote and deploy repositories. # See Repositories. def repositories() Repositories.instance end @@ -274,18 +288,17 @@ # # To specify an artifact and the means for creating it: # download(artifact("dojo:dojo-widget:zip:2.0")=> # "http://download.dojotoolkit.org/release-2.0/dojo-2.0-widget.zip") def artifact(spec, &block) - spec = Artifact.spec_to_hash(spec) + spec = Artifact.to_hash(spec) unless task = Artifact.lookup(spec) task = Artifact.define_task(repositories.locate(spec)) Artifact.register(task) end task.apply_spec spec - task.enhance(&block) if block_given? - task + task.enhance &block end # Creates multiple artifacts from a set of specifications and returns # an array of tasks. # @@ -318,21 +331,21 @@ def artifacts(*specs) specs.inject([]) do |set, spec| case spec when Hash set |= [artifact(spec)] - when /:.*:.*:/ + when /:/ set |= [artifact(spec)] when String set |= [file(spec)] when Rake::Task set |= [spec] when Project set |= artifacts(spec.packages) when Array set |= artifacts(*spec) else - fail "Invalid artifact specification: #{spec || 'nil'}" + fail "Invalid artifact specification: #{spec.to_s || 'nil'}" end set end end