rb_sys 0.9.64 → 0.9.65

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da8a462e7210f1746cca7dd9935d243b179176318aa766474fc33dd2de71227c
4
- data.tar.gz: 7cd4a0126f7240b2b58ee3338dceec912d1cd9de211adc55a6f4da320fa982cc
3
+ metadata.gz: 5dc9ecb59d4b2cc130afff772c740063ee2bdbf5bdf15fb866aa6dcbb501d396
4
+ data.tar.gz: b9c2c65ef48e0c5400ea857bb75ad9ec85ffac300a4290bc6f20ef00ca5e8163
5
5
  SHA512:
6
- metadata.gz: be81aba075a717c61a156cfcfafba1b9e7b36a33a045a2f04372b1c8244dd4280c0ebcc113fdc5c468bf72e49b6aaa8eff8d20784706ac779bab80e64d09ae04
7
- data.tar.gz: 3fa8cb0393b6cbbd53cba685b8616e5b747556ea583b8e943ec54b93a938b6b0f6610768c545e28076eb9806afb92108e1bdf6e0e2f9d78b6cdaf05666fa6bd9
6
+ metadata.gz: 3c4acd2a28480da0b8adb18a48a806354afae12d963b9394f6ce9bd41f89c703a27dbe9a6bc52136f897ad7e64ccba70c9bbfd4231a9b209964d130dc703d8ad
7
+ data.tar.gz: 849c5b3fd8209dfe41b6a5efc0dbd06d6760e849b32cd0182915f9e1d5e011885ba64d56ef7c8f8ed286094ba4efaaec2ad5ffa1134690579223ae826e81ba54
checksums.yaml.gz.sig CHANGED
Binary file
data/exe/rb-sys-dock CHANGED
@@ -1,82 +1,61 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
+
3
5
  require "optparse"
6
+ require "open3"
4
7
  require "rb_sys/version"
5
8
  require "rb_sys/toolchain_info"
9
+ require "rb_sys/cargo/metadata"
10
+ require "rb_sys/util/logger"
6
11
  require "fileutils"
7
12
  require "tmpdir"
8
13
 
