diff options
-rw-r--r-- | lib/rubygems/ext/cargo_builder.rb | 151 |
1 files changed, 87 insertions, 64 deletions
@@ -4,48 +4,67 @@ # over the `cargo rustc` command which takes care of building Rust code in a way # that Ruby can use. class Gem::Ext::CargoBuilder < Gem::Ext::Builder - attr_reader :spec def initialize(spec) @spec = spec end def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd) - require "rubygems/command" require "fileutils" require "shellwords" build_crate(dest_path, results, args, cargo_dir) - ext_path = rename_cdylib_for_ruby_compatibility(dest_path) - finalize_directory(ext_path, dest_path, lib_dir, cargo_dir) results end - private - def build_crate(dest_path, results, args, cargo_dir) - manifest = File.join(cargo_dir, "Cargo.toml") - given_ruby_static = ENV["RUBY_STATIC"] - ENV["RUBY_STATIC"] = "true" if ruby_static? && !given_ruby_static cargo = ENV.fetch("CARGO", "cargo") cmd = [] cmd += [cargo, "rustc"] cmd += ["--target-dir", dest_path] cmd += ["--manifest-path", manifest] - cmd += ["--lib", "--release", "--locked"] - cmd += ["--"] - cmd += [*cargo_rustc_args(dest_path)] cmd += Gem::Command.build_args cmd += args - self.class.run cmd, results, self.class.class_name, cargo_dir - results - ensure - ENV["RUBY_STATIC"] = given_ruby_static end def cargo_rustc_args(dest_dir) @@ -92,7 +111,7 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder def libruby_args(dest_dir) libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED") raw_libs = Shellwords.split(libs) - raw_libs.flat_map {|l| ldflag_to_link_modifier(l, dest_dir) } end def ruby_static? @@ -103,22 +122,33 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder # Ruby expects the dylib to follow a file name convention for loading def rename_cdylib_for_ruby_compatibility(dest_path) - dylib_path = validate_cargo_build!(dest_path) - dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}" - new_name = dylib_path.gsub(File.basename(dylib_path), dlext_name) - FileUtils.cp(dylib_path, new_name) - new_name end def validate_cargo_build!(dir) - prefix = so_ext == "dll" ? "" : "lib" - dylib_path = File.join(dir, "release", "#{prefix}#{cargo_crate_name}.#{so_ext}") raise DylibNotFoundError, dir unless File.exist?(dylib_path) dylib_path end def cargo_crate_name spec.metadata.fetch('cargo_crate_name', spec.name).tr('-', '_') end @@ -127,42 +157,19 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder split_flags("DLDFLAGS") .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir) } .compact - .flat_map {|arg| ldflag_to_link_modifier(arg, dest_dir) } end def rustc_lib_flags(dest_dir) - split_flags("LIBS").flat_map {|arg| ldflag_to_link_modifier(arg, dest_dir) } end def split_flags(var) Shellwords.split(RbConfig::CONFIG.fetch(var, "")) end - def ldflag_to_link_modifier(arg, dest_dir) - flag = arg[0..1] - val = arg[2..-1] - - case flag - when "-L" then ["-L", "native=#{val}"] - when "-l" then ["-l", val.to_s] - when "-F" then ["-l", "framework=#{val}"] - else ["-C", "link_arg=#{arg}"] - end - end - - def link_flag(link_name) - # These are provided by the CRT with MSVC - # @see https://.com/rust-lang/pkg-config-rs/blob/49a4ac189aafa365167c72e8e503565a7c2697c2/src/lib.rs#L622 - return [] if msvc_target? && ["m", "c", "pthread"].include?(link_name) - - if link_name.include?("ruby") - # Specify the lib kind and give it the name "ruby" for linking - kind = ruby_static? ? "static" : "dylib" - - ["-l", "#{kind}=ruby:#{link_name}"] - else - ["-l", link_name] - end end def msvc_target? @@ -182,20 +189,24 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder !!Gem::WIN_PATTERNS.find {|r| target_platform =~ r } end - # Intepolate substition vars in the arg (i.e. $(DEFFILE)) def maybe_resolve_ldflag_variable(input_arg, dest_dir) - str = input_arg.gsub(/\$\((\w+)\)/) do |var_name| - case var_name - # On windows, it is assumed that mkmf has setup an exports file for the - # extension, so we have to to create one ourselves. - when "DEFFILE" - write_deffile(dest_dir) - else - RbConfig::CONFIG[var_name] - end - end.strip - str == "" ? nil : str end def write_deffile(dest_dir) @@ -241,14 +252,18 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder # Good balance between binary size and debugability def debug_flags ["-C", "debuginfo=1"] end # Copied from ExtConfBuilder - def finalize_directory(ext_path, dest_path, lib_dir, extension_dir) require "fileutils" require "tempfile" begin tmp_dest = Dir.mktmpdir(".gem.", extension_dir) @@ -280,6 +295,14 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder path end # Error raised when no cdylib artifact was created class DylibNotFoundError < StandardError def initialize(dir) |