Skip to content

Commit 3ee21a5

Browse files
hsbtclaude
andcommitted
Adapt bundler jobserver specs to build logs no longer installed
These specs read the `make -jN` command line from `gem_make.out`, which a successful build no longer writes. The integration specs in `install_spec` now force the build to fail so the command lands in `build_info`, and the `parallel_installer` specs assert on the number of jobserver slots each gem's build acquired, which is exactly what becomes `make -jN`, instead of reading a build log. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 754a583 commit 3ee21a5

2 files changed

Lines changed: 54 additions & 36 deletions

File tree

spec/bundler/installer/parallel_installer_spec.rb

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -149,34 +149,37 @@
149149
let(:gem_two) { definition.specs.find {|spec| spec.name == "two" } }
150150

151151
it "takes all available slots" do
152-
redefine_build_jobs do
152+
acquired = track_build_jobs(rendezvous: true) do
153153
Bundler::ParallelInstaller.call(installer, definition.specs, 5, false, true)
154154
end
155155

156-
# Take 3 slots out of the 5 available.
157-
expect(File.read(File.join(gem_one.extension_dir, "gem_make.out"))).to include("make -j3")
156+
# Take 3 slots (capped per gem) out of the 5 available.
157+
expect(acquired["one"]).to eq(3)
158158
# Take the remaining 2 slots.
159-
expect(File.read(File.join(gem_two.extension_dir, "gem_make.out"))).to include("make -j2")
159+
expect(acquired["two"]).to eq(2)
160160
end
161161

162162
it "fallback to non parallel when no slots are available" do
163-
redefine_build_jobs do
163+
acquired = track_build_jobs(rendezvous: true) do
164164
Bundler::ParallelInstaller.call(installer, definition.specs, 3, false, true)
165165
end
166166

167167
# Take 3 slots out of the 3 available.
168-
expect(File.read(File.join(gem_one.extension_dir, "gem_make.out"))).to include("make -j3")
168+
expect(acquired["one"]).to eq(3)
169169
# Fallback to one slot (non parallel).
170-
expect(File.read(File.join(gem_two.extension_dir, "gem_make.out"))).to_not include("make -j")
170+
expect(acquired["two"]).to eq(1)
171171
end
172172

173173
it "uses one jobs when installing serially" do
174+
acquired = nil
174175
Bundler.settings.temporary(jobs: 1) do
175-
Bundler::ParallelInstaller.call(installer, definition.specs, 1, false, true)
176+
acquired = track_build_jobs do
177+
Bundler::ParallelInstaller.call(installer, definition.specs, 1, false, true)
178+
end
176179
end
177180

178-
expect(File.read(File.join(gem_one.extension_dir, "gem_make.out"))).to_not include("make -j")
179-
expect(File.read(File.join(gem_two.extension_dir, "gem_make.out"))).to_not include("make -j")
181+
expect(acquired["one"]).to eq(1)
182+
expect(acquired["two"]).to eq(1)
180183
end
181184

182185
it "release the job slots" do
@@ -188,39 +191,52 @@
188191
end
189192
end
190193

191-
Bundler::ParallelInstaller.call(installer, definition.specs, 3, false, true)
194+
acquired = track_build_jobs do
195+
Bundler::ParallelInstaller.call(installer, definition.specs, 3, false, true)
196+
end
192197

193198
# Take 3 slots out of the 3 available.
194-
expect(File.read(File.join(gem_one.extension_dir, "gem_make.out"))).to include("make -j3")
195-
# Take 3 slots that were released.
196-
expect(File.read(File.join(gem_two.extension_dir, "gem_make.out"))).to include("make -j3")
199+
expect(acquired["one"]).to eq(3)
200+
# Take 3 slots that were released by `one`.
201+
expect(acquired["two"]).to eq(3)
197202
end
198203

