Impossible uninitialized constant (regression in Rubygems 2.2) #784

Closed
indirect opened this Issue Jan 11, 2014 · 19 comments

Projects

None yet

6 participants

@indirect
RubyGems member

I have no idea how this is even possible, but every single Bundler build on Ruby 1.8.7 is failing right now because of Rubygems 2.2.0 raising uninitialized constant Gem::UserInteraction (NameError) when I try to run ronn after installing the gem.

I can't reproduce on my machine locally, and the backtrace points to lib/rubygems/ext/builder.rb:12, which is several lines after require 'rubygems/user_interaction'. I believe this is the same error causing Rubygems builds on Travis to fail against 1.8.7 as well.

Any ideas? Full backtrace here.

@indirect
RubyGems member

For the record, the relevant Rubygems code is right here:

https://github.com/rubygems/rubygems/blob/master/lib/rubygems/ext/builder.rb#L7-L12

@indirect
RubyGems member

Even more interesting, trying to fix this problem has resulted in an impossible-seeming backtrace that isn't part of Rubygems: somehow running the ronn gem executable isn't requiring a Ronn namespaced constant. But only on 1.8.7. From Travis:

/home/travis/.rvm/rubies/ruby-1.8.7-p374/bin/ruby -S ronn --roff --pipe man/bundle-update.ronn > lib/bundler/man/bundle-update
/home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/lib/ronn/document.rb:191:in `sniff': uninitialized constant Ronn::Document::Markdown (NameError)
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/lib/ronn/document.rb:76:in `initialize'
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/bin/ronn:166:in `new'
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/bin/ronn:166
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/bin/ronn:166:in `map'
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/bin/ronn:166
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/bin/ronn:23:in `load'
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/bin/ronn:23

This is on Rubygems 2.2.0, but also seems to happen when RUBYOPT is set to load any Rubygems version. And only happens 1.8.7 on Travis. :/

@indirect
RubyGems member

Okay, this is for sure a regression in Rubygems 2.2.0:

$ gem -v
2.2.1

$ ronn man/bundle-config.ronn 
/home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/lib/ronn/document.rb:191:in `sniff': uninitialized constant Ronn::Document::Markdown (NameError)
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/lib/ronn/document.rb:76:in `initialize'
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/bin/ronn:166:in `new'
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/bin/ronn:166
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/bin/ronn:166:in `map'
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/gems/ronn-0.7.3/bin/ronn:166
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/bin/ronn:23:in `load'
    from /home/travis/.rvm/gems/ruby-1.8.7-p374/bin/ronn:23

$ gem update --system 2.1.11
Updating rubygems-update
Successfully installed rubygems-update-2.1.11
Installing RubyGems 2.1.11
RubyGems 2.1.11 installed



------------------------------------------------------------------------------

RubyGems installed the following executables:
    /home/travis/.rvm/rubies/ruby-1.8.7-p374/bin/gem

RubyGems system software updated

$ ronn man/bundle-config.ronn 
     roff: man/bundle-config.1                        
     html: man/bundle-config.1.html                              +man
@indirect
RubyGems member

Now that I have a minimal repro case of ronn file.ronn, I was able to git bisect it:

d447a19dbf4add41249c99570c6473bd8f89ff7a is the first bad commit
commit d447a19dbf4add41249c99570c6473bd8f89ff7a
Author: Eric Hodel <drbrain@segment7.net>
Date:   Fri Oct 4 15:58:54 2013 -0700

    Move extension building to file existence check

    Previously extension building occurred on activate, but this was wrong.
    If a gem like nokogiri was installed for a different platform it could
    never activate because the extension would always be missing during the
    file existence check (contains_requirable_file?).

    Now, extension building happens during the existence check.

    This moved gem_dir up to BasicSpecification and causes the stub
    specification to load the real specification to build the extension.

The issue appears to be that extensions are no longer loaded on 1.8.7 with Rubygems 2.2.x, starting with that commit.

@drbrain
RubyGems member

This might be enough to go on for a fix, I'll check it out on Monday if you get no further.

@indirect
RubyGems member

After some more investigation, my leading theory as to why this is triggered on Travis and not on my laptop is that it is related to Gem::Ext::ExtConfBuilder, which (afaict?) is not used on OS X.

