Skip to content

Commit 54fefd3

Browse files
Gira ChawdaGira Chawda
authored andcommitted
Align single-ABI packaging with rake-compiler API
1 parent 9b06afb commit 54fefd3

4 files changed

Lines changed: 115 additions & 87 deletions

File tree

‎.github/actions/cibuildgem/action.yml‎

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ runs:
5454
path: |
5555
${{ inputs.working-directory }}/tmp/**/*.so
5656
${{ inputs.working-directory }}/tmp/**/*.bundle
57-
${{ inputs.working-directory }}/pkg/**/*.gem
57+
${{ inputs.working-directory }}/pkg/*.gem
5858
if-no-files-found: error
5959
retention-days: 1
6060
- name: "Download compiled binaries"
@@ -68,13 +68,22 @@ runs:
6868
working-directory: ${{ inputs.working-directory }}
6969
shell: bash
7070
run: |
71-
ruby_abi="$(ruby -e 'print RUBY_VERSION.split(".").first(2).join(".")')"
72-
single_abi_gems=(pkg/"${ruby_abi}"/*.gem)
73-
if [[ ! -e "${single_abi_gems[0]}" ]]; then
74-
echo "No single-ABI gem found for Ruby ${ruby_abi}" >&2
75-
exit 1
76-
fi
77-
gem install "${single_abi_gems[@]}"
71+
ruby <<'RUBY' > /tmp/single-abi-gems
72+
require "rubygems/package"
73+
74+
ruby_abi = RUBY_VERSION.split(".").first(2).join(".")
75+
expected_requirement = Gem::Requirement.new("~> #{ruby_abi}.0")
76+
gems = Dir["pkg/*.gem"].select do |path|
77+
spec = Gem::Package.new(path).spec
78+
spec.platform != Gem::Platform::RUBY && spec.required_ruby_version == expected_requirement
79+
end
80+
81+
abort "No single-ABI gem found for Ruby #{ruby_abi}" if gems.empty?
82+
83+
puts gems
84+
RUBY
85+
86+
xargs gem install < /tmp/single-abi-gems
7887
- name: "Copy staging binary to the libdir"
7988
if: "${{ inputs.step == 'test_cross' }}"
8089
working-directory: ${{ inputs.working-directory }}
@@ -110,8 +119,8 @@ runs:
110119
- name: "Push the gems to RubyGems"
111120
if: "${{ inputs.step == 'release' }}"
112121
shell: bash
113-
run: cibuildgem release --glob "pkg/**/*.gem"
122+
run: cibuildgem release --glob "pkg/*"
114123
- name: "Waiting for RubyGems to fully index the new gems"
115124
if: "${{ inputs.step == 'release' }}"
116125
shell: bash
117-
run: find pkg -type f -name '*.gem' -exec gem exec rubygems-await {} +
126+
run: gem exec rubygems-await pkg/*.gem

‎lib/cibuildgem/cli.rb‎

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,8 @@ def compile
4949
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."
5050
def package
5151
ENV["RUBY_CC_VERSION"] ||= compilation_task.ruby_cc_version
52-
run_rake_tasks!("cibuildgem:setup", :cross, :native, :gem)
53-
if options["include-single-abi"] == true
54-
compilation_task.ruby_versions.each do |ruby_version|
55-
ENV["RUBY_CC_VERSION"] = ruby_version.to_s
56-
run_rake_tasks!("cibuildgem:setup", :cross, :native, :gem)
57-
58-
ruby_abi = ruby_version.segments.first(2).join(".")
59-
next if Dir.glob(File.join("pkg", ruby_abi, "*.gem")).any?
60-
61-
raise "Could not build a single-ABI gem for Ruby #{ruby_abi}. Ensure RubyGems and rake-compiler support content-addressable gems."
62-
end
52+
with_content_addressable_packaging(options["include-single-abi"] == true) do
53+
run_rake_tasks!("cibuildgem:setup", :cross, :native, :gem)
6354
end
6455
end
6556

@@ -151,6 +142,19 @@ def print_normalized_platform
151142

152143
private
153144

145+
def with_content_addressable_packaging(enabled)
146+
previous = ENV[CompilationTasks::CONTENT_ADDRESSABLE_ENV]
147+
ENV[CompilationTasks::CONTENT_ADDRESSABLE_ENV] = enabled ? "true" : "false"
148+
149+
yield
150+
ensure
151+
if previous.nil?
152+
ENV.delete(CompilationTasks::CONTENT_ADDRESSABLE_ENV)
153+
else
154+
ENV[CompilationTasks::CONTENT_ADDRESSABLE_ENV] = previous
155+
end
156+
end
157+
154158
def run_rake_tasks!(*tasks)
155159
all_tasks = tasks.join(" ")
156160
rakelibdir = [File.expand_path("tasks", __dir__), "rakelib"].join(File::PATH_SEPARATOR)

‎lib/cibuildgem/compilation_tasks.rb‎

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@
77

88
module Cibuildgem
99
class CompilationTasks
10-
attr_reader :gemspec, :native, :create_packaging_task, :extension_task
10+
CONTENT_ADDRESSABLE_ENV = "CIBUILDGEM_CONTENT_ADDRESSABLE"
1111

12-
def initialize(create_packaging_task = false, gemspec = nil)
12+
attr_reader :gemspec, :native, :create_packaging_task, :extension_task, :content_addressable
13+
14+
def initialize(
15+
create_packaging_task = false,
16+
gemspec = nil,
17+
content_addressable: ENV[CONTENT_ADDRESSABLE_ENV] == "true"
18+
)
1319
@gemspec = Bundler.load_gemspec(gemspec || find_gemspec)
1420
verify_gemspec!
1521

1622
@create_packaging_task = create_packaging_task
23+
@content_addressable = content_addressable
1724
end
1825

1926
def setup
@@ -25,13 +32,11 @@ def setup
2532
setup_packaging if create_packaging_task
2633
end
2734

28-
def ruby_versions
35+
def ruby_cc_version
2936
required_ruby_version = @gemspec.required_ruby_version
30-
RubySeries.versions_to_compile_against(required_ruby_version)
31-
end
37+
selected_rubies = RubySeries.versions_to_compile_against(required_ruby_version)
3238

33-
def ruby_cc_version
34-
ruby_versions.map(&:to_s).join(":")
39+
selected_rubies.map(&:to_s).join(":")
3540
end
3641

3742
def normalized_platform
@@ -47,9 +52,6 @@ def normalized_platform
4752
private
4853

4954
def setup_packaging
50-
# Creates a Gem::PackageTask for the ordinary Ruby gem.
51-
# Elsewhere in rake-compiler we also create one for the compiled gem.
52-
# Internally in PackageTask, we define the :gem rake task which calls Gem::Package.build.
5355
Gem::PackageTask.new(gemspec) do |pkg|
5456
pkg.need_zip = true
5557
pkg.need_tar = true
@@ -67,6 +69,14 @@ def define_task(path, binary_name)
6769
@extension_task.cross_platform = normalized_platform
6870
@extension_task.cross_compile = true
6971
@extension_task.no_native = true
72+
if content_addressable
73+
if @extension_task.respond_to?(:content_addressable=)
74+
@extension_task.content_addressable = true
75+
else
76+
warn "WARNING: content-addressable packaging was requested, but rake-compiler does not support it. " \
77+
"Upgrade rake-compiler to build single-ABI gems."
78+
end
79+
end
7080

7181
Rake::ExtensionTask.enable!
7282
@extension_task.define

‎test/cli_test.rb‎

Lines changed: 61 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
module Cibuildgem
88
class CLITest < Minitest::Test
9+
TEST_CONTENT_ADDRESSABLE_ENV = "CIBUILDGEM_CONTENT_ADDRESSABLE"
10+
911
def setup
1012
super
1113

@@ -14,6 +16,7 @@ def setup
1416

1517
def teardown
1618
ENV.delete("RUBY_CC_VERSION")
19+
ENV.delete(TEST_CONTENT_ADDRESSABLE_ENV)
1720

1821
super
1922
end
@@ -303,69 +306,42 @@ def test_package_when_a_rakefile_defines_an_extension_task
303306
assert_predicate($CHILD_STATUS, :success?)
304307
end
305308

306-
def test_package_single_abi_runs_rake_tasks_for_each_ruby_version
309+
def test_package_single_abi_enables_content_addressable_packaging_for_one_rake_run
307310
cli = CLI.new([], { "include-single-abi" => true })
308-
compilation_task = Struct.new(:ruby_cc_version, :ruby_versions).new("3.3.8:3.2.8", [
309-
Gem::Version.new("3.3.8"),
310-
Gem::Version.new("3.2.8"),
311-
])
311+
compilation_task = Struct.new(:ruby_cc_version).new("3.3.8:3.2.8")
312312
rake_calls = []
313-
gem_globs = []
314313
run_rake_tasks = proc do |*tasks|
315-
rake_calls << [ENV.fetch("RUBY_CC_VERSION"), tasks]
316-
end
317-
find_built_gems = proc do |glob|
318-
gem_globs << glob
319-
[glob.sub("*", "built")]
314+
rake_calls << [
315+
ENV.fetch("RUBY_CC_VERSION"),
316+
ENV.fetch(TEST_CONTENT_ADDRESSABLE_ENV),
317+
tasks,
318+
]
320319
end
321320

322321
cli.stub(:compilation_task, compilation_task) do
323322
cli.stub(:run_rake_tasks!, run_rake_tasks) do
324-
Dir.stub(:glob, find_built_gems) do
325-
cli.package
326-
end
323+
cli.package
327324
end
328325
end
329326

330-
expected_tasks = ["cibuildgem:setup", :cross, :native, :gem]
331327
assert_equal(
332-
[
333-
["3.3.8:3.2.8", expected_tasks],
334-
["3.3.8", expected_tasks],
335-
["3.2.8", expected_tasks],
336-
],
328+
[["3.3.8:3.2.8", "true", ["cibuildgem:setup", :cross, :native, :gem]]],
337329
rake_calls,
338330
)
339-
assert_equal(["pkg/3.3/*.gem", "pkg/3.2/*.gem"], gem_globs)
331+
assert_nil(ENV[TEST_CONTENT_ADDRESSABLE_ENV])
340332
end
341333

342-
def test_package_single_abi_fails_when_no_single_abi_gem_is_built
343-
cli = CLI.new([], { "include-single-abi" => true })
344-
compilation_task = Struct.new(:ruby_cc_version, :ruby_versions).new(
345-
"3.3.8",
346-
[Gem::Version.new("3.3.8")],
347-
)
348-
349-
error = cli.stub(:compilation_task, compilation_task) do
350-
cli.stub(:run_rake_tasks!, nil) do
351-
Dir.stub(:glob, []) do
352-
assert_raises(RuntimeError) { cli.package }
353-
end
354-
end
355-
end
356-
357-
assert_equal(
358-
"Could not build a single-ABI gem for Ruby 3.3. Ensure RubyGems and rake-compiler support content-addressable gems.",
359-
error.message,
360-
)
361-
end
362-
363-
def test_package_defaults_to_multi_abi
334+
def test_package_defaults_to_multi_abi_and_preserves_ruby_cc_version_from_environment
335+
ENV["RUBY_CC_VERSION"] = "3.1.6"
364336
cli = CLI.new
365337
compilation_task = Struct.new(:ruby_cc_version).new("3.3.8:3.2.8")
366338
rake_calls = []
367339
run_rake_tasks = proc do |*tasks|
368-
rake_calls << [ENV.fetch("RUBY_CC_VERSION"), tasks]
340+
rake_calls << [
341+
ENV.fetch("RUBY_CC_VERSION"),
342+
ENV.fetch(TEST_CONTENT_ADDRESSABLE_ENV),
343+
tasks,
344+
]
369345
end
370346

371347
cli.stub(:compilation_task, compilation_task) do
@@ -375,27 +351,56 @@ def test_package_defaults_to_multi_abi
375351
end
376352

377353
assert_equal(
378-
[["3.3.8:3.2.8", ["cibuildgem:setup", :cross, :native, :gem]]],
354+
[["3.1.6", "false", ["cibuildgem:setup", :cross, :native, :gem]]],
379355
rake_calls,
380356
)
381357
end
382358

383-
def test_package_multi_abi_preserves_ruby_cc_version_from_environment
384-
ENV["RUBY_CC_VERSION"] = "3.1.6"
385-
cli = CLI.new
386-
compilation_task = Struct.new(:ruby_cc_version).new("3.3.8:3.2.8")
387-
ruby_cc_versions_used = []
388-
run_rake_tasks = proc do |*|
389-
ruby_cc_versions_used << ENV.fetch("RUBY_CC_VERSION")
359+
def test_compilation_task_enables_content_addressable_extension_task
360+
require "cibuildgem/extension_patch"
361+
362+
extension_task = Class.new do
363+
attr_accessor :config_script, :content_addressable, :cross_compile, :cross_platform, :ext_dir, :gem_spec,
364+
:lib_dir, :name, :no_native
365+
366+
def define; end
367+
end.new
368+
original_current = Rake::ExtensionTask.current
369+
370+
Dir.chdir("test/fixtures/with_ext") do
371+
Rake::ExtensionTask.current = extension_task
372+
CompilationTasks.new(false, nil, content_addressable: true).setup
390373
end
391374

392-
cli.stub(:compilation_task, compilation_task) do
393-
cli.stub(:run_rake_tasks!, run_rake_tasks) do
394-
cli.package
375+
assert_equal(true, extension_task.content_addressable)
376+
ensure
377+
Rake::ExtensionTask.current = original_current if Rake::ExtensionTask.respond_to?(:current=)
378+
Rake::Task.clear
379+
end
380+
381+
def test_compilation_task_warns_when_rake_compiler_does_not_support_content_addressable
382+
require "cibuildgem/extension_patch"
383+
384+
extension_task = Class.new do
385+
attr_accessor :config_script, :cross_compile, :cross_platform, :ext_dir, :gem_spec,
386+
:lib_dir, :name, :no_native
387+
388+
def define; end
389+
end.new
390+
original_current = Rake::ExtensionTask.current
391+
392+
_, err = capture_io do
393+
Dir.chdir("test/fixtures/with_ext") do
394+
Rake::ExtensionTask.current = extension_task
395+
CompilationTasks.new(false, nil, content_addressable: true).setup
395396
end
396397
end
397398

398-
assert_equal(["3.1.6"], ruby_cc_versions_used)
399+
assert_includes(err, "content-addressable packaging was requested")
400+
assert_includes(err, "Upgrade rake-compiler")
401+
ensure
402+
Rake::ExtensionTask.current = original_current if Rake::ExtensionTask.respond_to?(:current=)
403+
Rake::Task.clear
399404
end
400405

401406
def test_keep_the_extension_task_config_defined_by_the_gem

0 commit comments

Comments
 (0)