From aab7a55cd362540e7d67a2cf12c2f270d0d6bb59 Mon Sep 17 00:00:00 2001 From: lolwut Date: Thu, 12 Mar 2026 10:24:30 +0900 Subject: [ruby/rubygems] Warn when require_relative is used to load compiled extensions during gem build When a gem has native extensions and uses `require_relative` to load a path without a corresponding .rb file in the gem, warn that this will break in RubyGems 4.2, which will stop copying compiled extensions into the gem's lib directory. Recommend using `require` instead. https://github.com/ruby/rubygems/commit/1198c24a08 Co-Authored-By: Claude Opus 4.6 --- lib/rubygems/specification_policy.rb | 28 +++++++++++++++++ test/rubygems/test_gem_specification.rb | 55 +++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb index 0fcb635394..8ab82ee4a9 100644 --- a/lib/rubygems/specification_policy.rb +++ b/lib/rubygems/specification_policy.rb @@ -476,6 +476,7 @@ or set it to nil if you don't want to specify a license. validate_rake_extensions(builder) validate_rust_extensions(builder) + validate_extension_require_relative end def validate_rust_extensions(builder) # :nodoc: @@ -496,6 +497,33 @@ You have specified rake based extension, but rake is not added as runtime depend WARNING end + def validate_extension_require_relative # :nodoc: + return unless @specification.extensions.any? + + require_paths = @specification.require_paths + + @specification.files.each do |rb_file| + next unless rb_file.end_with?(".rb") + next unless require_paths.any? {|rp| rb_file.start_with?("#{rp}/") } + next unless File.file?(rb_file) + + File.foreach(rb_file).with_index(1) do |line, lineno| + next unless line =~ /^\s*require_relative\s+["']([^"']+)["']/ + + required_path = Regexp.last_match(1) + resolved = File.join(File.dirname(rb_file), required_path) + + next if @specification.files.any? {|f| f == "#{resolved}.rb" || f == resolved } + + warning <<~WARNING + #{rb_file}:#{lineno} uses `require_relative "#{required_path}"` to load a compiled extension. + This will break in RubyGems 4.2, which will stop copying compiled extensions into the gem's lib directory. + Use `require` instead of `require_relative` to load compiled extensions. + WARNING + end + end + end + def validate_unique_links links = @specification.metadata.slice(*METADATA_LINK_KEYS) grouped = links.group_by {|_key, uri| uri } diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb index 7675ade415..cf01a40b8c 100644 --- a/test/rubygems/test_gem_specification.rb +++ b/test/rubygems/test_gem_specification.rb @@ -2887,6 +2887,61 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use: end end + def test_validate_extension_require_relative_warning + util_setup_validate + + Dir.chdir @tempdir do + @a1.extensions = ["ext/a/extconf.rb"] + @a1.files = %w[lib/code.rb lib/a.rb ext/a/extconf.rb] + + File.write File.join("lib", "a.rb"), 'require_relative "a/a"' + + use_ui @ui do + @a1.validate + end + + assert_match(%r{require_relative "a/a"}, @ui.error) + assert_match(/will break in RubyGems 4\.2/, @ui.error) + assert_match(/Use `require` instead of `require_relative`/, @ui.error) + end + end + + def test_validate_extension_require_relative_no_warning_when_rb_exists + util_setup_validate + + Dir.chdir @tempdir do + @a1.extensions = ["ext/a/extconf.rb"] + @a1.files = %w[lib/code.rb lib/a.rb lib/a/a.rb ext/a/extconf.rb] + + FileUtils.mkdir_p File.join("lib", "a") + File.write File.join("lib", "a.rb"), 'require_relative "a/a"' + File.write File.join("lib", "a", "a.rb"), "" + + use_ui @ui do + @a1.validate + end + + refute_match(/require_relative/, @ui.error) + end + end + + def test_validate_extension_require_relative_no_warning_without_extensions + util_setup_validate + + Dir.chdir @tempdir do + @a1.extensions = [] + @a1.files = %w[lib/code.rb lib/a.rb] + + File.write File.join("lib", "a.rb"), 'require_relative "a/a"' + + use_ui @ui do + @a1.validate + end + + refute_match(/require_relative/, @ui.error) + end + end + def test_validate_description util_setup_validate -- cgit v1.2.3