Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not try to install gems that are not compatible #968

Closed
grosser opened this issue Jul 8, 2014 · 6 comments
Closed

Do not try to install gems that are not compatible #968

grosser opened this issue Jul 8, 2014 · 6 comments

Comments

@grosser
Copy link
Contributor

grosser commented Jul 8, 2014

for example on ruby 1.8.7:

gem i timecop
Fetching: timecop-0.7.1.gem (100%)
ERROR:  Error installing timecop:
    timecop requires Ruby version >= 1.9.2.

instead it should try to find a compatible version and install that

@drbrain drbrain added this to the Future milestone Jul 8, 2014
@drbrain
Copy link
Member

drbrain commented Jul 8, 2014

This is dependent upon the information being available in the index. This work is being performed by @indirect

@martindorey
Copy link

This has been causing me so much pain for so long that I worked out a solution for Ruby 1.9. Here's my environment:

martind@usgen:~$ lsb_release --codename
Codename:	wheezy
martind@usgen:~$ gem --version
1.8.23
martind@usgen:~$ ruby --version
ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
martind@usgen:~$ 

Here's a demonstration of the problem:

martind@usgen:~$ sudo gem install google-api-client
Fetching: uber-0.0.15.gem (100%)
Fetching: representable-2.3.0.gem (100%)
Fetching: retriable-2.1.0.gem (100%)
Fetching: public_suffix-2.0.4.gem (100%)
ERROR:  Error installing google-api-client:
	public_suffix requires Ruby version >= 2.0.
martind@usgen:~$ 

I also need to show you this at this stage so you know I'm not cheating later:

martind@usgen:~$ sudo gem install google-api-client --version '< 999999'
ERROR:  Error installing google-api-client:
	public_suffix requires Ruby version >= 2.0.
martind@usgen:~$ 

Here's my fix:

TARGET_DIRECTORY=/usr/local/lib/site_ruby/1.9.1/rubygems
sudo mkdir -p $TARGET_DIRECTORY
sudo cp /usr/lib/ruby/1.9.1/rubygems/dependency_installer.rb $TARGET_DIRECTORY/
sudo patch $TARGET_DIRECTORY/dependency_installer.rb <<EOF
--- /usr/lib/ruby/1.9.1/rubygems/dependency_installer.rb	2015-04-27 14:59:47.000000000 -0700
+++ /usr/local/lib/site_ruby/1.9.1/rubygems/dependency_installer.rb	2016-12-01 18:11:15.813880052 -0800
@@ -82,7 +82,7 @@
   # sources.  Gems are sorted with newer gems preferred over older gems, and
   # local gems preferred over remote gems.
 
-  def find_gems_with_sources(dep)
+  def original_find_gems_with_sources(dep)
     # Reset the errors
     @errors = nil
     gems_and_sources = []
@@ -125,6 +125,18 @@
     end
   end
 
+  def find_gems_with_sources(dep)
+    results = original_find_gems_with_sources(dep)
+    results.reject! do |dep_spec,|
+      if rrv = dep_spec.required_ruby_version then
+        !rrv.satisfied_by? Gem.ruby_version
+      else
+        false
+      end
+    end
+    return results
+  end
+
   ##
   # Gathers all dependencies necessary for the installation from local and
   # remote sources unless the ignore_dependencies was given.
EOF

Here's what happens after applying it:

martind@usgen:~$ sudo gem install google-api-client
ERROR:  Could not find a valid gem 'google-api-client' (>= 0) in any repository
ERROR:  Possible alternatives: google-api-client
martind@usgen:~$ 

Trouble is, by default, gem install only considers the latest version. To get it to try others, you need to give it a --version that doesn't start with >, like this:

martind@usgen:~$ sudo gem install google-api-client --version '< 999999'
Fetching: addressable-2.4.0.gem (100%)
Fetching: hurley-0.2.gem (100%)
Fetching: faraday-0.10.0.gem (100%)
Fetching: little-plugger-1.1.4.gem (100%)
Fetching: multi_json-1.12.1.gem (100%)
Fetching: logging-2.1.0.gem (100%)
Fetching: jwt-1.5.6.gem (100%)
Fetching: memoist-0.15.0.gem (100%)
Fetching: os-0.9.6.gem (100%)
Fetching: signet-0.7.3.gem (100%)
Fetching: googleauth-0.5.1.gem (100%)
Fetching: thor-0.19.4.gem (100%)
Fetching: google-api-client-0.9.4.gem (100%)
Successfully installed addressable-2.4.0
Successfully installed hurley-0.2
Successfully installed faraday-0.10.0
Successfully installed little-plugger-1.1.4
Successfully installed multi_json-1.12.1
Successfully installed logging-2.1.0
Successfully installed jwt-1.5.6
Successfully installed memoist-0.15.0
Successfully installed os-0.9.6
Successfully installed signet-0.7.3
Successfully installed googleauth-0.5.1
Successfully installed thor-0.19.4
Successfully installed google-api-client-0.9.4
13 gems installed
Installing ri documentation for addressable-2.4.0...
Installing ri documentation for hurley-0.2...
Installing ri documentation for faraday-0.10.0...
Installing ri documentation for little-plugger-1.1.4...
Installing ri documentation for multi_json-1.12.1...
Installing ri documentation for logging-2.1.0...
Installing ri documentation for jwt-1.5.6...
Installing ri documentation for memoist-0.15.0...
Installing ri documentation for os-0.9.6...
Installing ri documentation for signet-0.7.3...
Installing ri documentation for googleauth-0.5.1...
Installing ri documentation for thor-0.19.4...
Installing ri documentation for google-api-client-0.9.4...
Installing RDoc documentation for addressable-2.4.0...
Installing RDoc documentation for hurley-0.2...
Installing RDoc documentation for faraday-0.10.0...
Installing RDoc documentation for little-plugger-1.1.4...
Installing RDoc documentation for multi_json-1.12.1...
Installing RDoc documentation for logging-2.1.0...
Installing RDoc documentation for jwt-1.5.6...
Installing RDoc documentation for memoist-0.15.0...
Installing RDoc documentation for os-0.9.6...
Installing RDoc documentation for signet-0.7.3...
Installing RDoc documentation for googleauth-0.5.1...
Installing RDoc documentation for thor-0.19.4...
Installing RDoc documentation for google-api-client-0.9.4...
martind@usgen:~$ 

If invoked with such a constraint, gem fetches all the gemspec files. That's slow. I guess that'll be why the project developers don't want to encourage you to install this way, but wanted to wait for some proper data structure to arrive. The speed isn't bad enough for me to care. It's nowhere near as long as the time for installing ri and RDoc for eg but especially google-api-client. (I would normally use --no-ri and --no-rdoc - who looks for documentation anywhere but Google? - but, to make the examples simpler, I left them off.) I do this once per server. It's more important to me that this automated installation keeps working in the face of gem updates that I don't particularly need.

Another trouble is that the function I shimmed has a different signature in eg Ruby 2.1, but I haven't had these compatibility problems there... yet. The fix does, though, apply to Ruby 1.9.3 in Debian Wheezy and, with fuzz, to Ruby 1.9.2 in Debian Squeeze and Debian Lenny. Yes, yes, I know - it's 2016, why am I still using such old versions? I hope there are others, like the OP, in a similarly ancient boat, or I've bored you with this large issue hijack to no avail, sorry!

@martindorey
Copy link

That --version '< 999999' only causes find_gems_with_sources to consider all available candidates for the gem mentioned on the command line, not any gems that it might depend on and so on down the dependency tree. That's now bitten me, causing me to run with this too:

--- /usr/lib/ruby/1.9.1/rubygems/dependency_installer.rb	2013-12-02 02:53:23.000000000 -0800
+++ /usr/local/lib/site_ruby/1.9.1/rubygems/dependency_installer.rb	2017-01-16 17:05:36.766115898 -0800
@@ -106,6 +106,7 @@
               # guaranteed to match the newest specs
               (requirements.length > 1 or
                 (requirements.first != ">=" and requirements.first != ">"))
+        all = true
 
         found, @errors = Gem::SpecFetcher.fetcher.fetch_with_errors dep, all, true, dep.prerelease?
 
EOF

@martindorey
Copy link