199-
def redefine_build_jobs
204+
# Records how many jobserver slots each gem's build acquired. RubyGems turns
205+
# that count directly into `make -jN`, so asserting on it verifies slot
206+
# allocation and release without reading a build log, which a successful
207+
# build no longer writes. With +rendezvous+, "one" grabs its slots first and
208+
# holds them until "two" has grabbed the rest, making the split deterministic.
209+
def track_build_jobs(rendezvous: false)
210+
acquired = {}
200211
old_method = Bundler::RubyGemsGemInstaller.instance_method(:build_jobs)
201212
Bundler::RubyGemsGemInstaller.remove_method(:build_jobs)
202213

203-
# Rendezvous so that "one" grabs its slots first and keeps holding them
204-
# until "two" has grabbed the rest. Blocking on a queue avoids the
205-
# busy-wait and makes the ordering deterministic.
206214
one_acquired = Thread::Queue.new
207215
two_acquired = Thread::Queue.new
208216

209217
Bundler::RubyGemsGemInstaller.define_method(:build_jobs) do
210-
if spec.name == "one"
211-
value = old_method.bind(self).call
212-
one_acquired << true
213-
two_acquired.pop
214-
elsif spec.name == "two"
215-
one_acquired.pop
216-
value = old_method.bind(self).call
217-
two_acquired << true
218-
end
218+
value =
219+
if rendezvous && spec.name == "one"
220+
v = old_method.bind(self).call
221+
one_acquired << true
222+
two_acquired.pop
223+
v
224+
elsif rendezvous && spec.name == "two"
225+
one_acquired.pop
226+
v = old_method.bind(self).call
227+
two_acquired << true
228+
v
229+
else
230+
old_method.bind(self).call
231+
end
219232

233+
acquired[spec.name] = value
220234
value
221235
end
222236

223237
yield
238+
239+
acquired
224240
ensure
225241
Bundler::RubyGemsGemInstaller.remove_method(:build_jobs)
226242
Bundler::RubyGemsGemInstaller.define_method(:build_jobs, old_method)

spec/commands/install_spec.rb

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,10 +1369,18 @@ def run
13691369
s.extensions = extension
13701370

13711371
s.write(extension, extconf_code)
1372+
# A successful build no longer leaves gem_make.out behind. Force the
1373+
# build to fail at the make stage so the make command line, including
1374+
# the jobserver `-j`, is recorded in build_info for these assertions.
1375+
s.write("ext/mypsych/mypsych.c", "#error forced build failure for test")
13721376
end
13731377
end
13741378
end
13751379

1380+
def gem_make_out
1381+
File.read(File.join(@gemspec.build_info_dir, "#{@gemspec.full_name}.gem_make.out"))
1382+
end
1383+
13761384
after do
13771385
if @old_makeflags
13781386
ENV["MAKEFLAGS"] = @old_makeflags
@@ -1384,39 +1392,33 @@ def run
13841392
it "doesn't pass down -j to make when MAKEFLAGS is set" do
13851393
ENV["MAKEFLAGS"] = "-j1"
13861394

1387-
install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" })
1395+
install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" }, raise_on_error: false)
13881396
source "https://gem.repo4"
13891397
gem "mypsych"
13901398
G
13911399

1392-
gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
1393-
13941400
expect(gem_make_out).not_to include("make -j8")
13951401
end
13961402

13971403
it "uses 3 slots from the available pool when running the compilation of an extension", rubygems: ">= 4.1.0.dev" do
13981404
ENV.delete("MAKEFLAGS")
13991405

1400-
install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" })
1406+
install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" }, raise_on_error: false)
14011407
source "https://gem.repo4"
14021408
gem "mypsych"
14031409
G
14041410

1405-
gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
1406-
14071411
expect(gem_make_out).to include("make -j3")
14081412
end
14091413

14101414
it "consumes 3 slots from the pool when BUNDLE_JOBS isn't set", rubygems: ">= 4.1.0.dev" do
14111415
ENV.delete("MAKEFLAGS")
14121416

1413-
install_gemfile(<<~G)
1417+
install_gemfile(<<~G, raise_on_error: false)
14141418
source "https://gem.repo4"
14151419
gem "mypsych"
14161420
G
14171421

1418-
gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
1419-
14201422
expect(gem_make_out).to include("make -j3")
14211423
end
14221424
end

0 commit comments

Comments
 (0)