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


Reply via email to