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
21 changes: 21 additions & 0 deletions .github/workflows/test-jruby.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: test

on: [push, pull_request]

jobs:
build:
name: build (${{ matrix.ruby }} / ${{ matrix.os }})
strategy:
matrix:
ruby: [ 2.7, 2.6, head, jruby-9.3 ]
os: [ ubuntu-latest, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true # 'bundle install' and cache
- name: Run test
run: bundle exec rake test
6 changes: 5 additions & 1 deletion lib/open3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
# - Open3.pipeline : run a pipeline and wait for its completion
#

require 'open3/version'

module Open3
VERSION = "0.1.1"

# Open stdin, stdout, and stderr streams and start external executable.
# In addition, a thread to wait for the started process is created.
Expand Down Expand Up @@ -758,3 +759,6 @@ class << self
end

end

# JRuby uses different popen logic on Windows, require it here to reuse wrapper methods above.
require 'open3/jruby_windows' if RUBY_ENGINE == 'jruby' && JRuby::Util::ON_WINDOWS
127 changes: 127 additions & 0 deletions lib/open3/jruby_windows.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#
# 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
3 changes: 3 additions & 0 deletions lib/open3/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Open3
VERSION = "0.1.1"
end
6 changes: 2 additions & 4 deletions open3.gemspec
Original file line number Diff line number Diff line change
@@ -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|
Expand Down
13 changes: 10 additions & 3 deletions test/test_open3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

require 'test/unit'
require 'open3'
require_relative 'lib/jit_support'

if RUBY_ENGINE == 'ruby'
require_relative 'lib/jit_support'
end

class TestOpen3 < Test::Unit::TestCase
RUBY = EnvUtil.rubybin
Expand Down Expand Up @@ -92,7 +95,7 @@ def test_numeric_file_descriptor2
end

def test_numeric_file_descriptor3
skip "passing FDs bigger than 2 is not supported on Windows" if /mswin|mingw/ =~ RUBY_PLATFORM
skip "passing FDs bigger than 2 is not supported on Windows" if /mswin|mingw/ =~ RbConfig::CONFIG['host_os']
with_pipe {|r, w|
Open3.popen3(RUBY, '-e', 'IO.open(3).puts "foo"', 3 => w) {|i,o,e,t|
assert_equal("foo\n", r.gets, "[GH-808] [ruby-core:67347] [Bug #10699]")
Expand Down Expand Up @@ -127,7 +130,11 @@ def test_popen2
i.close
STDERR.reopen(old)
assert_equal("zo", o.read)
assert_equal("ze", JITSupport.remove_mjit_logs(r.read))
if defined?(JITSupport)
assert_equal("ze", JITSupport.remove_mjit_logs(r.read))
else
assert_equal("ze", r.read)
end
}
}
}
Expand Down