Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Use ~/.gem/$ruby/X.Y as the GEM_HOME #235

Open
postmodern opened this Issue · 31 comments

5 participants

Postmodern James Blanding Felix Bünemann Jonne Haß Semyon Perepelitsa
Postmodern
Owner

Now that Ruby has moved to Semantic Versioning it means each new release will have a unique X.Y.Z version. Since GEM_HOME is defined as ~/.gem/$ruby/X.Y.Z, this means each new release will have an empty gem dir, forcing users to re-install all gems.

Would it be safe to define GEM_HOME as ~/.gem/$ruby/X.Y, ensuring that gems are shared between patch releases?

Postmodern postmodern was assigned
Postmodern
Owner

There could be problems with gems that used internal C functions that were added in specific 1.9.x versions.

Postmodern
Owner

However, RubyGems 2.2.0 how keeps built C extensions in a separate extensions/ directory:

extensions/
`-- x86_64-linux
    `-- 2.1.0-static
         `-- bond-0.4.3
            |-- gem.build_complete
            `-- gem_make.out
Postmodern
Owner

The change might be as simple as export GEM_HOME="$HOME/.gem/$RUBY_ENGINE/${RUBY_VERSION%.*}".

Postmodern
Owner

Another blocker is that rubygems has generated fully qualified #! for it's binstubs, instead of the usual #!/usr/bin/env ruby. This makes it difficult to share rubygems between multiple Rubies.

Postmodern
Owner

Unfortunately there is no GEMOPT env variable we could set to enable the --env-shebang option by default.

James Blanding

We just ran into the fully qualified shebang issue ourselves when a coworker updated his version of ruby-2.0.0 and deleted the older version. Maybe it makes sense to just suggest in the README to include "--env-shebang" in the install/update parameters of gemrc?

As for removing one identifier level from ~/.gem/${ruby}/X.Y.Z, it seems slightly premature, given that 1.9 has not yet reached EOL and is distributed with Rubygems 1.8.

Postmodern
Owner

Probably a good idea to document --env-shebang and gem pristin. Long-term, I wish #!/usr/bin/env ruby would be the default, and #!/path/to/ruby would only be used when ruby has a custom suffix (ex: ruby1.8).

Felix Bünemann

I think it would make sense to use the ruby C API version for the directories, which would be 1.8 for ruby 1.8.x, 1.9.1 for ruby 1.9.1+, 2.0.0 for 2.0.x and 2.1.0 for 2.1.x. All gems compiled against the same API version are supposed to be binary compatible.

The c api version can be seen by looking at usr/include/ruby/version.h and the macros RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR and RUBY_API_VERSION_TEENY, but I don't know how to get it with ruby code. This also doesn't really work for implementations without C api, like jruby.

Felix Bünemann

This is probably interesting to the discussion: Introducing a semantic versioning scheme and branching policy

Felix Bünemann

The following code returns the C API version:

require 'rbconfig'
puts RbConfig::CONFIG["ruby_version"]

On jruby this returns "1.9" while RUBY_VERSION returns "2.0.0", which might be because C-Extension support was removed in newer versions.

Postmodern
Owner

Unfortunately using the API version would mean no gem separation between different versions. Also keep in mind that rubygems now generates an absolute #!/path/to/ruby for gem executables.

Felix Bünemann

Isn't no gem separation between minor versions the point of the discussion?
If upgrading rubies gem pristine --all --only-executables should do the trick. If sharing between rubyies with same API version is desired, then I guess --env-shebang in gemrc would do the trick.

Postmodern
Owner

Ah it appears ruby-core is bumping the API version on each minor release, instead of having one API version for all 2.x.y releases. This might work after all.

I still wish there was an ENV variable we could set to enable --env-shebang by default.

Felix Bünemann

Yes, that would be nice. For now I'm using this patch with 0.3.8:

--- chruby/0.3.8/share/chruby/chruby.sh 2014-05-15 00:48:18.000000000 +0200
+++ chruby/0.3.8/share/chruby/chruby.sh 2014-05-15 00:39:08.000000000 +0200
@@ -44,14 +44,16 @@

    eval "$("$RUBY_ROOT/bin/ruby" - <<EOF
 begin; require 'rubygems'; rescue LoadError; end
+begin; require 'rbconfig'; rescue LoadError; end
 puts "export RUBY_ENGINE=#{defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'};"
 puts "export RUBY_VERSION=#{RUBY_VERSION};"
