childprocess 0.8.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.document +6 -6
- data/.gitignore +28 -28
- data/.rspec +1 -1
- data/.travis.yml +42 -36
- data/CHANGELOG.md +67 -44
- data/Gemfile +18 -15
- data/LICENSE +20 -20
- data/README.md +216 -192
- data/Rakefile +61 -61
- data/appveyor.yml +42 -43
- data/childprocess.gemspec +32 -30
- data/ext/mkrf_conf.rb +24 -0
- data/lib/childprocess/abstract_io.rb +36 -36
- data/lib/childprocess/abstract_process.rb +192 -192
- data/lib/childprocess/errors.rb +37 -26
- data/lib/childprocess/jruby/io.rb +16 -16
- data/lib/childprocess/jruby/process.rb +184 -159
- data/lib/childprocess/jruby/pump.rb +53 -53
- data/lib/childprocess/jruby.rb +56 -56
- data/lib/childprocess/tools/generator.rb +145 -145
- data/lib/childprocess/unix/fork_exec_process.rb +78 -70
- data/lib/childprocess/unix/io.rb +21 -21
- data/lib/childprocess/unix/lib.rb +186 -186
- data/lib/childprocess/unix/platform/i386-linux.rb +12 -12
- data/lib/childprocess/unix/platform/i386-solaris.rb +11 -11
- data/lib/childprocess/unix/platform/x86_64-linux.rb +12 -12
- data/lib/childprocess/unix/platform/x86_64-macosx.rb +11 -11
- data/lib/childprocess/unix/posix_spawn_process.rb +134 -134
- data/lib/childprocess/unix/process.rb +90 -89
- data/lib/childprocess/unix.rb +9 -9
- data/lib/childprocess/version.rb +3 -3
- data/lib/childprocess/windows/handle.rb +91 -91
- data/lib/childprocess/windows/io.rb +25 -25
- data/lib/childprocess/windows/lib.rb +416 -416
- data/lib/childprocess/windows/process.rb +130 -130
- data/lib/childprocess/windows/process_builder.rb +178 -175
- data/lib/childprocess/windows/structs.rb +148 -148
- data/lib/childprocess/windows.rb +33 -33
- data/lib/childprocess.rb +210 -205
- data/spec/abstract_io_spec.rb +12 -12
- data/spec/childprocess_spec.rb +447 -391
- data/spec/get_env.ps1 +13 -0
- data/spec/io_spec.rb +228 -228
- data/spec/jruby_spec.rb +24 -24
- data/spec/pid_behavior.rb +12 -12
- data/spec/platform_detection_spec.rb +86 -86
- data/spec/spec_helper.rb +270 -261
- data/spec/unix_spec.rb +57 -57
- data/spec/windows_spec.rb +23 -23
- metadata +18 -33
@@ -1,159 +1,184 @@
|
|
1
|
-
require "java"
|
2
|
-
|
3
|
-
module ChildProcess
|
4
|
-
module JRuby
|
5
|
-
class Process < AbstractProcess
|
6
|
-
def initialize(args)
|
7
|
-
super(args)
|
8
|
-
|
9
|
-
@pumps = []
|
10
|
-
end
|
11
|
-
|
12
|
-
def io
|
13
|
-
@io ||= JRuby::IO.new
|
14
|
-
end
|
15
|
-
|
16
|
-
def exited?
|
17
|
-
return true if @exit_code
|
18
|
-
|
19
|
-
assert_started
|
20
|
-
@exit_code = @process.exitValue
|
21
|
-
stop_pumps
|
22
|
-
|
23
|
-
true
|
24
|
-
rescue java.lang.IllegalThreadStateException => ex
|
25
|
-
log(ex.class => ex.message)
|
26
|
-
false
|
27
|
-
ensure
|
28
|
-
log(:exit_code => @exit_code)
|
29
|
-
end
|
30
|
-
|
31
|
-
def stop(timeout = nil)
|
32
|
-
assert_started
|
33
|
-
|
34
|
-
@process.destroy
|
35
|
-
wait # no way to actually use the timeout here..
|
36
|
-
end
|
37
|
-
|
38
|
-
def wait
|
39
|
-
if exited?
|
40
|
-
exit_code
|
41
|
-
else
|
42
|
-
@process.waitFor
|
43
|
-
|
44
|
-
stop_pumps
|
45
|
-
@exit_code = @process.exitValue
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
#
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
def
|
112
|
-
@
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
1
|
+
require "java"
|
2
|
+
|
3
|
+
module ChildProcess
|
4
|
+
module JRuby
|
5
|
+
class Process < AbstractProcess
|
6
|
+
def initialize(args)
|
7
|
+
super(args)
|
8
|
+
|
9
|
+
@pumps = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def io
|
13
|
+
@io ||= JRuby::IO.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def exited?
|
17
|
+
return true if @exit_code
|
18
|
+
|
19
|
+
assert_started
|
20
|
+
@exit_code = @process.exitValue
|
21
|
+
stop_pumps
|
22
|
+
|
23
|
+
true
|
24
|
+
rescue java.lang.IllegalThreadStateException => ex
|
25
|
+
log(ex.class => ex.message)
|
26
|
+
false
|
27
|
+
ensure
|
28
|
+
log(:exit_code => @exit_code)
|
29
|
+
end
|
30
|
+
|
31
|
+
def stop(timeout = nil)
|
32
|
+
assert_started
|
33
|
+
|
34
|
+
@process.destroy
|
35
|
+
wait # no way to actually use the timeout here..
|
36
|
+
end
|
37
|
+
|
38
|
+
def wait
|
39
|
+
if exited?
|
40
|
+
exit_code
|
41
|
+
else
|
42
|
+
@process.waitFor
|
43
|
+
|
44
|
+
stop_pumps
|
45
|
+
@exit_code = @process.exitValue
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Implementation of ChildProcess::JRuby::Process#pid depends heavily on
|
50
|
+
# what Java SDK is being used; here, we look it up once at load, then
|
51
|
+
# define the method once to avoid runtime overhead.
|
52
|
+
normalised_java_version_major = java.lang.System.get_property("java.version")
|
53
|
+
.slice(/^(1\.)?([0-9]+)/, 2)
|
54
|
+
.to_i
|
55
|
+
if normalised_java_version_major >= 9
|
56
|
+
|
57
|
+
# On modern Javas, we can simply delegate through to `Process#pid`,
|
58
|
+
# which was introduced in Java 9.
|
59
|
+
#
|
60
|
+
# @return [Integer] the pid of the process after it has started
|
61
|
+
# @raise [NotImplementedError] when trying to access pid on platform for
|
62
|
+
# which it is unsupported in Java
|
63
|
+
def pid
|
64
|
+
@process.pid
|
65
|
+
rescue java.lang.UnsupportedOperationException => e
|
66
|
+
raise NotImplementedError, "pid is not supported on this platform: #{e.message}"
|
67
|
+
end
|
68
|
+
|
69
|
+
else
|
70
|
+
|
71
|
+
# On Legacy Javas, fall back to reflection.
|
72
|
+
#
|
73
|
+
# Only supported in JRuby on a Unix operating system, thanks to limitations
|
74
|
+
# in Java's classes
|
75
|
+
#
|
76
|
+
# @return [Integer] the pid of the process after it has started
|
77
|
+
# @raise [NotImplementedError] when trying to access pid on non-Unix platform
|
78
|
+
#
|
79
|
+
def pid
|
80
|
+
if @process.getClass.getName != "java.lang.UNIXProcess"
|
81
|
+
raise NotImplementedError, "pid is only supported by JRuby child processes on Unix"
|
82
|
+
end
|
83
|
+
|
84
|
+
# About the best way we can do this is with a nasty reflection-based impl
|
85
|
+
# Thanks to Martijn Courteaux
|
86
|
+
# http://stackoverflow.com/questions/2950338/how-can-i-kill-a-linux-process-in-java-with-sigkill-process-destroy-does-sigter/2951193#2951193
|
87
|
+
field = @process.getClass.getDeclaredField("pid")
|
88
|
+
field.accessible = true
|
89
|
+
field.get(@process)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def launch_process(&blk)
|
97
|
+
pb = java.lang.ProcessBuilder.new(@args)
|
98
|
+
|
99
|
+
pb.directory java.io.File.new(@cwd || Dir.pwd)
|
100
|
+
set_env pb.environment
|
101
|
+
|
102
|
+
begin
|
103
|
+
@process = pb.start
|
104
|
+
rescue java.io.IOException => ex
|
105
|
+
raise LaunchError, ex.message
|
106
|
+
end
|
107
|
+
|
108
|
+
setup_io
|
109
|
+
end
|
110
|
+
|
111
|
+
def setup_io
|
112
|
+
if @io
|
113
|
+
redirect(@process.getErrorStream, @io.stderr)
|
114
|
+
redirect(@process.getInputStream, @io.stdout)
|
115
|
+
else
|
116
|
+
@process.getErrorStream.close
|
117
|
+
@process.getInputStream.close
|
118
|
+
end
|
119
|
+
|
120
|
+
if duplex?
|
121
|
+
io._stdin = create_stdin
|
122
|
+
else
|
123
|
+
@process.getOutputStream.close
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def redirect(input, output)
|
128
|
+
if output.nil?
|
129
|
+
input.close
|
130
|
+
return
|
131
|
+
end
|
132
|
+
|
133
|
+
@pumps << Pump.new(input, output.to_outputstream).run
|
134
|
+
end
|
135
|
+
|
136
|
+
def stop_pumps
|
137
|
+
@pumps.each { |pump| pump.stop }
|
138
|
+
end
|
139
|
+
|
140
|
+
def set_env(env)
|
141
|
+
merged = ENV.to_hash
|
142
|
+
|
143
|
+
@environment.each { |k, v| merged[k.to_s] = v }
|
144
|
+
|
145
|
+
merged.each do |k, v|
|
146
|
+
if v
|
147
|
+
env.put(k, v.to_s)
|
148
|
+
elsif env.has_key? k
|
149
|
+
env.remove(k)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
removed_keys = env.key_set.to_a - merged.keys
|
154
|
+
removed_keys.each { |k| env.remove(k) }
|
155
|
+
end
|
156
|
+
|
157
|
+
def create_stdin
|
158
|
+
output_stream = @process.getOutputStream
|
159
|
+
|
160
|
+
stdin = output_stream.to_io
|
161
|
+
stdin.sync = true
|
162
|
+
stdin.instance_variable_set(:@childprocess_java_stream, output_stream)
|
163
|
+
|
164
|
+
class << stdin
|
165
|
+
# The stream provided is a BufferedeOutputStream, so we
|
166
|
+
# have to flush it to make the bytes flow to the process
|
167
|
+
def __childprocess_flush__
|
168
|
+
@childprocess_java_stream.flush
|
169
|
+
end
|
170
|
+
|
171
|
+
[:flush, :print, :printf, :putc, :puts, :write, :write_nonblock].each do |m|
|
172
|
+
define_method(m) do |*args|
|
173
|
+
super(*args)
|
174
|
+
self.__childprocess_flush__
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
stdin
|
180
|
+
end
|
181
|
+
|
182
|
+
end # Process
|
183
|
+
end # JRuby
|
184
|
+
end # ChildProcess
|
@@ -1,53 +1,53 @@
|
|
1
|
-
module ChildProcess
|
2
|
-
module JRuby
|
3
|
-
class Pump
|
4
|
-
BUFFER_SIZE = 2048
|
5
|
-
|
6
|
-
def initialize(input, output)
|
7
|
-
@input = input
|
8
|
-
@output = output
|
9
|
-
@stop = false
|
10
|
-
end
|
11
|
-
|
12
|
-
def stop
|
13
|
-
@stop = true
|
14
|
-
@thread && @thread.join
|
15
|
-
end
|
16
|
-
|
17
|
-
def run
|
18
|
-
@thread = Thread.new { pump }
|
19
|
-
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def pump
|
26
|
-
buffer = Java.byte[BUFFER_SIZE].new
|
27
|
-
|
28
|
-
until @stop && (@input.available == 0)
|
29
|
-
read, avail = 0, 0
|
30
|
-
|
31
|
-
while read != -1
|
32
|
-
avail = [@input.available, 1].max
|
33
|
-
avail = BUFFER_SIZE if avail > BUFFER_SIZE
|
34
|
-
read = @input.read(buffer, 0, avail)
|
35
|
-
|
36
|
-
if read > 0
|
37
|
-
@output.write(buffer, 0, read)
|
38
|
-
@output.flush
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
sleep 0.1
|
43
|
-
end
|
44
|
-
|
45
|
-
@output.flush
|
46
|
-
rescue java.io.IOException => ex
|
47
|
-
ChildProcess.logger.debug ex.message
|
48
|
-
ChildProcess.logger.debug ex.backtrace
|
49
|
-
end
|
50
|
-
|
51
|
-
end # Pump
|
52
|
-
end # JRuby
|
53
|
-
end # ChildProcess
|
1
|
+
module ChildProcess
|
2
|
+
module JRuby
|
3
|
+
class Pump
|
4
|
+
BUFFER_SIZE = 2048
|
5
|
+
|
6
|
+
def initialize(input, output)
|
7
|
+
@input = input
|
8
|
+
@output = output
|
9
|
+
@stop = false
|
10
|
+
end
|
11
|
+
|
12
|
+
def stop
|
13
|
+
@stop = true
|
14
|
+
@thread && @thread.join
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
@thread = Thread.new { pump }
|
19
|
+
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def pump
|
26
|
+
buffer = Java.byte[BUFFER_SIZE].new
|
27
|
+
|
28
|
+
until @stop && (@input.available == 0)
|
29
|
+
read, avail = 0, 0
|
30
|
+
|
31
|
+
while read != -1
|
32
|
+
avail = [@input.available, 1].max
|
33
|
+
avail = BUFFER_SIZE if avail > BUFFER_SIZE
|
34
|
+
read = @input.read(buffer, 0, avail)
|
35
|
+
|
36
|
+
if read > 0
|
37
|
+
@output.write(buffer, 0, read)
|
38
|
+
@output.flush
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
sleep 0.1
|
43
|
+
end
|
44
|
+
|
45
|
+
@output.flush
|
46
|
+
rescue java.io.IOException => ex
|
47
|
+
ChildProcess.logger.debug ex.message
|
48
|
+
ChildProcess.logger.debug ex.backtrace
|
49
|
+
end
|
50
|
+
|
51
|
+
end # Pump
|
52
|
+
end # JRuby
|
53
|
+
end # ChildProcess
|
data/lib/childprocess/jruby.rb
CHANGED
@@ -1,56 +1,56 @@
|
|
1
|
-
require 'java'
|
2
|
-
require 'jruby'
|
3
|
-
|
4
|
-
class Java::SunNioCh::FileChannelImpl
|
5
|
-
field_reader :fd
|
6
|
-
end
|
7
|
-
|
8
|
-
class Java::JavaIo::FileDescriptor
|
9
|
-
if ChildProcess.os == :windows
|
10
|
-
field_reader :handle
|
11
|
-
end
|
12
|
-
|
13
|
-
field_reader :fd
|
14
|
-
end
|
15
|
-
|
16
|
-
module ChildProcess
|
17
|
-
module JRuby
|
18
|
-
def self.posix_fileno_for(obj)
|
19
|
-
channel = ::JRuby.reference(obj).channel
|
20
|
-
begin
|
21
|
-
channel.getFDVal
|
22
|
-
rescue NoMethodError
|
23
|
-
fileno = channel.fd
|
24
|
-
if fileno.kind_of?(Java::JavaIo::FileDescriptor)
|
25
|
-
fileno = fileno.fd
|
26
|
-
end
|
27
|
-
|
28
|
-
fileno == -1 ? obj.fileno : fileno
|
29
|
-
end
|
30
|
-
rescue
|
31
|
-
# fall back
|
32
|
-
obj.fileno
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.windows_handle_for(obj)
|
36
|
-
channel = ::JRuby.reference(obj).channel
|
37
|
-
fileno = obj.fileno
|
38
|
-
|
39
|
-
begin
|
40
|
-
fileno = channel.getFDVal
|
41
|
-
rescue NoMethodError
|
42
|
-
fileno = channel.fd if channel.respond_to?(:fd)
|
43
|
-
end
|
44
|
-
|
45
|
-
if fileno.kind_of? Java::JavaIo::FileDescriptor
|
46
|
-
fileno.handle
|
47
|
-
else
|
48
|
-
Windows::Lib.handle_for fileno
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
require "childprocess/jruby/pump"
|
55
|
-
require "childprocess/jruby/io"
|
56
|
-
require "childprocess/jruby/process"
|
1
|
+
require 'java'
|
2
|
+
require 'jruby'
|
3
|
+
|
4
|
+
class Java::SunNioCh::FileChannelImpl
|
5
|
+
field_reader :fd
|
6
|
+
end
|
7
|
+
|
8
|
+
class Java::JavaIo::FileDescriptor
|
9
|
+
if ChildProcess.os == :windows
|
10
|
+
field_reader :handle
|
11
|
+
end
|
12
|
+
|
13
|
+
field_reader :fd
|
14
|
+
end
|
15
|
+
|
16
|
+
module ChildProcess
|
17
|
+
module JRuby
|
18
|
+
def self.posix_fileno_for(obj)
|
19
|
+
channel = ::JRuby.reference(obj).channel
|
20
|
+
begin
|
21
|
+
channel.getFDVal
|
22
|
+
rescue NoMethodError
|
23
|
+
fileno = channel.fd
|
24
|
+
if fileno.kind_of?(Java::JavaIo::FileDescriptor)
|
25
|
+
fileno = fileno.fd
|
26
|
+
end
|
27
|
+
|
28
|
+
fileno == -1 ? obj.fileno : fileno
|
29
|
+
end
|
30
|
+
rescue
|
31
|
+
# fall back
|
32
|
+
obj.fileno
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.windows_handle_for(obj)
|
36
|
+
channel = ::JRuby.reference(obj).channel
|
37
|
+
fileno = obj.fileno
|
38
|
+
|
39
|
+
begin
|
40
|
+
fileno = channel.getFDVal
|
41
|
+
rescue NoMethodError
|
42
|
+
fileno = channel.fd if channel.respond_to?(:fd)
|
43
|
+
end
|
44
|
+
|
45
|
+
if fileno.kind_of? Java::JavaIo::FileDescriptor
|
46
|
+
fileno.handle
|
47
|
+
else
|
48
|
+
Windows::Lib.handle_for fileno
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
require "childprocess/jruby/pump"
|
55
|
+
require "childprocess/jruby/io"
|
56
|
+
require "childprocess/jruby/process"
|