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

Make the setup script generated by bundle install --standalone multiplatform #6635

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Manifest.txt
Expand Up @@ -448,6 +448,7 @@ lib/rubygems/package/tar_writer.rb
lib/rubygems/package_task.rb
lib/rubygems/path_support.rb
lib/rubygems/platform.rb
lib/rubygems/platform/string_parser.rb
lib/rubygems/psych_tree.rb
lib/rubygems/query_utils.rb
lib/rubygems/rdoc.rb
Expand Down
37 changes: 36 additions & 1 deletion bundler/lib/bundler/installer/standalone.rb
Expand Up @@ -15,6 +15,7 @@ def generate
file.puts prevent_gem_activation
file.puts define_path_helpers
file.puts reverse_rubygems_kernel_mixin
file.puts platform_parser
paths.each do |path|
if Pathname.new(path).absolute?
file.puts %($:.unshift "#{path}")
Expand All @@ -33,7 +34,7 @@ def paths
Array(spec.require_paths).map do |path|
gem_path(path, spec).
sub(version_dir, '#{RUBY_ENGINE}/#{Gem.ruby_api_version}').
sub(extensions_dir, 'extensions/\k<platform>/#{Gem.extension_api_version}')
sub(extensions_dir, new_extensions_dir)
# This is a static string intentionally. It's interpolated at a later time.
end
end.flatten.compact
Expand Down Expand Up @@ -112,5 +113,39 @@ def reverse_rubygems_kernel_mixin
end
END
end

def platform_parser_path
"#{RbConfig::CONFIG["rubylibdir"]}/rubygems/platform/string_parser.rb"
end

def support_multiplatform_setup_script?
File.exist?(platform_parser_path)
end

def new_extensions_dir
if support_multiplatform_setup_script?
'extensions/#{local_platform}/#{Gem.extension_api_version}'
else
'extensions/\k<platform>/#{Gem.extension_api_version}'
end
end

def platform_parser
if support_multiplatform_setup_script?
parser_code = File.read(platform_parser_path).gsub("# frozen_string_literal: true\n\n", "")
<<~END
unless defined?(Gem::Platform::StringParser)
#{parser_code.chomp}
end
def local_platform
force_mswin_version = true
Gem::Platform::StringParser
.run(RbConfig::CONFIG["arch"], force_mswin_version)
.compact
.join "-"
end
END
end
end
end
end
59 changes: 50 additions & 9 deletions bundler/spec/install/gems/standalone_spec.rb
Expand Up @@ -253,15 +253,56 @@
G
end

it "generates a bundle/bundler/setup.rb with the proper paths" do
expected_path = bundled_app("bundle/bundler/setup.rb")
script_content = File.read(expected_path)
expect(script_content).to include("def self.ruby_api_version")
expect(script_content).to include("def self.extension_api_version")
extension_line = script_content.each_line.find {|line| line.include? "/extensions/" }.strip
platform = Gem::Platform.local
expect(extension_line).to start_with '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/'
expect(extension_line).to end_with platform.to_s + '/#{Gem.extension_api_version}/very_simple_binary-1.0")'
context "when multiplatform setup.rb isn't supported" do
before do
if File.exist?("#{RbConfig::CONFIG["rubylibdir"]}/rubygems/platform/string_parser.rb")
skip "multiplatform setup.rb is supported"
end
end

it "generates a bundle/bundler/setup.rb with the necessary path helpers" do
expected_path = bundled_app("bundle/bundler/setup.rb")
script_content = File.read(expected_path)

expect(script_content).to include("def self.ruby_api_version")
expect(script_content).to include("def self.extension_api_version")
expect(script_content).to_not include("def local_platform")
end

it "generates a bundle/bundler/setup.rb with the proper paths" do
expected_path = bundled_app("bundle/bundler/setup.rb")
script_content = File.read(expected_path)
extension_line = script_content.each_line.find {|line| line.include? "/extensions/" }.strip
platform = Gem::Platform.local

expect(extension_line).to start_with '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/'
expect(extension_line).to end_with platform.to_s + '/#{Gem.extension_api_version}/very_simple_binary-1.0")'
end
end

context "when multiplatform setup.rb is supported" do
before do
unless File.exist?("#{RbConfig::CONFIG["rubylibdir"]}/rubygems/platform/string_parser.rb")
skip "multiplatform setup.rb isn't supported"
end
end

it "generates a bundle/bundler/setup.rb with the local_platform helper" do
expected_path = bundled_app("bundle/bundler/setup.rb")
script_content = File.read(expected_path)
expect(script_content).to include("def self.ruby_api_version")
expect(script_content).to include("def self.extension_api_version")
expect(script_content).to include("def local_platform")
end

it "generates a bundle/bundler/setup.rb that is multiplatform" do
expected_path = bundled_app("bundle/bundler/setup.rb")
script_content = File.read(expected_path)
extension_line = script_content.each_line.find {|line| line.include? "/extensions/" }.strip

expect(extension_line).to start_with '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/'
expect(extension_line).to end_with '#{local_platform}/#{Gem.extension_api_version}/very_simple_binary-1.0")'
end
end
end

