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 2007/11/14 00:44:17 UTC
svn commit: r594720 [5/9] - in /incubator/buildr/trunk: ./ bin/ doc/
doc/css/ doc/images/ doc/pages/ lib/ lib/buildr/ lib/buildr/jetty/
lib/core/ lib/java/ lib/tasks/ test/
Added: incubator/buildr/trunk/lib/java/artifact.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/java/artifact.rb?rev=594720&view=auto
==============================================================================
--- incubator/buildr/trunk/lib/java/artifact.rb (added)
+++ incubator/buildr/trunk/lib/java/artifact.rb Tue Nov 13 15:44:11 2007
@@ -0,0 +1,738 @@
+require "core/project"
+require "core/transports"
+require "builder"
+
+module Buildr
+
+ desc "Download all artifacts"
+ task "artifacts"
+
+ # Mixin with a task to make it behave like an artifact. Implemented by the packaging tasks.
+ #
+ # An artifact has an identifier, group identifier, type, version number and
+ # optional classifier. All can be used to locate it in the local repository,
+ # download from or upload to a remote repository.
+ #
+ # The #to_spec and #to_hash methods allow it to be used everywhere an artifact is
+ # accepted.
+ module ActsAsArtifact
+
+ ARTIFACT_ATTRIBUTES = [:group, :id, :type, :classifier, :version]
+
+ class << self
+ private
+ def included(mod)
+ mod.extend self
+ end
+ end
+
+ # The artifact identifier.
+ attr_reader :id
+ # The group identifier.
+ attr_reader :group
+ # The file type. (Symbol)
+ attr_reader :type
+ # The version number.
+ attr_reader :version
+ # Optional artifact classifier.
+ attr_reader :classifier
+
+ def snapshot?
+ version =~ /-SNAPSHOT$/
+ end
+
+ # :call-seq:
+ # to_spec_hash() => Hash
+ #
+ # Returns the artifact specification as a hash. For example:
+ # com.example:app:jar:1.2
+ # becomes:
+ # { :group=>"com.example",
+ # :id=>"app",
+ # :type=>:jar,
+ # :version=>"1.2" }
+ def to_spec_hash()
+ base = { :group=>group, :id=>id, :type=>type, :version=>version }
+ classifier.blank? ? base : base.merge(:classifier=>classifier)
+ end
+ alias_method :to_hash, :to_spec_hash
+
+ # :call-seq:
+ # to_spec() => String
+ #
+ # 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
+
+ # :call-seq:
+ # pom() => Artifact
+ #
+ # Convenience method that returns a POM artifact.
+ def pom()
+ return self if type == :pom
+ Buildr.artifact(:group=>group, :id=>id, :version=>version, :type=>:pom, :classifier=>classifier)
+ end
+
+ # :call-seq:
+ # pom_xml() => string
+ #
+ # Creates POM XML for this artifact.
+ def pom_xml()
+ xml = Builder::XmlMarkup.new(:indent=>2)
+ xml.instruct!
+ xml.project do
+ xml.modelVersion "4.0.0"
+ xml.groupId group
+ xml.artifactId id
+ xml.version version
+ xml.classifier classifier if classifier
+ end
+ end
+
+ # :call-seq:
+ # upload()
+ # upload(url)
+ # upload(options)
+ #
+ # Uploads the artifact, its POM and digital signatures to remote server.
+ #
+ # In the first form, uses the upload options specified by repositories.release_to.
+ # In the second form, uses a URL that includes all the relevant information.
+ # In the third form, uses a hash with the options :url, :username, :password,
+ # and :permissions. All but :url are optional.
+ def upload(upload_to = nil)
+ # Where do we release to?
+ upload_to ||= Buildr.repositories.release_to
+ upload_to = { :url=>upload_to } unless Hash === upload_to
+ raise ArgumentError, "Don't know where to upload, perhaps you forgot to set repositories.release_to" if upload_to[:url].blank?
+
+ # Set the upload URI, including mandatory slash (we expect it to be the base directory).
+ # Username/password may be part of URI, or separate entities.
+ uri = URI.parse(upload_to[:url].clone)
+ uri.path = uri.path + "/" unless uri.path[-1] == "/"
+ uri.user = upload_to[:username] if upload_to[:username]
+ uri.password = upload_to[:password] if upload_to[:password]
+
+ # Upload artifact relative to base URL, need to create path before uploading.
+ puts "Deploying #{to_spec}" if verbose
+ path = group.gsub(".", "/") + "/#{id}/#{version}/#{File.basename(name)}"
+ URI.upload uri + path, name, :permissions=>upload_to[:permissions]
+ end
+
+ protected
+
+ # Apply specification to this artifact.
+ def apply_spec(spec)
+ spec = Artifact.to_hash(spec)
+ ARTIFACT_ATTRIBUTES.each { |key| instance_variable_set("@#{key}", spec[key]) }
+ self
+ end
+
+ def group_path
+ group.gsub(".", "/")
+ end
+
+ end
+
+
+ # A file task referencing an artifact in the local repository.
+ #
+ # This task includes all the artifact attributes (group, id, version, etc). It points
+ # to the artifact's path in the local repository. When invoked, it will download the
+ # artifact into the local repository if the artifact does not already exist.
+ #
+ # Note: You can enhance this task to create the artifact yourself, e.g. download it from
+ # a site that doesn't have a remote repository structure, copy it from a different disk, etc.
+ class Artifact < Rake::FileCreationTask
+
+ # The default artifact type.
+ DEFAULT_TYPE = :jar
+
+ include ActsAsArtifact
+
+ class << self
+
+ # :call-seq:
+ # lookup(spec) => Artifact
+ #
+ # Lookup a previously registered artifact task based on its specification (String or Hash).
+ def lookup(spec)
+ @artifacts ||= {}
+ @artifacts[to_spec(spec)]
+ end
+
+ # :call-seq:
+ # list() => specs
+ #
+ # Returns an array of specs for all the registered artifacts. (Anything created from artifact, or package).
+ def list()
+ @artifacts.keys
+ end
+
+ # :call-seq:
+ # register(artifacts) => artifacts
+ #
+ # Register an artifact task(s) for later lookup (see #lookup).
+ def register(*tasks)
+ @artifacts ||= {}
+ fail "You can only register an artifact task, one of the arguments is not a Task that responds to to_spec()" unless
+ tasks.all? { |task| task.respond_to?(:to_spec) && task.respond_to?(:invoke) }
+ tasks.each { |task| @artifacts[task.to_spec] = task }
+ tasks
+ end
+
+ # :call-seq:
+ # to_hash(spec_hash) => spec_hash
+ # to_hash(spec_string) => spec_hash
+ # to_hash(artifact) => spec_hash
+ #
+ # 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 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.
+ def to_hash(spec)
+ if spec.respond_to?(:to_spec)
+ to_hash spec.to_spec
+ elsif Hash === spec
+ rake_check_options spec, :id, :group, :type, :classifier, :version
+ # Sanitize the hash and check it's valid.
+ spec = ARTIFACT_ATTRIBUTES.inject({}) { |h, k| h[k] = spec[k].to_s if 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] = spec[:type].blank? ? DEFAULT_TYPE : spec[:type].to_sym
+ 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 "Expecting a String, Hash or object that responds to to_spec"
+ end
+ end
+
+ # :call-seq:
+ # to_spec(spec_hash) => spec_string
+ #
+ # Convert a hash back to a spec string. This method accepts
+ # a string, hash or any object that responds to to_spec.
+ 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_TYPE}#{classifier}#{version}"
+ end
+
+ # :call-seq:
+ # hash_to_file_name(spec_hash) => file_name
+ #
+ # Convert a hash spec to a file name.
+ 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_TYPE}"
+ end
+
+ end
+
+ def initialize(*args) #:nodoc:
+ super
+ enhance do |task|
+ # Default behavior: download the artifact from one of the remote repositories
+ # if the file does not exist. But this default behavior is counter productive
+ # 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.
+ task.enhance do
+ unless File.exist?(name)
+ puts "Downloading #{to_spec}" if verbose
+ download
+ pom.invoke rescue nil if pom && pom != self
+ end
+ end
+ end
+ end
+
+ # :call-seq:
+ # from(path) => self
+ #
+ # Use this when you want to install or upload an artifact from a given file, for example:
+ # test = artifact('group:id:jar:1.0').from('test.jar')
+ # install test
+ # See also Buildr#install and Buildr#deploy.
+ def from(path)
+ path = File.expand_path(path.to_s)
+ enhance [path] do
+ verbose false do
+ mkpath File.dirname(name)
+ pom.invoke unless type == :pom
+ cp path, name
+ puts "Installed #{path} as #{to_spec}" if verbose
+ end
+ end
+ unless type == :pom
+ pom.enhance do
+ verbose false do
+ mkpath File.dirname(pom.name)
+ File.open(pom.name, 'w') { |file| file.write pom.pom_xml }
+ end
+ end
+ end
+ self
+ end
+
+ protected
+
+ # :call-seq:
+ # download()
+ #
+ # Downloads an artifact from one of the remote repositories, and stores it in the local
+ # repository. Accepts a String or Hash artifact specification, and returns a path to the
+ # artifact in the local repository. Raises an exception if the artifact is not found.
+ #
+ # This method attempts to download the artifact from each repository in the order in
+ # which they are returned from #remote, until successful. It always downloads the POM first.
+ def download()
+ puts "Downloading #{to_spec}" if Rake.application.options.trace
+ remote = Buildr.repositories.remote.map { |repo_url| URI === repo_url ? repo_url : URI.parse(repo_url) }
+ remote = remote.each { |repo_url| repo_url.path += "/" unless repo_url.path[-1] == "/" }
+ fail "No remote repositories defined!" if remote.empty?
+ exact_success = remote.find do |repo_url|
+ begin
+ path = "#{group_path}/#{id}/#{version}/#{File.basename(name)}"
+ URI.download repo_url + path, name
+ true
+ rescue URI::NotFoundError
+ false
+ rescue Exception=>error
+ puts error if verbose
+ puts error.backtrace.join("\n") if Rake.application.options.trace
+ false
+ end
+ end
+
+ if exact_success
+ return
+ elsif snapshot?
+ download_m2_snapshot(remote)
+ else
+ fail_download(remote)
+ end
+ end
+
+ def download_m2_snapshot(remote_uris)
+ remote_uris.find do |repo_url|
+ snapshot_url = current_snapshot_repo_url(repo_url)
+ if snapshot_url
+ begin
+ URI.download snapshot_url, name
+ rescue URI::NotFoundError
+ false
+ end
+ else
+ false
+ end
+ end or fail_download(remote_uris)
+ end
+
+ def current_snapshot_repo_url(repo_url)
+ begin
+ metadata_path = "#{group_path}/#{id}/#{version}/maven-metadata.xml"
+ metadata_xml = StringIO.new
+ URI.download repo_url + metadata_path, metadata_xml
+ metadata = REXML::Document.new(metadata_xml.string).root
+ timestamp = REXML::XPath.first(metadata, "//timestamp").text
+ build_number = REXML::XPath.first(metadata, "//buildNumber").text
+ snapshot_of = version[0, version.size - 9]
+ repo_url + "#{group_path}/#{id}/#{version}/#{id}-#{snapshot_of}-#{timestamp}-#{build_number}.#{type}"
+ rescue URI::NotFoundError
+ nil
+ end
+ end
+
+ def fail_download(remote_uris)
+ fail "Failed to download #{to_spec}, tried the following repositories:\n#{remote_uris.join("\n")}"
+ end
+ end
+
+
+ # Holds the path to the local repository, URLs for remote repositories, and settings for release server.
+ #
+ # You can access this object from the #repositories method. For example:
+ # puts repositories.local
+ # repositories.remote << "http://example.com/repo"
+ # repositories.release_to = "sftp://example.com/var/www/public/repo"
+ class Repositories
+ include Singleton
+
+ # :call-seq:
+ # local() => path
+ #
+ # Returns the path to the local repository.
+ #
+ # The default path is .m2/repository relative to the home directory.
+ def local()
+ @local ||= ENV["local_repo"] || File.join(Gem::user_home, ".m2/repository")
+ end
+
+ # :call-seq:
+ # local = path
+ #
+ # Sets the path to the local repository.
+ #
+ # The best place to set the local repository path is from a buildr.rb file
+ # located in your home directory. That way all your projects will share the same
+ # path, without affecting other developers collaborating on these projects.
+ def local=(dir)
+ @local = dir ? File.expand_path(dir) : nil
+ end
+
+ # :call-seq:
+ # locate(spec) => path
+ #
+ # Locates an artifact in the local repository based on its specification, and returns
+ # a file path.
+ #
+ # 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.to_hash(spec)
+ File.join(local, spec[:group].split("."), spec[:id], spec[:version], Artifact.hash_to_file_name(spec))
+ end
+
+ # :call-seq:
+ # remote() => Array
+ #
+ # Returns an array of all the remote repository URLs.
+ #
+ # When downloading artifacts, repositories are accessed in the order in which they appear here.
+ # The best way is to add repositories individually, for example:
+ # repositories.remote << "http://example.com/repo"
+ def remote()
+ @remote ||= []
+ end
+
+ # :call-seq:
+ # remote = Array
+ # remote = url
+ # remote = nil
+ #
+ # With a String argument, clears the array and set it to that single URL.
+ #
+ # With an Array argument, clears the array and set it to these specific URLs.
+ #
+ # With nil, clears the array.
+ def remote=(urls)
+ case urls
+ when nil
+ @remote = nil
+ when Array
+ @remote = urls.dup
+ else
+ @remote = [urls.to_s]
+ end
+ end
+
+ # *Deprecated* Please use options.proxy.http instead of repositories.proxy.
+ def proxy()
+ warn_deprecated "Please use options.proxy.http instead of repositories.proxy"
+ Buildr.options.proxy.http
+ end
+
+ # *Deprecated* Please use options.proxy.http = <url> instead of repositories.proxy.
+ def proxy=(proxy)
+ warn_deprecated "Please use options.proxy.http = <url> instead of repositories.proxy"
+ Buildr.options.proxy.http = proxy
+ end
+
+ # *Deprecated* Just create an artifact and invoke it.
+ def download(spec)
+ warn_deprecated "Just create and artifact and invoke it."
+ spec = Artifact.to_hash(spec) unless Hash === spec
+ filename = locate(spec)
+
+ puts "Downloading #{Artifact.to_spec(spec)}" if Rake.application.options.trace
+ return filename if remote.any? do |repo_url|
+ repo_url = URI.parse(repo_url) unless URI === repo_url
+ repo_url.path += "/" unless repo_url.path[-1] == "/"
+ begin
+ path = spec[:group].gsub(".", "/") +
+ "/#{spec[:id]}/#{spec[:version]}/#{Artifact.hash_to_file_name(spec)}"
+ mkpath File.dirname(filename), :verbose=>false
+ # We absolutely need the POM, so make sure we download it before the artifact
+ # (unless the artifact is a POM).
+ URI.download repo_url + path.ext("pom"), filename.ext("pom") unless type == :pom
+ URI.download repo_url + path, filename
+ true
+ rescue URI::NotFoundError
+ false
+ rescue Exception=>error
+ puts error if verbose
+ puts error.backtrace.join("\n") if Rake.application.options.trace
+ false
+ end
+ end
+ fail "Failed to download #{Artifact.to_spec(spec)}, tried the following repositories:\n#{remote.join("\n")}"
+ end
+
+ # :call-seq:
+ # release_to = url
+ # release_to = hash
+ #
+ # Specifies the release server. Accepts a Hash with different repository settings
+ # (e.g. url, username, password), or a String to only set the repository URL.
+ #
+ # Besides the URL, all other settings depend on the transport protocol in use.
+ #
+ # For example:
+ # repositories.release_to = "sftp://john:secret@example.com/var/www/repo/"
+ # repositories.release_to = { :url=>"sftp://example.com/var/www/repo/",
+ # :username="john", :password=>"secret" }
+ def release_to=(options)
+ options = { :url=>options } unless Hash === options
+ @release_to = options
+ end
+
+ # :call-seq:
+ # release_to() => hash
+ #
+ # Returns the current release server setting as a Hash. This is a more convenient way to
+ # configure the settings, as it allows you to specify the settings progressively.
+ #
+ # For example, the Buildfile will contain the repository URL used by all developers:
+ # repositories.release_to[:url] ||= "sftp://example.com/var/www/repo"
+ # Your private buildr.rb will contain your credentials:
+ # repositories.release_to[:username] = "john"
+ # repositories.release_to[:password] = "secret"
+ def release_to()
+ @release_to ||= {}
+ end
+
+ # *Deprecated* See release_to.
+ def deploy_to=(options)
+ warn_deprecated "Please use release_to instead."
+ self.release_to = options
+ end
+
+ # *Deprecated* See release_to.
+ def deploy_to()
+ warn_deprecated "Please use release_to instead."
+ self.release_to
+ end
+
+ end
+
+ # :call-seq:
+ # repositories() => Repositories
+ #
+ # Returns an object you can use for setting the local repository path, remote repositories
+ # URL and release server settings.
+ #
+ # See Repositories.
+ def repositories()
+ Repositories.instance
+ end
+
+ # :call-seq:
+ # artifact(spec) => Artifact
+ # artifact(spec) { |task| ... } => Artifact
+ #
+ # Creates a file task to download and install the specified artifact in the local repository.
+ #
+ # You can use a String or a Hash for the artifact specification. The file task will point at
+ # the artifact's path inside the local repository. You can then use this tasks as a prerequisite
+ # for other tasks.
+ #
+ # This task will download and install the artifact only once. In fact, it will download and
+ # install the artifact if the artifact does not already exist. You can enhance it if you have
+ # a different way of creating the artifact in the local repository. See Artifact for more details.
+ #
+ # For example, to specify an artifact:
+ # artifact("log4j:log4j:jar:1.1")
+ #
+ # To use the artifact in a task:
+ # compile.with artifact("log4j:log4j:jar:1.1")
+ #
+ # 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) #:yields:task
+ spec = Artifact.to_hash(spec)
+ unless task = Artifact.lookup(spec)
+ task = Artifact.define_task(repositories.locate(spec))
+ task.send :apply_spec, spec
+ Rake::Task["rake:artifacts"].enhance [task]
+ Artifact.register(task)
+ end
+ task.enhance &block
+ end
+
+ # :call-seq:
+ # artifacts(*spec) => artifacts
+ #
+ # Handles multiple artifacts at a time. This method is the plural equivalent of
+ # #artifacts, but can do more things.
+ #
+ # You can pass any number of arguments, each of which can be:
+ # * An artifact specification (String or Hash). Returns the appropriate Artifact task.
+ # * An artifact of any other task. Returns the task as is.
+ # * A project. Returns all artifacts created (packaged) by that project.
+ # * A string. Returns that string, assumed to be a file name.
+ # * An array of artifacts or a Struct.
+ #
+ # For example, handling a collection of artifacts:
+ # xml = [ xerces, xalan, jaxp ]
+ # ws = [ axis, jax-ws, jaxb ]
+ # db = [ jpa, mysql, sqltools ]
+ # artifacts(xml, ws, db)
+ #
+ # Using artifacts created by a project:
+ # artifact project("my-app") # All packages
+ # artifact project("mu-app").package(:war) # Only the WAR
+ def artifacts(*specs)
+ specs.flatten.inject([]) do |set, spec|
+ case spec
+ when Hash
+ set |= [artifact(spec)]
+ when /([^:]+:){2,4}/ # A spec as opposed to a file name.
+ set |= [artifact(spec)]
+ when String # Must always expand path.
+ set |= [File.expand_path(spec)]
+ when Project
+ set |= artifacts(spec.packages)
+ when Rake::Task
+ set |= [spec]
+ when Struct
+ set |= artifacts(spec.values)
+ else
+ fail "Invalid artifact specification in: #{specs.to_s}"
+ end
+ end
+ end
+
+ def transitive(*specs)
+ specs.flatten.inject([]) do |set, spec|
+ case spec
+ when /([^:]+:){2,4}/ # A spec as opposed to a file name.
+ artifact = artifact(spec)
+ set |= [artifact] unless artifact.type == :pom
+ set |= POM.load(artifact.pom).dependencies.map { |spec| artifact(spec) }
+ when Hash
+ set |= [transitive(spec)]
+ when String # Must always expand path.
+ set |= transitive(file(File.expand_path(spec)))
+ when Project
+ set |= transitive(spec.packages)
+ when Rake::Task
+ set |= spec.respond_to?(:to_spec) ? transitive(spec.to_spec) : [spec]
+ when Struct
+ set |= transitive(spec.values)
+ else
+ fail "Invalid artifact specification in: #{specs.to_s}"
+ end
+ end
+ end
+
+ # :call-seq:
+ # groups(ids, :under=>group_name, :version=>number) => artifacts
+ #
+ # Convenience method for defining multiple artifacts that belong to the same group and version.
+ # Accepts multiple artifact identifiers follows by two hash values:
+ # * :under -- The group identifier
+ # * :version -- The version number
+ #
+ # For example:
+ # group "xbean", "xbean_xpath", "xmlpublic", :under=>"xmlbeans", :version=>"2.1.0"
+ # Or:
+ # group %w{xbean xbean_xpath xmlpublic}, :under=>"xmlbeans", :version=>"2.1.0"
+ def group(*args)
+ hash = args.pop
+ args.flatten.map { |id| artifact :group=>hash[:under], :version=>hash[:version], :id=>id }
+ end
+
+ # :call-seq:
+ # install(artifacts)
+ #
+ # Installs the specified artifacts in the local repository as part of the install task.
+ #
+ # You can use this to install various files in the local repository, for example:
+ # install artifact('group:id:jar:1.0').from('some_jar.jar')
+ # $ buildr install
+ def install(*args, &block)
+ artifacts = artifacts(args)
+ raise ArgumentError, 'This method can only install artifacts' unless artifacts.all? { |f| f.respond_to?(:to_spec) }
+ all = (artifacts + artifacts.map { |artifact| artifact.pom }).uniq
+ task('install').tap do |task|
+ task.enhance all, &block
+ task 'uninstall' do
+ verbose false do
+ all.map(&:to_s ).each { |file| rm file if File.exist?(file) }
+ end
+ end
+ end
+ end
+
+ # :call-seq:
+ # upload(artifacts)
+ #
+ # Uploads the specified artifacts to the release server as part of the upload task.
+ #
+ # You can use this to upload various files to the release server, for example:
+ # upload artifact('group:id:jar:1.0').from('some_jar.jar')
+ # $ buildr upload
+ def upload(*args, &block)
+ artifacts = artifacts(args)
+ raise ArgumentError, 'This method can only upload artifacts' unless artifacts.all? { |f| f.respond_to?(:to_spec) }
+ all = (artifacts + artifacts.map { |artifact| artifact.pom }).uniq
+ task('upload').tap do |task|
+ task.enhance &block if block
+ task.enhance all do
+ all.each { |artifact| artifact.upload }
+ end
+ end
+ end
+
+ # *Deprecated* For artifact, call it's upload method; for anything else, use URI.upload.
+ def deploy(*args)
+ warn_deprecated "If it's an artifact, call it's upload method directly. Otherwise, use URI.upload."
+ # Where do we release to?
+ options = Hash === args.last ? args.pop : {}
+ deploy_to = options[:url] ? options : repositories.release_to
+ fail "Don't know where to deploy, perhaps you forgot to set repositories.deploy_to" if deploy_to[:url].blank?
+
+ args.flatten.each { |arg| arg.invoke if arg.respond_to?(:invoke) }
+ # Set the upload URI, including mandatory slash (we expect it to be the base directory).
+ # Username/password may be part of URI, or separate entities.
+ uri = URI.parse(deploy_to[:url].clone)
+ uri.path = uri.path + "/" unless uri.path[-1] == "/"
+ uri.user = deploy_to[:username] if deploy_to[:username]
+ uri.password = deploy_to[:password] if deploy_to[:password]
+
+ args.each do |arg|
+ if arg.respond_to?(:to_spec)
+ # Upload artifact relative to base URL, need to create path before uploading.
+ puts "Deploying #{arg.to_spec}" if verbose
+ spec = arg.to_spec_hash
+ path = spec[:group].gsub(".", "/") + "/#{spec[:id]}/#{spec[:version]}/" + Artifact.hash_to_file_name(spec)
+ URI.upload uri + path, arg.to_s, :permissions=>deploy_to[:permissions]
+ else
+ # Upload file to URL.
+ puts "Deploying #{arg}" if verbose
+ path = File.basename(args.to_s)
+ path = File.join(options[:path], path) if options[:path]
+ URI.upload uri + path, arg.to_s, :permissions=>deploy_to[:permissions]
+ end
+ end
+ end
+
+end
Added: incubator/buildr/trunk/lib/java/compile.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/java/compile.rb?rev=594720&view=auto
==============================================================================
--- incubator/buildr/trunk/lib/java/compile.rb (added)
+++ incubator/buildr/trunk/lib/java/compile.rb Tue Nov 13 15:44:11 2007
@@ -0,0 +1,589 @@
+require "core/project"
+require "core/build"
+require "core/common"
+require "java/artifact"
+require "java/java"
+
+module Buildr
+ module Java
+
+ # Wraps Javac in a task that does all the heavy lifting.
+ #
+ # Accepts multiple source directories that are invoked as prerequisites before compilation.
+ # You can pass a task as a source directory, e.g. compile.from(apt).
+ #
+ # Likewise, classpath dependencies are invoked before compiling. All classpath dependencies
+ # are evaluated as #artifacts, so you can pass artifact specifications and even projects.
+ #
+ # Creates a file task for the target directory, so executing that task as a dependency will
+ # execute the compile task first.
+ #
+ # Compiler options are inherited form a parent task, e.g. the foo:bar:compile task inherits
+ # its options from the foo:compile task. Even if foo is an empty project that does not compile
+ # any classes itself, you can use it to set compile options for all its sub-projects.
+ #
+ # Normally, the project will take care of setting the source and target directory, and you
+ # only need to set options and classpath dependencies. See Project#compile.
+ class CompileTask < Rake::Task
+
+ # Compiler options, accessible from CompileTask#options.
+ #
+ # Supported options are:
+ # - warnings -- Generate warnings if true (opposite of -nowarn).
+ # - deprecation -- Output source locations where deprecated APIs are used.
+ # - source -- Source compatibility with specified release.
+ # - target -- Class file compatibility with specified release.
+ # - lint -- Value to pass to xlint argument. Use true to enable default lint
+ # options, or pass a specific setting as string or array of strings.
+ # - debug -- Generate debugging info.
+ # - other -- Array of options to pass to the Java compiler as is.
+ #
+ # For example:
+ # compile.options.warnings = true
+ # compile.options.source = options.target = "1.6"
+ class Options
+
+ include InheritedAttributes
+
+ OPTIONS = [:warnings, :deprecation, :source, :target, :lint, :debug, :other]
+
+ # Generate warnings (opposite of -nowarn).
+ attr_accessor :warnings
+ inherited_attr(:warnings) { verbose }
+ # Output source locations where deprecated APIs are used.
+ attr_accessor :deprecation
+ inherited_attr :deprecation, false
+ # Provide source compatibility with specified release.
+ attr_accessor :source
+ inherited_attr :source
+ # Generate class files for specific VM version.
+ attr_accessor :target
+ inherited_attr :target
+ # Values to pass to Xlint: string or array. Use true to enable
+ # Xlint with no values.
+ attr_accessor :lint
+ inherited_attr :lint, false
+ # Generate all debugging info.
+ attr_accessor :debug
+ inherited_attr(:debug) { Buildr.options.debug }
+ # Array of arguments passed to the Java compiler as is.
+ attr_accessor :other
+ inherited_attr :other
+
+ def initialize(parent = nil) #:nodoc:
+ @parent = parent
+ end
+
+ attr_reader :parent # :nodoc:
+
+ # Resets all the options.
+ def clear()
+ OPTIONS.each { |name| send "#{name}=", nil }
+ end
+
+ def to_s() #:nodoc:
+ OPTIONS.inject({}){ |hash, name| hash[name] = send(name) ; hash }.reject{ |name,value| value.nil? }.inspect
+ end
+
+ # Returns Javac command line arguments from the set of options.
+ def javac_args()
+ args = []
+ args << "-nowarn" unless warnings
+ args << "-verbose" if Rake.application.options.trace
+ args << "-g" if debug
+ args << "-deprecation" if deprecation
+ args << "-source" << source.to_s if source
+ args << "-target" << target.to_s if target
+ case lint
+ when Array
+ args << "-Xlint:#{lint.join(',')}"
+ when String
+ args << "-Xlint:#{lint}"
+ when true
+ args << "-Xlint"
+ end
+ args.concat(other.to_a) if other
+ args
+ end
+
+ end
+
+
+ def initialize(*args) #:nodoc:
+ super
+ parent = Project.task_in_parent_project(name)
+ if parent && parent.respond_to?(:options)
+ @options = Options.new(parent.options)
+ else
+ @options = Options.new
+ end
+ @sources = []
+ @classpath = []
+
+ enhance do |task|
+ mkpath target.to_s, :verbose=>false
+ Java.javac source_files.keys, :sourcepath=>sources.map(&:to_s).select { |source| File.directory?(source) }.uniq,
+ :classpath=>classpath, :output=>target, :javac_args=>options.javac_args, :name=>task.name
+ # By touching the target we let other tasks know we did something,
+ # and also prevent recompiling again for classpath dependencies.
+ touch target.to_s, :verbose=>false
+ end
+ end
+
+ # Source directories and files to compile.
+ attr_accessor :sources
+
+ # :call-seq:
+ # from(*sources) => self
+ #
+ # Adds source directories and files to compile, and returns self.
+ #
+ # For example:
+ # compile.from("src/java").into("classes").with("module1.jar")
+ def from(*sources)
+ @sources |= sources.flatten
+ self
+ end
+
+ # Classpath dependencies.
+ attr_accessor :classpath
+
+ # :call-seq:
+ # with(*artifacts) => self
+ #
+ # Adds files and artifacts as classpath dependencies, and returns self.
+ #
+ # Calls #artifacts on the arguments, so you can pass artifact specifications,
+ # tasks, projects, etc. Use this rather than setting the classpath directly.
+ #
+ # For example:
+ # compile.with("module1.jar", "log4j:log4j:jar:1.0", project("foo"))
+ def with(*specs)
+ @classpath |= Buildr.artifacts(specs.flatten).uniq
+ self
+ end
+
+ # The target directory for the generated class files.
+ attr_reader :target
+
+ # :call-seq:
+ # into(path) => self
+ #
+ # Sets the target directory and returns self. This will also set the compile task
+ # as a prerequisite to a file task on the target directory.
+ #
+ # For example:
+ # compile(src_dir).into(target_dir).with(artifacts)
+ # Both compile.invoke and file(target_dir).invoke will compile the source files.
+ def into(path)
+ path = File.expand_path(path.to_s)
+ @target = file(path).enhance([self]) unless @target && @target.to_s == path
+ self
+ end
+
+ # Returns the compiler options.
+ attr_reader :options
+
+ # :call-seq:
+ # using(options) => self
+ #
+ # Sets the compiler options from a hash and returns self.
+ #
+ # For example:
+ # compile.using(:warnings=>true, :source=>"1.5")
+ def using(*args)
+ args.pop.each { |key, value| options.send "#{key}=", value } if Hash === args.last
+ args.each { |key| options.send "#{key}=", value = true }
+ self
+ end
+
+ def timestamp() #:nodoc:
+ # If we compiled successfully, then the target directory reflects that.
+ # If we didn't, see needed?
+ target ? target.timestamp : Rake::EARLY
+ end
+
+ def needed?() #:nodoc:
+ return false if source_files.empty?
+ return true unless File.exist?(target.to_s)
+ return true if source_files.any? { |j, c| !File.exist?(c) || File.stat(j).mtime > File.stat(c).mtime }
+ oldest = source_files.map { |j, c| File.stat(c).mtime }.min
+ return classpath.any? { |path| application[path].timestamp > oldest }
+ end
+
+ def prerequisites() #:nodoc:
+ super + classpath + sources
+ end
+
+ def invoke_prerequisites() #:nodoc:
+ prerequisites.each { |n| application[n, @scope].invoke }
+ end
+
+ # Returns the files to compile. This list is derived from the list of sources,
+ # expanding directories into files, and includes only source files that are
+ # newer than the corresponding class file. Includes all files if one or more
+ # classpath dependency has been updated.
+ def source_files()
+ @source_files ||= @sources.map(&:to_s).inject({}) do |map, source|
+ raise "Compile task #{name} has source files, but no target directory" unless target
+ target_dir = target.to_s
+ if File.directory?(source)
+ base = Pathname.new(source)
+ FileList["#{source}/**/*.java"].reject { |file| File.directory?(file) }.
+ each { |file| map[file] = File.join(target_dir, Pathname.new(file).relative_path_from(base).to_s.ext('.class')) }
+ else
+ map[source] = File.join(target_dir, File.basename(source).ext('.class'))
+ end
+ map
+ end
+ end
+
+ end
+
+
+ # The resources task is executed by the compile task to copy resource files over
+ # to the target directory. You can enhance this task in the normal way, but mostly
+ # you will use the task's filter.
+ #
+ # For example:
+ # resources.filter.using "Copyright"=>"Acme Inc, 2007"
+ class ResourcesTask < Rake::Task
+
+ # Returns the filter used to copy resources over. See Buildr::Filter.
+ attr_reader :filter
+
+ def initialize(*args) #:nodoc:
+ super
+ @filter = Buildr::Filter.new
+ enhance { filter.run unless filter.sources.empty? }
+ end
+
+ # :call-seq:
+ # include(*files) => self
+ #
+ # Includes the specified files in the filter and returns self.
+ def include(*files)
+ filter.include *files
+ self
+ end
+
+ # :call-seq:
+ # exclude(*files) => self
+ #
+ # Excludes the specified files in the filter and returns self.
+ def exclude(*files)
+ filter.exclude *files
+ self
+ end
+
+ # :call-seq:
+ # from(*sources) => self
+ #
+ # Adds additional directories from which to copy resources.
+ #
+ # For example:
+ # resources.from _("src/etc")
+ def from(*sources)
+ filter.from *sources
+ self
+ end
+
+ # *Deprecated* Use #sources instead.
+ def source()
+ warn_deprecated "Please use sources instead."
+ filter.source
+ end
+
+ # Returns the list of source directories (each being a file task).
+ def sources()
+ filter.sources
+ end
+
+ # :call-seq:
+ # target() => task
+ #
+ # Returns the filter's target directory as a file task.
+ def target()
+ filter.target
+ end
+
+ def prerequisites() #:nodoc:
+ super + filter.sources.flatten
+ end
+
+ end
+
+
+ # A convenient task for creating Javadocs from the project's compile task. Minimizes all
+ # the hard work to calling #from and #using.
+ #
+ # For example:
+ # javadoc.from(projects("myapp:foo", "myapp:bar")).using(:windowtitle=>"My App")
+ # Or, short and sweet:
+ # desc "My App"
+ # define "myapp" do
+ # . . .
+ # javadoc projects("myapp:foo", "myapp:bar")
+ # end
+ class JavadocTask < Rake::Task
+
+ def initialize(*args) #:nodoc:
+ super
+ @options = {}
+ @classpath = []
+ @sourcepath = []
+ @files = FileList[]
+ enhance do |task|
+ rm_rf target.to_s, :verbose=>false
+ Java.javadoc source_files, options.merge(:classpath=>classpath, :sourcepath=>sourcepath, :name=>name, :output=>target.to_s)
+ touch target.to_s, :verbose=>false
+ end
+ end
+
+ # The target directory for the generated Javadoc files.
+ attr_reader :target
+
+ # :call-seq:
+ # into(path) => self
+ #
+ # Sets the target directory and returns self. This will also set the Javadoc task
+ # as a prerequisite to a file task on the target directory.
+ #
+ # For example:
+ # package :zip, :classifier=>"docs", :include=>javadoc.target
+ def into(path)
+ path = File.expand_path(path.to_s)
+ @target = file(path).enhance([self]) unless @target && @target.to_s == path
+ self
+ end
+
+ # :call-seq:
+ # include(*files) => self
+ #
+ # Includes additional source files and directories when generating the documentation
+ # and returns self. When specifying a directory, includes all .java files in that directory.
+ def include(*files)
+ @files.include *files
+ self
+ end
+
+ # :call-seq:
+ # exclude(*files) => self
+ #
+ # Excludes source files and directories from generating the documentation.
+ def exclude(*files)
+ @files.exclude *files
+ self
+ end
+
+ # Classpath dependencies.
+ attr_accessor :classpath
+
+ # :call-seq:
+ # with(*artifacts) => self
+ #
+ # Adds files and artifacts as classpath dependencies, and returns self.
+ def with(*specs)
+ @classpath |= Buildr.artifacts(specs.flatten).uniq
+ self
+ end
+
+ # Additional sourcepaths that are not part of the documented files.
+ attr_accessor :sourcepath
+
+ # Returns the Javadoc options.
+ attr_reader :options
+
+ # :call-seq:
+ # using(options) => self
+ #
+ # Sets the Javadoc options from a hash and returns self.
+ #
+ # For example:
+ # javadoc.using :windowtitle=>"My application"
+ def using(*args)
+ args.pop.each { |key, value| @options[key.to_sym] = value } if Hash === args.last
+ args.each { |key| @options[key.to_sym] = true }
+ self
+ end
+
+ # :call-seq:
+ # from(*sources) => self
+ #
+ # Includes files, directories and projects in the Javadoc documentation and returns self.
+ #
+ # You can call this method with Java source files and directories containing Java source files
+ # to include these files in the Javadoc documentation, similar to #include. You can also call
+ # this method with projects. When called with a project, it includes all the source files compiled
+ # by that project and classpath dependencies used when compiling.
+ #
+ # For example:
+ # javadoc.from projects("myapp:foo", "myapp:bar")
+ def from(*sources)
+ sources.flatten.each do |source|
+ case source
+ when Project
+ self.include source.compile.sources
+ self.with source.compile.classpath
+ when Rake::Task, String
+ self.include source
+ else
+ fail "Don't know how to generate Javadocs from #{source || 'nil'}"
+ end
+ end
+ self
+ end
+
+ def prerequisites() #:nodoc:
+ super + @files + classpath + sourcepath
+ end
+
+ def source_files() #:nodoc:
+ @source_files ||= @files.map(&:to_s).
+ map { |file| File.directory?(file) ? FileList[File.join(file, "**/*.java")] : file }.
+ flatten.reject { |file| @files.exclude?(file) }
+ end
+
+ def needed?() #:nodoc:
+ return false if source_files.empty?
+ return true unless File.exist?(target.to_s)
+ source_files.map { |src| File.stat(src.to_s).mtime }.max > File.stat(target.to_s).mtime
+ end
+
+ end
+
+ end
+
+
+ # Local task to execute the compile task of the current project.
+ # This task is not itself a compile task.
+ desc "Compile all projects"
+ Project.local_task("compile") { |name| "Compiling #{name}" }
+
+ desc "Create the Javadocs for this project"
+ Project.local_task("javadoc")
+
+ class Project
+
+ # *Deprecated* Add a prerequisite to the compile task instead.
+ def prepare(*prereqs, &block)
+ warn_deprecated "Add a prerequisite to the compile task instead of using the prepare task."
+ task("prepare").enhance prereqs, &block
+ end
+
+ # :call-seq:
+ # compile(*sources) => CompileTask
+ # compile(*sources) { |task| .. } => CompileTask
+ #
+ # The compile task does what its name suggests. This method returns the project's
+ # CompileTask. It also accepts a list of source directories and files to compile
+ # (equivalent to calling CompileTask#from on the task), and a block for any
+ # post-compilation work.
+ #
+ # The compile task will pick all the source files in the src/main/java directory,
+ # and unless specified, compile them into the target/classes directory. It will pick
+ # the default values for compiler options from the parent project's compile task.
+ #
+ # For example:
+ # # Force target compatibility.
+ # compile.options.source = "1.6"
+ # # Include Apt-generated source files.
+ # compile.from apt
+ # # Include Log4J and the api sub-project artifacts.
+ # compile.with "log4j:log4j:jar:1.2", project("api")
+ # # Run the OpenJPA bytecode enhancer after compilation.
+ # compile { open_jpa_enhance }
+ #
+ # For more information, see Java::CompileTask.
+ def compile(*sources, &block)
+ task("compile").from(sources).enhance &block
+ end
+
+ # :call-seq:
+ # resources(*prereqs) => ResourcesTask
+ # resources(*prereqs) { |task| .. } => ResourcesTask
+ #
+ # The resources task is executed by the compile task to copy resources files
+ # from the resource directory into the target directory.
+ #
+ # This method returns the project's resources task. It also accepts a list of
+ # prerequisites and a block, used to enhance the resources task.
+ #
+ # By default the resources task copies files from the src/main/resources into the
+ # same target directory as the #compile task. It does so using a filter that you
+ # can access by calling resources.filter (see Buildr::Filter).
+ #
+ # For example:
+ # resources.from _("src/etc")
+ # resources.filter.using "Copyright"=>"Acme Inc, 2007"
+ def resources(*prereqs, &block)
+ task("resources").enhance prereqs, &block
+ end
+
+ # :call-seq:
+ # javadoc(*sources) => JavadocTask
+ #
+ # This method returns the project's Javadoc task. It also accepts a list of source files,
+ # directories and projects to include when generating the Javadocs.
+ #
+ # By default the Javadoc task uses all the source directories from compile.sources and generates
+ # Javadocs in the target/javadoc directory. This method accepts sources and adds them by calling
+ # JavadocsTask#from.
+ #
+ # For example, if you want to generate Javadocs for a given project that includes all source files
+ # in two of its sub-projects:
+ # javadoc projects("myapp:foo", "myapp:bar").using(:windowtitle=>"Docs for foo and bar")
+ def javadoc(*sources, &block)
+ task("javadoc").from(*sources).enhance &block
+ end
+
+ end
+
+ Project.on_define do |project|
+ prepare = task("prepare")
+ # Resources task is a filter.
+ resources = Java::ResourcesTask.define_task("resources")
+ project.path_to("src/main/resources").tap { |dir| resources.from dir if File.exist?(dir) }
+ # Compile task requires prepare and performs resources, if anything compiled.
+ compile = Java::CompileTask.define_task("compile"=>[prepare, resources])
+ project.path_to("src/main/java").tap { |dir| compile.from dir if File.exist?(dir) }
+ compile.into project.path_to(:target, "classes")
+ resources.filter.into project.compile.target
+ Java::JavadocTask.define_task("javadoc"=>prepare).tap do |javadoc|
+ javadoc.into project.path_to(:target, "javadoc")
+ javadoc.using :windowtitle=>project.comment || project.name
+ end
+ project.recursive_task("compile")
+
+ project.enhance do |project|
+ # This comes last because the target path may change.
+ project.build project.compile.target
+ # This comes last so we can determine all the source paths and classpath dependencies.
+ project.javadoc.from project
+ project.clean { verbose(false) { rm_rf project.compile.target.to_s } }
+ end
+ end
+
+
+ class Options
+
+ # Returns the debug option (environment variable DEBUG).
+ def debug()
+ (ENV["DEBUG"] || ENV["debug"]) !~ /(no|off|false)/
+ end
+
+ # Sets the debug option (environment variable DEBUG).
+ #
+ # You can turn this option off directly, or by setting the environment variable
+ # DEBUG to "no". For example:
+ # buildr build DEBUG=no
+ #
+ # The release tasks runs a build with <tt>DEBUG=no</tt>.
+ def debug=(flag)
+ ENV["debug"] = nil
+ ENV["DEBUG"] = flag.to_s
+ end
+
+ end
+
+end
Added: incubator/buildr/trunk/lib/java/eclipse.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/java/eclipse.rb?rev=594720&view=auto
==============================================================================
--- incubator/buildr/trunk/lib/java/eclipse.rb (added)
+++ incubator/buildr/trunk/lib/java/eclipse.rb Tue Nov 13 15:44:11 2007
@@ -0,0 +1,155 @@
+require "pathname"
+require "core/project"
+require "java/artifact"
+
+module Buildr
+
+ # Global task "eclipse" generates artifacts for all projects.
+ desc "Generate Eclipse artifacts for all projects"
+ Project.local_task "eclipse"=>"artifacts"
+
+ Project.on_define do |project|
+ eclipse = project.recursive_task("eclipse")
+
+ project.enhance do |project|
+
+ # We need paths relative to the top project's base directory.
+ root_path = lambda { |p| f = lambda { |p| p.parent ? f[p.parent] : p.base_dir } ; f[p] }[project]
+
+ # We want the Eclipse files changed every time the Buildfile changes, but also anything loaded by
+ # the Buildfile (buildr.rb, separate file listing dependencies, etc), so we add anything required
+ # after the Buildfile. So which don't know where Buildr shows up exactly, ignore files that show
+ # in $LOADED_FEATURES that we cannot resolve.
+ sources = Buildr.build_files.map { |file| File.expand_path(file) }.select { |file| File.exist?(file) }
+ sources << File.expand_path(Rake.application.rakefile, root_path) if Rake.application.rakefile
+
+ # Check if project has scala facet
+ scala = project.task("scalac") if Rake::Task.task_defined?(project.name+":"+"scalac")
+
+ # Only for projects that are Eclipse packagable.
+ if project.packages.detect { |pkg| pkg.type.to_s =~ /(jar)|(war)|(rar)|(mar)|(aar)/ }
+ eclipse.enhance [ file(project.path_to(".classpath")), file(project.path_to(".project")) ]
+
+ # The only thing we need to look for is a change in the Buildfile.
+ file(project.path_to(".classpath")=>sources) do |task|
+ puts "Writing #{task.name}" if verbose
+
+ # Find a path relative to the project's root directory.
+ relative = lambda do |path|
+ msg = [:to_path, :to_str, :to_s].find { |msg| path.respond_to? msg }
+ path = path.__send__(msg)
+ Pathname.new(File.expand_path(path)).relative_path_from(Pathname.new(project.path_to)).to_s
+ end
+
+ m2repo = Buildr::Repositories.instance.local
+ excludes = [ '**/.svn/', '**/CVS/' ].join('|')
+
+ File.open(task.name, "w") do |file|
+ xml = Builder::XmlMarkup.new(:target=>file, :indent=>2)
+ xml.classpath do
+ # Note: Use the test classpath since Eclipse compiles both "main" and "test" classes using the same classpath
+ cp = project.test.compile.classpath.map(&:to_s) - [ project.compile.target.to_s ]
+ cp += scala.classpath.map(&:to_s) if scala
+ cp = cp.uniq
+
+ # Convert classpath elements into applicable Project objects
+ cp.collect! { |path| projects.detect { |prj| prj.packages.detect { |pkg| pkg.to_s == path } } || path }
+
+ # project_libs: artifacts created by other projects
+ project_libs, others = cp.partition { |path| path.is_a?(Project) }
+
+ # Separate artifacts from Maven2 repository
+ m2_libs, others = others.partition { |path| path.to_s.index(m2repo) == 0 }
+
+ # Generated: classpath elements in the project are assumed to be generated
+ generated, libs = others.partition { |path| path.to_s.index(project.path_to.to_s) == 0 }
+
+ xml.classpathentry :kind=>'con', :path=>'org.eclipse.jdt.launching.JRE_CONTAINER'
+ xml.classpathentry :kind=>'con', :path=>'ch.epfl.lamp.sdt.launching.SCALA_CONTAINER' if scala
+
+ srcs = project.compile.sources
+ srcs << scala.sources if scala
+
+ # hack until we have sunit task
+ project.path_to("src/test/scala").tap do |dir|
+ srcs += dir if scala and File.exist?(dir)
+ end
+
+ srcs = srcs.map { |src| relative[src] } + generated.map { |src| relative[src] }
+ srcs.sort.uniq.each do |path|
+ xml.classpathentry :kind=>'src', :path=>path, :excluding=>excludes
+ end
+
+ { :output => relative[project.compile.target],
+ :lib => libs.map(&:to_s),
+ :var => m2_libs.map { |path| path.to_s.sub(m2repo, 'M2_REPO') }
+ }.each do |kind, paths|
+ paths.sort.uniq.each do |path|
+ xml.classpathentry :kind=>kind, :path=>path
+ end
+ end
+
+ # Classpath elements from other projects
+ project_libs.map(&:id).sort.uniq.each do |project_id|
+ xml.classpathentry :kind=>'src', :combineaccessrules=>"false", :path=>"/#{project_id}"
+ end
+
+ # Main resources implicitly copied into project.compile.target
+ # TODO: find solution that uses project.test.resources.filter.sources
+ [ "src/main/resources" ].each do |path|
+ if File.exist? project.path_to(path)
+ xml.classpathentry :kind=>'src', :path=>path, :excluding=>excludes
+ end
+ end
+
+ # Test classes are generated in a separate output directory
+ test_sources = project.test.compile.sources.map { |src| relative[src] }
+ test_sources.each do |paths|
+ paths.sort.uniq.each do |path|
+ xml.classpathentry :kind=>'src', :path=>path, :output => relative[project.test.compile.target], :excluding=>excludes
+ end
+ end
+
+ # Test resources go in separate output directory as well
+ # TODO: find solution that uses project.test.resources.filter.sources
+ [ "src/test/resources" ].each do |path|
+ if File.exist? project.path_to(path)
+ xml.classpathentry :kind=>'src', :path=>path, :output => relative[project.test.compile.target], :excluding=>excludes
+ end
+ end
+ end
+ end
+ end
+
+ # The only thing we need to look for is a change in the Buildfile.
+ file(project.path_to(".project")=>sources) do |task|
+ puts "Writing #{task.name}" if verbose
+ File.open(task.name, "w") do |file|
+ xml = Builder::XmlMarkup.new(:target=>file, :indent=>2)
+ xml.projectDescription do
+ xml.name project.id
+ xml.projects
+ xml.buildSpec do
+ xml.buildCommand do
+ xml.name "org.eclipse.jdt.core.javabuilder"
+ end
+ if scala
+ xml.buildCommand do
+ #xml.name "ch.epfl.lamp.sdt.core.scalabuilder"
+ xml.name "scala.plugin.scalabuilder"
+ end
+ end
+ end
+ xml.natures do
+ xml.nature "org.eclipse.jdt.core.javanature"
+ #xml.nature "ch.epfl.lamp.sdt.core.scalanature" if scala
+ xml.nature "scala.plugin.scalanature" if scala
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+end # module Buildr
Added: incubator/buildr/trunk/lib/java/idea.ipr.template
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/java/idea.ipr.template?rev=594720&view=auto
==============================================================================
--- incubator/buildr/trunk/lib/java/idea.ipr.template (added)
+++ incubator/buildr/trunk/lib/java/idea.ipr.template Tue Nov 13 15:44:11 2007
@@ -0,0 +1,284 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4" relativePaths="false">
+ <component name="AntConfiguration">
+ <defaultAnt bundledAnt="true" />
+ </component>
+ <component name="BuildJarProjectSettings">
+ <option name="BUILD_JARS_ON_MAKE" value="false" />
+ </component>
+ <component name="CodeStyleManager">
+ <option name="USE_DEFAULT_CODE_STYLE_SCHEME" value="true" />
+ <option name="CODE_STYLE_SCHEME" value="" />
+ </component>
+ <component name="CodeStyleProjectProfileManger">
+ <option name="PROJECT_PROFILE" />
+ <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+ </component>
+ <component name="CodeStyleSettingsManager">
+ <option name="PER_PROJECT_SETTINGS" />
+ <option name="USE_PER_PROJECT_SETTINGS" value="false" />
+ </component>
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <option name="DEPLOY_AFTER_MAKE" value="0" />
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ </wildcardResourcePatterns>
+ </component>
+ <component name="DataSourceManager" />
+ <component name="DataSourceManagerImpl" />
+ <component name="DependenciesAnalyzeManager">
+ <option name="myForwardDirection" value="false" />
+ </component>
+ <component name="DependencyValidationManager" />
+ <component name="EclipseCompilerSettings">
+ <option name="DEBUGGING_INFO" value="true" />
+ <option name="GENERATE_NO_WARNINGS" value="true" />
+ <option name="DEPRECATION" value="false" />
+ <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+ <option name="MAXIMUM_HEAP_SIZE" value="128" />
+ </component>
+ <component name="EclipseEmbeddedCompilerSettings">
+ <option name="DEBUGGING_INFO" value="true" />
+ <option name="GENERATE_NO_WARNINGS" value="true" />
+ <option name="DEPRECATION" value="false" />
+ <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+ <option name="MAXIMUM_HEAP_SIZE" value="128" />
+ </component>
+ <component name="EntryPointsManager">
+ <entry_points />
+ </component>
+ <component name="ExportToHTMLSettings">
+ <option name="PRINT_LINE_NUMBERS" value="false" />
+ <option name="OPEN_IN_BROWSER" value="false" />
+ <option name="OUTPUT_DIRECTORY" />
+ </component>
+ <component name="GUI Designer component loader factory" />
+ <component name="IdProvider" IDEtalkID="0246D33576B8D4BC3331F9A5BB848389" />
+ <component name="ImportConfiguration">
+ <option name="VENDOR" />
+ <option name="RELEASE_TAG" />
+ <option name="LOG_MESSAGE" />
+ <option name="CHECKOUT_AFTER_IMPORT" value="true" />
+ </component>
+ <component name="InspectionProjectProfileManager">
+ <option name="PROJECT_PROFILE" value="Project Default" />
+ <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
+ <scopes />
+ <profiles>
+ <profile version="1.0" is_locked="false">
+ <option name="myName" value="Project Default" />
+ <option name="myLocal" value="false" />
+ <used_levels>
+ <error>
+ <option name="myName" value="ERROR" />
+ <option name="myVal" value="400" />
+ </error>
+ <warning>
+ <option name="myName" value="WARNING" />
+ <option name="myVal" value="300" />
+ </warning>
+ <information>
+ <option name="myName" value="INFO" />
+ <option name="myVal" value="200" />
+ </information>
+ <server>
+ <option name="myName" value="SERVER PROBLEM" />
+ <option name="myVal" value="100" />
+ </server>
+ </used_levels>
+ </profile>
+ </profiles>
+ </component>
+ <component name="JUnitProjectSettings">
+ <option name="TEST_RUNNER" value="UI" />
+ </component>
+ <component name="JavacSettings">
+ <option name="DEBUGGING_INFO" value="true" />
+ <option name="GENERATE_NO_WARNINGS" value="false" />
+ <option name="DEPRECATION" value="true" />
+ <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+ <option name="MAXIMUM_HEAP_SIZE" value="128" />
+ </component>
+ <component name="JavadocGenerationManager">
+ <option name="OUTPUT_DIRECTORY" />
+ <option name="OPTION_SCOPE" value="protected" />
+ <option name="OPTION_HIERARCHY" value="false" />
+ <option name="OPTION_NAVIGATOR" value="false" />
+ <option name="OPTION_INDEX" value="false" />
+ <option name="OPTION_SEPARATE_INDEX" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_USE" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_VERSION" value="false" />
+ <option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="false" />
+ <option name="OPTION_DEPRECATED_LIST" value="false" />
+ <option name="OTHER_OPTIONS" />
+ <option name="HEAP_SIZE" />
+ <option name="LOCALE" />
+ <option name="OPEN_IN_BROWSER" value="false" />
+ </component>
+ <component name="JikesSettings">
+ <option name="JIKES_PATH" value="" />
+ <option name="DEBUGGING_INFO" value="true" />
+ <option name="DEPRECATION" value="true" />
+ <option name="GENERATE_NO_WARNINGS" value="false" />
+ <option name="IS_EMACS_ERRORS_MODE" value="true" />
+ <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+ </component>
+ <component name="LogConsolePreferences">
+ <option name="FILTER_ERRORS" value="false" />
+ <option name="FILTER_WARNINGS" value="false" />
+ <option name="FILTER_INFO" value="true" />
+ <option name="CUSTOM_FILTER" />
+ </component>
+ <component name="Palette2">
+ <group name="Swing">
+ <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+ </item>
+ <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+ <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+ <initial-values>
+ <property name="text" value="Button" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="RadioButton" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="CheckBox" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="Label" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+ <preferred-size width="-1" height="20" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+ </item>
+ </group>
+ </component>
+ <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" />
+ <component name="ProjectRunConfigurationManager" />
+ <component name="RmicSettings">
+ <option name="IS_EANABLED" value="false" />
+ <option name="DEBUGGING_INFO" value="true" />
+ <option name="GENERATE_NO_WARNINGS" value="false" />
+ <option name="GENERATE_IIOP_STUBS" value="false" />
+ <option name="ADDITIONAL_OPTIONS_STRING" value="" />
+ </component>
+ <component name="StarteamVcsAdapter" />
+ <component name="VssVcs" />
+ <component name="com.intellij.jsf.UserDefinedFacesConfigs">
+ <option name="USER_DEFINED_CONFIGS">
+ <value>
+ <list size="0" />
+ </value>
+ </option>
+ </component>
+ <component name="uidesigner-configuration">
+ <option name="INSTRUMENT_CLASSES" value="true" />
+ <option name="COPY_FORMS_RUNTIME_TO_OUTPUT" value="true" />
+ <option name="DEFAULT_LAYOUT_MANAGER" value="GridLayoutManager" />
+ </component>
+ <UsedPathMacros>
+ <macro name="M2_REPO" />
+ </UsedPathMacros>
+</project>
+
+
Added: incubator/buildr/trunk/lib/java/idea.rb
URL: http://svn.apache.org/viewvc/incubator/buildr/trunk/lib/java/idea.rb?rev=594720&view=auto
==============================================================================
--- incubator/buildr/trunk/lib/java/idea.rb (added)
+++ incubator/buildr/trunk/lib/java/idea.rb Tue Nov 13 15:44:11 2007
@@ -0,0 +1,159 @@
+require "pathname"
+require "core/project"
+require "java/artifact"
+require 'stringio'
+require 'rexml/document'
+
+module Buildr
+
+ # Global task "idea" generates artifacts for all projects.
+ desc "Generate Idea artifacts for all projects"
+ Project.local_task "idea"=>"artifacts"
+
+ Project.on_define do |project|
+ idea = project.recursive_task("idea")
+
+ project.enhance do |project|
+
+ # We need paths relative to the top project's base directory.
+ root_path = lambda { |p| f = lambda { |p| p.parent ? f[p.parent] : p.base_dir } ; f[p] }[project]
+ # We want the Eclipse files changed every time the Buildfile changes, but also anything loaded by
+ # the Buildfile (buildr.rb, separate file listing dependencies, etc), so we add anything required
+ # after the Buildfile. So which don't know where Buildr shows up exactly, ignore files that show
+ # in $LOADED_FEATURES that we cannot resolve.
+ sources = Buildr.build_files.map { |file| File.expand_path(file) }.select { |file| File.exist?(file) }
+ sources << File.expand_path(Rake.application.rakefile, root_path) if Rake.application.rakefile
+
+ # Find a path relative to the project's root directory.
+ relative = lambda do |path|
+ msg = [:to_path, :to_str, :to_s].find { |msg| path.respond_to? msg }
+ path = path.__send__(msg)
+ Pathname.new(path).relative_path_from(Pathname.new(project.path_to)).to_s
+ end
+
+ m2repo = Buildr::Repositories.instance.local
+ excludes = [ '**/.svn/', '**/CVS/' ].join('|')
+
+ # Only for projects that are packageable.
+ task_name = project.path_to("#{project.name.gsub(':', '-')}.iml")
+ idea.enhance [ file(task_name) ]
+
+ # The only thing we need to look for is a change in the Buildfile.
+ file(task_name=>sources) do |task|
+ puts "Writing #{task.name}" if verbose
+
+ # Idea handles modules slightly differently if they're WARs
+ idea_types = Hash.new("JAVA_MODULE")
+ idea_types["war"] = "J2EE_WEB_MODULE"
+
+ # Note: Use the test classpath since Eclipse compiles both "main" and "test" classes using the same classpath
+ cp = project.test.compile.classpath.map(&:to_s) - [ project.compile.target.to_s ]
+
+ # Convert classpath elements into applicable Project objects
+ cp.collect! { |path| projects.detect { |prj| prj.packages.detect { |pkg| pkg.to_s == path } } || path }
+
+ # project_libs: artifacts created by other projects
+ project_libs, others = cp.partition { |path| path.is_a?(Project) }
+
+ # Separate artifacts from Maven2 repository
+ m2_libs, others = others.partition { |path| path.to_s.index(m2repo) == 0 }
+
+ # Generated: classpath elements in the project are assumed to be generated
+ generated, libs = others.partition { |path| path.to_s.index(project.path_to.to_s) == 0 }
+
+ File.open(task.name, "w") do |file|
+ xml = Builder::XmlMarkup.new(:target=>file, :indent=>2)
+ # Project type is going to be the first package type
+ xml.module(:version=>"4", :relativePaths=>"false", :type=>idea_types[project.packages.first.type.to_s]) do
+
+ xml.component :name=>"ModuleRootManager"
+ xml.component "name"=>"NewModuleRootManager", "inherit-compiler-output"=>"false" do
+ xml.output :url=>"file://$MODULE_DIR$/#{relative[project.compile.target]}"
+ xml.tag! "exclude-output"
+
+ # TODO project.test.target isn't recognized, what's the proper way to get the test compile path?
+ xml.tag! "output-test", :url=>"file://$MODULE_DIR$/target/test-classes"
+
+ xml.content(:url=>"file://$MODULE_DIR$") do
+ srcs = project.compile.sources.map { |src| relative[src] } + generated.map { |src| relative[src] }
+ srcs.sort.uniq.each do |path|
+ xml.sourceFolder :url=>"file://$MODULE_DIR$/#{path}", :isTestSource=>"false"
+ end
+ test_sources = project.test.compile.sources.map { |src| relative[src] }
+ test_sources.each do |paths|
+ paths.sort.uniq.each do |path|
+ xml.sourceFolder :url=>"file://$MODULE_DIR$/#{path}", :isTestSource=>"true"
+ end
+ end
+ {"src/main/resources"=>false, "src/test/resources"=>true}.each do |key, value|
+ xml.sourceFolder :url=>"file://$MODULE_DIR$/#{key}", :isTestSource=>"#{value}" if File.exist?(project.path_to(key))
+ end
+ xml.excludeFolder :url=>"file://$MODULE_DIR$/#{relative[project.compile.target]}"
+ end
+
+ xml.orderEntry :type=>"sourceFolder", :forTests=>"false"
+ xml.orderEntry :type=>"inheritedJdk"
+
+ # Classpath elements from other projects
+ project_libs.map(&:id).sort.uniq.each do |project_id|
+ xml.orderEntry :type=>'module', "module-name"=>project_id
+ end
+
+ # Libraries
+ ext_libs = libs.map {|path| "$MODULE_DIR$/#{path.to_s}" } +
+ m2_libs.map { |path| path.to_s.sub(m2repo, "$M2_REPO$") }
+ ext_libs.each do |path|
+ xml.orderEntry :type=>"module-library" do
+ xml.library do
+ xml.CLASSES do
+ xml.root :url=>"jar://#{path}!/"
+ end
+ xml.JAVADOC
+ xml.SOURCES
+ end
+ end
+ end
+
+ xml.orderEntryProperties
+ end
+ end
+ end
+ end
+
+ # Root project aggregates all the subprojects.
+ if project.parent == nil
+ task_name = project.path_to("#{project.name.gsub(':', '-')}.ipr")
+ idea.enhance [ file(task_name) ]
+
+ file(task_name=>sources) do |task|
+ puts "Writing #{task.name}" if verbose
+
+ # Generating just the little stanza that chanages from one project to another
+ partial = StringIO.new
+ xml = Builder::XmlMarkup.new(:target=>partial, :indent=>2)
+ xml.component(:name=>"ProjectModuleManager") do
+ xml.modules do
+ project.projects.each do |subp|
+ module_name = subp.name.gsub(":", "-")
+ module_path = subp.name.split(":"); module_path.shift
+ module_path = module_path.join("/")
+ path = "#{module_path}/#{module_name}.iml"
+ xml.module :fileurl=>"file://$PROJECT_DIR$/#{path}", :filepath=>"$PROJECT_DIR$/#{path}"
+ end
+ xml.module :fileurl=>"file://$PROJECT_DIR$/#{project.name}.iml", :filepath=>"$PROJECT_DIR$/#{project.name}.iml"
+ end
+ end
+
+ # Loading the whole fairly constant crap
+ template_xml = REXML::Document.new(File.open(File.dirname(__FILE__)+"/idea.ipr.template"))
+ include_xml = REXML::Document.new(partial.string)
+ template_xml.root.add_element(include_xml.root)
+ template_xml.write(File.new(task.name, "w"))
+
+ end
+ end
+
+ end
+ end
+
+end # module Buildr