@indirect indirect added a commit to bundler/bundler that referenced this issue Jan 12, 2014
@indirect indirect downgrade rubygems on 1.8
works around the regression in loading extensions on 1.8 introduced by
Rubygems 2.2. See also rubygems/rubygems#784
0671915
@drbrain
RubyGems member

Gem::Ext::ExtConfBuilder should be picked up here:

https://github.com/rubygems/rubygems/blob/master/lib/rubygems/ext/builder.rb#L111-L112

Ugh, this code makes me want to cry every time I look at it 😢

@indirect
RubyGems member
@drbrain
RubyGems member

ext_conf_builder.rb is required by rubygems/ext.rb:

https://github.com/rubygems/rubygems/blob/master/lib/rubygems/ext.rb

@kylef kylef referenced this issue in CocoaPods/CocoaPods Mar 19, 2014
Closed

uninitialized constant Gem::UserInteraction #1878

@rahulbelekar

I downgraded the gem version to 2.1.11, but while pod install it gives the same error and the gem version mentioned is 2.2.2.

And I'm still facing the error

@indirect
RubyGems member

This is still a regression in 2.2, and is actively breaking the Bundler test suite on 1.8.7 REE: https://travis-ci.org/bundler/bundler/jobs/24168760

@zzak zzak added the type - bug label May 22, 2014
@drbrain drbrain modified the milestone: Future, 2.3 May 28, 2014
@drbrain
RubyGems member

Since I can't reproduce this I'm moving it to a future milestone

@strzibny strzibny referenced this issue in bundler/bundler Dec 17, 2014
Closed

Fix rubygems 2.2 compatibility #3237

@voxik

This has quite easy explanation and it is due to rdiscount and its file layout

# ls /usr/lib64/ruby/gems/1.8/gems/rdiscount-1.6.8/lib -l
celkem 200
-rw-r--r--. 1 root root     20 23. pro 15.33 markdown.rb
-rw-r--r--. 1 root root   3240 23. pro 15.50 rdiscount.rb
-rwxr-xr-x. 1 root root 196477 23. pro 15.33 rdiscount.so

On this listing, you can see that there is rdiscount.so side by side with rdiscount.rb. In this case, Ruby's require prefers the .rb file and loads it first. However, since RubyGems 2.2.x, the load path looks like this:

/usr/lib64/ruby/gems/1.8/extensions/x86_64-linux/1.8/rdiscount-1.6.8
/usr/lib64/ruby/gems/1.8/gems/rdiscount-1.6.8/lib

and this is the extensions directory listing:

# ls -l /usr/lib64/ruby/gems/1.8/extensions/x86_64-linux/1.8/rdiscount-1.6.8
celkem 212
-rw-r--r--. 1 root root      0 23. pro 15.33 gem.build_complete
-rw-r--r--. 1 root root   8517 23. pro 15.33 gem_make.out
-rw-r--r--. 1 root root   5394 23. pro 15.33 mkmf.log
-rwxr-xr-x. 1 root root 196477 23. pro 15.33 rdiscount.so

Since the extensions are on the $LOAD_PATH first, the Ruby's require finds the rdiscount.so and never bothers to load the rdiscount.rb.

Hence this is your workaround:

mv /usr/lib64/ruby/gems/1.8/extensions/x86_64-linux/1.8/rdiscount-1.6.8/rdiscount.so{,.bak}

Not sure what is the proper solution though.

@voxik

This swaps the load path order and fixes the issue as well:

diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index f5fb0f5..52482be 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -145,7 +145,7 @@ class Gem::BasicSpecification
         File.join full_gem_path, path
       end

-      full_paths.unshift extension_dir unless @extensions.nil? || @extensions.empty?
+      full_paths << extension_dir unless @extensions.nil? || @extensions.empty?

       full_paths
     end

But then the .so file from /usr/lib64/ruby/gems/1.8/gems/rdiscount-1.6.8/lib is used if I am not mistaken.

@voxik

Another issue is this commit:

68da16d

If you will take a look on the tracebak, you'll se that during the load of "user_interaction.rb", RubyGems are trying to load "io/console" [1]. But in that moment, the Gem::UserInteraction is not know to Ruby yet, so indeed, the Builder cannot obviously find the constant. I tried to move the "require 'io/console'" to the end of the file, which resolved one particular issue, but soon I hit similar problem with Gem::Ext::ExtConfBuilder. The #924 might be cure for this.

To conclude, I am afraid that the 'io/console' should be lazily loaded, as it used to be.

