Merge RubyGems-3.4.0 and Bundler-2.4.0

This commit is contained in:
Hiroshi SHIBATA 2022-12-22 08:20:23 +09:00
parent d5635dfe36
commit f6620037ba
Notes: git 2022-12-24 07:57:26 +00:00
57 changed files with 2902 additions and 168 deletions

View File

@ -463,7 +463,7 @@ EOF
end
def local_platform
return Gem::Platform::RUBY if settings[:force_ruby_platform] || Gem.platforms == [Gem::Platform::RUBY]
return Gem::Platform::RUBY if settings[:force_ruby_platform]
Gem::Platform.local
end

View File

@ -10,7 +10,7 @@ module Bundler
AUTO_INSTALL_CMDS = %w[show binstubs outdated exec open console licenses clean].freeze
PARSEABLE_COMMANDS = %w[check config help exec platform show version].freeze
EXTENSIONS = ["c"].freeze
EXTENSIONS = ["c", "rust"].freeze
COMMAND_ALIASES = {
"check" => "c",
@ -762,7 +762,7 @@ module Bundler
# when deprecated version of `--ext` is called
# print out deprecation warning and pretend `--ext=c` was provided
if deprecated_ext_value?(arguments)
SharedHelpers.major_deprecation 2, "Option `--ext` without explicit value is deprecated. Please pass value like `--ext=c` for C extension. Pretending `--ext=c` was used for now."
SharedHelpers.major_deprecation 2, "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been deprecated. Please select a language, e.g. `--ext=rust` to generate a Rust extension. This gem will now be generated as if `--ext=c` was used."
arguments[arguments.index("--ext")] = "--ext=c"
end
end

View File

@ -31,6 +31,7 @@ module Bundler
@extension = options[:ext]
validate_ext_name if @extension
validate_rust_builder_rubygems_version if @extension == "rust"
travis_removal_info
end
@ -73,6 +74,7 @@ module Bundler
:git => use_git,
:github_username => github_username.empty? ? "[USERNAME]" : github_username,
:required_ruby_version => required_ruby_version,
:rust_builder_required_rubygems_version => rust_builder_required_rubygems_version,
:minitest_constant_name => minitest_constant_name,
}
ensure_safe_gem_name(name, constant_array)
@ -189,14 +191,23 @@ module Bundler
templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe]
if extension
if extension == "c"
templates.merge!(
"ext/newgem/extconf.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/extconf-c.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/newgem.h.tt" => "ext/#{name}/#{underscored_name}.h",
"ext/newgem/newgem.c.tt" => "ext/#{name}/#{underscored_name}.c"
)
end
if extension == "rust"
templates.merge!(
"Cargo.toml.tt" => "Cargo.toml",
"ext/newgem/Cargo.toml.tt" => "ext/#{name}/Cargo.toml",
"ext/newgem/extconf-rust.rb.tt" => "ext/#{name}/extconf.rb",
"ext/newgem/src/lib.rs.tt" => "ext/#{name}/src/lib.rs",
)
end
if target.exist? && !target.directory?
Bundler.ui.error "Couldn't create a new gem named `#{gem_name}` because there's an existing file named `#{gem_name}`."
exit Bundler::BundlerError.all_errors[Bundler::GenericSystemCallError]
@ -415,6 +426,10 @@ module Bundler
thor.run(%(#{editor} "#{file}"))
end
def rust_builder_required_rubygems_version
"3.3.11"
end
def required_ruby_version
"2.6.0"
end
@ -427,7 +442,6 @@ module Bundler
"1.3"
end
#
# TODO: remove at next minor release
def travis_removal_info
if options[:ci] == "travis"
@ -440,5 +454,12 @@ module Bundler
exit 1
end
end
def validate_rust_builder_rubygems_version
if Gem::Version.new(rust_builder_required_rubygems_version) > Gem.rubygems_version
Bundler.ui.error "Your RubyGems version (#{Gem.rubygems_version}) is too old to build Rust extension. Please update your RubyGems using `gem update --system` or any other way and try again."
exit 1
end
end
end
end

View File

@ -263,10 +263,10 @@ module Bundler
@locked_specs
elsif !unlocking? && nothing_changed?
if deleted_deps.any?
Bundler.ui.debug("Some dependencies were deleted, using a subset of the resolution from the lockfile")
Bundler.ui.debug "Some dependencies were deleted, using a subset of the resolution from the lockfile"
SpecSet.new(filter_specs(@locked_specs, @dependencies - deleted_deps))
else
Bundler.ui.debug("Found no changes, using resolution from the lockfile")
Bundler.ui.debug "Found no changes, using resolution from the lockfile"
if @locked_gems.may_include_redundant_platform_specific_gems?
SpecSet.new(filter_specs(@locked_specs, @dependencies))
else
@ -274,7 +274,7 @@ module Bundler
end
end
else
Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
Bundler.ui.debug "Found changes from the lockfile, re-resolving dependencies because #{change_reason}"
start_resolution
end
end
@ -806,12 +806,13 @@ module Bundler
end
new_spec = new_specs[s].first
# If the spec is no longer in the path source, unlock it. This
# commonly happens if the version changed in the gemspec
next unless new_spec
s.dependencies.replace(new_spec.dependencies)
if new_spec
s.dependencies.replace(new_spec.dependencies)
else
# If the spec is no longer in the path source, unlock it. This
# commonly happens if the version changed in the gemspec
@unlock[:gems] << s.name
end
end
if dep.nil? && requested_dependencies.find {|d| s.name == d.name }

View File

@ -7,7 +7,7 @@ require_relative "rubygems_ext"
module Bundler
class Dependency < Gem::Dependency
attr_reader :autorequire
attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref, :force_ruby_platform
attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref
ALL_RUBY_VERSIONS = ((18..27).to_a + (30..31).to_a).freeze
PLATFORM_MAP = {
@ -42,7 +42,7 @@ module Bundler
@env = options["env"]
@should_include = options.fetch("should_include", true)
@gemfile = options["gemfile"]
@force_ruby_platform = options["force_ruby_platform"]
@force_ruby_platform = options["force_ruby_platform"] if options.key?("force_ruby_platform")
@autorequire = Array(options["require"] || []) if options.key?("require")
end
@ -50,7 +50,7 @@ module Bundler
# Returns the platforms this dependency is valid for, in the same order as
# passed in the `valid_platforms` parameter
def gem_platforms(valid_platforms)
return [Gem::Platform::RUBY] if @force_ruby_platform
return [Gem::Platform::RUBY] if force_ruby_platform
return valid_platforms if @platforms.empty?
valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
module Bundler
module ForcePlatform
private
# The `:force_ruby_platform` value used by dependencies for resolution, and
# by locked specifications for materialization is `false` by default, except
# for TruffleRuby. TruffleRuby generally needs to force the RUBY platform
# variant unless the name is explicitly allowlisted.
def default_force_ruby_platform
return false unless RUBY_ENGINE == "truffleruby"
!Gem::Platform::REUSE_AS_BINARY_ON_TRUFFLERUBY.include?(name)
end
end
end

View File

@ -1,8 +1,11 @@
# frozen_string_literal: true
require_relative "force_platform"
module Bundler
class LazySpecification
include MatchPlatform
include ForcePlatform
attr_reader :name, :version, :dependencies, :platform
attr_accessor :source, :remote, :force_ruby_platform
@ -14,6 +17,7 @@ module Bundler
@platform = platform || Gem::Platform::RUBY
@source = source
@specification = nil
@force_ruby_platform = default_force_ruby_platform
end
def full_name

View File

@ -31,41 +31,32 @@ The generated project skeleton can be customized with OPTIONS, as explained belo
.
.SH "OPTIONS"
.
.TP
\fB\-\-exe\fR or \fB\-b\fR or \fB\-\-bin\fR
Specify that Bundler should create a binary executable (as \fBexe/GEM_NAME\fR) in the generated rubygem project\. This binary will also be added to the \fBGEM_NAME\.gemspec\fR manifest\. This behavior is disabled by default\.
.IP "\(bu" 4
\fB\-\-exe\fR or \fB\-b\fR or \fB\-\-bin\fR: Specify that Bundler should create a binary executable (as \fBexe/GEM_NAME\fR) in the generated rubygem project\. This binary will also be added to the \fBGEM_NAME\.gemspec\fR manifest\. This behavior is disabled by default\.
.
.TP
\fB\-\-no\-exe\fR
Do not create a binary (overrides \fB\-\-exe\fR specified in the global config)\.
.IP "\(bu" 4
\fB\-\-no\-exe\fR: Do not create a binary (overrides \fB\-\-exe\fR specified in the global config)\.
.
.TP
\fB\-\-coc\fR
Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
\fB\-\-coc\fR: Add a \fBCODE_OF_CONDUCT\.md\fR file to the root of the generated project\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
.TP
\fB\-\-no\-coc\fR
Do not create a \fBCODE_OF_CONDUCT\.md\fR (overrides \fB\-\-coc\fR specified in the global config)\.
.IP "\(bu" 4
\fB\-\-no\-coc\fR: Do not create a \fBCODE_OF_CONDUCT\.md\fR (overrides \fB\-\-coc\fR specified in the global config)\.
.
.TP
\fB\-\-ext=c\fR
Add boilerplate for C extension code to the generated project\. This behavior is disabled by default\.
.IP "\(bu" 4
\fB\-\-ext=c\fR, \fB\-\-ext=rust\fR Add boilerplate for C or Rust (currently magnus \fIhttps://docs\.rs/magnus\fR based) extension code to the generated project\. This behavior is disabled by default\.
.
.TP
\fB\-\-no\-ext\fR
Do not add C extension code (overrides \fB\-\-ext\fR specified in the global config)\.
.IP "\(bu" 4
\fB\-\-no\-ext\fR: Do not add extension code (overrides \fB\-\-ext\fR specified in the global config)\.
.
.TP
\fB\-\-mit\fR
Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.IP "\(bu" 4
\fB\-\-mit\fR: Add an MIT license to a \fBLICENSE\.txt\fR file in the root of the generated project\. Your name from the global git config is used for the copyright statement\. If this option is unspecified, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
.TP
\fB\-\-no\-mit\fR
Do not create a \fBLICENSE\.txt\fR (overrides \fB\-\-mit\fR specified in the global config)\.
.IP "\(bu" 4
\fB\-\-no\-mit\fR: Do not create a \fBLICENSE\.txt\fR (overrides \fB\-\-mit\fR specified in the global config)\.
.
.TP
\fB\-t\fR, \fB\-\-test=minitest\fR, \fB\-\-test=rspec\fR, \fB\-\-test=test\-unit\fR
Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR, \fBrspec\fR and \fBtest\-unit\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. Given no option is specified:
.IP "\(bu" 4
\fB\-t\fR, \fB\-\-test=minitest\fR, \fB\-\-test=rspec\fR, \fB\-\-test=test\-unit\fR: Specify the test framework that Bundler should use when generating the project\. Acceptable values are \fBminitest\fR, \fBrspec\fR and \fBtest\-unit\fR\. The \fBGEM_NAME\.gemspec\fR will be configured and a skeleton test/spec directory will be created based on this option\. Given no option is specified:
.
.IP
When Bundler is configured to generate tests, this defaults to Bundler\'s global config setting \fBgem\.test\fR\.
@ -76,9 +67,8 @@ When Bundler is configured to not generate tests, an interactive prompt will be
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
.TP
\fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR
Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.IP "\(bu" 4
\fB\-\-ci\fR, \fB\-\-ci=github\fR, \fB\-\-ci=gitlab\fR, \fB\-\-ci=circle\fR: Specify the continuous integration service that Bundler should use when generating the project\. Acceptable values are \fBgithub\fR, \fBgitlab\fR and \fBcircle\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.
.IP
When Bundler is configured to generate CI files, this defaults to Bundler\'s global config setting \fBgem\.ci\fR\.
@ -89,9 +79,8 @@ When Bundler is configured to not generate CI files, an interactive prompt will
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
.TP
\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR
Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.IP "\(bu" 4
\fB\-\-linter\fR, \fB\-\-linter=rubocop\fR, \fB\-\-linter=standard\fR: Specify the linter and code formatter that Bundler should add to the project\'s development dependencies\. Acceptable values are \fBrubocop\fR and \fBstandard\fR\. A configuration file will be generated in the project directory\. Given no option is specified:
.
.IP
When Bundler is configured to add a linter, this defaults to Bundler\'s global config setting \fBgem\.linter\fR\.
@ -102,9 +91,10 @@ When Bundler is configured not to add a linter, an interactive prompt will be di
.IP
When Bundler is unconfigured, an interactive prompt will be displayed and the answer will be saved in Bundler\'s global config for future \fBbundle gem\fR use\.
.
.TP
\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR
Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
.IP "\(bu" 4
\fB\-e\fR, \fB\-\-edit[=EDITOR]\fR: Open the resulting GEM_NAME\.gemspec in EDITOR, or the default editor if not specified\. The default is \fB$BUNDLER_EDITOR\fR, \fB$VISUAL\fR, or \fB$EDITOR\fR\.
.
.IP "" 0
.
.SH "SEE ALSO"
.

View File

@ -41,12 +41,12 @@ configuration file using the following names:
Do not create a `CODE_OF_CONDUCT.md` (overrides `--coc` specified in the
global config).
* `--ext=c`:
Add boilerplate for C extension code to the generated project. This behavior
* `--ext=c`, `--ext=rust`
Add boilerplate for C or Rust (currently [magnus](https://docs.rs/magnus) based) extension code to the generated project. This behavior
is disabled by default.
* `--no-ext`:
Do not add C extension code (overrides `--ext` specified in the global
Do not add extension code (overrides `--ext` specified in the global
config).
* `--mit`:

View File

@ -27,6 +27,17 @@ module Bundler
remove_from_candidates(spec)
end
@requirements = requirements
@packages = packages
root, logger = setup_solver
Bundler.ui.info "Resolving dependencies...", true
solve_versions(:root => root, :logger => logger)
end
def setup_solver
root = Resolver::Root.new(name_for_explicit_dependency_source)
root_version = Resolver::Candidate.new(0)
@ -42,24 +53,27 @@ module Bundler
end
end
root_dependencies = prepare_dependencies(requirements, packages)
root_dependencies = prepare_dependencies(@requirements, @packages)
@cached_dependencies = Hash.new do |dependencies, package|
dependencies[package] = if package.root?
{ root_version => root_dependencies }
else
Hash.new do |versions, version|
versions[version] = to_dependency_hash(version.dependencies, packages)
versions[version] = to_dependency_hash(version.dependencies, @packages)
end
end
end
logger = Bundler::UI::Shell.new
logger.level = debug? ? "debug" : "warn"
[root, logger]
end
def solve_versions(root:, logger:)
solver = PubGrub::VersionSolver.new(:source => self, :root => root, :logger => logger)
before_resolution
result = solver.solve
after_resolution
result.map {|package, version| version.to_specs(package) }.flatten.uniq
rescue PubGrub::SolveFailure => e
incompatibility = e.incompatibility
@ -82,8 +96,15 @@ module Bundler
end
end
names_to_unlock.uniq!
if names_to_unlock.any?
Bundler.ui.debug "Found conflicts with locked dependencies. Retrying with #{names_to_unlock.join(", ")} unlocked...", true
@base.unlock_names(names_to_unlock)
root, logger = setup_solver
retry
end
@ -144,14 +165,6 @@ module Bundler
false
end
def before_resolution
Bundler.ui.info "Resolving dependencies...", debug?
end
def after_resolution
Bundler.ui.info ""
end
def incompatibilities_for(package, version)
package_deps = @cached_dependencies[package]
sorted_versions = @sorted_versions[package]
@ -202,7 +215,7 @@ module Bundler
def all_versions_for(package)
name = package.name
results = @base[name] + @all_specs[name]
results = (@base[name] + @all_specs[name]).uniq(&:full_name)
locked_requirement = base_requirements[name]
results = filter_matching_specs(results, locked_requirement) if locked_requirement

View File

@ -16,6 +16,7 @@ require "rubygems/specification"
require "rubygems/source"
require_relative "match_metadata"
require_relative "force_platform"
require_relative "match_platform"
# Cherry-pick fixes to `Gem.ruby_version` to be useful for modern Bundler
@ -153,12 +154,16 @@ module Gem
end
class Dependency
include ::Bundler::ForcePlatform
attr_accessor :source, :groups
alias_method :eql?, :==
def force_ruby_platform
false
return @force_ruby_platform if defined?(@force_ruby_platform) && !@force_ruby_platform.nil?
@force_ruby_platform = default_force_ruby_platform
end
def encode_with(coder)
@ -277,6 +282,10 @@ module Gem
without_gnu_nor_abi_modifiers
end
end
if RUBY_ENGINE == "truffleruby" && !defined?(REUSE_AS_BINARY_ON_TRUFFLERUBY)
REUSE_AS_BINARY_ON_TRUFFLERUBY = %w[libv8 libv8-node sorbet-static].freeze
end
end
Platform.singleton_class.module_eval do

View File

@ -176,37 +176,32 @@ module Bundler
@depth = if !supports_fetching_unreachable_refs?
nil
elsif not_pinned?
elsif not_pinned? || pinned_to_full_sha?
1
elsif ref.include?("~")
parsed_depth = ref.split("~").last
parsed_depth.to_i + 1
elsif abbreviated_ref?
nil
else
1
end
end
def refspec
if fully_qualified_ref
"#{fully_qualified_ref}:#{fully_qualified_ref}"
elsif ref.include?("~")
parsed_ref = ref.split("~").first
"#{parsed_ref}:#{parsed_ref}"
return ref if pinned_to_full_sha?
ref_to_fetch = @revision || fully_qualified_ref
ref_to_fetch ||= if ref.include?("~")
ref.split("~").first
elsif ref.start_with?("refs/")
"#{ref}:#{ref}"
elsif abbreviated_ref?
nil
else
ref
else
"refs/*"
end
"#{ref_to_fetch}:#{ref_to_fetch}"
end
def fully_qualified_ref
return @fully_qualified_ref if defined?(@fully_qualified_ref)
@fully_qualified_ref = if branch
if branch
"refs/heads/#{branch}"
elsif tag
"refs/tags/#{tag}"
@ -219,8 +214,8 @@ module Bundler
branch || tag || ref.nil?
end
def abbreviated_ref?
ref =~ /\A\h+\z/ && ref !~ /\A\h{40}\z/
def pinned_to_full_sha?
ref =~ /\A\h{40}\z/
end
def legacy_locked_revision?

View File

@ -190,12 +190,10 @@ module Bundler
def specs_for_dependency(dep, platform)
specs_for_name = lookup[dep.name]
if platform.nil?
matching_specs = specs_for_name.map {|s| s.materialize_for_installation if Gem::Platform.match_spec?(s) }.compact
GemHelpers.sort_best_platform_match(matching_specs, Bundler.local_platform)
else
GemHelpers.select_best_platform_match(specs_for_name, dep.force_ruby_platform ? Gem::Platform::RUBY : platform)
end
target_platform = dep.force_ruby_platform ? Gem::Platform::RUBY : (platform || Bundler.local_platform)
matching_specs = GemHelpers.select_best_platform_match(specs_for_name, target_platform)
matching_specs.map!(&:materialize_for_installation).compact! if platform.nil?
matching_specs
end
def tsort_each_child(s)

View File

@ -0,0 +1,7 @@
# This Cargo.toml is here to let externals tools (IDEs, etc.) know that this is
# a Rust project. Your extensions depedencies should be added to the Cargo.toml
# in the ext/ directory.
[workspace]
members = ["./ext/<%= config[:name] %>"]
resolver = "2"

View File

@ -9,6 +9,9 @@ gem "rake", "~> 13.0"
<%- if config[:ext] -%>
gem "rake-compiler"
<%- if config[:ext] == 'rust' -%>
gem "rb_sys"
<%- end -%>
<%- end -%>
<%- if config[:test] -%>

View File

@ -1,18 +1,20 @@
# <%= config[:constant_name] %>
TODO: Delete this and the text below, and describe your gem
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/<%= config[:namespaced_path] %>`. To experiment with that code, run `bin/console` for an interactive prompt.
TODO: Delete this and the text above, and describe your gem
## Installation
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
Install the gem and add to the application's Gemfile by executing:
$ bundle add <%= config[:name] %>
$ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
If bundler is not being used to manage dependencies, install the gem by executing:
$ gem install <%= config[:name] %>
$ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
## Usage

View File

@ -39,7 +39,8 @@ require "standard/rake"
<% end -%>
<% if config[:ext] -%>
<% default_task_names.unshift(:clobber, :compile) -%>
<% default_task_names.unshift(:compile) -%>
<% default_task_names.unshift(:clobber) unless config[:ext] == 'rust' -%>
require "rake/extensiontask"
task build: :compile

View File

@ -3,8 +3,20 @@ jobs:
build:
docker:
- image: ruby:<%= RUBY_VERSION %>
<%- if config[:ext] == 'rust' -%>
environment:
RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN: 'true'
<%- end -%>
steps:
- checkout
<%- if config[:ext] == 'rust' -%>
- run:
name: Install Rust/Cargo dependencies
command: apt-get update && apt-get install -y clang
- run:
name: Install a RubyGems version that can compile rust extensions
command: gem update --system '<%= ::Gem.rubygems_version %>'
<%- end -%>
- run:
name: Run the default task
command: |

View File

@ -0,0 +1,15 @@
[package]
name = <%= config[:name].inspect %>
version = "0.1.0"
edition = "2021"
authors = ["<%= config[:author] %> <<%= config[:email] %>>"]
<%- if config[:mit] -%>
license = "MIT"
<%- end -%>
publish = false
[lib]
crate-type = ["cdylib"]
[dependencies]
magnus = { version = "0.4" }

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
require "mkmf"
require "rb_sys/mkmf"
create_rust_makefile(<%= config[:makefile_path].inspect %>)

View File

@ -0,0 +1,12 @@
use magnus::{define_module, function, prelude::*, Error};
fn hello(subject: String) -> String {
format!("Hello from Rust, {}!", subject)
}
#[magnus::init]
fn init() -> Result<(), Error> {
let module = <%= config[:constant_array].map {|c| "define_module(#{c.dump})?"}.join(".") %>;
module.define_singleton_method("hello", function!(hello, 1))?;
Ok(())
}

View File

@ -18,10 +18,20 @@ jobs:
steps:
- uses: actions/checkout@v3
<%- if config[:ext] == 'rust' -%>
- name: Set up Ruby & Rust
uses: oxidize-rb/actions/setup-ruby-and-rust@main
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
cargo-cache: true
rubygems: '<%= ::Gem.rubygems_version %>'
<%- else -%>
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
<%- end -%>
- name: Run the default task
run: bundle exec rake

View File

@ -12,6 +12,9 @@
*.o
*.a
mkmf.log
<%- if config[:ext] == 'rust' -%>
target/
<%- end -%>
<%- end -%>
<%- if config[:test] == "rspec" -%>

View File

@ -2,9 +2,17 @@ default:
image: ruby:<%= RUBY_VERSION %>
before_script:
<%- if config[:ext] == 'rust' -%>
- apt-get update && apt-get install -y clang
- gem update --system '<%= ::Gem.rubygems_version %>'
<%- end -%>
- gem install bundler -v <%= Bundler::VERSION %>
- bundle install
example_job:
<%- if config[:ext] == 'rust' -%>
variables:
RB_SYS_FORCE_INSTALL_RUST_TOOLCHAIN: 'true'
<%- end -%>
script:
- bundle exec rake

View File

@ -15,6 +15,9 @@ Gem::Specification.new do |spec|
spec.license = "MIT"
<%- end -%>
spec.required_ruby_version = ">= <%= config[:required_ruby_version] %>"
<%- if config[:ext] == 'rust' -%>
spec.required_rubygems_version = ">= <%= config[:rust_builder_required_rubygems_version] %>"
<%- end -%>
spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
@ -32,9 +35,12 @@ Gem::Specification.new do |spec|
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
<%- if config[:ext] -%>
<%- if config[:ext] == 'c' -%>
spec.extensions = ["ext/<%= config[:underscored_name] %>/extconf.rb"]
<%- end -%>
<%- if config[:ext] == 'rust' -%>
spec.extensions = ["ext/<%= config[:underscored_name] %>/Cargo.toml"]
<%- end -%>
# Uncomment to register a new dependency of your gem
# spec.add_dependency "example-gem", "~> 1.0"

View File

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

2162
lib/mjit/instruction.rb Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
require "rbconfig"
module Gem
VERSION = "3.4.0.dev".freeze
VERSION = "3.4.0".freeze
end
# Must be first since it unloads the prelude from 1.9.2
@ -1297,7 +1297,6 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/".freeze
autoload :BundlerVersionFinder, File.expand_path("rubygems/bundler_version_finder", __dir__)
autoload :ConfigFile, File.expand_path("rubygems/config_file", __dir__)
autoload :Dependency, File.expand_path("rubygems/dependency", __dir__)
autoload :DependencyList, File.expand_path("rubygems/dependency_list", __dir__)

View File

@ -277,7 +277,10 @@ class Gem::Dependency
requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version)
end.map(&:to_spec)
Gem::BundlerVersionFinder.prioritize!(matches) if prioritizes_bundler?
if prioritizes_bundler?
require_relative "bundler_version_finder"
Gem::BundlerVersionFinder.prioritize!(matches)
end
if platform_only
matches.reject! do |spec|

View File

@ -37,7 +37,8 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
def build_env
build_env = rb_config_env
build_env["RUBY_STATIC"] = "true" if ruby_static? && ENV.key?("RUBY_STATIC")
build_env["RUSTFLAGS"] = "#{ENV["RUSTFLAGS"]} --cfg=rb_sys_gem".strip
cfg = "--cfg=rb_sys_gem --cfg=rubygems --cfg=rubygems_#{Gem::VERSION.tr(".", "_")}"
build_env["RUSTFLAGS"] = [ENV["RUSTFLAGS"], cfg].compact.join(" ")
build_env
end
@ -47,6 +48,7 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
cmd = []
cmd += [cargo, "rustc"]
cmd += ["--crate-type", "cdylib"]
cmd += ["--target", ENV["CARGO_BUILD_TARGET"]] if ENV["CARGO_BUILD_TARGET"]
cmd += ["--target-dir", dest_path]
cmd += ["--manifest-path", manifest]
@ -103,14 +105,23 @@ class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# We want to use the same linker that Ruby uses, so that the linker flags from
# mkmf work properly.
def linker_args
# Have to handle CC="cl /nologo" on mswin
cc_flag = Shellwords.split(makefile_config("CC"))
linker = cc_flag.shift
link_args = cc_flag.flat_map {|a| ["-C", "link-arg=#{a}"] }
return mswin_link_args if linker == "cl"
["-C", "linker=#{linker}", *link_args]
end
def mswin_link_args
args = []
args += ["-l", makefile_config("LIBRUBYARG_SHARED").chomp(".lib")]
args += split_flags("LIBS").flat_map {|lib| ["-l", lib.chomp(".lib")] }
args += split_flags("LOCAL_LIBS").flat_map {|lib| ["-l", lib.chomp(".lib")] }
args
end
def libruby_args(dest_dir)
libs = makefile_config(ruby_static? ? "LIBRUBYARG_STATIC" : "LIBRUBYARG_SHARED")
raw_libs = Shellwords.split(libs)

View File

@ -3,20 +3,24 @@
class Gem::Ext::CargoBuilder < Gem::Ext::Builder
# Converts Ruby link flags into something cargo understands
class LinkFlagConverter
FILTERED_PATTERNS = [
/compress-debug-sections/, # Not supported by all linkers, and not required for Rust
].freeze
def self.convert(arg)
return [] if FILTERED_PATTERNS.any? {|p| p.match?(arg) }
case arg.chomp
when /^-L\s*(.+)$/
["-L", "native=#{$1}"]
when /^--library=(\w+\S+)$/, /^-l\s*(\w+\S+)$/
["-l", $1]
when /^-l\s*:lib(\S+).a$/
["-l", "static=#{$1}"]
when /^-l\s*:lib(\S+).(so|dylib|dll)$/
["-l", "dylib=#{$1}"]
when /^-l\s*([^:\s])+/ # -lfoo, but not -l:libfoo.a
["-l", $1]
when /^-F\s*(.*)$/
["-l", "framework=#{$1}"]
else
["-C", "link_arg=#{arg}"]
["-C", "link-args=#{arg}"]
end
end
end

View File

@ -460,6 +460,20 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
require_relative "ext"
builder = Gem::Ext::Builder.new(@specification)
validate_rake_extensions(builder)
validate_rust_extensions(builder)
end
def validate_rust_extensions(builder) # :nodoc:
rust_extension = @specification.extensions.any? {|s| builder.builder_for(s).is_a? Gem::Ext::CargoBuilder }
missing_cargo_lock = !@specification.files.include?("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.
ERROR
end
def validate_rake_extensions(builder) # :nodoc:
rake_extension = @specification.extensions.any? {|s| builder.builder_for(s) == Gem::Ext::RakeBuilder }
rake_dependency = @specification.dependencies.any? {|d| d.name == "rake" }

View File

@ -752,5 +752,94 @@ RSpec.describe "bundle lock" do
version solving has failed.
ERR
end
it "is able to display some explanation on crazy irresolvable cases" do
build_repo4 do
build_gem "activeadmin", "2.13.1" do |s|
s.add_dependency "ransack", "= 3.1.0"
end
# Activemodel is missing as a dependency in lockfile
build_gem "ransack", "3.1.0" do |s|
s.add_dependency "activemodel", ">= 6.0.4"
s.add_dependency "activesupport", ">= 6.0.4"
end
%w[6.0.4 7.0.2.3 7.0.3.1 7.0.4].each do |version|
build_gem "activesupport", version
# Activemodel is only available on 6.0.4
if version == "6.0.4"
build_gem "activemodel", version do |s|
s.add_dependency "activesupport", version
end
end
build_gem "rails", version do |s|
# Depednencies of Rails 7.0.2.3 are in reverse order
if version == "7.0.2.3"
s.add_dependency "activesupport", version
s.add_dependency "activemodel", version
else
s.add_dependency "activemodel", version
s.add_dependency "activesupport", version
end
end
end
end
gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "rails", ">= 7.0.2.3"
gem "activeadmin", "= 2.13.1"
G
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
activeadmin (2.13.1)
ransack (= 3.1.0)
ransack (3.1.0)
activemodel (>= 6.0.4)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
activeadmin (= 2.13.1)
ransack (= 3.1.0)
BUNDLED WITH
#{Bundler::VERSION}
L
bundle "lock", :raise_on_error => false
expect(err).to eq <<~ERR.strip
Could not find compatible versions
Because every version of activemodel depends on activesupport = 6.0.4
and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3,
every version of activemodel is incompatible with rails >= 7.0.2.3, < 7.0.3.1.
And because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3,
rails >= 7.0.2.3, < 7.0.3.1 is forbidden.
(1) So, because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1
and rails >= 7.0.4 depends on activemodel = 7.0.4,
rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4.
Because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3
and rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1,
rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 or activesupport = 7.0.3.1.
And because rails >= 7.0.4 depends on activesupport = 7.0.4
and every version of activemodel depends on activesupport = 6.0.4,
activemodel != 7.0.2.3 is incompatible with rails >= 7.0.2.3.
And because rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4 (1),
rails >= 7.0.2.3 is forbidden.
So, because Gemfile depends on rails >= 7.0.2.3,
version solving has failed.
ERR
end
end
end

View File

@ -310,28 +310,28 @@ RSpec.describe "bundle gem" do
expect(last_command).to be_success
end
it "has no rubocop offenses when using --ext and --linter=rubocop flag", :readline do
it "has no rubocop offenses when using --ext=c and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --ext=c --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
it "has no rubocop offenses when using --ext, --test=minitest, and --linter=rubocop flag", :readline do
it "has no rubocop offenses when using --ext=c, --test=minitest, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --ext=c --test=minitest --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
it "has no rubocop offenses when using --ext, --test=rspec, and --linter=rubocop flag", :readline do
it "has no rubocop offenses when using --ext=c, --test=rspec, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --ext=c --test=rspec --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
it "has no rubocop offenses when using --ext, --ext=test-unit, and --linter=rubocop flag", :readline do
it "has no rubocop offenses when using --ext=c, --test=test-unit, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --ext=c --test=test-unit --linter=rubocop"
bundle_exec_rubocop
@ -345,6 +345,42 @@ RSpec.describe "bundle gem" do
expect(last_command).to be_success
end
it "has no rubocop offenses when using --ext=rust and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle "gem #{gem_name} --ext=rust --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
it "has no rubocop offenses when using --ext=rust, --test=minitest, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle "gem #{gem_name} --ext=rust --test=minitest --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
it "has no rubocop offenses when using --ext=rust, --test=rspec, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle "gem #{gem_name} --ext=rust --test=rspec --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
it "has no rubocop offenses when using --ext=rust, --test=test-unit, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle "gem #{gem_name} --ext=rust --test=test-unit --linter=rubocop"
bundle_exec_rubocop
expect(last_command).to be_success
end
shared_examples_for "CI config is absent" do
it "does not create any CI files" do
expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
@ -1336,12 +1372,15 @@ RSpec.describe "bundle gem" do
context "is deprecated", :bundler => "< 3" do
it "prints deprecation when used after gem name" do
bundle ["gem", "--ext", gem_name].compact.join(" ")
expect(err).to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
expect(err).to include "[DEPRECATED]"
expect(err).to include "`--ext` with no arguments has been deprecated"
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
end
it "prints deprecation when used before gem name" do
bundle ["gem", gem_name, "--ext"].compact.join(" ")
expect(err).to include "[DEPRECATED]"
expect(err).to include "`--ext` with no arguments has been deprecated"
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
end
end
@ -1364,8 +1403,11 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
end
it "includes rake-compiler" do
it "includes rake-compiler, but no Rust related changes" do
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
expect(bundled_app("#{gem_name}/Gemfile").read).to_not include('gem "rb_sys"')
expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to_not include('spec.required_rubygems_version = ">= ')
end
it "depends on compile task for build" do
@ -1387,6 +1429,64 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
end
context "--ext parameter set with rust and old RubyGems" do
it "fails in friendly way" do
if ::Gem::Version.new("3.3.11") <= ::Gem.rubygems_version
skip "RubyGems compatible with Rust builder"
end
expect do
bundle ["gem", gem_name, "--ext=rust"].compact.join(" ")
end.to raise_error(RuntimeError, /too old to build Rust extension/)
end
end
context "--ext parameter set with rust" do
let(:flags) { "--ext=rust" }
before do
skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle ["gem", gem_name, flags].compact.join(" ")
end
it "is not deprecated" do
expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
end
it "builds ext skeleton" do
expect(bundled_app("#{gem_name}/Cargo.toml")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/Cargo.toml")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/src/lib.rs")).to exist
end
it "includes rake-compiler, rb_sys gems and required_rubygems_version constraint" do
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rb_sys"')
expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.required_rubygems_version = ">= ')
end
it "depends on compile task for build" do
rakefile = strip_whitespace <<-RAKEFILE
# frozen_string_literal: true
require "bundler/gem_tasks"
require "rake/extensiontask"
task build: :compile
Rake::ExtensionTask.new("#{gem_name}") do |ext|
ext.lib_dir = "lib/#{gem_name}"
end
task default: :compile
RAKEFILE
expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
end
end
context "gem naming with dashed", :readline do

View File

@ -1310,21 +1310,19 @@ RSpec.describe "bundle update --bundler" do
end
it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo, :realworld do
skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
end
install_gemfile <<-G
install_gemfile <<-G, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9")
bundle :update, :bundler => true, :artifice => "vcr", :verbose => true
bundle :update, :bundler => true, :artifice => "vcr", :verbose => true, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
# Only updates properly on modern RubyGems.
@ -1356,15 +1354,13 @@ RSpec.describe "bundle update --bundler" do
end
it "errors if the explicit target version does not exist", :realworld do
skip "ruby-head has a default Bundler version too high for this spec to work" if RUBY_PATCHLEVEL == -1
pristine_system_gems "bundler-2.3.9"
build_repo4 do
build_gem "rack", "1.0"
end
install_gemfile <<-G
install_gemfile <<-G, :env => { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" }
source "#{file_uri_for(gem_repo4)}"
gem "rack"
G

View File

@ -235,6 +235,29 @@ RSpec.describe "bundle install with git sources" do
G
end
it "works when a tag that does not look like a commit hash is used as the value of :ref" do
build_git "foo"
@remote = build_git("bar", :bare => true)
update_git "foo", :remote => file_uri_for(@remote.path)
update_git "foo", :push => "main"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem 'foo', :git => "#{@remote.path}"
G
# Create a new tag on the remote that needs fetching
update_git "foo", :tag => "v1.0.0"
update_git "foo", :push => "v1.0.0"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem 'foo', :git => "#{@remote.path}", :ref => "v1.0.0"
G
expect(err).to be_empty
end
it "works when the revision is a non-head ref" do
# want to ensure we don't fallback to main
update_git "foo", :path => lib_path("foo-1.0") do |s|

View File

@ -92,14 +92,12 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "demo", :path => lib_path("demo")
build_lib "aaa", :path => lib_path("demo/aaa")
gemfile = <<-G
gemfile lib_path("demo/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec
gem "aaa", :path => "./aaa"
G
File.open(lib_path("demo/Gemfile"), "w") {|f| f.puts gemfile }
lockfile = <<~L
PATH
remote: .
@ -314,18 +312,67 @@ RSpec.describe "bundle install with explicit source paths" do
s.add_dependency "rack", "1.0"
end
gemfile = <<-G
gemfile lib_path("foo/Gemfile"), <<-G
source "#{file_uri_for(gem_repo1)}"
gemspec
G
File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts gemfile }
bundle "install", :dir => lib_path("foo")
expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo")
expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo")
end
it "does not unlock dependencies of path sources" do
build_repo4 do
build_gem "graphql", "2.0.15"
build_gem "graphql", "2.0.16"
end
build_lib "foo", "0.1.0", :path => lib_path("foo") do |s|
s.add_dependency "graphql", "~> 2.0"
end
gemfile_path = lib_path("foo/Gemfile")
gemfile gemfile_path, <<-G
source "#{file_uri_for(gem_repo4)}"
gemspec
G
lockfile_path = lib_path("foo/Gemfile.lock")
original_lockfile = <<~L
PATH
remote: .
specs:
foo (0.1.0)
graphql (~> 2.0)
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
graphql (2.0.15)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
foo!
BUNDLED WITH
#{Bundler::VERSION}
L
lockfile lockfile_path, original_lockfile
build_lib "foo", "0.1.1", :path => lib_path("foo") do |s|
s.add_dependency "graphql", "~> 2.0"
end
bundle "install", :dir => lib_path("foo")
expect(lockfile_path).to read_as(original_lockfile.gsub("foo (0.1.0)", "foo (0.1.1)"))
end
it "supports gemspec syntax with an alternative path" do
build_lib "foo", "1.0", :path => lib_path("foo") do |s|
s.add_dependency "rack", "1.0"
@ -791,13 +838,11 @@ RSpec.describe "bundle install with explicit source paths" do
describe "when there are both a gemspec and remote gems" do
it "doesn't query rubygems for local gemspec name" do
build_lib "private_lib", "2.2", :path => lib_path("private_lib")
gemfile = <<-G
gemfile lib_path("private_lib/Gemfile"), <<-G
source "http://localgemserver.test"
gemspec
gem 'rack'
G
File.open(lib_path("private_lib/Gemfile"), "w") {|f| f.puts gemfile }
bundle :install, :env => { "DEBUG" => "1" }, :artifice => "endpoint", :dir => lib_path("private_lib")
expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$})
expect(out).not_to match(/^HTTP GET.*private_lib/)

View File

@ -52,6 +52,44 @@ RSpec.describe "bundle lock with git gems" do
expect(err).to be_empty
end
it "properly fetches a git source locked to an unreachable ref" do
# Create a commit and make it unreachable
git "checkout -b foo ", lib_path("foo-1.0")
unreachable_sha = update_git("foo").ref_for("HEAD")
git "checkout main ", lib_path("foo-1.0")
git "branch -D foo ", lib_path("foo-1.0")
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem 'foo', :git => "#{lib_path("foo-1.0")}"
G
lockfile <<-L
GIT
remote: #{lib_path("foo-1.0")}
revision: #{unreachable_sha}
specs:
foo (1.0)
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
foo!
BUNDLED WITH
#{Bundler::VERSION}
L
bundle "install"
expect(err).to be_empty
end
it "provides correct #full_gem_path" do
run <<-RUBY
puts Bundler.rubygems.find_name('foo').first.full_gem_path

View File

@ -24,13 +24,30 @@ module Gem
end
if ENV["BUNDLER_SPEC_PLATFORM"]
previous_platforms = @platforms
previous_local = Platform.local
class Platform
@local = new(ENV["BUNDLER_SPEC_PLATFORM"])
end
@platforms = [Gem::Platform::RUBY, Gem::Platform.local]
@platforms = previous_platforms.map {|platform| platform == previous_local ? Platform.local : platform }
end
if ENV["BUNDLER_SPEC_GEM_SOURCES"]
self.sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]]
end
if ENV["BUNDLER_IGNORE_DEFAULT_GEM"]
module RemoveDefaultBundlerStub
def default_stubs(pattern = "*")
super.delete_if {|stub| stub.name == "bundler" }
end
end
class Specification
class << self
prepend RemoveDefaultBundlerStub
end
end
end
end

View File

@ -162,7 +162,7 @@ module Spec
end
if exitstatus == 65
actual_platform = out.split("\n").last
next "#{name} was expected to be of platform #{platform} but was #{actual_platform}"
next "#{name} was expected to be of platform #{platform || "ruby"} but was #{actual_platform || "ruby"}"
end
if exitstatus == 66
actual_source = out.split("\n").last

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
require_relative "helper"
require "rubygems/bundler_version_finder"
class TestGemBundlerVersionFinder < Gem::TestCase
def setup

View File

@ -358,6 +358,8 @@ class TestGemDependency < Gem::TestCase
assert_equal [b, b_1], dep.to_specs
require "rubygems/bundler_version_finder"
Gem::BundlerVersionFinder.stub(:bundler_version, Gem::Version.new("1")) do
assert_equal [b_1, b], dep.to_specs
end

View File

@ -2,6 +2,7 @@
require_relative "helper"
require "rubygems/ext"
require "open3"
class TestGemExtCargoBuilder < Gem::TestCase
def setup
@ -22,25 +23,6 @@ class TestGemExtCargoBuilder < Gem::TestCase
FileUtils.cp_r(@fixture_dir.to_s, @ext)
end
def test_build_staticlib
skip_unsupported_platforms!
setup_rust_gem "rust_ruby_example"
content = @fixture_dir.join("Cargo.toml").read.gsub("cdylib", "staticlib")
File.write(File.join(@ext, "Cargo.toml"), content)
output = []
Dir.chdir @ext do
ENV.update(@rust_envs)
spec = Gem::Specification.new "rust_ruby_example", "0.1.0"
builder = Gem::Ext::CargoBuilder.new(spec)
assert_raise(Gem::Ext::CargoBuilder::DylibNotFoundError) do
builder.build nil, @dest_path, output
end
end
end
def test_build_cdylib
skip_unsupported_platforms!
setup_rust_gem "rust_ruby_example"
@ -66,6 +48,33 @@ class TestGemExtCargoBuilder < Gem::TestCase
raise(e)
end
def test_rubygems_cfg_passed_to_rustc
skip_unsupported_platforms!
setup_rust_gem "rust_ruby_example"
version_slug = Gem::VERSION.tr(".", "_")
output = []
replace_in_rust_file("src/lib.rs", "rubygems_x_x_x", "rubygems_#{version_slug}")
Dir.chdir @ext do
ENV.update(@rust_envs)
spec = Gem::Specification.new "rust_ruby_example", "0.1.0"
builder = Gem::Ext::CargoBuilder.new(spec)
builder.build nil, @dest_path, output
end
output = output.join "\n"
bundle = File.join(@dest_path, "release/rust_ruby_example.#{RbConfig::CONFIG['DLEXT']}")
assert_ffi_handle bundle, "hello_from_rubygems"
assert_ffi_handle bundle, "hello_from_rubygems_version"
refute_ffi_handle bundle, "should_never_exist"
rescue Exception => e
pp output if output
raise(e)
end
def test_build_fail
skip_unsupported_platforms!
setup_rust_gem "rust_ruby_example"
@ -140,7 +149,6 @@ class TestGemExtCargoBuilder < Gem::TestCase
def skip_unsupported_platforms!
pend "jruby not supported" if java_platform?
pend "truffleruby not supported (yet)" if RUBY_ENGINE == "truffleruby"
pend "mswin not supported (yet)" if RUBY_PLATFORM.include?("mswin") && ENV.key?("GITHUB_ACTIONS")
system(@rust_envs, "cargo", "-V", out: IO::NULL, err: [:child, :out])
pend "cargo not present" unless $?.success?
pend "ruby.h is not provided by ruby repo" if ruby_repo?
@ -151,4 +159,15 @@ class TestGemExtCargoBuilder < Gem::TestCase
dylib_handle = Fiddle.dlopen bundle
assert_nothing_raised { dylib_handle[name] }
end
def refute_ffi_handle(bundle, name)
require "fiddle"
dylib_handle = Fiddle.dlopen bundle
assert_raise { dylib_handle[name] }
end
def replace_in_rust_file(name, from, to)
content = @fixture_dir.join(name).read.gsub(from, to)
File.write(File.join(@ext, name), content)
end
end

View File

@ -21,6 +21,18 @@ unsafe extern "C" fn pub_reverse(_klass: VALUE, mut input: VALUE) -> VALUE {
rb_utf8_str_new(reversed_cstring.as_ptr(), size)
}
#[cfg(rubygems)]
#[no_mangle]
pub extern "C" fn hello_from_rubygems() {}
#[cfg(rubygems_0_0_0)]
#[no_mangle]
pub extern "C" fn should_never_exist() {}
#[cfg(rubygems_x_x_x)]
#[no_mangle]
pub extern "C" fn hello_from_rubygems_version() {}
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Init_rust_ruby_example() {

View File

@ -12,15 +12,15 @@ class TestGemExtCargoBuilderLinkFlagConverter < Gem::TestCase
test_lib_with_nonascii: ["-lws2_32", ["-l", "ws2_32"]],
test_simple_lib_space: ["-l foo", ["-l", "foo"]],
test_verbose_lib_space: ["--library=foo", ["-l", "foo"]],
test_libstatic_with_colon: ["-l:libssp.a", ["-l", "static=ssp"]],
test_libstatic_with_colon_space: ["-l :libssp.a", ["-l", "static=ssp"]],
test_unconventional_lib_with_colon: ["-l:ssp.a", ["-C", "link_arg=-l:ssp.a"]],
test_dylib_with_colon_space: ["-l :libssp.dylib", ["-l", "dylib=ssp"]],
test_so_with_colon_space: ["-l :libssp.so", ["-l", "dylib=ssp"]],
test_dll_with_colon_space: ["-l :libssp.dll", ["-l", "dylib=ssp"]],
test_libstatic_with_colon: ["-l:libssp.a", ["-C", "link-args=-l:libssp.a"]],
test_libstatic_with_colon_space: ["-l :libssp.a", ["-C", "link-args=-l :libssp.a"]],
test_unconventional_lib_with_colon: ["-l:ssp.a", ["-C", "link-args=-l:ssp.a"]],
test_dylib_with_colon_space: ["-l :libssp.dylib", ["-C", "link-args=-l :libssp.dylib"]],
test_so_with_colon_space: ["-l :libssp.so", ["-C", "link-args=-l :libssp.so"]],
test_dll_with_colon_space: ["-l :libssp.dll", ["-C", "link-args=-l :libssp.dll"]],
test_framework: ["-F/some/path", ["-l", "framework=/some/path"]],
test_framework_space: ["-F /some/path", ["-l", "framework=/some/path"]],
test_non_lib_dash_l: ["test_rubygems_20220413-976-lemgf9/prefix", ["-C", "link_arg=test_rubygems_20220413-976-lemgf9/prefix"]],
test_non_lib_dash_l: ["test_rubygems_20220413-976-lemgf9/prefix", ["-C", "link-args=test_rubygems_20220413-976-lemgf9/prefix"]],
}.freeze
CASES.each do |test_name, (arg, expected)|

View File

@ -2708,6 +2708,39 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
end
end
def test_validate_rust_extension_have_missing_cargo_toml_error
util_setup_validate
Dir.chdir @tempdir do
@a1.extensions = ["Cargo.toml"]
File.write File.join(@tempdir, "Cargo.toml"), ""
e = assert_raise Gem::InvalidSpecificationException do
use_ui @ui do
@a1.validate
end
end
assert_match(/but Cargo.lock is not part of the gem files/, e.message)
end
end
def test_validate_rust_extension_have_no_missing_cargo_toml_error
util_setup_validate
Dir.chdir @tempdir do
@a1.extensions = ["Cargo.toml"]
@a1.files << "Cargo.toml"
@a1.files << "Cargo.lock"
File.write File.join(@tempdir, "Cargo.toml"), ""
File.write File.join(@tempdir, "Cargo.lock"), ""
use_ui @ui do
@a1.validate
end
end
end
def test_validate_description
util_setup_validate

View File

@ -118,6 +118,8 @@ class TestKernel < Gem::TestCase
end
def test_gem_bundler_inferred_bundler_version
require "rubygems/bundler_version_finder"
Gem::BundlerVersionFinder.stub(:bundler_version, Gem::Version.new("1")) do
quick_gem "bundler", "1"
quick_gem "bundler", "2.a"

View File

@ -666,6 +666,24 @@ class TestGemRequire < Gem::TestCase
end
end
def test_require_does_not_crash_when_utilizing_bundler_version_finder
a1 = util_spec "a", "1.1", { "bundler" => ">= 0" }
a2 = util_spec "a", "1.2", { "bundler" => ">= 0" }
b1 = util_spec "bundler", "2.3.7"
b2 = util_spec "bundler", "2.3.24"
c = util_spec "c", "1", { "a" => [">= 1.1", "< 99.0"] }, "lib/test_gem_require_c.rb"
install_specs a1, a2, b1, b2, c
cmd = <<-RUBY
require "test_gem_require_c"
require "json"
RUBY
out = Gem::Util.popen({ "GEM_HOME" => @gemhome }, *ruby_with_rubygems_in_load_path, "-e", cmd)
puts out
assert $?.success?
end
private
def util_install_extension_file(name)

View File

@ -4,6 +4,7 @@ source "https://rubygems.org"
gem "test-unit", "~> 3.0"
gem "rake", "~> 13.0"
gem "rb_sys"
gem "webrick", "~> 1.6"
gem "parallel_tests", "~> 2.29"

View File

@ -10,6 +10,7 @@ GEM
parallel
power_assert (2.0.2)
rake (13.0.6)
rb_sys (0.9.52)
rdiscount (2.2.7)
ronn (0.7.3)
hpricot (>= 0.8.2)
@ -43,6 +44,7 @@ DEPENDENCIES
parallel (~> 1.19)
parallel_tests (~> 2.29)
rake (~> 13.0)
rb_sys
ronn (~> 0.7.3)
rspec-core (~> 3.12)
rspec-expectations (~> 3.12)
@ -52,4 +54,4 @@ DEPENDENCIES
webrick (~> 1.6)
BUNDLED WITH
2.4.0.dev
2.4.0

View File

@ -9,3 +9,4 @@ gem "rake"
gem "rake-compiler"
gem "rspec"
gem "test-unit"
gem "rb_sys"

View File

@ -14,6 +14,7 @@ GEM
rake (13.0.6)
rake-compiler (1.2.0)
rake
rb_sys (0.9.52)
regexp_parser (2.6.1)
rexml (3.2.5)
rspec (3.12.0)
@ -63,9 +64,10 @@ DEPENDENCIES
minitest
rake
rake-compiler
rb_sys
rspec
rubocop (~> 1.7)
test-unit
BUNDLED WITH
2.4.0.dev
2.4.0

View File

@ -9,3 +9,4 @@ gem "rake"
gem "rake-compiler"
gem "rspec"
gem "test-unit"
gem "rb_sys"

View File

@ -15,6 +15,7 @@ GEM
rake (13.0.6)
rake-compiler (1.2.0)
rake
rb_sys (0.9.52)
regexp_parser (2.6.1)
rexml (3.2.5)
rspec (3.12.0)
@ -71,9 +72,10 @@ DEPENDENCIES
minitest
rake
rake-compiler
rb_sys
rspec
standard (~> 1.0)
test-unit
BUNDLED WITH
2.4.0.dev
2.4.0

View File

@ -9,3 +9,4 @@ gem "compact_index", "~> 0.13.0"
gem "sinatra", "~> 2.0"
gem "rake", "13.0.1"
gem "builder", "~> 3.2"
gem "rb_sys"

View File

@ -11,6 +11,7 @@ GEM
rack-test (1.1.0)
rack (>= 1.0, < 3)
rake (13.0.1)
rb_sys (0.9.52)
ruby2_keywords (0.0.5)
sinatra (2.0.8.1)
mustermann (~> 1.0)
@ -36,8 +37,9 @@ DEPENDENCIES
rack (= 2.0.8)
rack-test (~> 1.1)
rake (= 13.0.1)
rb_sys
sinatra (~> 2.0)
webrick (= 1.7.0)
BUNDLED WITH
2.4.0.dev
2.4.0