Skip to content

Commit cae04de

Browse files
authored
Merge pull request #9630 from ruby/claude/confident-dhawan-40f0a0
Skip the make jobserver on Windows
2 parents f80a222 + b12db47 commit cae04de

2 files changed

Lines changed: 58 additions & 15 deletions

File tree

bundler/lib/bundler/installer/parallel_installer.rb

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,40 @@ def install_with_worker
119119
end
120120

121121
def with_jobserver
122-
r, w = IO.pipe
123-
r.close_on_exec = false
124-
w.close_on_exec = false
125-
w.write("*" * @size)
126-
127-
old_makeflags = ENV["MAKEFLAGS"]
128-
ENV["MAKEFLAGS"] = [old_makeflags, "--jobserver-auth=#{r.fileno},#{w.fileno}"].compact.join(" ")
129-
130-
yield
131-
ensure
132-
# Restore MAKEFLAGS before closing the pipe so a close failure can't
133-
# leave the process with descriptors that point at a closed pipe.
134-
old_makeflags ? ENV["MAKEFLAGS"] = old_makeflags : ENV.delete("MAKEFLAGS")
122+
# The jobserver hands tokens to child `make` processes through MAKEFLAGS
123+
# using the GNU make `--jobserver-auth` protocol. nmake, the default make
124+
# on mswin, instead reads MAKEFLAGS as bare option letters and aborts
125+
# every native extension build with `fatal error U1065: invalid option
126+
# '-'`. Skip the jobserver when nmake is in use. Other Windows toolchains
127+
# such as mingw use GNU make and keep working through the inherited pipe.
128+
return yield if nmake?
129+
130+
begin
131+
r, w = IO.pipe
132+
r.close_on_exec = false
133+
w.close_on_exec = false
134+
w.write("*" * @size)
135+
136+
old_makeflags = ENV["MAKEFLAGS"]
137+
ENV["MAKEFLAGS"] = [old_makeflags, "--jobserver-auth=#{r.fileno},#{w.fileno}"].compact.join(" ")
138+
139+
yield
140+
ensure
141+
# Restore MAKEFLAGS before closing the pipe so a close failure can't
142+
# leave the process with descriptors that point at a closed pipe.
143+
old_makeflags ? ENV["MAKEFLAGS"] = old_makeflags : ENV.delete("MAKEFLAGS")
144+
145+
r&.close
146+
w&.close
147+
end
148+
end
135149

136-
r&.close
137-
w&.close
150+
# Mirror how RubyGems' extension builder picks the make program so the
151+
# jobserver is only set up when a GNU-compatible make will consume it.
152+
def nmake?
153+
make = ENV["MAKE"] || ENV["make"]
154+
make ||= "nmake" if RUBY_PLATFORM.include?("mswin")
155+
/\bnmake/i.match?(make.to_s)
138156
end
139157

140158
def install_serially

spec/bundler/installer/parallel_installer_spec.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,4 +219,29 @@ def redefine_build_jobs
219219
Bundler::RubyGemsGemInstaller.define_method(:build_jobs, old_method)
220220
end
221221
end
222+
223+
describe "make jobserver with nmake" do
224+
# nmake reads MAKEFLAGS from the environment and treats its contents as
225+
# bare option letters, so a GNU make `--jobserver-auth` aborts the build
226+
# with `fatal error U1065: invalid option '-'`. The jobserver must be
227+
# skipped when nmake is the make program.
228+
it "leaves MAKEFLAGS untouched" do
229+
parallel_installer = Bundler::ParallelInstaller.new(nil, [], 5, false, false)
230+
231+
makeflags_before = ENV["MAKEFLAGS"]
232+
makeflags_during = :not_yielded
233+
234+
old_make = ENV["MAKE"]
235+
ENV["MAKE"] = "nmake"
236+
begin
237+
parallel_installer.send(:with_jobserver) do
238+
makeflags_during = ENV["MAKEFLAGS"]
239+
end
240+
ensure
241+
ENV["MAKE"] = old_make
242+
end
243+
244+
expect(makeflags_during).to eq(makeflags_before)
245+
end
246+
end
222247
end

0 commit comments

Comments
 (0)