From 7d42b442bb42d8958daf978a0fe14b948f49609f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Mon, 21 Oct 2019 19:26:51 +0200 Subject: [PATCH] [rubygems/rubygems] Support binstubs with `--enable-load-relative` prolog https://github.com/rubygems/rubygems/commit/32a5e9057a --- lib/rubygems/installer.rb | 48 +++++- test/rubygems/test_gem_installer.rb | 226 +++++++++++++++++++++------- 2 files changed, 208 insertions(+), 66 deletions(-) diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb index 0224b91898..4cda09f200 100644 --- a/lib/rubygems/installer.rb +++ b/lib/rubygems/installer.rb @@ -220,7 +220,17 @@ class Gem::Installer existing = nil File.open generated_bin, 'rb' do |io| - next unless io.gets =~ /^#!/ # shebang + line = io.gets + shebang = /^#!.*ruby/ + + if load_relative_enabled? + until line.nil? || line =~ shebang do + line = io.gets + end + end + + next unless line =~ shebang + io.gets # blankline # TODO detect a specially formatted comment instead of trying @@ -585,7 +595,6 @@ class Gem::Installer # def shebang(bin_file_name) - ruby_name = ruby_install_name if @env_shebang path = File.join gem_dir, spec.bindir, bin_file_name first_line = File.open(path, "rb") {|file| file.gets } || "" @@ -614,14 +623,12 @@ class Gem::Installer end "#!#{which}" - elsif not ruby_name - "#!#{Gem.ruby}#{opts}" - elsif opts - "#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}" - else + elsif @env_shebang # Create a plain shebang line. @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path } - "#!#{@env_path} #{ruby_name}" + "#!#{@env_path} #{ruby_install_name}" + else + "#{bash_prolog_script}#!#{Gem.ruby}#{opts}" end end @@ -980,4 +987,29 @@ TEXT def ruby_install_name rb_config["ruby_install_name"] end + + def load_relative_enabled? + rb_config["LIBRUBY_RELATIVE"] == 'yes' + end + + def bash_prolog_script + if load_relative_enabled? + script = +<<~EOS + bindir="${0%/*}" + EOS + + script << %Q(exec "$bindir/#{ruby_install_name}" "-x" "$0" "$@"\n) + + <<~EOS + #!/bin/sh + # -*- ruby -*- + _=_\\ + =begin + #{script.chomp} + =end + EOS + else + "" + end + end end diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb index a40b874107..c90fdab283 100644 --- a/test/rubygems/test_gem_installer.rb +++ b/test/rubygems/test_gem_installer.rb @@ -18,11 +18,12 @@ class TestGemInstaller < Gem::InstallerTestCase end def test_app_script_text - installer = setup_base_installer + load_relative "no" do + installer = setup_base_installer - util_make_exec @spec, '' + util_make_exec @spec, '' - expected = <<-EOF + expected = <<-EOF #!#{Gem.ruby} # # This file was generated by RubyGems. @@ -52,10 +53,11 @@ else gem "a", version load Gem.bin_path("a", "executable", version) end - EOF + EOF - wrapper = installer.app_script_text 'executable' - assert_equal expected, wrapper + wrapper = installer.app_script_text 'executable' + assert_equal expected, wrapper + end end def test_check_executable_overwrite @@ -724,17 +726,19 @@ gem 'other', version def test_generate_bin_uses_default_shebang pend "Symlinks not supported or not enabled" unless symlink_supported? - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - installer.wrappers = true - util_make_exec + installer.wrappers = true + util_make_exec - installer.generate_bin + installer.generate_bin - default_shebang = Gem.ruby - shebang_line = File.open("#{@gemhome}/bin/executable") {|f| f.readlines.first } - assert_match(/\A#!/, shebang_line) - assert_match(/#{default_shebang}/, shebang_line) + default_shebang = Gem.ruby + shebang_line = File.open("#{@gemhome}/bin/executable") {|f| f.readlines.first } + assert_match(/\A#!/, shebang_line) + assert_match(/#{default_shebang}/, shebang_line) + end end def test_generate_bin_with_dangling_symlink @@ -1804,13 +1808,15 @@ gem 'other', version end def test_shebang - installer = setup_base_installer + load_relative "no" do + installer = setup_base_installer - util_make_exec @spec, "#!/usr/bin/ruby" + util_make_exec @spec, "#!/usr/bin/ruby" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby}", shebang + assert_equal "#!#{Gem.ruby}", shebang + end end def test_process_options @@ -1844,42 +1850,80 @@ gem 'other', version end def test_shebang_arguments - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/usr/bin/ruby -ws" + util_make_exec @spec, "#!/usr/bin/ruby -ws" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby} -ws", shebang + assert_equal "#!#{Gem.ruby} -ws", shebang + end + end + + def test_shebang_arguments_with_load_relative + load_relative 'yes' do + installer = setup_base_installer + + util_make_exec @spec, "#!/usr/bin/ruby -ws" + + shebang = installer.shebang 'executable' + + shebang_lines = shebang.split "\n" + + assert_equal "#!/bin/sh", shebang_lines.shift + assert_includes shebang_lines, "#!#{Gem.ruby} -ws" + end end def test_shebang_empty - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, '' + util_make_exec @spec, '' - shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby}", shebang + shebang = installer.shebang 'executable' + assert_equal "#!#{Gem.ruby}", shebang + end end def test_shebang_env - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/usr/bin/env ruby" + util_make_exec @spec, "#!/usr/bin/env ruby" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby}", shebang + assert_equal "#!#{Gem.ruby}", shebang + end end def test_shebang_env_arguments - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/usr/bin/env ruby -ws" + util_make_exec @spec, "#!/usr/bin/env ruby -ws" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby} -ws", shebang + assert_equal "#!#{Gem.ruby} -ws", shebang + end + end + + def test_shebang_env_arguments_with_load_relative + load_relative 'yes' do + installer = setup_base_installer + + util_make_exec @spec, "#!/usr/bin/env ruby -ws" + + shebang = installer.shebang 'executable' + + shebang_lines = shebang.split "\n" + + assert_equal "#!/bin/sh", shebang_lines.shift + assert_includes shebang_lines, "#!#{Gem.ruby} -ws" + end end def test_shebang_env_shebang @@ -1897,63 +1941,120 @@ gem 'other', version end def test_shebang_nested - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/opt/local/ruby/bin/ruby" + util_make_exec @spec, "#!/opt/local/ruby/bin/ruby" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby}", shebang + assert_equal "#!#{Gem.ruby}", shebang + end end def test_shebang_nested_arguments - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/opt/local/ruby/bin/ruby -ws" + util_make_exec @spec, "#!/opt/local/ruby/bin/ruby -ws" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby} -ws", shebang + assert_equal "#!#{Gem.ruby} -ws", shebang + end + end + + def test_shebang_nested_arguments_with_load_relative + load_relative 'yes' do + installer = setup_base_installer + + util_make_exec @spec, "#!/opt/local/ruby/bin/ruby -ws" + + shebang = installer.shebang 'executable' + + shebang_lines = shebang.split "\n" + + assert_equal "#!/bin/sh", shebang_lines.shift + assert_includes shebang_lines, "#!#{Gem.ruby} -ws" + end end def test_shebang_version - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/usr/bin/ruby18" + util_make_exec @spec, "#!/usr/bin/ruby18" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby}", shebang + assert_equal "#!#{Gem.ruby}", shebang + end end def test_shebang_version_arguments - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/usr/bin/ruby18 -ws" + util_make_exec @spec, "#!/usr/bin/ruby18 -ws" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby} -ws", shebang + assert_equal "#!#{Gem.ruby} -ws", shebang + end + end + + def test_shebang_version_arguments_with_load_relative + load_relative 'yes' do + installer = setup_base_installer + + util_make_exec @spec, "#!/usr/bin/ruby18 -ws" + + shebang = installer.shebang 'executable' + + shebang_lines = shebang.split "\n" + + assert_equal "#!/bin/sh", shebang_lines.shift + assert_includes shebang_lines, "#!#{Gem.ruby} -ws" + end end def test_shebang_version_env - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/usr/bin/env ruby18" + util_make_exec @spec, "#!/usr/bin/env ruby18" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby}", shebang + assert_equal "#!#{Gem.ruby}", shebang + end end def test_shebang_version_env_arguments - installer = setup_base_installer + load_relative 'no' do + installer = setup_base_installer - util_make_exec @spec, "#!/usr/bin/env ruby18 -ws" + util_make_exec @spec, "#!/usr/bin/env ruby18 -ws" - shebang = installer.shebang 'executable' + shebang = installer.shebang 'executable' - assert_equal "#!#{Gem.ruby} -ws", shebang + assert_equal "#!#{Gem.ruby} -ws", shebang + end + end + + def test_shebang_version_env_arguments_with_load_relative + load_relative 'yes' do + installer = setup_base_installer + + util_make_exec @spec, "#!/usr/bin/env ruby18 -ws" + + shebang = installer.shebang 'executable' + + shebang_lines = shebang.split "\n" + + assert_equal "#!/bin/sh", shebang_lines.shift + assert_includes shebang_lines, "#!#{Gem.ruby} -ws" + end end def test_shebang_custom @@ -2283,4 +2384,13 @@ gem 'other', version def mask 0100755 end + + def load_relative(value) + orig_LIBRUBY_RELATIVE = RbConfig::CONFIG['LIBRUBY_RELATIVE'] + RbConfig::CONFIG['LIBRUBY_RELATIVE'] = value + + yield + ensure + RbConfig::CONFIG['LIBRUBY_RELATIVE'] = orig_LIBRUBY_RELATIVE + end end