[1] https://gist.github.com/indirect/90a19aa3eee72e412bfa#file-gistfile1-txt-L20

@indirect
RubyGems member

Any chance this will be fixed soon, now that it has a reproduction?

@steakknife

Confirmed that @voxik 's patch appears to resolve this issue on MRI 2.2.0 (e.g., encountered while running bundler's rake spec).

Before patch

$ rake spec
groff -Wall -mtty-char -mandoc -Tascii lib/bundler/man/bundle-exec | col -b > lib/bundler/man/bundle-exec.txt
/usr/local/ruby/ruby-2.2.0/bin/ruby -S ronn --roff --pipe man/bundle-install.ronn > lib/bundler/man/bundle-install
/Users/bmf/.gem/ruby/2.2.0/gems/ronn-0.7.3/lib/ronn/document.rb:191:in `sniff': uninitialized constant Ronn::Document::Markdown (NameError)
    from /Users/bmf/.gem/ruby/2.2.0/gems/ronn-0.7.3/lib/ronn/document.rb:76:in `initialize'
    from /Users/bmf/.gem/ruby/2.2.0/gems/ronn-0.7.3/bin/ronn:166:in `new'
    from /Users/bmf/.gem/ruby/2.2.0/gems/ronn-0.7.3/bin/ronn:166:in `block in <top (required)>'
    from /Users/bmf/.gem/ruby/2.2.0/gems/ronn-0.7.3/bin/ronn:166:in `map'
    from /Users/bmf/.gem/ruby/2.2.0/gems/ronn-0.7.3/bin/ronn:166:in `<top (required)>'
    from /Users/bmf/.gem/ruby/2.2.0/bin/ronn:23:in `load'
    from /Users/bmf/.gem/ruby/2.2.0/bin/ronn:23:in `<main>'
rake aborted!
Command failed with status (1): [/usr/local/ruby/ruby-2.2.0/bin/ruby -S ron...]
/Users/bmf/Projects/bundler/Rakefile:220:in `block (3 levels) in <top (required)>'
/Users/bmf/Projects/bundler/Rakefile:23:in `block in invoke'
/Users/bmf/Projects/bundler/Rakefile:22:in `invoke'
Tasks: TOP => spec => man:build => man:build_all_pages => lib/bundler/man/bundle-install.txt => lib/bundler/man/bundle-install
(See full trace by running task with --trace)

After patch

$ rake spec
groff -Wall -mtty-char -mandoc -Tascii lib/bundler/man/bundle-install | col -b > lib/bundler/man/bundle-install.txt
/usr/local/ruby/ruby-2.2.0/bin/ruby -S ronn --roff --pipe man/bundle-package.ronn > lib/bundler/man/bundle-package
groff -Wall -mtty-char -mandoc -Tascii lib/bundler/man/bundle-package | col -b > lib/bundler/man/bundle-package.txt
/usr/local/ruby/ruby-2.2.0/bin/ruby -S ronn --roff --pipe man/bundle-platform.ronn > lib/bundler/man/bundle-platform
groff -Wall -mtty-char -mandoc -Tascii lib/bundler/man/bundle-platform | col -b > lib/bundler/man/bundle-platform.txt
...
@voxik

@steakknife But that is just one part of the issue ... but the other part might be worth of moving into separate ticket anyway :)

@steakknife

@voxik True. Smaller fixes > epic refactoring, usually. The submitted PR unblocks bundler w/ latest MRI, which seems important to release sooner, unless there's impetus to deliver a meta, more stable solution. (I'm not familiar enough with either codebase to be useful for encompassing fix/es.) It would be nice to iron out the gem + bundler + 2.2.x speed-bumps.

The only point (possible edge-case), whether extension search order is important for other gems, e.g., have a separate method for path prepend (current) vs append (proposed fix)... because at scale, it could be an edge-case that matters (e.g., real-world apps, and apparently, ronn).

@drbrain drbrain pushed a commit that closed this issue Jan 15, 2015
@steakknife steakknife closes #784 5bb291f
@drbrain drbrain closed this in 5bb291f Jan 15, 2015
@miyagawa miyagawa referenced this issue in travis-ci/travis.rb Jan 20, 2015
Closed

Travis 1.7.5 and Ruby 2.2.0 doesn't run #261

@drbrain drbrain added a commit that referenced this issue Feb 15, 2015
@drbrain drbrain Add #784 to History 974e8c2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment