rb_sys 0.1.14 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +3 -1
- data/lib/rb_sys/mkmf.rb +18 -59
- data/lib/rb_sys/version.rb +1 -1
- data/vendor/rubygems/ext/cargo_builder/link_flag_converter.rb +23 -0
- data/vendor/rubygems/ext/cargo_builder.rb +336 -0
- data.tar.gz.sig +0 -0
- metadata +4 -4
- metadata.gz.sig +0 -0
- data/lib/rb_sys/cargo_builder/link_flag_converter.rb +0 -25
- data/lib/rb_sys/cargo_builder.rb +0 -328
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27affe876fb5901792fe8af81776d7b6b6094b7b97849c1a8894296c6d119f77
|
4
|
+
data.tar.gz: a1e59e9580925d7d0a470ec33c72f4fe296148efacb546901d40b4fb99c543bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acfa547f5b88a8274c384a6d72953b22eaf5c8b551092caac377a533d80160a1f9fb76dbdf09668957dbe016ed78b6c1e5bfd57bb0ccfaf1cbab85b675353046
|
7
|
+
data.tar.gz: c56ed902664615dd777a9611fcbe3b7ae2229a8832690c47b568340df9746278939fd82d3b05dea1c7b258116ce2deb59a1c30ac954b648ddb552b922e70eef8
|
checksums.yaml.gz.sig
CHANGED
data/lib/rb_sys/mkmf.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "rubygems/ext"
|
4
4
|
require "shellwords"
|
5
|
-
require_relative "cargo_builder"
|
5
|
+
require_relative "./../../vendor/rubygems/ext/cargo_builder"
|
6
6
|
|
7
7
|
# Root module
|
8
8
|
module RbSys
|
@@ -25,10 +25,10 @@ module RbSys
|
|
25
25
|
# . create_rust_makefile("my_extension") do |r|
|
26
26
|
# . # All of these are optional
|
27
27
|
# . r.env = { 'FOO' => 'bar' }
|
28
|
-
# . r.profile = ENV.fetch('
|
28
|
+
# . r.profile = ENV.fetch('CARGO_BUILD_PROFILE', :dev).to_sym
|
29
29
|
# . r.features = %w[some_cargo_feature]
|
30
30
|
# . end
|
31
|
-
def create_rust_makefile(target, &blk)
|
31
|
+
def create_rust_makefile(target, srcprefix = nil, &blk)
|
32
32
|
if target.include?("/")
|
33
33
|
target_prefix, target = File.split(target)
|
34
34
|
target_prefix[0, 0] = "/"
|
@@ -37,72 +37,40 @@ module RbSys
|
|
37
37
|
end
|
38
38
|
|
39
39
|
spec = Struct.new(:name, :metadata).new(target, {})
|
40
|
-
builder = CargoBuilder.new(spec)
|
40
|
+
builder = Gem::Ext::CargoBuilder.new(spec)
|
41
41
|
|
42
42
|
yield builder if blk
|
43
43
|
|
44
|
-
srcprefix
|
44
|
+
srcprefix ||= "$(srcdir)/#{srcprefix}".chomp("/")
|
45
45
|
RbConfig.expand(srcdir = srcprefix.dup)
|
46
46
|
|
47
|
-
full_cargo_command = cargo_command(srcdir, builder)
|
48
|
-
|
49
47
|
# rubocop:disable Style/GlobalVars
|
50
|
-
make_install =
|
51
|
-
RB_SYS_CARGO_PROFILE ?= #{builder.profile}
|
52
|
-
RB_SYS_CARGO_FEATURES ?= #{builder.features.join(",")}
|
53
|
-
CARGO ?= cargo
|
54
|
-
|
55
|
-
ifeq ($(RB_SYS_CARGO_PROFILE),dev)
|
56
|
-
RB_SYS_TARGET_DIR ?= debug
|
57
|
-
else
|
58
|
-
RB_SYS_TARGET_DIR ?= $(RB_SYS_CARGO_PROFILE)
|
59
|
-
endif
|
60
|
-
|
61
|
-
ifeq ($(RB_SYS_CARGO_PROFILE),release)
|
62
|
-
RB_SYS_CARGO_PROFILE_FLAG = --release
|
63
|
-
else
|
64
|
-
RB_SYS_CARGO_PROFILE_FLAG = --profile $(RB_SYS_CARGO_PROFILE)
|
65
|
-
endif
|
66
|
-
|
48
|
+
make_install = <<~MAKE
|
67
49
|
target_prefix = #{target_prefix}
|
68
|
-
|
69
|
-
|
70
|
-
CLEANLIBS = $(RUSTLIB) $(DLLIB) $(DEFFILE)
|
50
|
+
CARGO_PROFILE = release
|
51
|
+
CLEANLIBS = $(RUSTLIB) $(DLLIB)
|
71
52
|
DISTCLEANDIRS = target/
|
72
53
|
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
73
54
|
RUSTLIB = #{dllib_path(builder)}
|
74
55
|
TARGET = #{target}
|
75
56
|
DLLIB = $(TARGET).#{RbConfig::CONFIG["DLEXT"]}
|
76
|
-
|
77
|
-
DEFFILE = $(TARGET_DIR)/$(TARGET)-$(arch).def
|
57
|
+
|
78
58
|
#{base_makefile(srcdir)}
|
59
|
+
|
79
60
|
#{env_vars(builder)}
|
80
61
|
|
81
62
|
FORCE: ;
|
82
63
|
|
83
|
-
$(
|
84
|
-
\t
|
85
|
-
\t$(
|
86
|
-
|
87
|
-
$(DEFFILE): $(TARGET_DIR)
|
88
|
-
\t$(ECHO) generating $(@)
|
89
|
-
\t$(Q) ($(COPY) $(srcdir)/$(TARGET).def $@ 2> /dev/null) || (echo EXPORTS && echo $(TARGET_ENTRY)) > $@
|
64
|
+
$(DLLIB): FORCE
|
65
|
+
\t#{cargo_command(srcdir, builder)}
|
66
|
+
\t$(COPY) "$(RUSTLIB)" $@
|
90
67
|
|
91
|
-
|
92
|
-
\t$(
|
93
|
-
\t$(Q) #{full_cargo_command}
|
94
|
-
\t$(Q) $(COPY) "$(RUSTLIB)" $@
|
95
|
-
|
96
|
-
install: $(DLLIB) Makefile
|
97
|
-
\t$(ECHO) installing $(DLLIB)
|
98
|
-
\t$(Q) $(MAKEDIRS) $(RUBYARCHDIR)
|
99
|
-
\t$(Q) $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
68
|
+
install: $(DLLIB)
|
69
|
+
\t$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
100
70
|
|
101
71
|
all: #{$extout ? "install" : "$(DLLIB)"}
|
102
72
|
MAKE
|
103
73
|
|
104
|
-
gsub_cargo_command!(make_install, builder: builder)
|
105
|
-
|
106
74
|
File.write("Makefile", make_install)
|
107
75
|
end
|
108
76
|
# rubocop:enable Style/GlobalVars
|
@@ -118,12 +86,10 @@ module RbSys
|
|
118
86
|
end
|
119
87
|
|
120
88
|
def cargo_command(cargo_dir, builder)
|
121
|
-
builder.ext_dir = cargo_dir
|
122
89
|
dest_path = File.join(Dir.pwd, "target")
|
123
|
-
args =
|
124
|
-
|
125
|
-
cargo_cmd
|
126
|
-
Shellwords.join(cargo_cmd).gsub("\\=", "=").gsub(/\Acargo/, "$(CARGO)")
|
90
|
+
args = []
|
91
|
+
cargo_cmd = builder.cargo_command(cargo_dir, dest_path, args)
|
92
|
+
Shellwords.join(cargo_cmd).gsub("\\=", "=")
|
127
93
|
end
|
128
94
|
|
129
95
|
def env_vars(builder)
|
@@ -145,13 +111,6 @@ module RbSys
|
|
145
111
|
def dllib_path(builder)
|
146
112
|
builder.cargo_dylib_path(File.join(Dir.pwd, "target"))
|
147
113
|
end
|
148
|
-
|
149
|
-
def gsub_cargo_command!(cargo_command, builder:)
|
150
|
-
cargo_command.gsub!(/--profile \w+/, "$(RB_SYS_CARGO_PROFILE_FLAG)")
|
151
|
-
cargo_command.gsub!(%r{--features \S+}, "--features $(RB_SYS_CARGO_FEATURES)")
|
152
|
-
cargo_command.gsub!(%r{/target/\w+/}, "/target/$(RB_SYS_TARGET_DIR)/")
|
153
|
-
cargo_command
|
154
|
-
end
|
155
114
|
end
|
156
115
|
end
|
157
116
|
|
data/lib/rb_sys/version.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Gem::Ext::CargoBuilder < Gem::Ext::Builder
|
4
|
+
# Converts Ruby link flags into something cargo understands
|
5
|
+
class LinkFlagConverter
|
6
|
+
def self.convert(arg)
|
7
|
+
case arg.chomp
|
8
|
+
when /^-L\s*(.+)$/
|
9
|
+
["-L", "native=#{$1}"]
|
10
|
+
when /^--library=(\w+\S+)$/, /^-l\s*(\w+\S+)$/
|
11
|
+
["-l", $1]
|
12
|
+
when /^-l\s*:lib(\S+).a$/
|
13
|
+
["-l", "static=#{$1}"]
|
14
|
+
when /^-l\s*:lib(\S+).(so|dylib|dll)$/
|
15
|
+
["-l", "dylib=#{$1}"]
|
16
|
+
when /^-F\s*(.*)$/
|
17
|
+
["-l", "framework=#{$1}"]
|
18
|
+
else
|
19
|
+
["-C", "link_arg=#{arg}"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,336 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class is used by rubygems to build Rust extensions. It is a thin-wrapper
|
4
|
+
# over the `cargo rustc` command which takes care of building Rust code in a way
|
5
|
+
# that Ruby can use.
|
6
|
+
class Gem::Ext::CargoBuilder < Gem::Ext::Builder
|
7
|
+
attr_accessor :spec, :runner, :profile, :env, :features, :target, :extra_rustc_args
|
8
|
+
|
9
|
+
def initialize(spec)
|
10
|
+
require "rubygems/command"
|
11
|
+
require_relative "cargo_builder/link_flag_converter"
|
12
|
+
|
13
|
+
@spec = spec
|
14
|
+
@runner = self.class.method(:run)
|
15
|
+
@profile = ENV.fetch("CARGO_BUILD_PROFILE", :release).to_sym
|
16
|
+
@env = {}
|
17
|
+
@features = []
|
18
|
+
@target = ENV['CARGO_BUILD_TARGET']
|
19
|
+
@extra_rustc_args = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
|
23
|
+
require "fileutils"
|
24
|
+
require "shellwords"
|
25
|
+
|
26
|
+
build_crate(dest_path, results, args, cargo_dir)
|
27
|
+
validate_cargo_build!(dest_path)
|
28
|
+
rename_cdylib_for_ruby_compatibility(dest_path)
|
29
|
+
finalize_directory(dest_path, lib_dir, cargo_dir)
|
30
|
+
results
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_crate(dest_path, results, args, cargo_dir)
|
34
|
+
env = build_env
|
35
|
+
cmd = cargo_command(cargo_dir, dest_path, args)
|
36
|
+
runner.call cmd, results, "cargo", cargo_dir, env
|
37
|
+
|
38
|
+
results
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_env
|
42
|
+
build_env = rb_config_env
|
43
|
+
build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?("RUBY_STATIC")
|
44
|
+
build_env.merge(env)
|
45
|
+
end
|
46
|
+
|
47
|
+
def cargo_command(cargo_dir, dest_path, args = [])
|
48
|
+
manifest = File.join(cargo_dir, "Cargo.toml")
|
49
|
+
cargo = ENV.fetch("CARGO", "cargo")
|
50
|
+
|
51
|
+
cmd = []
|
52
|
+
cmd += [cargo, "rustc"]
|
53
|
+
cmd += ["--target", target] if target
|
54
|
+
cmd += ["--target-dir", dest_path]
|
55
|
+
cmd += ["--features", features.join(",")] unless features.empty?
|
56
|
+
cmd += ["--manifest-path", manifest]
|
57
|
+
cmd += ["--lib"]
|
58
|
+
cmd += ["--profile", profile.to_s]
|
59
|
+
cmd += ["--locked"] if profile.to_s == 'release'
|
60
|
+
cmd += Gem::Command.build_args
|
61
|
+
cmd += args
|
62
|
+
cmd += ["--"]
|
63
|
+
cmd += [*cargo_rustc_args(dest_path)]
|
64
|
+
cmd += extra_rustc_args
|
65
|
+
cmd
|
66
|
+
end
|
67
|
+
|
68
|
+
def cargo_dylib_path(dest_path)
|
69
|
+
prefix = so_ext == "dll" ? "" : "lib"
|
70
|
+
path_parts = [dest_path]
|
71
|
+
path_parts << target if target
|
72
|
+
path_parts += [profile_target_directory, "#{prefix}#{cargo_crate_name}.#{so_ext}"]
|
73
|
+
File.join(*path_parts)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def rb_config_env
|
79
|
+
result = {}
|
80
|
+
RbConfig::CONFIG.each { |k, v| result["RBCONFIG_#{k}"] = v }
|
81
|
+
result
|
82
|
+
end
|
83
|
+
|
84
|
+
def cargo_rustc_args(dest_dir)
|
85
|
+
[
|
86
|
+
*linker_args,
|
87
|
+
*mkmf_libpath,
|
88
|
+
*rustc_dynamic_linker_flags(dest_dir),
|
89
|
+
*rustc_lib_flags(dest_dir),
|
90
|
+
*platform_specific_rustc_args(dest_dir),
|
91
|
+
*debug_flags
|
92
|
+
]
|
93
|
+
end
|
94
|
+
|
95
|
+
def platform_specific_rustc_args(dest_dir, flags = [])
|
96
|
+
if mingw_target?
|
97
|
+
# On mingw platforms, mkmf adds libruby to the linker flags
|
98
|
+
flags += libruby_args(dest_dir)
|
99
|
+
|
100
|
+
# Make sure ALSR is used on mingw
|
101
|
+
# see https://github.com/rust-lang/rust/pull/75406/files
|
102
|
+
flags += ["-C", "link-arg=-Wl,--dynamicbase"]
|
103
|
+
flags += ["-C", "link-arg=-Wl,--disable-auto-image-base"]
|
104
|
+
|
105
|
+
# If the gem is installed on a host with build tools installed, but is
|
106
|
+
# run on one that isn't the missing libraries will cause the extension
|
107
|
+
# to fail on start.
|
108
|
+
flags += ["-C", "link-arg=-static-libgcc"]
|
109
|
+
end
|
110
|
+
|
111
|
+
flags
|
112
|
+
end
|
113
|
+
|
114
|
+
# We want to use the same linker that Ruby uses, so that the linker flags from
|
115
|
+
# mkmf work properly.
|
116
|
+
def linker_args
|
117
|
+
# Have to handle CC="cl /nologo" on mswin
|
118
|
+
cc_flag = Shellwords.split(makefile_config("CC"))
|
119
|
+
linker = cc_flag.shift
|
120
|
+
link_args = cc_flag.flat_map { |a| ["-C", "link-arg=#{a}"] }
|
121
|
+
|
122
|
+
["-C", "linker=#{linker}", *link_args]
|
123
|
+
end
|
124
|
+
|
125
|
+
def libruby_args(dest_dir)
|
126
|
+
libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED")
|
127
|
+
raw_libs = Shellwords.split(libs)
|
128
|
+
raw_libs.flat_map { |l| ldflag_to_link_modifier(l) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def ruby_static?
|
132
|
+
return true if %w[1 true].include?(ENV["RUBY_STATIC"])
|
133
|
+
|
134
|
+
makefile_config("ENABLE_SHARED") == "no"
|
135
|
+
end
|
136
|
+
|
137
|
+
# Ruby expects the dylib to follow a file name convention for loading
|
138
|
+
def rename_cdylib_for_ruby_compatibility(dest_path)
|
139
|
+
new_path = final_extension_path(dest_path)
|
140
|
+
FileUtils.cp(cargo_dylib_path(dest_path), new_path)
|
141
|
+
new_path
|
142
|
+
end
|
143
|
+
|
144
|
+
def validate_cargo_build!(dir)
|
145
|
+
dylib_path = cargo_dylib_path(dir)
|
146
|
+
|
147
|
+
raise DylibNotFoundError, dir unless File.exist?(dylib_path)
|
148
|
+
|
149
|
+
dylib_path
|
150
|
+
end
|
151
|
+
|
152
|
+
def final_extension_path(dest_path)
|
153
|
+
dylib_path = cargo_dylib_path(dest_path)
|
154
|
+
dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}"
|
155
|
+
dylib_path.gsub(File.basename(dylib_path), dlext_name)
|
156
|
+
end
|
157
|
+
|
158
|
+
def cargo_crate_name
|
159
|
+
spec.metadata.fetch("cargo_crate_name", spec.name).tr("-", "_")
|
160
|
+
end
|
161
|
+
|
162
|
+
def rustc_dynamic_linker_flags(dest_dir)
|
163
|
+
split_flags("DLDFLAGS")
|
164
|
+
.map { |arg| maybe_resolve_ldflag_variable(arg, dest_dir) }
|
165
|
+
.compact
|
166
|
+
.flat_map { |arg| ldflag_to_link_modifier(arg) }
|
167
|
+
end
|
168
|
+
|
169
|
+
def rustc_lib_flags(dest_dir)
|
170
|
+
split_flags("LIBS").flat_map { |arg| ldflag_to_link_modifier(arg) }
|
171
|
+
end
|
172
|
+
|
173
|
+
def split_flags(var)
|
174
|
+
Shellwords.split(RbConfig::CONFIG.fetch(var, ""))
|
175
|
+
end
|
176
|
+
|
177
|
+
def ldflag_to_link_modifier(arg)
|
178
|
+
LinkFlagConverter.convert(arg)
|
179
|
+
end
|
180
|
+
|
181
|
+
def msvc_target?
|
182
|
+
makefile_config("target_os").include?("msvc")
|
183
|
+
end
|
184
|
+
|
185
|
+
def darwin_target?
|
186
|
+
makefile_config("target_os").include?("darwin")
|
187
|
+
end
|
188
|
+
|
189
|
+
def mingw_target?
|
190
|
+
makefile_config("target_os").include?("mingw")
|
191
|
+
end
|
192
|
+
|
193
|
+
def win_target?
|
194
|
+
target_platform = RbConfig::CONFIG["target_os"]
|
195
|
+
!!Gem::WIN_PATTERNS.find { |r| target_platform =~ r }
|
196
|
+
end
|
197
|
+
|
198
|
+
# Interpolate substition vars in the arg (i.e. $(DEFFILE))
|
199
|
+
def maybe_resolve_ldflag_variable(input_arg, dest_dir)
|
200
|
+
var_matches = input_arg.match(/\$\((\w+)\)/)
|
201
|
+
|
202
|
+
return input_arg unless var_matches
|
203
|
+
|
204
|
+
var_name = var_matches[1]
|
205
|
+
|
206
|
+
return input_arg if var_name.nil? || var_name.chomp.empty?
|
207
|
+
|
208
|
+
case var_name
|
209
|
+
# On windows, it is assumed that mkmf has setup an exports file for the
|
210
|
+
# extension, so we have to to create one ourselves.
|
211
|
+
when "DEFFILE"
|
212
|
+
write_deffile(dest_dir)
|
213
|
+
else
|
214
|
+
RbConfig::CONFIG[var_name]
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def write_deffile(dest_path)
|
219
|
+
dest_dir = File.dirname(final_extension_path(dest_path))
|
220
|
+
FileUtils.mkdir_p(dest_dir)
|
221
|
+
deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def")
|
222
|
+
export_prefix = makefile_config("EXPORT_PREFIX") || ""
|
223
|
+
|
224
|
+
File.open(deffile_path, "w") do |f|
|
225
|
+
f.puts "EXPORTS"
|
226
|
+
f.puts "#{export_prefix.strip}Init_#{spec.name}"
|
227
|
+
end
|
228
|
+
|
229
|
+
deffile_path
|
230
|
+
end
|
231
|
+
|
232
|
+
# We have to basically reimplement RbConfig::CONFIG['SOEXT'] here to support
|
233
|
+
# Ruby < 2.5
|
234
|
+
#
|
235
|
+
# @see https://github.com/ruby/ruby/blob/c87c027f18c005460746a74c07cd80ee355b16e4/configure.ac#L3185
|
236
|
+
def so_ext
|
237
|
+
return RbConfig::CONFIG["SOEXT"] if RbConfig::CONFIG.key?("SOEXT")
|
238
|
+
|
239
|
+
if win_target?
|
240
|
+
"dll"
|
241
|
+
elsif darwin_target?
|
242
|
+
"dylib"
|
243
|
+
else
|
244
|
+
"so"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Corresponds to $(LIBPATH) in mkmf
|
249
|
+
def mkmf_libpath
|
250
|
+
["-L", "native=#{makefile_config("libdir")}"]
|
251
|
+
end
|
252
|
+
|
253
|
+
def makefile_config(var_name)
|
254
|
+
val = RbConfig::MAKEFILE_CONFIG[var_name]
|
255
|
+
|
256
|
+
return unless val
|
257
|
+
|
258
|
+
RbConfig.expand(val.dup)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Good balance between binary size and debugability
|
262
|
+
def debug_flags
|
263
|
+
return [] if profile == :dev
|
264
|
+
|
265
|
+
["-C", "debuginfo=1"]
|
266
|
+
end
|
267
|
+
|
268
|
+
# Copied from ExtConfBuilder
|
269
|
+
def finalize_directory(dest_path, lib_dir, extension_dir)
|
270
|
+
require "fileutils"
|
271
|
+
require "tempfile"
|
272
|
+
|
273
|
+
ext_path = final_extension_path(dest_path)
|
274
|
+
|
275
|
+
begin
|
276
|
+
tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
|
277
|
+
|
278
|
+
# Some versions of `mktmpdir` return absolute paths, which will break make
|
279
|
+
# if the paths contain spaces. However, on Ruby 1.9.x on Windows, relative
|
280
|
+
# paths cause all C extension builds to fail.
|
281
|
+
#
|
282
|
+
# As such, we convert to a relative path unless we are using Ruby 1.9.x on
|
283
|
+
# Windows. This means that when using Ruby 1.9.x on Windows, paths with
|
284
|
+
# spaces do not work.
|
285
|
+
#
|
286
|
+
# Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940
|
287
|
+
tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
|
288
|
+
|
289
|
+
if tmp_dest_relative
|
290
|
+
full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
|
291
|
+
|
292
|
+
# TODO: remove in RubyGems 3
|
293
|
+
if Gem.install_extension_in_lib && lib_dir
|
294
|
+
FileUtils.mkdir_p lib_dir
|
295
|
+
FileUtils.cp_r ext_path, lib_dir, remove_destination: true
|
296
|
+
end
|
297
|
+
|
298
|
+
FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
|
299
|
+
destent = ent.class.new(dest_path, ent.rel)
|
300
|
+
destent.exist? || FileUtils.mv(ent.path, destent.path)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
ensure
|
304
|
+
FileUtils.rm_rf tmp_dest if tmp_dest
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def get_relative_path(path, base)
|
309
|
+
path[0..base.length - 1] = "." if path.start_with?(base)
|
310
|
+
path
|
311
|
+
end
|
312
|
+
|
313
|
+
def profile_target_directory
|
314
|
+
case profile
|
315
|
+
when :release then "release"
|
316
|
+
when :dev then "debug"
|
317
|
+
else raise "unknown target directory for profile: #{profile}"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Error raised when no cdylib artifact was created
|
322
|
+
class DylibNotFoundError < StandardError
|
323
|
+
def initialize(dir)
|
324
|
+
files = Dir.glob(File.join(dir, "**", "*")).map { |f| "- #{f}" }.join "\n"
|
325
|
+
|
326
|
+
super <<~MSG
|
327
|
+
Dynamic library not found for Rust extension (in #{dir})
|
328
|
+
|
329
|
+
Make sure you set "crate-type" in Cargo.toml to "cdylib"
|
330
|
+
|
331
|
+
Found files:
|
332
|
+
#{files}
|
333
|
+
MSG
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rb_sys
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ian Ker-Seymer
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
Rl+ASkq2/1i07TkBpCf+2hq66+h/hx+/Y/KrUzXfe0jtvil0WESkJT2kqRqHWNhD
|
31
31
|
9GKBxaQlXokNDtWCm1/gl6cD8WRZ0N5S4ZGJT1FLLsA=
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2022-
|
33
|
+
date: 2022-05-25 00:00:00.000000000 Z
|
34
34
|
dependencies: []
|
35
35
|
description:
|
36
36
|
email:
|
@@ -43,11 +43,11 @@ files:
|
|
43
43
|
- LICENSE-MIT
|
44
44
|
- certs/ianks.pem
|
45
45
|
- lib/rb_sys.rb
|
46
|
-
- lib/rb_sys/cargo_builder.rb
|
47
|
-
- lib/rb_sys/cargo_builder/link_flag_converter.rb
|
48
46
|
- lib/rb_sys/mkmf.rb
|
49
47
|
- lib/rb_sys/version.rb
|
50
48
|
- sig/rb_sys.rbs
|
49
|
+
- vendor/rubygems/ext/cargo_builder.rb
|
50
|
+
- vendor/rubygems/ext/cargo_builder/link_flag_converter.rb
|
51
51
|
homepage: https://github.com/oxidize-rb/rb-sys
|
52
52
|
licenses:
|
53
53
|
- MIT
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,25 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RbSys
|
4
|
-
class CargoBuilder
|
5
|
-
# Converts Ruby link flags into something cargo understands
|
6
|
-
class LinkFlagConverter
|
7
|
-
def self.convert(arg)
|
8
|
-
case arg.chomp
|
9
|
-
when /^-L\s*(.+)$/
|
10
|
-
["-L", "native=#{$1}"]
|
11
|
-
when /^--library=(\w+\S+)$/, /^-l\s*(\w+\S+)$/
|
12
|
-
["-l", $1]
|
13
|
-
when /^-l\s*:lib(\S+).a$/
|
14
|
-
["-l", "static=#{$1}"]
|
15
|
-
when /^-l\s*:lib(\S+).(so|dylib|dll)$/
|
16
|
-
["-l", "dylib=#{$1}"]
|
17
|
-
when /^-F\s*(.*)$/
|
18
|
-
["-l", "framework=#{$1}"]
|
19
|
-
else
|
20
|
-
["-C", "link_arg=#{arg}"]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/lib/rb_sys/cargo_builder.rb
DELETED
@@ -1,328 +0,0 @@
|
|
1
|
-
module RbSys
|
2
|
-
class CargoBuilder < Gem::Ext::Builder
|
3
|
-
attr_accessor :spec, :runner, :profile, :env, :features, :target, :extra_rustc_args, :dry_run, :ext_dir
|
4
|
-
|
5
|
-
def initialize(spec)
|
6
|
-
require "rubygems/command"
|
7
|
-
require_relative "cargo_builder/link_flag_converter"
|
8
|
-
|
9
|
-
@spec = spec
|
10
|
-
@runner = self.class.method(:run)
|
11
|
-
@profile = ENV.fetch("RB_SYS_CARGO_BUILD_PROFILE", :release).to_sym
|
12
|
-
@env = {}
|
13
|
-
@features = []
|
14
|
-
@target = ENV["CARGO_BUILD_TARGET"]
|
15
|
-
@extra_rustc_args = []
|
16
|
-
@dry_run = true
|
17
|
-
@ext_dir = nil
|
18
|
-
end
|
19
|
-
|
20
|
-
def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
|
21
|
-
require "fileutils"
|
22
|
-
require "shellwords"
|
23
|
-
|
24
|
-
build_crate(dest_path, results, args, cargo_dir)
|
25
|
-
validate_cargo_build!(dest_path)
|
26
|
-
rename_cdylib_for_ruby_compatibility(dest_path)
|
27
|
-
finalize_directory(dest_path, lib_dir, cargo_dir)
|
28
|
-
results
|
29
|
-
end
|
30
|
-
|
31
|
-
def build_crate(dest_path, results, args, cargo_dir)
|
32
|
-
env = build_env
|
33
|
-
cmd = cargo_command(dest_path, args)
|
34
|
-
runner.call cmd, results, "cargo", cargo_dir, env
|
35
|
-
|
36
|
-
results
|
37
|
-
end
|
38
|
-
|
39
|
-
def build_env
|
40
|
-
build_env = rb_config_env
|
41
|
-
build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?("RUBY_STATIC")
|
42
|
-
build_env.merge(env)
|
43
|
-
end
|
44
|
-
|
45
|
-
def cargo_command(dest_path, args = [])
|
46
|
-
manifest = File.join(ext_dir, "Cargo.toml")
|
47
|
-
cargo = ENV.fetch("CARGO", "cargo")
|
48
|
-
|
49
|
-
cmd = []
|
50
|
-
cmd += [cargo, "rustc"]
|
51
|
-
cmd += ["--target", target] if target
|
52
|
-
cmd += ["--target-dir", dest_path]
|
53
|
-
cmd += ["--features", features.join(",")] unless features.empty?
|
54
|
-
cmd += ["--manifest-path", manifest]
|
55
|
-
cmd += ["--lib"]
|
56
|
-
cmd += ["--profile", profile.to_s]
|
57
|
-
cmd += Gem::Command.build_args
|
58
|
-
cmd += args
|
59
|
-
cmd += ["--"]
|
60
|
-
cmd += [*cargo_rustc_args(dest_path)]
|
61
|
-
cmd += extra_rustc_args
|
62
|
-
cmd
|
63
|
-
end
|
64
|
-
|
65
|
-
def cargo_dylib_path(dest_path)
|
66
|
-
prefix = so_ext == "dll" ? "" : "lib"
|
67
|
-
path_parts = [dest_path]
|
68
|
-
path_parts << target if target
|
69
|
-
path_parts += [profile_target_directory, "#{prefix}#{cargo_crate_name}.#{so_ext}"]
|
70
|
-
File.join(*path_parts)
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def rb_config_env
|
76
|
-
result = {}
|
77
|
-
RbConfig::CONFIG.each { |k, v| result["RBCONFIG_#{k}"] = v }
|
78
|
-
result
|
79
|
-
end
|
80
|
-
|
81
|
-
def cargo_rustc_args(dest_dir)
|
82
|
-
[
|
83
|
-
*linker_args,
|
84
|
-
*mkmf_libpath,
|
85
|
-
*rustc_dynamic_linker_flags(dest_dir),
|
86
|
-
*rustc_lib_flags(dest_dir),
|
87
|
-
*platform_specific_rustc_args(dest_dir)
|
88
|
-
]
|
89
|
-
end
|
90
|
-
|
91
|
-
def platform_specific_rustc_args(dest_dir, flags = [])
|
92
|
-
if mingw_target?
|
93
|
-
# On mingw platforms, mkmf adds libruby to the linker flags
|
94
|
-
flags += libruby_args(dest_dir)
|
95
|
-
|
96
|
-
# Make sure ALSR is used on mingw
|
97
|
-
# see https://github.com/rust-lang/rust/pull/75406/files
|
98
|
-
flags += ["-C", "link-arg=-Wl,--dynamicbase"]
|
99
|
-
flags += ["-C", "link-arg=-Wl,--disable-auto-image-base"]
|
100
|
-
|
101
|
-
# If the gem is installed on a host with build tools installed, but is
|
102
|
-
# run on one that isn't the missing libraries will cause the extension
|
103
|
-
# to fail on start.
|
104
|
-
flags += ["-C", "link-arg=-static-libgcc"]
|
105
|
-
end
|
106
|
-
|
107
|
-
flags
|
108
|
-
end
|
109
|
-
|
110
|
-
# We want to use the same linker that Ruby uses, so that the linker flags from
|
111
|
-
# mkmf work properly.
|
112
|
-
def linker_args
|
113
|
-
# Have to handle CC="cl /nologo" on mswin
|
114
|
-
cc_flag = Shellwords.split(makefile_config("CC"))
|
115
|
-
linker = cc_flag.shift
|
116
|
-
link_args = cc_flag.flat_map { |a| ["-C", "link-arg=#{a}"] }
|
117
|
-
|
118
|
-
["-C", "linker=#{linker}", *link_args]
|
119
|
-
end
|
120
|
-
|
121
|
-
def libruby_args(dest_dir)
|
122
|
-
libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED")
|
123
|
-
raw_libs = Shellwords.split(libs)
|
124
|
-
raw_libs.flat_map { |l| ldflag_to_link_modifier(l) }
|
125
|
-
end
|
126
|
-
|
127
|
-
def ruby_static?
|
128
|
-
return true if %w[1 true].include?(ENV["RUBY_STATIC"])
|
129
|
-
|
130
|
-
makefile_config("ENABLE_SHARED") == "no"
|
131
|
-
end
|
132
|
-
|
133
|
-
# Ruby expects the dylib to follow a file name convention for loading
|
134
|
-
def rename_cdylib_for_ruby_compatibility(dest_path)
|
135
|
-
new_path = final_extension_path(dest_path)
|
136
|
-
FileUtils.cp(cargo_dylib_path(dest_path), new_path)
|
137
|
-
new_path
|
138
|
-
end
|
139
|
-
|
140
|
-
def validate_cargo_build!(dir)
|
141
|
-
dylib_path = cargo_dylib_path(dir)
|
142
|
-
|
143
|
-
raise DylibNotFoundError, dir unless File.exist?(dylib_path)
|
144
|
-
|
145
|
-
dylib_path
|
146
|
-
end
|
147
|
-
|
148
|
-
def final_extension_path(dest_path)
|
149
|
-
dylib_path = cargo_dylib_path(dest_path)
|
150
|
-
dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}"
|
151
|
-
dylib_path.gsub(File.basename(dylib_path), dlext_name)
|
152
|
-
end
|
153
|
-
|
154
|
-
def cargo_crate_name
|
155
|
-
spec.metadata.fetch("cargo_crate_name", spec.name).tr("-", "_")
|
156
|
-
end
|
157
|
-
|
158
|
-
def rustc_dynamic_linker_flags(dest_dir)
|
159
|
-
split_flags("DLDFLAGS")
|
160
|
-
.map { |arg| maybe_resolve_ldflag_variable(arg, dest_dir) }
|
161
|
-
.compact
|
162
|
-
.flat_map { |arg| ldflag_to_link_modifier(arg) }
|
163
|
-
end
|
164
|
-
|
165
|
-
def rustc_lib_flags(dest_dir)
|
166
|
-
split_flags("LIBS").flat_map { |arg| ldflag_to_link_modifier(arg) }
|
167
|
-
end
|
168
|
-
|
169
|
-
def split_flags(var)
|
170
|
-
Shellwords.split(RbConfig::CONFIG.fetch(var, ""))
|
171
|
-
end
|
172
|
-
|
173
|
-
def ldflag_to_link_modifier(arg)
|
174
|
-
LinkFlagConverter.convert(arg)
|
175
|
-
end
|
176
|
-
|
177
|
-
def msvc_target?
|
178
|
-
makefile_config("target_os").include?("msvc")
|
179
|
-
end
|
180
|
-
|
181
|
-
def darwin_target?
|
182
|
-
makefile_config("target_os").include?("darwin")
|
183
|
-
end
|
184
|
-
|
185
|
-
def mingw_target?
|
186
|
-
makefile_config("target_os").include?("mingw")
|
187
|
-
end
|
188
|
-
|
189
|
-
def win_target?
|
190
|
-
target_platform = RbConfig::CONFIG["target_os"]
|
191
|
-
!!Gem::WIN_PATTERNS.find { |r| target_platform =~ r }
|
192
|
-
end
|
193
|
-
|
194
|
-
# Interpolate substition vars in the arg (i.e. $(DEFFILE))
|
195
|
-
def maybe_resolve_ldflag_variable(input_arg, dest_dir)
|
196
|
-
var_matches = input_arg.match(/\$\((\w+)\)/)
|
197
|
-
|
198
|
-
return input_arg unless var_matches
|
199
|
-
|
200
|
-
var_name = var_matches[1]
|
201
|
-
|
202
|
-
return input_arg if var_name.nil? || var_name.chomp.empty?
|
203
|
-
|
204
|
-
case var_name
|
205
|
-
# On windows, it is assumed that mkmf has setup an exports file for the
|
206
|
-
# extension, so we have to to create one ourselves.
|
207
|
-
when "DEFFILE"
|
208
|
-
write_deffile(dest_dir)
|
209
|
-
else
|
210
|
-
RbConfig::CONFIG[var_name]
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
def write_deffile(dest_path)
|
215
|
-
dest_dir = File.dirname(final_extension_path(dest_path))
|
216
|
-
FileUtils.mkdir_p(dest_dir)
|
217
|
-
deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def")
|
218
|
-
export_prefix = makefile_config("EXPORT_PREFIX") || ""
|
219
|
-
|
220
|
-
unless dry_run
|
221
|
-
File.open(deffile_path, "w") do |f|
|
222
|
-
f.puts "EXPORTS"
|
223
|
-
f.puts "#{export_prefix.strip}Init_#{spec.name}"
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
deffile_path
|
228
|
-
end
|
229
|
-
|
230
|
-
# We have to basically reimplement RbConfig::CONFIG['SOEXT'] here to support
|
231
|
-
# Ruby < 2.5
|
232
|
-
#
|
233
|
-
# @see https://github.com/ruby/ruby/blob/c87c027f18c005460746a74c07cd80ee355b16e4/configure.ac#L3185
|
234
|
-
def so_ext
|
235
|
-
return RbConfig::CONFIG["SOEXT"] if RbConfig::CONFIG.key?("SOEXT")
|
236
|
-
|
237
|
-
if win_target?
|
238
|
-
"dll"
|
239
|
-
elsif darwin_target?
|
240
|
-
"dylib"
|
241
|
-
else
|
242
|
-
"so"
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
# Corresponds to $(LIBPATH) in mkmf
|
247
|
-
def mkmf_libpath
|
248
|
-
["-L", "native=#{makefile_config("libdir")}"]
|
249
|
-
end
|
250
|
-
|
251
|
-
def makefile_config(var_name)
|
252
|
-
val = RbConfig::MAKEFILE_CONFIG[var_name]
|
253
|
-
|
254
|
-
return unless val
|
255
|
-
|
256
|
-
RbConfig.expand(val.dup)
|
257
|
-
end
|
258
|
-
|
259
|
-
# Copied from ExtConfBuilder
|
260
|
-
def finalize_directory(dest_path, lib_dir, extension_dir)
|
261
|
-
require "fileutils"
|
262
|
-
require "tempfile"
|
263
|
-
|
264
|
-
ext_path = final_extension_path(dest_path)
|
265
|
-
|
266
|
-
begin
|
267
|
-
tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
|
268
|
-
|
269
|
-
# Some versions of `mktmpdir` return absolute paths, which will break make
|
270
|
-
# if the paths contain spaces. However, on Ruby 1.9.x on Windows, relative
|
271
|
-
# paths cause all C extension builds to fail.
|
272
|
-
#
|
273
|
-
# As such, we convert to a relative path unless we are using Ruby 1.9.x on
|
274
|
-
# Windows. This means that when using Ruby 1.9.x on Windows, paths with
|
275
|
-
# spaces do not work.
|
276
|
-
#
|
277
|
-
# Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940
|
278
|
-
tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
|
279
|
-
|
280
|
-
if tmp_dest_relative
|
281
|
-
full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
|
282
|
-
|
283
|
-
# TODO: remove in RubyGems 3
|
284
|
-
if Gem.install_extension_in_lib && lib_dir
|
285
|
-
FileUtils.mkdir_p lib_dir
|
286
|
-
FileUtils.cp_r ext_path, lib_dir, remove_destination: true
|
287
|
-
end
|
288
|
-
|
289
|
-
FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
|
290
|
-
destent = ent.class.new(dest_path, ent.rel)
|
291
|
-
destent.exist? || FileUtils.mv(ent.path, destent.path)
|
292
|
-
end
|
293
|
-
end
|
294
|
-
ensure
|
295
|
-
FileUtils.rm_rf tmp_dest if tmp_dest
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
def get_relative_path(path, base)
|
300
|
-
path[0..base.length - 1] = "." if path.start_with?(base)
|
301
|
-
path
|
302
|
-
end
|
303
|
-
|
304
|
-
def profile_target_directory
|
305
|
-
case profile.to_sym
|
306
|
-
when :release then "release"
|
307
|
-
when :dev then "debug"
|
308
|
-
else raise "unknown target directory for profile: #{profile}"
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
|
-
# Error raised when no cdylib artifact was created
|
313
|
-
class DylibNotFoundError < StandardError
|
314
|
-
def initialize(dir)
|
315
|
-
files = Dir.glob(File.join(dir, "**", "*")).map { |f| "- #{f}" }.join "\n"
|
316
|
-
|
317
|
-
super <<~MSG
|
318
|
-
Dynamic library not found for Rust extension (in #{dir})
|
319
|
-
|
320
|
-
Make sure you set "crate-type" in Cargo.toml to "cdylib"
|
321
|
-
|
322
|
-
Found files:
|
323
|
-
#{files}
|
324
|
-
MSG
|
325
|
-
end
|
326
|
-
end
|
327
|
-
end
|
328
|
-
end
|