childprocess 0.2.8 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +8 -2
- data/README.md +1 -1
- data/lib/childprocess.rb +21 -19
- data/lib/childprocess/errors.rb +3 -3
- data/lib/childprocess/jruby.rb +49 -2
- data/lib/childprocess/tools/generator.rb +10 -3
- data/lib/childprocess/unix/lib.rb +10 -1
- data/lib/childprocess/unix/platform/x86_64-linux.rb +2 -1
- data/lib/childprocess/unix/posix_spawn_process.rb +53 -29
- data/lib/childprocess/version.rb +1 -1
- data/lib/childprocess/windows/lib.rb +10 -2
- data/lib/childprocess/windows/process_builder.rb +4 -4
- data/spec/io_spec.rb +10 -7
- data/spec/jruby_spec.rb +2 -2
- data/spec/spec_helper.rb +28 -2
- data/spec/unix_spec.rb +2 -1
- metadata +4 -4
data/.travis.yml
CHANGED
@@ -3,7 +3,13 @@ rvm:
|
|
3
3
|
- 1.9.2
|
4
4
|
- 1.9.3
|
5
5
|
- jruby
|
6
|
-
|
6
|
+
- rbx
|
7
7
|
env:
|
8
8
|
- CHILDPROCESS_POSIX_SPAWN=true
|
9
|
-
- CHILDPROCESS_POSIX_SPAWN=false
|
9
|
+
- CHILDPROCESS_POSIX_SPAWN=false
|
10
|
+
matrix:
|
11
|
+
exclude:
|
12
|
+
- rvm: rbx
|
13
|
+
env: CHILDPROCESS_POSIX_SPAWN=true
|
14
|
+
- rvm: jruby
|
15
|
+
env: CHILDPROCESS_POSIX_SPAWN=true
|
data/README.md
CHANGED
data/lib/childprocess.rb
CHANGED
@@ -10,17 +10,17 @@ module ChildProcess
|
|
10
10
|
|
11
11
|
class << self
|
12
12
|
def new(*args)
|
13
|
-
case
|
14
|
-
when :jruby
|
15
|
-
JRuby::Process.new(args)
|
16
|
-
when :windows
|
17
|
-
Windows::Process.new(args)
|
13
|
+
case os
|
18
14
|
when :macosx, :linux, :unix, :cygwin
|
19
15
|
if posix_spawn?
|
20
16
|
Unix::PosixSpawnProcess.new(args)
|
17
|
+
elsif jruby?
|
18
|
+
JRuby::Process.new(args)
|
21
19
|
else
|
22
20
|
Unix::ForkExecProcess.new(args)
|
23
21
|
end
|
22
|
+
when :windows
|
23
|
+
Windows::Process.new(args)
|
24
24
|
else
|
25
25
|
raise Error, "unsupported platform #{platform.inspect}"
|
26
26
|
end
|
@@ -32,10 +32,6 @@ module ChildProcess
|
|
32
32
|
:jruby
|
33
33
|
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "ironruby"
|
34
34
|
:ironruby
|
35
|
-
elsif RUBY_PLATFORM =~ /mswin|msys|mingw32/
|
36
|
-
:windows
|
37
|
-
elsif RUBY_PLATFORM =~ /cygwin/
|
38
|
-
:cygwin
|
39
35
|
else
|
40
36
|
os
|
41
37
|
end
|
@@ -46,7 +42,7 @@ module ChildProcess
|
|
46
42
|
end
|
47
43
|
|
48
44
|
def unix?
|
49
|
-
!
|
45
|
+
!windows?
|
50
46
|
end
|
51
47
|
|
52
48
|
def linux?
|
@@ -57,14 +53,12 @@ module ChildProcess
|
|
57
53
|
platform == :jruby
|
58
54
|
end
|
59
55
|
|
60
|
-
def jruby_on_unix?
|
61
|
-
jruby? and [:macosx, :linux, :unix].include? os
|
62
|
-
end
|
63
|
-
|
64
56
|
def windows?
|
65
|
-
|
57
|
+
os == :windows
|
66
58
|
end
|
67
59
|
|
60
|
+
@posix_spawn = false
|
61
|
+
|
68
62
|
def posix_spawn?
|
69
63
|
enabled = @posix_spawn || %w[1 true].include?(ENV['CHILDPROCESS_POSIX_SPAWN'])
|
70
64
|
return false unless enabled
|
@@ -85,6 +79,10 @@ module ChildProcess
|
|
85
79
|
false
|
86
80
|
end
|
87
81
|
|
82
|
+
#
|
83
|
+
# Set this to true to enable experimental use of posix_spawn.
|
84
|
+
#
|
85
|
+
|
88
86
|
def posix_spawn=(bool)
|
89
87
|
@posix_spawn = bool
|
90
88
|
end
|
@@ -95,12 +93,14 @@ module ChildProcess
|
|
95
93
|
host_os = RbConfig::CONFIG['host_os'].downcase
|
96
94
|
|
97
95
|
case host_os
|
98
|
-
when /mswin|msys|mingw32|cygwin/
|
99
|
-
:windows
|
100
|
-
when /darwin|mac os/
|
101
|
-
:macosx
|
102
96
|
when /linux/
|
103
97
|
:linux
|
98
|
+
when /darwin|mac os/
|
99
|
+
:macosx
|
100
|
+
when /mswin|msys|mingw32/
|
101
|
+
:windows
|
102
|
+
when /cygwin/
|
103
|
+
:cygwin
|
104
104
|
when /solaris|bsd/
|
105
105
|
:unix
|
106
106
|
else
|
@@ -161,3 +161,5 @@ module ChildProcess
|
|
161
161
|
|
162
162
|
end # class << self
|
163
163
|
end # ChildProcess
|
164
|
+
|
165
|
+
require 'jruby' if ChildProcess.jruby?
|
data/lib/childprocess/errors.rb
CHANGED
@@ -2,15 +2,15 @@ module ChildProcess
|
|
2
2
|
class Error < StandardError; end
|
3
3
|
class TimeoutError < StandardError; end
|
4
4
|
class SubclassResponsibility < StandardError; end
|
5
|
-
class
|
5
|
+
class InvalidEnvironmentVariable < StandardError; end
|
6
6
|
class LaunchError < StandardError; end
|
7
7
|
|
8
8
|
class MissingPlatformError < StandardError
|
9
9
|
def initialize
|
10
10
|
platform = ChildProcess.platform_name
|
11
11
|
|
12
|
-
message = "posix_spawn is not yet supported on #{ChildProcess.platform_name} (#{RUBY_PLATFORM}), falling back to
|
13
|
-
"
|
12
|
+
message = "posix_spawn is not yet supported on #{ChildProcess.platform_name} (#{RUBY_PLATFORM}), falling back to default implementation. " +
|
13
|
+
"If you believe this is an error, please file a bug at http://github.com/jarib/childprocess/issues"
|
14
14
|
|
15
15
|
super(message)
|
16
16
|
end
|
data/lib/childprocess/jruby.rb
CHANGED
@@ -1,9 +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
|
+
else
|
12
|
+
field_reader :fd
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
1
16
|
module ChildProcess
|
2
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
|
3
51
|
end
|
4
52
|
end
|
5
53
|
|
6
|
-
require "java"
|
7
54
|
require "childprocess/jruby/pump"
|
8
55
|
require "childprocess/jruby/io"
|
9
|
-
require "childprocess/jruby/process"
|
56
|
+
require "childprocess/jruby/process"
|
@@ -46,6 +46,7 @@ module ChildProcess
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def fetch_size(type_name, opts = {})
|
49
|
+
print "sizeof(#{type_name}): "
|
49
50
|
src = <<-EOF
|
50
51
|
int main() {
|
51
52
|
printf("%d", (unsigned int)sizeof(#{type_name}));
|
@@ -59,10 +60,14 @@ int main() {
|
|
59
60
|
raise "sizeof(#{type_name}) == #{output.to_i} (output=#{output})"
|
60
61
|
end
|
61
62
|
|
62
|
-
|
63
|
+
size = output.to_i
|
64
|
+
@sizeof[type_name] = size
|
65
|
+
|
66
|
+
puts size
|
63
67
|
end
|
64
68
|
|
65
69
|
def fetch_constant(name, opts)
|
70
|
+
print "#{name}: "
|
66
71
|
src = <<-EOF
|
67
72
|
int main() {
|
68
73
|
printf("%d", (unsigned int)#{name});
|
@@ -71,12 +76,14 @@ int main() {
|
|
71
76
|
EOF
|
72
77
|
|
73
78
|
output = execute(src, opts)
|
74
|
-
|
79
|
+
value = Integer(output)
|
80
|
+
@constants[name] = value
|
81
|
+
|
82
|
+
puts value
|
75
83
|
end
|
76
84
|
|
77
85
|
|
78
86
|
def execute(src, opts)
|
79
|
-
|
80
87
|
program = Array(opts[:define]).map do |key, value|
|
81
88
|
<<-SRC
|
82
89
|
#ifndef #{key}
|
@@ -1,10 +1,19 @@
|
|
1
|
+
|
1
2
|
module ChildProcess
|
2
3
|
module Unix
|
3
4
|
module Lib
|
4
5
|
extend FFI::Library
|
5
|
-
|
6
6
|
ffi_lib FFI::Library::LIBC
|
7
7
|
|
8
|
+
if ChildProcess.os == :macosx
|
9
|
+
attach_function :_NSGetEnviron, [], :pointer
|
10
|
+
def self.environ
|
11
|
+
_NSGetEnviron().read_pointer
|
12
|
+
end
|
13
|
+
elsif respond_to? :attach_variable
|
14
|
+
attach_variable :environ, :pointer
|
15
|
+
end
|
16
|
+
|
8
17
|
attach_function :strerror, [:int], :string
|
9
18
|
|
10
19
|
# int posix_spawnp(
|
@@ -13,22 +13,22 @@ module ChildProcess
|
|
13
13
|
|
14
14
|
if @io
|
15
15
|
if @io.stdout
|
16
|
-
actions.add_dup @io.stdout
|
16
|
+
actions.add_dup fileno_for(@io.stdout), fileno_for($stdout)
|
17
17
|
else
|
18
|
-
actions.add_open $stdout
|
18
|
+
actions.add_open fileno_for($stdout), "/dev/null", File::WRONLY, 0644
|
19
19
|
end
|
20
20
|
|
21
21
|
if @io.stderr
|
22
|
-
actions.add_dup @io.stderr
|
22
|
+
actions.add_dup fileno_for(@io.stderr), fileno_for($stderr)
|
23
23
|
else
|
24
|
-
actions.add_open $stderr
|
24
|
+
actions.add_open fileno_for($stderr), "/dev/null", File::WRONLY, 0644
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
if duplex?
|
29
29
|
reader, writer = ::IO.pipe
|
30
|
-
actions.add_dup reader
|
31
|
-
actions.add_close writer
|
30
|
+
actions.add_dup fileno_for(reader), fileno_for($stdin)
|
31
|
+
actions.add_close fileno_for(writer)
|
32
32
|
end
|
33
33
|
|
34
34
|
if defined? Platform::POSIX_SPAWN_USEVFORK
|
@@ -37,13 +37,17 @@ module ChildProcess
|
|
37
37
|
|
38
38
|
attrs.flags = flags
|
39
39
|
|
40
|
+
# wrap in helper classes in order to avoid GC'ed pointers
|
41
|
+
argv = Argv.new(@args)
|
42
|
+
envp = Envp.new(ENV.to_hash.merge(@environment))
|
43
|
+
|
40
44
|
ret = Lib.posix_spawnp(
|
41
45
|
pid_ptr,
|
42
46
|
@args.first, # TODO: not sure this matches exec() behaviour
|
43
47
|
actions,
|
44
48
|
attrs,
|
45
49
|
argv,
|
46
|
-
|
50
|
+
envp
|
47
51
|
)
|
48
52
|
|
49
53
|
if duplex?
|
@@ -62,38 +66,58 @@ module ChildProcess
|
|
62
66
|
::Process.detach(@pid) if detach?
|
63
67
|
end
|
64
68
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
+
if ChildProcess.jruby?
|
70
|
+
def fileno_for(obj)
|
71
|
+
ChildProcess::JRuby.posix_fileno_for(obj)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
def fileno_for(obj)
|
75
|
+
obj.fileno
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Argv
|
80
|
+
def initialize(args)
|
81
|
+
@ptrs = args.map do |e|
|
82
|
+
if e.include?("\0")
|
83
|
+
raise ArgumentError, "argument cannot contain null bytes: #{e.inspect}"
|
84
|
+
end
|
85
|
+
|
86
|
+
FFI::MemoryPointer.from_string(e.to_s)
|
69
87
|
end
|
70
|
-
|
88
|
+
|
89
|
+
@ptrs << nil
|
71
90
|
end
|
72
|
-
arg_ptrs << nil
|
73
91
|
|
74
|
-
|
75
|
-
|
92
|
+
def to_ptr
|
93
|
+
argv = FFI::MemoryPointer.new(:pointer, @ptrs.size)
|
94
|
+
argv.put_array_of_pointer(0, @ptrs)
|
76
95
|
|
77
|
-
|
78
|
-
|
96
|
+
argv
|
97
|
+
end
|
98
|
+
end # Argv
|
79
99
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
100
|
+
class Envp
|
101
|
+
def initialize(env)
|
102
|
+
@ptrs = env.map do |key, val|
|
103
|
+
if key =~ /=|\0/ || val.include?("\0")
|
104
|
+
raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
|
105
|
+
end
|
106
|
+
|
107
|
+
FFI::MemoryPointer.from_string("#{key}=#{val}")
|
84
108
|
end
|
85
109
|
|
86
|
-
|
110
|
+
@ptrs << nil
|
87
111
|
end
|
88
112
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
env.put_array_of_pointer(0, env_ptrs)
|
113
|
+
def to_ptr
|
114
|
+
env = FFI::MemoryPointer.new(:pointer, @ptrs.size)
|
115
|
+
env.put_array_of_pointer(0, @ptrs)
|
93
116
|
|
94
|
-
|
95
|
-
|
117
|
+
env
|
118
|
+
end
|
119
|
+
end # Envp
|
96
120
|
|
97
121
|
end
|
98
122
|
end
|
99
|
-
end
|
123
|
+
end
|
data/lib/childprocess/version.rb
CHANGED
@@ -252,12 +252,20 @@ module ChildProcess
|
|
252
252
|
)
|
253
253
|
|
254
254
|
str = buf.read_string(size).strip
|
255
|
-
|
255
|
+
if errnum == 0
|
256
|
+
"Unknown error (Windows says #{str.inspect}, but it did not.)"
|
257
|
+
else
|
258
|
+
"#{str} (#{errnum})"
|
259
|
+
end
|
256
260
|
end
|
257
261
|
|
258
262
|
def handle_for(fd_or_io)
|
259
263
|
if fd_or_io.kind_of?(IO) || fd_or_io.respond_to?(:fileno)
|
260
|
-
|
264
|
+
if ChildProcess.jruby?
|
265
|
+
handle = ChildProcess::JRuby.windows_handle_for(fd_or_io)
|
266
|
+
else
|
267
|
+
handle = get_osfhandle(fd_or_io.fileno)
|
268
|
+
end
|
261
269
|
elsif fd_or_io.kind_of?(Fixnum)
|
262
270
|
handle = get_osfhandle(fd_or_io)
|
263
271
|
elsif fd_or_io.respond_to?(:to_io)
|
@@ -48,12 +48,12 @@ module ChildProcess
|
|
48
48
|
strings = ENV.map { |k,v| "#{k}=#{v}\0" }
|
49
49
|
|
50
50
|
# extras
|
51
|
-
@environment.each do |key,
|
52
|
-
if key.include?("
|
53
|
-
raise
|
51
|
+
@environment.each do |key, val|
|
52
|
+
if key =~ /=|\0/ || val.include?("\0")
|
53
|
+
raise InvalidEnvironmentVariable, "#{key.inspect} => #{val.inspect}"
|
54
54
|
end
|
55
55
|
|
56
|
-
strings << "#{key}=#{
|
56
|
+
strings << "#{key}=#{val}\0"
|
57
57
|
end
|
58
58
|
|
59
59
|
strings << "\0" # terminate the env block
|
data/spec/io_spec.rb
CHANGED
@@ -85,10 +85,10 @@ describe ChildProcess do
|
|
85
85
|
# http://travis-ci.org/#!/jarib/childprocess/jobs/487331
|
86
86
|
#
|
87
87
|
|
88
|
-
it "works with pipes", :
|
88
|
+
it "works with pipes", :process_builder => false do
|
89
89
|
process = ruby(<<-CODE)
|
90
|
-
STDOUT.
|
91
|
-
STDERR.
|
90
|
+
STDOUT.print "stdout"
|
91
|
+
STDERR.print "stderr"
|
92
92
|
CODE
|
93
93
|
|
94
94
|
stdout, stdout_w = IO.pipe
|
@@ -109,12 +109,15 @@ describe ChildProcess do
|
|
109
109
|
stdout_w.close
|
110
110
|
stderr_w.close
|
111
111
|
|
112
|
-
stdout.read
|
113
|
-
stderr.read
|
112
|
+
out = stdout.read
|
113
|
+
err = stderr.read
|
114
|
+
|
115
|
+
[out, err].should == %w[stdout stderr]
|
114
116
|
end
|
115
117
|
|
116
118
|
it "can set close-on-exec when IO is inherited" do
|
117
|
-
|
119
|
+
port = random_free_port
|
120
|
+
server = TCPServer.new("127.0.0.1", port)
|
118
121
|
ChildProcess.close_on_exec server
|
119
122
|
|
120
123
|
process = sleeping_ruby
|
@@ -123,6 +126,6 @@ describe ChildProcess do
|
|
123
126
|
process.start
|
124
127
|
server.close
|
125
128
|
|
126
|
-
|
129
|
+
wait_until { can_bind? "127.0.0.1", port }
|
127
130
|
end
|
128
131
|
end
|
data/spec/jruby_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
require "pid_behavior"
|
3
3
|
|
4
|
-
if ChildProcess.jruby?
|
4
|
+
if ChildProcess.jruby? && !ChildProcess.windows?
|
5
5
|
describe ChildProcess::JRuby::IO do
|
6
6
|
let(:io) { ChildProcess::JRuby::IO.new }
|
7
7
|
|
@@ -11,7 +11,7 @@ if ChildProcess.jruby?
|
|
11
11
|
end
|
12
12
|
|
13
13
|
describe ChildProcess::JRuby::Process do
|
14
|
-
if ChildProcess.
|
14
|
+
if ChildProcess.unix?
|
15
15
|
it_behaves_like "a platform that provides the child's pid"
|
16
16
|
else
|
17
17
|
it "raises an error when trying to access the child's pid" do
|
data/spec/spec_helper.rb
CHANGED
@@ -115,6 +115,32 @@ module ChildProcessSpecHelper
|
|
115
115
|
10
|
116
116
|
end
|
117
117
|
|
118
|
+
def random_free_port
|
119
|
+
server = TCPServer.new('127.0.0.1', 0)
|
120
|
+
port = server.addr[1]
|
121
|
+
server.close
|
122
|
+
|
123
|
+
port
|
124
|
+
end
|
125
|
+
|
126
|
+
def wait_until(timeout = 10, &blk)
|
127
|
+
end_time = Time.now + timeout
|
128
|
+
|
129
|
+
until Time.now >= end_time
|
130
|
+
return if yield
|
131
|
+
sleep 0.05
|
132
|
+
end
|
133
|
+
|
134
|
+
raise "timed out"
|
135
|
+
end
|
136
|
+
|
137
|
+
def can_bind?(host, port)
|
138
|
+
TCPServer.new(host, port).close
|
139
|
+
true
|
140
|
+
rescue
|
141
|
+
false
|
142
|
+
end
|
143
|
+
|
118
144
|
end # ChildProcessSpecHelper
|
119
145
|
|
120
146
|
Thread.abort_on_exception = true
|
@@ -125,8 +151,8 @@ RSpec.configure do |c|
|
|
125
151
|
@process && @process.alive? && @process.stop
|
126
152
|
}
|
127
153
|
|
128
|
-
if
|
129
|
-
c.filter_run_excluding :
|
154
|
+
if ChildProcess.jruby? && !ChildProcess.posix_spawn?
|
155
|
+
c.filter_run_excluding :process_builder => false
|
130
156
|
end
|
131
157
|
|
132
158
|
if ChildProcess.linux? && ChildProcess.posix_spawn?
|
data/spec/unix_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require File.expand_path('../spec_helper', __FILE__)
|
2
2
|
require "pid_behavior"
|
3
3
|
|
4
|
-
if ChildProcess.unix? && !ChildProcess.posix_spawn?
|
4
|
+
if ChildProcess.unix? && !ChildProcess.jruby? && !ChildProcess.posix_spawn?
|
5
|
+
|
5
6
|
describe ChildProcess::Unix::Process do
|
6
7
|
it_behaves_like "a platform that provides the child's pid"
|
7
8
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: childprocess
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 9
|
10
|
+
version: 0.2.9
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jari Bakken
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-01-
|
18
|
+
date: 2012-01-12 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rspec
|