childprocess 0.9.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +5 -5
  2. data/.document +6 -6
  3. data/.gitignore +28 -28
  4. data/.rspec +1 -1
  5. data/.travis.yml +37 -44
  6. data/CHANGELOG.md +83 -49
  7. data/Gemfile +9 -15
  8. data/LICENSE +20 -20
  9. data/README.md +230 -196
  10. data/Rakefile +61 -61
  11. data/appveyor.yml +36 -60
  12. data/childprocess.gemspec +26 -30
  13. data/lib/childprocess/abstract_io.rb +36 -36
  14. data/lib/childprocess/abstract_process.rb +192 -192
  15. data/lib/childprocess/errors.rb +37 -26
  16. data/lib/childprocess/jruby/io.rb +16 -16
  17. data/lib/childprocess/jruby/process.rb +184 -159
  18. data/lib/childprocess/jruby/pump.rb +53 -53
  19. data/lib/childprocess/jruby.rb +56 -56
  20. data/lib/childprocess/tools/generator.rb +145 -145
  21. data/lib/childprocess/unix/fork_exec_process.rb +78 -70
  22. data/lib/childprocess/unix/io.rb +21 -21
  23. data/lib/childprocess/unix/lib.rb +186 -186
  24. data/lib/childprocess/unix/platform/arm64-macosx.rb +11 -0
  25. data/lib/childprocess/unix/platform/i386-linux.rb +12 -12
  26. data/lib/childprocess/unix/platform/i386-solaris.rb +11 -11
  27. data/lib/childprocess/unix/platform/x86_64-linux.rb +12 -12
  28. data/lib/childprocess/unix/platform/x86_64-macosx.rb +11 -11
  29. data/lib/childprocess/unix/posix_spawn_process.rb +134 -134
  30. data/lib/childprocess/unix/process.rb +90 -89
  31. data/lib/childprocess/unix.rb +9 -9
  32. data/lib/childprocess/version.rb +3 -3
  33. data/lib/childprocess/windows/handle.rb +91 -91
  34. data/lib/childprocess/windows/io.rb +25 -25
  35. data/lib/childprocess/windows/lib.rb +416 -416
  36. data/lib/childprocess/windows/process.rb +131 -130
  37. data/lib/childprocess/windows/process_builder.rb +178 -175
  38. data/lib/childprocess/windows/structs.rb +148 -148
  39. data/lib/childprocess/windows.rb +38 -33
  40. data/lib/childprocess.rb +210 -205
  41. data/spec/abstract_io_spec.rb +12 -12
  42. data/spec/childprocess_spec.rb +447 -422
  43. data/spec/get_env.ps1 +13 -0
  44. data/spec/io_spec.rb +228 -228
  45. data/spec/jruby_spec.rb +24 -24
  46. data/spec/pid_behavior.rb +12 -12
  47. data/spec/platform_detection_spec.rb +86 -86
  48. data/spec/spec_helper.rb +270 -261
  49. data/spec/unix_spec.rb +57 -57
  50. data/spec/windows_spec.rb +23 -23
  51. metadata +13 -43
@@ -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
- # Only supported in JRuby on a Unix operating system, thanks to limitations
51
- # in Java's classes
52
- #
53
- # @return [Integer] the pid of the process after it has started
54
- # @raise [NotImplementedError] when trying to access pid on non-Unix platform
55
- #
56
- def pid
57
- if @process.getClass.getName != "java.lang.UNIXProcess"
58
- raise NotImplementedError, "pid is only supported by JRuby child processes on Unix"
59
- end
60
-
61
- # About the best way we can do this is with a nasty reflection-based impl
62
- # Thanks to Martijn Courteaux
63
- # http://stackoverflow.com/questions/2950338/how-can-i-kill-a-linux-process-in-java-with-sigkill-process-destroy-does-sigter/2951193#2951193
64
- field = @process.getClass.getDeclaredField("pid")
65
- field.accessible = true
66
- field.get(@process)
67
- end
68
-
69
- private
70
-
71
- def launch_process(&blk)
72
- pb = java.lang.ProcessBuilder.new(@args)
73
-
74
- pb.directory java.io.File.new(@cwd || Dir.pwd)
75
- set_env pb.environment
76
-
77
- begin
78
- @process = pb.start
79
- rescue java.io.IOException => ex
80
- raise LaunchError, ex.message
81
- end
82
-
83
- setup_io
84
- end
85
-
86
- def setup_io
87
- if @io
88
- redirect(@process.getErrorStream, @io.stderr)
89
- redirect(@process.getInputStream, @io.stdout)
90
- else
91
- @process.getErrorStream.close
92
- @process.getInputStream.close
93
- end
94
-
95
- if duplex?
96
- io._stdin = create_stdin
97
- else
98
- @process.getOutputStream.close
99
- end
100
- end
101
-
102
- def redirect(input, output)
103
- if output.nil?
104
- input.close
105
- return
106
- end
107
-
108
- @pumps << Pump.new(input, output.to_outputstream).run
109
- end
110
-
111
- def stop_pumps
112
- @pumps.each { |pump| pump.stop }
113
- end
114
-
115
- def set_env(env)
116
- merged = ENV.to_hash
117
-
118
- @environment.each { |k, v| merged[k.to_s] = v }
119
-
120
- merged.each do |k, v|
121
- if v
122
- env.put(k, v.to_s)
123
- elsif env.has_key? k
124
- env.remove(k)
125
- end
126
- end
127
-
128
- removed_keys = env.key_set.to_a - merged.keys
129
- removed_keys.each { |k| env.remove(k) }
130
- end
131
-
132
- def create_stdin
133
- output_stream = @process.getOutputStream
134
-
135
- stdin = output_stream.to_io
136
- stdin.sync = true
137
- stdin.instance_variable_set(:@childprocess_java_stream, output_stream)
138
-
139
- class << stdin
140
- # The stream provided is a BufferedeOutputStream, so we
141
- # have to flush it to make the bytes flow to the process
142
- def __childprocess_flush__
143
- @childprocess_java_stream.flush
144
- end
145
-
146
- [:flush, :print, :printf, :putc, :puts, :write, :write_nonblock].each do |m|
147
- define_method(m) do |*args|
148
- super(*args)
149
- self.__childprocess_flush__
150
- end
151
- end
152
- end
153
-
154
- stdin
155
- end
156
-
157
- end # Process
158
- end # JRuby
159
- end # ChildProcess
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
@@ -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"