Skip to content
Merged
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
48 changes: 33 additions & 15 deletions bundler/lib/bundler/installer/parallel_installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,22 +119,40 @@ def install_with_worker
end

def with_jobserver
r, w = IO.pipe
r.close_on_exec = false
w.close_on_exec = false
w.write("*" * @size)

old_makeflags = ENV["MAKEFLAGS"]
ENV["MAKEFLAGS"] = [old_makeflags, "--jobserver-auth=#{r.fileno},#{w.fileno}"].compact.join(" ")

yield
ensure
# Restore MAKEFLAGS before closing the pipe so a close failure can't
# leave the process with descriptors that point at a closed pipe.
old_makeflags ? ENV["MAKEFLAGS"] = old_makeflags : ENV.delete("MAKEFLAGS")
# The jobserver hands tokens to child `make` processes through MAKEFLAGS
# using the GNU make `--jobserver-auth` protocol. nmake, the default make
# on mswin, instead reads MAKEFLAGS as bare option letters and aborts
# every native extension build with `fatal error U1065: invalid option
# '-'`. Skip the jobserver when nmake is in use. Other Windows toolchains
# such as mingw use GNU make and keep working through the inherited pipe.
return yield if nmake?

begin
r, w = IO.pipe
r.close_on_exec = false
w.close_on_exec = false
w.write("*" * @size)

old_makeflags = ENV["MAKEFLAGS"]
ENV["MAKEFLAGS"] = [old_makeflags, "--jobserver-auth=#{r.fileno},#{w.fileno}"].compact.join(" ")

yield
ensure
# Restore MAKEFLAGS before closing the pipe so a close failure can't
# leave the process with descriptors that point at a closed pipe.
old_makeflags ? ENV["MAKEFLAGS"] = old_makeflags : ENV.delete("MAKEFLAGS")

r&.close
w&.close
end
end

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

def install_serially
Expand Down
25 changes: 25 additions & 0 deletions spec/bundler/installer/parallel_installer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,29 @@ def redefine_build_jobs
Bundler::RubyGemsGemInstaller.define_method(:build_jobs, old_method)
end
end

describe "make jobserver with nmake" do
# nmake reads MAKEFLAGS from the environment and treats its contents as
# bare option letters, so a GNU make `--jobserver-auth` aborts the build
# with `fatal error U1065: invalid option '-'`. The jobserver must be
# skipped when nmake is the make program.
it "leaves MAKEFLAGS untouched" do
parallel_installer = Bundler::ParallelInstaller.new(nil, [], 5, false, false)

makeflags_before = ENV["MAKEFLAGS"]
makeflags_during = :not_yielded

old_make = ENV["MAKE"]
ENV["MAKE"] = "nmake"
begin
parallel_installer.send(:with_jobserver) do
makeflags_during = ENV["MAKEFLAGS"]
end
ensure
ENV["MAKE"] = old_make
end

expect(makeflags_during).to eq(makeflags_before)
end
end
end