autobuild 1.17.0 → 1.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +107 -0
  3. data/.travis.yml +3 -2
  4. data/Gemfile +2 -1
  5. data/Rakefile +1 -4
  6. data/autobuild.gemspec +18 -13
  7. data/bin/autobuild +4 -3
  8. data/lib/autobuild.rb +4 -5
  9. data/lib/autobuild/build_logfile.rb +6 -4
  10. data/lib/autobuild/config.rb +104 -41
  11. data/lib/autobuild/configurable.rb +32 -18
  12. data/lib/autobuild/environment.rb +126 -120
  13. data/lib/autobuild/exceptions.rb +48 -31
  14. data/lib/autobuild/import/archive.rb +134 -82
  15. data/lib/autobuild/import/cvs.rb +28 -24
  16. data/lib/autobuild/import/darcs.rb +13 -16
  17. data/lib/autobuild/import/git-lfs.rb +37 -30
  18. data/lib/autobuild/import/git.rb +246 -182
  19. data/lib/autobuild/import/hg.rb +23 -18
  20. data/lib/autobuild/import/svn.rb +48 -29
  21. data/lib/autobuild/importer.rb +534 -499
  22. data/lib/autobuild/mail_reporter.rb +77 -77
  23. data/lib/autobuild/package.rb +200 -122
  24. data/lib/autobuild/packages/autotools.rb +47 -42
  25. data/lib/autobuild/packages/cmake.rb +77 -65
  26. data/lib/autobuild/packages/dummy.rb +9 -8
  27. data/lib/autobuild/packages/genom.rb +1 -1
  28. data/lib/autobuild/packages/gnumake.rb +74 -31
  29. data/lib/autobuild/packages/import.rb +2 -6
  30. data/lib/autobuild/packages/orogen.rb +32 -31
  31. data/lib/autobuild/packages/pkgconfig.rb +2 -2
  32. data/lib/autobuild/packages/python.rb +12 -8
  33. data/lib/autobuild/packages/ruby.rb +22 -17
  34. data/lib/autobuild/parallel.rb +50 -46
  35. data/lib/autobuild/pkgconfig.rb +25 -13
  36. data/lib/autobuild/progress_display.rb +149 -64
  37. data/lib/autobuild/rake_task_extension.rb +12 -7
  38. data/lib/autobuild/reporting.rb +51 -26
  39. data/lib/autobuild/subcommand.rb +72 -65
  40. data/lib/autobuild/test.rb +9 -7
  41. data/lib/autobuild/test_utility.rb +12 -10
  42. data/lib/autobuild/timestamps.rb +28 -23
  43. data/lib/autobuild/tools.rb +17 -16
  44. data/lib/autobuild/utility.rb +67 -23
  45. data/lib/autobuild/version.rb +1 -1
  46. metadata +53 -37
@@ -1,19 +1,24 @@
1
1
  module Autobuild
2
2
  module RakeTaskExtension
3
3
  def already_invoked?
4
- !!@already_invoked
4
+ @already_invoked
5
5
  end
6
6
 
7
- def already_invoked=(value)
8
- @already_invoked = value
7
+ attr_writer :already_invoked
8
+
9
+ def disabled?
10
+ @disabled
11
+ end
12
+
13
+ def disabled!
14
+ disable
9
15
  end
10
16
 
11
- def disable!
12
- @already_invoked = true
13
- def self.needed?; false end
17
+ def disable
18
+ @disabled = true
14
19
  end
15
20
  end
16
21
  end
17
- class Rake::Task
22
+ class Rake::Task # rubocop:disable Style/ClassAndModuleChildren
18
23
  include Autobuild::RakeTaskExtension
19
24
  end
@@ -16,6 +16,7 @@ def color=(flag)
16
16
  def color?
17
17
  @colorizer.enabled?
18
18
  end
19
+
19
20
  def color(message, *style)
20
21
  @colorizer.decorate(message, *style)
21
22
  end
@@ -31,6 +32,7 @@ class << self
31
32
  def silent?
32
33
  @display.silent?
33
34
  end
35
+
34
36
  def silent=(flag)
35
37
  @display.silent = flag
36
38
  end
@@ -45,10 +47,23 @@ def self.progress_display_enabled?
45
47
  @display.progress_enabled?
46
48
  end
47
49
 
