Merge RubyGems 3.4.6 and Bundler 2.4.6 (#7214)

Merge RubyGems-3.4.6 and Bundler-2.4.6
This commit is contained in:
Hiroshi SHIBATA 2023-02-01 12:05:19 +09:00 committed by GitHub
parent 40e0b1e123
commit f4e6e78410
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 965 additions and 734 deletions

View file

@ -40,7 +40,11 @@ module Bundler
end end
if options[:standalone] if options[:standalone]
next Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") if gem_name == "bundler" if gem_name == "bundler"
Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") unless options[:all]
next
end
Bundler.settings.temporary(:path => (Bundler.settings[:path] || Bundler.root)) do Bundler.settings.temporary(:path => (Bundler.settings[:path] || Bundler.root)) do
installer.generate_standalone_bundler_executable_stubs(spec, installer_opts) installer.generate_standalone_bundler_executable_stubs(spec, installer_opts)
end end

View file

@ -31,6 +31,7 @@
# #
def gemfile(install = false, options = {}, &gemfile) def gemfile(install = false, options = {}, &gemfile)
require_relative "../bundler" require_relative "../bundler"
Bundler.reset!
opts = options.dup opts = options.dup
ui = opts.delete(:ui) { Bundler::UI::Shell.new } ui = opts.delete(:ui) { Bundler::UI::Shell.new }
@ -38,9 +39,8 @@ def gemfile(install = false, options = {}, &gemfile)
Bundler.ui = ui Bundler.ui = ui
raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty? raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty?
begin Bundler.with_unbundled_env do
Bundler.instance_variable_set(:@bundle_path, Pathname.new(Gem.dir)) Bundler.instance_variable_set(:@bundle_path, Pathname.new(Gem.dir))
old_gemfile = ENV["BUNDLE_GEMFILE"]
Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile" Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile"
Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins? Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins?
@ -65,11 +65,9 @@ def gemfile(install = false, options = {}, &gemfile)
runtime = Bundler::Runtime.new(nil, definition) runtime = Bundler::Runtime.new(nil, definition)
runtime.setup.require runtime.setup.require
end end
ensure end
if old_gemfile
ENV["BUNDLE_GEMFILE"] = old_gemfile if ENV["BUNDLE_GEMFILE"].nil?
else
ENV["BUNDLE_GEMFILE"] = "" ENV["BUNDLE_GEMFILE"] = ""
end end
end
end end

View file

@ -84,6 +84,9 @@ module Bundler
def reverse_rubygems_kernel_mixin def reverse_rubygems_kernel_mixin
<<~END <<~END
if Gem.respond_to?(:discover_gems_on_require=)
Gem.discover_gems_on_require = false
else
kernel = (class << ::Kernel; self; end) kernel = (class << ::Kernel; self; end)
[kernel, ::Kernel].each do |k| [kernel, ::Kernel].each do |k|
if k.private_method_defined?(:gem_original_require) if k.private_method_defined?(:gem_original_require)
@ -93,6 +96,7 @@ module Bundler
k.send(:private, :require) if private_require k.send(:private, :require) if private_require
end end
end end
end
END END
end end
end end

View file

@ -227,6 +227,9 @@ module Bundler
def reverse_rubygems_kernel_mixin def reverse_rubygems_kernel_mixin
# Disable rubygems' gem activation system # Disable rubygems' gem activation system
if Gem.respond_to?(:discover_gems_on_require=)
Gem.discover_gems_on_require = false
else
kernel = (class << ::Kernel; self; end) kernel = (class << ::Kernel; self; end)
[kernel, ::Kernel].each do |k| [kernel, ::Kernel].each do |k|
if k.private_method_defined?(:gem_original_require) if k.private_method_defined?(:gem_original_require)
@ -234,6 +237,7 @@ module Bundler
end end
end end
end end
end
def replace_gem(specs, specs_by_name) def replace_gem(specs, specs_by_name)
reverse_rubygems_kernel_mixin reverse_rubygems_kernel_mixin

View file

@ -1,7 +1,7 @@
# frozen_string_literal: false # frozen_string_literal: false
module Bundler module Bundler
VERSION = "2.4.5".freeze VERSION = "2.4.6".freeze
def self.bundler_major_version def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i @bundler_major_version ||= VERSION.split(".").first.to_i

View file

@ -8,7 +8,7 @@
require "rbconfig" require "rbconfig"
module Gem module Gem
VERSION = "3.4.5".freeze VERSION = "3.4.6".freeze
end end
# Must be first since it unloads the prelude from 1.9.2 # Must be first since it unloads the prelude from 1.9.2
@ -181,6 +181,8 @@ module Gem
@default_source_date_epoch = nil @default_source_date_epoch = nil
@discover_gems_on_require = true
## ##
# Try to activate a gem containing +path+. Returns true if # Try to activate a gem containing +path+. Returns true if
# activation succeeded or wasn't needed because it was already # activation succeeded or wasn't needed because it was already
@ -1163,8 +1165,16 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# RubyGems distributors (like operating system package managers) can # RubyGems distributors (like operating system package managers) can
# disable RubyGems update by setting this to error message printed to # disable RubyGems update by setting this to error message printed to
# end-users on gem update --system instead of actual update. # end-users on gem update --system instead of actual update.
attr_accessor :disable_system_update_message attr_accessor :disable_system_update_message
##
# Whether RubyGems should enhance builtin `require` to automatically
# check whether the path required is present in installed gems, and
# automatically activate them and add them to `$LOAD_PATH`.
attr_accessor :discover_gems_on_require
## ##
# Hash of loaded Gem::Specification keyed by name # Hash of loaded Gem::Specification keyed by name

View file

@ -34,6 +34,9 @@ module Kernel
# that file has already been loaded is preserved. # that file has already been loaded is preserved.
def require(path) # :doc: def require(path) # :doc:
return gem_original_require(path) unless Gem.discover_gems_on_require
begin
if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?) if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?)
monitor_owned = RUBYGEMS_ACTIVATION_MONITOR.mon_owned? monitor_owned = RUBYGEMS_ACTIVATION_MONITOR.mon_owned?
end end
@ -168,6 +171,7 @@ module Kernel
end end
end end
end end
end
private :require private :require

View file

@ -131,8 +131,7 @@ class Gem::Ext::Builder
when /CMakeLists.txt/ then when /CMakeLists.txt/ then
Gem::Ext::CmakeBuilder Gem::Ext::CmakeBuilder
when /Cargo.toml/ then when /Cargo.toml/ then
# We use the spec name here to ensure we invoke the correct init function later Gem::Ext::CargoBuilder.new
Gem::Ext::CargoBuilder.new(@spec)
else else
build_error("No builder for extension '#{extension}'") build_error("No builder for extension '#{extension}'")
end end

View file

