Don't assume ruby.exe is in gem bindir on Windows #942

Merged
merged 1 commit into from Sep 21, 2014

7 participants

@monban

Normally gem "binaries" install to the same place as ruby.exe. Under normal conditions, this is a safe assumption, and causes no problems. However when using bundler with the --deployment switch your gem binaries end up in project/vendor/bundle/ruby/VERSION/bin and your exe is somewhere else. Because of the way the Windows wrapper .bat files are written bundle exec tries to execute ruby.exe from your vendor/bundle directory and can't.
I am a little torn on whether this is a Rubygems problem or a Bundler problem. If I owned both systems I'd rather fix the wrapper batch files, which are build by Rubygems. So that's the solution I'm proposing.
All I can report is that this "works for me", I have not tested extensively (on different Windows versions, with PIK or other Ruby switchers, etc).
Final note, I'm unsure of under what circumstances quotes would show up in your Gem.ruby variable, so I am not sure why we are chomping them off on line 659. Because of that, I'm not sure if I re-implemented it correctly (calling it directly on Gem.ruby instead of the result of running File.basename on samesuch).

@drbrain
RubyGems member

Does gem install -i other_dir or gem install -b custom_bin exhibit the same behavior? If so, are they also fixed by this patch?

@drbrain
RubyGems member

@luislavena can you review this?

@drbrain drbrain added this to the 2.4 milestone Jun 13, 2014
@monban

gem install -i didn't seem to be affected by this, I didn't have time to test gem install -b, I'll do that on Monday (I don't have regular access to a Windows computer over the weekend).
I am however, starting to wonder if my solution is the best idea. For one thing it basically breaks the portability of the Ruby installation. Ideally I'd like to detect at runtime the proper Ruby to use. For our deployment I think I'll actually be configuring the wrappers to not specify a directory for Ruby at all, and just use whatever ruby.exe is on the path.
Ruby is really good about being dropped anywhere you like and ran, but this PR means the path to ruby.exe is hardcoded in the bat wrappers. Moving your Ruby directory requires you to run gem pristine --only-executables to regenerate them. That would be considered breaking behaviour for people who are accustomed to copying an entire Ruby installation from one computer or directory to another.

I think the real design decision here needs to be made on a higher level: the wrappers currently assume that Ruby and the executables from gems are always located in the same directory. bundle --deployment breaks that assumption. Which is the correct, and therefore desired behaviour?

@monban

Thought: maybe a gem option (switch / .gemrc) to specify whether to hardcode ruby location or just use the path? Default behaviour could be something like
1) If there is a ruby.exe on the path, don't include a path
2) If there isn't, ask the user if they want to add it or hardcode it

@drbrain
RubyGems member

I mean, I think gem install -b and gem install -i were broken on windows without your patch.

Without the --env-shebang option, on non-windows the wrappers hard-code the installing ruby.

My desired behaviors are:

  • The directory gems are installed in should be relocatable (this is non-windows behavior). I think this means hard-coding the full path in the executable by default so this works even if the executable and ruby.exe aren't in the same directory.
  • With gem install --env-shebang the executable will use whichever ruby.exe is in the $PATH. This could be given an alias with a more-windows-friendly name.

This would make windows and non-windows behavior identical.

@luislavena
RubyGems member

Hello,

The change to use Ruby's bindir was introduced in commit cd26858 (#818)

Before that, it took the ruby available in the PATH.

I didn't look deep into the reasoning for this change, but definitely more attention to the installation outside Ruby's bin directory should have been analyzed.

Perhaps @unak could explain the reason to limit batch files use Ruby's bin directory and not PATH available ruby.exe

I like @drbrain suggestion on making the behavior consistent. I use --env-shebang to avoid having the hardcoded path in the extensionless script so I can use bash.

In relation to quotes, the reason to add them around ruby.exe is to be able to work even when Ruby has been installed inside a directory with spaces (not recommended but still lots of folks do).

Also the first part of the binstub script is useless as it only runs on Windows 9x, so the full path is not used.

On my personal installation I no longer worry about batch files since I installed gem-exefy

Problem is Bundler did not enable RubyGems plugins the so it doesn't generate the .exe stub instead of the .bat one (at least, it used to do that)

@monban

Just to clarify on the quotes: I understand putting the path to the ruby.exe in quotes, as well as the script to be executed, but on line 659 .chomp('"') is called to remove quotes from the end of the filename... I'm just unsure why it is required since I can't imagine any circumstance in which Gem.ruby would return a string ending in ".

