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.
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 +42 -36
  6. data/CHANGELOG.md +67 -44
  7. data/Gemfile +18 -15
  8. data/LICENSE +20 -20
  9. data/README.md +216 -192
  10. data/Rakefile +61 -61
  11. data/appveyor.yml +42 -43
  12. data/childprocess.gemspec +32 -30
  13. data/ext/mkrf_conf.rb +24 -0
  14. data/lib/childprocess/abstract_io.rb +36 -36
  15. data/lib/childprocess/abstract_process.rb +192 -192
  16. data/lib/childprocess/errors.rb +37 -26
  17. data/lib/childprocess/jruby/io.rb +16 -16
  18. data/lib/childprocess/jruby/process.rb +184 -159
  19. data/lib/childprocess/jruby/pump.rb +53 -53
  20. data/lib/childprocess/jruby.rb +56 -56
  21. data/lib/childprocess/tools/generator.rb +145 -145
  22. data/lib/childprocess/unix/fork_exec_process.rb +78 -70
  23. data/lib/childprocess/unix/io.rb +21 -21
  24. data/lib/childprocess/unix/lib.rb +186 -186
  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 +130 -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 +33 -33
  40. data/lib/childprocess.rb +210 -205
  41. data/spec/abstract_io_spec.rb +12 -12
  42. data/spec/childprocess_spec.rb +447 -391
  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 +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
- # 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"