childprocess 0.8.0 → 2.0.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 +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
data/childprocess.gemspec CHANGED
@@ -1,30 +1,32 @@
1
- # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "childprocess/version"
4
-
5
- Gem::Specification.new do |s|
6
- s.name = "childprocess"
7
- s.version = ChildProcess::VERSION
8
- s.platform = Gem::Platform::RUBY
9
- s.authors = ["Jari Bakken", "Eric Kessler"]
10
- s.email = ["morrow748@gmail.com"]
11
- s.homepage = "http://github.com/enkessler/childprocess"
12
- s.summary = %q{A simple and reliable solution for controlling external programs running in the background on any Ruby / OS combination.}
13
- s.description = %q{This gem aims at being a simple and reliable solution for controlling external programs running in the background on any Ruby / OS combination.}
14
-
15
- s.rubyforge_project = "childprocess"
16
- s.license = 'MIT'
17
-
18
- s.files = `git ls-files`.split("\n")
19
- s.test_files = `git ls-files -- spec/*`.split("\n")
20
- s.require_paths = ["lib"]
21
-
22
- s.add_runtime_dependency "ffi", "~> 1.0", ">= 1.0.11"
23
-
24
- s.add_development_dependency "rspec", "~> 3.0"
25
- s.add_development_dependency "yard", "~> 0.0"
26
- s.add_development_dependency 'rake', '< 12.0'
27
- s.add_development_dependency 'coveralls', '< 1.0'
28
- end
29
-
30
-
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "childprocess/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "childprocess"
7
+ s.version = ChildProcess::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jari Bakken", "Eric Kessler", "Shane da Silva"]
10
+ s.email = ["morrow748@gmail.com", "shane@dasilva.io"]
11
+ s.homepage = "http://github.com/enkessler/childprocess"
12
+ s.summary = %q{A simple and reliable solution for controlling external programs running in the background on any Ruby / OS combination.}
13
+ s.description = %q{This gem aims at being a simple and reliable solution for controlling external programs running in the background on any Ruby / OS combination.}
14
+
15
+ s.rubyforge_project = "childprocess"
16
+ s.license = 'MIT'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- spec/*`.split("\n")
20
+ s.require_paths = ["lib"]
21
+
22
+ s.required_ruby_version = '>= 2.3.0'
23
+
24
+ s.add_development_dependency "rspec", "~> 3.0"
25
+ s.add_development_dependency "yard", "~> 0.0"
26
+ s.add_development_dependency 'coveralls', '< 1.0'
27
+
28
+ s.add_runtime_dependency 'rake', '< 13.0'
29
+
30
+ # Install FFI gem if we're running on Windows
31
+ s.extensions = 'ext/mkrf_conf.rb'
32
+ end
data/ext/mkrf_conf.rb ADDED
@@ -0,0 +1,24 @@
1
+ # Based on the example from https://en.wikibooks.org/wiki/Ruby_Programming/RubyGems#How_to_install_different_versions_of_gems_depending_on_which_version_of_ruby_the_installee_is_using
2
+ require 'rubygems'
3
+ require 'rubygems/command.rb'
4
+ require 'rubygems/dependency_installer.rb'
5
+
6
+ begin
7
+ Gem::Command.build_args = ARGV
8
+ rescue NoMethodError # rubocop:disable Lint/HandleExceptions
9
+ end
10
+
11
+ inst = Gem::DependencyInstaller.new
12
+
13
+ begin
14
+ if Gem.win_platform?
15
+ inst.install 'ffi', Gem::Requirement.new('~> 1.0', '>= 1.0.11')
16
+ end
17
+ rescue # rubocop:disable Lint/RescueWithoutErrorClass
18
+ exit(1)
19
+ end
20
+
21
+ # create dummy rakefile to indicate success
22
+ File.open(File.join(File.dirname(__FILE__), 'Rakefile'), 'w') do |f|
23
+ f.write("task :default\n")
24
+ end
@@ -1,36 +1,36 @@
1
- module ChildProcess
2
- class AbstractIO
3
- attr_reader :stderr, :stdout, :stdin
4
-
5
- def inherit!
6
- @stdout = STDOUT
7
- @stderr = STDERR
8
- end
9
-
10
- def stderr=(io)
11
- check_type io
12
- @stderr = io
13
- end
14
-
15
- def stdout=(io)
16
- check_type io
17
- @stdout = io
18
- end
19
-
20
- #
21
- # @api private
22
- #
23
-
24
- def _stdin=(io)
25
- check_type io
26
- @stdin = io
27
- end
28
-
29
- private
30
-
31
- def check_type(io)
32
- raise SubclassResponsibility, "check_type"
33
- end
34
-
35
- end
36
- end
1
+ module ChildProcess
2
+ class AbstractIO
3
+ attr_reader :stderr, :stdout, :stdin
4
+
5
+ def inherit!
6
+ @stdout = STDOUT
7
+ @stderr = STDERR
8
+ end
9
+
10
+ def stderr=(io)
11
+ check_type io
12
+ @stderr = io
13
+ end
14
+
15
+ def stdout=(io)
16
+ check_type io
17
+ @stdout = io
18
+ end
19
+
20
+ #
21
+ # @api private
22
+ #
23
+
24
+ def _stdin=(io)
25
+ check_type io
26
+ @stdin = io
27
+ end
28
+
29
+ private
30
+
31
+ def check_type(io)
32
+ raise SubclassResponsibility, "check_type"
33
+ end
34
+
35
+ end
36
+ end
@@ -1,192 +1,192 @@
1
- module ChildProcess
2
- class AbstractProcess
3
- POLL_INTERVAL = 0.1
4
-
5
- attr_reader :exit_code
6
-
7
- #
8
- # Set this to true if you do not care about when or if the process quits.
9
- #
10
- attr_accessor :detach
11
-
12
- #
13
- # Set this to true if you want to write to the process' stdin (process.io.stdin)
14
- #
15
- attr_accessor :duplex
16
-
17
- #
18
- # Modify the child's environment variables
19
- #
20
- attr_reader :environment
21
-
22
- #
23
- # Set the child's current working directory.
24
- #
25
- attr_accessor :cwd
26
-
27
- #
28
- # Set this to true to make the child process the leader of a new process group
29
- #
30
- # This can be used to make sure that all grandchildren are killed
31
- # when the child process dies.
32
- #
33
- attr_accessor :leader
34
-
35
- #
36
- # Create a new process with the given args.
37
- #
38
- # @api private
39
- # @see ChildProcess.build
40
- #
41
-
42
- def initialize(args)
43
- unless args.all? { |e| e.kind_of?(String) }
44
- raise ArgumentError, "all arguments must be String: #{args.inspect}"
45
- end
46
-
47
- @args = args
48
- @started = false
49
- @exit_code = nil
50
- @io = nil
51
- @cwd = nil
52
- @detach = false
53
- @duplex = false
54
- @leader = false
55
- @environment = {}
56
- end
57
-
58
- #
59
- # Returns a ChildProcess::AbstractIO subclass to configure the child's IO streams.
60
- #
61
-
62
- def io
63
- raise SubclassResponsibility, "io"
64
- end
65
-
66
- #
67
- # @return [Integer] the pid of the process after it has started
68
- #
69
-
70
- def pid
71
- raise SubclassResponsibility, "pid"
72
- end
73
-
74
- #
75
- # Launch the child process
76
- #
77
- # @return [AbstractProcess] self
78
- #
79
-
80
- def start
81
- launch_process
82
- @started = true
83
-
84
- self
85
- end
86
-
87
- #
88
- # Forcibly terminate the process, using increasingly harsher methods if possible.
89
- #
90
- # @param [Integer] timeout (3) Seconds to wait before trying the next method.
91
- #
92
-
93
- def stop(timeout = 3)
94
- raise SubclassResponsibility, "stop"
95
- end
96
-
97
- #
98
- # Block until the process has been terminated.
99
- #
100
- # @return [Integer] The exit status of the process
101
- #
102
-
103
- def wait
104
- raise SubclassResponsibility, "wait"
105
- end
106
-
107
- #
108
- # Did the process exit?
109
- #
110
- # @return [Boolean]
111
- #
112
-
113
- def exited?
114
- raise SubclassResponsibility, "exited?"
115
- end
116
-
117
- #
118
- # Has the process started?
119
- #
120
- # @return [Boolean]
121
- #
122
-
123
- def started?
124
- @started
125
- end
126
-
127
- #
128
- # Is this process running?
129
- #
130
- # @return [Boolean]
131
- #
132
-
133
- def alive?
134
- started? && !exited?
135
- end
136
-
137
- #
138
- # Returns true if the process has exited and the exit code was not 0.
139
- #
140
- # @return [Boolean]
141
- #
142
-
143
- def crashed?
144
- exited? && @exit_code != 0
145
- end
146
-
147
- #
148
- # Wait for the process to exit, raising a ChildProcess::TimeoutError if
149
- # the timeout expires.
150
- #
151
-
152
- def poll_for_exit(timeout)
153
- log "polling #{timeout} seconds for exit"
154
-
155
- end_time = Time.now + timeout
156
- until (ok = exited?) || Time.now > end_time
157
- sleep POLL_INTERVAL
158
- end
159
-
160
- unless ok
161
- raise TimeoutError, "process still alive after #{timeout} seconds"
162
- end
163
- end
164
-
165
- private
166
-
167
- def launch_process
168
- raise SubclassResponsibility, "launch_process"
169
- end
170
-
171
- def detach?
172
- @detach
173
- end
174
-
175
- def duplex?
176
- @duplex
177
- end
178
-
179
- def leader?
180
- @leader
181
- end
182
-
183
- def log(*args)
184
- ChildProcess.logger.debug "#{self.inspect} : #{args.inspect}"
185
- end
186
-
187
- def assert_started
188
- raise Error, "process not started" unless started?
189
- end
190
-
191
- end # AbstractProcess
192
- end # ChildProcess
1
+ module ChildProcess
2
+ class AbstractProcess
3
+ POLL_INTERVAL = 0.1
4
+
5
+ attr_reader :exit_code
6
+
7
+ #
8
+ # Set this to true if you do not care about when or if the process quits.
9
+ #
10
+ attr_accessor :detach
11
+
12
+ #
13
+ # Set this to true if you want to write to the process' stdin (process.io.stdin)
14
+ #
15
+ attr_accessor :duplex
16
+
17
+ #
18
+ # Modify the child's environment variables
19
+ #
20
+ attr_reader :environment
21
+
22
+ #
23
+ # Set the child's current working directory.
24
+ #
25
+ attr_accessor :cwd
26
+
27
+ #
28
+ # Set this to true to make the child process the leader of a new process group
29
+ #
30
+ # This can be used to make sure that all grandchildren are killed
31
+ # when the child process dies.
32
+ #
33
+ attr_accessor :leader
34
+
35
+ #
36
+ # Create a new process with the given args.
37
+ #
38
+ # @api private
39
+ # @see ChildProcess.build
40
+ #
41
+
42
+ def initialize(args)
43
+ unless args.all? { |e| e.kind_of?(String) }
44
+ raise ArgumentError, "all arguments must be String: #{args.inspect}"
45
+ end
46
+
47
+ @args = args
48
+ @started = false
49
+ @exit_code = nil
50
+ @io = nil
51
+ @cwd = nil
52
+ @detach = false
53
+ @duplex = false
54
+ @leader = false
55
+ @environment = {}
56
+ end
57
+
58
+ #
59
+ # Returns a ChildProcess::AbstractIO subclass to configure the child's IO streams.
60
+ #
61
+
62
+ def io
63
+ raise SubclassResponsibility, "io"
64
+ end
65
+
66
+ #
67
+ # @return [Integer] the pid of the process after it has started
68
+ #
69
+
70
+ def pid
71
+ raise SubclassResponsibility, "pid"
72
+ end
73
+
74
+ #
75
+ # Launch the child process
76
+ #
77
+ # @return [AbstractProcess] self
78
+ #
79
+
80
+ def start
81
+ launch_process
82
+ @started = true
83
+
84
+ self
85
+ end
86
+
87
+ #
88
+ # Forcibly terminate the process, using increasingly harsher methods if possible.
89
+ #
90
+ # @param [Integer] timeout (3) Seconds to wait before trying the next method.
91
+ #
92
+
93
+ def stop(timeout = 3)
94
+ raise SubclassResponsibility, "stop"
95
+ end
96
+
97
+ #
98
+ # Block until the process has been terminated.
99
+ #
100
+ # @return [Integer] The exit status of the process
101
+ #
102
+
103
+ def wait
104
+ raise SubclassResponsibility, "wait"
105
+ end
106
+
107
+ #
108
+ # Did the process exit?
109
+ #
110
+ # @return [Boolean]
111
+ #
112
+
113
+ def exited?
114
+ raise SubclassResponsibility, "exited?"
115
+ end
116
+
117
+ #
118
+ # Has the process started?
119
+ #
120
+ # @return [Boolean]
121
+ #
122
+
123
+ def started?
124
+ @started
125
+ end
126
+
127
+ #
128
+ # Is this process running?
129
+ #
130
+ # @return [Boolean]
131
+ #
132
+
133
+ def alive?
134
+ started? && !exited?
135
+ end
136
+
137
+ #
138
+ # Returns true if the process has exited and the exit code was not 0.
139
+ #
140
+ # @return [Boolean]
141
+ #
142
+
143
+ def crashed?
144
+ exited? && @exit_code != 0
145
+ end
146
+
147
+ #
148
+ # Wait for the process to exit, raising a ChildProcess::TimeoutError if
149
+ # the timeout expires.
150
+ #
151
+
152
+ def poll_for_exit(timeout)
153
+ log "polling #{timeout} seconds for exit"
154
+
155
+ end_time = Time.now + timeout
156
+ until (ok = exited?) || Time.now > end_time
157
+ sleep POLL_INTERVAL
158
+ end
159
+
160
+ unless ok
161
+ raise TimeoutError, "process still alive after #{timeout} seconds"
162
+ end
163
+ end
164
+
165
+ private
166
+
167
+ def launch_process
168
+ raise SubclassResponsibility, "launch_process"
169
+ end
170
+
171
+ def detach?
172
+ @detach
173
+ end
174
+
175
+ def duplex?
176
+ @duplex
177
+ end
178
+
179
+ def leader?
180
+ @leader
181
+ end
182
+
183
+ def log(*args)
184
+ ChildProcess.logger.debug "#{self.inspect} : #{args.inspect}"
185
+ end
186
+
187
+ def assert_started
188
+ raise Error, "process not started" unless started?
189
+ end
190
+
191
+ end # AbstractProcess
192
+ end # ChildProcess
@@ -1,26 +1,37 @@
1
- module ChildProcess
2
- class Error < StandardError
3
- end
4
-
5
- class TimeoutError < Error
6
- end
7
-
8
- class SubclassResponsibility < Error
9
- end
10
-
11
- class InvalidEnvironmentVariable < Error
12
- end
13
-
14
- class LaunchError < Error
15
- end
16
-
17
- class MissingPlatformError < Error
18
- def initialize
19
- message = "posix_spawn is not yet supported on #{ChildProcess.platform_name} (#{RUBY_PLATFORM}), falling back to default implementation. " +
20
- "If you believe this is an error, please file a bug at http://github.com/enkessler/childprocess/issues"
21
-
22
- super(message)
23
- end
24
-
25
- end
26
- end
1
+ module ChildProcess
2
+ class Error < StandardError
3
+ end
4
+
5
+ class TimeoutError < Error
6
+ end
7
+
8
+ class SubclassResponsibility < Error
9
+ end
10
+
11
+ class InvalidEnvironmentVariable < Error
12
+ end
13
+
14
+ class LaunchError < Error
15
+ end
16
+
17
+ class MissingFFIError < Error
18
+ def initialize
19
+ message = "FFI is a required pre-requisite for posix_spawn, falling back to default implementation. " +
20
+ "Please add it to your deployment to unlock this functionality. " +
21
+ "If you believe this is an error, please file a bug at http://github.com/enkessler/childprocess/issues"
22
+
23
+ super(message)
24
+ end
25
+
26
+ end
27
+
28
+ class MissingPlatformError < Error
29
+ def initialize
30
+ message = "posix_spawn is not yet supported on #{ChildProcess.platform_name} (#{RUBY_PLATFORM}), falling back to default implementation. " +
31
+ "If you believe this is an error, please file a bug at http://github.com/enkessler/childprocess/issues"
32
+
33
+ super(message)
34
+ end
35
+
36
+ end
37
+ end
@@ -1,16 +1,16 @@
1
- module ChildProcess
2
- module JRuby
3
- class IO < AbstractIO
4
- private
5
-
6
- def check_type(output)
7
- unless output.respond_to?(:to_outputstream) && output.respond_to?(:write)
8
- raise ArgumentError, "expected #{output.inspect} to respond to :to_outputstream"
9
- end
10
- end
11
-
12
- end # IO
13
- end # Unix
14
- end # ChildProcess
15
-
16
-
1
+ module ChildProcess
2
+ module JRuby
3
+ class IO < AbstractIO
4
+ private
5
+
6
+ def check_type(output)
7
+ unless output.respond_to?(:to_outputstream) && output.respond_to?(:write)
8
+ raise ArgumentError, "expected #{output.inspect} to respond to :to_outputstream"
9
+ end
10
+ end
11
+
12
+ end # IO
13
+ end # Unix
14
+ end # ChildProcess
15
+
16
+