50
+ def self.progress_display_synchronize(&block)
51
+ @display.synchronize(&block)
52
+ end
53
+
54
+ # @deprecated use {progress_display_mode=} instead
48
55
  def self.progress_display_enabled=(value)
49
56
  @display.progress_enabled = value
50
57
  end
51
58
 
59
+ def self.progress_display_mode=(value)
60
+ @display.progress_mode = value
61
+ end
62
+
63
+ def self.progress_display_period=(value)
64
+ @display.progress_period = value
65
+ end
66
+
52
67
  def self.message(*args, **options)
53
68
  @display.message(*args, **options)
54
69
  end
@@ -80,7 +95,7 @@ def self.progress_done(key, display_last = true, message: nil)
80
95
  #
81
96
  # It does not use a logging framework like Log4r, but it should ;-)
82
97
  module Reporting
83
- @@reporters = Array.new
98
+ @reporters = Array.new
84
99
 
85
100
  ## Run a block and report known exception
86
101
  # If an exception is fatal, the program is terminated using exit()
@@ -89,18 +104,22 @@ def self.report(on_package_failures: default_report_on_package_failures)
89
104
  rescue Interrupt => e
90
105
  interrupted = e
91
106
  rescue Autobuild::Exception => e
92
- return report_finish_on_error([e], on_package_failures: on_package_failures, interrupted_by: interrupted)
107
+ return report_finish_on_error([e],
108
+ on_package_failures: on_package_failures,
109
+ interrupted_by: interrupted)
93
110
  end
94
111
 
95
112
  # If ignore_erorrs is true, check if some packages have failed
96
113
  # on the way. If so, raise an exception to inform the user about
97
114
  # it
98
115
  errors = []
99
- Autobuild::Package.each do |name, pkg|
116
+ Autobuild::Package.each do |_name, pkg|
100
117
  errors.concat(pkg.failures)
101
118
  end
102
119
 
103
- report_finish_on_error(errors, on_package_failures: on_package_failures, interrupted_by: interrupted)
120
+ report_finish_on_error(errors,
121
+ on_package_failures: on_package_failures,
122
+ interrupted_by: interrupted)
104
123
  end
105
124
 
106
125
  # @api private
@@ -121,15 +140,18 @@ def self.default_report_on_package_failures
121
140
  #
122
141
  # @param [Symbol] on_package_failures how does the reporting should behave.
123
142
  #
124
- def self.report_finish_on_error(errors, on_package_failures: default_report_on_package_failures, interrupted_by: nil)
125
- if not_package_error = errors.find { |e| !e.respond_to?(:fatal?) }
143
+ def self.report_finish_on_error(errors,
144
+ on_package_failures: default_report_on_package_failures, interrupted_by: nil)
145
+ if (not_package_error = errors.find { |e| !e.respond_to?(:fatal?) })
126
146
  raise not_package_error
127
147
  end
128
- if ![:raise, :report_silent, :exit_silent].include?(on_package_failures)
148
+
149
+ unless %i[raise report_silent exit_silent].include?(on_package_failures)
129
150
  errors.each { |e| error(e) }
130
151
  end
152
+
131
153
  fatal = errors.any?(&:fatal?)
132
- if !fatal
154
+ unless fatal
133
155
  if interrupted_by
134
156
  raise interrupted_by
135
157
  else
