You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@buildr.apache.org by do...@apache.org on 2012/09/24 06:08:23 UTC

svn commit: r1389201 - in /buildr/trunk: CHANGELOG lib/buildr/core/application.rb lib/buildr/core/generate.rb spec/core/generate_from_eclipse_spec.rb

Author: donaldp
Date: Mon Sep 24 04:08:22 2012
New Revision: 1389201

URL: http://svn.apache.org/viewvc?rev=1389201&view=rev
Log:
BUILDR-652 Generate buildfile from Eclipse workspace. (Niklaus Giger)

Added:
    buildr/trunk/spec/core/generate_from_eclipse_spec.rb
Modified:
    buildr/trunk/CHANGELOG
    buildr/trunk/lib/buildr/core/application.rb
    buildr/trunk/lib/buildr/core/generate.rb

Modified: buildr/trunk/CHANGELOG
URL: http://svn.apache.org/viewvc/buildr/trunk/CHANGELOG?rev=1389201&r1=1389200&r2=1389201&view=diff
==============================================================================
--- buildr/trunk/CHANGELOG (original)
+++ buildr/trunk/CHANGELOG Mon Sep 24 04:08:22 2012
@@ -1,4 +1,5 @@
 1.4.8 (Pending)
+* Added:  BUILDR-652 Generate buildfile from Eclipse workspace. (Niklaus Giger)
 * Fixed:  BUILDR-627 Support explicitly listed source files in buildr cc task. (Christopher Tiwald)
 * Fixed:  BUILDR-606 Transitive artifact resolution should not include artifacts in 'provided' scope in poms to
           match maven behaviour. (Julio Arias)

Modified: buildr/trunk/lib/buildr/core/application.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/application.rb?rev=1389201&r1=1389200&r2=1389201&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/application.rb (original)
+++ buildr/trunk/lib/buildr/core/application.rb Mon Sep 24 04:08:22 2012
@@ -387,23 +387,31 @@ module Buildr
     end
 
     def ask_generate_buildfile
-      source = choose do |menu|
-        menu.header = "To use Buildr you need a buildfile. Do you want me to create one?"
-        menu.choice("From Maven2 POM file") { 'pom.xml' } if File.exist?('pom.xml')
-        menu.choice("From directory structure") { Dir.pwd }
-        menu.choice("Cancel") { }
+      source, fromEclipse = choose do |menu|
+        menu.header = "ngng: To use Buildr you need a buildfile. Do you want me to create one?"
+        menu.choice("From eclipse .project files") { [Dir.pwd, true] } if Generate.hasEclipseProject
+        menu.choice("From Maven2 POM file") { ['pom.xml', false] } if File.exist?('pom.xml')
+        menu.choice("From directory structure") { [Dir.pwd, false] }
+        menu.choice("Cancel") {}
       end
       if source
-        buildfile = raw_generate_buildfile(source)
+        buildfile = raw_generate_buildfile(source, fromEclipse)
         [buildfile, File.dirname(buildfile)]
       end
     end
 
-    def raw_generate_buildfile(source)
+    def raw_generate_buildfile(source, fromEclipse)
       # We need rakefile to be known, for settings.build to be accessible.
       @rakefile = File.expand_path(DEFAULT_BUILDFILES.first)
       fail "Buildfile already exists" if File.exist?(@rakefile) && !(tty_output? && agree('Buildfile exists, overwrite?'))
-      script = File.directory?(source) ? Generate.from_directory(source) : Generate.from_maven2_pom(source)
+      script = nil
+      if fromEclipse
+        script = Generate.from_eclipse(source)
+      elsif File.directory?(source)
+        script = Generate.from_directory(source)
+      else
+        script = Generate.from_maven2_pom(source)
+      end
       File.open @rakefile, 'w' do |file|
         file.puts script
       end

Modified: buildr/trunk/lib/buildr/core/generate.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/lib/buildr/core/generate.rb?rev=1389201&r1=1389200&r2=1389201&view=diff
==============================================================================
--- buildr/trunk/lib/buildr/core/generate.rb (original)
+++ buildr/trunk/lib/buildr/core/generate.rb Mon Sep 24 04:08:22 2012
@@ -20,7 +20,7 @@ module Buildr
       script = nil
       choose do |menu|
         menu.header = "To use Buildr you need a buildfile. Do you want me to create one?"
