lib/autobuild/subcommand.rb in autobuild-1.17.0 vs lib/autobuild/subcommand.rb in autobuild-1.18.0
- old
+ new
@@ -1,9 +1,10 @@
require 'set'
require 'autobuild/exceptions'
require 'autobuild/reporting'
require 'fcntl'
+require 'English'
module Autobuild
@logfiles = Set.new
def self.clear_logfiles
@logfiles.clear
@@ -22,22 +23,25 @@
end
def self.statistics
@statistics
end
+
def self.reset_statistics
@statistics = Hash.new
end
+
def self.add_stat(package, phase, duration)
if !@statistics[package]
@statistics[package] = { phase => duration }
elsif !@statistics[package][phase]
@statistics[package][phase] = duration
else
@statistics[package][phase] += duration
end
end
+
reset_statistics
@parallel_build_level = nil
@displayed_error_line_count = 10
class << self
@@ -50,14 +54,13 @@
# this may be an integer or 'ALL' (which will be translated to -1)
# this is not using an attr_accessor to be able to validate the values
def displayed_error_line_count=(value)
@displayed_error_line_count = validate_displayed_error_line_count(value)
end
- def displayed_error_line_count
- @displayed_error_line_count
- end
+ attr_reader :displayed_error_line_count
+
# Returns the number of processes that can run in parallel during the
# build. This is a system-wide value that can be overriden in a
# per-package fashion by using Package#parallel_build_level.
#
# If not set, defaults to the number of CPUs on the system
@@ -76,17 +79,17 @@
end
end
# Returns the number of CPUs present on this system
def self.autodetect_processor_count
- if @processor_count
- return @processor_count
- end
+ return @processor_count if @processor_count
if File.file?('/proc/cpuinfo')
cpuinfo = File.readlines('/proc/cpuinfo')
- physical_ids, core_count, processor_ids = [], [], []
+ physical_ids = []
+ core_count = []
+ processor_ids = []
cpuinfo.each do |line|
case line
when /^processor\s+:\s+(\d+)$/
processor_ids << Integer($1)
when /^physical id\s+:\s+(\d+)$/
@@ -97,11 +100,14 @@
end
# Try to count the number of physical cores, not the number of
# logical ones. If the info is not available, fallback to the
# logical count
- if (physical_ids.size == core_count.size) && (physical_ids.size == processor_ids.size)
+ has_consistent_info =
+ (physical_ids.size == core_count.size) &&
+ (physical_ids.size == processor_ids.size)
+ if has_consistent_info
info = Array.new
while (id = physical_ids.shift)
info[id] = core_count.shift
end
@processor_count = info.compact.inject(&:+)
@@ -110,22 +116,21 @@
end
else
result = Open3.popen3("sysctl", "-n", "hw.ncpu") do |_, io, _|
io.read
end
- if !result.empty?
- @processor_count = Integer(result.chomp.strip)
- end
+ @processor_count = Integer(result.chomp.strip) unless result.empty?
end
# The format of the cpuinfo file is ... let's say not very standardized.
# If the cpuinfo detection fails, inform the user and set it to 1
- if !@processor_count
+ unless @processor_count
# Hug... What kind of system is it ?
Autobuild.message "INFO: cannot autodetect the number of CPUs on this sytem"
Autobuild.message "INFO: turning parallel builds off"
- Autobuild.message "INFO: you can manually set the number of parallel build processes to N"
+ Autobuild.message "INFO: you can manually set the number of parallel build "\
+ "processes to N"
Autobuild.message "INFO: (and therefore turn this message off)"
Autobuild.message "INFO: with"
Autobuild.message " Autobuild.parallel_build_level = N"
@processor_count = 1
end
@@ -133,24 +138,28 @@
@processor_count
end
def self.validate_displayed_error_line_count(lines)
if lines == 'ALL'
- return Float::INFINITY
+ Float::INFINITY
elsif lines.to_i > 0
- return lines.to_i
+ lines.to_i
+ else
+ raise ConfigException.new, 'Autobuild.displayed_error_line_count can only "\
+ "be a positive integer or \'ALL\''
end
- raise ConfigException.new, 'Autobuild.displayed_error_line_count can only be a positive integer or \'ALL\''
end
end
-
-module Autobuild::Subprocess
- class Failed < Exception
- def retry?; @retry end
+module Autobuild::Subprocess # rubocop:disable Style/ClassAndModuleChildren
+ class Failed < RuntimeError
attr_reader :status
+ def retry?
+ @retry
+ end
+
def initialize(status, do_retry)
@status = status
@retry = do_retry
end
end
@@ -204,33 +213,33 @@
# @return [String] the command standard output
def self.run(target, phase, *command)
STDOUT.sync = true
input_streams = []
- options = Hash[retry: false, env: ENV.to_hash, env_inherit: true, encoding: 'BINARY']
+ options = {
+ retry: false, encoding: 'BINARY',
+ env: ENV.to_hash, env_inherit: true
+ }
+
if command.last.kind_of?(Hash)
options = command.pop
options = Kernel.validate_options options,
input: nil, working_directory: nil, retry: false,
input_streams: [],
env: ENV.to_hash,
env_inherit: true,
encoding: 'BINARY'
- if options[:input]
- input_streams << File.open(options[:input])
- end
- if options[:input_streams]
- input_streams += options[:input_streams]
- end
+ input_streams << File.open(options[:input]) if options[:input]
+ input_streams.concat(options[:input_streams]) if options[:input_streams]
end
start_time = Time.now
# Filter nil and empty? in command
- command.reject! { |o| o.nil? || (o.respond_to?(:empty?) && o.empty?) }
- command.collect! { |o| o.to_s }
+ command.reject! { |o| o.nil? || (o.respond_to?(:empty?) && o.empty?) }
+ command.collect!(&:to_s)
if target.respond_to?(:name)
target_name = target.name
target_type = target.class
else
@@ -244,17 +253,19 @@
if target.respond_to?(:working_directory)
options[:working_directory] ||= target.working_directory
end
- logname = File.join(logdir, "#{target_name.gsub(/[:]/,'_')}-#{phase.to_s.gsub(/[:]/,'_')}.log")
- if !File.directory?(File.dirname(logname))
+ logname = File.join(logdir, "#{target_name.gsub(/[:]/, '_')}-"\
+ "#{phase.to_s.gsub(/[:]/, '_')}.log")
+ unless File.directory?(File.dirname(logname))
FileUtils.mkdir_p File.dirname(logname)
end
if Autobuild.verbose
- Autobuild.message "#{target_name}: running #{command.join(" ")}\n (output goes to #{logname})"
+ Autobuild.message "#{target_name}: running #{command.join(' ')}\n"\
+ " (output goes to #{logname})"
end
open_flag = if Autobuild.keep_oldlogs then 'a'
elsif Autobuild.registered_logfile?(logname) then 'a'
else 'w'
@@ -265,36 +276,32 @@
subcommand_output = Array.new
env = options[:env].dup
if options[:env_inherit]
ENV.each do |k, v|
- if !env.has_key?(k)
- env[k] = v
- end
+ env[k] = v unless env.key?(k)
end
end
status = File.open(logname, open_flag) do |logfile|
- if Autobuild.keep_oldlogs
- logfile.puts
- end
+ logfile.puts if Autobuild.keep_oldlogs
logfile.puts
logfile.puts "#{Time.now}: running"
- logfile.puts " #{command.join(" ")}"
+ logfile.puts " #{command.join(' ')}"
logfile.puts "with environment:"
env.keys.sort.each do |key|
- if value = env[key]
+ if (value = env[key])
logfile.puts " '#{key}'='#{value}'"
end
end
logfile.puts
logfile.puts "#{Time.now}: running"
- logfile.puts " #{command.join(" ")}"
+ logfile.puts " #{command.join(' ')}"
logfile.flush
logfile.sync = true
- if !input_streams.empty?
+ unless input_streams.empty?
pread, pwrite = IO.pipe # to feed subprocess stdin
end
outread, outwrite = IO.pipe
outread.sync = true
@@ -302,63 +309,66 @@
cread, cwrite = IO.pipe # to control that exec goes well
if Autobuild.windows?
Dir.chdir(options[:working_directory]) do
- if !system(*command)
- raise Failed.new($?.exitstatus, nil),
+ unless system(*command)
+ raise Failed.new($CHILD_STATUS.exitstatus, nil),
"'#{command.join(' ')}' returned status #{status.exitstatus}"
end
end
- return
+ return # rubocop:disable Lint/NonLocalExitFromIterator
end
cwrite.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
pid = fork do
begin
- if options[:working_directory] && (options[:working_directory] != Dir.pwd)
- Dir.chdir(options[:working_directory])
- end
- logfile.puts "in directory #{Dir.pwd}"
+ logfile.puts "in directory #{options[:working_directory] || Dir.pwd}"
cwrite.sync = true
if Autobuild.nice
Process.setpriority(Process::PRIO_PROCESS, 0, Autobuild.nice)
end
outread.close
$stderr.reopen(outwrite.dup)
$stdout.reopen(outwrite.dup)
- if !input_streams.empty?
+ unless input_streams.empty?
pwrite.close
$stdin.reopen(pread)
end
- exec(env, *command, close_others: false)
+ exec(env, *command,
+ chdir: options[:working_directory] || Dir.pwd,
+ close_others: false)
rescue Errno::ENOENT
cwrite.write([CONTROL_COMMAND_NOT_FOUND].pack('I'))
exit(100)
rescue Interrupt
cwrite.write([CONTROL_INTERRUPT].pack('I'))
exit(100)
- rescue ::Exception
+ rescue ::Exception => e
+ STDERR.puts e
+ STDERR.puts e.backtrace.join("\n ")
cwrite.write([CONTROL_UNEXPECTED].pack('I'))
exit(100)
end
end
readbuffer = StringIO.new
# Feed the input
- if !input_streams.empty?
+ unless input_streams.empty?
pread.close
begin
input_streams.each do |instream|
instream.each_line do |line|
- readbuffer.write(outread.readpartial(128)) while IO.select([outread], nil, nil, 0)
+ while IO.select([outread], nil, nil, 0)
+ readbuffer.write(outread.readpartial(128))
+ end
pwrite.write(line)
end
end
rescue Errno::ENOENT => e
raise Failed.new(nil, false),
@@ -383,19 +393,17 @@
"something unexpected happened"
end
end
transparent_prefix = "#{target_name}:#{phase}: "
- if target_type
- transparent_prefix = "#{target_type}:#{transparent_prefix}"
- end
+ transparent_prefix = "#{target_type}:#{transparent_prefix}" if target_type
# If the caller asked for process output, provide it to him
# line-by-line.
outwrite.close
- if !input_streams.empty?
+ unless input_streams.empty?
readbuffer.write(outread.read)
readbuffer.seek(0)
outread.close
outread = readbuffer
end
@@ -426,10 +434,11 @@
if !status.exitstatus || status.exitstatus > 0
if status.termsig == 2 # SIGINT == 2
raise Interrupt, "subcommand #{command.join(' ')} interrupted"
end
+
if status.termsig
raise Failed.new(status.exitstatus, nil),
"'#{command.join(' ')}' terminated by signal #{status.termsig}"
else
raise Failed.new(status.exitstatus, nil),
@@ -439,23 +448,21 @@
duration = Time.now - start_time
Autobuild.add_stat(target, phase, duration)
FileUtils.mkdir_p(Autobuild.logdir)
File.open(File.join(Autobuild.logdir, "stats.log"), 'a') do |io|
- formatted_time = "#{start_time.strftime('%F %H:%M:%S')}.#{'%.03i' % [start_time.tv_usec / 1000]}"
+ formatted_msec = format('%.03i', start_time.tv_usec / 1000)
+ formatted_time = "#{start_time.strftime('%F %H:%M:%S')}.#{formatted_msec}"
io.puts "#{formatted_time} #{target_name} #{phase} #{duration}"
end
- if target.respond_to?(:add_stat)
- target.add_stat(phase, duration)
- end
+ target.add_stat(phase, duration) if target.respond_to?(:add_stat)
subcommand_output
-
rescue Failed => e
- error = Autobuild::SubcommandFailed.new(target, command.join(" "), logname, e.status, subcommand_output)
+ error = Autobuild::SubcommandFailed.new(target, command.join(" "),
+ logname, e.status, subcommand_output)
error.retry = if e.retry?.nil? then options[:retry]
else e.retry?
end
error.phase = phase
raise error, e.message
end
-
end