@@ -138,30 +160,29 @@ def self.report_finish_on_error(errors, on_package_failures: default_report_on_p
138
160
  end
139
161
 
140
162
  if on_package_failures == :raise
141
- if interrupted_by
142
- raise interrupted_by
143
- end
163
+ raise interrupted_by if interrupted_by
144
164
 
145
165
  e = if errors.size == 1 then errors.first
146
- else CompositeException.new(errors)
147
- end
166
+ else CompositeException.new(errors)
167
+ end
148
168
  raise e
149
- elsif [:report_silent, :report].include?(on_package_failures)
169
+ elsif %i[report_silent report].include?(on_package_failures)
150
170
  if interrupted_by
151
171
  raise interrupted_by
152
172
  else
153
173
  return errors
154
174
  end
155
- elsif [:exit, :exit_silent].include?(on_package_failures)
175
+ elsif %i[exit exit_silent].include?(on_package_failures)
156
176
  exit 1
157
177
  else
158
- raise ArgumentError, "unexpected value for on_package_failures: #{on_package_failures}"
178
+ raise ArgumentError, "unexpected value for on_package_failures: "\
179
+ "#{on_package_failures}"
159
180
  end
160
181
  end
161
182
 
162
183
  ## Reports a successful build to the user
163
184
  def self.success
164
- each_reporter { |rep| rep.success }
185
+ each_reporter(&:success)
165
186
  end
166
187
 
167
188
  ## Reports that the build failed to the user
@@ -171,19 +192,19 @@ def self.error(error)
171
192
 
172
193
  ## Add a new reporter
173
194
  def self.<<(reporter)
174
- @@reporters << reporter
195
+ @reporters << reporter
175
196
  end
176
197
 
177
198
  def self.remove(reporter)
178
- @@reporters.delete(reporter)
199
+ @reporters.delete(reporter)
179
200
  end
180
201
 
181
202
  def self.clear_reporters
182
- @@reporters.clear
203
+ @reporters.clear
183
204
  end
184
205
 
185
206
  def self.each_reporter(&iter)
186
- @@reporters.each(&iter)
207
+ @reporters.each(&iter)
187
208
  end
188
209
 
189
210
  ## Iterate on all log files
@@ -195,6 +216,7 @@ def self.each_log(&block)
195
216
  ## Base class for reporters
196
217
  class Reporter
197
218
  def error(error); end
219
+
198
220
  def success; end
199
221
  end
200
222
 
@@ -203,11 +225,10 @@ class StdoutReporter < Reporter
203
225
  def error(error)
204
226
  STDERR.puts "Build failed: #{error}"
205
227
  end
228
+
206
229
  def success
207
230
  puts "Build finished successfully at #{Time.now}"
208
- if Autobuild.post_success_message
209
- puts Autobuild.post_success_message
210
- end
231
+ puts Autobuild.post_success_message if Autobuild.post_success_message
211
232
  end
212
233
  end
213
234
 
@@ -216,12 +237,16 @@ def success
216
237
  [1_000_000.0, "M"],
217
238
  [1_000.0, "k"],
218
239
  [1.0, ""]
219
- ]
240
+ ].freeze
220
241
 
221
242
  def self.human_readable_size(size)
222
243
  HUMAN_READABLE_SIZES.each do |scale, name|
223
244
  scaled_size = (size / scale)
224
- return format("%3.1f%s", scaled_size, name) if scaled_size > 1
245
+ if scaled_size > 1
246
+ return format("%3.1<scaled>f%<scale_name>s",
247
+ scaled: scaled_size,
248
+ scale_name: name)
249
+ end
225
250
  end
226
251
  end
227
252
  end
@@ -2,6 +2,7 @@
2
2
  require 'autobuild/exceptions'
3
3
  require 'autobuild/reporting'
4
4
  require 'fcntl'
5
+ require 'English'
5
6
 
6
7
  module Autobuild
7
8
  @logfiles = Set.new
@@ -24,9 +25,11 @@ def self.registered_logfile?(logfile)
24
25
  def self.statistics
25
26
  @statistics
26
27
  end
28
+
27
29
  def self.reset_statistics
28
30
  @statistics = Hash.new
29
31
  end
32
+
30
33
  def self.add_stat(package, phase, duration)
31
34
  if !@statistics[package]
32
35
  @statistics[package] = { phase => duration }
@@ -36,6 +39,7 @@ def self.add_stat(package, phase, duration)
36
39
  @statistics[package][phase] += duration
37
40
  end
38
41
  end
42
+
39
43
  reset_statistics
40
44
 
41
45
  @parallel_build_level = nil
@@ -52,9 +56,8 @@ class << self
52
56
  def displayed_error_line_count=(value)
53
57
  @displayed_error_line_count = validate_displayed_error_line_count(value)
54
58
  end
55
- def displayed_error_line_count
56
- @displayed_error_line_count
57
- end
59
+
60
+ attr_reader :displayed_error_line_count
58
61
 
59
62
  # Returns the number of processes that can run in parallel during the
60
63
  # build. This is a system-wide value that can be overriden in a
@@ -78,13 +81,13 @@ def parallel_build_level
78
81
 
79
82
  # Returns the number of CPUs present on this system
80
83
  def self.autodetect_processor_count
81
- if @processor_count
82
- return @processor_count
83
- end
84
+ return @processor_count if @processor_count
84
85
 
85
86
  if File.file?('/proc/cpuinfo')
86
87
  cpuinfo = File.readlines('/proc/cpuinfo')
