rb_sys1 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5fa473b69ad2496084571b1f592c94a1b2c2928ff98211b94961b489ff1e5392
4
+ data.tar.gz: 519bf8fb1a5a6420d3c146e29885f6e583918653f9bee99f7b02a7beea4c4159
5
+ SHA512:
6
+ metadata.gz: 67227d00cc8c812876b944a62604441946906a6913708133e5d449e2bedb181680c3b8cc6f6423c67f37d25a26eaf6ea0328042c7448e1b7685ef768dd7dec3e
7
+ data.tar.gz: bc7def6f80d2e0f5e888bf572701c171e76f4def3b7e428b9825f163c36a2888d4f7ec31e2c59ee78cf519b56b91a43aac1fb39a0127123e1995ed630bbc10d4
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "rb_sys"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/certs/ianks.pem ADDED
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDfDCCAmSgAwIBAgIBATANBgkqhkiG9w0BAQUFADBCMRQwEgYDVQQDDAtpLmtl
3
+ cnNleW1lcjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYD
4
+ Y29tMB4XDTIyMDQxMzE5NTY1MVoXDTIzMDQxMzE5NTY1MVowQjEUMBIGA1UEAwwL
5
+ aS5rZXJzZXltZXIxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixk
6
+ ARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMJ2pG+er4cP
7
+ PasxsMIKL9/tmLL4gh80EMuF3SCS0qZoh+Oo8dkvRYxW8NXdwEIcp3cCNgE+5G+J
8
+ TCMOVF8S15n1Z1P7xxXiXxa/BIofKKbtatVRngm14uR/6pjdkvLXqlrWdS57bNwv
9
+ 7LtpzYVfDHfsl/qRWaEi4jq00PNCRSWjcva8teqswjBg8KlwGtyygpezPbVSWP8Y
10
+ vzWZmVF7fqRBXU78Ah0+pNOhslBXDTvI3xJdN4hQ3H7rLjpD/qxKWq/8o+Qvx6cX
11
+ dNZ3ugH/Pr3BAsqt4JFLXin9AK7PO9GDMH5JXJrUb+hAt2VNIZqpz9VlKA6BA0jN
12
+ eWGea+yCZkECAwEAAaN9MHswCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
13
+ BBYEFOkrF6hsocaIMOjR/K3JBzyXCLJPMCAGA1UdEQQZMBeBFWkua2Vyc2V5bWVy
14
+ QGdtYWlsLmNvbTAgBgNVHRIEGTAXgRVpLmtlcnNleW1lckBnbWFpbC5jb20wDQYJ
15
+ KoZIhvcNAQEFBQADggEBADHnvHbZn5nldcpArIQcFF9URCBloCBZHqLFMhlGj5BX
16
+ bFvuoq4CBmEFiJpTazeoEaxPlsmIK1+4CqBXwg8lHEMC9RV5g9QtJHQry2eFE/8U
17
+ WrgCbqdxb8HBl9ayOHRcdfcv0RHBlmBHwBOoQ+hXMlInjssjfIX4BS39mTYefclE
18
+ zxOFntl+vb4kluo0j9/BGzdnGCo7iE+GfGSENFdOn2J38RCDLUzgOCmCtxzSvldq
19
+ Rl+ASkq2/1i07TkBpCf+2hq66+h/hx+/Y/KrUzXfe0jtvil0WESkJT2kqRqHWNhD
20
+ 9GKBxaQlXokNDtWCm1/gl6cD8WRZ0N5S4ZGJT1FLLsA=
21
+ -----END CERTIFICATE-----
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems/ext"
4
+ require "shellwords"
5
+ require_relative "./../../vendor/rubygems/ext/cargo_builder"
6
+
7
+ # Root module
8
+ module RbSys
9
+ # Helpers for creating a Ruby compatible makefile for Rust
10
+ module Mkmf
11
+ def create_rust_makefile(target, srcprefix = nil)
12
+ if target.include?("/")
13
+ target_prefix, target = File.split(target)
14
+ target_prefix[0, 0] = "/"
15
+ else
16
+ target_prefix = ""
17
+ end
18
+
19
+ spec = Struct.new(:name, :metadata).new(target, {})
20
+ builder = Gem::Ext::CargoBuilder.new(spec)
21
+ srcprefix ||= "$(srcdir)/#{srcprefix}".chomp("/")
22
+ RbConfig.expand(srcdir = srcprefix.dup)
23
+
24
+ # rubocop:disable Style/GlobalVars
25
+ make_install = <<~MAKE
26
+ target_prefix = #{target_prefix}
27
+ CARGO_PROFILE = release
28
+ CLEANLIBS = target/ $(RUSTLIB) $(DLLIB)
29
+ RUBYARCHDIR = $(sitearchdir)$(target_prefix)
30
+ RUSTLIB = #{dllib_path(builder)}
31
+ TARGET = #{target}
32
+ DLLIB = $(TARGET).#{RbConfig::CONFIG["DLEXT"]}
33
+
34
+ #{base_makefile(srcdir)}
35
+
36
+ #{env_vars(builder)}
37
+
38
+ FORCE: ;
39
+
40
+ $(DLLIB): FORCE
41
+ \t#{cargo_command(srcdir, builder)}
42
+ \t$(COPY) "$(RUSTLIB)" $@
43
+
44
+ install: $(DLLIB)
45
+ \t$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
46
+
47
+ all: #{$extout ? "install" : "$(DLLIB)"}
48
+ MAKE
49
+
50
+ File.write("Makefile", make_install)
51
+ end
52
+ # rubocop:enable Style/GlobalVars
53
+
54
+ private
55
+
56
+ def base_makefile(cargo_dir)
57
+ base_makefile = dummy_makefile(__dir__).join("\n")
58
+ base_makefile.gsub!("all install static install-so install-rb", "all static install-so install-rb")
59
+ base_makefile.gsub!("clean-so::", "clean-so:\n\t-$(Q)$(RM) $(DLLIB)\n")
60
+ base_makefile.gsub!(/^srcdir = .*$/, "srcdir = #{cargo_dir}")
61
+ base_makefile
62
+ end
63
+
64
+ def cargo_command(cargo_dir, builder)
65
+ dest_path = File.join(Dir.pwd, "target")
66
+ args = []
67
+ cargo_cmd = builder.cargo_command(cargo_dir, dest_path, args)
68
+ Shellwords.join(cargo_cmd).gsub("\\=", "=")
69
+ end
70
+
71
+ def env_vars(builder)
72
+ builder.build_env.map { |k, v| %($(DLLIB): export #{k} = #{v.gsub("\n", '\n')}) }.join("\n")
73
+ end
74
+
75
+ def dllib_path(builder)
76
+ builder.cargo_dylib_path(File.join(Dir.pwd, "target"))
77
+ end
78
+ end
79
+ end
80
+
81
+ include RbSys::Mkmf # rubocop:disable Style/MixinUsage
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RbSys
4
+ VERSION = "0.1.0"
5
+ end
data/lib/rb_sys.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rb_sys/version"
4
+
5
+ module RbSys
6
+ class Error < StandardError; end
7
+ # Your code goes here...
8
+ end
data/sig/rb_sys.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module RbSys
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ 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,330 @@
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
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 = :release
16
+ end
17
+
18
+ def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
19
+ require "fileutils"
20
+ require "shellwords"
21
+
22
+ build_crate(dest_path, results, args, cargo_dir)
23
+ validate_cargo_build!(dest_path)
24
+ rename_cdylib_for_ruby_compatibility(dest_path)
25
+ finalize_directory(dest_path, lib_dir, cargo_dir)
26
+ results
27
+ end
28
+
29
+ def build_crate(dest_path, results, args, cargo_dir)
30
+ env = build_env
31
+ cmd = cargo_command(cargo_dir, dest_path, args)
32
+ runner.call cmd, results, "cargo", cargo_dir, env
33
+
34
+ results
35
+ end
36
+
37
+ def build_env
38
+ build_env = rb_config_env
39
+ build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?("RUBY_STATIC")
40
+ build_env
41
+ end
42
+
43
+ def cargo_command(cargo_dir, dest_path, args = [])
44
+ manifest = File.join(cargo_dir, "Cargo.toml")
45
+ cargo = ENV.fetch("CARGO", "cargo")
46
+
47
+ cmd = []
48
+ cmd += [cargo, "rustc"]
49
+ cmd += ["--target", ENV["CARGO_BUILD_TARGET"]] if ENV["CARGO_BUILD_TARGET"]
50
+ cmd += ["--target-dir", dest_path]
51
+ cmd += ["--manifest-path", manifest]
52
+ cmd += ["--lib"]
53
+ cmd += ["--profile", profile.to_s]
54
+ cmd += ["--locked"] if profile == :release
55
+ cmd += Gem::Command.build_args
56
+ cmd += args
57
+ cmd += ["--"]
58
+ cmd += [*cargo_rustc_args(dest_path)]
59
+ cmd
60
+ end
61
+
62
+ def cargo_dylib_path(dest_path)
63
+ prefix = so_ext == "dll" ? "" : "lib"
64
+ path_parts = [dest_path]
65
+ path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"]
66
+ path_parts += [profile_target_directory, "#{prefix}#{cargo_crate_name}.#{so_ext}"]
67
+ File.join(*path_parts)
68
+ end
69
+
70
+ private
71
+
72
+ def rb_config_env
73
+ result = {}
74
+ RbConfig::CONFIG.each { |k, v| result["RBCONFIG_#{k}"] = v }
75
+ result
76
+ end
77
+
78
+ def cargo_rustc_args(dest_dir)
79
+ [
80
+ *linker_args,
81
+ *mkmf_libpath,
82
+ *rustc_dynamic_linker_flags(dest_dir),
83
+ *rustc_lib_flags(dest_dir),
84
+ *platform_specific_rustc_args(dest_dir),
85
+ *debug_flags
86
+ ]
87
+ end
88
+
89
+ def platform_specific_rustc_args(dest_dir, flags = [])
90
+ if mingw_target?
91
+ # On mingw platforms, mkmf adds libruby to the linker flags
92
+ flags += libruby_args(dest_dir)
93
+
94
+ # Make sure ALSR is used on mingw
95
+ # see https://github.com/rust-lang/rust/pull/75406/files
96
+ flags += ["-C", "link-arg=-Wl,--dynamicbase"]
97
+ flags += ["-C", "link-arg=-Wl,--disable-auto-image-base"]
98
+
99
+ # If the gem is installed on a host with build tools installed, but is
100
+ # run on one that isn't the missing libraries will cause the extension
101
+ # to fail on start.
102
+ flags += ["-C", "link-arg=-static-libgcc"]
103
+ end
104
+
105
+ flags
106
+ end
107
+
108
+ # We want to use the same linker that Ruby uses, so that the linker flags from
109
+ # mkmf work properly.
110
+ def linker_args
111
+ # Have to handle CC="cl /nologo" on mswin
112
+ cc_flag = Shellwords.split(makefile_config("CC"))
113
+ linker = cc_flag.shift
114
+ link_args = cc_flag.flat_map { |a| ["-C", "link-arg=#{a}"] }
115
+
116
+ ["-C", "linker=#{linker}", *link_args]
117
+ end
118
+
119
+ def libruby_args(dest_dir)
120
+ libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED")
121
+ raw_libs = Shellwords.split(libs)
122
+ raw_libs.flat_map { |l| ldflag_to_link_modifier(l) }
123
+ end
124
+
125
+ def ruby_static?
126
+ return true if %w[1 true].include?(ENV["RUBY_STATIC"])
127
+
128
+ makefile_config("ENABLE_SHARED") == "no"
129
+ end
130
+
131
+ # Ruby expects the dylib to follow a file name convention for loading
132
+ def rename_cdylib_for_ruby_compatibility(dest_path)
133
+ new_path = final_extension_path(dest_path)
134
+ FileUtils.cp(cargo_dylib_path(dest_path), new_path)
135
+ new_path
136
+ end
137
+
138
+ def validate_cargo_build!(dir)
139
+ dylib_path = cargo_dylib_path(dir)
140
+
141
+ raise DylibNotFoundError, dir unless File.exist?(dylib_path)
142
+
143
+ dylib_path
144
+ end
145
+
146
+ def final_extension_path(dest_path)
147
+ dylib_path = cargo_dylib_path(dest_path)
148
+ dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}"
149
+ dylib_path.gsub(File.basename(dylib_path), dlext_name)
150
+ end
151
+
152
+ def cargo_crate_name
153
+ spec.metadata.fetch("cargo_crate_name", spec.name).tr("-", "_")
154
+ end
155
+
156
+ def rustc_dynamic_linker_flags(dest_dir)
157
+ split_flags("DLDFLAGS")
158
+ .map { |arg| maybe_resolve_ldflag_variable(arg, dest_dir) }
159
+ .compact
160
+ .flat_map { |arg| ldflag_to_link_modifier(arg) }
161
+ end
162
+
163
+ def rustc_lib_flags(dest_dir)
164
+ split_flags("LIBS").flat_map { |arg| ldflag_to_link_modifier(arg) }
165
+ end
166
+
167
+ def split_flags(var)
168
+ Shellwords.split(RbConfig::CONFIG.fetch(var, ""))
169
+ end
170
+
171
+ def ldflag_to_link_modifier(arg)
172
+ LinkFlagConverter.convert(arg)
173
+ end
174
+
175
+ def msvc_target?
176
+ makefile_config("target_os").include?("msvc")
177
+ end
178
+
179
+ def darwin_target?
180
+ makefile_config("target_os").include?("darwin")
181
+ end
182
+
183
+ def mingw_target?
184
+ makefile_config("target_os").include?("mingw")
185
+ end
186
+
187
+ def win_target?
188
+ target_platform = RbConfig::CONFIG["target_os"]
189
+ !!Gem::WIN_PATTERNS.find { |r| target_platform =~ r }
190
+ end
191
+
192
+ # Interpolate substition vars in the arg (i.e. $(DEFFILE))
193
+ def maybe_resolve_ldflag_variable(input_arg, dest_dir)
194
+ var_matches = input_arg.match(/\$\((\w+)\)/)
195
+
196
+ return input_arg unless var_matches
197
+
198
+ var_name = var_matches[1]
199
+
200
+ return input_arg if var_name.nil? || var_name.chomp.empty?
201
+
202
+ case var_name
203
+ # On windows, it is assumed that mkmf has setup an exports file for the
204
+ # extension, so we have to to create one ourselves.
205
+ when "DEFFILE"
206
+ write_deffile(dest_dir)
207
+ else
208
+ RbConfig::CONFIG[var_name]
209
+ end
210
+ end
211
+
212
+ def write_deffile(dest_path)
213
+ dest_dir = File.dirname(final_extension_path(dest_path))
214
+ FileUtils.mkdir_p(dest_dir)
215
+ deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def")
216
+ export_prefix = makefile_config("EXPORT_PREFIX") || ""
217
+
218
+ File.open(deffile_path, "w") do |f|
219
+ f.puts "EXPORTS"
220
+ f.puts "#{export_prefix.strip}Init_#{spec.name}"
221
+ end
222
+
223
+ deffile_path
224
+ end
225
+
226
+ # We have to basically reimplement RbConfig::CONFIG['SOEXT'] here to support
227
+ # Ruby < 2.5
228
+ #
229
+ # @see https://github.com/ruby/ruby/blob/c87c027f18c005460746a74c07cd80ee355b16e4/configure.ac#L3185
230
+ def so_ext
231
+ return RbConfig::CONFIG["SOEXT"] if RbConfig::CONFIG.key?("SOEXT")
232
+
233
+ if win_target?
234
+ "dll"
235
+ elsif darwin_target?
236
+ "dylib"
237
+ else
238
+ "so"
239
+ end
240
+ end
241
+
242
+ # Corresponds to $(LIBPATH) in mkmf
243
+ def mkmf_libpath
244
+ ["-L", "native=#{makefile_config("libdir")}"]
245
+ end
246
+
247
+ def makefile_config(var_name)
248
+ val = RbConfig::MAKEFILE_CONFIG[var_name]
249
+
250
+ return unless val
251
+
252
+ RbConfig.expand(val.dup)
253
+ end
254
+
255
+ # Good balance between binary size and debugability
256
+ def debug_flags
257
+ return [] if profile == :dev
258
+
259
+ ["-C", "debuginfo=1"]
260
+ end
261
+
262
+ # Copied from ExtConfBuilder
263
+ def finalize_directory(dest_path, lib_dir, extension_dir)
264
+ require "fileutils"
265
+ require "tempfile"
266
+
267
+ ext_path = final_extension_path(dest_path)
268
+
269
+ begin
270
+ tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
271
+
272
+ # Some versions of `mktmpdir` return absolute paths, which will break make
273
+ # if the paths contain spaces. However, on Ruby 1.9.x on Windows, relative
274
+ # paths cause all C extension builds to fail.
275
+ #
276
+ # As such, we convert to a relative path unless we are using Ruby 1.9.x on
277
+ # Windows. This means that when using Ruby 1.9.x on Windows, paths with
278
+ # spaces do not work.
279
+ #
280
+ # Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940
281
+ tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
282
+
283
+ if tmp_dest_relative
284
+ full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
285
+
286
+ # TODO: remove in RubyGems 3
287
+ if Gem.install_extension_in_lib && lib_dir
288
+ FileUtils.mkdir_p lib_dir
289
+ FileUtils.cp_r ext_path, lib_dir, remove_destination: true
290
+ end
291
+
292
+ FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
293
+ destent = ent.class.new(dest_path, ent.rel)
294
+ destent.exist? || FileUtils.mv(ent.path, destent.path)
295
+ end
296
+ end
297
+ ensure
298
+ FileUtils.rm_rf tmp_dest if tmp_dest
299
+ end
300
+ end
301
+
302
+ def get_relative_path(path, base)
303
+ path[0..base.length - 1] = "." if path.start_with?(base)
304
+ path
305
+ end
306
+
307
+ def profile_target_directory
308
+ case profile
309
+ when :release then "release"
310
+ when :dev then "debug"
311
+ else raise "unknown target directory for profile: #{profile}"
312
+ end
313
+ end
314
+
315
+ # Error raised when no cdylib artifact was created
316
+ class DylibNotFoundError < StandardError
317
+ def initialize(dir)
318
+ files = Dir.glob(File.join(dir, "**", "*")).map { |f| "- #{f}" }.join "\n"
319
+
320
+ super <<~MSG
321
+ Dynamic library not found for Rust extension (in #{dir})
322
+
323
+ Make sure you set "crate-type" in Cargo.toml to "cdylib"
324
+
325
+ Found files:
326
+ #{files}
327
+ MSG
328
+ end
329
+ end
330
+ end
metadata ADDED
@@ -0,0 +1,53 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rb_sys1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ian Ker-Seymer
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-04-21 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - i.kerseymer@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - bin/console
21
+ - bin/setup
22
+ - certs/ianks.pem
23
+ - lib/rb_sys.rb
24
+ - lib/rb_sys/mkmf.rb
25
+ - lib/rb_sys/version.rb
26
+ - sig/rb_sys.rbs
27
+ - vendor/rubygems/ext/cargo_builder.rb
28
+ - vendor/rubygems/ext/cargo_builder/link_flag_converter.rb
29
+ homepage: https://github.com/oxidize-rb/rb-sys
30
+ licenses:
31
+ - MIT
32
+ metadata:
33
+ rubygems_mfa_required: 'true'
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.4.0
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubygems_version: 3.3.12
50
+ signing_key:
51
+ specification_version: 4
52
+ summary: Helpers for compiling Rust extensions for ruby
53
+ test_files: []