[rubygems/rubygems] Add --target-rbconfig
option to gem install
and gem update
commands
This patch adds `--target-rbconfig` option to specify the rbconfig.rb file for the deployment target platform. This is useful when cross-compiling gems. At the moment, this option is only available for `extconf.rb`-based extensions. https://github.com/rubygems/rubygems/commit/cf2843f7a2
This commit is contained in:
parent
91bbb78313
commit
273d41b9e3
@ -18,6 +18,7 @@ require_relative "rubygems/compatibility"
|
|||||||
require_relative "rubygems/defaults"
|
require_relative "rubygems/defaults"
|
||||||
require_relative "rubygems/deprecate"
|
require_relative "rubygems/deprecate"
|
||||||
require_relative "rubygems/errors"
|
require_relative "rubygems/errors"
|
||||||
|
require_relative "rubygems/target_rbconfig"
|
||||||
|
|
||||||
##
|
##
|
||||||
# RubyGems is the Ruby standard for publishing and managing third party
|
# RubyGems is the Ruby standard for publishing and managing third party
|
||||||
@ -179,6 +180,8 @@ module Gem
|
|||||||
|
|
||||||
@discover_gems_on_require = true
|
@discover_gems_on_require = true
|
||||||
|
|
||||||
|
@target_rbconfig = nil
|
||||||
|
|
||||||
##
|
##
|
||||||
# Try to activate a gem containing +path+. Returns true if
|
# Try to activate a gem containing +path+. Returns true if
|
||||||
# activation succeeded or wasn't needed because it was already
|
# activation succeeded or wasn't needed because it was already
|
||||||
@ -396,6 +399,23 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
|
|||||||
paths.spec_cache_dir
|
paths.spec_cache_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The RbConfig object for the deployment target platform.
|
||||||
|
#
|
||||||
|
# This is usually the same as the running platform, but may be
|
||||||
|
# different if you are cross-compiling.
|
||||||
|
|
||||||
|
def self.target_rbconfig
|
||||||
|
@target_rbconfig || Gem::TargetRbConfig.for_running_ruby
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.set_target_rbconfig(rbconfig_path)
|
||||||
|
@target_rbconfig = Gem::TargetRbConfig.from_path(rbconfig_path)
|
||||||
|
Gem::Platform.local(refresh: true)
|
||||||
|
Gem.platforms << Gem::Platform.local unless Gem.platforms.include? Gem::Platform.local
|
||||||
|
@target_rbconfig
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Quietly ensure the Gem directory +dir+ contains all the proper
|
# Quietly ensure the Gem directory +dir+ contains all the proper
|
||||||
# subdirectories. If we can't create a directory due to a permission
|
# subdirectories. If we can't create a directory due to a permission
|
||||||
@ -450,7 +470,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
|
|||||||
# distinction as extensions cannot be shared between the two.
|
# distinction as extensions cannot be shared between the two.
|
||||||
|
|
||||||
def self.extension_api_version # :nodoc:
|
def self.extension_api_version # :nodoc:
|
||||||
if RbConfig::CONFIG["ENABLE_SHARED"] == "no"
|
if target_rbconfig["ENABLE_SHARED"] == "no"
|
||||||
"#{ruby_api_version}-static"
|
"#{ruby_api_version}-static"
|
||||||
else
|
else
|
||||||
ruby_api_version
|
ruby_api_version
|
||||||
@ -810,7 +830,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
|
|||||||
# Returns a String containing the API compatibility version of Ruby
|
# Returns a String containing the API compatibility version of Ruby
|
||||||
|
|
||||||
def self.ruby_api_version
|
def self.ruby_api_version
|
||||||
@ruby_api_version ||= RbConfig::CONFIG["ruby_version"].dup
|
@ruby_api_version ||= target_rbconfig["ruby_version"].dup
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.env_requirement(gem_name)
|
def self.env_requirement(gem_name)
|
||||||
|
@ -19,13 +19,14 @@ class Gem::Ext::Builder
|
|||||||
$1.downcase
|
$1.downcase
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.make(dest_path, results, make_dir = Dir.pwd, sitedir = nil, targets = ["clean", "", "install"])
|
def self.make(dest_path, results, make_dir = Dir.pwd, sitedir = nil, targets = ["clean", "", "install"],
|
||||||
|
target_rbconfig: Gem.target_rbconfig)
|
||||||
unless File.exist? File.join(make_dir, "Makefile")
|
unless File.exist? File.join(make_dir, "Makefile")
|
||||||
raise Gem::InstallError, "Makefile not found"
|
raise Gem::InstallError, "Makefile not found"
|
||||||
end
|
end
|
||||||
|
|
||||||
# try to find make program from Ruby configure arguments first
|
# try to find make program from Ruby configure arguments first
|
||||||
RbConfig::CONFIG["configure_args"] =~ /with-make-prog\=(\w+)/
|
target_rbconfig["configure_args"] =~ /with-make-prog\=(\w+)/
|
||||||
make_program_name = ENV["MAKE"] || ENV["make"] || $1
|
make_program_name = ENV["MAKE"] || ENV["make"] || $1
|
||||||
make_program_name ||= RUBY_PLATFORM.include?("mswin") ? "nmake" : "make"
|
make_program_name ||= RUBY_PLATFORM.include?("mswin") ? "nmake" : "make"
|
||||||
make_program = Shellwords.split(make_program_name)
|
make_program = Shellwords.split(make_program_name)
|
||||||
@ -131,10 +132,11 @@ class Gem::Ext::Builder
|
|||||||
# have build arguments, saved, set +build_args+ which is an ARGV-style
|
# have build arguments, saved, set +build_args+ which is an ARGV-style
|
||||||
# array.
|
# array.
|
||||||
|
|
||||||
def initialize(spec, build_args = spec.build_args)
|
def initialize(spec, build_args = spec.build_args, target_rbconfig = Gem.target_rbconfig)
|
||||||
@spec = spec
|
@spec = spec
|
||||||
@build_args = build_args
|
@build_args = build_args
|
||||||
@gem_dir = spec.full_gem_path
|
@gem_dir = spec.full_gem_path
|
||||||
|
@target_rbconfig = target_rbconfig
|
||||||
|
|
||||||
@ran_rake = false
|
@ran_rake = false
|
||||||
end
|
end
|
||||||
@ -191,7 +193,7 @@ EOF
|
|||||||
FileUtils.mkdir_p dest_path
|
FileUtils.mkdir_p dest_path
|
||||||
|
|
||||||
results = builder.build(extension, dest_path,
|
results = builder.build(extension, dest_path,
|
||||||
results, @build_args, lib_dir, extension_dir)
|
results, @build_args, lib_dir, extension_dir, @target_rbconfig)
|
||||||
|
|
||||||
verbose { results.join("\n") }
|
verbose { results.join("\n") }
|
||||||
|
|
||||||
|
@ -16,10 +16,15 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
|
|||||||
@profile = :release
|
@profile = :release
|
||||||
end
|
end
|
||||||
|
|
||||||
def build(extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
|
def build(extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd,
|
||||||
|
target_rbconfig=Gem.target_rbconfig)
|
||||||
require "tempfile"
|
require "tempfile"
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
|
||||||
|
if target_rbconfig.path
|
||||||
|
warn "--target-rbconfig is not yet supported for Rust extensions. Ignoring"
|
||||||
|
end
|
||||||
|
|
||||||
# Where's the Cargo.toml of the crate we're building
|
# Where's the Cargo.toml of the crate we're building
|
||||||
cargo_toml = File.join(cargo_dir, "Cargo.toml")
|
cargo_toml = File.join(cargo_dir, "Cargo.toml")
|
||||||
# What's the crate's name
|
# What's the crate's name
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
|
class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
|
||||||
def self.build(extension, dest_path, results, args=[], lib_dir=nil, cmake_dir=Dir.pwd)
|
def self.build(extension, dest_path, results, args=[], lib_dir=nil, cmake_dir=Dir.pwd,
|
||||||
|
target_rbconfig=Gem.target_rbconfig)
|
||||||
|
if target_rbconfig.path
|
||||||
|
warn "--target-rbconfig is not yet supported for CMake extensions. Ignoring"
|
||||||
|
end
|
||||||
|
|
||||||
unless File.exist?(File.join(cmake_dir, "Makefile"))
|
unless File.exist?(File.join(cmake_dir, "Makefile"))
|
||||||
require_relative "../command"
|
require_relative "../command"
|
||||||
cmd = ["cmake", ".", "-DCMAKE_INSTALL_PREFIX=#{dest_path}", *Gem::Command.build_args]
|
cmd = ["cmake", ".", "-DCMAKE_INSTALL_PREFIX=#{dest_path}", *Gem::Command.build_args]
|
||||||
@ -9,7 +14,7 @@ class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
|
|||||||
run cmd, results, class_name, cmake_dir
|
run cmd, results, class_name, cmake_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
make dest_path, results, cmake_dir
|
make dest_path, results, cmake_dir, target_rbconfig: target_rbconfig
|
||||||
|
|
||||||
results
|
results
|
||||||
end
|
end
|
||||||
|
@ -7,14 +7,19 @@
|
|||||||
#++
|
#++
|
||||||
|
|
||||||
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
|
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
|
||||||
def self.build(extension, dest_path, results, args=[], lib_dir=nil, configure_dir=Dir.pwd)
|
def self.build(extension, dest_path, results, args=[], lib_dir=nil, configure_dir=Dir.pwd,
|
||||||
|
target_rbconfig=Gem.target_rbconfig)
|
||||||
|
if target_rbconfig.path
|
||||||
|
warn "--target-rbconfig is not yet supported for configure-based extensions. Ignoring"
|
||||||
|
end
|
||||||
|
|
||||||
unless File.exist?(File.join(configure_dir, "Makefile"))
|
unless File.exist?(File.join(configure_dir, "Makefile"))
|
||||||
cmd = ["sh", "./configure", "--prefix=#{dest_path}", *args]
|
cmd = ["sh", "./configure", "--prefix=#{dest_path}", *args]
|
||||||
|
|
||||||
run cmd, results, class_name, configure_dir
|
run cmd, results, class_name, configure_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
make dest_path, results, configure_dir
|
make dest_path, results, configure_dir, target_rbconfig: target_rbconfig
|
||||||
|
|
||||||
results
|
results
|
||||||
end
|
end
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
#++
|
#++
|
||||||
|
|
||||||
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
|
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
|
||||||
def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd)
|
def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd,
|
||||||
|
target_rbconfig=Gem.target_rbconfig)
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
require "tempfile"
|
require "tempfile"
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
cmd = ruby << File.basename(extension)
|
cmd = ruby << File.basename(extension)
|
||||||
|
cmd << "--target-rbconfig=#{target_rbconfig.path}" if target_rbconfig.path
|
||||||
cmd.push(*args)
|
cmd.push(*args)
|
||||||
|
|
||||||
run(cmd, results, class_name, extension_dir) do |s, r|
|
run(cmd, results, class_name, extension_dir) do |s, r|
|
||||||
@ -39,7 +41,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
|
|||||||
|
|
||||||
ENV["DESTDIR"] = nil
|
ENV["DESTDIR"] = nil
|
||||||
|
|
||||||
make dest_path, results, extension_dir, tmp_dest_relative
|
make dest_path, results, extension_dir, tmp_dest_relative, target_rbconfig: target_rbconfig
|
||||||
|
|
||||||
full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
|
full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
|
||||||
|
|
||||||
@ -55,7 +57,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
|
|||||||
destent.exist? || FileUtils.mv(ent.path, destent.path)
|
destent.exist? || FileUtils.mv(ent.path, destent.path)
|
||||||
end
|
end
|
||||||
|
|
||||||
make dest_path, results, extension_dir, tmp_dest_relative, ["clean"]
|
make dest_path, results, extension_dir, tmp_dest_relative, ["clean"], target_rbconfig: target_rbconfig
|
||||||
ensure
|
ensure
|
||||||
ENV["DESTDIR"] = destdir
|
ENV["DESTDIR"] = destdir
|
||||||
end
|
end
|
||||||
|
@ -9,7 +9,12 @@ require_relative "../shellwords"
|
|||||||
#++
|
#++
|
||||||
|
|
||||||
class Gem::Ext::RakeBuilder < Gem::Ext::Builder
|
class Gem::Ext::RakeBuilder < Gem::Ext::Builder
|
||||||
def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd)
|
def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd,
|
||||||
|
target_rbconfig=Gem.target_rbconfig)
|
||||||
|
if target_rbconfig.path
|
||||||
|
warn "--target-rbconfig is not yet supported for Rake extensions. Ignoring"
|
||||||
|
end
|
||||||
|
|
||||||
if /mkrf_conf/i.match?(File.basename(extension))
|
if /mkrf_conf/i.match?(File.basename(extension))
|
||||||
run([Gem.ruby, File.basename(extension), *args], results, class_name, extension_dir)
|
run([Gem.ruby, File.basename(extension), *args], results, class_name, extension_dir)
|
||||||
end
|
end
|
||||||
|
@ -179,6 +179,11 @@ module Gem::InstallUpdateOptions
|
|||||||
"Suggest alternates when gems are not found") do |v,_o|
|
"Suggest alternates when gems are not found") do |v,_o|
|
||||||
options[:suggest_alternate] = v
|
options[:suggest_alternate] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_option(:"Install/Update", "--target-rbconfig [FILE]",
|
||||||
|
"rbconfig.rb for the deployment target platform") do |v, _o|
|
||||||
|
Gem.set_target_rbconfig(v)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -847,7 +847,7 @@ TEXT
|
|||||||
# configure scripts and rakefiles or mkrf_conf files.
|
# configure scripts and rakefiles or mkrf_conf files.
|
||||||
|
|
||||||
def build_extensions
|
def build_extensions
|
||||||
builder = Gem::Ext::Builder.new spec, build_args
|
builder = Gem::Ext::Builder.new spec, build_args, Gem.target_rbconfig
|
||||||
|
|
||||||
builder.build_extensions
|
builder.build_extensions
|
||||||
end
|
end
|
||||||
@ -993,7 +993,7 @@ TEXT
|
|||||||
end
|
end
|
||||||
|
|
||||||
def rb_config
|
def rb_config
|
||||||
RbConfig::CONFIG
|
Gem.target_rbconfig
|
||||||
end
|
end
|
||||||
|
|
||||||
def ruby_install_name
|
def ruby_install_name
|
||||||
|
@ -12,9 +12,10 @@ class Gem::Platform
|
|||||||
|
|
||||||
attr_accessor :cpu, :os, :version
|
attr_accessor :cpu, :os, :version
|
||||||
|
|
||||||
def self.local
|
def self.local(refresh: false)
|
||||||
@local ||= begin
|
return @local if @local && !refresh
|
||||||
arch = RbConfig::CONFIG["arch"]
|
@local = begin
|
||||||
|
arch = Gem.target_rbconfig["arch"]
|
||||||
arch = "#{arch}_60" if /mswin(?:32|64)$/.match?(arch)
|
arch = "#{arch}_60" if /mswin(?:32|64)$/.match?(arch)
|
||||||
new(arch)
|
new(arch)
|
||||||
end
|
end
|
||||||
|
50
lib/rubygems/target_rbconfig.rb
Normal file
50
lib/rubygems/target_rbconfig.rb
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rbconfig"
|
||||||
|
|
||||||
|
##
|
||||||
|
# A TargetConfig is a wrapper around an RbConfig object that provides a
|
||||||
|
# consistent interface for querying configuration for *deployment target
|
||||||
|
# platform*, where the gem being installed is intended to run on.
|
||||||
|
#
|
||||||
|
# The TargetConfig is typically created from the RbConfig of the running Ruby
|
||||||
|
# process, but can also be created from an RbConfig file on disk for cross-
|
||||||
|
# compiling gems.
|
||||||
|
|
||||||
|
class Gem::TargetRbConfig
|
||||||
|
attr_reader :path
|
||||||
|
|
||||||
|
def initialize(rbconfig, path)
|
||||||
|
@rbconfig = rbconfig
|
||||||
|
@path = path
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Creates a TargetRbConfig for the platform that RubyGems is running on.
|
||||||
|
|
||||||
|
def self.for_running_ruby
|
||||||
|
new(::RbConfig, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Creates a TargetRbConfig from the RbConfig file at the given path.
|
||||||
|
# Typically used for cross-compiling gems.
|
||||||
|
|
||||||
|
def self.from_path(rbconfig_path)
|
||||||
|
namespace = Module.new do |m|
|
||||||
|
# Load the rbconfig.rb file within a new anonymous module to avoid
|
||||||
|
# conflicts with the rbconfig for the running platform.
|
||||||
|
Kernel.load rbconfig_path, m
|
||||||
|
end
|
||||||
|
rbconfig = namespace.const_get(:RbConfig)
|
||||||
|
|
||||||
|
new(rbconfig, rbconfig_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Queries the configuration for the given key.
|
||||||
|
|
||||||
|
def [](key)
|
||||||
|
@rbconfig::CONFIG[key]
|
||||||
|
end
|
||||||
|
end
|
@ -310,6 +310,65 @@ install:
|
|||||||
assert_path_exist @spec.extension_dir
|
assert_path_exist @spec.extension_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_build_extensions_with_target_rbconfig
|
||||||
|
fake_rbconfig = File.join @tempdir, "fake_rbconfig.rb"
|
||||||
|
File.open fake_rbconfig, "w" do |f|
|
||||||
|
f.write <<~RUBY
|
||||||
|
module RbConfig
|
||||||
|
CONFIG = {}
|
||||||
|
MAKEFILE_CONFIG = {}
|
||||||
|
|
||||||
|
def self.fire_update!(key, value); end
|
||||||
|
def self.expand(val, config = CONFIG); val; end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
RbConfig::CONFIG.each do |k, v|
|
||||||
|
f.puts %(RbConfig::CONFIG["#{k}"] = "#{v}")
|
||||||
|
end
|
||||||
|
RbConfig::MAKEFILE_CONFIG.each do |k, v|
|
||||||
|
f.puts %(RbConfig::MAKEFILE_CONFIG["#{k}"] = "#{v}")
|
||||||
|
end
|
||||||
|
f.puts "RbConfig::CONFIG['host_os'] = 'fake_os'"
|
||||||
|
f.puts "RbConfig::CONFIG['arch'] = 'fake_arch'"
|
||||||
|
end
|
||||||
|
|
||||||
|
system(Gem.ruby, "-rmkmf", "-e", "exit MakeMakefile::RbConfig::CONFIG['host_os'] == 'fake_os'",
|
||||||
|
"--", "--target-rbconfig=#{fake_rbconfig}")
|
||||||
|
pend "This version of mkmf does not support --target-rbconfig" unless $?.success?
|
||||||
|
|
||||||
|
@spec.extensions << "extconf.rb"
|
||||||
|
@builder = Gem::Ext::Builder.new @spec, "", Gem::TargetRbConfig.from_path(fake_rbconfig)
|
||||||
|
|
||||||
|
FileUtils.mkdir_p @spec.gem_dir
|
||||||
|
|
||||||
|
File.open File.join(@spec.gem_dir, "extconf.rb"), "w" do |f|
|
||||||
|
f.write <<-'RUBY'
|
||||||
|
require 'mkmf'
|
||||||
|
|
||||||
|
extconf_args = File.join __dir__, 'rbconfig_dump'
|
||||||
|
File.open extconf_args, 'w' do |f|
|
||||||
|
["host_os", "arch"].each do |k|
|
||||||
|
f.puts "#{k}=#{MakeMakefile::RbConfig::CONFIG[k]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
create_makefile 'a'
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@builder.build_extensions
|
||||||
|
end
|
||||||
|
|
||||||
|
path = File.join @spec.gem_dir, "rbconfig_dump"
|
||||||
|
|
||||||
|
assert_equal <<~DUMP, File.read(path)
|
||||||
|
host_os=fake_os
|
||||||
|
arch=fake_arch
|
||||||
|
DUMP
|
||||||
|
assert_path_exist @spec.extension_dir
|
||||||
|
end
|
||||||
|
|
||||||
def test_initialize
|
def test_initialize
|
||||||
build_info_dir = File.join @gemhome, "build_info"
|
build_info_dir = File.join @gemhome, "build_info"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user