Skip to content

Commit 390b9f8

Browse files
committed
Install content-addressable gemspecs under specifications/<ruby_abi>/
Assisted-By: devx/9c2464e2-74cd-4d36-a6c0-50aef8c2ad01
1 parent 8fb09fa commit 390b9f8

18 files changed

Lines changed: 385 additions & 35 deletions

lib/bundler/endpoint_specification.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ def _remote_specification
161161
end
162162

163163
def local_specification_path
164-
"#{base_dir}/specifications/#{full_name}.gemspec"
164+
File.join(Gem::SpecificationRecord.specification_dir_for(self, base_dir),
165+
"#{full_name}.gemspec")
165166
end
166167

167168
def parse_metadata(data)

lib/bundler/rubygems_ext.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,21 @@ def self.content_addressed_row?(suffix, platform, required_ruby_version = nil, v
4545
end
4646
end
4747

48+
# Can be removed once RubyGems 4.0.0 support is dropped
49+
class SpecificationRecord
50+
unless respond_to?(:specification_dir_for)
51+
def self.specification_dir_for(spec, base_dir)
52+
File.join(base_dir, "specifications")
53+
end
54+
end
55+
56+
unless respond_to?(:dirs_from)
57+
def self.dirs_from(paths)
58+
paths.map {|path| File.join(path, "specifications") }
59+
end
60+
end
61+
end
62+
4863
# Can be removed once RubyGems 3.5.11 support is dropped
4964
unless Gem.respond_to?(:freebsd_platform?)
5065
def self.freebsd_platform?

lib/bundler/rubygems_integration.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def gem_cache
111111

112112
def spec_cache_dirs
113113
@spec_cache_dirs ||= begin
114-
dirs = gem_path.map {|dir| File.join(dir, "specifications") }
114+
dirs = gem_path.flat_map {|dir| Gem::SpecificationRecord.dirs_from([dir]) }
115115
dirs << Gem.spec_cache_dir
116116
dirs.uniq.select {|dir| File.directory? dir }
117117
end

lib/rubygems/commands/lock_command.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ def execute
100100
end
101101

102102
def spec_path(gem_full_name)
103-
gemspecs = Gem.path.map do |path|
104-
File.join path, "specifications", "#{gem_full_name}.gemspec"
103+
gemspecs = Gem::SpecificationRecord.dirs_from(Gem.path).map do |spec_dir|
104+
File.join spec_dir, "#{gem_full_name}.gemspec"
105105
end
106106

107107
gemspecs.find {|path| File.exist? path }

lib/rubygems/content_address.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ def self.ruby_abi_for(required_ruby_version)
5353
return nil if requirements.size != 1
5454

5555
op, version = requirements.first
56-
return nil if op != "~>" || version.segments.size != 3 || version.segments[2] != 0
56+
segments = version.segments
57+
return nil if op != "~>" || segments.size != 3 || segments[2] != 0
58+
return nil unless segments[0].is_a?(Integer) && segments[1].is_a?(Integer)
5759

58-
version.segments[0..1].join(".")
60+
segments[0..1].join(".")
5961
end
6062

6163
##

lib/rubygems/installer.rb

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,11 @@ def install
308308

309309
say clean_text(spec.post_install_message.to_s) if options[:post_install_message] && !spec.post_install_message.nil?
310310

311-
Gem::Specification.add_spec(spec) unless @install_dir
311+
if incompatible_abi_install?
312+
say "#{spec.full_name} is scoped to Ruby ABI #{spec.ruby_abi} and will not be visible to the running Ruby (ABI #{Gem.ruby_abi})"
313+
else
314+
Gem::Specification.add_spec(spec) unless @install_dir
315+
end
312316

313317
load_plugin unless options[:install_plugin] == false
314318

@@ -357,9 +361,11 @@ def installed_specs
357361
@installed_specs ||= begin
358362
specs = []
359363

360-
Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path|
361-
spec = Gem::Specification.load path
362-
specs << spec if spec
364+
Gem::SpecificationRecord.dirs_from([gem_home]).each do |dir|
365+
Gem::Util.glob_files_in_dir("*.gemspec", dir).each do |path|
366+
spec = Gem::Specification.load path
367+
specs << spec if spec
368+
end
363369
end
364370

365371
specs
@@ -395,7 +401,7 @@ def installation_satisfies_dependency?(dependency)
395401
#
396402

397403
def spec_file
398-
File.join gem_home, "specifications", "#{spec.full_name}.gemspec"
404+
File.join Gem::SpecificationRecord.specification_dir_for(spec, gem_home), "#{spec.full_name}.gemspec"
399405
end
400406

401407
def default_spec_dir
@@ -419,7 +425,24 @@ def default_spec_file
419425
def write_spec
420426
spec.installed_by_version = Gem.rubygems_version
421427

422-
Gem.write_binary(spec_file, spec.to_ruby_for_cache)
428+
spec_file = self.spec_file
429+
spec_dir = File.dirname spec_file
430+
dir_mode = options[:dir_mode]
431+
content_addressed = Gem::ContentAddress.content_addressed?(spec)
432+
433+
if File.directory? spec_dir
434+
if content_addressed && dir_mode && !File.writable?(spec_dir)
435+
File.chmod(0o755, spec_dir)
436+
end
437+
else
438+
ensure_writable_dir spec_dir
439+
end
440+
441+
begin
442+
Gem.write_binary(spec_file, spec.to_ruby_for_cache)
443+
ensure
444+
File.chmod(dir_mode, spec_dir) if dir_mode && content_addressed
445+
end
423446
end
424447

425448
##
@@ -973,6 +996,10 @@ def ensure_writable_dir(dir) # :nodoc:
973996

974997
private
975998

999+
def incompatible_abi_install?
1000+
Gem::ContentAddress.content_addressed?(spec) && spec.ruby_abi != Gem.ruby_abi
1001+
end
1002+
9761003
def assign_content_address
9771004
address = @package.content_address
9781005
expected = options[:content_address]

lib/rubygems/package.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ def extract_tar_gz(io, destination_dir, pattern = "*") # :nodoc:
556556
end
557557
end
558558

559-
if dir_mode
559+
if dir_mode && !directories.empty?
560560
File.chmod(dir_mode, *directories)
561561
end
562562
end

lib/rubygems/request_set.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,10 @@ def specs
518518
end
519519

520520
def specs_in(dir)
521-
Gem::Util.glob_files_in_dir("*.gemspec", File.join(dir, "specifications")).map do |g|
522-
Gem::Specification.load g
521+
Gem::SpecificationRecord.dirs_from([dir]).flat_map do |spec_dir|
522+
Gem::Util.glob_files_in_dir("*.gemspec", spec_dir).map do |g|
523+
Gem::Specification.load g
524+
end
523525
end
524526
end
525527

lib/rubygems/resolver/lock_specification.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ def initialize(set, name, version, sources, platform)
3030
def install(options = {})
3131
destination = options[:install_dir] || Gem.dir
3232

33-
if File.exist? File.join(destination, "specifications", spec.spec_name)
33+
installed = Gem::SpecificationRecord.dirs_from([destination]).any? do |spec_dir|
34+
File.exist? File.join(spec_dir, spec.spec_name)
35+
end
36+
37+
if installed
3438
yield nil
3539
return
3640
end

lib/rubygems/specification.rb

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,9 @@ def self.reset
12631263
# Keeps track of all currently known specifications
12641264

12651265
def self.specification_record
1266-
@specification_record ||= Gem::SpecificationRecord.new(dirs)
1266+
@specification_record ||= Gem::SpecificationRecord.new(
1267+
Gem::SpecificationRecord.dirs_with_abi(dirs)
1268+
)
12671269
end
12681270

12691271
# DOC: This method needs documented or nodoc'd
@@ -2026,13 +2028,19 @@ def initialize_copy(other_spec)
20262028

20272029
def base_dir
20282030
return Gem.dir unless loaded_from
2029-
@base_dir ||= if default_gem?
2031+
@base_dir ||= if default_gem? || loaded_from_abi_scoped_spec_dir?
20302032
File.dirname File.dirname File.dirname loaded_from
20312033
else
20322034
File.dirname File.dirname loaded_from
20332035
end
20342036
end
20352037

2038+
def loaded_from_abi_scoped_spec_dir?
2039+
!loaded_from.nil? &&
2040+
Gem::SpecificationRecord.abi_scoped_spec_dir?(File.dirname(loaded_from))
2041+
end
2042+
private :loaded_from_abi_scoped_spec_dir?
2043+
20362044
def inspect # :nodoc:
20372045
if $DEBUG
20382046
super
@@ -2328,11 +2336,14 @@ def source # :nodoc:
23282336
end
23292337

23302338
##
2331-
# Returns the full path to the directory containing this spec's
2332-
# gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications
2333-
2339+
# Full path to the directory containing this spec's gemspec file.
2340+
# ABI-scoped for content-addressed specs.
23342341
def spec_dir
2335-
@spec_dir ||= File.join base_dir, "specifications"
2342+
@spec_dir ||= if loaded_from && Gem::ContentAddress.content_addressed?(self)
2343+
File.dirname loaded_from
2344+
else
2345+
Gem::SpecificationRecord.specification_dir_for(self, base_dir)
2346+
end
23362347
end
23372348

23382349
##

0 commit comments

Comments
 (0)