@ -6,30 +6,60 @@
class Gem::Ext::CargoBuilder < Gem::Ext::Builder class Gem::Ext::CargoBuilder < Gem::Ext::Builder
attr_accessor :spec, :runner, :profile attr_accessor :spec, :runner, :profile
def initialize(spec) def initialize
require_relative "../command" require_relative "../command"
require_relative "cargo_builder/link_flag_converter" require_relative "cargo_builder/link_flag_converter"
@spec = spec
@runner = self.class.method(:run) @runner = self.class.method(:run)
@profile = :release @profile = :release
end end
def build(_extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd) def build(extension, dest_path, results, args = [], lib_dir = nil, cargo_dir = Dir.pwd)
require "tempfile"
require "fileutils" require "fileutils"
require "shellwords"
build_crate(dest_path, results, args, cargo_dir) # Where's the Cargo.toml of the crate we're building
validate_cargo_build!(dest_path) cargo_toml = File.join(cargo_dir, "Cargo.toml")
rename_cdylib_for_ruby_compatibility(dest_path) # What's the crate's name
finalize_directory(dest_path, lib_dir, cargo_dir) crate_name = cargo_crate_name(cargo_dir, cargo_toml, results)
results
begin
# Create a tmp dir to do the build in
tmp_dest = Dir.mktmpdir(".gem.", cargo_dir)
# Run the build
cmd = cargo_command(cargo_toml, tmp_dest, args, crate_name)
runner.call(cmd, results, "cargo", cargo_dir, build_env)
# Where do we expect Cargo to write the compiled library
dylib_path = cargo_dylib_path(tmp_dest, crate_name)
# Helpful error if we didn't find the compiled library
raise DylibNotFoundError, tmp_dest unless File.exist?(dylib_path)
# Cargo and Ruby differ on how the library should be named, rename from
# what Cargo outputs to what Ruby expects
dlext_name = "#{crate_name}.#{makefile_config("DLEXT")}"
dlext_path = File.join(File.dirname(dylib_path), dlext_name)
FileUtils.cp(dylib_path, dlext_path)
nesting = extension_nesting(extension)
# TODO: remove in RubyGems 4
if Gem.install_extension_in_lib && lib_dir
nested_lib_dir = File.join(lib_dir, nesting)
FileUtils.mkdir_p nested_lib_dir
FileUtils.cp_r dlext_path, nested_lib_dir, remove_destination: true
end end
def build_crate(dest_path, results, args, cargo_dir) # move to final destination
env = build_env nested_dest_path = File.join(dest_path, nesting)
cmd = cargo_command(cargo_dir, dest_path, args) FileUtils.mkdir_p nested_dest_path
runner.call cmd, results, "cargo", cargo_dir, env FileUtils.cp_r dlext_path, nested_dest_path, remove_destination: true
ensure
# clean up intermediary build artifacts
FileUtils.rm_rf tmp_dest if tmp_dest
end
results results
end end
@ -42,39 +72,59 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
build_env build_env
end end
def cargo_command(cargo_dir, dest_path, args = []) def cargo_command(cargo_toml, dest_path, args = [], crate_name = nil)
manifest = File.join(cargo_dir, "Cargo.toml") require "shellwords"
cargo = ENV.fetch("CARGO", "cargo")
cmd = [] cmd = []
cmd += [cargo, "rustc"] cmd += [cargo, "rustc"]
cmd += ["--crate-type", "cdylib"] cmd += ["--crate-type", "cdylib"]
cmd += ["--target", ENV["CARGO_BUILD_TARGET"]] if ENV["CARGO_BUILD_TARGET"] cmd += ["--target", ENV["CARGO_BUILD_TARGET"]] if ENV["CARGO_BUILD_TARGET"]
cmd += ["--target-dir", dest_path] cmd += ["--target-dir", dest_path]
cmd += ["--manifest-path", manifest] cmd += ["--manifest-path", cargo_toml]
cmd += ["--lib"] cmd += ["--lib"]
cmd += ["--profile", profile.to_s] cmd += ["--profile", profile.to_s]
cmd += ["--locked"] cmd += ["--locked"]
cmd += Gem::Command.build_args cmd += Gem::Command.build_args
cmd += args cmd += args
cmd += ["--"] cmd += ["--"]
cmd += [*cargo_rustc_args(dest_path)] cmd += [*cargo_rustc_args(dest_path, crate_name)]
cmd cmd
end end
private private
def cargo
ENV.fetch("CARGO", "cargo")
end
# returns the directory nesting of the extension, ignoring the first part, so
# "ext/foo/bar/Cargo.toml" becomes "foo/bar"
def extension_nesting(extension)
parts = extension.to_s.split(Regexp.union([File::SEPARATOR, File::ALT_SEPARATOR].compact))
parts = parts.each_with_object([]) do |segment, final|
next if segment == "."
if segment == ".."
raise Gem::InstallError, "extension outside of gem root" if final.empty?
next final.pop
end
final << segment
end
File.join(parts[1...-1])
end
def rb_config_env def rb_config_env
result = {} result = {}
RbConfig::CONFIG.each {|k, v| result["RBCONFIG_#{k}"] = v } RbConfig::CONFIG.each {|k, v| result["RBCONFIG_#{k}"] = v }
result result
end end
def cargo_rustc_args(dest_dir) def cargo_rustc_args(dest_dir, crate_name)
[ [
*linker_args, *linker_args,
*mkmf_libpath, *mkmf_libpath,
*rustc_dynamic_linker_flags(dest_dir), *rustc_dynamic_linker_flags(dest_dir, crate_name),
*rustc_lib_flags(dest_dir), *rustc_lib_flags(dest_dir),
*platform_specific_rustc_args(dest_dir), *platform_specific_rustc_args(dest_dir),
] ]
@ -134,42 +184,70 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
makefile_config("ENABLE_SHARED") == "no" makefile_config("ENABLE_SHARED") == "no"
end end
# Ruby expects the dylib to follow a file name convention for loading def cargo_dylib_path(dest_path, crate_name)
def rename_cdylib_for_ruby_compatibility(dest_path)
new_path = final_extension_path(dest_path)
FileUtils.cp(cargo_dylib_path(dest_path), new_path)
new_path
end
def validate_cargo_build!(dir)
dylib_path = cargo_dylib_path(dir)
raise DylibNotFoundError, dir unless File.exist?(dylib_path)
dylib_path
end
def final_extension_path(dest_path)
dylib_path = cargo_dylib_path(dest_path)
dlext_name = "#{spec.name}.#{makefile_config("DLEXT")}"
dylib_path.gsub(File.basename(dylib_path), dlext_name)
end
def cargo_dylib_path(dest_path)
prefix = so_ext == "dll" ? "" : "lib" prefix = so_ext == "dll" ? "" : "lib"
path_parts = [dest_path] path_parts = [dest_path]
path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"] path_parts << ENV["CARGO_BUILD_TARGET"] if ENV["CARGO_BUILD_TARGET"]
path_parts += ["release", "#{prefix}#{cargo_crate_name}.#{so_ext}"] path_parts += ["release", "#{prefix}#{crate_name}.#{so_ext}"]
File.join(*path_parts) File.join(*path_parts)
end end
def cargo_crate_name def cargo_crate_name(cargo_dir, manifest_path, results)
spec.metadata.fetch("cargo_crate_name", spec.name).tr("-", "_") require "open3"
Gem.load_yaml
output, status =
begin
Open3.capture2e(cargo, "metadata", "--no-deps", "--format-version", "1", :chdir => cargo_dir)
rescue => error
raise Gem::InstallError, "cargo metadata failed #{error.message}"
end end
def rustc_dynamic_linker_flags(dest_dir) unless status.success?
if Gem.configuration.really_verbose
puts output
else
results << output
end
exit_reason =
if status.exited?
", exit code #{status.exitstatus}"
elsif status.signaled?
", uncaught signal #{status.termsig}"
end
raise Gem::InstallError, "cargo metadata failed#{exit_reason}"
end
# cargo metadata output is specified as json, but with the
# --format-version 1 option the output is compatible with YAML, so we can
# avoid the json dependency
metadata = Gem::SafeYAML.safe_load(output)
package = metadata["packages"].find {|pkg| normalize_path(pkg["manifest_path"]) == manifest_path }
unless package
found = metadata["packages"].map {|md| "#{md["name"]} at #{md["manifest_path"]}" }
raise Gem::InstallError, <<-EOF
failed to determine cargo package name
looking for: #{manifest_path}
found:
#{found.join("\n")}
EOF
end
package["name"].tr("-", "_")
end
def normalize_path(path)
return path unless File::ALT_SEPARATOR
path.tr(File::ALT_SEPARATOR, File::SEPARATOR)
end
def rustc_dynamic_linker_flags(dest_dir, crate_name)
split_flags("DLDFLAGS") split_flags("DLDFLAGS")
.map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir) } .map {|arg| maybe_resolve_ldflag_variable(arg, dest_dir, crate_name) }
.compact .compact
.flat_map {|arg| ldflag_to_link_modifier(arg) } .flat_map {|arg| ldflag_to_link_modifier(arg) }
end end
@ -204,7 +282,7 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
end end
# Interpolate substitution vars in the arg (i.e. $(DEFFILE)) # Interpolate substitution vars in the arg (i.e. $(DEFFILE))
def maybe_resolve_ldflag_variable(input_arg, dest_dir) def maybe_resolve_ldflag_variable(input_arg, dest_dir, crate_name)
var_matches = input_arg.match(/\$\((\w+)\)/) var_matches = input_arg.match(/\$\((\w+)\)/)
return input_arg unless var_matches return input_arg unless var_matches
@ -217,19 +295,19 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# On windows, it is assumed that mkmf has setup an exports file for the # On windows, it is assumed that mkmf has setup an exports file for the
# extension, so we have to to create one ourselves. # extension, so we have to to create one ourselves.
when "DEFFILE" when "DEFFILE"
write_deffile(dest_dir) write_deffile(dest_dir, crate_name)
else else
RbConfig::CONFIG[var_name] RbConfig::CONFIG[var_name]
end end
end end
def write_deffile(dest_dir) def write_deffile(dest_dir, crate_name)
deffile_path = File.join(dest_dir, "#{spec.name}-#{RbConfig::CONFIG["arch"]}.def") deffile_path = File.join(dest_dir, "#{crate_name}-#{RbConfig::CONFIG["arch"]}.def")
export_prefix = makefile_config("EXPORT_PREFIX") || "" export_prefix = makefile_config("EXPORT_PREFIX") || ""
File.open(deffile_path, "w") do |f| File.open(deffile_path, "w") do |f|
f.puts "EXPORTS" f.puts "EXPORTS"
f.puts "#{export_prefix.strip}Init_#{spec.name}" f.puts "#{export_prefix.strip}Init_#{crate_name}"
end end
deffile_path deffile_path
@ -264,44 +342,6 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
RbConfig.expand(val.dup) RbConfig.expand(val.dup)
end end
# Copied from ExtConfBuilder
def finalize_directory(dest_path, lib_dir, extension_dir)
require "fileutils"
require "tempfile"
ext_path = final_extension_path(dest_path)
begin
tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
# Some versions of `mktmpdir` return absolute paths, which will break make
# if the paths contain spaces.
#
# As such, we convert to a relative path.
tmp_dest_relative = get_relative_path(tmp_dest.clone, extension_dir)
full_tmp_dest = File.join(extension_dir, tmp_dest_relative)
# TODO: remove in RubyGems 4
if Gem.install_extension_in_lib && lib_dir
FileUtils.mkdir_p lib_dir
FileUtils.cp_r ext_path, lib_dir, remove_destination: true
end
FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
destent = ent.class.new(dest_path, ent.rel)
destent.exist? || FileUtils.mv(ent.path, destent.path)
end
ensure
FileUtils.rm_rf tmp_dest if tmp_dest
end
end
def get_relative_path(path, base)
path[0..base.length - 1] = "." if path.start_with?(base)
path
end
# Error raised when no cdylib artifact was created # Error raised when no cdylib artifact was created
class DylibNotFoundError < StandardError class DylibNotFoundError < StandardError
def initialize(dir) def initialize(dir)

