rubygems-update 3.4.5 → 3.4.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1115 -1095
  3. data/Manifest.txt +5 -5
  4. data/bundler/CHANGELOG.md +12 -0
  5. data/bundler/lib/bundler/build_metadata.rb +2 -2
  6. data/bundler/lib/bundler/cli/binstubs.rb +5 -1
  7. data/bundler/lib/bundler/inline.rb +6 -8
  8. data/bundler/lib/bundler/installer/standalone.rb +11 -7
  9. data/bundler/lib/bundler/rubygems_integration.rb +8 -4
  10. data/bundler/lib/bundler/version.rb +1 -1
  11. data/lib/rubygems/core_ext/kernel_require.rb +113 -109
  12. data/lib/rubygems/ext/builder.rb +1 -2
  13. data/lib/rubygems/ext/cargo_builder.rb +129 -89
  14. data/lib/rubygems/specification_policy.rb +1 -1
  15. data/lib/rubygems.rb +11 -1
  16. data/rubygems-update.gemspec +1 -1
  17. data/test/rubygems/bundler_test_gem.rb +419 -0
  18. data/test/rubygems/test_gem.rb +0 -412
  19. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/custom_name.gemspec +2 -4
  20. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/{Cargo.lock → ext/custom_name_lib/Cargo.lock} +0 -0
  21. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/{Cargo.toml → ext/custom_name_lib/Cargo.toml} +0 -0
  22. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/{src → ext/custom_name_lib/src}/lib.rs +1 -1
  23. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/lib/custom_name.rb +1 -0
  24. data/test/rubygems/test_gem_ext_cargo_builder.rb +9 -16
  25. data/test/rubygems/test_gem_ext_cargo_builder_unit.rb +5 -10
  26. metadata +8 -8
  27. data/test/rubygems/test_gem_ext_cargo_builder/custom_name/build.rb +0 -21
  28. data/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/build.rb +0 -21
@@ -6,30 +6,60 @@
6
6
  class Gem::Ext::CargoBuilder < Gem::Ext::Builder
7
7
  attr_accessor :spec, :runner, :profile
8
8
 
9
- def initialize(spec)
9
+ def initialize
10
10
  require_relative "../command"
11
11
  require_relative "cargo_builder/link_flag_converter"
12
12
 
13
- @spec = spec
14
13
  @runner = self.class.method(:run)
15
14
  @profile = :release
16
15
  end
17
16
 