87
- physical_ids, core_count, processor_ids = [], [], []
88
+ physical_ids = []
89
+ core_count = []
90
+ processor_ids = []
88
91
  cpuinfo.each do |line|
89
92
  case line
90
93
  when /^processor\s+:\s+(\d+)$/
@@ -99,7 +102,10 @@ def self.autodetect_processor_count
99
102
  # Try to count the number of physical cores, not the number of
100
103
  # logical ones. If the info is not available, fallback to the
101
104
  # logical count
102
- if (physical_ids.size == core_count.size) && (physical_ids.size == processor_ids.size)
105
+ has_consistent_info =
106
+ (physical_ids.size == core_count.size) &&
107
+ (physical_ids.size == processor_ids.size)
108
+ if has_consistent_info
103
109
  info = Array.new
104
110
  while (id = physical_ids.shift)
105
111
  info[id] = core_count.shift
@@ -112,18 +118,17 @@ def self.autodetect_processor_count
112
118
  result = Open3.popen3("sysctl", "-n", "hw.ncpu") do |_, io, _|
113
119
  io.read
114
120
  end
115
- if !result.empty?
116
- @processor_count = Integer(result.chomp.strip)
117
- end
121
+ @processor_count = Integer(result.chomp.strip) unless result.empty?
118
122
  end
119
123
 
120
124
  # The format of the cpuinfo file is ... let's say not very standardized.
121
125
  # If the cpuinfo detection fails, inform the user and set it to 1
122
- if !@processor_count
126
+ unless @processor_count
123
127
  # Hug... What kind of system is it ?
124
128
  Autobuild.message "INFO: cannot autodetect the number of CPUs on this sytem"
125
129
  Autobuild.message "INFO: turning parallel builds off"
126
- Autobuild.message "INFO: you can manually set the number of parallel build processes to N"
130
+ Autobuild.message "INFO: you can manually set the number of parallel build "\
131
+ "processes to N"
127
132
  Autobuild.message "INFO: (and therefore turn this message off)"
128
133
  Autobuild.message "INFO: with"
129
134
  Autobuild.message " Autobuild.parallel_build_level = N"
@@ -135,20 +140,24 @@ def self.autodetect_processor_count
135
140
 
136
141
  def self.validate_displayed_error_line_count(lines)
137
142
  if lines == 'ALL'
138
- return Float::INFINITY
143
+ Float::INFINITY
139
144
  elsif lines.to_i > 0
140
- return lines.to_i
145
+ lines.to_i
146
+ else
147
+ raise ConfigException.new, 'Autobuild.displayed_error_line_count can only "\
148
+ "be a positive integer or \'ALL\''
141
149
  end
142
- raise ConfigException.new, 'Autobuild.displayed_error_line_count can only be a positive integer or \'ALL\''
143
150
  end
144
151
  end
145
152
 
146
-
147
- module Autobuild::Subprocess
148
- class Failed < Exception
149
- def retry?; @retry end
153
+ module Autobuild::Subprocess # rubocop:disable Style/ClassAndModuleChildren
154
+ class Failed < RuntimeError
150
155
  attr_reader :status
151
156
 
157
+ def retry?
158
+ @retry
159
+ end
160
+
152
161
  def initialize(status, do_retry)
153
162
  @status = status
154
163
  @retry = do_retry
@@ -206,7 +215,11 @@ def self.run(target, phase, *command)
206
215
  STDOUT.sync = true
207
216
 
208
217
  input_streams = []
209
- options = Hash[retry: false, env: ENV.to_hash, env_inherit: true, encoding: 'BINARY']
218
+ options = {
219
+ retry: false, encoding: 'BINARY',
220
+ env: ENV.to_hash, env_inherit: true
221
+ }
222
+
210
223
  if command.last.kind_of?(Hash)
211
224
  options = command.pop
212
225
  options = Kernel.validate_options options,
@@ -216,19 +229,15 @@ def self.run(target, phase, *command)
216
229
  env_inherit: true,
217
230
  encoding: 'BINARY'
218
231
 
219
- if options[:input]
220
- input_streams << File.open(options[:input])
221
- end
222
- if options[:input_streams]
223
- input_streams += options[:input_streams]
224
- end
232
+ input_streams << File.open(options[:input]) if options[:input]
233
+ input_streams.concat(options[:input_streams]) if options[:input_streams]
225
234
  end