Oh, when I was here a year ago, I thought this had already been fixed properly upstream and that I was only still having problems because I was stuck with a version from the stone age. Now it's afflicting me with Ruby 2.1, I've contrived a bodge for that version. First, though, a backport of my above bodge to Ruby 1.8:

--- /usr/lib/ruby/vendor_ruby/1.8/rubygems/dependency_installer.rb	2017-09-26 14:29:36.000000000 -0700
+++ /usr/local/lib/site_ruby/1.8/rubygems/dependency_installer.rb	2018-04-06 16:23:20.039506561 -0700
@@ -82,7 +82,7 @@
   # sources.  Gems are sorted with newer gems preferred over older gems, and
   # local gems preferred over remote gems.
 
-  def find_gems_with_sources(dep)
+  def original_find_gems_with_sources(dep)
     # Reset the errors
     @errors = nil
     gems_and_sources = []
@@ -106,6 +106,7 @@
               # guaranteed to match the newest specs
               (requirements.length > 1 or
                 (requirements.first != ">=" and requirements.first != ">"))
+        all = true
 
         found, @errors = Gem::SpecFetcher.fetcher.fetch_with_errors dep, all, true, dep.prerelease?
 
@@ -125,6 +126,18 @@
     end
   end
 
+  def find_gems_with_sources(dep)
+    results = original_find_gems_with_sources(dep)
+    results.reject! do |dep_spec,|
+      if rrv = dep_spec.required_ruby_version then
+        !rrv.satisfied_by? Gem.ruby_version
+      else
+        false
+      end
+    end
+    return results
+  end
+
   ##
   # Gathers all dependencies necessary for the installation from local and
   # remote sources unless the ignore_dependencies was given.

For Ruby 2.1, doing roughly the same was necessary, I think for the gems on the command line, but sadly not sufficient, I think for dependencies thereof. Here's both stuck together:

--- /usr/lib/ruby/2.1.0/rubygems/dependency_installer.rb	2016-06-06 22:04:42.000000000 -0700
+++ /usr/local/lib/site_ruby/2.1.0/rubygems/dependency_installer.rb	2018-04-06 14:53:50.183566723 -0700
@@ -195,7 +195,7 @@
   # sources.  Gems are sorted with newer gems preferred over older gems, and
   # local gems preferred over remote gems.
 
-  def find_gems_with_sources dep, best_only=false # :nodoc:
+  def original_find_gems_with_sources dep, best_only=false # :nodoc:
     set = Gem::AvailableSet.new
 
     if consider_local?
@@ -254,6 +254,21 @@
     set
   end
 
+  def find_gems_with_sources dep, best_only=false # :nodoc:
+    results = original_find_gems_with_sources(dep)
+    filtered = Gem::AvailableSet.new
+    results.set.each do |result|
+      dep_spec = result.spec
+      if rrv = dep_spec.required_ruby_version then
+        if !rrv.satisfied_by? Gem.ruby_version then
+          next
+        end
+      end
+      filtered.add(*result)
+    end
+    return filtered
+  end
+
   ##
   # Finds a spec and the source_uri it came from for gem +gem_name+ and
   # +version+.  Returns an Array of specs and sources required for
--- /usr/lib/ruby/2.1.0/rubygems/dependency.rb	2016-06-06 22:04:42.000000000 -0700
+++ /usr/local/lib/site_ruby/2.1.0/rubygems/dependency.rb	2018-04-06 14:49:04.664163762 -0700
@@ -95,7 +95,8 @@
   # of a gem?
 
   def latest_version?
-    @requirement.none?
+    #@requirement.none?
+    false
   end
 
   def pretty_print q # :nodoc:
--- /usr/lib/ruby/2.1.0/rubygems/resolver.rb	2016-06-06 22:04:42.000000000 -0700
+++ /usr/local/lib/site_ruby/2.1.0/rubygems/resolver.rb	2018-04-16 12:26:09.480063281 -0700
@@ -315,6 +315,21 @@
 
       matching, all = find_possible dep
 
