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

require_relative through symlinks behaves differently in jruby than in MRI ruby #3092

Closed
storrence opened this issue Jun 29, 2015 · 5 comments

Comments

@storrence
Copy link

If you require a source file that contains require_relative through a symlink in a different diretory, MRI ruby interprets the require_relative relative to the directory containing the link target but jruby 9.0.0.0.rc1 interprets it relative to the directory containing the symlink itself.

The real application code that brought this to my attention is simulated by the following source files and symlink:

included_by_target.rb
link_target.rb
main.rb
subdir/link.rb -> ../link_target.rb
# link_target.rb
require_relative './included_by_target'
class LinkTarget
  include IncludedByTarget
end
# included_by_target.rb
module IncludedByTarget
  def do_something
    puts "  #{self.class.name}#do_something"
  end
end
# main.rb
require 'subdir/link'
LinkTarget.new.do_something

This code works in MRI ruby:

$ruby --version
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]

$ruby -I . main.rb
  LinkTarget#do_something

but fails under jruby 9.0.0.0.rc1. Note that the interpreter is looking for the included file in the directory containing the symlink rather than in the directory containing the linked file:

$ruby --version
jruby 9.0.0.0.rc1 (2.2.2) 2015-06-10 a0bf3b3 OpenJDK 64-Bit Server VM 25.45-b02 on 1.8.0_45-b14 +jit [linux-amd64]

$ruby -I . main.rb
LoadError: no such file to load -- /home/storrence/temp/ruby/jruby_require_relative/subdir/included_by_target
           require at org/jruby/RubyKernel.java:940
           require at /home/storrence/.rvm/rubies/jruby-9.0.0.0.rc1/lib/ruby/stdlib/rubygems/core_ext/kernel_require.rb:54
  require_relative at uri:classloader:/jruby/kernel/kernel.rb:24
             <top> at /home/storrence/temp/ruby/jruby_require_relative/subdir/link.rb:1
           require at org/jruby/RubyKernel.java:940
            (root) at /home/storrence/.rvm/rubies/jruby-9.0.0.0.rc1/lib/ruby/stdlib/rubygems/core_ext/kernel_require.rb:1
             <top> at main.rb:1
@eregon
Copy link
Member

eregon commented Jun 30, 2015

i.e., in my understanding since require_relative was introduced, MRI always uses the realpath/canonicalized path for storing in $LOADED_FEATURES and comparisons.

@storrence storrence changed the title require_relative through symlinks behaves differently in jruby 9.0.0.0.rc1 than in MRI ruby 2.2.2 require_relative through symlinks behaves differently in jruby than in MRI ruby Jun 30, 2015
@storrence
Copy link
Author

This difference in behavior between jruby and the MRI is not new. The test code above behaves identically in MRI ruby 1.9.3 (p547) and 2.2.2. Similarly the test code behaves identically in jruby 1.7.4, 1.7.20.1 and 9.0.0.0.rc1.

I modified the link_target.rb as follows to include some debugging output. The line labelled 'absolute_feature' is copied from the jruby implementation of require_relative (9.0.0.0.rc1 core/src/main/ruby/jruby/kernel/kernel.rb line 21) with the arguments resolved to the equivalent local values.

# link_target.rb

$stderr.puts ""
$stderr.puts "__FILE__                     = " + __FILE__
$stderr.puts "File.absolute_path(__FILE__) = " + File.absolute_path(__FILE__)
$stderr.puts "File.realdirpath(__FILE__)   = " + File.realdirpath(__FILE__)
$stderr.puts "File.realpath(__FILE__)      = " + File.realpath(__FILE__)
$stderr.puts ""
$stderr.puts "absolute_feature = " + File.expand_path('./included_by_target', File.dirname(__FILE__))
$stderr.puts "mimic_mri        = " + File.expand_path('./included_by_target', File.dirname(File.realpath(__FILE__)))
$stderr.puts ""

require_relative './included_by_target'
class LinkTarget
  include IncludedByTarget
end

which produce the following output from MRI ruby:

$ruby -I . main.rb 

__FILE__                     = /home/storrence/temp/ruby/jruby_require_relative/subdir/link.rb
File.absolute_path(__FILE__) = /home/storrence/temp/ruby/jruby_require_relative/subdir/link.rb
File.realdirpath(__FILE__)   = /home/storrence/temp/ruby/jruby_require_relative/link_target.rb
File.realpath(__FILE__)      = /home/storrence/temp/ruby/jruby_require_relative/link_target.rb

absolute_feature = /home/storrence/temp/ruby/jruby_require_relative/subdir/included_by_target
mimic_mri        = /home/storrence/temp/ruby/jruby_require_relative/included_by_target

  LinkTarget#do_something

and this output from jruby:

$ruby -I . main.rb

__FILE__                     = /home/storrence/temp/ruby/jruby_require_relative/subdir/link.rb
File.absolute_path(__FILE__) = /home/storrence/temp/ruby/jruby_require_relative/subdir/link.rb
File.realdirpath(__FILE__)   = /home/storrence/temp/ruby/jruby_require_relative/subdir/link.rb
File.realpath(__FILE__)      = /home/storrence/temp/ruby/jruby_require_relative/link_target.rb

absolute_feature = /home/storrence/temp/ruby/jruby_require_relative/subdir/included_by_target
mimic_mri        = /home/storrence/temp/ruby/jruby_require_relative/included_by_target

LoadError: no such file to load -- /home/storrence/temp/ruby/jruby_require_relative/subdir/included_by_target
           require at org/jruby/RubyKernel.java:940
           require at /home/storrence/.rvm/rubies/jruby-9.0.0.0.rc1/lib/ruby/stdlib/rubygems/core_ext/kernel_require.rb:54
  require_relative at uri:classloader:/jruby/kernel/kernel.rb:24
             <top> at /home/storrence/temp/ruby/jruby_require_relative/subdir/link.rb:11
           require at org/jruby/RubyKernel.java:940
            (root) at /home/storrence/.rvm/rubies/jruby-9.0.0.0.rc1/lib/ruby/stdlib/rubygems/core_ext/kernel_require.rb:1
             <top> at main.rb:1

The difference in require_relative behavior between MRI and jruby appears to be that the MRI resolves the relative-path argument to its realpath (analogous to the line labeled 'mimic_mri') and jruby doesn't.

@headius
Copy link
Member

headius commented Jul 1, 2015

So is this as simple as just throwing File.realpath into our require_relative impl?

@headius
Copy link
Member

headius commented Jul 2, 2015

I have a fix locally and it appears to pass specs and tests.

@eregon
Copy link
Member

eregon commented Jul 2, 2015

(my mistake, it seems normal require does not canonicalize the path)

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

4 participants