Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/actions/cibuildgem/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ inputs:
step:
description: "The step to run"
required: true
include-single-abi:
description: "Whether to also compile a set of single-ABI gems."
required: false
default: false
test-command:
description: "The command to run the test suite. By default cibuildgem will run either `bundle exec rake test` or `bundle exec rake spec` depending on the test framework used."
required: false
Expand Down Expand Up @@ -41,7 +45,7 @@ runs:
if: "${{ inputs.step == 'compile' }}"
working-directory: ${{ inputs.working-directory }}
shell: bash
run: cibuildgem package
run: cibuildgem package --include-single-abi "${{ inputs.include-single-abi }}"
- name: Upload artifacts
if: "${{ inputs.step == 'compile' }}"
uses: actions/upload-artifact@v4
Expand Down
13 changes: 10 additions & 3 deletions lib/cibuildgem/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def compile
run_rake_tasks!("cibuildgem:setup", :compile)
end

desc "package", "Compile and package a 'fat gem'.", hide: true
desc "package", "Compile and package a 'multi-ABI gem'.", hide: true
long_desc <<~MSG
This command should normally run on CI, using the cibuildgem workflow. It will not work locally unless
the environment is properly setup.
Expand All @@ -42,13 +42,20 @@ def compile
- A gem without precompiled binary (Ruby platform).

The gem with precompiled binaries will be packaged with multiple binaries compatible for different
Ruby ABI (depending on what Ruby version the gem supports).
Ruby ABI (depending on what Ruby version the gem supports). If `--include-single-abi` is passed,
cibuildgem will also compile a set of single-ABI gems per Ruby version supported by the gem.
MSG
method_option "gemspec", type: "string", required: false, desc: "The gemspec to use. Defaults to the gemspec from the current working directory."
method_option "include-single-abi", type: :boolean, required: false, default: false, desc: "Whether to also compile a set of single-ABI gems. Defaults to false."
def package
ENV["RUBY_CC_VERSION"] ||= compilation_task.ruby_cc_version

run_rake_tasks!("cibuildgem:setup", :cross, :native, :gem)
if options["include-single-abi"] == true
compilation_task.ruby_versions.each do |ruby_version|
ENV["RUBY_CC_VERSION"] = ruby_version.to_s
run_rake_tasks!("cibuildgem:setup", :cross, :native, :gem)
end
end
end

desc "test", "Run the test suites of the target gem"
Expand Down
11 changes: 8 additions & 3 deletions lib/cibuildgem/compilation_tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ def setup
setup_packaging if create_packaging_task
end

def ruby_cc_version
def ruby_versions
required_ruby_version = @gemspec.required_ruby_version
selected_rubies = RubySeries.versions_to_compile_against(required_ruby_version)
RubySeries.versions_to_compile_against(required_ruby_version)
end

selected_rubies.map(&:to_s).join(":")
def ruby_cc_version
ruby_versions.map(&:to_s).join(":")
end

def normalized_platform
Expand All @@ -45,6 +47,9 @@ def normalized_platform
private

def setup_packaging
# Creates a Gem::PackageTask for the ordinary Ruby gem.
# Elsewhere in rake-compiler we also create one for the compiled gem.
# Internally in PackageTask, we define the :gem rake task which calls Gem::Package.build.
Gem::PackageTask.new(gemspec) do |pkg|
pkg.need_zip = true
pkg.need_tar = true
Expand Down
66 changes: 66 additions & 0 deletions test/cli_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,72 @@ def test_package_when_a_rakefile_defines_an_extension_task
assert_predicate($CHILD_STATUS, :success?)
end

def test_package_single_abi_runs_rake_tasks_for_each_ruby_version
cli = CLI.new([], { "include-single-abi" => true })
compilation_task = Struct.new(:ruby_cc_version, :ruby_versions).new("3.3.8:3.2.8", [
Gem::Version.new("3.3.8"),
Gem::Version.new("3.2.8"),
])
rake_calls = []
run_rake_tasks = proc do |*tasks|
rake_calls << [ENV.fetch("RUBY_CC_VERSION"), tasks]
end

cli.stub(:compilation_task, compilation_task) do
cli.stub(:run_rake_tasks!, run_rake_tasks) do
cli.package
end
end

expected_tasks = ["cibuildgem:setup", :cross, :native, :gem]
assert_equal(
[
["3.3.8:3.2.8", expected_tasks],
["3.3.8", expected_tasks],
["3.2.8", expected_tasks],
],
rake_calls,
)
end

def test_package_defaults_to_multi_abi
cli = CLI.new
compilation_task = Struct.new(:ruby_cc_version).new("3.3.8:3.2.8")
rake_calls = []
run_rake_tasks = proc do |*tasks|
rake_calls << [ENV.fetch("RUBY_CC_VERSION"), tasks]
end

cli.stub(:compilation_task, compilation_task) do
cli.stub(:run_rake_tasks!, run_rake_tasks) do
cli.package
end
end

assert_equal(
[["3.3.8:3.2.8", ["cibuildgem:setup", :cross, :native, :gem]]],
rake_calls,
)
end

def test_package_multi_abi_preserves_ruby_cc_version_from_environment
ENV["RUBY_CC_VERSION"] = "3.1.6"
cli = CLI.new
compilation_task = Struct.new(:ruby_cc_version).new("3.3.8:3.2.8")
ruby_cc_versions_used = []
run_rake_tasks = proc do |*|
ruby_cc_versions_used << ENV.fetch("RUBY_CC_VERSION")
end

cli.stub(:compilation_task, compilation_task) do
cli.stub(:run_rake_tasks!, run_rake_tasks) do
cli.package
end
end

assert_equal(["3.1.6"], ruby_cc_versions_used)
end

def test_keep_the_extension_task_config_defined_by_the_gem
Dir.chdir("test/fixtures/with_configured_ext") do
cli = CLI.new
Expand Down
Loading