-
+	menu.choice("From eclipse .project files") { script = Generate.from_eclipse(Dir.pwd).join("\n") } if has_eclipse_project?
         menu.choice("From maven2 pom file") { script = Generate.from_maven2_pom('pom.xml').join("\n") } if File.exists?("pom.xml")
         menu.choice("From directory structure") { script = Generate.from_directory(Dir.pwd).join("\n") }
         menu.choice("Skip") { }
@@ -35,14 +35,40 @@ module Buildr
 
     class << self
 
+      def compatibility_option(path)
+        # compile.options.target = '1.5'
+      end
+
+      def get_project_natures(projectFile)
+        return nil unless File.exists?(projectFile)
+        File.open(projectFile) do |f|
+          root = REXML::Document.new(f).root
+          return nil if root == nil
+          natures = root.elements.collect("natures/nature") { |n| n.text }
+          return natures if natures
+        end
+        return nil
+      end
+
+      def get_build_property(path, propertyName)
+        propertiesFile = File.join(path, 'build.properties')
+        return nil unless File.exists?(propertiesFile)
+        inhalt = Hash.from_java_properties(File.read(propertiesFile))
+        binDef = inhalt[propertyName]
+      end
+
+      def has_eclipse_project?
+        candidates = Dir.glob("**/.project")
+        return false if candidates.size == 0
+        candidates.find { |x| get_project_natures(x) }
+        return false
+      end
+
+
       HEADER = "# Generated by Buildr #{Buildr::VERSION}, change to your liking\n\n"
 
-      def from_directory(path = Dir.pwd, root = true)
-        Dir.chdir(path) do
-          name = File.basename(path)
-          if root
-            script = HEADER.split("\n")
-            header = <<-EOF
+      def getEclipseBuildfileHeader(path, name)
+        x = <<-EOF
 #{"require 'buildr/scala'\n" if Dir.glob(path + "/**/*.scala").size > 0}
 #{"require 'buildr/groovy'\n" if Dir.glob(path + "/**/*.groovy").size > 0}
 # Version number for this release
@@ -61,6 +87,121 @@ define "#{name}" do
   project.group = GROUP
   manifest["Implementation-Vendor"] = COPYRIGHT
             EOF
