class Gem::Resolver

给定一组 Gem::Dependency 对象作为 needed,以及通过 set 查询可用规范集的方法,计算一组 ActivationRequest 对象,这些对象指示应激活以满足所有要求的所有规范。

常量

DEBUG_RESOLVER

如果设置了 DEBUG_RESOLVER 环境变量,则会为解析器启用调试模式。这将在解析一组依赖项时显示有关解析器状态的信息。

SINGLE_POSSIBILITY_CONSTRAINT_PENALTY

属性

development[RW]

如果应考虑所有开发依赖项,则将 Set 设置为 true。

development_shallow[RW]

如果应考虑直接开发依赖项,则将 Set 设置为 true。

ignore_dependencies[RW]

当为 true 时,不会查找所请求 gem 的任何依赖项。

skip_gems[RW]

要跳过解析的 gem 的哈希表。以 gem 名称作为键,以 gem 规范数组作为值。

soft_missing[RW]
stats[R]

无法在配置的源中找到的依赖项列表。

公共类方法

compose_sets(*sets) 点击以切换源代码

sets 组合成一个 ComposedSet,允许以统一的方式进行规范查找。如果其中一个 sets 本身是一个 ComposedSet,则它的集将被展平到结果 ComposedSet 中。

# File rubygems/resolver.rb, line 59
def self.compose_sets(*sets)
  sets.compact!

  sets = sets.flat_map do |set|
    case set
    when Gem::Resolver::BestSet then
      set
    when Gem::Resolver::ComposedSet then
      set.sets
    else
      set
    end
  end

  case sets.length
  when 0 then
    raise ArgumentError, "one set in the composition must be non-nil"
  when 1 then
    sets.first
  else
    Gem::Resolver::ComposedSet.new(*sets)
  end
end
for_current_gems(needed) 点击以切换源代码

创建一个 Resolver,仅针对 needed 依赖项的已安装 gem 进行查询。

# File rubygems/resolver.rb, line 87
def self.for_current_gems(needed)
  new needed, Gem::Resolver::CurrentSet.new
end
new(needed, set = nil) 点击以切换源代码

创建 Resolver 对象,它将从 needed Dependency 对象开始解析树。

set 是一个对象,它提供在哪里查找满足依赖项的规范。默认情况下,这是 IndexSet,它将查询 rubygems.org。

# File rubygems/resolver.rb, line 99
def initialize(needed, set = nil)
  @set = set || Gem::Resolver::IndexSet.new
  @needed = needed

  @development         = false
  @development_shallow = false
  @ignore_dependencies = false
  @skip_gems           = {}
  @soft_missing        = false
  @stats               = Gem::Resolver::Stats.new
end

公共实例方法

allow_missing?(dependency) 点击以切换源代码
# File rubygems/resolver.rb, line 271
def allow_missing?(dependency)
  @soft_missing
end
debug?() 点击以切换源代码
# File rubygems/resolver.rb, line 176
def debug?
  DEBUG_RESOLVER
end
dependencies_for(specification) 点击以切换源代码
# File rubygems/resolver.rb, line 252
def dependencies_for(specification)
  return [] if @ignore_dependencies
  spec = specification.spec
  requests(spec, specification)
end
name_for(dependency) 点击以切换源代码
# File rubygems/resolver.rb, line 267
def name_for(dependency)
  dependency.name
end
output() 点击以切换源代码
# File rubygems/resolver.rb, line 172
def output
  @output ||= debug? ? $stdout : File.open(IO::NULL, "w")
end
requirement_satisfied_by?(requirement, activated, spec) 点击以切换源代码
# File rubygems/resolver.rb, line 258
def requirement_satisfied_by?(requirement, activated, spec)
  matches_spec = requirement.matches_spec? spec
  return matches_spec if @soft_missing

  matches_spec &&
    spec.spec.required_ruby_version.satisfied_by?(Gem.ruby_version) &&
    spec.spec.required_rubygems_version.satisfied_by?(Gem.rubygems_version)
end
resolve() 点击以切换源代码

开始解析!返回一个 ActivationRequest 对象数组。

# File rubygems/resolver.rb, line 185
def resolve
  Gem::Molinillo::Resolver.new(self, self).resolve(@needed.map {|d| DependencyRequest.new d, nil }).tsort.filter_map(&:payload)
rescue Gem::Molinillo::VersionConflict => e
  conflict = e.conflicts.values.first
  raise Gem::DependencyResolutionError, Conflict.new(conflict.requirement_trees.first.first, conflict.existing, conflict.requirement)
ensure
  @output.close if defined?(@output) && !debug?
end
search_for(dependency) 点击以切换源代码
# File rubygems/resolver.rb, line 223
def search_for(dependency)
  possibles, all = find_possible(dependency)
  if !@soft_missing && possibles.empty?
    exc = Gem::UnsatisfiableDependencyError.new dependency, all
    exc.errors = @set.errors
    raise exc
  end

  groups = Hash.new {|hash, key| hash[key] = [] }

  # create groups & sources in the same loop
  sources = possibles.map do |spec|
    source = spec.source
    groups[source] << spec
    source
  end.uniq.reverse

  activation_requests = []

  sources.each do |source|
    groups[source].
      sort_by {|spec| [spec.version, spec.platform =~ Gem::Platform.local ? 1 : 0] }. # rubocop:disable Performance/RegexpMatch
      map {|spec| ActivationRequest.new spec, dependency }.
      each {|activation_request| activation_requests << activation_request }
  end

  activation_requests
end
sort_dependencies(dependencies, activated, conflicts) 点击以切换源代码
# File rubygems/resolver.rb, line 275
def sort_dependencies(dependencies, activated, conflicts)
  dependencies.sort_by.with_index do |dependency, i|
    name = name_for(dependency)
    [
      activated.vertex_named(name).payload ? 0 : 1,
      amount_constrained(dependency),
      conflicts[name] ? 0 : 1,
      activated.vertex_named(name).payload ? 0 : search_for(dependency).count,
      i, # for stable sort
    ]
  end
end

私有实例方法

amount_constrained(dependency) 点击以切换源代码

返回一个 (-infty, 0] 中的整数,一个更接近 0 的数字表示依赖项的约束性较小

具有 0 或 1 个可能性的依赖项(忽略版本要求)被赋予非常负的值,因此它们始终在不受约束的依赖项之前首先排序

# File rubygems/resolver.rb, line 297
def amount_constrained(dependency)
  @amount_constrained ||= {}
  @amount_constrained[dependency.name] ||= begin
    name_dependency = Gem::Dependency.new(dependency.name)
    dependency_request_for_name = Gem::Resolver::DependencyRequest.new(name_dependency, dependency.requester)
    all = @set.find_all(dependency_request_for_name).size

    if all <= 1
      all - SINGLE_POSSIBILITY_CONSTRAINT_PENALTY
    else
      search = search_for(dependency).size
      search - all
    end
  end
end