Return-Path: Delivered-To: apmail-buildr-commits-archive@www.apache.org Received: (qmail 72226 invoked from network); 13 Sep 2009 23:59:08 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 13 Sep 2009 23:59:08 -0000 Received: (qmail 66759 invoked by uid 500); 13 Sep 2009 23:59:08 -0000 Delivered-To: apmail-buildr-commits-archive@buildr.apache.org Received: (qmail 66732 invoked by uid 500); 13 Sep 2009 23:59:08 -0000 Mailing-List: contact commits-help@buildr.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@buildr.apache.org Delivered-To: mailing list commits@buildr.apache.org Received: (qmail 66723 invoked by uid 99); 13 Sep 2009 23:59:08 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 13 Sep 2009 23:59:08 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 13 Sep 2009 23:58:53 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 46F26238888D; Sun, 13 Sep 2009 23:58:32 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r814442 - in /buildr/trunk: CHANGELOG lib/buildr/ide/eclipse.rb spec/ide/eclipse_spec.rb Date: Sun, 13 Sep 2009 23:58:32 -0000 To: commits@buildr.apache.org From: boisvert@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090913235832.46F26238888D@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: boisvert Date: Sun Sep 13 23:58:31 2009 New Revision: 814442 URL: http://svn.apache.org/viewvc?rev=814442&view=rev Log: BUILDR-300: Make Eclipse task more configurable; thanks to Antoine Toulme for base contribution Modified: buildr/trunk/CHANGELOG buildr/trunk/lib/buildr/ide/eclipse.rb buildr/trunk/spec/ide/eclipse_spec.rb Modified: buildr/trunk/CHANGELOG URL: http://svn.apache.org/viewvc/buildr/trunk/CHANGELOG?rev=814442&r1=814441&r2=814442&view=diff ============================================================================== --- buildr/trunk/CHANGELOG (original) +++ buildr/trunk/CHANGELOG Sun Sep 13 23:58:31 2009 @@ -1,4 +1,5 @@ 1.3.5 (Pending) +* Added: BUILDR-300: Make Eclipse task more configurable (Antoine Toulme, Alex Boisvert) * Fixed: BUILDR-307 Failures are not reported correctly for ScalaTest (Jeremie Lenfant-Engelmann) * Fixed: BUILDR-278 tasks/*.rake files are loaded after the buildfile (Rhett Sutphin) * Fixed: BUILDR-304 Referencing an existing package task using the package Modified: buildr/trunk/lib/buildr/ide/eclipse.rb URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/ide/eclipse.rb?rev=814442&r1=814441&r2=814442&view=diff ============================================================================== --- buildr/trunk/lib/buildr/ide/eclipse.rb (original) +++ buildr/trunk/lib/buildr/ide/eclipse.rb Sun Sep 13 23:58:31 2009 @@ -22,28 +22,121 @@ module Eclipse #:nodoc: include Extension - def eclipse - Eclipse.instance - end - class Eclipse - include Singleton - + attr_reader :options - - def initialize - @options = Options.new + + def initialize(project) + @project = project + @options = Options.new(project) + end + + # :call-seq: + # natures=(natures) + # Sets the Eclipse project natures on the project. + # + def natures=(var) + @natures = arrayfy(var) + end + + # :call-seq: + # natures() => [n1, n2] + # Returns the Eclipse project natures on the project. + # They may be derived from the parent project if no specific natures have been set + # on the project. + # + # An Eclipse project nature is used internally by Eclipse to determine the aspects of a project. + def natures(*values) + if values.size > 0 + @natures ||= [] + @natures += values + else + @natures || (@project.parent ? @project.parent.eclipse.natures : []) + end + end + + # :call-seq: + # classpath_containers=(cc) + # Sets the Eclipse project classpath containers on the project. + # + def classpath_containers=(var) + @classpath_containers = arrayfy(var) + end + + # :call-seq: + # classpath_containers() => [con1, con2] + # Returns the Eclipse project classpath containers on the project. + # They may be derived from the parent project if no specific classpath containers have been set + # on the project. + # + # A classpath container is an Eclipse pre-determined ensemble of dependencies made available to + # the project classpath. + def classpath_containers(*values) + if values.size > 0 + @classpath_containers ||= [] + @classpath_containers += values + else + @classpath_containers || (@project.parent ? @project.parent.eclipse.classpath_containers : []) + end + end + + # :call-seq: + # builders=(builders) + # Sets the Eclipse project builders on the project. + # + def builders=(var) + @builders = arrayfy(var) + end + + # :call-seq: + # builders() => [b1, b2] + # Returns the Eclipse project builders on the project. + # They may be derived from the parent project if no specific builders have been set + # on the project. + # + # A builder is an Eclipse background job that parses the source code to produce built artifacts. + def builders(*values) + if values.size > 0 + @builders ||= [] + @builders += values + else + @builders || (@project.parent ? @project.parent.eclipse.builders : []) + end + end + + private + + def arrayfy(obj) + obj.is_a?(Array) ? obj : [obj] end end - + class Options - attr_accessor :m2_repo_var - def initialize - @m2_repo_var = 'M2_REPO' + attr_writer :m2_repo_var + + def initialize(project) + @project = project + end + + # The classpath variable used to point at the local maven2 repository. + # Example: + # eclipse.options.m2_repo_var = 'M2_REPO' + def m2_repo_var(*values) + fail "m2_repo_var can only accept one value: #{values}" if values.size > 1 + if values.size > 0 + @m2_repo_var = values[0] + else + @m2_repo_var || (@project.parent ? @project.parent.eclipse.options.m2_repo_var : 'M2_REPO') + end end end - + + def eclipse + @eclipse ||= Eclipse.new(self) + @eclipse + end + first_time do # Global task "eclipse" generates artifacts for all projects. desc 'Generate Eclipse artifacts for all projects' @@ -57,19 +150,11 @@ after_define do |project| eclipse = project.task('eclipse') - # Check if project has scala facet - scala = project.compile.language == :scala - - # Only for projects that we support - supported_languages = [:java, :scala] - supported_packaging = %w(jar war rar mar aar) - if (supported_languages.include?(project.compile.language) || - supported_languages.include?(project.test.compile.language) || - project.packages.detect { |pkg| supported_packaging.include?(pkg.type.to_s) }) - eclipse.enhance [ file(project.path_to('.classpath')), file(project.path_to('.project')) ] + 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')=>Buildr.application.buildfile) do |task| + # The only thing we need to look for is a change in the Buildfile. + file(project.path_to('.classpath')=>Buildr.application.buildfile) do |task| + if (project.eclipse.natures.reject { |x| x.is_a?(Symbol) }.size > 0) info "Writing #{task.name}" m2repo = Buildr::Repositories.instance.local @@ -106,16 +191,19 @@ classpathentry.output project.compile.target if project.compile.target classpathentry.lib libs - classpathentry.var m2_libs, Eclipse.instance.options.m2_repo_var, m2repo + classpathentry.var m2_libs, project.eclipse.options.m2_repo_var, m2repo - classpathentry.con 'ch.epfl.lamp.sdt.launching.SCALA_CONTAINER' if scala - classpathentry.con 'org.eclipse.jdt.launching.JRE_CONTAINER' + project.eclipse.classpath_containers.each { |container| + classpathentry.con container + } end end end + end - # The only thing we need to look for is a change in the Buildfile. - file(project.path_to('.project')=>Buildr.application.buildfile) do |task| + # The only thing we need to look for is a change in the Buildfile. + file(project.path_to('.project')=>Buildr.application.buildfile) do |task| + if (project.eclipse.natures.reject { |x| x.is_a?(Symbol) }.size > 0) info "Writing #{task.name}" File.open(task.name, 'w') do |file| xml = Builder::XmlMarkup.new(:target=>file, :indent=>2) @@ -123,25 +211,21 @@ xml.name project.id xml.projects xml.buildSpec do - if scala + project.eclipse.builders.each { |builder| xml.buildCommand do - xml.name 'ch.epfl.lamp.sdt.core.scalabuilder' + xml.name builder end - else - xml.buildCommand do - xml.name 'org.eclipse.jdt.core.javabuilder' - end - end + } end xml.natures do - xml.nature 'ch.epfl.lamp.sdt.core.scalanature' if scala - xml.nature 'org.eclipse.jdt.core.javanature' + project.eclipse.natures.each { |nature| + xml.nature nature unless nature.is_a? Symbol + } end end end end end - end @@ -156,11 +240,11 @@ @excludes = [ '**/.svn/', '**/CVS/' ].join('|') @paths_written = [] end - + def write &block @xml.classpath &block end - + def con path @xml.classpathentry :kind=>'con', :path=>path end @@ -188,7 +272,7 @@ @xml.classpathentry :kind=>'src', :combineaccessrules=>'false', :path=>"/#{project_id}" end end - + def output target @xml.classpathentry :kind=>'output', :path=>relative(target) end @@ -207,7 +291,7 @@ @xml.classpathentry :kind=>'var', :path=>relative_lib_path, :sourcepath=>relative_source_path end end - + private # Find a path relative to the project's root directory. @@ -236,10 +320,15 @@ end end - -end # module Buildr +end # module Buildr class Buildr::Project include Buildr::Eclipse end + +# Order is significant for auto-detection, from most specific to least +require 'buildr/ide/eclipse/plugin' +require 'buildr/ide/eclipse/scala' +require 'buildr/ide/eclipse/java' + Modified: buildr/trunk/spec/ide/eclipse_spec.rb URL: http://svn.apache.org/viewvc/buildr/trunk/spec/ide/eclipse_spec.rb?rev=814442&r1=814441&r2=814442&view=diff ============================================================================== --- buildr/trunk/spec/ide/eclipse_spec.rb (original) +++ buildr/trunk/spec/ide/eclipse_spec.rb Sun Sep 13 23:58:31 2009 @@ -17,14 +17,17 @@ require File.join(File.dirname(__FILE__), '../spec_helpers') -JAVA_CONTAINER = 'org.eclipse.jdt.launching.JRE_CONTAINER' -SCALA_CONTAINER = 'ch.epfl.lamp.sdt.launching.SCALA_CONTAINER' - -SCALA_NATURE = 'ch.epfl.lamp.sdt.core.scalanature' -JAVA_NATURE = 'org.eclipse.jdt.core.javanature' - -SCALA_BUILDER = 'ch.epfl.lamp.sdt.core.scalabuilder' -JAVA_BUILDER = 'org.eclipse.jdt.core.javabuilder' +JAVA_CONTAINER = Buildr::Eclipse::Java::CONTAINER +SCALA_CONTAINER = Buildr::Eclipse::Scala::CONTAINER +PLUGIN_CONTAINER = Buildr::Eclipse::Plugin::CONTAINER + +JAVA_NATURE = Buildr::Eclipse::Java::NATURE +SCALA_NATURE = Buildr::Eclipse::Scala::NATURE +PLUGIN_NATURE = Buildr::Eclipse::Plugin::NATURE + +JAVA_BUILDER = Buildr::Eclipse::Java::BUILDER +SCALA_BUILDER = Buildr::Eclipse::Scala::BUILDER +PLUGIN_BUILDERS = Buildr::Eclipse::Plugin::BUILDERS module EclipseHelper @@ -32,72 +35,75 @@ task('eclipse').invoke REXML::Document.new(File.open('.classpath')).root.elements end - - def classpath_sources attribute='path' + + def classpath_sources(attribute='path') classpath_xml_elements.collect("classpathentry[@kind='src']") { |n| n.attributes[attribute] } end - + # - def classpath_specific_output path + def classpath_specific_output(path) specific_output = classpath_xml_elements.collect("classpathentry[@path='#{path}']") { |n| n.attributes['output'] } raise "expected: one output attribute for path '#{path}, got: #{specific_output.inspect}" if specific_output.length > 1 specific_output[0] end - + # def classpath_default_output default_output = classpath_xml_elements.collect("classpathentry[@kind='output']") { |n| n.attributes['path'] } raise "expected: one path attribute for kind='output', got: #{default_output.inspect}" if default_output.length > 1 default_output[0] end - + # - def sourcepath_for_path path + def sourcepath_for_path(path) classpath_xml_elements.collect("classpathentry[@kind='var',@path='#{path}']") do |n| n.attributes['sourcepath'] || 'no source artifact' end end - + def project_xml_elements task('eclipse').invoke REXML::Document.new(File.open('.project')).root.elements end + def project_natures + project_xml_elements.collect("natures/nature") { |n| n.text } + end + + def build_commands + project_xml_elements.collect("buildSpec/buildCommand/name") { |n| n.text } + end + + def classpath_containers(attribute='path') + classpath_xml_elements.collect("classpathentry[@kind='con']") { |n| n.attributes[attribute] } + end end describe Buildr::Eclipse do include EclipseHelper - + describe "eclipse's .project file" do - def project_natures - project_xml_elements.collect("natures/nature") { |n| n.text } - end - - def build_commands - project_xml_elements.collect("buildSpec/buildCommand/name") { |n| n.text } - end - describe 'java project' do before do write 'buildfile' write 'src/main/java/Main.java' end - + it 'should have Java nature' do define('foo') project_natures.should include(JAVA_NATURE) end - + it 'should have Java build command' do define('foo') build_commands.should include(JAVA_BUILDER) end end - + describe 'nested java project' do - + it 'should have name corresponding to its project definition' do mkdir 'foo' define('myproject') { @@ -110,42 +116,102 @@ end end - + describe 'scala project' do - + + before do + define 'foo' do + eclipse.natures :scala + end + end + + it 'should have Scala nature before Java nature' do + project_natures.should include(SCALA_NATURE) + project_natures.should include(JAVA_NATURE) + project_natures.index(SCALA_NATURE).should < project_natures.index(JAVA_NATURE) + end + + it 'should have Scala build command and no Java build command' do + build_commands.should include(SCALA_BUILDER) + build_commands.should_not include(JAVA_BUILDER) + end + end + + describe 'standard scala project' do + before do write 'buildfile' write 'src/main/scala/Main.scala' + define 'foo' end - + it 'should have Scala nature before Java nature' do - define('foo') project_natures.should include(SCALA_NATURE) project_natures.should include(JAVA_NATURE) project_natures.index(SCALA_NATURE).should < project_natures.index(JAVA_NATURE) end - + + it 'should have Scala build command and no Java build command' do + build_commands.should include(SCALA_BUILDER) + build_commands.should_not include(JAVA_BUILDER) + end + end + + describe 'non-standard scala project' do + + before do + write 'buildfile' + write 'src/main/foo/Main.scala' + define 'foo' do + eclipse.natures = :scala + end + end + + it 'should have Scala nature before Java nature' do + project_natures.should include(SCALA_NATURE) + project_natures.should include(JAVA_NATURE) + project_natures.index(SCALA_NATURE).should < project_natures.index(JAVA_NATURE) + end + it 'should have Scala build command and no Java build command' do - define('foo') build_commands.should include(SCALA_BUILDER) build_commands.should_not include(JAVA_BUILDER) end end + + describe 'Plugin project' do + + before do + write 'buildfile' + write 'src/main/java/Activator.java' + write 'plugin.xml' + end + + it 'should have plugin nature before Java nature' do + define('foo') + project_natures.should include(PLUGIN_NATURE) + project_natures.should include(JAVA_NATURE) + project_natures.index(PLUGIN_NATURE).should < project_natures.index(JAVA_NATURE) + end + + it 'should have plugin build commands and the Java build command' do + define('foo') + build_commands.should include(PLUGIN_BUILDERS[0]) + build_commands.should include(PLUGIN_BUILDERS[1]) + build_commands.should include(JAVA_BUILDER) + end + end end - + describe "eclipse's .classpath file" do - + describe 'scala project' do - def classpath_containers attribute='path' - classpath_xml_elements.collect("classpathentry[@kind='con']") { |n| n.attributes[attribute] } - end - before do write 'buildfile' write 'src/main/scala/Main.scala' end - + it 'should have SCALA_CONTAINER before JAVA_CONTAINER' do define('foo') classpath_containers.should include(SCALA_CONTAINER) @@ -153,15 +219,15 @@ classpath_containers.index(SCALA_CONTAINER).should < classpath_containers.index(JAVA_CONTAINER) end end - + describe 'source folders' do - + before do write 'buildfile' write 'src/main/java/Main.java' write 'src/test/java/Test.java' end - + describe 'source', :shared=>true do it 'should ignore CVS and SVN files' do define('foo') @@ -172,106 +238,106 @@ end end end - + describe 'main code' do it_should_behave_like 'source' - + it 'should accept to come from the default directory' do define('foo') classpath_sources.should include('src/main/java') end - + it 'should accept to come from a user-defined directory' do define('foo') { compile path_to('src/java') } classpath_sources.should include('src/java') end - + it 'should accept a file task as a main source folder' do define('foo') { compile apt } classpath_sources.should include('target/generated/apt') end - + it 'should go to the default target directory' do define('foo') classpath_specific_output('src/main/java').should be(nil) classpath_default_output.should == 'target/classes' end end - + describe 'test code' do it_should_behave_like 'source' - + it 'should accept to come from the default directory' do define('foo') classpath_sources.should include('src/test/java') end - + it 'should accept to come from a user-defined directory' do define('foo') { test.compile path_to('src/test') } classpath_sources.should include('src/test') end - + it 'should go to the default target directory' do define('foo') classpath_specific_output('src/test/java').should == 'target/test/classes' end - + it 'should accept to be the only code in the project' do rm 'src/main/java/Main.java' define('foo') classpath_sources.should include('src/test/java') end end - + describe 'main resources' do it_should_behave_like 'source' - + before do write 'src/main/resources/config.xml' end - + it 'should accept to come from the default directory' do define('foo') classpath_sources.should include('src/main/resources') end - + it 'should share a classpath entry if it comes from a directory with code' do write 'src/main/java/config.properties' define('foo') { resources.from('src/main/java').exclude('**/*.java') } classpath_sources.select { |path| path == 'src/main/java'}.length.should == 1 end - + it 'should go to the default target directory' do define('foo') classpath_specific_output('src/main/resources').should == 'target/resources' end end - + describe 'test resources' do it_should_behave_like 'source' - + before do write 'src/test/resources/config-test.xml' end - + it 'should accept to come from the default directory' do define('foo') classpath_sources.should include('src/test/resources') end - + it 'should share a classpath entry if it comes from a directory with code' do write 'src/test/java/config-test.properties' define('foo') { test.resources.from('src/test/java').exclude('**/*.java') } classpath_sources.select { |path| path == 'src/test/java'}.length.should == 1 end - + it 'should go to the default target directory' do define('foo') classpath_specific_output('src/test/resources').should == 'target/test/resources' end end end - + describe 'project depending on another project' do it 'should have the underlying project in its classpath' do mkdir 'foo' @@ -287,34 +353,34 @@ end end end - + describe 'maven2 artifact dependency' do before do define('foo') { compile.using(:javac).with('com.example:library:jar:2.0') } artifact('com.example:library:jar:2.0') { |task| write task.name } task('eclipse').invoke end - + it 'should have a reference in the .classpath file relative to the local M2 repo' do classpath_xml_elements.collect("classpathentry[@kind='var']") { |n| n.attributes['path'] }. should include('M2_REPO/com/example/library/2.0/library-2.0.jar') end - + it 'should be downloaded' do file(artifact('com.example:library:jar:2.0').name).should exist end - + it 'should have a source artifact reference in the .classpath file' do sourcepath_for_path('M2_REPO/com/example/library/2.0/library-2.0.jar'). should == ['M2_REPO/com/example/library/2.0/library-2.0-sources.jar'] end end - + describe 'maven2 repository variable' do it 'should be configurable' do define('foo') do eclipse.options.m2_repo_var = 'PROJ_REPO' - compile.using(:javac).with('com.example:library:jar:2.0') + compile.using(:javac).with('com.example:library:jar:2.0') end artifact('com.example:library:jar:2.0') { |task| write task.name } @@ -322,5 +388,90 @@ classpath_xml_elements.collect("classpathentry[@kind='var']") { |n| n.attributes['path'] }. should include('PROJ_REPO/com/example/library/2.0/library-2.0.jar') end + + it 'should pick the parent value by default' do + define('foo') do + eclipse.options.m2_repo_var = 'FOO_REPO' + define('bar') + + define('bar2') do + eclipse.options.m2_repo_var = 'BAR2_REPO' + end + end + project('foo:bar').eclipse.options.m2_repo_var.should eql('FOO_REPO') + project('foo:bar2').eclipse.options.m2_repo_var.should eql('BAR2_REPO') + end + end + + describe 'natures variable' do + it 'should be configurable' do + define('foo') do + eclipse.natures = 'dummyNature' + compile.using(:javac).with('com.example:library:jar:2.0') + end + artifact('com.example:library:jar:2.0') { |task| write task.name } + project_natures.should include('dummyNature') + end + + it 'should pick the parent value by default' do + define('foo') do + eclipse.natures = 'foo_nature' + define('bar') + + define('bar2') do + eclipse.natures = 'bar2_nature' + end + end + project('foo:bar').eclipse.natures.should include('foo_nature') + project('foo:bar2').eclipse.natures.should include('bar2_nature') + end + end + + describe 'builders variable' do + it 'should be configurable' do + define('foo') do + eclipse.builders 'dummyBuilder' + compile.using(:javac).with('com.example:library:jar:2.0') + end + artifact('com.example:library:jar:2.0') { |task| write task.name } + build_commands.should include('dummyBuilder') + end + + it 'should pick the parent value by default' do + define('foo') do + eclipse.builders = 'foo_builder' + define('bar') + + define('bar2') do + eclipse.builders = 'bar2_builder' + end + end + project('foo:bar').eclipse.builders.should include('foo_builder') + project('foo:bar2').eclipse.builders.should include('bar2_builder') + end + end + + describe 'classpath_containers variable' do + it 'should be configurable' do + define('foo') do + eclipse.classpath_containers = 'myOlGoodContainer' + compile.using(:javac).with('com.example:library:jar:2.0') + end + artifact('com.example:library:jar:2.0') { |task| write task.name } + classpath_containers.should include('myOlGoodContainer') + end + + it 'should pick the parent value by default' do + define('foo') do + eclipse.classpath_containers = 'foo_classpath_containers' + define('bar') + + define('bar2') do + eclipse.classpath_containers = 'bar2_classpath_containers' + end + end + project('foo:bar').eclipse.classpath_containers.should include('foo_classpath_containers') + project('foo:bar2').eclipse.classpath_containers.should include('bar2_classpath_containers') + end end end