summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-11-21 23:27:30 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-11-21 23:27:30 +0000
commit5307d803f5cce7b14a6afd1d51f6d53ec85ca87d (patch)
treeaac2997a9ff000fbf2f1f9f27077bb7b2403f2c9 /lib
parentb1529a30e08040b717adef8ac1fa8be1c060e7e1 (diff)
* lib/rubygems: Update to RubyGems master 50a8210. Important changes
in this commit: RubyGems now automatically checks for gem.deps.rb or Gemfile when running ruby executables. This behavior is similar to `bundle exec rake`. This change may be reverted before Ruby 2.1.0 if too many bugs are found. * test/rubygems: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@43767 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/rubygems.rb99
-rw-r--r--lib/rubygems/doctor.rb7
-rw-r--r--lib/rubygems/exceptions.rb2
-rw-r--r--lib/rubygems/request_set.rb15
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb17
-rw-r--r--lib/rubygems/resolver.rb51
-rw-r--r--lib/rubygems/resolver/api_set.rb49
-rw-r--r--lib/rubygems/resolver/api_specification.rb37
-rw-r--r--lib/rubygems/resolver/composed_set.rb19
-rw-r--r--lib/rubygems/resolver/conflict.rb41
-rw-r--r--lib/rubygems/resolver/git_set.rb33
-rw-r--r--lib/rubygems/resolver/requirement_list.rb4
-rw-r--r--lib/rubygems/source.rb44
-rw-r--r--lib/rubygems/source/git.rb55
-rw-r--r--lib/rubygems/specification.rb14
-rw-r--r--lib/rubygems/test_case.rb6
-rw-r--r--lib/rubygems/util.rb29
17 files changed, 356 insertions, 166 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 2e8ad7bba1..a6c97ed16d 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -215,50 +215,6 @@ module Gem
end
end
- def self.detect_gemdeps
- if path = ENV['RUBYGEMS_GEMDEPS']
- path = path.dup.untaint
-
- if path == "-"
- here = Dir.pwd.untaint
- start = here
-
- begin
- while true
- path = GEM_DEP_FILES.find { |f| File.file?(f) }
-
- if path
- path = File.join here, path
- break
- end
-
- Dir.chdir ".."
-
- # If we're at a toplevel, stop.
- return if Dir.pwd == here
-
- here = Dir.pwd
- end
- ensure
- Dir.chdir start
- end
- end
-
- path.untaint
-
- return unless File.file? path
-
- rs = Gem::RequestSet.new
- rs.load_gemdeps path
-
- rs.resolve_current.map do |s|
- sp = s.full_spec
- sp.activate
- sp
- end
- end
- end
-
##
# Find the full path to the executable for gem +name+. If the +exec_name+
# is not given, the gem's default_executable is chosen, otherwise the
@@ -1035,6 +991,61 @@ module Gem
load_plugin_files files
end
+ ##
+ # Looks for gem dependency files (gem.deps.rb, Gemfile, Isolate) from the
+ # current directory up and activates the gems in the first file found.
+ #
+ # This is run automatically when rubygems starts. To disable, set
+ # the <code>RUBYGEMS_GEMDEPS=</code> environment variable to an empty
+ # string.
+
+ def self.use_gemdeps
+ return unless path = ENV['RUBYGEMS_GEMDEPS'] || '-'
+ path = path.dup.untaint
+
+ if path == "-"
+ here = Dir.pwd.untaint
+ start = here
+
+ begin
+ while true
+ path = GEM_DEP_FILES.find { |f| File.file?(f) }
+
+ if path
+ path = File.join here, path
+ break
+ end
+
+ Dir.chdir ".."
+
+ # If we're at a toplevel, stop.
+ return if Dir.pwd == here
+
+ here = Dir.pwd
+ end
+ ensure
+ Dir.chdir start
+ end
+ end
+
+ path.untaint
+
+ return unless File.file? path
+
+ rs = Gem::RequestSet.new
+ rs.load_gemdeps path
+
+ rs.resolve_current.map do |s|
+ sp = s.full_spec
+ sp.activate
+ sp
+ end
+ end
+
+ class << self
+ alias detect_gemdeps use_gemdeps # :nodoc:
+ end
+
# FIX: Almost everywhere else we use the `def self.` way of defining class
# methods, and then we switch over to `class << self` here. Pick one or the
# other.
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
index 2cb8901b4d..0aa0f7b79f 100644
--- a/lib/rubygems/doctor.rb
+++ b/lib/rubygems/doctor.rb
@@ -27,10 +27,13 @@ class Gem::Doctor
['gems', ''],
]
- raise 'Update REPOSITORY_EXTENSION_MAP' unless
- Gem::REPOSITORY_SUBDIRECTORIES.sort ==
+ missing =
+ Gem::REPOSITORY_SUBDIRECTORIES.sort -
REPOSITORY_EXTENSION_MAP.map { |(k,_)| k }.sort
+ raise "Update REPOSITORY_EXTENSION_MAP, missing: #{missing.join ', '}" unless
+ missing.empty?
+
##
# Creates a new Gem::Doctor that will clean up +gem_repository+. Only one
# gem repository may be cleaned at a time.
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index ee3d8fecdf..6bd50eca2c 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -223,7 +223,7 @@ class Gem::UnsatisfiableDependencyError < Gem::Exception
attr_reader :dependency
##
- # Creates a new UnsatisfiableDepedencyError for the unsatisfiable
+ # Creates a new UnsatisfiableDependencyError for the unsatisfiable
# Gem::Resolver::DependencyRequest +dep+
def initialize dep, platform_mismatch=nil
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 42d457063f..d91a39cb22 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -2,6 +2,7 @@ require 'rubygems'
require 'rubygems/dependency'
require 'rubygems/dependency_list'
require 'rubygems/installer'
+require 'rubygems/resolver'
require 'tsort'
##
@@ -146,7 +147,15 @@ class Gem::RequestSet
resolve
- install options, &block
+ if options[:explain]
+ puts "Gems to install:"
+
+ specs.map { |s| s.full_name }.sort.each do |s|
+ puts " #{s}"
+ end
+ else
+ install options, &block
+ end
end
def install_into dir, force = true, options = {}
@@ -201,7 +210,7 @@ class Gem::RequestSet
# Resolve the requested dependencies and return an Array of Specification
# objects to be activated.
- def resolve set = Gem::Resolver::IndexSet.new
+ def resolve set = Gem::Resolver::BestSet.new
@sets << set
@sets << @git_set
@sets << @vendor_set
@@ -253,7 +262,7 @@ class Gem::RequestSet
end
else
unless @soft_missing
- raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
+ raise Gem::DependencyError, "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
end
end
end
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index 8e29eb87e5..0c27b1a61a 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -115,7 +115,7 @@ class Gem::RequestSet::GemDependencyAPI
##
# A Hash containing gem names and files to require from those gems.
- attr_reader :requires
+ attr_reader :requires # :nodoc:
##
# A set of gems that are loaded via the +:path+ option to #gem
@@ -125,7 +125,7 @@ class Gem::RequestSet::GemDependencyAPI
##
# The groups of gems to exclude from installation
- attr_accessor :without_groups
+ attr_accessor :without_groups # :nodoc:
##
# Creates a new GemDependencyAPI that will add dependencies to the
@@ -282,6 +282,8 @@ class Gem::RequestSet::GemDependencyAPI
true
end
+ private :gem_github
+
##
# Handles the :group and :groups +options+ for the gem with the given
# +name+.
@@ -361,7 +363,7 @@ class Gem::RequestSet::GemDependencyAPI
def gem_requires name, options # :nodoc:
if options.include? :require then
if requires = options.delete(:require) then
- @requires[name].concat requires
+ @requires[name].concat Array requires
end
else
@requires[name] << name
@@ -370,6 +372,11 @@ class Gem::RequestSet::GemDependencyAPI
private :gem_requires
+ ##
+ # :category: Gem Dependencies DSL
+ #
+ # Block form for specifying gems from a git +repository+.
+
def git repository
@current_repository = repository
@@ -424,6 +431,8 @@ class Gem::RequestSet::GemDependencyAPI
##
# :category: Gem Dependencies DSL
+ #
+ # Block form for restricting gems to a particular platform.
def platform what
@current_platform = what
@@ -436,6 +445,8 @@ class Gem::RequestSet::GemDependencyAPI
##
# :category: Gem Dependencies DSL
+ #
+ # Block form for restricting gems to a particular platform.
alias :platforms :platform
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index 2669cc4f24..828c61de01 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -29,9 +29,23 @@ class Gem::Resolver
attr_accessor :soft_missing
+ ##
+ # Combines +sets+ into a ComposedSet that allows specification lookup in a
+ # uniform manner. If one of the +sets+ is itself a ComposedSet its sets are
+ # flattened into the result ComposedSet.
+
def self.compose_sets *sets
sets.compact!
+ sets = sets.map do |set|
+ case set
+ when Gem::Resolver::ComposedSet then
+ set.sets
+ else
+ set
+ end
+ end.flatten
+
case sets.length
when 0 then
raise ArgumentError, 'one set in the composition must be non-nil'
@@ -77,6 +91,15 @@ class Gem::Resolver
end
end
+ def explain_list(stage, data)
+ if DEBUG_RESOLVER
+ STDOUT.printf "%20s (%d entries)\n", stage.to_s.upcase, data.size
+ data.each do |d|
+ STDOUT.printf "%20s %s\n", "", d
+ end
+ end
+ end
+
##
# Creates an ActivationRequest for the given +dep+ and the last +possible+
# specification.
@@ -134,8 +157,6 @@ class Gem::Resolver
# If no good candidate is found, the first state is tried.
def find_conflict_state conflict, states # :nodoc:
- rejected = []
-
until states.empty? do
state = states.pop
@@ -145,14 +166,9 @@ class Gem::Resolver
state.conflicts << [state.spec, conflict]
return state
end
-
- rejected << state
end
- return rejected.shift
- ensure
- rejected = rejected.concat states
- states.replace rejected
+ nil
end
##
@@ -172,14 +188,23 @@ class Gem::Resolver
# If the existing activation indicates that there are other possibles for
# it, then issue the conflict on the dependency for the activation itself.
- # Otherwise, issue it on the requester's request itself.
- if existing.others_possible? or existing.request.requester.nil? then
+ # Otherwise, if there was a requester, issue it on the requester's
+ # request itself.
+ # Finally, if the existing request has no requester (toplevel) unwind to
+ # it anyway.
+
+ if existing.others_possible?
conflict =
Gem::Resolver::Conflict.new dep, existing
- else
+ elsif dep.requester
depreq = dep.requester.request
conflict =
Gem::Resolver::Conflict.new depreq, existing, dep
+ elsif existing.request.requester.nil?
+ conflict =
+ Gem::Resolver::Conflict.new dep, existing
+ else
+ raise Gem::DependencyError, "Unable to figure out how to unwind conflict"
end
@conflicts << conflict unless @conflicts.include? conflict
@@ -234,6 +259,8 @@ class Gem::Resolver
while !needed.empty?
dep = needed.remove
explain :try, [dep, dep.requester ? dep.requester.request : :toplevel]
+ explain_list :next5, needed.next5
+ explain_list :specs, Array(specs).map { |x| x.full_name }.sort
# If there is already a spec activated for the requested name...
if specs && existing = specs.find { |s| dep.name == s.name }
@@ -284,7 +311,7 @@ class Gem::Resolver
# Retry resolution with this spec and add it's dependencies
spec, act = activation_request state.dep, state.possibles
- needed = requests spec, act, state.needed
+ needed = requests spec, act, state.needed.dup
specs = Gem::List.prepend state.specs, act
return needed, specs
diff --git a/lib/rubygems/resolver/api_set.rb b/lib/rubygems/resolver/api_set.rb
index 60bf911063..89ee3c9b15 100644
--- a/lib/rubygems/resolver/api_set.rb
+++ b/lib/rubygems/resolver/api_set.rb
@@ -10,13 +10,28 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
attr_reader :dep_uri # :nodoc:
##
+ # The Gem::Source that gems are fetched from
+
+ attr_reader :source
+
+ ##
+ # The corresponding place to fetch gems.
+
+ attr_reader :uri
+
+ ##
# Creates a new APISet that will retrieve gems from +uri+ using the RubyGems
- # API described at http://guides.rubygems.org/rubygems-org-api
+ # API URL +dep_uri+ which is described at
+ # http://guides.rubygems.org/rubygems-org-api
+
+ def initialize dep_uri = 'https://rubygems.org/api/v1/dependencies'
+ dep_uri = URI dep_uri unless URI === dep_uri # for ruby 1.8
- def initialize uri = 'https://rubygems.org/api/v1/dependencies'
- uri = URI uri unless URI === uri # for ruby 1.8
- @data = Hash.new { |h,k| h[k] = [] }
- @dep_uri = uri
+ @dep_uri = dep_uri
+ @uri = dep_uri + '../../..'
+
+ @data = Hash.new { |h,k| h[k] = [] }
+ @source = Gem::Source.new @uri
end
##
@@ -41,15 +56,35 @@ class Gem::Resolver::APISet < Gem::Resolver::Set
def prefetch reqs
names = reqs.map { |r| r.dependency.name }
- needed = names.find_all { |d| !@data.key?(d) }
+ needed = names - @data.keys
return if needed.empty?
uri = @dep_uri + "?gems=#{needed.sort.join ','}"
str = Gem::RemoteFetcher.fetcher.fetch_path uri
+ loaded = []
+
Marshal.load(str).each do |ver|
- @data[ver[:name]] << ver
+ name = ver[:name]
+
+ @data[name] << ver
+ loaded << name
+ end
+
+ (needed - loaded).each do |missing|
+ @data[missing] = []
+ end
+ end
+
+ def pretty_print q # :nodoc:
+ q.group 2, '[APISet', ']' do
+ q.breakable
+ q.text "URI: #{@dep_uri}"
+
+ q.breakable
+ q.text 'gem names:'
+ q.pp @data.keys
end
end
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index 19611e17d8..0ab8e830c6 100644
--- a/lib/rubygems/resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -34,5 +34,42 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
@dependencies == other.dependencies
end
+ def pretty_print q # :nodoc:
+ q.group 2, '[APISpecification', ']' do
+ q.breakable
+ q.text "name: #{name}"
+
+ q.breakable
+ q.text "version: #{version}"
+
+ q.breakable
+ q.text "platform: #{platform}"
+
+ q.breakable
+ q.text 'dependencies:'
+ q.breakable
+ q.pp @dependencies
+
+ q.breakable
+ q.text "set uri: #{@set.dep_uri}"
+ end
+ end
+
+ ##
+ # Fetches a Gem::Specification for this APISpecification.
+
+ def spec # :nodoc:
+ @spec ||=
+ begin
+ tuple = Gem::NameTuple.new @name, @version, @platform
+
+ source.fetch_spec tuple
+ end
+ end
+
+ def source # :nodoc:
+ @set.source
+ end
+
end
diff --git a/lib/rubygems/resolver/composed_set.rb b/lib/rubygems/resolver/composed_set.rb
index e4aa15e4d0..702bd9ccfc 100644
--- a/lib/rubygems/resolver/composed_set.rb
+++ b/lib/rubygems/resolver/composed_set.rb
@@ -1,17 +1,36 @@
+##
+# A ComposedSet allows multiple sets to be queried like a single set.
+#
+# To create a composed set with any number of sets use:
+#
+# Gem::Resolver.compose_sets set1, set2
+#
+# This method will eliminate nesting of composed sets.
+
class Gem::Resolver::ComposedSet < Gem::Resolver::Set
attr_reader :sets # :nodoc:
+ ##
+ # Creates a new ComposedSet containing +sets+. Use
+ # Gem::Resolver::compose_sets instead.
+
def initialize *sets
@sets = sets
end
+ ##
+ # Finds all specs matching +req+ in all sets.
+
def find_all req
res = []
@sets.each { |s| res += s.find_all(req) }
res
end
+ ##
+ # Prefetches +reqs+ in all sets.
+
def prefetch reqs
@sets.each { |s| s.prefetch(reqs) }
end
diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb
index b081972658..20c6eced31 100644
--- a/lib/rubygems/resolver/conflict.rb
+++ b/lib/rubygems/resolver/conflict.rb
@@ -4,25 +4,38 @@
class Gem::Resolver::Conflict
+ ##
+ # The specification that was activated prior to the conflict
+
attr_reader :activated
+ ##
+ # The dependency that is in conflict with the activated gem.
+
attr_reader :dependency
attr_reader :failed_dep # :nodoc:
+ ##
+ # Creates a new resolver conflict when +dependency+ is in conflict with an
+ # already +activated+ specification.
+
def initialize(dependency, activated, failed_dep=dependency)
@dependency = dependency
@activated = activated
@failed_dep = failed_dep
end
- def == other
+ def == other # :nodoc:
self.class === other and
@dependency == other.dependency and
@activated == other.activated and
@failed_dep == other.failed_dep
end
+ ##
+ # A string explanation of the conflict.
+
def explain
"<Conflict wanted: #{@failed_dep}, had: #{activated.spec.full_name}>"
end
@@ -41,11 +54,15 @@ class Gem::Resolver::Conflict
activated = @activated.spec.full_name
requirement = @failed_dep.dependency.requirement
- " Activated %s instead of (%s) via:\n %s\n" % [
- activated, requirement, request_path.join(', ')
+ " Activated %s via:\n %s\n instead of (%s) via:\n %s\n" % [
+ activated, request_path(@activated).join(', '),
+ requirement, request_path(requester).join(', '),
]
end
+ ##
+ # Returns true if the conflicting dependency's name matches +spec+.
+
def for_spec?(spec)
@dependency.name == spec.name
end
@@ -72,16 +89,17 @@ class Gem::Resolver::Conflict
end
##
- # Path of specifications that requested this dependency
+ # Path of activations from the +current+ list.
- def request_path
- current = requester
- path = []
+ def request_path current
+ path = []
while current do
- path << current.spec.full_name
+ spec_name = current.spec.full_name
+ requirement = current.request.dependency.requirement
+ path << "#{current.spec.full_name} (#{requirement})"
- current = current.request.requester
+ current = current.parent
end
path = ['user request (gem command or Gemfile)'] if path.empty?
@@ -98,5 +116,8 @@ class Gem::Resolver::Conflict
end
-Gem::Resolver::DependencyConflict = Gem::Resolver::Conflict
+##
+# TODO: Remove in RubyGems 3
+
+Gem::Resolver::DependencyConflict = Gem::Resolver::Conflict # :nodoc:
diff --git a/lib/rubygems/resolver/git_set.rb b/lib/rubygems/resolver/git_set.rb
index 3c38d3dca0..c912e367d9 100644
--- a/lib/rubygems/resolver/git_set.rb
+++ b/lib/rubygems/resolver/git_set.rb
@@ -42,38 +42,27 @@ class Gem::Resolver::GitSet < Gem::Resolver::Set
# Finds all git gems matching +req+
def find_all req
- @repositories.keys.select do |name|
- name == req.name
- end.map do |name|
- @specs[name] || load_spec(name)
- end.select do |spec|
+ prefetch nil
+
+ specs.values.select do |spec|
req.matches_spec? spec
end
end
- def load_spec name
- repository, reference = @repositories[name]
-
- source = Gem::Source::Git.new name, repository, reference
-
- spec = source.load_spec name
-
- git_spec =
- Gem::Resolver::GitSpecification.new self, spec, source
-
- @specs[name] = git_spec
- end
-
##
# Prefetches specifications from the git repositories in this set.
def prefetch reqs
- names = reqs.map { |req| req.name }
+ return unless @specs.empty?
+
+ @repositories.each do |name, (repository, reference)|
+ source = Gem::Source::Git.new name, repository, reference
- @repositories.each_key do |name|
- next unless names.include? name
+ source.specs.each do |spec|
+ git_spec = Gem::Resolver::GitSpecification.new self, spec, source
- load_spec name
+ @specs[spec.name] = git_spec
+ end
end
end
diff --git a/lib/rubygems/resolver/requirement_list.rb b/lib/rubygems/resolver/requirement_list.rb
index 8123e84fc7..04e437b2a9 100644
--- a/lib/rubygems/resolver/requirement_list.rb
+++ b/lib/rubygems/resolver/requirement_list.rb
@@ -37,4 +37,8 @@ class Gem::Resolver::RequirementList
def remove
@list.shift
end
+
+ def next5
+ @list[0,5]
+ end
end
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index a40a27594b..b6e2d97523 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -1,13 +1,30 @@
require 'uri'
require 'fileutils'
+##
+# A Source knows how to list and fetch gems from a RubyGems marshal index.
+#
+# There are other Source subclasses for installed gems, local gems, the
+# bundler dependency API and so-forth.
+
class Gem::Source
- FILES = {
+
+ include Comparable
+
+ FILES = { # :nodoc:
:released => 'specs',
:latest => 'latest_specs',
:prerelease => 'prerelease_specs',
}
+ ##
+ # The URI this source will fetch gems from.
+
+ attr_reader :uri
+
+ ##
+ # Creates a new Source which will use the index located at +uri+.
+
def initialize(uri)
unless uri.kind_of? URI
uri = URI.parse(uri.to_s)
@@ -17,13 +34,17 @@ class Gem::Source
@api_uri = nil
end
- attr_reader :uri
+ ##
+ # Use an SRV record on the host to look up the true endpoint for the index.
- def api_uri
+ def api_uri # :nodoc:
require 'rubygems/remote_fetcher'
@api_uri ||= Gem::RemoteFetcher.fetcher.api_endpoint uri
end
+ ##
+ # Sources are ordered by installation preference.
+
def <=>(other)
case other
when Gem::Source::Installed,
@@ -46,13 +67,11 @@ class Gem::Source
end
end
- include Comparable
-
- def ==(other)
+ def == other # :nodoc:
self.class === other and @uri == other.uri
end
- alias_method :eql?, :==
+ alias_method :eql?, :== # :nodoc:
##
# Returns a Set that can fetch specifications from this source.
@@ -70,7 +89,7 @@ class Gem::Source
end
end
- def hash
+ def hash # :nodoc:
@uri.hash
end
@@ -83,6 +102,9 @@ class Gem::Source
File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end
+ ##
+ # Returns true when it is possible and safe to update the cache directory.
+
def update_cache?
@update_cache ||=
begin
@@ -166,6 +188,10 @@ class Gem::Source
end
end
+ ##
+ # Downloads +spec+ and writes it to +dir+. See also
+ # Gem::RemoteFetcher#download.
+
def download(spec, dir=Dir.pwd)
fetcher = Gem::RemoteFetcher.fetcher
fetcher.download spec, api_uri.to_s, dir
@@ -176,7 +202,7 @@ class Gem::Source
q.breakable
q.text @uri.to_s
if api = api_uri
- g.text api
+ q.text api.to_s
end
end
end
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
index fdce03c59a..82690923ff 100644
--- a/lib/rubygems/source/git.rb
+++ b/lib/rubygems/source/git.rb
@@ -36,9 +36,11 @@ class Gem::Source::Git < Gem::Source
attr_reader :need_submodules
##
- # Creates a new git gem source for a gem with the given +name+ that will be
- # loaded from +reference+ in +repository+. If +submodules+ is true,
- # submodules will be checked out when the gem is installed.
+ # Creates a new git gem source for a gems from loaded from +repository+ at
+ # the given +reference+. The +name+ is only used to track the repository
+ # back to a gem dependencies file, it has no real significance as a git
+ # repository may contain multiple gems. If +submodules+ is true, submodules
+ # will be checked out when the gem is installed.
def initialize name, repository, reference, submodules = false
super(nil)
@@ -126,34 +128,6 @@ class Gem::Source::Git < Gem::Source
end
##
- # Loads a Gem::Specification for +name+ from this git repository.
-
- def load_spec name
- cache
-
- gemspec_reference = "#{@reference}:#{name}.gemspec"
-
- Dir.chdir repo_cache_dir do
- source = Gem::Util.popen @git, 'show', gemspec_reference
-
- source.force_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
- source.untaint
-
- begin
- spec = eval source, binding, gemspec_reference
-
- return spec if Gem::Specification === spec
-
- warn "git gem specification for #{@repository} #{gemspec_reference} is not a Gem::Specification (#{spec.class} instead)."
- rescue SignalException, SystemExit
- raise
- rescue SyntaxError, Exception
- warn "invalid git gem specification for #{@repository} #{gemspec_reference}"
- end
- end
- end
-
- ##
# The directory where the git gem's repository will be cached.
def repo_cache_dir # :nodoc:
@@ -164,13 +138,30 @@ class Gem::Source::Git < Gem::Source
# Converts the git reference for the repository into a commit hash.
def rev_parse # :nodoc:
- # HACK no safe equivalent of ` exists on 1.8.7
Dir.chdir repo_cache_dir do
Gem::Util.popen(@git, 'rev-parse', @reference).strip
end
end
##
+ # Loads all gemspecs in the repository
+
+ def specs
+ checkout
+
+ Dir.chdir install_dir do
+ Dir['{,*,*/*}.gemspec'].map do |spec_file|
+ directory = File.dirname spec_file
+ file = File.basename spec_file
+
+ Dir.chdir directory do
+ Gem::Specification.load file
+ end
+ end.compact
+ end
+ end
+
+ ##
# A hash for the git gem based on the git repository URI.
def uri_hash # :nodoc:
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index b95e2c0699..22e2981198 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -27,6 +27,7 @@ class Date; end
# Gem::Specification.new do |s|
# s.name = 'example'
# s.version = '0.1.0'
+# s.licenses = ['MIT']
# s.summary = "This is an example!"
# s.description = "Much longer explanation of the example!"
# s.authors = ["Ruby Coder"]
@@ -530,6 +531,7 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # :category: Recommended gemspec attributes
# The license for this gem.
#
# The license must be a short name, no more than 64 characters.
@@ -538,7 +540,12 @@ class Gem::Specification < Gem::BasicSpecification
# text of the license should be inside of the gem when you build it.
#
# See http://opensource.org/licenses/alphabetical for a list of licenses and
- # their abbreviations (or short names).
+ # their abbreviations (or short names). GitHub also provides a
+ # license picker at http://choosealicense.com/
+ #
+ # According to copyright law, not having an OSI-approved open source license
+ # means you have no rights to use the code for any purpose-- in other words,
+ # "all rights reserved".
#
# You can set multiple licenses with #licenses=
#
@@ -550,6 +557,7 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # :category: Recommended gemspec attributes
# The license(s) for the library.
#
# Each license must be a short name, no more than 64 characters.
@@ -2526,8 +2534,8 @@ class Gem::Specification < Gem::BasicSpecification
}
warning <<-warning if licenses.empty?
-licenses is empty. Use a license abbreviation from:
- http://opensource.org/licenses/alphabetical
+licenses is empty, but is recommended. Use a license abbreviation from:
+http://opensource.org/licenses/alphabetical
warning
validate_permissions
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index d1b471f619..f3967aba8b 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -1149,8 +1149,10 @@ Also, a list:
def dependency_request dep, from_name, from_version, parent = nil
remote = Gem::Source.new @uri
- parent ||= Gem::Resolver::DependencyRequest.new \
- dep, nil
+ unless parent then
+ parent_dep = dep from_name, from_version
+ parent = Gem::Resolver::DependencyRequest.new parent_dep, nil
+ end
spec = Gem::Resolver::IndexSpecification.new \
nil, from_name, from_version, remote, Gem::Platform::RUBY
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index af53e599b5..42663974a5 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -40,27 +40,24 @@ module Gem::Util
# for a command.
def self.popen *command
- begin
- r, = IO.popen command, &:read
- rescue TypeError # ruby 1.8 only supports string command
- r, w = IO.pipe
+ IO.popen command, &:read
+ rescue TypeError # ruby 1.8 only supports string command
+ r, w = IO.pipe
- pid = fork do
- STDIN.close
- STDOUT.reopen w
+ pid = fork do
+ STDIN.close
+ STDOUT.reopen w
- exec(*command)
- end
+ exec(*command)
+ end
- w.close
+ w.close
- begin
- return r.read
- ensure
- Process.wait pid
- end
+ begin
+ return r.read
+ ensure
+ Process.wait pid
end
-
end
end