diff options
author | Charles Oliver Nutter <headius@headius.com> | 2021-09-29 13:21:31 -0500 |
---|---|---|
committer | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2021-12-09 19:28:54 +0900 |
commit | 01febcab3e6258051e2fc083b906d9ac2bdc3927 (patch) | |
tree | 43e34e3794bdbf374d530346578190fffca7ea90 /lib | |
parent | 34ebd1392318b45f120f4d598f710bfcf5069c51 (diff) |
[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
Diffstat (limited to 'lib')
-rw-r--r-- | lib/open3.gemspec | 6 | ||||
-rw-r--r-- | lib/open3.rb | 9 | ||||
-rw-r--r-- | lib/open3/jruby_windows.rb | 118 | ||||
-rw-r--r-- | lib/open3/version.rb | 3 |
4 files changed, 130 insertions, 6 deletions
diff --git a/lib/open3.gemspec b/lib/open3.gemspec index ad9485adc7..89c2cbe564 100644 --- a/lib/open3.gemspec +++ b/lib/open3.gemspec @@ -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| diff --git a/lib/open3.rb b/lib/open3.rb index c574696bb1..2f035e3bcd 100644 --- a/lib/open3.rb +++ b/lib/open3.rb @@ -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 diff --git a/lib/open3/jruby_windows.rb b/lib/open3/jruby_windows.rb new file mode 100644 index 0000000000..24b9a1ba7e --- /dev/null +++ b/lib/open3/jruby_windows.rb @@ -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 diff --git a/lib/open3/version.rb b/lib/open3/version.rb new file mode 100644 index 0000000000..5a8e84b4ae --- /dev/null +++ b/lib/open3/version.rb @@ -0,0 +1,3 @@ +module Open3 + VERSION = "0.1.1" +end |