18
- def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
17
+ def build(extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
18
+ require "tempfile"
19
19
  require "fileutils"
20
- require "shellwords"
21
20
 
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
21
+ # Where's the Cargo.toml of the crate we're building
22
+ cargo_toml = File.join(cargo_dir, "Cargo.toml")
23
+ # What's the crate's name
24
+ crate_name = cargo_crate_name(cargo_dir, cargo_toml, results)
25
+
26
+ begin
27
+ # Create a tmp dir to do the build in
28
+ tmp_dest = Dir.mktmpdir(".gem.", cargo_dir)
29
+
30
+ # Run the build
31
+ cmd = cargo_command(cargo_toml, tmp_dest, args, crate_name)
32
+ runner.call(cmd, results, "cargo", cargo_dir, build_env)
33
+
34
+ # Where do we expect Cargo to write the compiled library
35
+ dylib_path = cargo_dylib_path(tmp_dest, crate_name)
28
36
 
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
37
+ # Helpful error if we didn't find the compiled library
38
+ raise DylibNotFoundError, tmp_dest unless File.exist?(dylib_path)
39
+
40
+ # Cargo and Ruby differ on how the library should be named, rename from
41
+ # what Cargo outputs to what Ruby expects
42
+ dlext_name = "#{crate_name}.#{makefile_config("DLEXT")}"
43
+ dlext_path = File.join(File.dirname(dylib_path), dlext_name)
44
+ FileUtils.cp(dylib_path, dlext_path)
45
+
46
+ nesting = extension_nesting(extension)
47
+
48
+ # TODO: remove in RubyGems 4
49
+ if Gem.install_extension_in_lib && lib_dir
50
+ nested_lib_dir = File.join(lib_dir, nesting)
51
+ FileUtils.mkdir_p nested_lib_dir
52
+ FileUtils.cp_r dlext_path, nested_lib_dir, remove_destination: true
53
+ end
54
+
55
+ # move to final destination
56
+ nested_dest_path = File.join(dest_path, nesting)
57
+ FileUtils.mkdir_p nested_dest_path
58
+ FileUtils.cp_r dlext_path, nested_dest_path, remove_destination: true
59
+ ensure
60
+ # clean up intermediary build artifacts
61
+ FileUtils.rm_rf tmp_dest if tmp_dest
62
+ end
33
63
 
34
64
  results
35
65
  end
@@ -42,39 +72,59 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
42
72
  build_env
43
73
  end
44
74
 
45
- def cargo_command(cargo_dir, dest_path, args = [])
46
- manifest = File.join(cargo_dir, "Cargo.toml")
47
- cargo = ENV.fetch("CARGO", "cargo")
75
+ def cargo_command(cargo_toml, dest_path, args = [], crate_name = nil)
76
+ require "shellwords"
48
77
 
49
78
  cmd = []
50
79
  cmd += [cargo, "rustc"]
51
80
  cmd += ["--crate-type", "cdylib"]
52
81
  cmd += ["--target", ENV["CARGO_BUILD_TARGET"]] if ENV["CARGO_BUILD_TARGET"]
53
82
  cmd += ["--target-dir", dest_path]
54
- cmd += ["--manifest-path", manifest]
83
+ cmd += ["--manifest-path", cargo_toml]
55
84
  cmd += ["--lib"]
56
85
  cmd += ["--profile", profile.to_s]
57
86
  cmd += ["--locked"]
58
87
  cmd += Gem::Command.build_args
59
88
  cmd += args
60
89
  cmd += ["--"]
61
- cmd += [*cargo_rustc_args(dest_path)]
90
+ cmd += [*cargo_rustc_args(dest_path, crate_name)]
62
91
  cmd
63
92
  end
64
93
 
65
94
  private
66
95
 
96
+ def cargo
97
+ ENV.fetch("CARGO", "cargo")
98
+ end
99
+
100
+ # returns the directory nesting of the extension, ignoring the first part, so
101
+ # "ext/foo/bar/Cargo.toml" becomes "foo/bar"
102
+ def extension_nesting(extension)
103
+ parts = extension.to_s.split(Regexp.union([File::SEPARATOR, File::ALT_SEPARATOR].compact))
104
+
105
+ parts = parts.each_with_object([]) do |segment, final|
106
+ next if segment == "."
107
+ if segment == ".."
108
+ raise Gem::InstallError, "extension outside of gem root" if final.empty?
109
+ next final.pop
110
+ end
111
+ final << segment
112
+ end
113
+
114
+ File.join(parts[1...-1])
115
+ end
116
+
67
117
  def rb_config_env
68
118
  result = {}
69
119
  RbConfig::CONFIG.each {|k, v| result["RBCONFIG_#{k}"] = v }
70
120
  result
71
121
  end
72
122
 
73
- def cargo_rustc_args(dest_dir)
123
+ def cargo_rustc_args(dest_dir, crate_name)
74
124
  [
75
125
  *linker_args,
76
126
  *mkmf_libpath,
77
- *rustc_dynamic_linker_flags(dest_dir),
127
+ *rustc_dynamic_linker_flags(dest_dir, crate_name),
78
128
  *rustc_lib_flags(dest_dir),
79
129
  *platform_specific_rustc_args(dest_dir),
80
130
  ]
@@ -134,42 +184,70 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
134
184
  makefile_config("ENABLE_SHARED") == "no"
135
185
  end
136
186
 
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
187
+ def cargo_dylib_path(dest_path, crate_name)
188
+ prefix = so_ext == "dll" ? "" : "lib"
189
+ path_parts = [dest_path]
190
+ path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"]
191
+ path_parts += ["release", "#{prefix}#{crate_name}.#{so_ext}"]
192
+ File.join(*path_parts)
142
193
  end
143
194
 
144
- def validate_cargo_build!(dir)
145
- dylib_path = cargo_dylib_path(dir)
195
+ def cargo_crate_name(cargo_dir, manifest_path, results)
196
+ require "open3"
197
+ Gem.load_yaml
146
198
 
147
- raise DylibNotFoundError, dir unless File.exist?(dylib_path)
199
+ output, status =
200
+ begin
201
+ Open3.capture2e(cargo, "metadata", "--no-deps", "--format-version", "1", :chdir => cargo_dir)
202
+ rescue => error
203
+ raise Gem::InstallError, "cargo metadata failed #{error.message}"
204
+ end
148
205
 
149
- dylib_path
150
- end
206
+ unless status.success?
207
+ if Gem.configuration.really_verbose
208
+ puts output
209
+ else
210
+ results << output
211
+ end
151
212
 
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
213
+ exit_reason =
214
+ if status.exited?
215
+ ", exit code #{status.exitstatus}"
216
+ elsif status.signaled?
217
+ ", uncaught signal #{status.termsig}"
218
+ end
157
219
 
158
- def cargo_dylib_path(dest_path)
159
- prefix = so_ext == "dll" ? "" : "lib"
160
- path_parts = [dest_path]
161
- path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"]
162
- path_parts += ["release", "#{prefix}#{cargo_crate_name}.#{so_ext}"]
163
- File.join(*path_parts)
220
+ raise Gem::InstallError, "cargo metadata failed#{exit_reason}"
221
+ end
222
+
223
+ # cargo metadata output is specified as json, but with the
224
+ # --format-version 1 option the output is compatible with YAML, so we can
225
+ # avoid the json dependency
226
+ metadata = Gem::SafeYAML.safe_load(output)
227
+ package = metadata["packages"].find {|pkg| normalize_path(pkg["manifest_path"]) == manifest_path }
228
+ unless package
229
+ found = metadata["packages"].map {|md| "#{md["name"]} at #{md["manifest_path"]}" }
230
+ raise Gem::InstallError, <<-EOF
231
+ failed to determine cargo package name
232
+
233
+ looking for: #{manifest_path}
234
+
235
+ found:
236
+ #{found.join("\n")}
237
+ EOF
238
+ end
239
+ package["name"].tr("-", "_")
164
240
  end
165
241
 
166
- def cargo_crate_name
167
- spec.metadata.fetch("cargo_crate_name", spec.name).tr("-", "_")
242
+ def normalize_path(path)
243
+ return path unless File::ALT_SEPARATOR
244
+
245
+ path.tr(File::ALT_SEPARATOR, File::SEPARATOR)
168
246
  end
169
247
 
170
- def rustc_dynamic_linker_flags(dest_dir)
248
+ def rustc_dynamic_linker_flags(dest_dir, crate_name)
171
249
  split_flags("DLDFLAGS")
172
- .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir) }
250
+ .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir, crate_name) }
173
251
  .compact
