childprocess 4.1.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +23 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +0 -2
  5. data/README.md +3 -20
  6. data/lib/childprocess/abstract_process.rb +1 -1
  7. data/lib/childprocess/errors.rb +0 -21
  8. data/lib/childprocess/process_spawn_process.rb +127 -0
  9. data/lib/childprocess/unix/process.rb +3 -64
  10. data/lib/childprocess/unix.rb +2 -4
  11. data/lib/childprocess/version.rb +1 -1
  12. data/lib/childprocess/windows/process.rb +13 -116
  13. data/lib/childprocess/windows.rb +0 -29
  14. data/lib/childprocess.rb +16 -53
  15. data/spec/childprocess_spec.rb +39 -15
  16. data/spec/io_spec.rb +1 -1
  17. data/spec/spec_helper.rb +3 -18
  18. data/spec/unix_spec.rb +3 -7
  19. metadata +8 -27
  20. data/.travis.yml +0 -37
  21. data/appveyor.yml +0 -36
  22. data/lib/childprocess/jruby/io.rb +0 -16
  23. data/lib/childprocess/jruby/process.rb +0 -184
  24. data/lib/childprocess/jruby/pump.rb +0 -53
  25. data/lib/childprocess/jruby.rb +0 -56
  26. data/lib/childprocess/tools/generator.rb +0 -146
  27. data/lib/childprocess/unix/fork_exec_process.rb +0 -78
  28. data/lib/childprocess/unix/lib.rb +0 -186
  29. data/lib/childprocess/unix/platform/arm64-macosx.rb +0 -11
  30. data/lib/childprocess/unix/platform/i386-linux.rb +0 -12
  31. data/lib/childprocess/unix/platform/i386-solaris.rb +0 -11
  32. data/lib/childprocess/unix/platform/x86_64-linux.rb +0 -12
  33. data/lib/childprocess/unix/platform/x86_64-macosx.rb +0 -11
  34. data/lib/childprocess/unix/posix_spawn_process.rb +0 -134
  35. data/lib/childprocess/windows/handle.rb +0 -91
  36. data/lib/childprocess/windows/lib.rb +0 -416
  37. data/lib/childprocess/windows/process_builder.rb +0 -178
  38. data/lib/childprocess/windows/structs.rb +0 -149
  39. data/spec/jruby_spec.rb +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a5e6dc8970dbfa81b42c587480b4cd7a564995c2a6a0be84f2c8192edcf6d0c
4
- data.tar.gz: 766be22f3575a6ef9ba837a37722f0a852f509fc9efb2a77b01ca121b865a28e
3
+ metadata.gz: 3b5d923affd397c31cc2193e6dc9d6e1a6d32e485f3721210e65caf72b9ee138
4
+ data.tar.gz: '06896c2aa64a9c521b45206d1a0638a8aa63e433a013c261909a920fab135c75'
5
5
  SHA512:
