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
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
+