174
252
  .flat_map {|arg| ldflag_to_link_modifier(arg) }
175
253
  end
@@ -204,7 +282,7 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
204
282
  end
205
283
 
206
284
  # Interpolate substitution vars in the arg (i.e. $(DEFFILE))
207
- def maybe_resolve_ldflag_variable(input_arg, dest_dir)
285
+ def maybe_resolve_ldflag_variable(input_arg, dest_dir, crate_name)
208
286
  var_matches = input_arg.match(/\$\((\w+)\)/)
209
287
 
210
288
  return input_arg unless var_matches
@@ -217,19 +295,19 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
217
295
  # On windows, it is assumed that mkmf has setup an exports file for the
218
296
  # extension, so we have to to create one ourselves.
219
297
  when "DEFFILE"
220
- write_deffile(dest_dir)
298
+ write_deffile(dest_dir, crate_name)
221
299
  else
222
300
  RbConfig::CONFIG[var_name]
223
301
  end
224
302
  end
225
303
 
226
- def write_deffile(dest_dir)
227
- deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def")
304
+ def write_deffile(dest_dir, crate_name)
305
+ deffile_path = File.join(dest_dir, "#{crate_name}-#{RbConfig::CONFIG["arch"]}.def")
228
306
  export_prefix = makefile_config("EXPORT_PREFIX") || ""
229
307
 
230
308
  File.open(deffile_path, "w") do |f|
231
309
  f.puts "EXPORTS"
232
- f.puts "#{export_prefix.strip}Init_#{spec.name}"
310
+ f.puts "#{export_prefix.strip}Init_#{crate_name}"
233
311
  end
234
312
 
235
313
  deffile_path