View file

@ -466,7 +466,7 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
def validate_rust_extensions(builder) # :nodoc: def validate_rust_extensions(builder) # :nodoc:
rust_extension = @specification.extensions.any? {|s| builder.builder_for(s).is_a? Gem::Ext::CargoBuilder } rust_extension = @specification.extensions.any? {|s| builder.builder_for(s).is_a? Gem::Ext::CargoBuilder }
missing_cargo_lock = !@specification.files.include?("Cargo.lock") missing_cargo_lock = !@specification.files.any? {|f| f.end_with?("Cargo.lock") }
error <<-ERROR if rust_extension && missing_cargo_lock error <<-ERROR if rust_extension && missing_cargo_lock
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. 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.

View file

@ -369,6 +369,7 @@ RSpec.describe "bundle binstubs <gem>" do
install_gemfile <<-G install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}" source "#{file_uri_for(gem_repo1)}"
gem "rack" gem "rack"
gem "rails"
G G
end end
@ -396,6 +397,26 @@ RSpec.describe "bundle binstubs <gem>" do
expect(bundled_app("bin/rackup.cmd")).to exist expect(bundled_app("bin/rackup.cmd")).to exist
end end
end end
context "when the gem is bundler" do
it "warns without generating a standalone binstub" do
bundle "binstubs bundler --standalone"
expect(bundled_app("bin/bundle")).not_to exist
expect(bundled_app("bin/bundler")).not_to exist
expect(err).to include("Sorry, Bundler can only be run via RubyGems.")
end
end
context "when specified --all option" do
it "generates standalone binstubs for all gems except bundler" do
bundle "binstubs --standalone --all"
expect(bundled_app("bin/rackup")).to exist
expect(bundled_app("bin/rails")).to exist
expect(bundled_app("bin/bundle")).not_to exist
expect(bundled_app("bin/bundler")).not_to exist
expect(err).not_to include("Sorry, Bundler can only be run via RubyGems.")
end
end
end end
context "when the bin already exists" do context "when the bin already exists" do

View file

@ -2,11 +2,10 @@
RSpec.describe "bundle exec" do RSpec.describe "bundle exec" do
let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] } let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] }
before :each do
system_gems(system_gems_to_install, :path => default_bundle_path)
end
it "works with --gemfile flag" do it "works with --gemfile flag" do
system_gems(system_gems_to_install, :path => default_bundle_path)
create_file "CustomGemfile", <<-G create_file "CustomGemfile", <<-G
source "#{file_uri_for(gem_repo1)}" source "#{file_uri_for(gem_repo1)}"
gem "rack", "1.0.0" gem "rack", "1.0.0"
@ -17,6 +16,8 @@ RSpec.describe "bundle exec" do
end end
it "activates the correct gem" do it "activates the correct gem" do
system_gems(system_gems_to_install, :path => default_bundle_path)
gemfile <<-G gemfile <<-G
source "#{file_uri_for(gem_repo1)}" source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1" gem "rack", "0.9.1"
@ -27,6 +28,8 @@ RSpec.describe "bundle exec" do
end end
it "works and prints no warnings when HOME is not writable" do it "works and prints no warnings when HOME is not writable" do
system_gems(system_gems_to_install, :path => default_bundle_path)
gemfile <<-G gemfile <<-G
source "#{file_uri_for(gem_repo1)}" source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1" gem "rack", "0.9.1"
@ -209,8 +212,6 @@ RSpec.describe "bundle exec" do
end end
context "with default gems" do context "with default gems" do
let(:system_gems_to_install) { [] }
let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false } let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false }
context "when not specified in Gemfile" do context "when not specified in Gemfile" do
@ -402,6 +403,8 @@ RSpec.describe "bundle exec" do
end end
it "raises a helpful error when exec'ing to something outside of the bundle" do it "raises a helpful error when exec'ing to something outside of the bundle" do
system_gems(system_gems_to_install, :path => default_bundle_path)
bundle "config set clean false" # want to keep the rackup binstub bundle "config set clean false" # want to keep the rackup binstub
install_gemfile <<-G install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}" source "#{file_uri_for(gem_repo1)}"
@ -706,6 +709,8 @@ RSpec.describe "bundle exec" do
RUBY RUBY
before do before do
system_gems(system_gems_to_install, :path => default_bundle_path)
bundled_app(path).open("w") {|f| f << executable } bundled_app(path).open("w") {|f| f << executable }
bundled_app(path).chmod(0o755) bundled_app(path).chmod(0o755)

