rb_sys 0.1.14 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d969efd4bca62e26a6a7712b64b3332f38fb461dfced29c6121246909ef5f2b
4
- data.tar.gz: 0b0000f89e5d9236cdc8ce829dcf930c37a2e57935612ffaa8533b09854eec1f
3
+ metadata.gz: 27affe876fb5901792fe8af81776d7b6b6094b7b97849c1a8894296c6d119f77
4
+ data.tar.gz: a1e59e9580925d7d0a470ec33c72f4fe296148efacb546901d40b4fb99c543bf
5
5
  SHA512:
6
- metadata.gz: 0d93ec24df3af15692ae906a6415854dcdb3c791e7a19228c0d127d92f4853292e435bc32f0ff2748839ad9e2119fea1c5facdf3623676072ea6abe2035a4fa4
7
- data.tar.gz: 825eb9ed1e5e65fd75869e8302a4b6184281c176e0cad3d460f14b02d471548fa26cb36a68a433d1d6fb9839fa0bb0da80e5e3be55eb38cb596f9997e198209c
6
+ metadata.gz: acfa547f5b88a8274c384a6d72953b22eaf5c8b551092caac377a533d80160a1f9fb76dbdf09668957dbe016ed78b6c1e5bfd57bb0ccfaf1cbab85b675353046
7
+ data.tar.gz: c56ed902664615dd777a9611fcbe3b7ae2229a8832690c47b568340df9746278939fd82d3b05dea1c7b258116ce2deb59a1c30ac954b648ddb552b922e70eef8
checksums.yaml.gz.sig CHANGED
@@ -1 +1,3 @@
1
- .
1
+ �r=6m����9�.7�@��E�ϐѠ�;�
2
+ �������U&���?�xQ��ۛy\���寶X�6�2�M��FRD$�� q4I�k��,��,���~�@�5@A��t`l�u�Mr���
3
+ j<�
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('RB_SYS_CARGO_PROFILE', :dev).to_sym
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 = "$(srcdir)/#{builder.ext_dir}".chomp("/")
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 = +<<~MAKE
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
- TARGET_NAME = #{target[/\A\w+/]}
69
- TARGET_ENTRY = #{RbConfig::CONFIG["EXPORT_PREFIX"]}Init_$(TARGET_NAME)
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
- TARGET_DIR = #{Dir.pwd}/target/$(RB_SYS_TARGET_DIR)
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
- $(TARGET_DIR):
84
- \t$(ECHO) creating target directory \\($(@)\\)
85
- \t$(Q) $(MAKEDIRS) $(TARGET_DIR)
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
- $(DLLIB): $(DEFFILE) FORCE
92
- \t$(ECHO) generating $(@) \\("$(RB_SYS_CARGO_PROFILE)"\\)
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 = ARGV.dup
124
- args.shift if args.first == "--"
125
- cargo_cmd = builder.cargo_command(dest_path, args)
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RbSys
4
- VERSION = "0.1.14"
4
+ VERSION = "0.9.0"
5
5
  end
@@ -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.1.14
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-06-28 00:00:00.000000000 Z
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
@@ -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