+puts "export RUBY_API_VERSION=#{RbConfig::CONFIG["ruby_version"] rescue RUBY_VERSION}"
 puts "export GEM_ROOT=#{Gem.default_dir.inspect};" if defined?(Gem)
 EOF
 )"

    if (( $UID != 0 )); then
-       export GEM_HOME="$HOME/.gem/$RUBY_ENGINE/$RUBY_VERSION"
+       export GEM_HOME="$HOME/.gem/$RUBY_ENGINE/$RUBY_API_VERSION"
        export GEM_PATH="$GEM_HOME${GEM_ROOT:+:$GEM_ROOT}${GEM_PATH:+:$GEM_PATH}"
        export PATH="$GEM_HOME/bin${GEM_ROOT:+:$GEM_ROOT/bin}:$PATH"
    fi
Postmodern
Owner

Actually you could use Gem.ruby_api_version.

Postmodern
Owner

Actually Gem.ruby_api_version was added sometime in 2.x.

Postmodern
Owner

One problem with using API version is lack of separation between 1.9.1, 1.9.2, 1.9.3.

Postmodern
Owner

We could wait until 1.9.x is EoLed and then switch to using the API version.

Felix Bünemann

Ah, that's nicer.

I grepped through the rubygems source, and there seems to be an environment variable GEMRC that can point to additional gem config files, so it would be possible to inject an environment specific gemrc.

Apparently you can do something like:
GEMRC=/path/to/nother/gemrc:$TMP/extra/gemrc

It should be possible to share gems between 1.9.1, 1.9.2 and 1.9.3 as they all have the same C api version.

Felix Bünemann

If you upgrade a debian system that's running eg. ruby-1.9.2-p0 as the system ruby to a newer release that runs eg. ruby-1.9.3-p194 you can continue using all your gems, cause the gems are binary compatible. So I don't understand why the GEMs should be separated if they are compatible.

Postmodern
Owner

@felixbuenemann gems should be seperated by "version". Allowing gems from 1.9.x versions to mix would add an edge-case to how chruby separates gems. Also, rubygems 1.8.23 will generate absolute #! even if --env-shebang is included in ~/.gemrc.

Felix Bünemann

Hmm, maybe it would make sense to just detect the rubygems version and fallback to the full version scheme if it's too old?

Postmodern
Owner

If we could somehow query the ruby version (sans the patch-level number) for both 1.9.x and 2.x, that would also work.

  • Given 1.9.3, gemdir version should be 1.9.3.
  • Given 2.1.0, gemdir version should be 2.1.
Felix Bünemann

I'm probably misunderstanding you, but that's already returned by RUBY_VERSION, the patchlevel is stored in RUBY_PATCHLEVEL.

Postmodern
Owner

Er, by patch-level I mean RUBY_PATCHLEVEL wrt 1.9.x and the last number in RUBY_VERSION in 2.x.

Felix Bünemann

Ah, ok, so you mean the major releases like 1.9.2, 1.9.3, 2.0, 2.1, 2.2.

Felix Bünemann

Would this be sufficient?

RUBY_ENGINE == "ruby" && RUBY_VERSION.sub(/([2-9]\.[0-9])\.[0-9]/, '\1')

This could also be done in the shell (no bash expert here ;-):

$([[ $RUBY_ENGINE = ruby  &&  ${RUBY_VERSION%%.*} -ge 2 ]] && echo ${RUBY_VERSION%.*} || echo $RUBY_VERSION)
Jonne Haß

+1 to use the ABI version, which also would play nicely with rubygems --user-install install option.

Matt Palmer mpalmer referenced this issue from a commit in mpalmer/puppet-module-chruby
Matt Palmer mpalmer Set GEM_HOME to use RUBY_API_VERSION
See postmodern#235 (comment) for
the source of the patch.
95f22fe
Semyon Perepelitsa

What's the problem with using RbConfig::CONFIG["ruby_version"]? Ruby 1.9.2 maintenance has ended this year. Ruby 1.9.3 versions can share gems between patch-level releases. Ruby 2.0.x and 2.1.x will use 2.0.0 and 2.1.0 directories respectively.

Postmodern
Owner

Until rubygems defaults to using #!/usr/bin/env ruby instead of an explicit #!/path/to/ruby, it's looking like separate gem directories for each ruby is the only way.

Felix Bünemann

Maybe it could be a configuration option for chruby? I have --env-shebang in my gemrc, so it's a small issue, but I understand that it makes it hard to make it the default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.