6
- metadata.gz: 8e4ac84967566de68d200d983bfebca6fce9575d7e8fb0aa0324b64dc4b6e0d29d29abe173ce99a54f8b37501eb515573b457f6b612a395f3f5d2bc672da6802
7
- data.tar.gz: 16bc7aaffd67ab47f1f8a8ded31681fb88f1767421838c3ddb1f1033fb7dc0f5c5d664b7dce3fb4107edb9818a73ce2769f23a3e1f30bf51ffc3dd088d8a7690
6
+ metadata.gz: a0a5d3cfee92a6e4896eaf557dbf4fc50d54b54d42c43033776165991b1b606f12ccd1e17d0dbe26963b7d002482ee40877b3ea024765b371393e4a0cbd0343f
7
+ data.tar.gz: 6814491e59bbb30b61393670ca84d5da9d61ea5cb5dbc54a45599ef9cb7318bbf82751fddaf5c7ee5ef501718d7cad4a08636fab3a379148899af3acd0303bd4
@@ -0,0 +1,23 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ os: [ ubuntu, macos, windows ]
9
+ ruby: [ '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', jruby, truffleruby ]
10
+ exclude:
11
+ - { os: windows, ruby: truffleruby }
12
+ # fails to load rspec: RuntimeError: CRITICAL: RUBYGEMS_ACTIVATION_MONITOR.owned?: before false -> after true
13
+ - { os: windows, ruby: jruby }
14
+ runs-on: ${{ matrix.os }}-latest
15
+ env:
16
+ CHILDPROCESS_UNSET: should-be-unset
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: ${{ matrix.ruby }}
22
+ bundler-cache: true
23
+ - run: bundle exec rake spec
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ### Version 5.0.0 / 2024-01-06
2
+
3
+ * [#175](https://github.com/enkessler/childprocess/pull/175): Replace all backends by `Process.spawn` for portability, reliability and simplicity.
4
+ * [#185](https://github.com/enkessler/childprocess/pull/185): Add support for Ruby 3.x
5
+
1
6
  ### Version 4.1.0 / 2021-06-08
2
7
 
3
8
  * [#170](https://github.com/enkessler/childprocess/pull/170): Update gem homepage to use `https://`
data/Gemfile CHANGED
@@ -5,5 +5,3 @@ gemspec
5
5
 
6
6
  # Used for local development/testing only
7
7
  gem 'rake'
8
-
9
- gem 'ffi' if ENV['CHILDPROCESS_POSIX_SPAWN'] == 'true' || Gem.win_platform?
data/README.md CHANGED
@@ -16,8 +16,6 @@ a standalone library.
16
16
 
17
17
  * Ruby 2.4+, JRuby 9+
18
18
 
19
- Windows users **must** ensure the `ffi` gem (`>= 1.0.11`) is installed in order to use ChildProcess.
20
-
21
19
  # Usage
22
20
 
23
21
  The object returned from `ChildProcess.build` will implement `ChildProcess::AbstractProcess`.
@@ -73,9 +71,9 @@ begin
73
71
  process = ChildProcess.build("sh" , "-c",
74
72
  "for i in {1..3}; do echo $i; sleep 1; done")
75
73
  process.io.stdout = w
76
- process.start # This results in a fork, inheriting the write end of the pipe.
74
+ process.start # This results in a subprocess inheriting the write end of the pipe.
77
75
 
78
- # Close parent's copy of the write end of the pipe so when the (forked) child
76
+ # Close parent's copy of the write end of the pipe so when the child
79
77
  # process closes its write end of the pipe the parent receives EOF when
80
78
  # attempting to read from it. If the parent leaves its write end open, it
81
79
  # will not detect EOF.
@@ -138,17 +136,6 @@ search.io.stdin.close
138
136
  search.wait
139
137
  ```
140
138
 
141
- #### Prefer posix_spawn on *nix
142
-
143
- If the parent process is using a lot of memory, `fork+exec` can be very expensive. The `posix_spawn()` API removes this overhead.
144
-
145
- ```ruby
146
- ChildProcess.posix_spawn = true
147
- process = ChildProcess.build(*args)
148
- ```
149
-
150
- To be able to use this, please make sure that you have the `ffi` gem installed.
151
-
152
139
  ### Ensure entire process tree dies
153
140
 
154
141
  By default, the child process does not create a new process group. This means there's no guarantee that the entire process tree will die when the child process is killed. To solve this:
@@ -195,11 +182,7 @@ ChildProcess.logger = logger
195
182
 
196
183
  # Implementation
197
184
 
198
- How the process is launched and killed depends on the platform:
199
-
200
- * Unix : `fork + exec` (or `posix_spawn` if enabled)
201
- * Windows : `CreateProcess()` and friends
202
- * JRuby : `java.lang.{Process,ProcessBuilder}`
185
+ ChildProcess 5+ uses `Process.spawn` from the Ruby core library for maximum portability.
203
186
 
204
187
  # Note on Patches/Pull Requests
205
188
 
@@ -39,7 +39,7 @@ module ChildProcess
39
39
  # @see ChildProcess.build
40
40
  #
41
41
 
42
- def initialize(args)
42
+ def initialize(*args)
43
43
  unless args.all? { |e| e.kind_of?(String) }
44
44
  raise ArgumentError, "all arguments must be String: #{args.inspect}"
45
45
  end
@@ -13,25 +13,4 @@ module ChildProcess
13
13
 
14
14
  class LaunchError < Error
15
15
  end
16
-
17
- class MissingFFIError < Error
18
- def initialize
19
- message = "FFI is a required pre-requisite for Windows or posix_spawn support in the ChildProcess gem. " +
20
- "Ensure the `ffi` gem is installed. " +
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
16
  end
@@ -0,0 +1,127 @@
1
+ require_relative 'abstract_process'
2
+
3
+ module ChildProcess
4
+ class ProcessSpawnProcess < AbstractProcess
5
+ attr_reader :pid
6
+
7
+ def exited?
8
+ return true if @exit_code
9
+
10
+ assert_started
11
+ pid, status = ::Process.waitpid2(@pid, ::Process::WNOHANG | ::Process::WUNTRACED)
12
+ pid = nil if pid == 0 # may happen on jruby
13
+
14
+ log(:pid => pid, :status => status)
15
+
16
+ if pid
17
+ set_exit_code(status)
18
+ end
19
+
20
+ !!pid
21
+ rescue Errno::ECHILD
22
+ # may be thrown for detached processes
23
+ true
24
+ end
25
+
26
+ def wait
27
+ assert_started
28
+
29
+ if exited?
30
+ exit_code
31
+ else
32
+ _, status = ::Process.waitpid2(@pid)
33
+
34
+ set_exit_code(status)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def launch_process
41
+ environment = {}
42
+ @environment.each_pair do |key, value|
43
+ key = key.to_s
44
+ value = value.nil? ? nil : value.to_s
45
+
46
+ if key.include?("\0") || key.include?("=") || value.to_s.include?("\0")
47
+ raise InvalidEnvironmentVariable, "#{key.inspect} => #{value.to_s.inspect}"
48
+ end
49
+ environment[key] = value
50
+ end
51
+
52
+ options = {}
53
+
54
+ options[:out] = io.stdout ? io.stdout.fileno : File::NULL
55
+ options[:err] = io.stderr ? io.stderr.fileno : File::NULL
56
+
57
+ if duplex?
58
+ reader, writer = ::IO.pipe
59
+ options[:in] = reader.fileno
60
+ unless ChildProcess.windows?
61
+ options[writer.fileno] = :close
62
+ end
63
+ end
64
+
65
+ if leader?
66
+ if ChildProcess.windows?
67
+ options[:new_pgroup] = true
68
+ else
69
+ options[:pgroup] = true
70
+ end
71
+ end
72
+
73
+ options[:chdir] = @cwd if @cwd
74
+
75
+ if @args.size == 1
76
+ # When given a single String, Process.spawn would think it should use the shell
77
+ # if there is any special character in it. However, ChildProcess should never
78
+ # use the shell. So we use the [cmdname, argv0] form to force no shell.
79
+ arg = @args[0]
80
+ args = [[arg, arg]]
81
+ else
82
+ args = @args
83
+ end
84
+
85
+ begin
86
+ @pid = ::Process.spawn(environment, *args, options)
87
+ rescue SystemCallError => e
88
+ raise LaunchError, e.message
89
+ end
90
+
91
+ if duplex?
92
+ io._stdin = writer
93
+ reader.close
94
+ end
95
+
96
+ ::Process.detach(@pid) if detach?
97
+ end
98
+
99
+ def set_exit_code(status)
100
+ @exit_code = status.exitstatus || status.termsig
101
+ end
102
+
103
+ def send_term
104
+ send_signal 'TERM'
105
+ end
106
+
107
+ def send_kill
108
+ send_signal 'KILL'
109
+ end
110
+
111
+ def send_signal(sig)
112
+ assert_started
113
+
114
+ log "sending #{sig}"
115
+ if leader?
116
+ if ChildProcess.unix?
117
+ ::Process.kill sig, -@pid # negative pid == process group
118
+ else
119
+ output = `taskkill /F /T /PID #{@pid}`
120
+ log output
121
+ end
122
+ else
123
+ ::Process.kill sig, @pid
124
+ end
125
+ end
126
+ end
127
+ end
@@ -1,8 +1,8 @@
1
+ require_relative '../process_spawn_process'
2
+
1
3
  module ChildProcess
2
4
  module Unix
3
- class Process < AbstractProcess
4
- attr_reader :pid
5
-
5
+ class Process < ProcessSpawnProcess
6
6
  def io
7
7
  @io ||= Unix::IO.new
8
8
  end
@@ -24,67 +24,6 @@ module ChildProcess
24
24
  # and send_kill
25
25
  true
26
26
  end
27
-
28
- def exited?
29
- return true if @exit_code
30
-
31
- assert_started
32
- pid, status = ::Process.waitpid2(@pid, ::Process::WNOHANG | ::Process::WUNTRACED)
33
- pid = nil if pid == 0 # may happen on jruby
34
-
35
- log(:pid => pid, :status => status)
36
-
37
- if pid
38
- set_exit_code(status)
39
- end
40
-
41
- !!pid
42
- rescue Errno::ECHILD
43
- # may be thrown for detached processes
44
- true
45
- end
46
-
47
- def wait
48
- assert_started
49
-
50
- if exited?
51
- exit_code
52
- else
53
- _, status = ::Process.waitpid2(@pid)
54
-
55
- set_exit_code(status)
56
- end
57
- end
58
-
59
- private
60
-
61
- def send_term
62
- send_signal 'TERM'
63
- end
64
-
65
- def send_kill
66
- send_signal 'KILL'
67
- end
68
-
69
- def send_signal(sig)
70
- assert_started
71
-
72
- log "sending #{sig}"
73
- ::Process.kill sig, _pid
74
- end
75
-
76
- def set_exit_code(status)
77
- @exit_code = status.exitstatus || status.termsig
78
- end
79
-
80
- def _pid
81
- if leader?
82
- -@pid # negative pid == process group
83
- else
84
- @pid
85
- end
86
- end
87
-
88
27
  end # Process
89
28
  end # Unix
90
29
  end # ChildProcess
@@ -3,7 +3,5 @@ module ChildProcess
3
3
  end
4
4
  end
5
5
 
6
- require "childprocess/unix/io"
7
- require "childprocess/unix/process"
8
- require "childprocess/unix/fork_exec_process"
9
- # PosixSpawnProcess + ffi is required on demand.
6
+ require_relative "unix/io"
7
+ require_relative "unix/process"
@@ -1,3 +1,3 @@
1
1
  module ChildProcess
2
- VERSION = '4.1.0'
2
+ VERSION = '5.0.0'
3
3
  end
@@ -1,131 +1,28 @@
1
+ require_relative '../process_spawn_process'
2
+
1
3
  module ChildProcess
2
4
  module Windows
3
- class Process < AbstractProcess
4
-
5
- attr_reader :pid
6
-
5
+ class Process < ProcessSpawnProcess
7
6
  def io
8
7
  @io ||= Windows::IO.new
9
8
  end
10
9
 
11
10
  def stop(timeout = 3)
12
11
  assert_started
12
+ send_kill
13
13
 
14
- log "sending KILL"
15
- @handle.send(WIN_SIGKILL)
16
-
17
- poll_for_exit(timeout)
18
- ensure
19
- close_handle
20
- close_job_if_necessary
21
- end
22
-
23
- def wait
24
- if exited?
25
- exit_code
26
- else
27
- @handle.wait
28
- @exit_code = @handle.exit_code
29
-
30
- close_handle
31
- close_job_if_necessary
32
-
33
- @exit_code
34
- end
35
- end
36
-
37
- def exited?
38
- return true if @exit_code
39
- assert_started
40
-
41
- code = @handle.exit_code
42
- exited = code != PROCESS_STILL_ACTIVE
43
-
44
- log(:exited? => exited, :code => code)
45
-
46
- if exited
47
- @exit_code = code
48
- close_handle
49
- close_job_if_necessary
50
- end
51
-
52
- exited
53
- end
54
-
55
- private
56
-
57
- def launch_process
58
- builder = ProcessBuilder.new(@args)
59
- builder.leader = leader?
60
- builder.detach = detach?
61
- builder.duplex = duplex?
62
- builder.environment = @environment unless @environment.empty?
63
- builder.cwd = @cwd
64
-
65
- if @io
66
- builder.stdout = @io.stdout
67
- builder.stderr = @io.stderr
68
- end
69
-
70
- @pid = builder.start
71
- @handle = Handle.open @pid
72
-
73
- if leader?
74
- @job = Job.new(detach?, true)
75
- @job << @handle
76
- end
77
-
78
- if duplex?
79
- raise Error, "no stdin stream" unless builder.stdin
80
- io._stdin = builder.stdin
81
- end
82
-
83
- self
84
- end
85
-
86
- def close_handle
87
- @handle.close
88
- end
89
-
90
- def close_job_if_necessary
91
- @job.close if leader?
92
- end
93
-
94
-
95
- class Job
96
- def initialize(detach, leader)
97
- @pointer = Lib.create_job_object(nil, nil)
98
-
99
- if @pointer.nil? || @pointer.null?
100
- raise Error, "unable to create job object"
101
- end
102
-
103
- basic = JobObjectBasicLimitInformation.new
104
- basic[:LimitFlags] |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE if !detach
105
- basic[:LimitFlags] |= JOB_OBJECT_LIMIT_BREAKAWAY_OK if leader
106
-
107
- extended = JobObjectExtendedLimitInformation.new
108
- extended[:BasicLimitInformation] = basic
109
-
110
- ret = Lib.set_information_job_object(
111
- @pointer,
112
- JOB_OBJECT_EXTENDED_LIMIT_INFORMATION,
113
- extended,
114
- extended.size
115
- )
116
-
117
- Lib.check_error ret
118
- end
119
-
120
- def <<(handle)
121
- Lib.check_error Lib.assign_process_to_job_object(@pointer, handle.pointer)
14
+ begin
15
+ return poll_for_exit(timeout)
16
+ rescue TimeoutError
17
+ # try next
122
18
  end
123
19
 
124
- def close
125
- Lib.close_handle @pointer
126
- end
20
+ wait
21
+ rescue Errno::ECHILD, Errno::ESRCH
22
+ # handle race condition where process dies between timeout
23
+ # and send_kill
24
+ true
127
25
  end
128
-
129
26
  end # Process
130
27
  end # Windows
131
28
  end # ChildProcess
@@ -1,38 +1,9 @@
1
1
  require "rbconfig"
2
2
 
3
- begin
4
- require 'ffi'
5
- rescue LoadError
6
- raise ChildProcess::MissingFFIError
7
- end
8
-
9
3
  module ChildProcess
10
4
  module Windows
11
- module Lib
12
- extend FFI::Library
13
-
14
- def self.msvcrt_name
15
- host_part = RbConfig::CONFIG['host_os'].split("_")[1]
16
- manifest = File.join(RbConfig::CONFIG['bindir'], 'ruby.exe.manifest')
17
-
18
- if host_part && host_part.to_i > 80 && File.exists?(manifest)
19
- "msvcr#{host_part}"
20
- else
21
- "msvcrt"
22
- end
23
- end
24
-
25
- ffi_lib "kernel32", msvcrt_name
26
- ffi_convention :stdcall
27
-
28
-
29
- end # Library
30
5
  end # Windows
31
6
  end # ChildProcess
32
7
 
33
- require "childprocess/windows/lib"
34
- require "childprocess/windows/structs"
35
- require "childprocess/windows/handle"
36
8
  require "childprocess/windows/io"
37
- require "childprocess/windows/process_builder"
38
9
  require "childprocess/windows/process"
data/lib/childprocess.rb CHANGED
@@ -2,6 +2,7 @@ require 'childprocess/version'
2
2
  require 'childprocess/errors'
3
3
  require 'childprocess/abstract_process'
4
4
  require 'childprocess/abstract_io'
5
+ require 'childprocess/process_spawn_process'
5
6
  require "fcntl"
6
7
  require 'logger'
7
8
 
@@ -15,15 +16,9 @@ module ChildProcess
15
16
  def new(*args)
16
17
  case os
17
18
  when :macosx, :linux, :solaris, :bsd, :cygwin, :aix
18
- if posix_spawn?
19
- Unix::PosixSpawnProcess.new(args)
20
- elsif jruby?
21
- JRuby::Process.new(args)
22
- else
23
- Unix::ForkExecProcess.new(args)
24
- end
19
+ Unix::Process.new(*args)
25
20
  when :windows
26
- Windows::Process.new(args)
21
+ Windows::Process.new(*args)
27
22
  else
28
23
  raise Error, "unsupported platform #{platform_name.inspect}"
29
24
  end
@@ -40,13 +35,7 @@ module ChildProcess
40
35
  end
41
36
 
42
37
  def platform
43
- if RUBY_PLATFORM == "java"
44
- :jruby
45
- elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "ironruby"
46
- :ironruby
47
- else
48
- os
49
- end
38
+ os
50
39
  end
51
40
 
52
41
  def platform_name
@@ -62,35 +51,18 @@ module ChildProcess
62
51
  end
63
52
 
64
53
  def jruby?
65
- platform == :jruby
54
+ RUBY_ENGINE == 'jruby'
66
55
  end
67
56
 
68
57
  def windows?
69
58
  os == :windows
70
59
  end
71
60
 
72
- def posix_spawn?
73
- enabled = @posix_spawn || %w[1 true].include?(ENV['CHILDPROCESS_POSIX_SPAWN'])
74
- return false unless enabled
75
-
76
- begin
77
- require 'ffi'
78
- rescue LoadError
79
- raise ChildProcess::MissingFFIError
80
- end
81
-
82
- begin
83
- require "childprocess/unix/platform/#{ChildProcess.platform_name}"
84
- rescue LoadError
85
- raise ChildProcess::MissingPlatformError
86
- end
87
-
88
- require "childprocess/unix/lib"
89
- require 'childprocess/unix/posix_spawn_process'
61
+ def posix_spawn_chosen_explicitly?
62
+ @posix_spawn || %w[1 true].include?(ENV['CHILDPROCESS_POSIX_SPAWN'])
63
+ end
90
64
 
91
- true
92
- rescue ChildProcess::MissingPlatformError => ex
93
- warn_once ex.message
65
+ def posix_spawn?
94
66
  false
95
67
  end
96
68
 
@@ -103,6 +75,8 @@ module ChildProcess
103
75
  end
104
76
 
105
77
  def os
78
+ return :windows if ENV['FAKE_WINDOWS'] == 'true'
79
+
106
80
  @os ||= (
107
81
  require "rbconfig"
108
82
  host_os = RbConfig::CONFIG['host_os'].downcase
@@ -158,17 +132,6 @@ module ChildProcess
158
132
  def close_on_exec(file)
159
133
  if file.respond_to?(:close_on_exec=)
160
134
  file.close_on_exec = true
161
- elsif file.respond_to?(:fcntl) && defined?(Fcntl::FD_CLOEXEC)
162
- file.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
163
-
164
- if jruby? && posix_spawn?
165
- # on JRuby, the fcntl call above apparently isn't enough when
166
- # we're launching the process through posix_spawn.
167
- fileno = JRuby.posix_fileno_for(file)
168
- Unix::Lib.fcntl fileno, Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
169
- end
170
- elsif windows?
171
- Windows::Lib.dont_inherit file
172
135
  else
173
136
  raise Error, "not sure how to set close-on-exec for #{file.inspect} on #{platform_name.inspect}"
174
137
  end
@@ -203,8 +166,8 @@ module ChildProcess
203
166
  end # class << self
204
167
  end # ChildProcess
205
168
 
206
- require 'jruby' if ChildProcess.jruby?
207
-
208
- require 'childprocess/unix' if ChildProcess.unix?
209
- require 'childprocess/windows' if ChildProcess.windows?
210
- require 'childprocess/jruby' if ChildProcess.jruby?
169
+ if ChildProcess.windows?
170
+ require 'childprocess/windows'
171
+ else
172
+ require 'childprocess/unix'
173
+ end