1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

[ruby/open3] Add JRuby's Windows (JDK non-native) Open3 support

This adds JRuby's logic used on platforms where we do not have
native access to posix_spawn and related posix functions needed
to do fully-native subprocess launching and management. The code
here instead uses the JDK ProcessBuilder logic to simulate most
of the Open3 functionality.

This code does not pass all tests, currently, but provides most of
the key functionality on pure-Java (i.e. no native FFI) platforms.

https://github.com/ruby/open3/commit/689da19c42
This commit is contained in:
Charles Oliver Nutter 2021-09-29 13:21:31 -05:00 committed by Hiroshi SHIBATA
parent 34ebd13923
commit 01febcab3e
No known key found for this signature in database
GPG key ID: F9CF13417264FAC2
4 changed files with 130 additions and 6 deletions

View file

@ -1,10 +1,8 @@
# frozen_string_literal: true
name = File.basename(__FILE__, ".gemspec")
version = ["lib", Array.new(name.count("-"), "..").join("/")].find do |dir|
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
/^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
end rescue nil
version = File.foreach(File.join(__dir__, "lib/open3/version.rb")) do |line|
/^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
end
Gem::Specification.new do |spec|

View file

@ -29,9 +29,14 @@
# - Open3.pipeline : run a pipeline and wait for its completion
#
module Open3
VERSION = "0.1.1"
require 'open3/version'
if RUBY_ENGINE == 'jruby' && JRuby::Util::ON_WINDOWS
require_relative 'open3/jruby_windows'
return
end
module Open3
# Open stdin, stdout, and stderr streams and start external executable.
# In addition, a thread to wait for the started process is created.
# The thread has a pid method and a thread variable :pid which is the pid of

118
lib/open3/jruby_windows.rb Normal file
View file

@ -0,0 +1,118 @@
#
# 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:)
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

3
lib/open3/version.rb Normal file
View file

@ -0,0 +1,3 @@
module Open3
VERSION = "0.1.1"
end