226
235
 
227
236
  start_time = Time.now
228
237
 
229
238
  # Filter nil and empty? in command
230
- command.reject! { |o| o.nil? || (o.respond_to?(:empty?) && o.empty?) }
231
- command.collect! { |o| o.to_s }
239
+ command.reject! { |o| o.nil? || (o.respond_to?(:empty?) && o.empty?) }
240
+ command.collect!(&:to_s)
232
241
 
233
242
  if target.respond_to?(:name)
234
243
  target_name = target.name
@@ -246,13 +255,15 @@ def self.run(target, phase, *command)
246
255
  options[:working_directory] ||= target.working_directory
247
256
  end
248
257
 
249
- logname = File.join(logdir, "#{target_name.gsub(/[:]/,'_')}-#{phase.to_s.gsub(/[:]/,'_')}.log")
250
- if !File.directory?(File.dirname(logname))
258
+ logname = File.join(logdir, "#{target_name.gsub(/[:]/, '_')}-"\
259
+ "#{phase.to_s.gsub(/[:]/, '_')}.log")
260
+ unless File.directory?(File.dirname(logname))
251
261
  FileUtils.mkdir_p File.dirname(logname)
252
262
  end
253
263
 
254
264
  if Autobuild.verbose
255
- Autobuild.message "#{target_name}: running #{command.join(" ")}\n (output goes to #{logname})"
265
+ Autobuild.message "#{target_name}: running #{command.join(' ')}\n"\
266
+ " (output goes to #{logname})"
256
267
  end
257
268
 
258
269
  open_flag = if Autobuild.keep_oldlogs then 'a'
@@ -267,32 +278,28 @@ def self.run(target, phase, *command)
267
278
  env = options[:env].dup
268
279
  if options[:env_inherit]
269
280
  ENV.each do |k, v|
270
- if !env.has_key?(k)
271
- env[k] = v
272
- end
281
+ env[k] = v unless env.key?(k)
273
282
  end
274
283
  end
275
284
 
276
285
  status = File.open(logname, open_flag) do |logfile|
277
- if Autobuild.keep_oldlogs
278
- logfile.puts
279
- end
286
+ logfile.puts if Autobuild.keep_oldlogs
280
287
  logfile.puts
281
288
  logfile.puts "#{Time.now}: running"
282
- logfile.puts " #{command.join(" ")}"
289
+ logfile.puts " #{command.join(' ')}"
283
290
  logfile.puts "with environment:"
284
291
  env.keys.sort.each do |key|
285
- if value = env[key]
292
+ if (value = env[key])
286
293
  logfile.puts " '#{key}'='#{value}'"
287
294
  end
288
295
  end
289
296
  logfile.puts
290
297
  logfile.puts "#{Time.now}: running"
291
- logfile.puts " #{command.join(" ")}"
298
+ logfile.puts " #{command.join(' ')}"
292
299
  logfile.flush
293
300
  logfile.sync = true
294
301
 
295
- if !input_streams.empty?
302
+ unless input_streams.empty?
296
303
  pread, pwrite = IO.pipe # to feed subprocess stdin
297
304
  end
298
305
 
@@ -304,22 +311,19 @@ def self.run(target, phase, *command)
304
311
 
305
312
  if Autobuild.windows?
306
313
  Dir.chdir(options[:working_directory]) do
307
- if !system(*command)
308
- raise Failed.new($?.exitstatus, nil),
314
+ unless system(*command)
315
+ raise Failed.new($CHILD_STATUS.exitstatus, nil),
309
316
  "'#{command.join(' ')}' returned status #{status.exitstatus}"
310
317
  end
311
318
  end
312
- return
319
+ return # rubocop:disable Lint/NonLocalExitFromIterator
313
320
  end
314
321
 
315
322
  cwrite.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
316
323
 
317
324
  pid = fork do
318
325
  begin
319
- if options[:working_directory] && (options[:working_directory] != Dir.pwd)
320
- Dir.chdir(options[:working_directory])
321
- end
322
- logfile.puts "in directory #{Dir.pwd}"
326
+ logfile.puts "in directory #{options[:working_directory] || Dir.pwd}"
323
327
 
324
328
  cwrite.sync = true
325
329
  if Autobuild.nice
@@ -330,19 +334,23 @@ def self.run(target, phase, *command)
330
334
  $stderr.reopen(outwrite.dup)
