DTR 0.0.1 → 0.0.2
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.
- data/CHANGES +1 -0
- data/README +58 -41
- data/Rakefile +76 -15
- data/TODO +12 -2
- data/bin/dtr +50 -29
- data/install.rb +2 -3
- data/lib/dtr.rb +65 -71
- data/lib/dtr/env_store.rb +58 -0
- data/lib/dtr/inject.rb +155 -0
- data/lib/dtr/inject_with_svn.rb +8 -0
- data/lib/dtr/inject_without_updating_codebase.rb +6 -0
- data/lib/dtr/raketasks.rb +48 -0
- data/lib/dtr/ruby_ext.rb +3 -25
- data/lib/dtr/runner.rb +174 -0
- data/lib/dtr/service_provider.rb +91 -0
- data/lib/dtr/svn.rb +20 -0
- data/test/inject_test.rb +25 -0
- data/test/scenario_tests.rb +172 -0
- data/test/svn_test.rb +11 -0
- data/test/test_helper.rb +25 -108
- metadata +13 -20
- data/lib/dtr/base.rb +0 -281
- data/lib/dtr/command_line.rb +0 -173
- data/lib/dtr/drb_dtr.rb +0 -275
- data/show_process_exit_code.bat +0 -5
- data/test/average_packer_test.rb +0 -32
- data/test/base_test.rb +0 -236
- data/test/original_test_reports_test.rb +0 -91
- data/test/ruby_ext_test.rb +0 -13
- data/test/ruby_runner_test.rb +0 -56
- data/test/scenario_setup_and_run_tests_simply.rb +0 -51
- data/test/server_test.rb +0 -39
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: DTR
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.0.2
|
7
|
+
date: 2007-10-08 00:00:00 +08:00
|
8
8
|
summary: DTR is a distributed test runner to run tests on distributed computers for decreasing build time.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -30,32 +30,25 @@ authors:
|
|
30
30
|
- Li Xiao
|
31
31
|
files:
|
32
32
|
- install.rb
|
33
|
-
- bin
|
34
33
|
- CHANGES
|
35
|
-
- doc
|
36
|
-
- html
|
37
|
-
- lib
|
38
34
|
- LICENSE.TXT
|
39
35
|
- Rakefile
|
40
36
|
- README
|
41
|
-
- show_process_exit_code.bat
|
42
|
-
- test
|
43
|
-
- testdata
|
44
|
-
- tmp
|
45
37
|
- TODO
|
46
38
|
- bin/dtr
|
47
|
-
- lib/dtr/
|
48
|
-
- lib/dtr/
|
49
|
-
- lib/dtr/
|
39
|
+
- lib/dtr/env_store.rb
|
40
|
+
- lib/dtr/inject.rb
|
41
|
+
- lib/dtr/inject_without_updating_codebase.rb
|
42
|
+
- lib/dtr/inject_with_svn.rb
|
43
|
+
- lib/dtr/raketasks.rb
|
50
44
|
- lib/dtr/ruby_ext.rb
|
45
|
+
- lib/dtr/runner.rb
|
46
|
+
- lib/dtr/service_provider.rb
|
47
|
+
- lib/dtr/svn.rb
|
51
48
|
- lib/dtr.rb
|
52
|
-
- test/
|
53
|
-
- test/
|
54
|
-
- test/
|
55
|
-
- test/ruby_ext_test.rb
|
56
|
-
- test/ruby_runner_test.rb
|
57
|
-
- test/scenario_setup_and_run_tests_simply.rb
|
58
|
-
- test/server_test.rb
|
49
|
+
- test/inject_test.rb
|
50
|
+
- test/scenario_tests.rb
|
51
|
+
- test/svn_test.rb
|
59
52
|
- test/test_helper.rb
|
60
53
|
- doc/jamis.rb
|
61
54
|
test_files: []
|
data/lib/dtr/base.rb
DELETED
@@ -1,281 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
require 'dtr/command_line'
|
3
|
-
|
4
|
-
module DTR
|
5
|
-
class AveragePacker
|
6
|
-
def pack(test_files, num_packets)
|
7
|
-
raise 'No test files given' if test_files.nil? || test_files.empty?
|
8
|
-
@packets = Array.new(num_packets) {[]}
|
9
|
-
test_files.each_with_index {|tf,i| @packets[i%num_packets] << tf }
|
10
|
-
|
11
|
-
@packets
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class RubyRunner
|
16
|
-
def run(server, client, test_files, signature)
|
17
|
-
test_files.each do |test_file|
|
18
|
-
logger.info { "\tRun #{test_file}..." }
|
19
|
-
unless File.exist?(test_file.untaint)
|
20
|
-
logger.info {"\tSkip test file not exists: #{test_file}"}
|
21
|
-
next
|
22
|
-
end
|
23
|
-
|
24
|
-
server_reports = server.reports
|
25
|
-
break unless server_reports # if can't get reports, server maybe shutdown
|
26
|
-
|
27
|
-
if server_reports[test_file]
|
28
|
-
logger.info {"\tSkip test file has report on server: #{test_file}"}
|
29
|
-
next
|
30
|
-
end
|
31
|
-
result = client.execute("ruby #{test_file}")
|
32
|
-
tests, assertions, failures, errors = parse(result[:stdout])
|
33
|
-
report = TestReport.new(test_file, result, tests, assertions, failures, errors)
|
34
|
-
|
35
|
-
logger.info { report.to_yaml } unless report.succeeded?
|
36
|
-
|
37
|
-
break unless server.update signature, report
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
SUMMARY_REGEX = /^\s*(\d+) tests, (\d+) assertions, (\d+) failures, (\d+) errors$/
|
42
|
-
def parse(stdout)
|
43
|
-
summary = stdout.strip.split("\n").reverse.find do |line|
|
44
|
-
SUMMARY_REGEX =~ line.strip
|
45
|
-
end
|
46
|
-
if SUMMARY_REGEX =~ summary
|
47
|
-
[$1, $2, $3, $4].collect{|str| str.to_i}
|
48
|
-
else
|
49
|
-
logger.info {"\tStdout has no tests summary line: #{stdout}"}
|
50
|
-
[0, 0, 0, 0]
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
class Server
|
56
|
-
|
57
|
-
class NoAliveClientsError < StandardError
|
58
|
-
end
|
59
|
-
|
60
|
-
attr_accessor :server_signature, :test_files, :packer, :runner_name, :latest_build_time
|
61
|
-
|
62
|
-
def initialize(test_files=[])
|
63
|
-
@test_files = test_files
|
64
|
-
@packer = AveragePacker.new
|
65
|
-
@runner_name = RubyRunner.name
|
66
|
-
@server_signature = Object.new.to_s
|
67
|
-
@report_signature = nil
|
68
|
-
@raise_on_no_alive_client = DTROPTIONS[:raise_on_no_alive_client]
|
69
|
-
end
|
70
|
-
|
71
|
-
def latest_build_summary
|
72
|
-
raise 'Server never ran!' unless latest_build_time
|
73
|
-
%{Finished in #{format('%.1f', latest_build_time)} seconds.
|
74
|
-
|
75
|
-
#{collect_report(:tests)}, #{collect_report(:assertions)}, #{collect_report(:failures)}, #{collect_report(:errors)}}
|
76
|
-
end
|
77
|
-
|
78
|
-
def clients
|
79
|
-
clients_map.values
|
80
|
-
end
|
81
|
-
|
82
|
-
def available_clients
|
83
|
-
alive_clients = self.clients.select{|client| client.server_signature == @server_signature}
|
84
|
-
raise NoAliveClientsError.new if @raise_on_no_alive_client && alive_clients.empty?
|
85
|
-
alive_clients.select{|client| client.idle?}
|
86
|
-
end
|
87
|
-
|
88
|
-
def succeeded?
|
89
|
-
complete? && !reports.empty? && reports.values.all?{|report| report.succeeded?}
|
90
|
-
end
|
91
|
-
|
92
|
-
def complete?
|
93
|
-
@complete ||= false
|
94
|
-
end
|
95
|
-
|
96
|
-
def run(opt={})
|
97
|
-
opt = {:wait_for_reports => true, :setup => false}.merge opt
|
98
|
-
run_start(opt)
|
99
|
-
@run_once = false
|
100
|
-
loop do
|
101
|
-
break if complete?
|
102
|
-
run_once unless @run_once
|
103
|
-
@run_once = DTROPTIONS[:packing_once]
|
104
|
-
break if !opt[:wait_for_reports] || complete?
|
105
|
-
sleep(DTROPTIONS[:wait_a_moment]) if DTROPTIONS[:wait_a_moment]
|
106
|
-
break if complete?
|
107
|
-
end
|
108
|
-
ensure
|
109
|
-
@report_signature = nil if opt[:wait_for_reports]
|
110
|
-
self.latest_build_time = Time.now - @build_start_time
|
111
|
-
end
|
112
|
-
|
113
|
-
def reports
|
114
|
-
@reports ||= {}
|
115
|
-
end
|
116
|
-
|
117
|
-
def client_setup_logs
|
118
|
-
@client_setup_logs ||= {}
|
119
|
-
end
|
120
|
-
|
121
|
-
def add_client(client)
|
122
|
-
clients_map[client.name] = client
|
123
|
-
client_setup_logs[client.name] = nil
|
124
|
-
end
|
125
|
-
|
126
|
-
def update_setup_log(signature, report)
|
127
|
-
with_valid_signature(signature) do
|
128
|
-
client_setup_logs[report[:client_name]] = report
|
129
|
-
if report[:exit_code] != 0
|
130
|
-
clients_map.delete(report[:client_name])
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def update(signature, report)
|
136
|
-
with_valid_signature(signature) do
|
137
|
-
self.reports[report.test] = report
|
138
|
-
@report_signature = nil if @complete = self.reports.keys.size == @test_files.size
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
private
|
143
|
-
def run_start(opt)
|
144
|
-
@build_start_time = Time.now
|
145
|
-
if test_files.nil? || test_files.empty?
|
146
|
-
@complete = true
|
147
|
-
return
|
148
|
-
end
|
149
|
-
@report_signature = @build_start_time.to_s
|
150
|
-
@setup_clients = opt[:setup] ? [] : nil
|
151
|
-
end
|
152
|
-
|
153
|
-
def run_once
|
154
|
-
clients = available_clients
|
155
|
-
|
156
|
-
logger.debug { "run tests on #{clients.collect{|c|c.name}.join(', ')}"} unless clients.empty?
|
157
|
-
no_report_tests = test_files - reports.keys
|
158
|
-
return if clients.empty? || no_report_tests.empty?
|
159
|
-
|
160
|
-
packets = packer.pack no_report_tests, clients.size
|
161
|
-
clients.each do |client|
|
162
|
-
if @setup_clients && !@setup_clients.include?(client)
|
163
|
-
@setup_clients << client
|
164
|
-
client.setup(@report_signature)
|
165
|
-
end
|
166
|
-
client.run self.runner_name, packets.pop, @report_signature
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
def with_valid_signature(signature)
|
171
|
-
if !@report_signature.nil? && signature == @report_signature
|
172
|
-
yield
|
173
|
-
true
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def collect_report(type)
|
178
|
-
"#{reports.values.collect{|r| r.send(type)}.inject(0){|r, i| r += i}} #{type}"
|
179
|
-
end
|
180
|
-
|
181
|
-
def clients_map
|
182
|
-
@clients_map ||= {}
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
class Client
|
187
|
-
|
188
|
-
RUNNERS = {RubyRunner.name => RubyRunner.new}
|
189
|
-
|
190
|
-
attr_reader :name
|
191
|
-
|
192
|
-
def initialize(server, name, setup_cmd=DTROPTIONS[:setup])
|
193
|
-
@server = server
|
194
|
-
@name = name
|
195
|
-
@executing_cmd = nil
|
196
|
-
@tmp_dir = DTROPTIONS[:tmp_dir]
|
197
|
-
@setup_cmd = setup_cmd
|
198
|
-
end
|
199
|
-
|
200
|
-
def idle?
|
201
|
-
@executing_cmd.nil?
|
202
|
-
end
|
203
|
-
|
204
|
-
def run(runner_name, test_files, signature)
|
205
|
-
RUNNERS[runner_name].run(@server, self, test_files, signature)
|
206
|
-
end
|
207
|
-
|
208
|
-
def setup(signature)
|
209
|
-
result = execute(@setup_cmd)
|
210
|
-
@server.update_setup_log(signature, result)
|
211
|
-
result
|
212
|
-
end
|
213
|
-
|
214
|
-
def server_signature
|
215
|
-
@server.server_signature
|
216
|
-
end
|
217
|
-
|
218
|
-
def completed_cmds
|
219
|
-
@completed_cmds ||= []
|
220
|
-
end
|
221
|
-
|
222
|
-
def execute(cmd)
|
223
|
-
@executing_cmd = cmd
|
224
|
-
|
225
|
-
outputs = {:stdout => "#{@tmp_dir}/execution_stdout.log", :stderr => "#{@tmp_dir}/execution_stderr.log"}
|
226
|
-
outputs.values.each do |log_file|
|
227
|
-
File.delete log_file if File.exist? log_file
|
228
|
-
end
|
229
|
-
|
230
|
-
begin
|
231
|
-
CommandLine.execute cmd, outputs
|
232
|
-
exit_code = 0
|
233
|
-
rescue CommandLine::ExecutionError => e
|
234
|
-
exit_code = e.exitstatus
|
235
|
-
end
|
236
|
-
|
237
|
-
outputs.each_key do |key|
|
238
|
-
outputs[key] = File.open(outputs[key], 'r') {|f| f.read}
|
239
|
-
end
|
240
|
-
|
241
|
-
self.completed_cmds << cmd
|
242
|
-
@executing_cmd = nil
|
243
|
-
{:client_name => @name, :stdout => outputs[:stdout], :stderr => outputs[:stderr], :exit_code => exit_code}
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
class TestReport
|
248
|
-
attr_reader :test, :stdout, :stderr, :exit_code, :client_name, :tests, :assertions, :failures, :errors
|
249
|
-
def initialize(test, execution_result, tests, assertions, failures, errors)
|
250
|
-
@test = test
|
251
|
-
@client_name = execution_result[:client_name]
|
252
|
-
@stdout = execution_result[:stdout]
|
253
|
-
@stderr = execution_result[:stderr]
|
254
|
-
@exit_code = execution_result[:exit_code]
|
255
|
-
@tests = tests
|
256
|
-
@assertions = assertions
|
257
|
-
@failures = failures
|
258
|
-
@errors = errors
|
259
|
-
end
|
260
|
-
|
261
|
-
def succeeded?
|
262
|
-
@failures == 0 && @errors == 0
|
263
|
-
end
|
264
|
-
|
265
|
-
def failed?
|
266
|
-
@failures > 0
|
267
|
-
end
|
268
|
-
|
269
|
-
def error?
|
270
|
-
@errors > 0
|
271
|
-
end
|
272
|
-
|
273
|
-
def ==(another)
|
274
|
-
test == another.test
|
275
|
-
end
|
276
|
-
|
277
|
-
def successes
|
278
|
-
tests - failures - errors
|
279
|
-
end
|
280
|
-
end
|
281
|
-
end
|
data/lib/dtr/command_line.rb
DELETED
@@ -1,173 +0,0 @@
|
|
1
|
-
# borrowed (with modifications) from the RSCM project
|
2
|
-
require 'rbconfig'
|
3
|
-
module Platform
|
4
|
-
def family
|
5
|
-
target_os = Config::CONFIG["target_os"] or raise 'Cannot determine operating system'
|
6
|
-
case target_os
|
7
|
-
when /linux/, /Linux/ then 'linux'
|
8
|
-
when /32/ then 'mswin32'
|
9
|
-
when /darwin/ then 'powerpc-darwin'
|
10
|
-
when /cyg/ then 'cygwin'
|
11
|
-
when /solaris/ then 'solaris'
|
12
|
-
when /freebsd/, /netbsd/ then 'bsd'
|
13
|
-
else raise "Unknown OS: #{target_os}"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
module_function :family
|
17
|
-
|
18
|
-
def user
|
19
|
-
family == "mswin32" ? ENV['USERNAME'] : ENV['USER']
|
20
|
-
end
|
21
|
-
module_function :user
|
22
|
-
|
23
|
-
def prompt(dir=Dir.pwd)
|
24
|
-
prompt = "#{dir.gsub(/\//, File::SEPARATOR)} #{user}$"
|
25
|
-
end
|
26
|
-
module_function :prompt
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
module CommandLine
|
31
|
-
QUOTE_REPLACEMENT = (Platform.family == "mswin32") ? "\"" : "\\\""
|
32
|
-
LESS_THAN_REPLACEMENT = (Platform.family == "mswin32") ? "<" : "\\<"
|
33
|
-
class OptionError < StandardError; end
|
34
|
-
class ExecutionError < StandardError
|
35
|
-
attr_reader :cmd, :dir, :exitstatus, :stderr
|
36
|
-
def initialize(cmd, full_cmd, dir, exitstatus, stderr)
|
37
|
-
@cmd, @full_cmd, @dir, @exitstatus, @stderr = cmd, full_cmd, dir, exitstatus, stderr
|
38
|
-
end
|
39
|
-
def to_s
|
40
|
-
"\ndir : #{@dir}\n" +
|
41
|
-
"command : #{@cmd}\n" +
|
42
|
-
"executed command : #{@full_cmd}\n" +
|
43
|
-
"exitstatus: #{@exitstatus}\n" +
|
44
|
-
"STDERR TAIL START\n#{@stderr}\nSTDERR TAIL END\n"
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Executes +cmd+.
|
49
|
-
# If the +:stdout+ and +:stderr+ options are specified, a line consisting
|
50
|
-
# of a prompt (including +cmd+) will be appended to the respective output streams will be appended
|
51
|
-
# to those files, followed by the output itself. Example:
|
52
|
-
#
|
53
|
-
# CommandLine.execute("echo hello world", {:stdout => "stdout.log", :stderr => "stderr.log"})
|
54
|
-
#
|
55
|
-
# will result in the following being written to stdout.log:
|
56
|
-
#
|
57
|
-
# /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
|
58
|
-
# hello world
|
59
|
-
#
|
60
|
-
# -and to stderr.log:
|
61
|
-
# /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
|
62
|
-
#
|
63
|
-
# If a block is passed, the stdout io will be yielded to it (as with IO.popen). In this case the output
|
64
|
-
# will not be written to the stdout file (even if it's specified):
|
65
|
-
#
|
66
|
-
# /Users/aslakhellesoy/scm/buildpatterns/repos/damagecontrol/trunk aslakhellesoy$ echo hello world
|
67
|
-
# [output captured and therefore not logged]
|
68
|
-
#
|
69
|
-
# If the exitstatus of the command is different from the value specified by the +:exitstatus+ option
|
70
|
-
# (which defaults to 0) then an ExecutionError is raised, its message containing the last 400 bytes of stderr
|
71
|
-
# (provided +:stderr+ was specified)
|
72
|
-
#
|
73
|
-
# You can also specify the +:dir+ option, which will cause the command to be executed in that directory
|
74
|
-
# (default is current directory).
|
75
|
-
#
|
76
|
-
# You can also specify a hash of environment variables in +:env+, which will add additional environment variables
|
77
|
-
# to the default environment.
|
78
|
-
#
|
79
|
-
# Finally, you can specify several commands within one by separating them with '&&' (as you would in a shell).
|
80
|
-
# This will result in several lines to be appended to the log (as if you had executed the commands separately).
|
81
|
-
#
|
82
|
-
# See the unit test for more examples.
|
83
|
-
def execute(cmd, options={}, &proc)
|
84
|
-
raise "Can't have newline in cmd" if cmd =~ /\n/
|
85
|
-
options = {
|
86
|
-
:dir => Dir.pwd.untaint,
|
87
|
-
:env => {},
|
88
|
-
:mode => 'r',
|
89
|
-
:exitstatus => 0
|
90
|
-
}.merge(options)
|
91
|
-
|
92
|
-
options[:stdout] = %{"#{File.expand_path(options[:stdout])}"}.untaint if options[:stdout]
|
93
|
-
options[:stderr] = %{"#{File.expand_path(options[:stderr])}"}.untaint if options[:stderr]
|
94
|
-
|
95
|
-
|
96
|
-
if options[:dir].nil?
|
97
|
-
e(cmd, options, &proc)
|
98
|
-
else
|
99
|
-
Dir.chdir(options[:dir]) do
|
100
|
-
e(cmd, options, &proc)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
module_function :execute
|
105
|
-
|
106
|
-
private
|
107
|
-
|
108
|
-
def full_cmd(cmd, options, &proc)
|
109
|
-
commands = cmd.split("&&").collect{|c| c.strip}
|
110
|
-
stdout_opt = options[:stdout] ? ">> #{options[:stdout]}" : ""
|
111
|
-
stderr_opt = options[:stderr] ? "2>> #{options[:stderr]}" : ""
|
112
|
-
capture_info_command = (block_given? && options[:stdout])? "echo [output captured and therefore not logged] >> #{options[:stdout]} && " : ""
|
113
|
-
|
114
|
-
full_cmd = commands.collect do |c|
|
115
|
-
escaped_command = c.gsub(/"/, QUOTE_REPLACEMENT).gsub(/</, LESS_THAN_REPLACEMENT)
|
116
|
-
stdout_prompt_command = options[:stdout] ? "echo #{Platform.prompt} #{escaped_command} >> #{options[:stdout]} && " : ""
|
117
|
-
stderr_prompt_command = options[:stderr] ? "echo #{Platform.prompt} #{escaped_command} >> #{options[:stderr]} && " : ""
|
118
|
-
redirected_command = block_given? ? "#{c} #{stderr_opt}" : "#{c} #{stdout_opt} #{stderr_opt}"
|
119
|
-
|
120
|
-
stdout_prompt_command + capture_info_command + stderr_prompt_command + redirected_command
|
121
|
-
end.join(" && ")
|
122
|
-
full_cmd.untaint
|
123
|
-
end
|
124
|
-
module_function :full_cmd
|
125
|
-
|
126
|
-
def verify_exit_code(cmd, full_cmd, options)
|
127
|
-
if($?.exitstatus != options[:exitstatus])
|
128
|
-
error_message = "#{options[:stderr]} doesn't exist"
|
129
|
-
if options[:stderr] && File.exist?(options[:stderr])
|
130
|
-
File.open(options[:stderr]) do |errio|
|
131
|
-
begin
|
132
|
-
errio.seek(-1200, IO::SEEK_END)
|
133
|
-
rescue Errno::EINVAL
|
134
|
-
# ignore - it just means we didn't have 400 bytes.
|
135
|
-
end
|
136
|
-
error_message = errio.read
|
137
|
-
end
|
138
|
-
end
|
139
|
-
raise ExecutionError.new(cmd, full_cmd, options[:dir] || Dir.pwd, $?.exitstatus, error_message)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
module_function :verify_exit_code
|
143
|
-
|
144
|
-
def e(cmd, options, &proc)
|
145
|
-
full_cmd = full_cmd(cmd, options, &proc)
|
146
|
-
|
147
|
-
options[:env].each{|k,v| ENV[k]=v}
|
148
|
-
begin
|
149
|
-
STDOUT.puts "#{Platform.prompt} #{cmd}" if options[:stdout].nil?
|
150
|
-
IO.popen(full_cmd, options[:mode]) do |io|
|
151
|
-
if(block_given?)
|
152
|
-
proc.call(io)
|
153
|
-
else
|
154
|
-
io.each_line do |line|
|
155
|
-
STDOUT.puts line if options[:stdout].nil?
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
rescue Errno::ENOENT => e
|
160
|
-
unless options[:stderr].nil?
|
161
|
-
File.open(options[:stderr], "a") {|io| io.write(e.message)}
|
162
|
-
else
|
163
|
-
STDERR.puts e.message
|
164
|
-
STDERR.puts e.backtrace.join("\n")
|
165
|
-
end
|
166
|
-
raise ExecutionError.new(cmd, full_cmd, options[:dir] || Dir.pwd.untaint, nil, e.message)
|
167
|
-
ensure
|
168
|
-
verify_exit_code(cmd, full_cmd, options)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
module_function :e
|
172
|
-
|
173
|
-
end
|