View file

@ -821,6 +821,64 @@ RSpec.describe "bundle install with specific platforms" do
bundle :install bundle :install
end end
it "automatically fixes the lockfile if the specific platform is locked and we move to a newer ruby version for which a native package is not available" do
#
# Given an existing application using native gems (e.g., nokogiri)
# And a lockfile generated with a stable ruby version
# When want test the application against ruby-head and `bundle install`
# Then bundler should fall back to the generic ruby platform gem
#
simulate_platform "x86_64-linux" do
build_repo4 do
build_gem "nokogiri", "1.14.0"
build_gem "nokogiri", "1.14.0" do |s|
s.platform = "x86_64-linux"
s.required_ruby_version = "< #{Gem.ruby_version}"
end
end
gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "nokogiri", "1.14.0"
G
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
nokogiri (1.14.0-x86_64-linux)
PLATFORMS
x86_64-linux
DEPENDENCIES
nokogiri (= 1.14.0)
BUNDLED WITH
#{Bundler::VERSION}
L
bundle :install
expect(lockfile).to eq(<<~L)
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
nokogiri (1.14.0)
PLATFORMS
x86_64-linux
DEPENDENCIES
nokogiri (= 1.14.0)
BUNDLED WITH
#{Bundler::VERSION}
L
end
end
private private
def setup_multiplatform_gem def setup_multiplatform_gem

View file

