diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 8aef28f..a31c147 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -1,31 +1,27 @@
-name: Ruby
+name: jar-dependencies
-on:
- push:
- branches: [ master ]
- pull_request:
- branches: [ master ]
+on: [push, pull_request]
jobs:
test:
-
runs-on: ubuntu-latest
strategy:
+ fail-fast: false
matrix:
- ruby-version: [jruby-9.3, jruby-9.4, jruby-10.0]
+ ruby-version: [jruby-9.4, jruby-10.0, jruby-head]
steps:
- - uses: actions/checkout@v5
- - name: Set up Ruby
+ - uses: actions/checkout@v6
+ - name: Set up JRuby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
bundler-cache: true
- - name: Install dependencies
- run: bundle install
- - name: Run test
- run: jruby -Ilib -rbundler/setup -S rake specs
+ - name: Download Mima jars
+ run: bundle exec rake download_jars
+ - name: Run tests
+ run: bundle exec rake specs
- name: Run RuboCop
if: matrix.ruby-version == 'jruby-10.0' # Only run RuboCop on modern JRuby; target older jrubies via .rubocop.yml
- run: jruby -Ilib -rbundler/setup -S rubocop lib specs
+ run: bundle exec rubocop lib
diff --git a/.gitignore b/.gitignore
index abf24fc..001f426 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ build.log
pom*
specs/repository/*
example/dependencies.list
+lib/jars/mima/*.jar
diff --git a/Gemfile b/Gemfile
index 470ea63..04be930 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-source 'https://rubygems.org'
+source 'https://gem.coop'
gemspec
diff --git a/Rakefile b/Rakefile
index 288002a..383cd83 100644
--- a/Rakefile
+++ b/Rakefile
@@ -4,6 +4,7 @@ task default: [:specs]
require 'bundler/gem_tasks'
require 'rubocop/rake_task'
+require 'rake/clean'
RuboCop::RakeTask.new
@@ -15,3 +16,47 @@ task :specs do
require File.basename(f.sub(/.rb$/, ''))
end
end
+
+require_relative 'lib/jars/mima/version'
+
+MIMA_VERSION = Jars::Mima::MIMA_VERSION
+SLF4J_VERSION = Jars::Mima::SLF4J_VERSION
+MAVEN_CENTRAL = 'https://repo.maven.apache.org/maven2'
+MIMA_DIR = 'lib/jars/mima'
+
+MIMA_JARS = {
+ "slf4j-api-#{SLF4J_VERSION}.jar" =>
+ "#{MAVEN_CENTRAL}/org/slf4j/slf4j-api/#{SLF4J_VERSION}/slf4j-api-#{SLF4J_VERSION}.jar",
+ "slf4j-simple-#{SLF4J_VERSION}.jar" =>
+ "#{MAVEN_CENTRAL}/org/slf4j/slf4j-simple/#{SLF4J_VERSION}/slf4j-simple-#{SLF4J_VERSION}.jar",
+ "jcl-over-slf4j-#{SLF4J_VERSION}.jar" =>
+ "#{MAVEN_CENTRAL}/org/slf4j/jcl-over-slf4j/#{SLF4J_VERSION}/jcl-over-slf4j-#{SLF4J_VERSION}.jar",
+ "context-#{MIMA_VERSION}.jar" =>
+ "#{MAVEN_CENTRAL}/eu/maveniverse/maven/mima/context/#{MIMA_VERSION}/context-#{MIMA_VERSION}.jar",
+ "standalone-static-uber-#{MIMA_VERSION}.jar" =>
+ "#{MAVEN_CENTRAL}/eu/maveniverse/maven/mima/runtime/standalone-static-uber/#{MIMA_VERSION}/standalone-static-uber-#{MIMA_VERSION}.jar"
+}
+
+MIMA_JARS.each_key { |jar| CLEAN.include(File.join(MIMA_DIR, jar)) }
+
+desc 'download Mima (and dependent SLF4J) jars'
+task :download_jars do
+ require 'fileutils'
+ require 'open-uri'
+
+ FileUtils.mkdir_p(MIMA_DIR)
+
+ MIMA_JARS.each do |filename, url|
+ target = File.join(MIMA_DIR, filename)
+ if File.exist?(target)
+ puts " exists: #{target}"
+ next
+ end
+
+ puts " downloading #{filename}..."
+ URI.open(url) do |remote| # rubocop:disable Security/Open
+ File.open(target, 'wb') { |f| f.write(remote.read) }
+ end
+ puts " saved: #{target}"
+ end
+end
diff --git a/jar-dependencies.gemspec b/jar-dependencies.gemspec
index fe2f0db..b7bfc2a 100644
--- a/jar-dependencies.gemspec
+++ b/jar-dependencies.gemspec
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
s.homepage = 'https://github.com/jruby/jar-dependencies'
s.bindir = 'exe'
- s.executables = [lock_jars = 'lock_jars']
+ s.executables = ['lock_jars']
s.license = 'MIT'
@@ -30,18 +30,6 @@ Gem::Specification.new do |s|
s.required_ruby_version = '>= 2.6'
s.add_development_dependency 'minitest', '~> 5.10'
- s.add_development_dependency 'ruby-maven', ruby_maven_version = '~> 3.9'
- s.post_install_message = <<~TEXT
-
- if you want to use the executable #{lock_jars} then install ruby-maven gem before using #{lock_jars}
-
- $ gem install ruby-maven -v '#{ruby_maven_version}'
-
- or add it as a development dependency to your Gemfile
-
- gem 'ruby-maven', '#{ruby_maven_version}'
-
- TEXT
s.metadata['rubygems_mfa_required'] = 'true'
end
diff --git a/lib/jar_dependencies.rb b/lib/jar_dependencies.rb
index 54232c7..212b6e4 100644
--- a/lib/jar_dependencies.rb
+++ b/lib/jar_dependencies.rb
@@ -51,8 +51,9 @@ module Jars
UNKNOWN = 'unknown'
end
- autoload :MavenSettings, 'jars/maven_settings'
autoload :Classpath, 'jars/classpath'
+ autoload :MavenSettings, 'jars/maven_settings'
+ autoload :Mima, 'jars/mima'
@jars_lock = false
@jars = {}
@@ -144,12 +145,7 @@ def lock
def jars_lock_from_class_loader
return unless defined?(JRUBY_VERSION)
- if JRuby::Util.respond_to?(:class_loader_resources)
- JRuby::Util.class_loader_resources('Jars.lock')
- else
- require 'jruby'
- JRuby.runtime.jruby_class_loader.get_resources('Jars.lock').collect(&:to_s)
- end
+ JRuby::Util.class_loader_resources('Jars.lock')
end
def lock_path(basedir = nil)
diff --git a/lib/jars/attach_jars_pom.rb b/lib/jars/attach_jars_pom.rb
deleted file mode 100644
index d08ba36..0000000
--- a/lib/jars/attach_jars_pom.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-# this file is maven DSL
-
-10_000.times do |i|
- coord = ENV_JAVA["jars.#{i}"]
- break unless coord
-
- artifact = Maven::Tools::Artifact.from_coordinate(coord)
- exclusions = []
- 10_000.times do |j|
- exclusion = ENV_JAVA["jars.#{i}.exclusions.#{j}"]
- break unless exclusion
-
- exclusions << exclusion
- end
- scope = ENV_JAVA["jars.#{i}.scope"]
- artifact.scope = scope if scope
- classifier = ENV_JAVA["jars.#{i}.classifier"]
- artifact.classifier = classifier if classifier
-
- # declare the artifact inside the POM
- dependency_artifact(artifact) do
- exclusions.each do |ex|
- exclusion ex
- end
- end
-end
diff --git a/lib/jars/gemspec_pom.rb b/lib/jars/gemspec_pom.rb
deleted file mode 100644
index d997623..0000000
--- a/lib/jars/gemspec_pom.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-# this file is maven DSL and used by maven via jars/maven_exec.rb
-
-def eval_file(file)
- file = File.join(__dir__, file)
- eval(File.read(file), nil, file) # rubocop:disable Security/Eval
-end
-
-eval_file('attach_jars_pom.rb')
-eval_file('output_jars_pom.rb')
diff --git a/lib/jars/installer.rb b/lib/jars/installer.rb
index 4f73d6f..6dd6c78 100644
--- a/lib/jars/installer.rb
+++ b/lib/jars/installer.rb
@@ -171,7 +171,7 @@ def install_jars(write_require_file: true)
end
def ruby_maven_install_options=(options)
- @mvn.ruby_maven_install_options = options
+ # no-op: kept for backward compatibility with post_install_hook
end
def jars?
diff --git a/lib/jars/lock_down.rb b/lib/jars/lock_down.rb
index 4e9ebbb..d04317d 100644
--- a/lib/jars/lock_down.rb
+++ b/lib/jars/lock_down.rb
@@ -3,7 +3,6 @@
require 'fileutils'
require 'jar_dependencies'
require 'jars/version'
-require 'jars/maven_factory'
require 'jars/gemspec_artifacts'
module Jars
@@ -15,38 +14,49 @@ def initialize(debug, verbose)
@verbose = verbose
end
- def maven_new
- factory = MavenFactory.new({}, @debug, @verbose)
- pom = File.expand_path('lock_down_pom.rb', __dir__)
- m = factory.maven_new(pom)
- m['jruby.plugins.version'] = Jars::JRUBY_PLUGINS_VERSION
- m['dependency.plugin.version'] = Jars::DEPENDENCY_PLUGIN_VERSION
- m['jars.basedir'] = File.expand_path(basedir)
- jarfile = File.expand_path(Jars.jarfile)
- m['jars.jarfile'] = jarfile if File.exist?(jarfile)
- attach_jar_coordinates_from_bundler_dependencies(m)
- m
+ def basedir
+ File.expand_path('.')
end
- private :maven_new
- def maven
- @maven ||= maven_new
- end
+ def collect_artifacts
+ artifacts = []
+ done = []
- def basedir
- File.expand_path('.')
+ attach_jar_coordinates_from_bundler_dependencies(artifacts, done)
+
+ # Also collect from local gemspec if present
+ specs = Dir['*.gemspec']
+ if specs.size == 1
+ spec = eval(File.read(specs.first), TOPLEVEL_BINDING, specs.first) # rubocop:disable Security/Eval
+ ga = GemspecArtifacts.new(spec)
+ ga.artifacts.each do |a|
+ unless done.include?(a.key)
+ artifacts << a
+ done << a.key
+ end
+ end
+ end
+
+ artifacts
end
+ private :collect_artifacts
- def attach_jar_coordinates_from_bundler_dependencies(maven)
+ def attach_jar_coordinates_from_bundler_dependencies(artifacts, done)
load_path = $LOAD_PATH.dup
require 'bundler'
# TODO: make this group a commandline option
Bundler.setup('default')
- maven.property('jars.bundler', true)
cwd = File.expand_path('.')
Gem.loaded_specs.each_value do |spec|
- # if gemspec is local then include all dependencies
- maven.attach_jars(spec, all_dependencies: cwd == spec.full_gem_path)
+ all = cwd == spec.full_gem_path # if gemspec is local then include all dependencies
+ ga = GemspecArtifacts.new(spec)
+ ga.artifacts.each do |a|
+ next if done.include?(a.key)
+ next unless all || (a.scope != 'provided' && a.scope != 'test')
+
+ artifacts << a
+ done << a.key
+ end
end
rescue LoadError => e
if Jars.verbose?
@@ -62,40 +72,73 @@ def attach_jar_coordinates_from_bundler_dependencies(maven)
$LOAD_PATH.replace(load_path)
end
- def lock_down(vendor_dir = nil, force: false, update: false, tree: nil)
- out = File.expand_path('.jars.output')
- tree_provided = tree
- tree ||= File.expand_path('.jars.tree')
- maven.property('jars.outputFile', out)
- maven.property('maven.repo.local', Jars.local_maven_repo)
- maven.property('jars.home', File.expand_path(vendor_dir)) if vendor_dir
- maven.property('jars.lock', File.expand_path(Jars.lock))
- maven.property('jars.force', force)
- maven.property('jars.update', update) if update
- # tell not to use Jars.lock as part of POM when running mvn
- maven.property('jars.skip.lock', true)
-
- args = ['gem:jars-lock']
- args += ['dependency:tree', '-P -gemfile.lock', "-DoutputFile=#{tree}"] if tree_provided
+ def lock_down(vendor_dir = nil, force: false, update: false, tree: nil) # rubocop:disable Lint/UnusedMethodArgument
+ require 'jars/mima'
+
+ lock_file = File.expand_path(Jars.lock)
+
+ if !force && File.exist?(lock_file)
+ puts 'Jars.lock already exists, use --force to overwrite'
+ return
+ end
+
+ artifacts = collect_artifacts
+
+ if artifacts.empty?
+ puts 'no jar dependencies found'
+ return
+ end
puts
puts '-- jar root dependencies --'
puts
- status = maven.exec(*args)
- exit 1 unless status
- if File.exist?(tree)
+ artifacts.each do |a|
+ puts " #{a.to_gacv}:#{a.scope || 'compile'}"
+ puts " exclusions: #{a.exclusions}" if a.exclusions && !a.exclusions.empty?
+ end
+
+ context = Jars::Mima.create_context
+ begin
+ resolved = Jars::Mima.resolve_with_context(context, artifacts, all_dependencies: true)
+ ensure
+ context.close
+ end
+
+ # Write Jars.lock
+ File.open(lock_file, 'w') do |f|
+ resolved.each do |dep|
+ next unless dep.type == 'jar'
+
+ f.puts dep.to_lock_entry
+ end
+ end
+
+ # Optionally vendor jars
+ if vendor_dir
+ vendor_path = File.expand_path(vendor_dir)
+ resolved.each do |dep|
+ next unless dep.type == 'jar' && dep.runtime? && !dep.system?
+
+ target = File.join(vendor_path, dep.jar_path)
+ FileUtils.mkdir_p(File.dirname(target))
+ FileUtils.cp(dep.file, target)
+ end
+ end
+
+ if tree
puts
puts '-- jar dependency tree --'
puts
- puts File.read(tree)
+ resolved.each do |dep|
+ prefix = dep.classifier ? "#{dep.classifier}:" : ''
+ puts " #{dep.group_id}:#{dep.artifact_id}:#{prefix}#{dep.version}:#{dep.scope}"
+ end
puts
end
+
puts
- puts File.read(out).gsub("#{File.dirname(out)}/", '')
+ puts File.read(lock_file)
puts
- ensure
- FileUtils.rm_f out
- FileUtils.rm_f tree
end
end
end
diff --git a/lib/jars/lock_down_pom.rb b/lib/jars/lock_down_pom.rb
deleted file mode 100644
index 3dffa35..0000000
--- a/lib/jars/lock_down_pom.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-# this file is maven DSL and used by maven via jars/lock_down.rb
-
-basedir(ENV_JAVA['jars.basedir'])
-
-def eval_file(file)
- file = File.join(__dir__, file)
- eval(File.read(file), nil, file) # rubocop:disable Security/Eval
-end
-
-eval_file('attach_jars_pom.rb')
-
-jfile = ENV_JAVA['jars.jarfile']
-jarfile(jfile) if jfile
-
-# need to fix the version of this plugin for gem:jars_lock goal
-jruby_plugin :gem, ENV_JAVA['jruby.plugins.version']
-
-# if you use bundler we collect all root jar dependencies
-# from each gemspec file. otherwise we need to resolve
-# the gemspec artifact in the maven way
-unless ENV_JAVA['jars.bundler']
- begin
- gemspec
- rescue
- nil
- end
-end
-
-properties('project.build.sourceEncoding' => 'utf-8')
-
-plugin :dependency, ENV_JAVA['dependency.plugin.version']
-
-eval_file('output_jars_pom.rb')
diff --git a/lib/jars/maven_exec.rb b/lib/jars/maven_exec.rb
index 798c018..db82755 100644
--- a/lib/jars/maven_exec.rb
+++ b/lib/jars/maven_exec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'jar_dependencies'
-require 'jars/maven_factory'
+require 'jars/gemspec_artifacts'
module Jars
class MavenExec
@@ -65,21 +65,27 @@ def ruby_maven_install_options=(options)
end
def resolve_dependencies_list(file)
- factory = MavenFactory.new(@options)
- maven = factory.maven_new(File.expand_path('gemspec_pom.rb', __dir__))
+ require 'jars/mima'
+ artifacts = GemspecArtifacts.new(@spec)
is_local_file = File.expand_path(File.dirname(@specfile)) == File.expand_path(Dir.pwd)
- maven.attach_jars(@spec, all_dependencies: is_local_file)
- maven['jars.specfile'] = @specfile.to_s
- maven['outputAbsoluteArtifactFilename'] = 'true'
- maven['includeTypes'] = 'jar'
- maven['outputScope'] = 'true'
- maven['useRepositoryLayout'] = 'true'
- maven['outputDirectory'] = Jars.home.to_s
- maven['outputFile'] = file.to_s
+ resolved = Jars::Mima.resolve_artifacts(artifacts.artifacts, all_dependencies: is_local_file)
- maven.exec('dependency:copy-dependencies', 'dependency:list')
+ # Write output in Maven dependency:list format for Installer::Dependency compatibility
+ allowed_types = %w[jar pom].freeze
+ File.open(file, 'w') do |f|
+ f.puts
+ f.puts 'The following files have been resolved:'
+ resolved.each do |dep|
+ next unless allowed_types.include?(dep.type)
+
+ line = +" #{dep.group_id}:#{dep.artifact_id}:#{dep.type}:"
+ line << "#{dep.classifier}:" if dep.classifier
+ line << "#{dep.version}:#{dep.scope}:#{dep.file}"
+ f.puts line
+ end
+ end
end
end
end
diff --git a/lib/jars/maven_factory.rb b/lib/jars/maven_factory.rb
deleted file mode 100644
index c78fee9..0000000
--- a/lib/jars/maven_factory.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-# frozen_string_literal: true
-
-require 'jar_dependencies'
-require 'jars/gemspec_artifacts'
-
-module Jars
- class MavenFactory
- module AttachJars
- def attach_jars(spec, all_dependencies: false)
- @index ||= 0
- @done ||= []
-
- deps = GemspecArtifacts.new(spec)
- deps.artifacts.each do |a|
- # for this gemspec we want to include all artifacts but
- # for all others we want to exclude provided and test artifacts
- next unless !@done.include?(a.key) && (all_dependencies || ((a.scope != 'provided') && (a.scope != 'test')))
-
- # ruby dsl is not working reliably for classifier
- self["jars.#{@index}"] = a.to_coord_no_classifier
- if a.exclusions
- jndex = 0
- a.exclusions.each do |ex|
- self["jars.#{@index}.exclusions.#{jndex}"] = ex.to_s
- jndex += 1
- end
- end
- self["jars.#{@index}.scope"] = a.scope if a.scope
- self["jars.#{@index}.classifier"] = a.classifier if a.classifier
- @index += 1
- @done << a.key
- end
- end
- end
-
- attr_reader :debug, :verbose
-
- def initialize(options = nil, debug = Jars.debug?, verbose = Jars.verbose?)
- @options = (options || {}).dup
- @options.delete(:ignore_dependencies)
- @debug = debug
- @verbose = verbose
- @installed_maven = false
- end
-
- def maven_new(pom)
- lazy_load_maven
- maven = setup(Maven::Ruby::Maven.new)
-
- maven.extend AttachJars
- # TODO: copy pom to tmp dir in case it is not a real file
- maven.options['-f'] = pom
- maven
- end
-
- private
-
- def setup(maven)
- maven.verbose = @verbose
- maven.options['-X'] = nil if @debug
- if @verbose
- maven.options['-e'] = nil
- elsif !@debug
- maven.options['--quiet'] = nil
- end
- maven['verbose'] = (@debug || @verbose) == true
-
- maven.options['-s'] = Jars::MavenSettings.effective_settings if Jars.maven_settings
-
- maven['maven.repo.local'] = java.io.File.new(Jars.local_maven_repo).absolute_path.to_s
-
- maven
- end
-
- def lazy_load_maven
- add_gem_to_load_path('ruby-maven')
- add_gem_to_load_path('ruby-maven-libs')
- if @installed_maven
- puts
- puts 'using maven for the first time results in maven'
- puts 'downloading all its default plugin and can take time.'
- puts 'as those plugins get cached on disk and further execution'
- puts 'of maven is much faster then the first time.'
- puts
- end
- require 'maven/ruby/maven'
- end
-
- def find_spec_via_rubygems(name, req)
- require 'rubygems/dependency'
- dep = Gem::Dependency.new(name, req)
- dep.matching_specs(true).last
- end
-
- def add_gem_to_load_path(name)
- # if the gem is already activated => good
- return if Gem.loaded_specs[name]
-
- # just install gem if needed and add it to the load_path
- # and leave activated gems as they are
- req = requirement(name)
- unless (spec = find_spec_via_rubygems(name, req))
- spec = install_gem(name, req)
- end
- raise "failed to resolve gem '#{name}' if you're using Bundler add it as a dependency" unless spec
-
- path = File.join(spec.full_gem_path, spec.require_path)
- $LOAD_PATH << path unless $LOAD_PATH.include?(path)
- end
-
- def requirement(name)
- jars = Gem.loaded_specs['jar-dependencies']
- dep = jars&.dependencies&.detect { |d| d.name == name }
- dep.nil? ? Gem::Requirement.create('>0') : dep.requirement
- end
-
- def install_gem(name, req)
- @installed_maven = true
- puts "Installing gem '#{name}' . . ."
- require 'rubygems/dependency_installer'
- inst = Gem::DependencyInstaller.new(@options ||= {})
- inst.install(name, req).first
- rescue => e
- if Jars.verbose?
- warn e.inspect
- warn e.backtrace.join("\n")
- end
- raise "there was an error installing '#{name} (#{req})' " \
- "#{@options[:domain]}. please install it manually: #{e.inspect}"
- end
- end
-end
diff --git a/lib/jars/maven_settings.rb b/lib/jars/maven_settings.rb
index 7f1b291..39ae004 100644
--- a/lib/jars/maven_settings.rb
+++ b/lib/jars/maven_settings.rb
@@ -2,8 +2,6 @@
module Jars
class MavenSettings
- LINE_SEPARATOR = ENV_JAVA['line.separator']
-
class << self
def local_settings
@_jars_maven_local_settings_ = nil unless instance_variable_defined?(:@_jars_maven_local_settings_)
@@ -36,36 +34,6 @@ def user_settings
@_jars_maven_user_settings_ || nil
end
- def effective_settings
- @_jars_effective_maven_settings_ = nil unless instance_variable_defined?(:@_jars_effective_maven_settings_)
- if @_jars_effective_maven_settings_.nil?
- begin
- require 'rubygems/request'
-
- http = Gem::Request.proxy_uri(Gem.configuration[:http_proxy] || Gem::Request.get_proxy_from_env('http'))
- https = Gem::Request.proxy_uri(Gem.configuration[:https_proxy] || Gem::Request.get_proxy_from_env('https'))
- rescue
- Jars.debug('ignore rubygems proxy configuration as rubygems is too old')
- end
- @_jars_effective_maven_settings_ = if http.nil? && https.nil?
- settings
- else
- setup_interpolated_settings(http, https) || settings
- end
- end
- @_jars_effective_maven_settings_
- end
-
- def cleanup
- File.unlink(effective_settings) if effective_settings != settings
- ensure
- reset
- end
-
- def reset
- instance_variables.each { |var| instance_variable_set(var, nil) }
- end
-
def settings
@_jars_maven_settings_ = nil unless instance_variable_defined?(:@_jars_maven_settings_)
local_settings || user_settings if @_jars_maven_settings_.nil?
@@ -85,44 +53,8 @@ def global_settings
@_jars_maven_global_settings_ || nil
end
- private
-
- def setup_interpolated_settings(http, https)
- proxy = raw_proxy_settings_xml(http, https).gsub("\n", LINE_SEPARATOR)
- if settings.nil?
- raw = "#{LINE_SEPARATOR}#{proxy}"
- else
- raw = File.read(settings)
- if raw.include?('')
- Jars.warn("can not interpolated proxy info for #{settings}")
- return
- else
- raw.sub!('', "#{LINE_SEPARATOR}#{proxy}")
- end
- end
- tempfile = java.io.File.create_temp_file('settings', '.xml')
- tempfile.delete_on_exit
- File.write(tempfile.path, raw)
- tempfile.path
- end
-
- def raw_proxy_settings_xml(http, https)
- raw = File.read(File.join(File.dirname(__FILE__), 'settings.xml'))
- if http
- raw.sub!('__HTTP_ACTIVE__', 'true')
- raw.sub!('__HTTP_SERVER__', http.host)
- raw.sub!('__HTTP_PORT__', http.port.to_s)
- else
- raw.sub!('__HTTP_ACTIVE__', 'false')
- end
- if https
- raw.sub!('__HTTPS_ACTIVE__', 'true')
- raw.sub!('__HTTPS_SERVER__', https.host)
- raw.sub!('__HTTPS_PORT__', https.port.to_s)
- else
- raw.sub!('__HTTPS_ACTIVE__', 'false')
- end
- raw
+ def reset
+ instance_variables.each { |var| instance_variable_set(var, nil) }
end
end
end
diff --git a/lib/jars/mima.rb b/lib/jars/mima.rb
new file mode 100644
index 0000000..0e26f4f
--- /dev/null
+++ b/lib/jars/mima.rb
@@ -0,0 +1,262 @@
+# frozen_string_literal: true
+
+require 'jars/mima/version'
+require 'jars/gemspec_artifacts'
+
+module Jars
+ # Resolver backed by the Mima Java library (MIni MAven).
+ # Replaces the previous ruby-maven based resolution pipeline.
+ #
+ # Mima wraps the Maven Resolver (Aether) API as a standalone library (no Maven process is spawned).
+ module Mima
+ class << self
+ @@jars_loaded = nil # rubocop:disable Style/ClassVars
+
+ # Loads the bundled Mima jars onto the classpath.
+ # Safe to call multiple times; only loads once.
+ def ensure_jars_loaded
+ return if @@jars_loaded
+
+ mima_dir = File.expand_path('mima', File.dirname(__FILE__))
+
+ load File.join(mima_dir, "slf4j-api-#{SLF4J_VERSION}.jar")
+ load File.join(mima_dir, "slf4j-simple-#{SLF4J_VERSION}.jar")
+ load File.join(mima_dir, "jcl-over-slf4j-#{SLF4J_VERSION}.jar")
+ load File.join(mima_dir, "context-#{MIMA_VERSION}.jar")
+ load File.join(mima_dir, "standalone-static-uber-#{MIMA_VERSION}.jar")
+
+ @@jars_loaded = true # rubocop:disable Style/ClassVars
+ end
+
+ # Builds a +ContextOverrides+ from jar-dependencies configuration
+ # (local repo, user/global settings.xml).
+ #
+ # @return [Java::eu.maveniverse.maven.mima.context.ContextOverrides]
+ def context_overrides
+ ensure_jars_loaded
+
+ builder = Java::eu.maveniverse.maven.mima.context.ContextOverrides.create
+ builder.withUserSettings(true)
+
+ # Local repository override
+ local_repo = ::Jars.local_maven_repo
+ builder.withLocalRepositoryOverride(java.nio.file.Paths.get(local_repo)) if local_repo
+
+ # User settings.xml override
+ settings = ::Jars::MavenSettings.settings
+ builder.withUserSettingsXmlOverride(java.nio.file.Paths.get(settings)) if settings
+
+ # Global settings.xml override
+ global = ::Jars::MavenSettings.global_settings
+ builder.withGlobalSettingsXmlOverride(java.nio.file.Paths.get(global)) if global
+
+ builder.build
+ end
+
+ # Creates a Mima Context. Caller is responsible for closing it.
+ #
+ # @param overrides [Java::eu.maveniverse.maven.mima.context.ContextOverrides, nil]
+ # optional overrides; when +nil+, {#context_overrides} is used
+ # @return [Java::eu.maveniverse.maven.mima.context.Context]
+ def create_context(overrides = nil)
+ ensure_jars_loaded
+
+ overrides ||= context_overrides
+ runtime = Java::eu.maveniverse.maven.mima.context.Runtimes::INSTANCE.getRuntime
+ runtime.create(overrides)
+ end
+
+ # Resolves transitive dependencies for the given artifacts.
+ # Creates (and closes) its own Mima context.
+ #
+ # @param artifacts [Array] jar dependency declarations
+ # @param all_dependencies [Boolean] when +true+, include provided/test scoped artifacts
+ # @return [Array] resolved artifacts with local file paths
+ def resolve_artifacts(artifacts, all_dependencies: false)
+ context = create_context
+ begin
+ resolve_with_context(context, artifacts, all_dependencies: all_dependencies)
+ ensure
+ context.close
+ end
+ end
+
+ # Resolves transitive dependencies using an existing Mima context.
+ #
+ # @param context [Java::eu.maveniverse.maven.mima.context.Context] an open Mima context
+ # @param artifacts [Array] jar dependency declarations
+ # @param all_dependencies [Boolean] when +true+, include provided/test scoped artifacts
+ # @return [Array] resolved artifacts with local file paths
+ def resolve_with_context(context, artifacts, all_dependencies: false)
+ deps = artifacts_to_dependencies(artifacts, all_dependencies: all_dependencies)
+ return [] if deps.empty?
+
+ collect_request = org.eclipse.aether.collection.CollectRequest.new
+ deps.each { |d| collect_request.addDependency(d) }
+ collect_request.setRepositories(context.remoteRepositories)
+
+ dependency_request = org.eclipse.aether.resolution.DependencyRequest.new
+ dependency_request.setCollectRequest(collect_request)
+
+ result = context.repositorySystem.resolveDependencies(
+ context.repositorySystemSession, dependency_request
+ )
+
+ root = result.getRoot
+ collect_resolved(root)
+ end
+
+ private
+
+ # Converts {GemspecArtifacts::Artifact} objects to Aether +Dependency+ list,
+ # filtering by scope unless +all_dependencies+ is set.
+ #
+ # @param artifacts [Array]
+ # @param all_dependencies [Boolean]
+ # @return [Array]
+ def artifacts_to_dependencies(artifacts, all_dependencies: false)
+ filtered = artifacts.select do |a|
+ all_dependencies || (a.scope != 'provided' && a.scope != 'test')
+ end
+
+ filtered.map do |a|
+ aether_artifact = build_aether_artifact(a)
+ scope = a.scope || 'compile'
+ dep = org.eclipse.aether.graph.Dependency.new(aether_artifact, scope)
+
+ if a.exclusions && !a.exclusions.empty?
+ exclusions = a.exclusions.map do |ex|
+ org.eclipse.aether.graph.Exclusion.new(ex.group_id, ex.artifact_id, '*', '*')
+ end
+ dep = dep.setExclusions(exclusions)
+ end
+
+ dep
+ end
+ end
+
+ # Converts a single {GemspecArtifacts::Artifact} to an Aether +DefaultArtifact+.
+ #
+ # @param artifact [Jars::GemspecArtifacts::Artifact]
+ # @return [org.eclipse.aether.artifact.DefaultArtifact]
+ def build_aether_artifact(artifact)
+ version = Jars::MavenVersion.new(artifact.version) || artifact.version
+ if artifact.classifier
+ org.eclipse.aether.artifact.DefaultArtifact.new(
+ artifact.group_id, artifact.artifact_id, artifact.classifier,
+ artifact.type || 'jar', version
+ )
+ else
+ org.eclipse.aether.artifact.DefaultArtifact.new(
+ artifact.group_id, artifact.artifact_id,
+ artifact.type || 'jar', version
+ )
+ end
+ end
+
+ # Walks the resolved dependency tree and collects {ResolvedDependency} objects.
+ #
+ # @param node [org.eclipse.aether.graph.DependencyNode] root of the resolved tree
+ # @param result [Array] accumulator
+ # @return [Array]
+ def collect_resolved(node, result = [])
+ node.getChildren.each do |child|
+ dep = child.getDependency
+ next unless dep
+
+ artifact = dep.getArtifact
+ next unless artifact.getFile # skip unresolved
+
+ result << ResolvedDependency.new(
+ artifact.getGroupId,
+ artifact.getArtifactId,
+ artifact.getVersion,
+ artifact.getClassifier.to_s.empty? ? nil : artifact.getClassifier,
+ artifact.getExtension,
+ dep.getScope,
+ artifact.getFile.getAbsolutePath
+ )
+
+ collect_resolved(child, result)
+ end
+ result
+ end
+ end
+
+ # Structured result from dependency resolution.
+ #
+ # @!attribute [r] group_id
+ # @return [String] Maven group ID
+ # @!attribute [r] artifact_id
+ # @return [String] Maven artifact ID
+ # @!attribute [r] version
+ # @return [String] resolved version
+ # @!attribute [r] classifier
+ # @return [String, nil] Maven classifier (e.g. +"sources"+, +"no_aop"+)
+ # @!attribute [r] type
+ # @return [String] artifact type/extension (e.g. +"jar"+, +"pom"+)
+ # @!attribute [r] scope
+ # @return [String] Maven scope (+"compile"+, +"runtime"+, +"test"+, +"provided"+, +"system"+)
+ # @!attribute [r] file
+ # @return [String] absolute path to the resolved artifact in the local repository
+ ResolvedDependency = Struct.new(:group_id, :artifact_id, :version, :classifier, :type, :scope, :file) do
+ # @return [Boolean] true if scope is neither +test+ nor +provided+
+ def runtime?
+ scope != 'test' && scope != 'provided'
+ end
+
+ # @return [Boolean] true if scope is +system+
+ def system?
+ scope == 'system'
+ end
+
+ # Maven repository layout path relative to repo root.
+ #
+ # @return [String] e.g. +"org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar"+
+ def path
+ parts = group_id.split('.')
+ parts << artifact_id
+ parts << version
+ filename = +"#{artifact_id}-#{version}"
+ filename << "-#{classifier}" if classifier
+ filename << ".#{type || 'jar'}"
+ parts << filename
+ File.join(parts)
+ end
+
+ # Formats a line suitable for the +Jars.lock+ file.
+ #
+ # @return [String] e.g. +"org.slf4j:slf4j-api:1.7.36:compile:"+
+ def to_lock_entry
+ entry = +"#{group_id}:#{artifact_id}:"
+ entry << "#{classifier}:" if classifier
+ entry << "#{version}:#{scope}:"
+ entry
+ end
+
+ # Colon-separated GAV string suitable for +require_jar+ calls.
+ #
+ # @return [String] e.g. +"org.slf4j:slf4j-api:1.7.36"+
+ def gav
+ parts = [group_id, artifact_id]
+ parts << classifier if classifier
+ parts << version
+ parts.join(':')
+ end
+
+ # Relative jar path for vendoring / +require_jar+.
+ #
+ # @return [String] e.g. +"org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar"+
+ def jar_path
+ parts = group_id.split('.')
+ parts << artifact_id
+ parts << version
+ filename = +"#{artifact_id}-#{version}"
+ filename << "-#{classifier}" if classifier
+ filename << '.jar'
+ parts << filename
+ File.join(parts)
+ end
+ end
+ end
+end
diff --git a/lib/jars/mima/context-2.4.42.jar b/lib/jars/mima/context-2.4.42.jar
new file mode 100644
index 0000000..5acb5bf
Binary files /dev/null and b/lib/jars/mima/context-2.4.42.jar differ
diff --git a/lib/jars/mima/jcl-over-slf4j-1.7.36.jar b/lib/jars/mima/jcl-over-slf4j-1.7.36.jar
new file mode 100644
index 0000000..3ecd7d5
Binary files /dev/null and b/lib/jars/mima/jcl-over-slf4j-1.7.36.jar differ
diff --git a/lib/jars/mima/slf4j-api-1.7.36.jar b/lib/jars/mima/slf4j-api-1.7.36.jar
new file mode 100644
index 0000000..7d3ce68
Binary files /dev/null and b/lib/jars/mima/slf4j-api-1.7.36.jar differ
diff --git a/lib/jars/mima/slf4j-simple-1.7.36.jar b/lib/jars/mima/slf4j-simple-1.7.36.jar
new file mode 100644
index 0000000..ef831a8
Binary files /dev/null and b/lib/jars/mima/slf4j-simple-1.7.36.jar differ
diff --git a/lib/jars/mima/standalone-static-uber-2.4.42.jar b/lib/jars/mima/standalone-static-uber-2.4.42.jar
new file mode 100644
index 0000000..c824521
Binary files /dev/null and b/lib/jars/mima/standalone-static-uber-2.4.42.jar differ
diff --git a/lib/jars/mima/version.rb b/lib/jars/mima/version.rb
new file mode 100644
index 0000000..dc0f535
--- /dev/null
+++ b/lib/jars/mima/version.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Jars
+ module Mima
+ MIMA_VERSION = '2.4.42'
+ SLF4J_VERSION = '1.7.36'
+ end
+end
diff --git a/lib/jars/output_jars_pom.rb b/lib/jars/output_jars_pom.rb
deleted file mode 100644
index f75cf9f..0000000
--- a/lib/jars/output_jars_pom.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-# this file is maven DSL
-
-if ENV_JAVA['jars.quiet'] != 'true'
- model.dependencies.each do |d|
- puts " #{d.group_id}:#{d.artifact_id}" \
- "#{":#{d.classifier}" if d.classifier}" \
- ":#{d.version}:#{d.scope || 'compile'}"
- next if d.exclusions.empty?
-
- puts " exclusions: #{d.exclusions.collect do |e|
- "#{e.group_id}:#{e.artifact_id}"
- end.join}"
- end
-end
diff --git a/lib/jars/settings.xml b/lib/jars/settings.xml
deleted file mode 100644
index 676e4ca..0000000
--- a/lib/jars/settings.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
- __HTTP_ACTIVE__
- http
- __HTTP_SERVER__
- __HTTP_PORT__
-
-
-
- __HTTPS_ACTIVE__
- https
- __HTTPS_SERVER__
- __HTTPS_PORT__
-
-
-
diff --git a/lib/jars/version.rb b/lib/jars/version.rb
index d6c8807..585a066 100644
--- a/lib/jars/version.rb
+++ b/lib/jars/version.rb
@@ -2,6 +2,4 @@
module Jars
VERSION = '0.5.7'
- JRUBY_PLUGINS_VERSION = '3.0.2'
- DEPENDENCY_PLUGIN_VERSION = '2.8'
end
diff --git a/specs/maven_factory_spec.rb b/specs/maven_factory_spec.rb
deleted file mode 100644
index a248b08..0000000
--- a/specs/maven_factory_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# frozen_string_literal: true
-
-require File.expand_path('setup', File.dirname(__FILE__))
-
-require 'jars/maven_factory'
-
-describe Jars::MavenFactory do
- after do
- ENV['JARS_VERBOSE'] = nil
- ENV['JARS_DEBUG'] = nil
- ENV['JARS_MAVEN_SETTINGS'] = nil
- Jars.reset
- end
-
- it 'uses logging config' do
- ENV['JARS_VERBOSE'] = nil
- ENV['JARS_DEBUG'] = nil
- Jars.reset
- maven = Jars::MavenFactory.new.maven_new('pom')
- _(maven.options.key?('--quiet')).must_equal true
- _(maven.options.key?('-X')).must_equal false
- _(maven.options['-Dverbose']).must_equal false
-
- ENV['JARS_VERBOSE'] = 'true'
- ENV['JARS_DEBUG'] = nil
- Jars.reset
- maven = Jars::MavenFactory.new.maven_new('pom')
- _(maven.options.key?('--quiet')).must_equal false
- _(maven.options.key?('-e')).must_equal true
- _(maven.options.key?('-X')).must_equal false
- _(maven.options['-Dverbose']).must_equal true
-
- ENV['JARS_VERBOSE'] = nil
- ENV['JARS_DEBUG'] = 'true'
- Jars.reset
- maven = Jars::MavenFactory.new.maven_new('pom')
- _(maven.options.key?('--quiet')).must_equal false
- _(maven.options.key?('-e')).must_equal false
- _(maven.options.key?('-X')).must_equal true
- _(maven.options['-Dverbose']).must_equal true
-
- ENV['JARS_VERBOSE'] = 'true'
- ENV['JARS_DEBUG'] = 'true'
- Jars.reset
- maven = Jars::MavenFactory.new.maven_new('pom')
- _(maven.options.key?('--quiet')).must_equal false
- _(maven.options.key?('-e')).must_equal true
- _(maven.options.key?('-X')).must_equal true
- _(maven.options['-Dverbose']).must_equal true
- end
-
- it 'uses proxy settings from Gem.configuration' do
- skip('pending until it realy works')
- ENV['JARS_MAVEN_SETTINGS'] = 'specs/does/no/exists/settings.xml'
- Gem.configuration[:proxy] = 'https://localhost:3128'
- Jars.reset
- maven = Jars::MavenFactory.new.maven_new('pom')
- _(maven.options.key?('-DproxySet=true')).must_equal true
- _(maven.options.key?('-DproxyHost=localhost')).must_equal true
- _(maven.options.key?('-DproxyPort=3128')).must_equal true
-
- Gem.configuration[:proxy] = :noproxy
- Jars.reset
- maven = Jars::MavenFactory.new.maven_new('pom')
- _(maven.options.key?('-DproxySet=true')).must_equal false
- _(maven.options.key?('-DproxyHost=localhost')).must_equal false
- _(maven.options.key?('-DproxyPort=3128')).must_equal false
-
- ENV['JARS_MAVEN_SETTINGS'] = 'specs/settings.xml'
- Gem.configuration[:proxy] = 'https://localhost:3128'
- Jars.reset
- maven = Jars::MavenFactory.new.maven_new('pom')
- _(maven.options.key?('-DproxySet=true')).must_equal false
- _(maven.options.key?('-DproxyHost=localhost')).must_equal false
- _(maven.options.key?('-DproxyPort=3128')).must_equal false
- end
-end