Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Somewhat hacky modifications to allow RubyGems to install Maven artif…

…acts. Incorporates maven_gem into main repository for now.
  • Loading branch information...
commit eeda76472c6271b05f5571449b98a27f2ef844d8 1 parent 373815b
@headius headius authored
View
113 lib/ruby/site_ruby/1.8/rubygems/remote_fetcher.rb
@@ -4,6 +4,7 @@
require 'uri'
require 'rubygems'
+require 'maven_gem'
##
# RemoteFetcher handles the details of fetching gems and gem information from
@@ -93,73 +94,81 @@ def download(spec, source_uri, install_dir = Gem.dir)
URI.escape(source_uri))
end
- scheme = source_uri.scheme
+ if source_uri.to_s[/^maven:/]
+ pom_uri = source_uri.to_s.sub("maven:", "")
+ pom_uri += "#{spec.name.gsub('.', '/')}/#{spec.version}/#{spec.name.split('.')[-1]}-#{spec.version}.pom"
+ pom_doc = MavenGem::PomFetcher.fetch(pom_uri)
+ pom = MavenGem::PomSpec.parse_pom(pom_doc)
+ gem_dir = MavenGem::PomSpec.create_gem(spec, pom, local_gem_path)
+ FileUtils.mv "/tmp/" + pom.gem_file, local_gem_path
+ else
+ scheme = source_uri.scheme
- # URI.parse gets confused by MS Windows paths with forward slashes.
- scheme = nil if scheme =~ /^[a-z]$/i
+ # URI.parse gets confused by MS Windows paths with forward slashes.
+ scheme = nil if scheme =~ /^[a-z]$/i
+ case scheme
+ when 'http', 'https' then
+ unless File.exist? local_gem_path then
+ begin
+ say "Downloading gem #{gem_file_name}" if
+ Gem.configuration.really_verbose
- case scheme
- when 'http', 'https' then
- unless File.exist? local_gem_path then
- begin
- say "Downloading gem #{gem_file_name}" if
- Gem.configuration.really_verbose
+ remote_gem_path = source_uri + "gems/#{gem_file_name}"
- remote_gem_path = source_uri + "gems/#{gem_file_name}"
+ gem = self.fetch_path remote_gem_path
+ rescue Gem::RemoteFetcher::FetchError
+ raise if spec.original_platform == spec.platform
- gem = self.fetch_path remote_gem_path
- rescue Gem::RemoteFetcher::FetchError
- raise if spec.original_platform == spec.platform
+ alternate_name = "#{spec.original_name}.gem"
- alternate_name = "#{spec.original_name}.gem"
+ say "Failed, downloading gem #{alternate_name}" if
+ Gem.configuration.really_verbose
- say "Failed, downloading gem #{alternate_name}" if
- Gem.configuration.really_verbose
+ remote_gem_path = source_uri + "gems/#{alternate_name}"
- remote_gem_path = source_uri + "gems/#{alternate_name}"
+ gem = self.fetch_path remote_gem_path
+ end
- gem = self.fetch_path remote_gem_path
+ File.open local_gem_path, 'wb' do |fp|
+ fp.write gem
+ end
end
+ when 'file' then
+ begin
+ path = source_uri.path
+ path = File.dirname(path) if File.extname(path) == '.gem'
+
+ remote_gem_path = File.join(path, 'gems', gem_file_name)
- File.open local_gem_path, 'wb' do |fp|
- fp.write gem
+ FileUtils.cp(remote_gem_path, local_gem_path)
+ rescue Errno::EACCES
+ local_gem_path = source_uri.to_s
end
- end
- when 'file' then
- begin
- path = source_uri.path
- path = File.dirname(path) if File.extname(path) == '.gem'
- remote_gem_path = File.join(path, 'gems', gem_file_name)
+ say "Using local gem #{local_gem_path}" if
+ Gem.configuration.really_verbose
+ when nil then # TODO test for local overriding cache
+ source_path = if Gem.win_platform? && source_uri.scheme &&
+ !source_uri.path.include?(':') then
+ "#{source_uri.scheme}:#{source_uri.path}"
+ else
+ source_uri.path
+ end
- FileUtils.cp(remote_gem_path, local_gem_path)
- rescue Errno::EACCES
- local_gem_path = source_uri.to_s
- end
+ source_path = URI.unescape source_path
- say "Using local gem #{local_gem_path}" if
- Gem.configuration.really_verbose
- when nil then # TODO test for local overriding cache
- source_path = if Gem.win_platform? && source_uri.scheme &&
- !source_uri.path.include?(':') then
- "#{source_uri.scheme}:#{source_uri.path}"
- else
- source_uri.path
- end
-
- source_path = URI.unescape source_path
-
- begin
- FileUtils.cp source_path, local_gem_path unless
- File.expand_path(source_path) == File.expand_path(local_gem_path)
- rescue Errno::EACCES
- local_gem_path = source_uri.to_s
- end
+ begin
+ FileUtils.cp source_path, local_gem_path unless
+ File.expand_path(source_path) == File.expand_path(local_gem_path)
+ rescue Errno::EACCES
+ local_gem_path = source_uri.to_s
+ end
- say "Using local gem #{local_gem_path}" if
- Gem.configuration.really_verbose
- else
- raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}"
+ say "Using local gem #{local_gem_path}" if
+ Gem.configuration.really_verbose
+ else
+ raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}"
+ end
end
local_gem_path
View
105 lib/ruby/site_ruby/1.8/rubygems/spec_fetcher.rb
@@ -1,5 +1,6 @@
require 'zlib'
require 'fileutils'
+require 'rexml/document'
require 'rubygems/remote_fetcher'
require 'rubygems/user_interaction'
@@ -57,6 +58,9 @@ def initialize
# Returns the local directory to write +uri+ to.
def cache_dir(uri)
+ if uri.to_s[/^maven:/]
+ uri = URI.parse(uri.to_s.sub("maven:", ""))
+ end
File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(uri.path)
end
@@ -101,10 +105,18 @@ def fetch_spec(spec, source_uri)
if File.exist? local_spec then
spec = Gem.read_binary local_spec
else
- uri.path << '.rz'
+ if source_uri.to_s[/^maven:/]
+ pom_uri = source_uri.to_s.sub("maven:", "")
+ pom_uri += "#{spec[0].gsub('.', '/')}/#{spec[1]}/#{spec[0].split('.')[-1]}-#{spec[1]}.pom"
+ pom_doc = MavenGem::PomFetcher.fetch(pom_uri)
+ pom = MavenGem::PomSpec.parse_pom(pom_doc)
+ spec = Marshal.dump(MavenGem::PomSpec.generate_spec(pom))
+ else
+ uri.path << '.rz'
- spec = @fetcher.fetch_path uri
- spec = Gem.inflate spec
+ spec = @fetcher.fetch_path uri
+ spec = Gem.inflate spec
+ end
if @update_cache then
FileUtils.mkdir_p cache_dir
@@ -123,35 +135,85 @@ def fetch_spec(spec, source_uri)
# Find spec names that match +dependency+. If +all+ is true, all
# matching released versions are returned. If +matching_platform+
# is false, gems for all platforms are returned.
+
+ def get_type(all, prerelease)
+ if all
+ :all
+ elsif prerelease
+ :prerelease
+ else
+ :latest
+ end
+ end
+
+ SPEC_FILES = {
+ :latest => 'latest_specs',
+ :prerelease => 'prerelease_specs',
+ :all => 'specs'
+ }
def find_matching_with_errors(dependency, all = false, matching_platform = true, prerelease = false)
- found = {}
+ # TODO: make type the only argument
+ type = get_type(all, prerelease)
+ file = SPEC_FILES[type]
+ cache = { :latest => @latest_specs,
+ :prerelease => @prerelease_specs,
+ :all => @specs }[type]
+
+ specs_and_sources = []
rejected_specs = {}
- list(all, prerelease).each do |source_uri, specs|
- found[source_uri] = specs.select do |spec_name, version, spec_platform|
- if dependency.match?(spec_name, version)
- if matching_platform and !Gem::Platform.match(spec_platform)
- pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version))
- pm.add_platform spec_platform
- false
- else
- true
+ Gem.sources.each do |source_uri|
+ if source_uri[/^maven:/]
+ # maven processing
+
+ # peel off maven:
+ maven_repo = source_uri.sub("maven:", "")
+ # add metadata xml
+ maven_repo += dependency.name.gsub(".", "/") + "/maven-metadata.xml"
+
+ # fetch and process metadata XML
+ maven_uri = URI.parse(maven_repo)
+ begin
+ xml_data = @fetcher.fetch_path(maven_uri)
+
+ xml = REXML::Document.new(xml_data)
+
+ # scan all versions for matching ones
+ REXML::XPath.each(xml, "//versions/version") do |version|
+ if dependency.match?(dependency.name, version.text)
+ # no platform check here, since maven jars are a jruby-specific feature
+ specs_and_sources.push [[dependency.name, version.text, "java"], source_uri.to_s]
+ end
+ end
+ end
+ else
+ source_uri = URI.parse source_uri
+
+ unless cache.include? source_uri
+ cache[source_uri] = load_specs source_uri, file
+ end
+
+ cache[source_uri].each do |gem|
+ # filter out prereleases
+ next if type == all && (!gems[1] || gems[1].prerelease?)
+
+ spec_name, version, spec_platform = gem
+ if dependency.match?(spec_name, version) and
+ if matching_platform and !Gem::Platform.match(spec_platform)
+ pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version))
+ pm.add_platform spec_platform
+ else
+ specs_and_sources.push [gem, source_uri.to_s]
+ end
end
end
end
end
-
+
errors = rejected_specs.values
- specs_and_sources = []
-
- found.each do |source_uri, specs|
- uri_str = source_uri.to_s
- specs_and_sources.push(*specs.map { |spec| [spec, uri_str] })
- end
-
[specs_and_sources, errors]
end
@@ -208,6 +270,7 @@ def list(all = false, prerelease = false)
:all => @specs }[type]
Gem.sources.each do |source_uri|
+ next if source_uri[/^maven:/]
source_uri = URI.parse source_uri
unless cache.include? source_uri
View
26 lib/ruby/site_ruby/shared/maven_gem.rb
@@ -0,0 +1,26 @@
+$:.unshift(File.dirname(__FILE__)) unless
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
+
+require 'maven_gem/xml_utils'
+require 'maven_gem/pom_spec'
+require 'maven_gem/pom_fetcher'
+require 'rubygems'
+require 'rubygems/gem_runner'
+
+module MavenGem
+ def self.install(group, artifact = nil, version = nil)
+ gem = build(group, artifact, version)
+ Gem::GemRunner.new.run(["install", gem])
+ ensure
+ FileUtils.rm_f(gem) if gem
+ end
+
+ def self.build(group, artifact = nil, version = nil)
+ gem = if artifact
+ url = MavenGem::PomSpec.to_maven_url(group, artifact, version)
+ MavenGem::PomSpec.build(url)
+ else
+ MavenGem::PomSpec.build(group)
+ end
+ end
+end
View
31 lib/ruby/site_ruby/shared/maven_gem/pom_fetcher.rb
@@ -0,0 +1,31 @@
+require 'net/http'
+require 'uri'
+
+module MavenGem
+ class PomFetcher
+
+ def self.fetch(path, options = {})
+ puts "Reading POM from #{path}" if options[:verbose]
+
+ fetch_pom(path, options)
+ end
+
+ def self.clean_pom(pom) #avoid namespaces errors and gotchas
+ pom.gsub(/<project[^>]+/, '<project>')
+ end
+
+ def self.fetch_pom(path, options = {})
+ path =~ /^http:\/\// ? fetch_from_url(path, options) :
+ fetch_from_file(path, options)
+ end
+
+ private
+ def self.fetch_from_url(path, options = {})
+ Net::HTTP.get(URI.parse(path))
+ end
+
+ def self.fetch_from_file(path, options = {})
+ File.read(path)
+ end
+ end
+end
View
188 lib/ruby/site_ruby/shared/maven_gem/pom_spec.rb
@@ -0,0 +1,188 @@
+require 'ostruct'
+require 'fileutils'
+
+module MavenGem
+ class PomSpec
+ extend MavenGem::XmlUtils
+
+ def self.build(location)
+ pom_doc = MavenGem::PomFetcher.fetch(location)
+ pom = MavenGem::PomSpec.parse_pom(pom_doc)
+ spec = MavenGem::PomSpec.generate_spec(pom)
+ MavenGem::PomSpec.create_gem(spec, pom)
+ end
+
+ # Unless the maven version string is a valid Gem version string create a substitute
+ # gem version string by dividing the maven version string into it's numeric elements
+ # and joining them back together with '.' characters.
+ #
+ # The string 'alpha' in a maven version string is converted to '0'.
+ # The string 'beta' in a maven version string is converted to '1'.
+ #
+ # In general gem versions strings need to either be an Integer or start with a
+ # digit and a '.'.
+ #
+ # For example the flying saucer core-renderer jar that uses itext to generate a
+ # pdf from styled xhtml input has a version string of "R8pre2".
+ #
+ # Installing this jar:
+ #
+ # jruby -S gem maven org/xhtmlrenderer core-renderer R8pre2
+ #
+ # results in:
+ #
+ # Successfully installed core-renderer-8.2-java
+ # 1 gem installed
+ #
+ # jruby -S gem list core-renderer
+ #
+ # *** LOCAL GEMS ***
+ #
+ # core-renderer (8.2)
+ #
+ # In addition the following constants are created in the new maven gem:
+ #
+ # CoreRenderer::VERSION # => "8.2"
+ # CoreRenderer::MAVEN_VERSION # => "R8pre2"
+ #
+ # TODO: Parse maven version number modifiers, i.e: [1.5,) [1.5,1.6], (,1.5]
+ def self.maven_to_gem_version(maven_version)
+ maven_version = maven_version.gsub(/alpha/, '0')
+ maven_version = maven_version.gsub(/beta/, '1')
+ maven_numbers = maven_version.gsub(/\D+/, '.').split('.').find_all { |i| i.length > 0 }
+ if maven_numbers.empty?
+ '0.0.0'
+ else
+ maven_numbers.join('.')
+ end
+ end
+
+ def self.parse_pom(pom_doc, options = {})
+ puts "Processing POM" if options[:verbose]
+
+ pom = OpenStruct.new
+ document = REXML::Document.new(pom_doc)
+
+ pom.group = xpath_group(document)
+ pom.artifact = xpath_text(document, '/project/artifactId')
+ pom.maven_version = xpath_text(document, '/project/version') || xpath_text(document, '/project/parent/version')
+ pom.version = maven_to_gem_version(pom.maven_version)
+ pom.description = xpath_text(document, '/project/description')
+ pom.url = xpath_text(document, '/project/url')
+ pom.dependencies = xpath_dependencies(document)
+ pom.authors = xpath_authors(document)
+
+ pom.name = maven_to_gem_name(pom.group, pom.artifact)
+ pom.lib_name = "#{pom.artifact}.rb"
+ pom.gem_name = "#{pom.name}-#{pom.version}"
+ pom.jar_file = "#{pom.artifact}-#{pom.maven_version}.jar"
+ pom.remote_dir = "#{pom.group.gsub('.', '/')}/#{pom.artifact}/#{pom.version}"
+ pom.remote_jar_url = "#{maven_base_url}/#{pom.remote_dir}/#{pom.jar_file}"
+ pom.gem_file = "#{pom.gem_name}-java.gem"
+ pom
+ end
+
+ def self.generate_spec(pom, options = {})
+ spec = Gem::Specification.new do |specification|
+ specification.platform = "java"
+ specification.version = pom.version
+ specification.name = pom.name
+ pom.dependencies.each {|dep| specification.dependencies << dep}
+ specification.authors = pom.authors
+ specification.description = pom.description
+ specification.homepage = pom.url
+
+ specification.files = ["lib/#{pom.lib_name}", "lib/#{pom.jar_file}"]
+ end
+ end
+
+ def self.create_gem(spec, pom, options = {})
+ gem_dir = create_files(spec, pom, options)
+ ensure
+ FileUtils.rm_r(gem_dir) if gem_dir
+ end
+
+ def self.to_maven_url(group, artifact, version)
+ "#{maven_base_url}/#{group.gsub('.', '/')}/#{artifact}/#{version}/#{artifact}-#{version}.pom"
+ end
+
+ private
+
+ def self.maven_to_gem_name(group, artifact, options = {})
+ "#{group}.#{artifact}"
+ end
+
+ def self.create_files(specification, pom, options = {})
+ gem_dir = create_tmp_directories(pom, options)
+
+ ruby_file_contents(gem_dir, pom, options)
+ jar_file_contents(gem_dir, pom, options)
+ metadata_contents(gem_dir, specification, pom, options)
+ gem_contents(gem_dir, pom, options)
+
+ gem_dir
+ end
+
+ def self.create_tmp_directories(pom, options = {})
+ gem_dir = "/tmp/#{pom.name}.#{$$}"
+ puts "Using #{gem_dir} work dir" if options[:verbose]
+ unless File.exist?(gem_dir)
+ FileUtils.mkdir_p(gem_dir)
+ FileUtils.mkdir_p("#{gem_dir}/lib")
+ end
+ gem_dir
+ end
+
+ def self.ruby_file_contents(gem_dir, pom, options = {})
+ titleized_classname = pom.artifact.split('-').collect { |e| e.capitalize }.join
+ ruby_file_content = <<HEREDOC
+module #{titleized_classname}
+ VERSION = '#{pom.version}'
+ MAVEN_VERSION = '#{pom.maven_version}'
+end
+begin
+ require 'java'
+ require File.dirname(__FILE__) + '/#{pom.jar_file}'
+rescue LoadError
+ puts 'JAR-based gems require JRuby to load. Please visit www.jruby.org.'
+ raise
+end
+HEREDOC
+
+ ruby_file = "#{gem_dir}/lib/#{pom.lib_name}"
+ puts "Writing #{ruby_file}" if options[:verbose]
+ File.open(ruby_file, 'w') do |file|
+ file.write(ruby_file_content)
+ end
+ end
+
+ def self.jar_file_contents(gem_dir, pom, options = {})
+ puts "Fetching #{pom.remote_jar_url}" if options[:verbose]
+ uri = URI.parse(pom.remote_jar_url)
+ jar_contents = Net::HTTP.get(uri)
+ File.open("#{gem_dir}/lib/#{pom.jar_file}", 'w') {|f| f.write(jar_contents)}
+ end
+
+ def self.metadata_contents(gem_dir, spec, pom, options = {})
+ metadata_file = "#{gem_dir}/metadata"
+ puts "Writing #{metadata_file}" if options[:verbose]
+ File.open(metadata_file, 'w') do |file|
+ file.write(spec.to_yaml)
+ end
+ end
+
+ def self.gem_contents(gem_dir, pom, options = {})
+ puts "Building #{pom.gem_file}" if options[:verbose]
+ Dir.chdir(gem_dir) do
+ fail unless
+ system('gzip metadata') and
+ system('tar czf data.tar.gz lib/*') and
+ system("tar cf ../#{pom.gem_file} data.tar.gz metadata.gz")
+ end
+ end
+
+ def self.maven_base_url
+ "http://mirrors.ibiblio.org/pub/mirrors/maven2"
+ end
+ end
+end
View
49 lib/ruby/site_ruby/shared/maven_gem/xml_utils.rb
@@ -0,0 +1,49 @@
+require 'rexml/document'
+require 'rexml/xpath'
+
+module MavenGem
+ module XmlUtils
+ def xpath_text(element, node)
+ first = REXML::XPath.first(element, node) and first.text
+ end
+
+ def xpath_dependencies(element)
+ deps = REXML::XPath.first(element, '/project/dependencies')
+ pom_dependencies = []
+
+ if deps
+ deps.elements.each do |dep|
+ next if xpath_text(dep, 'optional') == 'true'
+
+ dep_group = xpath_text(dep, 'groupId')
+ dep_artifact = xpath_text(dep, 'artifactId')
+ dep_version = xpath_text(dep, 'version')
+
+ # TODO: Parse maven version number modifiers, i.e: [1.5,)
+ pom_dependencies << if dep_version
+ Gem::Dependency.new(maven_to_gem_name(dep_group, dep_artifact),
+ "=#{maven_to_gem_version(dep_version)}")
+ else
+ Gem::Dependency.new(maven_to_gem_name(dep_group, dep_artifact))
+ end
+ end
+ end
+
+ pom_dependencies
+ end
+
+ def xpath_authors(element)
+ developers = REXML::XPath.first(element, 'project/developers')
+
+ authors = if developers
+ developers.elements.map do |el|
+ xpath_text(el, 'name')
+ end
+ end || []
+ end
+
+ def xpath_group(element)
+ xpath_text(element, '/project/groupId') || xpath_text(element, '/project/parent/groupId')
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.