summaryrefslogtreecommitdiff
path: root/lib/rubygems/ext
diff options
context:
space:
mode:
authorMat Sadler <[email protected]>2023-01-22 21:16:12 -0800
committergit <[email protected]>2023-01-30 17:39:47 +0000
commitca951f671920b64c8275ffccdc680848f60cbede ()
treecab2758553a2b5f1749021c9c7a911658529af55 /lib/rubygems/ext
parent00e1ee4a7eb9f1703ddaf15158fefe0f7b594839 (diff)
[rubygems/rubygems] use cargo to get crate name
the final copying of the extension into place has been slimmed down, reflecting that it only needs to copy a single file, rather than replicating the more involved process used for a C ext this also refactors #build so that #cargo_crate_name only needs to be called once, and hopefully the build flow is easier to understand https://.com/rubygems/rubygems/commit/5a0d7f2e6c
-rw-r--r--lib/rubygems/ext/builder.rb3
-rw-r--r--lib/rubygems/ext/cargo_builder.rb181
2 files changed, 93 insertions, 91 deletions
@@ -131,8 +131,7 @@ class Gem::Ext::Builder
when /CMakeLists.txt/ then
Gem::Ext::CmakeBuilder
when /Cargo.toml/ then
- # We use the spec name here to ensure we invoke the correct init function later
- Gem::Ext::CargoBuilder.new(@spec)
else
build_error("No builder for extension '#{extension}'")
end
@@ -6,30 +6,57 @@
class Gem::Ext::CargoBuilder < Gem::Ext::Builder
attr_accessor :spec, :runner, :profile
- def initialize(spec)
require_relative "../command"
require_relative "cargo_builder/link_flag_converter"
- @spec = spec
@runner = self.class.method(:run)
@profile = :release
end
def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
require "fileutils"
- require "shellwords"
- build_crate(dest_path, results, args, cargo_dir)
- validate_cargo_build!(dest_path)
- rename_cdylib_for_ruby_compatibility(dest_path)
- finalize_directory(dest_path, lib_dir, cargo_dir)
- results
- end
- def build_crate(dest_path, results, args, cargo_dir)
- env = build_env
- cmd = cargo_command(cargo_dir, dest_path, args)
- runner.call cmd, results, "cargo", cargo_dir, env
results
end
@@ -42,39 +69,42 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
build_env
end
- def cargo_command(cargo_dir, dest_path, args = [])
- manifest = File.join(cargo_dir, "Cargo.toml")
- cargo = ENV.fetch("CARGO", "cargo")
cmd = []
cmd += [cargo, "rustc"]
cmd += ["--crate-type", "cdylib"]
cmd += ["--target", ENV["CARGO_BUILD_TARGET"]] if ENV["CARGO_BUILD_TARGET"]
cmd += ["--target-dir", dest_path]
- cmd += ["--manifest-path", manifest]
cmd += ["--lib"]
cmd += ["--profile", profile.to_s]
cmd += ["--locked"]
cmd += Gem::Command.build_args
cmd += args
cmd += ["--"]
- cmd += [*cargo_rustc_args(dest_path)]
cmd
end
private
def rb_config_env
result = {}
RbConfig::CONFIG.each {|k, v| result["RBCONFIG_#{k}"] = v }
result
end
- def cargo_rustc_args(dest_dir)
[
*linker_args,
*mkmf_libpath,
- *rustc_dynamic_linker_flags(dest_dir),
*rustc_lib_flags(dest_dir),
*platform_specific_rustc_args(dest_dir),
]
@@ -134,42 +164,53 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
makefile_config("ENABLE_SHARED") == "no"
end
- # Ruby expects the dylib to follow a file name convention for loading
- def rename_cdylib_for_ruby_compatibility(dest_path)
- new_path = final_extension_path(dest_path)
- FileUtils.cp(cargo_dylib_path(dest_path), new_path)
- new_path
end
- def validate_cargo_build!(dir)
- dylib_path = cargo_dylib_path(dir)
- raise DylibNotFoundError, dir unless File.exist?(dylib_path)
- dylib_path
- end
- def final_extension_path(dest_path)
- dylib_path = cargo_dylib_path(dest_path)
- dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}"
- dylib_path.gsub(File.basename(dylib_path), dlext_name)
- end
- def cargo_dylib_path(dest_path)
- prefix = so_ext == "dll" ? "" : "lib"
- path_parts = [dest_path]
- path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"]
- path_parts += ["release", "#{prefix}#{cargo_crate_name}.#{so_ext}"]
- File.join(*path_parts)
- end
- def cargo_crate_name
- spec.metadata.fetch("cargo_crate_name", spec.name).tr("-", "_")
end
- def rustc_dynamic_linker_flags(dest_dir)
split_flags("DLDFLAGS")
- .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir) }
.compact
.flat_map {|arg| ldflag_to_link_modifier(arg) }
end
@@ -204,7 +245,7 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
end
# Interpolate substitution vars in the arg (i.e. $(DEFFILE))
- def maybe_resolve_ldflag_variable(input_arg, dest_dir)
var_matches = input_arg.match(/\$\((\w+)\)/)
return input_arg unless var_matches
@@ -217,19 +258,19 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# 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
- def write_deffile(dest_dir)
- deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def")
export_prefix = makefile_config("EXPORT_PREFIX") || ""
File.open(deffile_path, "w") do |f|
f.puts "EXPORTS"
- f.puts "#{export_prefix.strip}Init_#{spec.name}"
end
deffile_path
@@ -264,44 +305,6 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
RbConfig.expand(val.dup)
end
- # Copied from ExtConfBuilder
- def finalize_directory(dest_path, lib_dir, extension_dir)
- require "fileutils"
- require "tempfile"
-
- ext_path = final_extension_path(dest_path)
-
- begin
- tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
-
- # Some versions of `mktmpdir` return absolute paths, which will break make
- # if the paths contain spaces.
- #
- # As such, we convert to a relative path.
- tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
-
- full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
-
- # TODO: remove in RubyGems 4
- if Gem.install_extension_in_lib && lib_dir
- FileUtils.mkdir_p lib_dir
- FileUtils.cp_r ext_path, lib_dir, remove_destination: true
- end
-
- FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
- destent = ent.class.new(dest_path, ent.rel)
- destent.exist? || FileUtils.mv(ent.path, destent.path)
- end
- ensure
- FileUtils.rm_rf tmp_dest if tmp_dest
- end
- end
-
- def get_relative_path(path, base)
- path[0..base.length - 1] = "." if path.start_with?(base)
- path
- end
-
# Error raised when no cdylib artifact was created
class DylibNotFoundError < StandardError
def initialize(dir)