331
335
  $stdout.reopen(outwrite.dup)
332
336
 
333
- if !input_streams.empty?
337
+ unless input_streams.empty?
334
338
  pwrite.close
335
339
  $stdin.reopen(pread)
336
340
  end
337
341
 
338
- exec(env, *command, close_others: false)
342
+ exec(env, *command,
343
+ chdir: options[:working_directory] || Dir.pwd,
344
+ close_others: false)
339
345
  rescue Errno::ENOENT
340
346
  cwrite.write([CONTROL_COMMAND_NOT_FOUND].pack('I'))
341
347
  exit(100)
342
348
  rescue Interrupt
343
349
  cwrite.write([CONTROL_INTERRUPT].pack('I'))
344
350
  exit(100)
345
- rescue ::Exception
351
+ rescue ::Exception => e
352
+ STDERR.puts e
353
+ STDERR.puts e.backtrace.join("\n ")
346
354
  cwrite.write([CONTROL_UNEXPECTED].pack('I'))
347
355
  exit(100)
348
356
  end
@@ -351,12 +359,14 @@ def self.run(target, phase, *command)
351
359
  readbuffer = StringIO.new
352
360
 
353
361
  # Feed the input
354
- if !input_streams.empty?
362
+ unless input_streams.empty?
355
363
  pread.close
356
364
  begin
357
365
  input_streams.each do |instream|
358
366
  instream.each_line do |line|
359
- readbuffer.write(outread.readpartial(128)) while IO.select([outread], nil, nil, 0)
367
+ while IO.select([outread], nil, nil, 0)
368
+ readbuffer.write(outread.readpartial(128))
369
+ end
360
370
  pwrite.write(line)
361
371
  end
362
372
  end
@@ -385,15 +395,13 @@ def self.run(target, phase, *command)
385
395
  end
386
396
 
387
397
  transparent_prefix = "#{target_name}:#{phase}: "
388
- if target_type
389
- transparent_prefix = "#{target_type}:#{transparent_prefix}"
390
- end
398
+ transparent_prefix = "#{target_type}:#{transparent_prefix}" if target_type
391
399
 
392
400
  # If the caller asked for process output, provide it to him
393
401
  # line-by-line.
394
402
  outwrite.close
395
403
 
396
- if !input_streams.empty?
404
+ unless input_streams.empty?
397
405
  readbuffer.write(outread.read)
398
406
  readbuffer.seek(0)
399
407
  outread.close
@@ -428,6 +436,7 @@ def self.run(target, phase, *command)
428
436
  if status.termsig == 2 # SIGINT == 2
429
437
  raise Interrupt, "subcommand #{command.join(' ')} interrupted"
430
438
  end
439
+
431
440
  if status.termsig
432
441
  raise Failed.new(status.exitstatus, nil),
433
442
  "'#{command.join(' ')}' terminated by signal #{status.termsig}"
@@ -441,21 +450,19 @@ def self.run(target, phase, *command)
441
450
  Autobuild.add_stat(target, phase, duration)
442
451
  FileUtils.mkdir_p(Autobuild.logdir)
443
452
  File.open(File.join(Autobuild.logdir, "stats.log"), 'a') do |io|
444
- formatted_time = "#{start_time.strftime('%F %H:%M:%S')}.#{'%.03i' % [start_time.tv_usec / 1000]}"
453
+ formatted_msec = format('%.03i', start_time.tv_usec / 1000)
454
+ formatted_time = "#{start_time.strftime('%F %H:%M:%S')}.#{formatted_msec}"
445
455
  io.puts "#{formatted_time} #{target_name} #{phase} #{duration}"
446
456
  end
447
- if target.respond_to?(:add_stat)
448
- target.add_stat(phase, duration)
449
- end
457
+ target.add_stat(phase, duration) if target.respond_to?(:add_stat)
450
458
  subcommand_output
451
-
452
459
  rescue Failed => e
453
- error = Autobuild::SubcommandFailed.new(target, command.join(" "), logname, e.status, subcommand_output)
460
+ error = Autobuild::SubcommandFailed.new(target, command.join(" "),
461
+ logname, e.status, subcommand_output)
454
462
  error.retry = if e.retry?.nil? then options[:retry]
455
463
  else e.retry?
456
464
  end
457
465
  error.phase = phase
458
466
  raise error, e.message
459
467
  end
460
-
461
468
  end