+        return x
+      end
+
+      def setLayout(source=nil, output = nil)
+        script = ""
+        if source
+          source = source.sub(/\/$/, '') # remove trailing /
+          script += <<-EOF
+  layout[:source, :main, :java] = "#{source}"
+  layout[:source, :main, :scala] = "#{source}"
+EOF
+        end
+        if output
+          output = output.sub(/\/$/, '') # remove trailing /
+          script += <<-EOF
+  layout[:target, :main] = "#{output}"
+  layout[:target, :main, :java] = "#{output}"
+  layout[:target, :main, :scala] = "#{output}"
+EOF
+        end
+        return script
+      end
+
+      # tries to read as much information as needed at the moment from an existing Eclipse workspace
+      # Here are some links to the relevant information
+      # * http://help.eclipse.org/juno/index.jsp?topic=/org.eclipse.pde.doc.user/reference/pde_feature_generating_build.htm
+      # * http://wiki.eclipse.org/FAQ_What_is_the_plug-in_manifest_file_%28plugin.xml%29%3F
+      # * http://help.eclipse.org/juno/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/misc/bundle_manifest.html
+      # * http://help.eclipse.org/juno/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/misc/plugin_manifest.html
+      # * http://help.eclipse.org/juno/index.jsp?topic=/org.eclipse.pde.doc.user/tasks/pde_compilation_env.htm
+      def from_eclipse(path = Dir.pwd, root = true)
+        # We need two passes to be able to determine the dependencies correctly
+        Dir.chdir(path) do
+          name = File.basename(path)
+          dot_projects = []
+          mf = nil # avoid reloading manifest
+          if root
+            @@allProjects = Hash.new
+            @@topDir = File.expand_path(Dir.pwd)
+            script = HEADER.split("\n")
+            script << "require 'buildr/ide/eclipse'"
+            header = getEclipseBuildfileHeader(path, name)
+            script += header.split("\n")
+            script << "  # you may see hints about which jars are missing and should resolve them correctly"
+            script << "  # dependencies  << 'junit should be commented out and replace by correct ARTIFACT definition. Eg"
+            script << "  # dependencies  << 'junit:junit:jar:3.8.2'"
+            script << setLayout('src', 'bin') # default values for eclipse
+            dot_projects = Dir.glob('**/.project').find_all { |dot_project| get_project_natures(dot_project) }
+            dot_projects.sort.each { |dot_project| from_eclipse(File.dirname(dot_project), false) } if dot_projects
+          else
+            # Skip fragments. Buildr cannot handle it without the help of buildr4osgi
+            return [""] if File.exists?('fragment.xml')
+            projectName = name
+            version = ""
+            mfName = File.join('META-INF', 'MANIFEST.MF')
+            if File.exists?(mfName)
+              mf = Packaging::Java::Manifest.parse(IO.readlines(mfName).join(''))
+              if mf.main['Bundle-SymbolicName']
+                projectName = mf.main['Bundle-SymbolicName'].split(';')[0]
+                bundleVersion = mf.main['Bundle-Version']
+                version = ", :version => \"#{bundleVersion}\"" unless "1.0.0".eql?(bundleVersion)
+              end
+            end
+            # in the first run we just want to know that we exist
+            unless @@allProjects[projectName]
+              @@allProjects[projectName] = Dir.pwd
+              return
+            end
+            base_dir = ""
+            unless File.join(@@topDir, projectName).eql?(File.expand_path(Dir.pwd))
+              base_dir = ", :base_dir => \"#{File.expand_path(Dir.pwd).sub(@@topDir+File::SEPARATOR, '')}\""
+            end
+            script = [%{define "#{projectName}"#{version}#{base_dir} do}]
+          end
+          natures = get_project_natures('.project')
+          if natures && natures.index('org.eclipse.pde.PluginNature')
+            script << "  package(:jar)"
+          end
+          if mf && mf.main['Require-Bundle']
+            mf.main['Require-Bundle'].split(',').each do
+              |bundle|
+              requiredName = bundle.split(';')[0]
+              if @@allProjects.has_key?(requiredName)
+                script << "  dependencies << projects(\"#{requiredName}\")"
+              else
+                script << "  # dependencies  << '#{requiredName}'"
+              end
+            end
+          end
+          script << "  compile.with dependencies # Add more classpath dependencies" if Dir.glob(File.join('src', '**', '*.java')).size > 0
+          script << "  resources" if File.exist?("rsc")
+          sourceProp = get_build_property('.', 'source..')
+          outputProp = get_build_property('.', 'output..')
+          if (sourceProp && !/src\/+/.match(sourceProp)) or (outputProp && !/bin\/+/.match(outputProp))
+            setLayout(sourceProp, outputProp) # default values are overridden in this project
+          end
+          unless dot_projects.empty?
+            script << ""
+            dot_projects.sort.each do |dot_project|
+              next if File.dirname(File.expand_path(dot_project)).eql?(File.expand_path(Dir.pwd))
+              next unless get_project_natures(dot_project)
+              script << from_eclipse(File.dirname(dot_project), false).flatten.map { |line| "  " + line } << ""
+            end
+          end
+          script << "end\n\n"
+          script.flatten
+        end
+      end
+
+      def from_directory(path = Dir.pwd, root = true)
+        Dir.chdir(path) do
+          name = File.basename(path)
+          if root
+            script = HEADER.split("\n")
+            header = getEclipseBuildfileHeader(path, name)
             script += header.split("\n")
           else
             script = [ %{define "#{name}" do} ]

Added: buildr/trunk/spec/core/generate_from_eclipse_spec.rb
URL: http://svn.apache.org/viewvc/buildr/trunk/spec/core/generate_from_eclipse_spec.rb?rev=1389201&view=auto
==============================================================================
--- buildr/trunk/spec/core/generate_from_eclipse_spec.rb (added)
+++ buildr/trunk/spec/core/generate_from_eclipse_spec.rb Mon Sep 24 04:08:22 2012
@@ -0,0 +1,280 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with this
+# work for additional information regarding copyright ownership.  The ASF
+# licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helpers'))
+
+module EclipseHelper
+  
+  def setupExample(group, projectName, options = {})
+	options[:symName] ? symName = options[:symName] :symName = File.basename(projectName)
+	requiredPlugins = nil
+	if options[:requires]
+	  requiredPlugins = "Require-Bundle: #{options[:requires]};bundle-version=\"1.1.0\",\n"
+	end
+        write "#{group}/#{projectName}/META-INF/MANIFEST.MF", <<-MANIFEST
+Manifest-Version: 1.0
+Name: #{projectName}
+Bundle-Version: 1.1
+Specification-Title: "Examples for #{File.basename(projectName)}"
+Specification-Version: "1.0"
+Specification-Vendor: "Acme Corp.".
+Implementation-Title: "#{File.basename(projectName)}"
+Implementation-Version: "build57"
+Implementation-Vendor: "Acme Corp."
+Bundle-SymbolicName: #{symName}
+#{requiredPlugins}
+MANIFEST
+        write "#{group}/#{projectName}/.project", <<-DOT_PROJECT
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+        <name>#{File.basename(projectName)}</name>
+        <comment></comment>
+        <projects>
+        </projects>
+        <buildSpec>
+                <buildCommand>
+                        <name>org.eclipse.jdt.core.javabuilder</name>
+                        <arguments>
+                        </arguments>
+                </buildCommand>
+                <buildCommand>
+                        <name>org.eclipse.pde.ManifestBuilder</name>
+                        <arguments>
+                        </arguments>
+                </buildCommand>
+                <buildCommand>
+                        <name>org.eclipse.pde.SchemaBuilder</name>
+                        <arguments>
+                        </arguments>
+                </buildCommand>
+        </buildSpec>
+        <natures>
+                <nature>org.eclipse.pde.PluginNature</nature>
+                <nature>org.eclipse.jdt.core.javanature</nature>
+        </natures>
+</projectDescription>
+DOT_PROJECT
+
+write "#{group}/#{projectName}/.classpath", <<-DOT_CLASSPATH
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+        <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+        <classpathentry kind="src" path="src"/>
+        <classpathentry combineaccessrules="false" kind="src" path="/another.plugin"/>
+        <classpathentry kind="output" path="bin"/>
+</classpath>
+DOT_CLASSPATH
+
+write "#{group}/#{projectName}/plugin.xml", <<-PLUGIN_XML
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<plugin>
+<!--some comment
+-->
+</plugin>
+PLUGIN_XML
+        write "#{group}/#{projectName}/build.properties", <<-BUILD_PROPERTIES
+source.. = src/
+output.. = bin/
+javacDefaultEncoding.. = UTF-8
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               rsc/,
+BUILD_PROPERTIES
+  end
+end
+
+describe Buildr::Generate do
+  include EclipseHelper
+  describe 'it should find a single eclipse project' do
+    top = "top_#{__LINE__}"
+    
+    before do
+      setupExample(top, 'single')
+      File.open(File.join(top, 'buildfile'), 'w') { |file| file.write Generate.from_eclipse(File.join(Dir.pwd, top)).join("\n") }
+    end
+    
+    it 'should create a valid buildfile' do
+      define('first')
+      File.exists?(File.join(top, 'single', '.project')).should be_true
+      File.exists?(File.join(top, '.project')).should be_false
+      File.exists?('.project').should be_false
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain("GROUP = \"#{top}\"")
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+
+    it "should not add project if the corresponding .project file is not an eclipse project" do
+      buildFile = File.join(top, 'buildfile')
+      FileUtils.rm(buildFile)
+      write File.join(top, 'myproject', '.project'), '# Dummy file'
+      File.open(File.join(top, 'buildfile'), 'w') { |file| file.write Generate.from_eclipse(File.join(Dir.pwd, top)).join("\n") }
+      file(buildFile).should exist
+      file(buildFile).should contain('define "single"')
+      file(buildFile).should_not contain('define "myproject"')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+
+      it 'should honour Bundle-Version in MANIFEST.MF' do
+	define('bundle_version')
+	buildFile = File.join(top, 'buildfile')
+	file(buildFile).should exist
+	file(buildFile).should contain('define "single"')
+	file(buildFile).should contain('define "single", :version => "1.1"')
+	lambda { Buildr.application.run }.should_not raise_error
+      end
+
+    it "should pass source in build.properties to layout[:source, :main, :java] and layout[:source, :main, :scala]" do
+      define('layout_source')
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain('define')
+      file(buildFile).should contain('define "single"')
+      file(buildFile).should contain('layout[:source, :main, :java]')
+      file(buildFile).should contain('layout[:source, :main, :scala]')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+    
+    it "should pass output in build.properties to layout[:target, :main], etc" do
+      define('layout_target')
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain('define')
+      file(buildFile).should contain('define "single"')
+      file(buildFile).should contain('layout[:target, :main]')
+      file(buildFile).should contain('layout[:target, :main, :java]')
+      file(buildFile).should contain('layout[:target, :main, :scala]')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+    
+    it "should package an eclipse plugin" do
+      define('layout_target')
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain('define')
+      file(buildFile).should contain('package(:jar)')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+
+  end
+
+  describe 'it should check for a SymbolicName in MANIFEST.MF' do
+    top = "top_#{__LINE__}"
+    before do
+      setupExample(top, 'single', { :symName => 'singleSymbolicName'} )
+      File.open(File.join(top, 'buildfile'), 'w') { |file| file.write Generate.from_eclipse(File.join(Dir.pwd, top)).join("\n") }
+    end
+    it "should set the project name to the SymbolicName from the MANIFEST.MF" do
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain('define "singleSymbolicName"')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+  end
+    
+  describe 'it should accecpt singleton SymbolicName in MANIFEST.MF' do
+    top = "top_#{__LINE__}"
+    before do
+      setupExample(top, 'single', { :symName => 'singleSymbolicName;singleton:=true'})
+      File.open(File.join(top, 'buildfile'), 'w') { |file| file.write Generate.from_eclipse(File.join(Dir.pwd, top)).join("\n") }
+    end
+    
+    it "should not get confused if SymbolicName in MANIFEST.MF is a singleton:=true" do
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain('define "singleSymbolicName"')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+  end
+
+  describe 'it should find an eclipse project deep' do
+    top = "top_#{__LINE__}"
+    before do
+      setupExample(top, 'nested/single')
+      File.open(File.join(top, 'buildfile'), 'w') { |file| file.write Generate.from_eclipse(File.join(Dir.pwd, top)).join("\n") }
+    end
+
+    it 'should create a valid buildfile for a nested project' do
+      setupExample(top, 'single')
+      define('nested/second')
+      File.exists?(File.join(top, 'single', '.project')).should be_true
+      File.exists?(File.join(top, '.project')).should be_false
+      File.exists?('.project').should be_false
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should contain("GROUP = \"#{top}\"")
+      file(buildFile).should contain('define "single"')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+    
+    it "should correct the path for a nested plugin" do
+      define('base_dir')
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain('define "single"')
+      file(buildFile).should contain('define "single", :version => "1.1", :base_dir => "nested/single"')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+
+  end
+
+  describe 'it should detect dependencies between projects' do
+    top = "top_#{__LINE__}"
+    before do
+      setupExample(top, 'first')
+      write(File.join(top, 'first', 'src','org','demo','Demo.java'))
+      write(File.join(top, 'first', 'rsc','aResource.txt'))
+      setupExample(top, 'second', { :requires => 'first'} )
+      write(File.join(top, 'second', 'src','org','second','Demo.java'))
+      setupExample(top, 'aFragment', { :fragment_host => 'second'})
+      write(File.join(top, 'aFragment', 'fragment.xml'))
+      File.open(File.join(top, 'buildfile'), 'w') { |file| file.write Generate.from_eclipse(File.join(Dir.pwd, top)).join("\n") }
+    end
+
+    it 'should add "compile.with dependencies" in MANIFEST.MF' do
+      define('base_dir')
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain('compile.with dependencies')
+      file(buildFile).should contain('resources')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+                                                           #dependencies << projects("first")
+
+    it 'should honour Require-Bundle in MANIFEST.MF' do
+      define('base_dir')
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+      file(buildFile).should contain(/define "second"/)
+      file(buildFile).should contain(                     /dependencies << projects\("first"\)/)
+      file(buildFile).should contain(/define "second".*do.*dependencies << projects\("first"\).* end/m)
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+    
+    # Fragments are only supported with buildr4osi which is not (yet?) part of buildr
+    it 'should skip fragments.'  do
+      define('base_dir')
+      buildFile = File.join(top, 'buildfile')
+      file(buildFile).should exist
+#      system("cat #{buildFile}")  # if $VERBOSE
+      file(buildFile).should contain('define "first"')
+      lambda { Buildr.application.run }.should_not raise_error
+    end
+
+  end
+
+end