@@ -264,44 +342,6 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
264
342
  RbConfig.expand(val.dup)
265
343
  end
266
344
 
267
- # Copied from ExtConfBuilder
268
- def finalize_directory(dest_path, lib_dir, extension_dir)
269
- require "fileutils"
270
- require "tempfile"
271
-
272
- ext_path = final_extension_path(dest_path)
273
-
274
- begin
275
- tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
276
-
277
- # Some versions of `mktmpdir` return absolute paths, which will break make
278
- # if the paths contain spaces.
279
- #
280
- # As such, we convert to a relative path.
281
- tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
282
-
283
- full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
284
-
285
- # TODO: remove in RubyGems 4
286
- if Gem.install_extension_in_lib && lib_dir
287
- FileUtils.mkdir_p lib_dir
288
- FileUtils.cp_r ext_path, lib_dir, remove_destination: true
289
- end
290
-
291
- FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
292
- destent = ent.class.new(dest_path, ent.rel)
293
- destent.exist? || FileUtils.mv(ent.path, destent.path)
294
- end
295
- ensure
296
- FileUtils.rm_rf tmp_dest if tmp_dest
297
- end
298
- end
299
-
300
- def get_relative_path(path, base)
301
- path[0..base.length - 1] = "." if path.start_with?(base)
302
- path
303
- end
304
-
305
345
  # Error raised when no cdylib artifact was created
306
346
  class DylibNotFoundError < StandardError
307
347
  def initialize(dir)
@@ -466,7 +466,7 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
466
466
 
467
467
  def validate_rust_extensions(builder) # :nodoc:
468
468
  rust_extension = @specification.extensions.any? {|s| builder.builder_for(s).is_a? Gem::Ext::CargoBuilder }
469
- missing_cargo_lock = !@specification.files.include?("Cargo.lock")
469
+ missing_cargo_lock = !@specification.files.any? {|f| f.end_with?("Cargo.lock") }
470
470
 
471
471
  error <<-ERROR if rust_extension && missing_cargo_lock
472
472
  You have specified rust based extension, but Cargo.lock is not part of the gem files. Please run `cargo generate-lockfile` or any other command to generate Cargo.lock and ensure it is added to your gem files section in gemspec.
data/lib/rubygems.rb CHANGED
@@ -8,7 +8,7 @@
8
8
  require "rbconfig"
9
9
 
10
10
  module Gem
11
- VERSION = "3.4.5".freeze
11
+ VERSION = "3.4.6".freeze
12
12
  end
13
13
 
14
14
  # Must be first since it unloads the prelude from 1.9.2
@@ -181,6 +181,8 @@ module Gem
181
181
 
182
182
  @default_source_date_epoch = nil
183
183
 
184
+ @discover_gems_on_require = true
185
+
184
186
  ##
185
187
  # Try to activate a gem containing +path+. Returns true if
186
188
  # activation succeeded or wasn't needed because it was already
@@ -1163,8 +1165,16 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
1163
1165
  # RubyGems distributors (like operating system package managers) can
1164
1166
  # disable RubyGems update by setting this to error message printed to
1165
1167
  # end-users on gem update --system instead of actual update.
1168
+
1166
1169
  attr_accessor :disable_system_update_message
1167
1170
 
1171
+ ##
1172
+ # Whether RubyGems should enhance builtin `require` to automatically
1173
+ # check whether the path required is present in installed gems, and
1174
+ # automatically activate them and add them to `$LOAD_PATH`.
1175
+
1176
+ attr_accessor :discover_gems_on_require
1177
+
1168
1178
  ##
1169
1179
  # Hash of loaded Gem::Specification keyed by name
1170
1180
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "rubygems-update"
5
- s.version = "3.4.5"
5
+ s.version = "3.4.6"
6
6
  s.authors = ["Jim Weirich", "Chad Fowler", "Eric Hodel", "Luis Lavena", "Aaron Patterson", "Samuel Giddins", "André Arko", "Evan Phoenix", "Hiroshi SHIBATA"]
7
7
  s.email = ["", "", "drbrain@segment7.net", "luislavena@gmail.com", "aaron@tenderlovemaking.com", "segiddins@segiddins.me", "andre@arko.net", "evan@phx.io", "hsbt@ruby-lang.org"]
8
8