9
- options = {
10
- version: RbSys::VERSION
14
+ OPTIONS = {
15
+ docker_platform: "linux/amd64",
16
+ version: RbSys::VERSION,
17
+ directory: Dir.pwd
11
18
  }
12
19
 
13
- def log(level, message, emoji: true, io: $stderr)
14
- emoji_opt, shellcode = case level
15
- when :error
16
- ["❌", "\e[1;31m"]
17
- when :warn
18
- ["⚠️", "\e[1;33m"]
19
- when :info
20
- ["ℹ️", "\e[1;37m"]
21
- when :notice
22
- ["🐳", "\e[1;34m"]
23
- when :trace
24
- return unless ENV["LOG_LEVEL"] == "trace"
25
-
26
- ["🔍", "\e[1;2m"]
27
- else raise "Unknown log level: #{level.inspect}"
28
- end
29
-
30
- emoji_opt = if emoji.is_a?(String)
31
- emoji + " "
32
- elsif emoji
33
- emoji_opt + " "
34
- end
20
+ def cargo_metadata
21
+ @cargo_metadata ||= RbSys::Cargo::Metadata.new("rb-sys-dock", deps: true)
22
+ end
35
23
 
36
- # Escape the message for bash shell codes (e.g. \e[1;31m)
37
- escaped = message.gsub("\\", "\\\\\\").gsub("\e", "\\e")
24
+ def logger
25
+ return @logger if @logger
38
26
 
39
- io.puts "#{shellcode}#{emoji_opt}#{escaped}\e[0m"
27
+ io = ARGV.include?("--quiet") ? File.open(File::NULL, "w") : $stderr
28
+ @logger ||= RbSys::Util::Logger.new(io: io)
40
29
  end
41
30
 
42
- def default_docker_command
43
- return @default_docker_command if defined?(@default_docker_command)
31
+ OptionParser.new do |opts|
32
+ opts.banner = <<~MSG
33
+ Usage: rb-sys-dock [OPTIONS] [COMMAND]
44
34
 
45
- @default_docker_command = ENV.fetch("DOCKER", "docker")
46
- end
35
+ A CLI to facillitate building Ruby on Rust extensions using Docker.
47
36
 
48
- def run_command!(cmd)
49
- log(:trace, "Running command: $ #{cmd}")
50
- stdout, stderr, status = Open3.capture3(cmd)
37
+ Examples:
51
38
 
52
- if status.success?
53
- stdout
54
- else
55
- log(:error, "Error running command: $ #{cmd}")
56
- warn(stderr)
57
- exit(status.exitstatus)
58
- end
59
- end
39
+ Build for Linux (x86_64)
40
+ $ rb-sys-dock --platform x86_64-linux --build
60
41
 
61
- def docker(cmd)
62
- require "open3"
42
+ Build for macOS (Ruby 3.1 and 3.2)
43
+ $ rb-sys-dock -p arm64-darwin --ruby-versions 3.1,3.2 --build
63
44
 
64
- run_command!("#{default_docker_command} #{cmd}")
65
- rescue Errno::ENOENT
66
- log(:trace, "Could not find docker command, trying podman")
45
+ Enter a shell
46
+ $ rb-sys-dock --list-platforms
67
47
 
68
- begin
69
- stdout = run_command!("podman #{cmd}")
70
- @default_docker_command = "podman"
71
- stdout
72
- rescue Errno::ENOENT
73
- log(:error, "Could not find docker or podman command, please install one of them")
74
- exit(1)
75
- end
76
- end
48
+ Run a command in the container with a specific directory mounted
49
+ $ rb-sys-dock -p x64-mingw-ucrt -d /tmp/mygem -- "env | grep RUBY"
77
50
 
78
- OptionParser.new do |opts|
79
- opts.banner = "Usage: rb-sys-dock --platform PLATFORM [COMMAND]"
51
+ List all supported platforms
52
+ $ rb-sys-dock --list-platforms
53
+
54
+ Options:
55
+ MSG
56
+
57
+ opts.on("--quiet", "Prints no logging output") do
58
+ end
80
59
 
81
60
  opts.on("-v", "--version", "Prints version") do
82
61
  require "rb_sys/version"
@@ -84,6 +63,10 @@ OptionParser.new do |opts|
84
63
  exit
85
64
  end
86
65
 
66
+ opts.on("--build", "Run the default command to cross-compile a gem") do
67
+ OPTIONS[:build] = true
68
+ end
69
+
87
70
  opts.on("-p", "--platform PLATFORM", "Platform to build for (i.e. x86_64-linux)") do |p|
88
71
  toolchain_info = begin
89
72
  RbSys::ToolchainInfo.new(p)
@@ -91,42 +74,40 @@ OptionParser.new do |opts|
91
74
  supported_list = RbSys::ToolchainInfo.all
92
75
  supported_list.select!(&:supported?)
93
76
  list = supported_list.map { |p| "- #{p} (#{p.rust_target})" }.join("\n")
94
- log(:error, "Platform #{p} is not supported, please use one of:\n\n#{list}")
77
+ logger.error("Platform #{p} is not supported, please use one of:\n\n#{list}")
95
78
  exit(1)
96
79
  end
97
80
 
98
- options[:platform] = p
99
- options[:toolchain_info] = toolchain_info
100
- end
101
-
102
- opts.on("--latest", "Use the latest version of the Docker image") do
103
- log(:notice, "Using latest version of the Docker image", emoji: "🆕")
104
- options[:version] = "latest"
105
- options[:no_cache] = true
106
- end
107
-
108
- opts.on("--list-platforms", "--list", "List all supported platforms") do
109
- log(:notice, "Supported platforms listed below:")
110
-
111
- RbSys::ToolchainInfo.supported.each do |p|
112
- log(:info, "- #{p} (#{p.rust_target})", emoji: false, io: $stdout)
113
- end
114
-
115
- exit(0)
81
+ OPTIONS[:platform] = p
82
+ OPTIONS[:toolchain_info] = toolchain_info
116
83
  end
117
84
 
118
- opts.on("--ruby-versions LIST", "List all supported Ruby versions") do |arg|
119
- log(:notice, "Requested Ruby versions: #{arg}")
120
-
85
+ opts.on("-r", "--ruby-versions LIST", "List all supported Ruby versions") do |arg|
121
86
  vers = arg.split(/[^0-9.]/).map do |v|
122
87
  parts = v.split(".")
123
88
  parts[2] = "0" if parts[2].nil?
124
89
  parts.join(".")
125
90
  end
126
91
 
92
+ OPTIONS[:ruby_versions] = vers
93
+
94
+ logger.info("Building for Ruby requested versions: #{vers}")
95
+
127
96
  ENV["RUBY_CC_VERSION"] = vers.join(":")
128
97
  end
129
98
 
99
+ opts.on("--tag TAG", "Use a specific version of the Docker image") do |tag|
100
+ logger.info("Using version #{tag} of the Docker image")
101
+ OPTIONS[:version] = tag
102
+ OPTIONS[:no_cache] = tag == "latest"
103
+ end
104
+
105
+ opts.on("--list-platforms", "--list", "List all supported platforms") do
106
+ logger.info("Supported platforms listed below:")
107
+ list_platforms
108
+ exit(0)
109
+ end
110
+
130
111
  opts.on("-h", "--help", "Prints this help") do
131
112
  puts opts
132
113
  exit
@@ -135,14 +116,60 @@ OptionParser.new do |opts|
135
116
  opts.on("-V", "--verbose", "Prints verbose output") do
136
117
  ENV["LOG_LEVEL"] = "trace"
137
118
  ENV["VERBOSE"] = "1"
138
- options[:verbose] = true
119
+ logger.level = :trace
120
+ OPTIONS[:verbose] = true
139
121
  end
140
122
 
141
- opts.on("--build", "Build the Docker image") do
142
- options[:build] = true
123
+ opts.on("--mount-toolchains", "Mount local Rustup toolchain (instead of pre-installed from Docker container)") do
124
+ OPTIONS[:mount_rustup_toolchains] = true
125
+ end
126
+
127
+ opts.on("-d", "--directory DIR", "Directory to run the command in") do |val|
128
+ OPTIONS[:directory] = File.expand_path(val)
143
129
  end
144
130
  end.parse!
145
131
 
132
+ def list_platforms
133
+ RbSys::ToolchainInfo.supported.each do |p|
134
+ old = logger.io
135
+ logger.io = $stdout
136
+ puts "- #{p.platform}"
137
+ ensure
138
+ logger.io = old
139
+ end
140
+ end
141
+
142
+ def default_docker_command
143
+ return @default_docker_command if defined?(@default_docker_command)
144
+
145
+ @default_docker_command = ENV.fetch("DOCKER") do
146
+ if !(docker = `which docker`).empty?
147
+ docker.strip
148
+ elsif !(podman = `which podman`).empty?
149
+ podman.strip
150
+ else
151
+ logger.fatal("Could not find docker or podman command, please install one of them")
152
+ end
153
+ end
154
+ end
155
+
156
+ def run_command!(*cmd)
157
+ logger.trace("Running command:\n\t$ #{cmd.join(" ")}")
158
+ stdout, stderr, status = Open3.capture3(*cmd)
159
+
160
+ if status.success?
161
+ stdout
162
+ else
163
+ logger.error("Error running command: $ #{cmd}")
164
+ warn(stderr)
165
+ exit(status.exitstatus)
166
+ end
167
+ end
168
+
169
+ def docker(cmd)
170
+ run_command!("#{default_docker_command} #{cmd}")
171
+ end
172
+
146
173
  def determine_cache_dir
147
174
  return ENV["RB_SYS_DOCK_CACHE_DIR"] if ENV["RB_SYS_DOCK_CACHE_DIR"]
148
175
  return File.join(ENV["XDG_CACHE_HOME"], "rb-sys-dock") if ENV["XDG_CACHE_HOME"]
@@ -172,53 +199,93 @@ def mount_cargo_registry
172
199
  end
173
200
 
174
201
  dir = File.join("registry")
175
- log(:trace, "Mounting cargo registry dir: #{dir}")
202
+ logger.trace("Mounting cargo registry dir: #{dir}")
176
203
  FileUtils.mkdir_p(dir)
177
204
 
178
- "--mount type=bind,source=#{File.join(local_registry_dir, dir)},target=#{File.join("/usr/local/cargo", dir)},readonly=false"
205
+ mount_shared_bind_dir(File.join(local_registry_dir, dir), File.join("/usr/local/cargo", dir))
206
+ end
207
+
208
+ def mount_rustup_toolchains
209
+ return unless OPTIONS[:mount_rustup_toolchains]
210
+
211
+ local_rustup_dir = if OPTIONS[:mount_rustup_toolchains].is_a?(String)
212
+ OPTIONS[:mount_rustup_toolchains]
213
+ elsif ENV["RUSTUP_HOME"]
214
+ ENV["RUSTUP_HOME"]
215
+ elsif File.exist?(rustup_home = File.join(ENV["HOME"], ".rustup"))
216
+ rustup_home
217
+ else
218
+ logger.fatal("Could not find Rustup home directory, please set RUSTUP_HOME")
219
+ end
220
+
221
+ logger.info("Mounting rustup toolchains from #{local_rustup_dir}")
222
+
223
+ target_triple = OPTIONS[:toolchain_info].rust_target
224
+ dkr_triple = "x86_64-unknown-linux-gnu"
225
+ dkr_toolchain = "stable-#{dkr_triple}"
226
+ dkr_toolchain_dir = "/usr/local/rustup/toolchains/#{dkr_toolchain}"
227
+ installed_toolchains = Dir.glob(File.join(local_rustup_dir, "toolchains", "*")).map { |f| File.basename(f) }
228
+ has_host_toolchain = installed_toolchains.any? { |t| t.end_with?(dkr_toolchain) }
229
+
230
+ if !has_host_toolchain
231
+ logger.info("Installing default toolchain for docker image (#{dkr_toolchain})")
232
+ run_command!("rustup", "toolchain", "add", dkr_toolchain, "--force-non-host")
233
+ end
234
+
235
+ has_target = run_command!("rustup target list --installed --toolchain #{dkr_toolchain}").include?(target_triple)
236
+
237
+ if !has_target
238
+ logger.info("Installing target for docker image (#{target_triple})")
239
+ run_command!("rustup", "target", "add", target_triple, "--toolchain", dkr_toolchain)
240
+ end
241
+
242
+ volume("#{local_rustup_dir}/toolchains/#{dkr_toolchain}", dkr_toolchain_dir, mode: "z,ro")
179
243
  end
180
244
 
181
245
  def volume(src, dest, mode: "rw")
182
246
  "--volume #{src}:#{dest}:rw"
183
247
  end
184
248
 
185
- def mount_bundle_cache(options)
186
- dir = File.join(cache_dir, options.fetch(:toolchain_info).platform, "bundle")
249
+ def mount_bundle_cache
250
+ dir = File.join(cache_dir, ruby_platform, "bundle")
187
251
  bundle_path = File.join(docker_tmp, "bundle")
188
252
  FileUtils.mkdir_p(dir)
189
- log(:trace, "Mounting bundle cache: #{dir}")
253
+ logger.trace("Mounting bundle cache: #{dir}")
190
254
 
191
255
  "#{volume(dir, bundle_path)} -e BUNDLE_PATH=#{bundle_path.inspect}"
192
256
  end
193
257
 
194
- def tmp_target_dir(options)
258
+ def tmp_target_dir
195
259
  return @tmp_target_dir if defined?(@tmp_target_dir)
196
260
 
197
- dir = File.join(Dir.pwd, "tmp", "rb-sys-dock", options.fetch(:toolchain_info).platform, "target")
261
+ dir = File.join(working_directory, "tmp", "rb-sys-dock", ruby_platform, "target")
198
262
  FileUtils.mkdir_p(dir)
199
263
  @tmp_target_dir = dir
200
264
  end
201
265
 
202
- def mount_target_dir(options)
203
- "-v #{tmp_target_dir(options)}:#{File.join(Dir.pwd, "target")}"
266
+ def working_directory
267
+ OPTIONS.fetch(:directory)
204
268
  end
205
269
 
206
- def mount_command_history(options)
270
+ def mount_target_dir
271
+ "-v #{tmp_target_dir}:#{File.join(working_directory, "target")}"
272
+ end
273
+
274
+ def mount_command_history
207
275
  return unless $stdin.tty?
208
276
 
209
- history_dir = File.join(cache_dir, options.fetch(:platform), "commandhistory")
277
+ history_dir = File.join(cache_dir, OPTIONS.fetch(:platform), "commandhistory")
210
278
  FileUtils.mkdir_p(history_dir)
211
279
  "-v #{history_dir}:#{File.join(docker_tmp, "commandhistory")}"
212
280
  end
213
281
 
214
- def default_command_to_run(input_args, options)
282
+ def default_command_to_run(input_args)
215
283
  input_cmd = input_args.empty? ? "true" : input_args.join(" ")
216
284
 
217
- if options[:build]
285
+ if OPTIONS[:build]
218
286
  with_bundle = +"test -f Gemfile && bundle install && #{input_cmd} && bundle exec rake native:$RUBY_TARGET gem"
219
287
  without_bundle = "#{input_cmd} && rake native:$RUBY_TARGET gem"
220
- log(:notice, "Running default build command:")
221
- log(:notice, " $ rake native:#{options[:toolchain_info].platform} gem")
288
+ logger.info("Running default build command (rake native:#{ruby_platform} gem)")
222
289
  "bash -c '(#{with_bundle}) || (#{without_bundle})'"
223
290
  else
224
291
  input_args.empty? ? "bash" : "bash -c '#{input_args.join(" ")}'"
@@ -242,11 +309,27 @@ def interactive?(input_args)
242
309
  $stdin.tty?
243
310
  end
244
311
 
245
- def mount_tmp_dir(options)
246
- "--mount type=bind,source=#{Dir.mktmpdir},destination=#{Dir.pwd}/tmp/#{options.fetch(:toolchain_info).platform},readonly=false"
312
+ def mount_shared_bind_dir(src, dest)
313
+ "--mount type=bind,source=#{src},destination=#{dest},readonly=false"
314
+ end
315
+
316
+ def mount_tmp_dir
317
+ "--mount type=bind,source=#{Dir.mktmpdir},destination=#{working_directory}/tmp/#{ruby_platform},readonly=false"
318
+ end
319
+
320
+ def toolchain_info
321
+ @toolchain_info ||= OPTIONS.fetch(:toolchain_info) do
322
+ logger.error("Could not determine ruby platform, please set ruby platform with --platform to one of:")
323
+ list_platforms
324
+ logger.fatal("Exiting...")
325
+ end
247
326
  end
248
327
 
249
- def rcd(input_args, options)
328
+ def ruby_platform
329
+ @ruby_platform ||= toolchain_info.platform
330
+ end
331
+
332
+ def rcd(input_args)
250
333
  wrapper_command = []
251
334
  wrapper_command << "sigfw" unless interactive?(input_args)
252
335
  wrapper_command << "runas"
@@ -256,12 +339,14 @@ def rcd(input_args, options)
256
339
 
257
340
  cmd = <<~SH
258
341
  #{default_docker_command} run \
259
- -v #{Dir.pwd}:#{Dir.pwd} \
260
- #{mount_tmp_dir(options)} \
261
- #{mount_target_dir(options)} \
342
+ --platform #{OPTIONS.fetch(:docker_platform)} \
343
+ -v #{working_directory}:#{working_directory} \
344
+ #{mount_tmp_dir} \
345
+ #{mount_target_dir} \
262
346
  #{mount_cargo_registry} \
263
- #{mount_bundle_cache(options)} \
264
- #{mount_command_history(options)} \
347
+ #{mount_rustup_toolchains} \
348
+ #{mount_bundle_cache} \
349
+ #{mount_command_history} \
265
350
  #{user_mapping} \
266
351
  -e GEM_PRIVATE_KEY_PASSPHRASE \
267
352
  -e ftp_proxy \
@@ -269,52 +354,64 @@ def rcd(input_args, options)
269
354
  -e https_proxy \
270
355
  -e RCD_HOST_RUBY_PLATFORM=#{RbConfig::CONFIG["arch"]} \
271
356
  -e RCD_HOST_RUBY_VERSION=#{RUBY_VERSION} \
357
+ -e RUSTUP_PERMIT_COPY_RENAME=true \
272
358
  -e RCD_IMAGE \
273
359
  -e RB_SYS_DOCK_TMPDIR="/tmp/rb-sys-dock" \
274
- -e RB_SYS_CARGO_TARGET_DIR=#{tmp_target_dir(options).inspect} \
360
+ -e RB_SYS_CARGO_TARGET_DIR=#{tmp_target_dir.inspect} \
275
361
  #{ENV["RUBY_CC_VERSION"] ? "-e RUBY_CC_VERSION=#{ENV["RUBY_CC_VERSION"]}" : ""} \
276
362
  -e RAKEOPT \
277
363
  -e TERM \
278
- -e LC_ALL=#{ENV.fetch("LC_ALL", "en_US.UTF-8")} \
279
- -w #{Dir.pwd} \
364
+ -w #{working_directory} \
280
365
  --rm \
281
366
  --interactive \
282
367
  #{docker_options.join(" ")} \
283
368
  #{ENV.fetch("RCD_IMAGE")} \
284
369
  #{wrapper_command.join(" ")} \
285
- #{default_command_to_run(input_args, options)}
370
+ #{default_command_to_run(input_args)}
286
371
  SH
287
372
 
288
- log(:trace, "Running command: $ #{cmd}")
373
+ cmd.gsub!(/\s+/, " ")
374
+
375
+ logger.trace("Running command:\n\t$ #{cmd}")
289
376
 
290
377
  exec(cmd)
291
378
  end
292
379
 
293
- def download_image(options)
380
+ def download_image
294
381
  image = ENV.fetch("RCD_IMAGE")
295
382
 
296
- if docker("images -q #{image}").strip.empty? || options[:no_cache]
383
+ if docker("images -q #{image}").strip.empty? || OPTIONS[:no_cache]
297
384
  # Nicely formatted message that we are downloading the image which might take awhile
298
- log(:notice, "Downloading container #{image.inspect}, this might take awhile...")
299
- docker("pull #{image} --quiet > /dev/null")
385
+ logger.info("Downloading container #{image.inspect}, this might take awhile...")
386
+ docker("pull #{image} --platform #{OPTIONS[:docker_platform]} --quiet > /dev/null")
300
387
  end
301
388
  end
302
389
 
303
- def log_some_useful_info(options)
304
- return if options[:build]
390
+ def log_some_useful_info
391
+ return if OPTIONS[:build]
305
392
 
306
393
  if ARGV.empty?
307
- log(:notice, "Entering shell in Docker container #{ENV["RCD_IMAGE"].inspect}")
394
+ logger.info("Entering shell in Docker container #{ENV["RCD_IMAGE"].inspect}")
308
395
  else
309
- log(:notice, "Running command #{ARGV.inspect} in Docker container #{ENV["RCD_IMAGE"].inspect}")
396
+ logger.info("Running command #{ARGV.inspect} in Docker container #{ENV["RCD_IMAGE"].inspect}")
310
397
  end
311
398
  end
312
399
 
313
- def set_env(options)
314
- ENV["RCD_IMAGE"] ||= "rbsys/#{options[:toolchain_info].platform}:#{options[:version]}"
400
+ def set_env
401
+ ENV["RCD_IMAGE"] ||= "rbsys/#{ruby_platform}:#{OPTIONS[:version]}"
402
+ end
403
+
404
+ def lint_rb_sys
405
+ cargo_version = cargo_metadata.rb_sys_version
406
+ return if cargo_version == RbSys::VERSION
407
+ logger.warn("Cargo rb-sys version (#{cargo_version}) does not match Ruby gem version (#{RbSys::VERSION})")
408
+ rescue => e
409
+ logger.warn("Could not determine Cargo rb-sys version")
410
+ logger.trace("Error was: #{e.inspect}")
315
411
  end
316
412
 
317
- set_env(options)
318
- download_image(options)
319
- log_some_useful_info(options)
320
- rcd(ARGV, options)
413
+ set_env
414
+ lint_rb_sys
415
+ download_image
416
+ log_some_useful_info
417
+ rcd(ARGV)
@@ -12,10 +12,13 @@ module RbSys
12
12
  # Initializes a new Cargo::Metadata instance.
13
13
  #
14
14
  # @param name [String] the name of the Cargo project
15
- def initialize(name)
15
+ def initialize(name, deps: false)
16
16
  raise ArgumentError, "name must be a String" unless name.is_a?(String)
17
17
 
18
18
  @name = name
19
+ @cargo_metadata = nil
20
+ @package_metadata = nil
21
+ @deps = deps
19
22
  end
20
23
 
21
24
  # Returns the path where the Cargo project's Cargo.toml is located.
@@ -88,6 +91,13 @@ module RbSys
88
91
  package_metadata.fetch("metadata")
89
92
  end
90
93
 
94
+ # Returns the rb-sys version, if any.
95
+ def rb_sys_version
96
+ pkg = packages.find { |p| p.fetch("name") == "rb-sys" }
97
+ return unless pkg
98
+ pkg["version"]
99
+ end
100
+
91
101
  private
92
102
 
93
103
  def package_metadata
@@ -103,7 +113,8 @@ module RbSys
103
113
 
104
114
  ::Gem.load_yaml
105
115
  cargo = ENV["CARGO"] || "cargo"
106
- args = ["metadata", "--no-deps", "--format-version", "1"]
116
+ args = ["metadata", "--format-version", "1"]
117
+ args << "--no-deps" unless @deps
107
118
  out, stderr, status = Open3.capture3(cargo, *args)
108
119
  raise "exited with non-zero status (#{status})" unless status.success?
109
120
  data = Gem::SafeYAML.safe_load(out)
@@ -4,7 +4,7 @@ require_relative "error"
4
4
  begin
5
5
  require "rake/extensiontask"
6
6
  rescue LoadError
7
- abort "Please install rake-compiler to use this gem"
7
+ abort "Please install rake-compiler to use this feature"
8
8
  end
9
9
 
10
10
  module RbSys
@@ -22,8 +22,9 @@ module RbSys
22
22
  # @param gem_spec [Gem::Specification] the gem specification to build (needed for cross-compiling)
23
23
  # @return [Rake::ExtensionTask]
24
24
  class ExtensionTask < Rake::ExtensionTask
25
- def init(name = nil, gem_spec = nil)
26
- super
25
+ def init(name = nil, gem_spec = :undefined)
26
+ super(name, lint_gem_spec(name, gem_spec))
27
+
27
28
  @orginal_ext_dir = @ext_dir
28
29
  @ext_dir = cargo_metadata.manifest_directory
29
30
  @source_pattern = nil
@@ -46,6 +47,8 @@ module RbSys
46
47
  def define
47
48
  super
48
49
  define_env_tasks
50
+
51
+ CLEAN.include(target_directory) if defined?(CLEAN)
49
52
  end
50
53
 
51
54
  def cargo_metadata
@@ -118,5 +121,34 @@ module RbSys
118
121
  desc 'Compile the native Rust extension with the "release" profile'
119
122
  task "compile:release" => ["rb_sys:env:release", "compile"]
120
123
  end
124
+
125
+ private
126
+
127
+ def lint_gem_spec(name, gs)
128
+ gem_spec = case gs
129
+ when :undefined
130
+ return
131
+ when Gem::Specification
132
+ gs
133
+ when String
134
+ Gem::Specification.load(gem_spec) || raise(ArgumentError, "Unable to load gemspec from file #{gs.inspect}")
135
+ else
136
+ raise ArgumentError, "gem_spec must be a Gem::Specification, got #{gs.class}"
137
+ end
138
+
139
+ gem_spec.files.each do |f|
140
+ if /\.(dll|so|dylib|lib|bundle)$/.match?(f)
141
+ warn "⚠️ gemspec includes native artifact (#{f}), please remove it."
142
+ end
143
+ end
144
+
145
+ if (gem_crate_name = gem_spec.metadata["cargo_crate_name"])
146
+ if name != gem_crate_name
147
+ warn "⚠️ cargo_crate_name (#{gem_crate_name}) does not match extension task crate name (#{name})"
148
+ end
149
+ end
150
+
151
+ gem_spec
152
+ end
121
153
  end
122
154
  end
data/lib/rb_sys/mkmf.rb CHANGED
@@ -246,7 +246,6 @@ module RbSys
246
246
  \t$(Q) curl --proto '=https' --tlsv1.2 --retry 10 --retry-connrefused -fsSL "https://sh.rustup.rs" | sh -s -- --no-modify-path --profile $(RB_SYS_RUSTUP_PROFILE) --default-toolchain none -y
247
247
  \t$(Q) rustup toolchain install $(RB_SYS_DEFAULT_TOOLCHAIN) --profile $(RB_SYS_RUSTUP_PROFILE)
248
248
  \t$(Q) rustup default $(RB_SYS_DEFAULT_TOOLCHAIN)
249
- \t$(Q) rustup component add rustfmt
250
249
 
251
250
  $(RUSTLIB): $(CARGO)
252
251
  #{endif_stmt}
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RbSys
4
+ module Util
5
+ class Logger
6
+ attr_accessor :io, :level
7
+
8
+ def initialize(io: $stderr, level: :info)
9
+ @io = ENV["GITHUB_ACTIONS"] ? $stdout : io
10
+ @level = level
11
+ end
12
+
13
+ def error(message, **opts)
14
+ add(:error, message, **opts)
15
+ end
16
+
17
+ def warn(message, **opts)
18
+ add(:warn, message, **opts)
19
+ end
20
+
21
+ def info(message, **opts)
22
+ add(:info, message, **opts)
23
+ end
24
+
25
+ def notice(message, **opts)
26
+ add(:notice, message, **opts)
27
+ end
28
+
29
+ def trace(message, **opts)
30
+ return unless level == :trace
31
+
32
+ add(:trace, message, **opts)
33
+ end
34
+
35
+ def fatal(message, **opts)
36
+ error(message, **opts)
37
+ abort
38
+ end
39
+
40
+ private
41
+
42
+ LEVEL_STYLES = {
43
+ warn: ["⚠️", "\e[1;33m"],
44
+ error: ["❌", "\e[1;31m"],
45
+ notice: ["👋", "\e[1;37m"],
46
+ info: ["🐳", "\e[1;34m"],
47
+ trace: ["🔍", "\e[1;2m"]
48
+ }
49
+
50
+ if ENV["GITHUB_ACTIONS"]
51
+ def add(level, message, emoji: true)
52
+ emote, _ = LEVEL_STYLES.fetch(level.to_sym)
53
+ io.puts "::#{level}::#{emote} #{message}"
54
+ end
55
+ else
56
+ def add(level, message, emoji: true)
57
+ emoji_opt, shellcode = LEVEL_STYLES.fetch(level.to_sym)
58
+
59
+ emoji_opt = if emoji.is_a?(String)
60
+ emoji + " "
61
+ elsif emoji
62
+ emoji_opt + " "
63
+ end
64
+
65
+ # Escape the message for bash shell codes (e.g. \033[1;31m)
66
+ escaped = message.gsub("\\", "\\\\\\").gsub("\033", "\\033")
67
+
68
+ io.puts "#{shellcode}#{emoji_opt}#{escaped}\033[0m"
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RbSys
4
- VERSION = "0.9.64"
4
+ VERSION = "0.9.65"
5
5
  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.9.64
4
+ version: 0.9.65
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: 2023-02-07 00:00:00.000000000 Z
33
+ date: 2023-02-13 00:00:00.000000000 Z
34
34
  dependencies: []
35
35
  description:
36
36
  email:
@@ -57,6 +57,7 @@ files:
57
57
  - lib/rb_sys/mkmf/config.rb
58
58
  - lib/rb_sys/toolchain_info.rb
59
59
  - lib/rb_sys/toolchain_info/data.rb
60
+ - lib/rb_sys/util/logger.rb
60
61
  - lib/rb_sys/version.rb
61
62
  homepage: https://oxidize-rb.github.io/rb-sys/
62
63
  licenses:
@@ -79,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
80
  - !ruby/object:Gem::Version
80
81
  version: '0'
81
82
  requirements: []
82
- rubygems_version: 3.4.2
83
+ rubygems_version: 3.4.6
83
84
  signing_key:
84
85
  specification_version: 4
85
86
  summary: Helpers for compiling Rust extensions for ruby
metadata.gz.sig CHANGED
Binary file