Also why is the script executed with @ prepended (lines 663 and 666)? That turns off echoing (so the actual command doesn't get shown) but that's already taken care of with @echo off on the first line. Just makes it harder to debug and has no advantage, imho.

I am currently installing Windows 8.1 on a vm (anyone know of any good vm hosting services that will let you run Windows for development purposes?) so hopefully I can try some experiments over the weekend.

@luislavena
RubyGems member
@luislavena
RubyGems member

/cc @unak @nobu can you guys comment on the reason for above change?

The change introduced in #818 limit the .bat files to be only along the Ruby's bin directory, which differs from the original behavior (using PATH-available ruby)

Thank you.

@monban

Apologies for taking so long to get back to this, I can finally confirm that currently gem install -i %dir% does generate a .bat hardcoded to a non-extant ruby.exe. As does -n, (you said -b, but I assume you mean -n).

Now, without this pull request, the .bat files use %~dp0 for the location of ruby.exe, which is good (assuming you aren't doing -i, -n, or bundle install --deployment) because it allows you to move ruby around on your system, or even between systems, and you can just run the bat files and everything is nicely portable and it all works. This pull request breaks the portability of ruby in that respect.

EDIT: FYI if you use this branch, or if it gets merged (though I think I no longer recommend it), you can use gem pristine --all --only-executables to generate your .bat files after moving Ruby

@drbrain drbrain modified the milestone: 2.5, 2.4 Jul 17, 2014
@unak

Sorry for late reply.

  1. My patch is wrong, because I simply missed the case that installing gem's bin into the diffrent directory from ruby's bindir.
  2. The reason why I changed so is: There are over 80 ruby interpreters in my PC (because I'm a developer of ruby itself, you know). I usually set PATH to only one ruby(trunk HEAD)'s bindir. When I want to use another ruby interpreter, I simply run it with full path name. Of cource I run gem command and other ruby bundles commands with the same way. So, if I want to install a new gem for a ruby interpreter, I type "C:\lan\ruby-2.0.0\bin\gem install foo", and, perhaps "C:\lan\ruby-2.0.0\bin\foo.bat" will be generated. But, before my patch, when I run "C:\lan\ruby-2.0.0\bin\foo.bat", the batch file call the ruby.exe in PATH, not "C:\lan\ruby-2.0.0\bin\ruby.exe". Oops.
  3. Conclusion: monban's patch seems OK for me.

Sorry for the inconvenience.

@unak unak merged commit bb44422 into rubygems:master Sep 21, 2014

1 check passed

Details continuous-integration/travis-ci The Travis CI build passed
@drbrain drbrain added a commit that referenced this pull request Sep 23, 2014
@drbrain drbrain Add #942 to History 59318f0
@drbrain drbrain added a commit that referenced this pull request Sep 23, 2014
@drbrain drbrain Attribution for #942 should be for #1000 3cdf15b
@drbrain drbrain added a commit that referenced this pull request Sep 23, 2014
@drbrain drbrain Add #942 to History (correctly, this time) d8f9b04
@slackfan

Does this mean, that
gem pristine --all --only-executables
is the only way at the moment to use the Ruby interpreter in PATH and there is no more a way to create the batch files the old (more flexible) way?

@slackfan

For cross readers. I was required to workaround the behavior change using a custom action (Wrote a c# DLL) in the MSI which triggers the mentioned command. Far away from perfect and high effort but working.

@bwl21

I am afraid that
gem pristine --all --only-executables
does not really help, since it does no longer work after moving ruby on my system.
At least gem.bat needs to be fixed.

@slackfan slackfan added a commit to slackfan/rubygems that referenced this pull request Dec 5, 2015
@slackfan slackfan Assume ruby.exe to be available as defined by the environment
The behavior introduced with pull request #942 when creating the windows
batch file of a ruby gem has the downside that it is no longer easy
possible to use a different (J)Ruby installation but the same %GEM_HOME%
directory.  This is for example needed when distributing all gems
available in a %GEM_HOME% directory accross various installations which
may have different Ruby installations.

The proposed changes tries to fix the mentioned flaws by utilizing set
environment variables. It keeps the existing behavior if no environment
variable is set but uses the environment variables if set.
8af8041
@krainboltgreene krainboltgreene added bug fix and removed bug report labels Jan 1, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment