1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/lib/open3/jruby_windows.rb
Charles Oliver Nutter eb06b37c46
[ruby/open3] Update to match JRuby 9.4
This allows the wrapper functions in the main open3 to be defined
while using our ProcessBuilder logic for the internal popen
implementation.

Note this adds logic to reject redirects from a numeric fd to a
live IO object (or not a String or to_path object) since we cannot
support direct IO redirects with ProcesBuilder.

This patch allows tests to complete with the ProcessBuilder impl.
Only three tests fail:

* test_numeric_file_descriptor2 and test_numeric_file_descriptor2
  fail due to redirecting streams to a pipe IO.
* test_pid fails expecting a real PID which we cannot provide via
  ProcessBuilder.

73f986c233
2021-12-09 19:28:54 +09:00

127 lines
2.9 KiB
Ruby

#
# Custom implementation of Open3.popen{3,2,2e} that uses java.lang.ProcessBuilder rather than pipes and spawns.
#
require 'jruby' # need access to runtime for RubyStatus construction
module Open3
java_import java.lang.ProcessBuilder
java_import org.jruby.RubyProcess
java_import org.jruby.util.ShellLauncher
def popen3(*cmd, &block)
if cmd.size > 0 && Hash === cmd[-1]
opts = cmd.pop
else
opts = {}
end
processbuilder_run(cmd, opts, io: IO_3, &block)
end
module_function :popen3
IO_3 = proc do |process|
[process.getOutputStream.to_io, process.getInputStream.to_io, process.getErrorStream.to_io]
end
BUILD_2 = proc do |builder|
builder.redirectError(ProcessBuilder::Redirect::INHERIT)
end
IO_2 = proc do |process|
[process.getOutputStream.to_io, process.getInputStream.to_io]
end
def popen2(*cmd, &block)
if cmd.size > 0 && Hash === cmd[-1]
opts = cmd.pop
else
opts = {}
end
processbuilder_run(cmd, opts, build: BUILD_2, io: IO_2, &block)
end
module_function :popen2
BUILD_2E = proc do |builder|
builder.redirectErrorStream(true)
end
def popen2e(*cmd, &block)
if cmd.size > 0 && Hash === cmd[-1]
opts = cmd.pop
else
opts = {}
end
processbuilder_run(cmd, opts, build: BUILD_2E, io: IO_2, &block)
end
module_function :popen2e
def processbuilder_run(cmd, opts, build: nil, io:)
opts.each do |k, v|
if Integer === k
if IO == v || !(String === v || v.respond_to?(:to_path))
# target is an open IO or a non-pathable object, bail out
raise NotImplementedError.new("redirect to an open IO is not implemented on this platform")
end
end
end
if Hash === cmd[0]
env = cmd.shift;
else
env = {}
end
if cmd.size == 1 && (cmd[0] =~ / / || ShellLauncher.shouldUseShell(cmd[0]))
cmd = [RbConfig::CONFIG['SHELL'], JRuby::Util::ON_WINDOWS ? '/c' : '-c', cmd[0]]
end
builder = ProcessBuilder.new(cmd.to_java(:string))
builder.directory(java.io.File.new(opts[:chdir] || Dir.pwd))
environment = builder.environment
env.each { |k, v| v.nil? ? environment.remove(k) : environment.put(k, v) }
build.call(builder) if build
process = builder.start
pid = org.jruby.util.ShellLauncher.getPidFromProcess(process)
parent_io = io.call(process)
parent_io.each {|i| i.sync = true}
wait_thr = DetachThread.new(pid) { RubyProcess::RubyStatus.newProcessStatus(JRuby.runtime, process.waitFor << 8, pid) }
result = [*parent_io, wait_thr]
if defined? yield
begin
return yield(*result)
ensure
parent_io.each(&:close)
wait_thr.join
end
end
result
end
module_function :processbuilder_run
class << self
private :processbuilder_run
end
class DetachThread < Thread
attr_reader :pid
def initialize(pid)
super
@pid = pid
self[:pid] = pid
end
end
end