@ -230,6 +230,113 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty expect(err).to be_empty
end end
it "doesn't reinstall already installed gems" do
system_gems "rack-1.0.0"
script <<-RUBY
require '#{entrypoint}'
ui = Bundler::UI::Shell.new
ui.level = "confirm"
gemfile(true, ui: ui) do
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
gem "rack"
end
RUBY
expect(out).to include("Installing activesupport")
expect(out).not_to include("Installing rack")
expect(err).to be_empty
end
it "installs gems in later gemfile calls" do
system_gems "rack-1.0.0"
script <<-RUBY
require '#{entrypoint}'
ui = Bundler::UI::Shell.new
ui.level = "confirm"
gemfile(true, ui: ui) do
source "#{file_uri_for(gem_repo1)}"
gem "rack"
end
gemfile(true, ui: ui) do
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
end
RUBY
expect(out).to include("Installing activesupport")
expect(out).not_to include("Installing rack")
expect(err).to be_empty
end
it "doesn't reinstall already installed gems in later gemfile calls" do
system_gems "rack-1.0.0"
script <<-RUBY
require '#{entrypoint}'
ui = Bundler::UI::Shell.new
ui.level = "confirm"
gemfile(true, ui: ui) do
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
end
gemfile(true, ui: ui) do
source "#{file_uri_for(gem_repo1)}"
gem "rack"
end
RUBY
expect(out).to include("Installing activesupport")
expect(out).not_to include("Installing rack")
expect(err).to be_empty
end
it "installs gems with native extensions in later gemfile calls" do
system_gems "rack-1.0.0"
build_git "foo" do |s|
s.add_dependency "rake"
s.extensions << "Rakefile"
s.write "Rakefile", <<-RUBY
task :default do
path = File.expand_path("lib", __dir__)
FileUtils.mkdir_p(path)
File.open("\#{path}/foo.rb", "w") do |f|
f.puts "FOO = 'YES'"
end
end
RUBY
end
script <<-RUBY
require '#{entrypoint}'
ui = Bundler::UI::Shell.new
ui.level = "confirm"
gemfile(true, ui: ui) do
source "#{file_uri_for(gem_repo1)}"
gem "rack"
end
gemfile(true, ui: ui) do
source "#{file_uri_for(gem_repo1)}"
gem "foo", :git => "#{lib_path("foo-1.0")}"
end
require 'foo'
puts FOO
puts $:.grep(/ext/)
RUBY
expect(out).to include("YES")
expect(out).to include(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
expect(err).to be_empty
end
it "installs inline gems when a Gemfile.lock is present" do it "installs inline gems when a Gemfile.lock is present" do
gemfile <<-G gemfile <<-G
source "https://notaserver.com" source "https://notaserver.com"

View file

@ -1519,4 +1519,29 @@ end
expect(err).to be_empty expect(err).to be_empty
end end
end end
it "does not undo the Kernel.require decorations", :rubygems => ">= 3.4.6" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
script = bundled_app("bin/script")
create_file(script, <<~RUBY)
module Kernel
module_function
alias_method :require_before_extra_monkeypatches, :require
def require(path)
puts "requiring \#{path} used the monkeypatch"
require_before_extra_monkeypatches(path)
end
end
require "bundler/setup"
require "foo"
RUBY
sys_exec "#{Gem.ruby} #{script}", :raise_on_error => false
expect(out).to include("requiring foo used the monkeypatch")
end
end end

View file

@ -0,0 +1,419 @@
require_relative "helper"
class TestBundlerGem < Gem::TestCase
PROJECT_DIR = File.expand_path("../..", __dir__).tap(&Gem::UNTAINT)
def test_self_use_gemdeps
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("-") do
FileUtils.mkdir_p "detect/a/b"
FileUtils.mkdir_p "detect/a/Isolate"
FileUtils.touch "detect/Isolate"
begin
Dir.chdir "detect/a/b"
Gem.use_gemdeps
assert_equal add_bundler_full_name([]), loaded_spec_names
ensure
Dir.chdir @tempdir
end
end
end
end
def test_self_find_files_with_gemfile
with_local_bundler_at(Gem.dir) do
cwd = File.expand_path("test/rubygems", PROJECT_DIR)
actual_load_path = $LOAD_PATH.unshift(cwd).dup
discover_path = File.join "lib", "sff", "discover.rb"
foo1, _ = %w[1 2].map do |version|
spec = quick_gem "sff", version do |s|
s.files << discover_path
end
write_file(File.join "gems", spec.full_name, discover_path) do |fp|
fp.puts "# #{spec.full_name}"
end
spec
end
Gem.refresh
write_file(File.join Dir.pwd, "Gemfile") do |fp|
fp.puts "source 'https://rubygems.org'"
fp.puts "gem '#{foo1.name}', '#{foo1.version}'"
end
Gem.use_gemdeps(File.join Dir.pwd, "Gemfile")
expected = [
File.expand_path("test/rubygems/sff/discover.rb", PROJECT_DIR),
File.join(foo1.full_gem_path, discover_path),
].sort
assert_equal expected, Gem.find_files("sff/discover").sort
assert_equal expected, Gem.find_files("sff/**.rb").sort, "[ruby-core:31730]"
assert_equal cwd, actual_load_path.shift
end
end
def test_auto_activation_of_specific_gemdeps_file
with_local_bundler_at(Gem.dir) do
a = util_spec "a", "1", nil, "lib/a.rb"
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
path = File.join @tempdir, "gem.deps.rb"
File.open path, "w" do |f|
f.puts "gem 'a'"
f.puts "gem 'b'"
f.puts "gem 'c'"
end
with_rubygems_gemdeps(path) do
Gem.use_gemdeps
assert_equal add_bundler_full_name(%W[a-1 b-1 c-1]), loaded_spec_names
end
end
end
def test_auto_activation_of_used_gemdeps_file
with_local_bundler_at(Gem.dir) do
a = util_spec "a", "1", nil, "lib/a.rb"
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
path = File.join @tempdir, "gem.deps.rb"
File.open path, "w" do |f|
f.puts "gem 'a'"
f.puts "gem 'b'"
f.puts "gem 'c'"
end
with_rubygems_gemdeps("-") do
expected_specs = [a, b, util_spec("bundler", Bundler::VERSION), c].compact.map(&:full_name)
Gem.use_gemdeps
assert_equal expected_specs, loaded_spec_names
end
end
end
def test_looks_for_gemdeps_files_automatically_from_binstubs
path = File.join(@tempdir, "gd-tmp")
with_local_bundler_at(path) do
a = util_spec "a", "1" do |s|
s.executables = %w[foo]
s.bindir = "exe"
end
write_file File.join(@tempdir, "exe", "foo") do |fp|
fp.puts "puts Gem.loaded_specs.values.map(&:full_name).sort"
end
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
install_gem a, :install_dir => path
install_gem b, :install_dir => path
install_gem c, :install_dir => path
ENV["GEM_PATH"] = path
with_rubygems_gemdeps("-") do
new_PATH = [File.join(path, "bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
new_RUBYOPT = "-I#{rubygems_path} -I#{bundler_path}"
path = File.join @tempdir, "gem.deps.rb"
File.open path, "w" do |f|
f.puts "gem 'a'"
end
out0 = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
IO.popen("foo", &:read).split(/\n/)
end
File.open path, "a" do |f|
f.puts "gem 'b'"
f.puts "gem 'c'"
end
out = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
IO.popen("foo", &:read).split(/\n/)
end
assert_equal ["b-1", "c-1"], out - out0
end
end
end
def test_looks_for_gemdeps_files_automatically_from_binstubs_in_parent_dir
path = File.join(@tempdir, "gd-tmp")
with_local_bundler_at(path) do
pend "IO.popen has issues on JRuby when passed :chdir" if Gem.java_platform?
a = util_spec "a", "1" do |s|
s.executables = %w[foo]
s.bindir = "exe"
end
write_file File.join(@tempdir, "exe", "foo") do |fp|
fp.puts "puts Gem.loaded_specs.values.map(&:full_name).sort"
end
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
install_gem a, :install_dir => path
install_gem b, :install_dir => path
install_gem c, :install_dir => path
ENV["GEM_PATH"] = path
with_rubygems_gemdeps("-") do
Dir.mkdir "sub1"
new_PATH = [File.join(path, "bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
new_RUBYOPT = "-I#{rubygems_path} -I#{bundler_path}"
path = File.join @tempdir, "gem.deps.rb"
File.open path, "w" do |f|
f.puts "gem 'a'"
end
out0 = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
IO.popen("foo", :chdir => "sub1", &:read).split(/\n/)
end
File.open path, "a" do |f|
f.puts "gem 'b'"
f.puts "gem 'c'"
end
out = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
IO.popen("foo", :chdir => "sub1", &:read).split(/\n/)
end
Dir.rmdir "sub1"
assert_equal ["b-1", "c-1"], out - out0
end
end
end
def test_use_gemdeps
with_local_bundler_at(Gem.dir) do
gem_deps_file = "gem.deps.rb".tap(&Gem::UNTAINT)
spec = util_spec "a", 1
install_specs spec
spec = Gem::Specification.find {|s| s == spec }
refute spec.activated?
File.open gem_deps_file, "w" do |io|
io.write 'gem "a"'
end
assert_nil Gem.gemdeps
Gem.use_gemdeps gem_deps_file
assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
refute_nil Gem.gemdeps
end
end
def test_use_gemdeps_ENV
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps(nil) do
spec = util_spec "a", 1
refute spec.activated?
File.open "gem.deps.rb", "w" do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
refute spec.activated?
end
end
end
def test_use_gemdeps_argument_missing
with_local_bundler_at(Gem.dir) do
e = assert_raise ArgumentError do
Gem.use_gemdeps "gem.deps.rb"
end
assert_equal "Unable to find gem dependencies file at gem.deps.rb",
e.message
end
end
def test_use_gemdeps_argument_missing_match_ENV
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("gem.deps.rb") do
e = assert_raise ArgumentError do
Gem.use_gemdeps "gem.deps.rb"
end
assert_equal "Unable to find gem dependencies file at gem.deps.rb",
e.message
end
end
end
def test_use_gemdeps_automatic
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("-") do
spec = util_spec "a", 1
install_specs spec
spec = Gem::Specification.find {|s| s == spec }
refute spec.activated?
File.open "Gemfile", "w" do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
end
end
end
def test_use_gemdeps_automatic_missing
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("-") do
Gem.use_gemdeps
assert true # count
end
end
end
def test_use_gemdeps_disabled
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("") do
spec = util_spec "a", 1
refute spec.activated?
File.open "gem.deps.rb", "w" do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
refute spec.activated?
end
end
end
def test_use_gemdeps_missing_gem
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("x") do
File.open "x", "w" do |io|
io.write 'gem "a"'
end
expected = <<-EXPECTED
Could not find gem 'a' in locally installed gems.
You may need to `bundle install` to install missing gems
EXPECTED
Gem::Deprecate.skip_during do
actual_stdout, actual_stderr = capture_output do
Gem.use_gemdeps
end
assert_empty actual_stdout
assert_equal(expected, actual_stderr)
end
end
end
end
def test_use_gemdeps_specific
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("x") do
spec = util_spec "a", 1
install_specs spec
spec = Gem::Specification.find {|s| s == spec }
refute spec.activated?
File.open "x", "w" do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
end
end
end
private
def add_bundler_full_name(names)
names << "bundler-#{Bundler::VERSION}".freeze
names.sort!
names
end
def with_path_and_rubyopt(path_value, rubyopt_value)
path, ENV["PATH"] = ENV["PATH"], path_value
rubyopt, ENV["RUBYOPT"] = ENV["RUBYOPT"], rubyopt_value
yield
ensure
ENV["PATH"] = path
ENV["RUBYOPT"] = rubyopt
end
def with_rubygems_gemdeps(value)
rubygems_gemdeps, ENV["RUBYGEMS_GEMDEPS"] = ENV["RUBYGEMS_GEMDEPS"], value
yield
ensure
ENV["RUBYGEMS_GEMDEPS"] = rubygems_gemdeps
end
def with_local_bundler_at(path)
require "bundler"
# If bundler gemspec exists, pretend it's installed
bundler_gemspec = File.expand_path("../../bundler/bundler.gemspec", __dir__)
if File.exist?(bundler_gemspec)
target_gemspec_location = "#{path}/specifications/bundler-#{Bundler::VERSION}.gemspec"
FileUtils.mkdir_p File.dirname(target_gemspec_location)
File.write target_gemspec_location, Gem::Specification.load(bundler_gemspec).to_ruby_for_cache
end
yield
ensure
Bundler.reset!
end
end

View file

@ -616,27 +616,6 @@ class TestGem < Gem::TestCase
assert_equal %w[https://rubygems.org/], Gem.default_sources assert_equal %w[https://rubygems.org/], Gem.default_sources
end end
def test_self_use_gemdeps
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("-") do
FileUtils.mkdir_p "detect/a/b"
FileUtils.mkdir_p "detect/a/Isolate"
FileUtils.touch "detect/Isolate"
begin
Dir.chdir "detect/a/b"
Gem.use_gemdeps
assert_equal add_bundler_full_name([]), loaded_spec_names
ensure
Dir.chdir @tempdir
end
end
end
end
def test_self_dir def test_self_dir
assert_equal @gemhome, Gem.dir assert_equal @gemhome, Gem.dir
end end
@ -776,43 +755,6 @@ class TestGem < Gem::TestCase
assert_equal cwd, $LOAD_PATH.shift assert_equal cwd, $LOAD_PATH.shift
end end
def test_self_find_files_with_gemfile
with_local_bundler_at(Gem.dir) do
cwd = File.expand_path("test/rubygems", PROJECT_DIR)
actual_load_path = $LOAD_PATH.unshift(cwd).dup
discover_path = File.join "lib", "sff", "discover.rb"
foo1, _ = %w[1 2].map do |version|
spec = quick_gem "sff", version do |s|
s.files << discover_path
end
write_file(File.join "gems", spec.full_name, discover_path) do |fp|
fp.puts "# #{spec.full_name}"
end
spec
end
Gem.refresh
write_file(File.join Dir.pwd, "Gemfile") do |fp|
fp.puts "source 'https://rubygems.org'"
fp.puts "gem '#{foo1.name}', '#{foo1.version}'"
end
Gem.use_gemdeps(File.join Dir.pwd, "Gemfile")
expected = [
File.expand_path("test/rubygems/sff/discover.rb", PROJECT_DIR),
File.join(foo1.full_gem_path, discover_path),
].sort
assert_equal expected, Gem.find_files("sff/discover").sort
assert_equal expected, Gem.find_files("sff/**.rb").sort, "[ruby-core:31730]"
assert_equal cwd, actual_load_path.shift
end
end
def test_self_find_latest_files def test_self_find_latest_files
cwd = File.expand_path("test/rubygems", PROJECT_DIR) cwd = File.expand_path("test/rubygems", PROJECT_DIR)
$LOAD_PATH.unshift cwd $LOAD_PATH.unshift cwd
@ -1640,168 +1582,6 @@ class TestGem < Gem::TestCase
"Wrong spec selected" "Wrong spec selected"
end end
def test_auto_activation_of_specific_gemdeps_file
with_local_bundler_at(Gem.dir) do
a = util_spec "a", "1", nil, "lib/a.rb"
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
path = File.join @tempdir, "gem.deps.rb"
File.open path, "w" do |f|
f.puts "gem 'a'"
f.puts "gem 'b'"
f.puts "gem 'c'"
end
with_rubygems_gemdeps(path) do
Gem.use_gemdeps
assert_equal add_bundler_full_name(%W[a-1 b-1 c-1]), loaded_spec_names
end
end
end
def test_auto_activation_of_used_gemdeps_file
with_local_bundler_at(Gem.dir) do
a = util_spec "a", "1", nil, "lib/a.rb"
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
path = File.join @tempdir, "gem.deps.rb"
File.open path, "w" do |f|
f.puts "gem 'a'"
f.puts "gem 'b'"
f.puts "gem 'c'"
end
with_rubygems_gemdeps("-") do
expected_specs = [a, b, util_spec("bundler", Bundler::VERSION), c].compact.map(&:full_name)
Gem.use_gemdeps
assert_equal expected_specs, loaded_spec_names
end
end
end
def add_bundler_full_name(names)
names << "bundler-#{Bundler::VERSION}".freeze
names.sort!
names
end
def test_looks_for_gemdeps_files_automatically_from_binstubs
path = File.join(@tempdir, "gd-tmp")
with_local_bundler_at(path) do
a = util_spec "a", "1" do |s|
s.executables = %w[foo]
s.bindir = "exe"
end
write_file File.join(@tempdir, "exe", "foo") do |fp|
fp.puts "puts Gem.loaded_specs.values.map(&:full_name).sort"
end
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
install_gem a, :install_dir => path
install_gem b, :install_dir => path
install_gem c, :install_dir => path
ENV["GEM_PATH"] = path
with_rubygems_gemdeps("-") do
new_PATH = [File.join(path, "bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
new_RUBYOPT = "-I#{rubygems_path} -I#{bundler_path}"
path = File.join @tempdir, "gem.deps.rb"
File.open path, "w" do |f|
f.puts "gem 'a'"
end
out0 = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
IO.popen("foo", &:read).split(/\n/)
end
File.open path, "a" do |f|
f.puts "gem 'b'"
f.puts "gem 'c'"
end
out = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
IO.popen("foo", &:read).split(/\n/)
end
assert_equal ["b-1", "c-1"], out - out0
end
end
end
def test_looks_for_gemdeps_files_automatically_from_binstubs_in_parent_dir
path = File.join(@tempdir, "gd-tmp")
with_local_bundler_at(path) do
pend "IO.popen has issues on JRuby when passed :chdir" if Gem.java_platform?
a = util_spec "a", "1" do |s|
s.executables = %w[foo]
s.bindir = "exe"
end
write_file File.join(@tempdir, "exe", "foo") do |fp|
fp.puts "puts Gem.loaded_specs.values.map(&:full_name).sort"
end
b = util_spec "b", "1", nil, "lib/b.rb"
c = util_spec "c", "1", nil, "lib/c.rb"
install_specs a, b, c
install_gem a, :install_dir => path
install_gem b, :install_dir => path
install_gem c, :install_dir => path
ENV["GEM_PATH"] = path
with_rubygems_gemdeps("-") do
Dir.mkdir "sub1"
new_PATH = [File.join(path, "bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
new_RUBYOPT = "-I#{rubygems_path} -I#{bundler_path}"
path = File.join @tempdir, "gem.deps.rb"
File.open path, "w" do |f|
f.puts "gem 'a'"
end
out0 = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
IO.popen("foo", :chdir => "sub1", &:read).split(/\n/)
end
File.open path, "a" do |f|
f.puts "gem 'b'"
f.puts "gem 'c'"
end
out = with_path_and_rubyopt(new_PATH, new_RUBYOPT) do
IO.popen("foo", :chdir => "sub1", &:read).split(/\n/)
end
Dir.rmdir "sub1"
assert_equal ["b-1", "c-1"], out - out0
end
end
end
def test_register_default_spec def test_register_default_spec
Gem.clear_default_specs Gem.clear_default_specs
@ -1842,162 +1622,6 @@ class TestGem < Gem::TestCase
assert_equal old_style, Gem.find_unresolved_default_spec("foo.rb") assert_equal old_style, Gem.find_unresolved_default_spec("foo.rb")
end end
def test_use_gemdeps
with_local_bundler_at(Gem.dir) do
gem_deps_file = "gem.deps.rb".tap(&Gem::UNTAINT)
spec = util_spec "a", 1
install_specs spec
spec = Gem::Specification.find {|s| s == spec }
refute spec.activated?
File.open gem_deps_file, "w" do |io|
io.write 'gem "a"'
end
assert_nil Gem.gemdeps
Gem.use_gemdeps gem_deps_file
assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
refute_nil Gem.gemdeps
end
end
def test_use_gemdeps_ENV
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps(nil) do
spec = util_spec "a", 1
refute spec.activated?
File.open "gem.deps.rb", "w" do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
refute spec.activated?
end
end
end
def test_use_gemdeps_argument_missing
with_local_bundler_at(Gem.dir) do
e = assert_raise ArgumentError do
Gem.use_gemdeps "gem.deps.rb"
end
assert_equal "Unable to find gem dependencies file at gem.deps.rb",
e.message
end
end
def test_use_gemdeps_argument_missing_match_ENV
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("gem.deps.rb") do
e = assert_raise ArgumentError do
Gem.use_gemdeps "gem.deps.rb"
end
assert_equal "Unable to find gem dependencies file at gem.deps.rb",
e.message
end
end
end
def test_use_gemdeps_automatic
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("-") do
spec = util_spec "a", 1
install_specs spec
spec = Gem::Specification.find {|s| s == spec }
refute spec.activated?
File.open "Gemfile", "w" do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
end
end
end
def test_use_gemdeps_automatic_missing
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("-") do
Gem.use_gemdeps
assert true # count
end
end
end
def test_use_gemdeps_disabled
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("") do
spec = util_spec "a", 1
refute spec.activated?
File.open "gem.deps.rb", "w" do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
refute spec.activated?
end
end
end
def test_use_gemdeps_missing_gem
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("x") do
File.open "x", "w" do |io|
io.write 'gem "a"'
end
expected = <<-EXPECTED
Could not find gem 'a' in locally installed gems.
You may need to `bundle install` to install missing gems
EXPECTED
Gem::Deprecate.skip_during do
actual_stdout, actual_stderr = capture_output do
Gem.use_gemdeps
end
assert_empty actual_stdout
assert_equal(expected, actual_stderr)
end
end
end
end
def test_use_gemdeps_specific
with_local_bundler_at(Gem.dir) do
with_rubygems_gemdeps("x") do
spec = util_spec "a", 1
install_specs spec
spec = Gem::Specification.find {|s| s == spec }
refute spec.activated?
File.open "x", "w" do |io|
io.write 'gem "a"'
end
Gem.use_gemdeps
assert_equal add_bundler_full_name(%W[a-1]), loaded_spec_names
end
end
end
def test_operating_system_defaults def test_operating_system_defaults
operating_system_defaults = Gem.operating_system_defaults operating_system_defaults = Gem.operating_system_defaults
@ -2110,40 +1734,4 @@ You may need to `bundle install` to install missing gems
def util_cache_dir def util_cache_dir
File.join Gem.dir, "cache" File.join Gem.dir, "cache"
end end
def with_path_and_rubyopt(path_value, rubyopt_value)
path, ENV["PATH"] = ENV["PATH"], path_value
rubyopt, ENV["RUBYOPT"] = ENV["RUBYOPT"], rubyopt_value
yield
ensure
ENV["PATH"] = path
ENV["RUBYOPT"] = rubyopt
end
def with_rubygems_gemdeps(value)
rubygems_gemdeps, ENV["RUBYGEMS_GEMDEPS"] = ENV["RUBYGEMS_GEMDEPS"], value
yield
ensure
ENV["RUBYGEMS_GEMDEPS"] = rubygems_gemdeps
end
def with_local_bundler_at(path)
require "bundler"
# If bundler gemspec exists, pretend it's installed
bundler_gemspec = File.expand_path("../../bundler/bundler.gemspec", __dir__)
if File.exist?(bundler_gemspec)
target_gemspec_location = "#{path}/specifications/bundler-#{Bundler::VERSION}.gemspec"
FileUtils.mkdir_p File.dirname(target_gemspec_location)
File.write target_gemspec_location, Gem::Specification.load(bundler_gemspec).to_ruby_for_cache
end
yield
ensure
Bundler.reset!
end
end end

View file

@ -31,13 +31,12 @@ class TestGemExtCargoBuilder < Gem::TestCase
Dir.chdir @ext do Dir.chdir @ext do
ENV.update(@rust_envs) ENV.update(@rust_envs)
spec = Gem::Specification.new "rust_ruby_example", "0.1.0" builder = Gem::Ext::CargoBuilder.new
builder = Gem::Ext::CargoBuilder.new(spec) builder.build "Cargo.toml", @dest_path, output
builder.build nil, @dest_path, output
end end
output = output.join "\n" output = output.join "\n"
bundle = File.join(@dest_path, "release/rust_ruby_example.#{RbConfig::CONFIG['DLEXT']}") bundle = File.join(@dest_path, "rust_ruby_example.#{RbConfig::CONFIG['DLEXT']}")
assert_match(/Finished/, output) assert_match(/Finished/, output)
assert_match(/release/, output) assert_match(/release/, output)
@ -58,13 +57,12 @@ class TestGemExtCargoBuilder < Gem::TestCase
Dir.chdir @ext do Dir.chdir @ext do
ENV.update(@rust_envs) ENV.update(@rust_envs)
spec = Gem::Specification.new "rust_ruby_example", "0.1.0" builder = Gem::Ext::CargoBuilder.new
builder = Gem::Ext::CargoBuilder.new(spec) builder.build "Cargo.toml", @dest_path, output
builder.build nil, @dest_path, output
end end
output = output.join "\n" output = output.join "\n"
bundle = File.join(@dest_path, "release/rust_ruby_example.#{RbConfig::CONFIG['DLEXT']}") bundle = File.join(@dest_path, "rust_ruby_example.#{RbConfig::CONFIG['DLEXT']}")
assert_ffi_handle bundle, "hello_from_rubygems" assert_ffi_handle bundle, "hello_from_rubygems"
assert_ffi_handle bundle, "hello_from_rubygems_version" assert_ffi_handle bundle, "hello_from_rubygems_version"
@ -79,22 +77,17 @@ class TestGemExtCargoBuilder < Gem::TestCase
skip_unsupported_platforms! skip_unsupported_platforms!
setup_rust_gem "rust_ruby_example" setup_rust_gem "rust_ruby_example"
output = []
FileUtils.rm(File.join(@ext, "src/lib.rs")) FileUtils.rm(File.join(@ext, "src/lib.rs"))
error = assert_raise(Gem::InstallError) do error = assert_raise(Gem::InstallError) do
Dir.chdir @ext do Dir.chdir @ext do
ENV.update(@rust_envs) ENV.update(@rust_envs)
spec = Gem::Specification.new "rust_ruby_example", "0.1.0" builder = Gem::Ext::CargoBuilder.new
builder = Gem::Ext::CargoBuilder.new(spec) builder.build "Cargo.toml", @dest_path, []
builder.build nil, @dest_path, output
end end
end end
output = output.join "\n" assert_match /cargo\s.*\sfailed/, error.message
assert_match "cargo failed", error.message
end end
def test_full_integration def test_full_integration

View file

@ -1,21 +0,0 @@
if ENV["RUBYOPT"] || defined? Gem
ENV.delete "RUBYOPT"
require "rbconfig"
cmd = [RbConfig.ruby, "--disable-gems", "build.rb", *ARGV]
exec(*cmd)
end
require "tmpdir"
lp = File.expand_path("../../../../lib", __dir__)
gem = ["ruby", "-I#{lp}", File.expand_path("../../../../bin/gem", __dir__)]
gemspec = File.expand_path("custom_name.gemspec", __dir__)
Dir.mktmpdir("custom_name") do |dir|
built_gem = File.expand_path(File.join(dir, "custom_name.gem"))
system(*gem, "build", gemspec, "--output", built_gem)
system(*gem, "install", "--verbose", "--local", built_gem, *ARGV)
system %q(ruby -rcustom_name -e "puts 'Result: ' + CustomName.say_hello")
end

View file

@ -2,9 +2,7 @@ Gem::Specification.new do |s|
s.name = "custom_name" s.name = "custom_name"
s.version = "0.1.0" s.version = "0.1.0"
s.summary = "A Rust extension for Ruby" s.summary = "A Rust extension for Ruby"
s.extensions = ["Cargo.toml"] s.extensions = ["ext/custom_name_lib/Cargo.toml"]
s.authors = ["Ian Ker-Seymer"] s.authors = ["Ian Ker-Seymer"]
s.files = ["Cargo.toml", "Cargo.lock", "src/lib.rs"] s.files = ["lib/custom_name.rb", "ext/custom_name_lib/Cargo.toml", "ext/custom_name_lib/Cargo.lock", "ext/custom_name_lib/src/lib.rs"]
s.metadata["cargo_crate_name"] = "custom-name-ext"
end end

View file

@ -12,7 +12,7 @@ unsafe extern "C" fn say_hello(_klass: VALUE) -> VALUE {
#[allow(non_snake_case)] #[allow(non_snake_case)]
#[no_mangle] #[no_mangle]
pub extern "C" fn Init_custom_name() { pub extern "C" fn Init_custom_name_ext() {
let name = CString::new("CustomName").unwrap(); let name = CString::new("CustomName").unwrap();
let function_name = CString::new("say_hello").unwrap(); let function_name = CString::new("say_hello").unwrap();
// bindgen does not properly detect the arity of the ruby callback function, so we have to transmute // bindgen does not properly detect the arity of the ruby callback function, so we have to transmute

View file

@ -0,0 +1 @@
require "custom_name_lib/custom_name_ext"

View file

@ -1,21 +0,0 @@
if ENV["RUBYOPT"] || defined? Gem
ENV.delete "RUBYOPT"
require "rbconfig"
cmd = [RbConfig.ruby, "--disable-gems", "build.rb", *ARGV]
exec(*cmd)
end
require "tmpdir"
lp = File.expand_path("../../../../lib", __dir__)
gem = ["ruby", "-I#{lp}", File.expand_path("../../../../bin/gem", __dir__)]
gemspec = File.expand_path("rust_ruby_example.gemspec", __dir__)
Dir.mktmpdir("rust_ruby_example") do |dir|
built_gem = File.expand_path(File.join(dir, "rust_ruby_example.gem"))
system(*gem, "build", gemspec, "--output", built_gem)
system(*gem, "install", "--verbose", "--local", built_gem, *ARGV)
system %q(ruby -rrust_ruby_example -e "puts 'Result: ' + RustRubyExample.reverse('hello world')")
end

View file

@ -6,8 +6,7 @@ require "rubygems/ext"
class TestGemExtCargoBuilderUnit < Gem::TestCase class TestGemExtCargoBuilderUnit < Gem::TestCase
def test_cargo_command_passes_args def test_cargo_command_passes_args
skip_unsupported_platforms! skip_unsupported_platforms!
spec = Gem::Specification.new "rust_ruby_example", "0.1.0" builder = Gem::Ext::CargoBuilder.new
builder = Gem::Ext::CargoBuilder.new(spec)
command = builder.cargo_command(Dir.pwd, @tempdir, ["--all-features"]) command = builder.cargo_command(Dir.pwd, @tempdir, ["--all-features"])
assert_includes command, "--all-features" assert_includes command, "--all-features"
@ -15,8 +14,7 @@ class TestGemExtCargoBuilderUnit < Gem::TestCase
def test_cargo_command_locks_in_release_profile def test_cargo_command_locks_in_release_profile
skip_unsupported_platforms! skip_unsupported_platforms!
spec = Gem::Specification.new "rust_ruby_example", "0.1.0" builder = Gem::Ext::CargoBuilder.new
builder = Gem::Ext::CargoBuilder.new(spec)
builder.profile = :release builder.profile = :release
command = builder.cargo_command(Dir.pwd, @tempdir) command = builder.cargo_command(Dir.pwd, @tempdir)
@ -27,8 +25,7 @@ class TestGemExtCargoBuilderUnit < Gem::TestCase
skip_unsupported_platforms! skip_unsupported_platforms!
old_cargo = ENV["CARGO"] old_cargo = ENV["CARGO"]
ENV["CARGO"] = "mycargo" ENV["CARGO"] = "mycargo"
spec = Gem::Specification.new "rust_ruby_example", "0.1.0" builder = Gem::Ext::CargoBuilder.new
builder = Gem::Ext::CargoBuilder.new(spec)
command = builder.cargo_command(Dir.pwd, @tempdir) command = builder.cargo_command(Dir.pwd, @tempdir)
assert_includes command, "mycargo" assert_includes command, "mycargo"
@ -38,8 +35,7 @@ class TestGemExtCargoBuilderUnit < Gem::TestCase
def test_build_env_includes_rbconfig def test_build_env_includes_rbconfig
skip_unsupported_platforms! skip_unsupported_platforms!
spec = Gem::Specification.new "rust_ruby_example", "0.1.0" builder = Gem::Ext::CargoBuilder.new
builder = Gem::Ext::CargoBuilder.new(spec)
env = builder.build_env env = builder.build_env
assert_equal env.fetch("RBCONFIG_RUBY_SO_NAME"), RbConfig::CONFIG["RUBY_SO_NAME"] assert_equal env.fetch("RBCONFIG_RUBY_SO_NAME"), RbConfig::CONFIG["RUBY_SO_NAME"]
@ -49,8 +45,7 @@ class TestGemExtCargoBuilderUnit < Gem::TestCase
skip_unsupported_platforms! skip_unsupported_platforms!
old_cargo = ENV["CARGO_BUILD_TARGET"] old_cargo = ENV["CARGO_BUILD_TARGET"]
ENV["CARGO_BUILD_TARGET"] = "x86_64-unknown-linux-gnu" ENV["CARGO_BUILD_TARGET"] = "x86_64-unknown-linux-gnu"
spec = Gem::Specification.new "rust_ruby_example", "0.1.0" builder = Gem::Ext::CargoBuilder.new
builder = Gem::Ext::CargoBuilder.new(spec)
command = builder.cargo_command(Dir.pwd, @tempdir, ["--locked"]) command = builder.cargo_command(Dir.pwd, @tempdir, ["--locked"])
assert_includes command, "--target" assert_includes command, "--target"

View file

@ -54,4 +54,4 @@ DEPENDENCIES
webrick (~> 1.6) webrick (~> 1.6)
BUNDLED WITH BUNDLED WITH
2.4.5 2.4.6

View file

@ -70,4 +70,4 @@ DEPENDENCIES
test-unit test-unit
BUNDLED WITH BUNDLED WITH
2.4.5 2.4.6

View file

@ -78,4 +78,4 @@ DEPENDENCIES
test-unit test-unit
BUNDLED WITH BUNDLED WITH
2.4.5 2.4.6

View file

@ -42,4 +42,4 @@ DEPENDENCIES
webrick (= 1.7.0) webrick (= 1.7.0)
BUNDLED WITH BUNDLED WITH
2.4.5 2.4.6