Skip to content

Commit 6812c27

Browse files
authored
Merge pull request #150 from rake-compiler/flavorjones-expose-cross-rubies
feat: introduce `RakeCompilerDock.set_ruby_cc_version`
2 parents a173ba7 + 2205d50 commit 6812c27

File tree

4 files changed

+204
-1
lines changed

4 files changed

+204
-1
lines changed

README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,44 @@ Other environment variables can be set or passed through to the container like t
352352
RakeCompilerDock.sh "rake cross native gem OPENSSL_VERSION=#{ENV['OPENSSL_VERSION']}"
353353
```
354354

355+
### Choosing specific Ruby versions to support
356+
357+
If you only want to precompile for certain Ruby versions, you can specify those versions by overwriting the `RUBY_CC_VERSION` environment variable.
358+
359+
For example, if you wanted to only support Ruby 3.4 and 3.3, you might set this variable to:
360+
361+
``` ruby
362+
ENV["RUBY_CC_VERSION"] = "3.4.1:3.3.7"
363+
```
364+
365+
In practice, though, hardcoding these versions is brittle because the patch versions in the container may very from release to release.
366+
367+
A more robust way to do this is to use `RakeCompilerDock.ruby_cc_version` which accepts an array of Ruby minor versions or patch version requirements.
368+
369+
``` ruby
370+
RakeCompilerDock.ruby_cc_version("3.3", "3.4")
371+
# => "3.4.1:3.3.7"
372+
373+
RakeCompilerDock.ruby_cc_version("~> 3.4.0", "~> 3.3.0")
374+
# => "3.4.1:3.3.7"
375+
376+
RakeCompilerDock.ruby_cc_version("~> 3.3")
377+
# => "3.4.1:3.3.7"
378+
```
379+
380+
So you can either set the environment variable directly:
381+
382+
``` ruby
383+
ENV["RUBY_CC_VERSION"] = RakeCompilerDock.ruby_cc_version("~> 3.1")
384+
```
385+
386+
or do the same thing using the `set_ruby_cc_version` convenience method:
387+
388+
``` ruby
389+
RakeCompilerDock.set_ruby_cc_version("~> 3.1") # equivalent to the above assignment
390+
```
391+
392+
355393

356394
## More information
357395

lib/rake_compiler_dock.rb

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,5 +73,86 @@ def exec(*args, &block)
7373
Starter.exec(*args, &block)
7474
end
7575

76-
module_function :exec, :sh, :image_name
76+
# Retrieve the cross-rubies that are available in the docker image. This can be used to construct
77+
# a custom `RUBY_CC_VERSION` string that is valid.
78+
#
79+
# Returns a Hash<minor_version => corresponding_patch_version>
80+
#
81+
# For example:
82+
#
83+
# RakeCompilerDock.cross_rubies
84+
# # => {
85+
# # "3.4" => "3.4.1",
86+
# # "3.3" => "3.3.5",
87+
# # "3.2" => "3.2.6",
88+
# # "3.1" => "3.1.6",
89+
# # "3.0" => "3.0.7",
90+
# # "2.7" => "2.7.8",
91+
# # "2.6" => "2.6.10",
92+
# # "2.5" => "2.5.9",
93+
# # "2.4" => "2.4.10",
94+
# # }
95+
#
96+
def cross_rubies
97+
{
98+
"3.4" => "3.4.1",
99+
"3.3" => "3.3.7",
100+
"3.2" => "3.2.6",
101+
"3.1" => "3.1.6",
102+
"3.0" => "3.0.7",
103+
"2.7" => "2.7.8",
104+
"2.6" => "2.6.10",
105+
"2.5" => "2.5.9",
106+
"2.4" => "2.4.10",
107+
}
108+
end
109+
110+
# Returns a valid RUBY_CC_VERSION string for the given requirements,
111+
# where each `requirement` may be:
112+
#
113+
# - a String that matches the minor version exactly
114+
# - a String that can be used as a Gem::Requirement constructor argument
115+
# - a Gem::Requirement object
116+
#
117+
# Note that the returned string will contain versions sorted in descending order.
118+
#
119+
# For example:
120+
# RakeCompilerDock.ruby_cc_version("2.7", "3.4")
121+
# # => "3.4.1:2.7.8"
122+
#
123+
# RakeCompilerDock.ruby_cc_version("~> 3.2")
124+
# # => "3.4.1:3.3.7:3.2.6"
125+
#
126+
# RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("~> 3.2"))
127+
# # => "3.4.1:3.3.7:3.2.6"
128+
#
129+
def ruby_cc_version(*requirements)
130+
cross = cross_rubies
131+
output = []
132+
133+
if requirements.empty?
134+
output += cross.values
135+
else
136+
requirements.each do |requirement|
137+
if cross[requirement]
138+
output << cross[requirement]
139+
else
140+
requirement = Gem::Requirement.new(requirement) unless requirement.is_a?(Gem::Requirement)
141+
versions = cross.values.find_all { |v| requirement.satisfied_by?(Gem::Version.new(v)) }
142+
raise("No matching ruby version for requirement: #{requirement.inspect}") if versions.empty?
143+
output += versions
144+
end
145+
end
146+
end
147+
148+
output.uniq.sort.reverse.join(":")
149+
end
150+
151+
# Set the environment variable `RUBY_CC_VERSION` to the value returned by `ruby_cc_version`,
152+
# for the given requirements.
153+
def set_ruby_cc_version(*requirements)
154+
ENV["RUBY_CC_VERSION"] = ruby_cc_version(*requirements)
155+
end
156+
157+
module_function :exec, :sh, :image_name, :cross_rubies, :ruby_cc_version, :set_ruby_cc_version
77158
end

test/test_environment_variables.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def test_RUBY_CC_VERSION
3434
df = File.read(File.expand_path("../../Dockerfile.mri.erb", __FILE__))
3535
df =~ /^ENV RUBY_CC_VERSION=(.*)$/
3636
assert_equal $1, rcd_env['RUBY_CC_VERSION']
37+
38+
assert_equal RakeCompilerDock.ruby_cc_version, rcd_env['RUBY_CC_VERSION']
3739
end
3840

3941
def test_RAKE_EXTENSION_TASK_NO_NATIVE

test/test_versions.rb

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
require 'rake_compiler_dock'
2+
require 'rbconfig'
3+
require 'test/unit'
4+
5+
class TestVersions < Test::Unit::TestCase
6+
def test_cross_rubies
7+
cross = RakeCompilerDock.cross_rubies
8+
assert_operator(cross, :is_a?, Hash)
9+
cross.each do |minor, patch|
10+
assert_match(/^\d+\.\d+$/, minor)
11+
assert_match(/^\d+\.\d+\.\d+$/, patch)
12+
end
13+
end
14+
15+
def test_ruby_cc_versions_no_args
16+
cross = RakeCompilerDock.cross_rubies
17+
expected = cross.values.sort.reverse.join(":")
18+
19+
assert_equal(expected, RakeCompilerDock.ruby_cc_version)
20+
end
21+
22+
def test_ruby_cc_versions_strings
23+
cross = RakeCompilerDock.cross_rubies
24+
25+
expected = cross["3.4"]
26+
assert_equal(expected, RakeCompilerDock.ruby_cc_version("3.4"))
27+
28+
expected = [cross["3.4"], cross["3.2"]].join(":")
29+
assert_equal(expected, RakeCompilerDock.ruby_cc_version("3.4", "3.2"))
30+
31+
expected = [cross["3.4"], cross["3.2"]].join(":")
32+
assert_equal(expected, RakeCompilerDock.ruby_cc_version("3.2", "3.4"))
33+
34+
assert_raises do
35+
RakeCompilerDock.ruby_cc_version("9.8")
36+
end
37+
38+
assert_raises do
39+
RakeCompilerDock.ruby_cc_version("foo")
40+
end
41+
end
42+
43+
def test_ruby_cc_versions_requirements
44+
cross = RakeCompilerDock.cross_rubies
45+
46+
expected = cross["3.4"]
47+
assert_equal(expected, RakeCompilerDock.ruby_cc_version("~> 3.4"))
48+
assert_equal(expected, RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("~> 3.4")))
49+
50+
expected = [cross["3.4"], cross["3.3"], cross["3.2"]].join(":")
51+
assert_equal(expected, RakeCompilerDock.ruby_cc_version("~> 3.2"))
52+
assert_equal(expected, RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("~> 3.2")))
53+
54+
expected = [cross["3.4"], cross["3.2"]].join(":")
55+
assert_equal(expected, RakeCompilerDock.ruby_cc_version("~> 3.2.0", "~> 3.4.0"))
56+
assert_equal(expected, RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("~> 3.2.0"), Gem::Requirement.new("~> 3.4.0")))
57+
58+
expected = [cross["3.4"], cross["3.3"], cross["3.2"]].join(":")
59+
assert_equal(expected, RakeCompilerDock.ruby_cc_version(">= 3.2"))
60+
assert_equal(expected, RakeCompilerDock.ruby_cc_version(Gem::Requirement.new(">= 3.2")))
61+
62+
assert_raises do
63+
RakeCompilerDock.ruby_cc_version(Gem::Requirement.new("> 9.8"))
64+
end
65+
end
66+
67+
def test_set_ruby_cc_versions
68+
original_ruby_cc_versions = ENV["RUBY_CC_VERSION"]
69+
cross = RakeCompilerDock.cross_rubies
70+
71+
RakeCompilerDock.set_ruby_cc_version(Gem::Requirement.new("~> 3.2.0"), Gem::Requirement.new("~> 3.4.0"))
72+
assert_equal([cross["3.4"], cross["3.2"]].join(":"), ENV["RUBY_CC_VERSION"])
73+
74+
RakeCompilerDock.set_ruby_cc_version("~> 3.2.0", "~> 3.4.0")
75+
assert_equal([cross["3.4"], cross["3.2"]].join(":"), ENV["RUBY_CC_VERSION"])
76+
77+
RakeCompilerDock.set_ruby_cc_version("~> 3.1")
78+
assert_equal([cross["3.4"], cross["3.3"], cross["3.2"], cross["3.1"]].join(":"), ENV["RUBY_CC_VERSION"])
79+
ensure
80+
ENV["RUBY_CC_VERSION"] = original_ruby_cc_versions
81+
end
82+
end

0 commit comments

Comments
 (0)