Class: Gem::Ext::CargoBuilder
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Exceptions:
| |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
Builder
|
|
Instance Chain:
|
|
Inherits: |
Gem::Ext::Builder
|
Defined in: | lib/rubygems/ext/cargo_builder.rb, lib/rubygems/ext/cargo_builder/link_flag_converter.rb |
Overview
This class is used by rubygems to build Rust extensions. It is a thin-wrapper over the cargo rustc
command which takes care of building Rust code in a way that Ruby can use.
Class Method Summary
- .new(spec) ⇒ CargoBuilder constructor
Builder
- Inherited
.class_name, .make, | |
.new | Creates a new extension builder for #spec. |
.run |
Instance Attribute Summary
- #profile rw
- #runner rw
- #spec rw
- #darwin_target? ⇒ Boolean readonly private
- #mingw_target? ⇒ Boolean readonly private
- #msvc_target? ⇒ Boolean readonly private
- #ruby_static? ⇒ Boolean readonly private
- #win_target? ⇒ Boolean readonly private
Builder
- Inherited
::Gem::DefaultUserInteraction
- Included
Instance Method Summary
- #build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
- #build_crate(dest_path, results, args, cargo_dir)
- #build_env
- #cargo_command(cargo_dir, dest_path, args = [])
- #cargo_crate_name private
- #cargo_dylib_path(dest_path) private
- #cargo_rustc_args(dest_dir) private
-
#debug_flags
private
Good balance between binary size and debugability.
- #final_extension_path(dest_path) private
-
#finalize_directory(dest_path, lib_dir, extension_dir)
private
Copied from
ExtConfBuilder
. - #get_relative_path(path, base) private
- #ldflag_to_link_modifier(arg) private
- #libruby_args(dest_dir) private
-
#linker_args
private
We want to use the same linker that Ruby uses, so that the linker flags from mkmf work properly.
- #makefile_config(var_name) private
-
#maybe_resolve_ldflag_variable(input_arg, dest_dir)
private
Interpolate substition vars in the arg (i.e.
-
#mkmf_libpath
private
Corresponds to $(LIBPATH) in mkmf.
- #platform_specific_rustc_args(dest_dir, flags = []) private
- #profile_target_directory private
- #rb_config_env private
-
#rename_cdylib_for_ruby_compatibility(dest_path)
private
Ruby expects the dylib to follow a file name convention for loading.
- #rustc_dynamic_linker_flags(dest_dir) private
- #rustc_lib_flags(dest_dir) private
-
#so_ext
private
We have to basically reimplement RbConfig::CONFIG here to support Ruby < 2.5.
- #split_flags(var) private
- #validate_cargo_build!(dir) private
- #write_deffile(dest_dir) private
Builder
- Inherited
#build_extensions | Builds extensions. |
#build_error | Logs the build |
#build_extension, | |
#builder_for | Chooses the extension builder class for |
#write_gem_make_out | Writes |
::Gem::UserInteraction
- Included
#alert | Displays an alert |
#alert_error | Displays an error |
#alert_warning | Displays a warning |
#ask | Asks a |
#ask_for_password | Asks for a password with a |
#ask_yes_no | Asks a yes or no |
#choose_from_list | Asks the user to answer |
#say | Displays the given |
#terminate_interaction | Terminates the RubyGems process with the given |
#verbose | Calls |
::Gem::DefaultUserInteraction
- Included
::Gem::Text
- Included
#clean_text | Remove any non-printable characters and make the text suitable for printing. |
#format_text | Wraps |
#levenshtein_distance | Returns a value representing the “cost” of transforming str1 into str2 Vendored version of |
#truncate_text, #min3 |
Constructor Details
.new(spec) ⇒ CargoBuilder
Instance Attribute Details
#darwin_target? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubygems/ext/cargo_builder.rb', line 183
def darwin_target? makefile_config("target_os").include?("darwin") end
#mingw_target? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubygems/ext/cargo_builder.rb', line 187
def mingw_target? makefile_config("target_os").include?("mingw") end
#msvc_target? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubygems/ext/cargo_builder.rb', line 179
def msvc_target? makefile_config("target_os").include?("msvc") end
#profile (rw)
[ GitHub ]
#ruby_static? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubygems/ext/cargo_builder.rb', line 121
def ruby_static? return true if %w[1 true].include?(ENV["RUBY_STATIC"]) makefile_config("ENABLE_SHARED") == "no" end
#runner (rw)
[ GitHub ]#spec (rw)
[ GitHub ]
#win_target? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubygems/ext/cargo_builder.rb', line 191
def win_target? target_platform = RbConfig::CONFIG["target_os"] !!Gem::WIN_PATTERNS.find {|r| target_platform =~ r } end
Instance Method Details
#build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 18
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
#build_crate(dest_path, results, args, cargo_dir)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 29
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
#build_env
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 37
def build_env build_env = rb_config_env build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?("RUBY_STATIC") build_env["RUSTFLAGS"] = "#{ENV["RUSTFLAGS"]} --cfg=rb_sys_gem".strip build_env end
#cargo_command(cargo_dir, dest_path, args = [])
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 44
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 += ["--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"] if profile == :release cmd += Gem::Command.build_args cmd += args cmd += ["--"] cmd += [*cargo_rustc_args(dest_path)] cmd end
#cargo_crate_name (private)
[ GitHub ]#cargo_dylib_path(dest_path) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 148
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 += [profile_target_directory, "#{prefix}#{cargo_crate_name}.#{so_ext}"] File.join(*path_parts) end
#cargo_rustc_args(dest_dir) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 71
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), *debug_flags, ] end
#debug_flags (private)
Good balance between binary size and debugability
# File 'lib/rubygems/ext/cargo_builder.rb', line 258
def debug_flags return [] if profile == :dev ["-C", "debuginfo=1"] end
#final_extension_path(dest_path) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 142
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
#finalize_directory(dest_path, lib_dir, extension_dir) (private)
Copied from ExtConfBuilder
# File 'lib/rubygems/ext/cargo_builder.rb', line 265
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
#get_relative_path(path, base) (private)
[ GitHub ]#ldflag_to_link_modifier(arg) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 175
def ldflag_to_link_modifier(arg) LinkFlagConverter.convert(arg) end
#libruby_args(dest_dir) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 115
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) } end
#linker_args (private)
We want to use the same linker that Ruby uses, so that the linker flags from mkmf work properly.
# File 'lib/rubygems/ext/cargo_builder.rb', line 106
def linker_args # Have to handle CC="cl /nologo" on mswin cc_flag = Shellwords.split(makefile_config("CC")) linker = cc_flag.shift link_args = cc_flag.flat_map {|a| ["-C", "link-arg=#{a}"] } ["-C", "linker=#{linker}", *link_args] end
#makefile_config(var_name) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 249
def makefile_config(var_name) val = RbConfig::MAKEFILE_CONFIG[var_name] return unless val RbConfig. (val.dup) end
#maybe_resolve_ldflag_variable(input_arg, dest_dir) (private)
Interpolate substition vars in the arg (i.e. $(DEFFILE))
# File 'lib/rubygems/ext/cargo_builder.rb', line 197
def maybe_resolve_ldflag_variable(input_arg, dest_dir) var_matches = input_arg.match(/\$\((\w+)\)/) return input_arg unless var_matches var_name = var_matches[1] return input_arg if var_name.nil? || var_name.chomp.empty? 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
#mkmf_libpath (private)
Corresponds to $(LIBPATH) in mkmf
# File 'lib/rubygems/ext/cargo_builder.rb', line 245
def mkmf_libpath ["-L", "native=#{makefile_config("libdir")}"] end
#platform_specific_rustc_args(dest_dir, flags = []) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 82
def platform_specific_rustc_args(dest_dir, flags = []) if mingw_target? # On mingw platforms, mkmf adds libruby to the linker flags flags += libruby_args(dest_dir) # Make sure ALSR is used on mingw # see https://github.com/rust-lang/rust/pull/75406/files flags += ["-C", "link-arg=-Wl,--dynamicbase"] flags += ["-C", "link-arg=-Wl,--disable-auto-image-base"] # If the gem is installed on a host with build tools installed, but is # run on one that isn't the missing libraries will cause the extension # to fail on start. flags += ["-C", "link-arg=-static-libgcc"] elsif darwin_target? # Ventura does not always have this flag enabled flags += ["-C", "link-arg=-Wl,-undefined,dynamic_lookup"] end flags end
#profile_target_directory (private)
[ GitHub ]#rb_config_env (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 65
def rb_config_env result = {} RbConfig::CONFIG.each {|k, v| result["RBCONFIG_#{k}"] = v } result end
#rename_cdylib_for_ruby_compatibility(dest_path) (private)
Ruby expects the dylib to follow a file name convention for loading
# File 'lib/rubygems/ext/cargo_builder.rb', line 128
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
#rustc_dynamic_linker_flags(dest_dir) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 160
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
#rustc_lib_flags(dest_dir) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 167
def rustc_lib_flags(dest_dir) split_flags("LIBS").flat_map {|arg| ldflag_to_link_modifier(arg) } end
#so_ext (private)
We have to basically reimplement RbConfig::CONFIG here to support Ruby < 2.5
# File 'lib/rubygems/ext/cargo_builder.rb', line 232
def so_ext return RbConfig::CONFIG["SOEXT"] if RbConfig::CONFIG.key?("SOEXT") if win_target? "dll" elsif darwin_target? "dylib" else "so" end end
#split_flags(var) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 171
def split_flags(var) Shellwords.split(RbConfig::CONFIG.fetch(var, "")) end
#validate_cargo_build!(dir) (private)
# File 'lib/rubygems/ext/cargo_builder.rb', line 134
def validate_cargo_build!(dir) dylib_path = cargo_dylib_path(dir) raise DylibNotFoundError, dir unless File.exist?(dylib_path) dylib_path end
#write_deffile(dest_dir) (private)
[ GitHub ]# File 'lib/rubygems/ext/cargo_builder.rb', line 216
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 end