Expand Down
60 changes: 6 additions & 54 deletions lib/rubygems/platform.rb
Expand Up @@ -14,9 +14,8 @@ class Gem::Platform

def self.local
@local ||= begin
arch = RbConfig::CONFIG["arch"]
arch = "#{arch}_60" if /mswin(?:32|64)$/.match?(arch)
new(arch)
force_mswin_version = true
new(StringParser.run(RbConfig::CONFIG["arch"], force_mswin_version))
end
end

Expand Down Expand Up @@ -77,57 +76,8 @@ def initialize(arch)
when Array then
@cpu, @os, @version = arch
when String then
arch = arch.split "-"

if arch.length > 2 && arch.last !~ /\d+(\.\d+)?$/ # reassemble x86-linux-{libc}
extra = arch.pop
arch.last << "-#{extra}"
end

cpu = arch.shift

@cpu = case cpu
when /i\d86/ then "x86"
else cpu
end

if arch.length == 2 && arch.last =~ /^\d+(\.\d+)?$/ # for command-line
@os, @version = arch
return
end

os, = arch
if os.nil?
@cpu = nil
os = cpu
end # legacy jruby

@os, @version = case os
when /aix(\d+)?/ then ["aix", $1]
when /cygwin/ then ["cygwin", nil]
when /darwin(\d+)?/ then ["darwin", $1]
when /^macruby$/ then ["macruby", nil]
when /freebsd(\d+)?/ then ["freebsd", $1]
when /^java$/, /^jruby$/ then ["java", nil]
when /^java([\d.]*)/ then ["java", $1]
when /^dalvik(\d+)?$/ then ["dalvik", $1]
when /^dotnet$/ then ["dotnet", nil]
when /^dotnet([\d.]*)/ then ["dotnet", $1]
when /linux-?(\w+)?/ then ["linux", $1]
when /mingw32/ then ["mingw32", nil]
when /mingw-?(\w+)?/ then ["mingw", $1]
when /(mswin\d+)(\_(\d+))?/ then
os = $1
version = $3
@cpu = "x86" if @cpu.nil? && os =~ /32$/
[os, version]
when /netbsdelf/ then ["netbsdelf", nil]
when /openbsd(\d+\.\d+)?/ then ["openbsd", $1]
when /solaris(\d+\.\d+)?/ then ["solaris", $1]
# test
when /^(\w+_platform)(\d+)?/ then [$1, $2]
else ["unknown", nil]
end
# The string parser has its own file because we need to reuse it in Bundler::Standalone.
@cpu, @os, @version = StringParser.run(arch)
when Gem::Platform then
@cpu = arch.cpu
@os = arch.os
Expand Down Expand Up @@ -255,3 +205,5 @@ def =~(other)

CURRENT = "current"
end

require_relative "platform/string_parser"
60 changes: 60 additions & 0 deletions lib/rubygems/platform/string_parser.rb
@@ -0,0 +1,60 @@
# frozen_string_literal: true

module Gem::Platform::StringParser
def self.run(arch, force_mswin_version = false)
arch = "#{arch}_60" if /mswin(?:32|64)$/.match?(arch) && force_mswin_version
arch = arch.split "-"

if arch.length > 2 && arch.last !~ /\d+(\.\d+)?$/ # reassemble x86-linux-{libc}
extra = arch.pop
arch.last << "-#{extra}"
end

cpu = arch.shift

parsed_cpu = case cpu
when /i\d86/ then "x86"
else cpu
end

if arch.length == 2 && arch.last =~ /^\d+(\.\d+)?$/ # for command-line
parsed_os, parsed_version = arch
return [parsed_cpu, parsed_os, parsed_version]
end

os, = arch
if os.nil?
parsed_cpu = nil
os = cpu
end # legacy jruby

parsed_os, parsed_version = case os
when /aix(\d+)?/ then ["aix", $1]
when /cygwin/ then ["cygwin", nil]
when /darwin(\d+)?/ then ["darwin", $1]
when /^macruby$/ then ["macruby", nil]
when /freebsd(\d+)?/ then ["freebsd", $1]
when /^java$/, /^jruby$/ then ["java", nil]
when /^java([\d.]*)/ then ["java", $1]
when /^dalvik(\d+)?$/ then ["dalvik", $1]
when /^dotnet$/ then ["dotnet", nil]
when /^dotnet([\d.]*)/ then ["dotnet", $1]
when /linux-?(\w+)?/ then ["linux", $1]
when /mingw32/ then ["mingw32", nil]
when /mingw-?(\w+)?/ then ["mingw", $1]
when /(mswin\d+)(\_(\d+))?/ then
os = $1
version = $3
parsed_cpu = "x86" if parsed_cpu.nil? && os =~ /32$/
[os, version]
when /netbsdelf/ then ["netbsdelf", nil]
when /openbsd(\d+\.\d+)?/ then ["openbsd", $1]
when /solaris(\d+\.\d+)?/ then ["solaris", $1]
# test
when /^(\w+_platform)(\d+)?/ then [$1, $2]
else ["unknown", nil]
end

[parsed_cpu, parsed_os, parsed_version]
end
end