+      # Sort them so that we try the highest versions first.
+      matching = matching.sort_by do |s|
+        [s.source, s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
+      end
+
+      while !matching.empty?
+        spec = matching.last
+        break unless spec.respond_to?(:spec)
+        full_spec = spec.spec
+        rrv = full_spec.required_ruby_version
+        break unless rrv
+        break if rrv.satisfied_by? Gem.ruby_version
+        matching.pop
+      end
+
       case matching.size
       when 0
         resolve_for_zero dep, all
@@ -355,11 +370,6 @@
   # choices.
 
   def resolve_for_multiple needed, specs, states, dep, possible # :nodoc:
-    # Sort them so that we try the highest versions first.
-    possible = possible.sort_by do |s|
-      [s.source, s.version, s.platform == Gem::Platform::RUBY ? -1 : 1]
-    end
-
     spec, act = activation_request dep, possible
 
     # We may need to try all of +possible+, so we setup state to unwind back

@scrosland
Copy link

I've taken the Ruby 2.1 patch from @martindorey and ported it to Ruby 2.3. The latter uses Molinillo as a dependency resolver, so that necessitated a slight reworking of the patch. Here it is:

--- /usr/lib/ruby/2.3.0/rubygems/dependency_installer.rb        2016-11-21 07:58:27.000000000 +0000
+++ /usr/local/lib/site_ruby/2.3.0/rubygems/dependency_installer.rb     2019-10-17 13:18:27.795255788 +0100
@@ -198,7 +198,7 @@
   # sources.  Gems are sorted with newer gems preferred over older gems, and
   # local gems preferred over remote gems.

-  def find_gems_with_sources dep, best_only=false # :nodoc:
+  def original_find_gems_with_sources dep, best_only=false # :nodoc:
     set = Gem::AvailableSet.new

     if consider_local?
@@ -267,6 +267,21 @@
     set
   end

+  def find_gems_with_sources dep, best_only=false # :nodoc:
+    results = original_find_gems_with_sources(dep)
+    filtered = Gem::AvailableSet.new
+    results.set.each do |result|
+      dep_spec = result.spec
+      if rrv = dep_spec.required_ruby_version then
+        if !rrv.satisfied_by? Gem.ruby_version then
+          next
+        end
+      end
+      filtered.add(*result)
+    end
+    return filtered
+  end
+
   ##
   # Finds a spec and the source_uri it came from for gem +gem_name+ and
   # +version+.  Returns an Array of specs and sources required for
--- /usr/lib/ruby/2.3.0/rubygems/dependency.rb  2016-11-21 07:58:27.000000000 +0000
+++ /usr/local/lib/site_ruby/2.3.0/rubygems/dependency.rb       2019-10-17 13:18:27.795255788 +0100
@@ -96,7 +96,8 @@
   # of a gem?

   def latest_version?
-    @requirement.none?
+    #@requirement.none?
+    false
   end

   def pretty_print q # :nodoc:
--- /usr/lib/ruby/2.3.0/rubygems/resolver.rb    2016-11-21 07:58:27.000000000 +0000
+++ /usr/local/lib/site_ruby/2.3.0/rubygems/resolver.rb 2019-10-17 13:21:49.443215912 +0100
@@ -227,13 +227,26 @@

   def search_for(dependency)
     possibles, all = find_possible(dependency)
+
+    possibles = possibles.sort_by { |s| [s.source, s.version, s.platform.to_s == Gem::Platform.local.to_s ? 1 : 0] }
+
+    while !possibles.empty?
+      spec = possibles.last
+      break unless spec.respond_to?(:spec)
+      full_spec = spec.spec
+      rrv = full_spec.required_ruby_version
+      break unless rrv
+      break if rrv.satisfied_by? Gem.ruby_version
+      possibles.pop
+    end
+
     if !@soft_missing && possibles.empty?
       @missing << dependency
       exc = Gem::UnsatisfiableDependencyError.new dependency, all
       exc.errors = @set.errors
       raise exc
     end
-    possibles.sort_by { |s| [s.source, s.version, s.platform.to_s == Gem::Platform.local.to_s ? 1 : 0] }.
+    possibles.
       map { |s| ActivationRequest.new s, dependency, [] }
   end

@deivid-rodriguez
Copy link
Member

This is an unfortunate long standing issue which we have never managed to fix yet. Solving this is still in the roadmap, and there's a newer ticket tracking this very same problem, so I'll close this old ticket in favour of that one, since it should contain more up to date information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants