mirror of
https://github.com/ruby/ruby.git
synced 2025-09-19 10:33:58 +02:00

Compatibly renamed Gem::DependencyResolver to Gem::Resolver. Added support for git gems in gem.deps.rb and Gemfile. Fixed resolver bugs. * test/rubygems: ditto. * lib/rubygems/LICENSE.txt: Updated to license from RubyGems trunk. [ruby-trunk - Bug #9086] * lib/rubygems/commands/which_command.rb: RubyGems now indicates failure when any file is missing. [ruby-trunk - Bug #9004] * lib/rubygems/ext/builder: Extensions are now installed into the extension install directory and the first directory in the require path from the gem. This allows backwards compatibility with msgpack and other gems that calculate full require paths. [ruby-trunk - Bug #9106] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43714 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
502 lines
12 KiB
Ruby
502 lines
12 KiB
Ruby
##
|
|
# A semi-compatible DSL for the Bundler Gemfile and Isolate formats.
|
|
|
|
class Gem::RequestSet::GemDependencyAPI
|
|
|
|
ENGINE_MAP = { # :nodoc:
|
|
:jruby => %w[jruby],
|
|
:jruby_18 => %w[jruby],
|
|
:jruby_19 => %w[jruby],
|
|
:maglev => %w[maglev],
|
|
:mri => %w[ruby],
|
|
:mri_18 => %w[ruby],
|
|
:mri_19 => %w[ruby],
|
|
:mri_20 => %w[ruby],
|
|
:mri_21 => %w[ruby],
|
|
:rbx => %w[rbx],
|
|
:ruby => %w[ruby rbx maglev],
|
|
:ruby_18 => %w[ruby rbx maglev],
|
|
:ruby_19 => %w[ruby rbx maglev],
|
|
:ruby_20 => %w[ruby rbx maglev],
|
|
:ruby_21 => %w[ruby rbx maglev],
|
|
}
|
|
|
|
x86_mingw = Gem::Platform.new 'x86-mingw32'
|
|
x64_mingw = Gem::Platform.new 'x64-mingw32'
|
|
|
|
PLATFORM_MAP = { # :nodoc:
|
|
:jruby => Gem::Platform::RUBY,
|
|
:jruby_18 => Gem::Platform::RUBY,
|
|
:jruby_19 => Gem::Platform::RUBY,
|
|
:maglev => Gem::Platform::RUBY,
|
|
:mingw => x86_mingw,
|
|
:mingw_18 => x86_mingw,
|
|
:mingw_19 => x86_mingw,
|
|
:mingw_20 => x86_mingw,
|
|
:mingw_21 => x86_mingw,
|
|
:mri => Gem::Platform::RUBY,
|
|
:mri_18 => Gem::Platform::RUBY,
|
|
:mri_19 => Gem::Platform::RUBY,
|
|
:mri_20 => Gem::Platform::RUBY,
|
|
:mri_21 => Gem::Platform::RUBY,
|
|
:mswin => Gem::Platform::RUBY,
|
|
:rbx => Gem::Platform::RUBY,
|
|
:ruby => Gem::Platform::RUBY,
|
|
:ruby_18 => Gem::Platform::RUBY,
|
|
:ruby_19 => Gem::Platform::RUBY,
|
|
:ruby_20 => Gem::Platform::RUBY,
|
|
:ruby_21 => Gem::Platform::RUBY,
|
|
:x64_mingw => x64_mingw,
|
|
:x64_mingw_20 => x64_mingw,
|
|
:x64_mingw_21 => x64_mingw
|
|
}
|
|
|
|
gt_eq_0 = Gem::Requirement.new '>= 0'
|
|
tilde_gt_1_8_0 = Gem::Requirement.new '~> 1.8.0'
|
|
tilde_gt_1_9_0 = Gem::Requirement.new '~> 1.9.0'
|
|
tilde_gt_2_0_0 = Gem::Requirement.new '~> 2.0.0'
|
|
tilde_gt_2_1_0 = Gem::Requirement.new '~> 2.1.0'
|
|
|
|
VERSION_MAP = { # :nodoc:
|
|
:jruby => gt_eq_0,
|
|
:jruby_18 => tilde_gt_1_8_0,
|
|
:jruby_19 => tilde_gt_1_9_0,
|
|
:maglev => gt_eq_0,
|
|
:mingw => gt_eq_0,
|
|
:mingw_18 => tilde_gt_1_8_0,
|
|
:mingw_19 => tilde_gt_1_9_0,
|
|
:mingw_20 => tilde_gt_2_0_0,
|
|
:mingw_21 => tilde_gt_2_1_0,
|
|
:mri => gt_eq_0,
|
|
:mri_18 => tilde_gt_1_8_0,
|
|
:mri_19 => tilde_gt_1_9_0,
|
|
:mri_20 => tilde_gt_2_0_0,
|
|
:mri_21 => tilde_gt_2_1_0,
|
|
:mswin => gt_eq_0,
|
|
:rbx => gt_eq_0,
|
|
:ruby => gt_eq_0,
|
|
:ruby_18 => tilde_gt_1_8_0,
|
|
:ruby_19 => tilde_gt_1_9_0,
|
|
:ruby_20 => tilde_gt_2_0_0,
|
|
:ruby_21 => tilde_gt_2_1_0,
|
|
:x64_mingw => gt_eq_0,
|
|
:x64_mingw_20 => tilde_gt_2_0_0,
|
|
:x64_mingw_21 => tilde_gt_2_1_0,
|
|
}
|
|
|
|
WINDOWS = { # :nodoc:
|
|
:mingw => :only,
|
|
:mingw_18 => :only,
|
|
:mingw_19 => :only,
|
|
:mingw_20 => :only,
|
|
:mingw_21 => :only,
|
|
:mri => :never,
|
|
:mri_18 => :never,
|
|
:mri_19 => :never,
|
|
:mri_20 => :never,
|
|
:mri_21 => :never,
|
|
:mswin => :only,
|
|
:rbx => :never,
|
|
:ruby => :never,
|
|
:ruby_18 => :never,
|
|
:ruby_19 => :never,
|
|
:ruby_20 => :never,
|
|
:ruby_21 => :never,
|
|
:x64_mingw => :only,
|
|
:x64_mingw_20 => :only,
|
|
:x64_mingw_21 => :only,
|
|
}
|
|
|
|
##
|
|
# A set of gems that are loaded via the +:git+ option to #gem
|
|
|
|
attr_reader :git_set # :nodoc:
|
|
|
|
##
|
|
# A Hash containing gem names and files to require from those gems.
|
|
|
|
attr_reader :requires
|
|
|
|
##
|
|
# A set of gems that are loaded via the +:path+ option to #gem
|
|
|
|
attr_reader :vendor_set # :nodoc:
|
|
|
|
##
|
|
# The groups of gems to exclude from installation
|
|
|
|
attr_accessor :without_groups
|
|
|
|
##
|
|
# Creates a new GemDependencyAPI that will add dependencies to the
|
|
# Gem::RequestSet +set+ based on the dependency API description in +path+.
|
|
|
|
def initialize set, path
|
|
@set = set
|
|
@path = path
|
|
|
|
@current_groups = nil
|
|
@current_platform = nil
|
|
@current_repository = nil
|
|
@default_sources = true
|
|
@git_set = @set.git_set
|
|
@requires = Hash.new { |h, name| h[name] = [] }
|
|
@vendor_set = @set.vendor_set
|
|
@gem_sources = {}
|
|
@without_groups = []
|
|
end
|
|
|
|
##
|
|
# Adds +dependencies+ to the request set if any of the +groups+ are allowed.
|
|
# This is used for gemspec dependencies.
|
|
|
|
def add_dependencies groups, dependencies # :nodoc:
|
|
return unless (groups & @without_groups).empty?
|
|
|
|
dependencies.each do |dep|
|
|
@set.gem dep.name, *dep.requirement
|
|
end
|
|
end
|
|
|
|
private :add_dependencies
|
|
|
|
##
|
|
# Finds a gemspec with the given +name+ that lives at +path+.
|
|
|
|
def find_gemspec name, path # :nodoc:
|
|
glob = File.join path, "#{name}.gemspec"
|
|
|
|
spec_files = Dir[glob]
|
|
|
|
case spec_files.length
|
|
when 1 then
|
|
spec_file = spec_files.first
|
|
|
|
spec = Gem::Specification.load spec_file
|
|
|
|
return spec if spec
|
|
|
|
raise ArgumentError, "invalid gemspec #{spec_file}"
|
|
when 0 then
|
|
raise ArgumentError, "no gemspecs found at #{Dir.pwd}"
|
|
else
|
|
raise ArgumentError,
|
|
"found multiple gemspecs at #{Dir.pwd}, " +
|
|
"use the name: option to specify the one you want"
|
|
end
|
|
end
|
|
|
|
##
|
|
# Loads the gem dependency file
|
|
|
|
def load
|
|
instance_eval File.read(@path).untaint, @path, 1
|
|
end
|
|
|
|
##
|
|
# :category: Gem Dependencies DSL
|
|
# :call-seq:
|
|
# gem(name)
|
|
# gem(name, *requirements)
|
|
# gem(name, *requirements, options)
|
|
#
|
|
# Specifies a gem dependency with the given +name+ and +requirements+. You
|
|
# may also supply +options+ following the +requirements+
|
|
|
|
def gem name, *requirements
|
|
options = requirements.pop if requirements.last.kind_of?(Hash)
|
|
options ||= {}
|
|
|
|
options[:git] = @current_repository if @current_repository
|
|
|
|
source_set = false
|
|
|
|
source_set ||= gem_path name, options
|
|
source_set ||= gem_git name, options
|
|
source_set ||= gem_github name, options
|
|
|
|
return unless gem_platforms options
|
|
|
|
groups = gem_group name, options
|
|
|
|
return unless (groups & @without_groups).empty?
|
|
|
|
unless source_set then
|
|
raise ArgumentError,
|
|
"duplicate source (default) for gem #{name}" if
|
|
@gem_sources.include? name
|
|
|
|
@gem_sources[name] = :default
|
|
end
|
|
|
|
gem_requires name, options
|
|
|
|
@set.gem name, *requirements
|
|
end
|
|
|
|
##
|
|
# Handles the git: option from +options+ for gem +name+.
|
|
#
|
|
# Returns +true+ if the path option was handled.
|
|
|
|
def gem_git name, options # :nodoc:
|
|
if gist = options.delete(:gist) then
|
|
options[:git] = "https://gist.github.com/#{gist}.git"
|
|
end
|
|
|
|
return unless repository = options.delete(:git)
|
|
|
|
raise ArgumentError,
|
|
"duplicate source git: #{repository} for gem #{name}" if
|
|
@gem_sources.include? name
|
|
|
|
reference = nil
|
|
reference ||= options.delete :ref
|
|
reference ||= options.delete :branch
|
|
reference ||= options.delete :tag
|
|
reference ||= 'master'
|
|
|
|
submodules = options.delete :submodules
|
|
|
|
@git_set.add_git_gem name, repository, reference, submodules
|
|
|
|
@gem_sources[name] = repository
|
|
|
|
true
|
|
end
|
|
|
|
private :gem_git
|
|
|
|
##
|
|
# Handles the github: option from +options+ for gem +name+.
|
|
#
|
|
# Returns +true+ if the path option was handled.
|
|
|
|
def gem_github name, options # :nodoc:
|
|
return unless path = options.delete(:github)
|
|
|
|
options[:git] = "git://github.com/#{path}.git"
|
|
|
|
gem_git name, options
|
|
|
|
true
|
|
end
|
|
|
|
##
|
|
# Handles the :group and :groups +options+ for the gem with the given
|
|
# +name+.
|
|
|
|
def gem_group name, options # :nodoc:
|
|
g = options.delete :group
|
|
all_groups = g ? Array(g) : []
|
|
|
|
groups = options.delete :groups
|
|
all_groups |= groups if groups
|
|
|
|
all_groups |= @current_groups if @current_groups
|
|
|
|
all_groups
|
|
end
|
|
|
|
private :gem_group
|
|
|
|
##
|
|
# Handles the path: option from +options+ for gem +name+.
|
|
#
|
|
# Returns +true+ if the path option was handled.
|
|
|
|
def gem_path name, options # :nodoc:
|
|
return unless directory = options.delete(:path)
|
|
|
|
raise ArgumentError,
|
|
"duplicate source path: #{directory} for gem #{name}" if
|
|
@gem_sources.include? name
|
|
|
|
@vendor_set.add_vendor_gem name, directory
|
|
|
|
@gem_sources[name] = directory
|
|
|
|
true
|
|
end
|
|
|
|
private :gem_path
|
|
|
|
##
|
|
# Handles the platforms: option from +options+. Returns true if the
|
|
# platform matches the current platform.
|
|
|
|
def gem_platforms options # :nodoc:
|
|
platform_names = Array(options.delete :platforms)
|
|
platform_names << @current_platform if @current_platform
|
|
|
|
return true if platform_names.empty?
|
|
|
|
platform_names.any? do |platform_name|
|
|
raise ArgumentError, "unknown platform #{platform_name.inspect}" unless
|
|
platform = PLATFORM_MAP[platform_name]
|
|
|
|
next false unless Gem::Platform.match platform
|
|
|
|
if engines = ENGINE_MAP[platform_name] then
|
|
next false unless engines.include? Gem.ruby_engine
|
|
end
|
|
|
|
case WINDOWS[platform_name]
|
|
when :only then
|
|
next false unless Gem.win_platform?
|
|
when :never then
|
|
next false if Gem.win_platform?
|
|
end
|
|
|
|
VERSION_MAP[platform_name].satisfied_by? Gem.ruby_version
|
|
end
|
|
end
|
|
|
|
private :gem_platforms
|
|
|
|
##
|
|
# Handles the require: option from +options+ and adds those files, or the
|
|
# default file to the require list for +name+.
|
|
|
|
def gem_requires name, options # :nodoc:
|
|
if options.include? :require then
|
|
if requires = options.delete(:require) then
|
|
@requires[name].concat requires
|
|
end
|
|
else
|
|
@requires[name] << name
|
|
end
|
|
end
|
|
|
|
private :gem_requires
|
|
|
|
def git repository
|
|
@current_repository = repository
|
|
|
|
yield
|
|
|
|
ensure
|
|
@current_repository = nil
|
|
end
|
|
|
|
##
|
|
# Returns the basename of the file the dependencies were loaded from
|
|
|
|
def gem_deps_file # :nodoc:
|
|
File.basename @path
|
|
end
|
|
|
|
##
|
|
# :category: Gem Dependencies DSL
|
|
#
|
|
# Loads dependencies from a gemspec file.
|
|
|
|
def gemspec options = {}
|
|
name = options.delete(:name) || '{,*}'
|
|
path = options.delete(:path) || '.'
|
|
development_group = options.delete(:development_group) || :development
|
|
|
|
spec = find_gemspec name, path
|
|
|
|
groups = gem_group spec.name, {}
|
|
|
|
add_dependencies groups, spec.runtime_dependencies
|
|
|
|
groups << development_group
|
|
|
|
add_dependencies groups, spec.development_dependencies
|
|
|
|
gem_requires spec.name, options
|
|
end
|
|
|
|
##
|
|
# :category: Gem Dependencies DSL
|
|
# Block form for placing a dependency in the given +groups+.
|
|
|
|
def group *groups
|
|
@current_groups = groups
|
|
|
|
yield
|
|
|
|
ensure
|
|
@current_groups = nil
|
|
end
|
|
|
|
##
|
|
# :category: Gem Dependencies DSL
|
|
|
|
def platform what
|
|
@current_platform = what
|
|
|
|
yield
|
|
|
|
ensure
|
|
@current_platform = nil
|
|
end
|
|
|
|
##
|
|
# :category: Gem Dependencies DSL
|
|
|
|
alias :platforms :platform
|
|
|
|
##
|
|
# :category: Gem Dependencies DSL
|
|
# Restricts this gem dependencies file to the given ruby +version+. The
|
|
# +:engine+ options from Bundler are currently ignored.
|
|
|
|
def ruby version, options = {}
|
|
engine = options[:engine]
|
|
engine_version = options[:engine_version]
|
|
|
|
raise ArgumentError,
|
|
'you must specify engine_version along with the ruby engine' if
|
|
engine and not engine_version
|
|
|
|
unless RUBY_VERSION == version then
|
|
message = "Your Ruby version is #{RUBY_VERSION}, " +
|
|
"but your #{gem_deps_file} requires #{version}"
|
|
|
|
raise Gem::RubyVersionMismatch, message
|
|
end
|
|
|
|
if engine and engine != Gem.ruby_engine then
|
|
message = "Your ruby engine is #{Gem.ruby_engine}, " +
|
|
"but your #{gem_deps_file} requires #{engine}"
|
|
|
|
raise Gem::RubyVersionMismatch, message
|
|
end
|
|
|
|
if engine_version then
|
|
my_engine_version = Object.const_get "#{Gem.ruby_engine.upcase}_VERSION"
|
|
|
|
if engine_version != my_engine_version then
|
|
message =
|
|
"Your ruby engine version is #{Gem.ruby_engine} #{my_engine_version}, " +
|
|
"but your #{gem_deps_file} requires #{engine} #{engine_version}"
|
|
|
|
raise Gem::RubyVersionMismatch, message
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
##
|
|
# :category: Gem Dependencies DSL
|
|
#
|
|
# Sets +url+ as a source for gems for this dependency API.
|
|
|
|
def source url
|
|
Gem.sources.clear if @default_sources
|
|
|
|
@default_sources = false
|
|
|
|
Gem.sources << url
|
|
end
|
|
|
|
# TODO: remove this typo name at RubyGems 3.0
|
|
|
|
Gem::RequestSet::GemDepedencyAPI = self # :nodoc:
|
|
|
|
end
|
|
|