require 'open3'
#require_relative('./array.rb')
#require_relative('./hash.rb')
#require_relative('./timer.rb')
BUFFER_SIZE=1024 if(!defined?(BUFFER_SIZE))
# = Command
#
# execution of system commands
#
# = Keys
#
# - :input The input of the commands.
# - :timeout The timeout in seconds.
# a value of zero is to infinite timeout.
# defaults to zero
# - :directory The working directory for the command.
# defaults to the current directory
# - :exit_code The exit code of the command
# - :output The output contains the stdout output of the command
# - :error The error contains stderr output of the command
# - :machine The name of the machine the command executed on
# - :user The user name
# - :start_time
# - :end_time
#
class Command < Hash
def initialize command
self[:input] = ''
self[:timeout] = 0
self[:directory] = ''
self[:exit_code] = 0
self[:output] = ''
self[:error] = ''
self[:machine] = ''
self[:user] = ''
self[:start_time] = nil
self[:end_time] = nil
if(command.kind_of?(String))
self[:input] = command
end
if(command.kind_of?(Hash))
command.each{|k,v|
self[k.to_sym]=v
}
end
end
def quiet?
(self.has_key?(:quiet) && self[:quiet])
end
def execute value=nil
if(!value.nil? && value.is_a?(Hash))
value.each{|k,v|self[k]=v}
end
pwd=Dir.pwd
self[:directory] = pwd if(!self.has_key?(:directory) || self[:directory].length==0)
if(self[:timeout] > 0)
puts "#{self[:input]} (#{self[:directory]}) timeout #{self[:timeout].to_s}" if(!quiet?)
else
puts "#{self[:input]} (#{self[:directory]})" if(!quiet?)
end
self[:machine] = Command.machine
self[:user] = Command.user
self[:start_time]=Time.now
timer=Timer.new
Dir.chdir(self[:directory]) do
if self[:input].include?('<%') && self[:input].include?('%>')
ruby = self[:input].gsub("<%","").gsub("%>","")
begin
self[:output]=eval(ruby)
rescue
self[:exit_code]=1
self[:error]="unable to eval(#{ruby})"
end
self[:elapsed] = timer.elapsed_str
self[:end_time] = Time.now
else
begin
if(self[:timeout] <= 0)
self[:output],self[:error],status= Open3.capture3(self[:input])
self[:exit_code]=status.to_i
self[:elapsed] = timer.elapsed_str
self[:end_time] = Time.now
else
require_relative 'timeout.rb'
#puts run_with_timeout(self[:input], self[:timeout], 1).to_s
#self[:output] = run_with_timeout(self[:input], self[:timeout], 1)
result=run_with_timeout2(self[:directory],self[:input], self[:timeout], 2)
self[:output]=result[0]
self[:error]=result[1]
self[:exit_code]=result[2]
self[:elapsed] = timer.elapsed_str
self[:end_time] = Time.now
if(timer.elapsed >= self[:timeout])
self[:exit_code]=1
self[:error] = self[:error] + "timed out"
end
end
rescue Exception => e
self[:elapsed] = timer.elapsed_str
self[:end_time] = Time.now
self[:error] = "Exception: " + e.to_s
self[:exit_code]=1
end
end
end
if(self[:exit_code] != 0)
if(!quiet?)
puts "exit_code=#{self[:exit_code]}"
puts self[:output]
puts self[:error]
end
if(!self.has_key?(:ignore_failure) || !self[:ignore_failure])
raise "#{self[:input]} failed"
end #unless (self.has_key?(:ignore_failure) && self[:ignore_failure]==true)
end
end
def self.machine
if !ENV['COMPUTERNAME'].nil?
return ENV['COMPUTERNAME']
end
machine = `hostname`
machine = machine.split('.')[0] if machine.include?('.')
return machine.strip
end
def self.user
ENV['USER'].nil? ? ENV['USERNAME'] : ENV['USER']
end
def self.home
["USERPROFILE","HOME"].each {|v|
return ENV[v].gsub('\\','/') unless ENV[v].nil?
}
dir="~"
dir=ENV["HOME"] unless ENV["HOME"].nil?
dir=ENV["USERPROFILE"].gsub('\\','/') unless ENV["USERPROFILE"].nil?
return dir
end
def self.dev_root
["DEV_HOME","DEV_ROOT"].each {|v|
return ENV[v].gsub('\\','/') unless ENV[v].nil?
}
dir=home
#dir=ENV["DEV_ROOT"].gsub('\\','/') unless ENV["DEV_ROOT"].nil?
return dir
end
def self.exit_code command
cmd = Command.new(command)
cmd[:ignore_failure]=true
cmd[:quiet]=true
cmd.execute
cmd[:exit_code]
end
def self.output command
cmd = Command.new(command)
cmd[:ignore_failure]=true
cmd[:quiet]=true
cmd.execute
cmd[:output]
end
def getFormattedTimeSpan timespan
seconds = timespan
seconds.to_s + " sec"
end
def summary
duration=""
duration=getFormattedTimeSpan(self[:end_time]-[:start_time]) + " - " if(!self[:end_time].nil?)
duration + "#{self[:exit_code].to_s} #{self[:input]} (#{self[:directory]})"
end
def to_html
if self[:exit_code] == 0
[
'
'
].join
else
[
' | ',
self[:input],
' | ',
map { |k, v| ["#{k} | ", v.respond_to?(:to_html) ? v.to_html : "#{v} | "] },
' ',
' |
|
'
].join
end
end
end
class Add < Array
def update
if(File.exists?('.git') && File.exists?('.gitignore'))
add 'git add --all'
else
if(defined?(SOURCE))
if(File.exists?('.svn'))
SOURCE.each{|f|
add "svn add #{f} --parents" if Command.output("svn status #{f}").include?('?')
add "svn add #{f} --parents" if Command.exit_code("svn status #{f}") != 0
}
end
if(File.exists?('.git'))
SOURCE.each{|f|
add "git add #{f} -v" if `git status #{f}`.include?('untracked')
}
end
end
end
end
end
class Analyze < Array
def update
if(`gem list countloc`.include?('countloc ('))
FileUtils.mkdir('doc') if(!File.exists?('doc'))
add 'countloc -r * --html doc/countloc.html'
end
end
end
class Array
def execute value=nil
i=0
while i < self.length
self[i]=Command.new(self[i]) if(self[i].is_a?(String))
self[i]=Command.new(self[i]) if(self[i].is_a?(Hash) && !self[i].is_a?(Command))
if(!value.nil? && value.is_a?(Hash))
value.each{|k,v|self[i][k]=v}
end
self[i].execute if(self[i].is_a?(Command))
i=i+1
end
end
def add command
self << command if(!include?(command))
end
def to_html
html=Array.new
html << ''
self.each{|e|
html << e.to_html if e.respond_to?(:to_html)
}
html << '
'
html.join
end
end
require 'rake'
SLN_FILES=FileList.new('*.sln','*/*.sln','*/*/*.sln')
class Build < Array
def update
changed = true
#changed = Git.has_changes? if(File.exists?('.git') && defined?(Git))
#changed = Svn.has_changes? if(File.exists?('.svn') && defined?(Svn))
if(changed)
Dir.glob('*.gemspec'){|gemspec|
add "gem build #{gemspec}" if !File.exist?(Gemspec.gemfile gemspec)
}
SLN_FILES.each{|sln_file|
vs_version=MSBuild.get_vs_version(sln_file)
if(MSBuild.has_version?(vs_version))
MSBuild.get_configurations(sln_file).each{ |configuration|
MSBuild.get_platforms(sln_file).each{|platform|
#Console.debug "configuration='#{configuration}', platform='#{platform}'"
self.add "\"#{MSBuild.get_version(vs_version)}\" \"#{sln_file}\" /nologo /p:Configuration=#{configuration} /p:Platform=\"#{platform}\""
}
}
else
"puts version #{vs_version} not found for MsBuild"
end
}
end
end
end
class Clean < Array
def update
['.yardoc','log','tmp','obj'].each{|dir|
CLEAN.include(dir) if File.exists?(dir)
}
CLEAN.include('*.{suo,sdf}')
#add '<%Rake::Task[:clean].reenable%>'
add '<%Rake::Task[:clean].invoke%>'
end
end
class Clobber < Array
def update
['bin'].each{|dir|
CLOBBER.include(dir) if File.exists?(dir)
}
clean=Clean.new
clean.update
CLOBBER.include('*.gem')
#add '<%Rake::Task[:clobber].reenable%>'
add '<%Rake::Task[:clobber].invoke%>'
end
end
require 'open3'
#require_relative('./array.rb')
#require_relative('./hash.rb')
#require_relative('./timer.rb')
BUFFER_SIZE=1024 if(!defined?(BUFFER_SIZE))
# = Command
#
# execution of system commands
#
# = Keys
#
# - :input The input of the commands.
# - :timeout The timeout in seconds.
# a value of zero is to infinite timeout.
# defaults to zero
# - :directory The working directory for the command.
# defaults to the current directory
# - :exit_code The exit code of the command
# - :output The output contains the stdout output of the command
# - :error The error contains stderr output of the command
# - :machine The name of the machine the command executed on
# - :user The user name
# - :start_time
# - :end_time
#
class Command < Hash
def initialize command
self[:input] = ''
self[:timeout] = 0
self[:directory] = ''
self[:exit_code] = 0
self[:output] = ''
self[:error] = ''
self[:machine] = ''
self[:user] = ''
self[:start_time] = nil
self[:end_time] = nil
if(command.kind_of?(String))
self[:input] = command
end
if(command.kind_of?(Hash))
command.each{|k,v|
self[k.to_sym]=v
}
end
end
def quiet?
(self.has_key?(:quiet) && self[:quiet])
end
def execute value=nil
if(!value.nil? && value.is_a?(Hash))
value.each{|k,v|self[k]=v}
end
pwd=Dir.pwd
self[:directory] = pwd if(!self.has_key?(:directory) || self[:directory].length==0)
if(self[:timeout] > 0)
puts "#{self[:input]} (#{self[:directory]}) timeout #{self[:timeout].to_s}" if(!quiet?)
else
puts "#{self[:input]} (#{self[:directory]})" if(!quiet?)
end
self[:machine] = Command.machine
self[:user] = Command.user
self[:start_time]=Time.now
timer=Timer.new
Dir.chdir(self[:directory]) do
if self[:input].include?('<%') && self[:input].include?('%>')
ruby = self[:input].gsub("<%","").gsub("%>","")
begin
self[:output]=eval(ruby)
rescue
self[:exit_code]=1
self[:error]="unable to eval(#{ruby})"
end
self[:elapsed] = timer.elapsed_str
self[:end_time] = Time.now
else
begin
if(self[:timeout] <= 0)
self[:output],self[:error],status= Open3.capture3(self[:input])
self[:exit_code]=status.to_i
self[:elapsed] = timer.elapsed_str
self[:end_time] = Time.now
else
require_relative 'timeout.rb'
#puts run_with_timeout(self[:input], self[:timeout], 1).to_s
#self[:output] = run_with_timeout(self[:input], self[:timeout], 1)
result=run_with_timeout2(self[:directory],self[:input], self[:timeout], 2)
self[:output]=result[0]
self[:error]=result[1]
self[:exit_code]=result[2]
self[:elapsed] = timer.elapsed_str
self[:end_time] = Time.now
if(timer.elapsed >= self[:timeout])
self[:exit_code]=1
self[:error] = self[:error] + "timed out"
end
end
rescue Exception => e
self[:elapsed] = timer.elapsed_str
self[:end_time] = Time.now
self[:error] = "Exception: " + e.to_s
self[:exit_code]=1
end
end
end
if(self[:exit_code] != 0)
if(!quiet?)
puts "exit_code=#{self[:exit_code]}"
puts self[:output]
puts self[:error]
end
if(!self.has_key?(:ignore_failure) || !self[:ignore_failure])
raise "#{self[:input]} failed"
end #unless (self.has_key?(:ignore_failure) && self[:ignore_failure]==true)
end
end
def self.machine
if !ENV['COMPUTERNAME'].nil?
return ENV['COMPUTERNAME']
end
machine = `hostname`
machine = machine.split('.')[0] if machine.include?('.')
return machine.strip
end
def self.user
ENV['USER'].nil? ? ENV['USERNAME'] : ENV['USER']
end
def self.home
["USERPROFILE","HOME"].each {|v|
return ENV[v].gsub('\\','/') unless ENV[v].nil?
}
dir="~"
dir=ENV["HOME"] unless ENV["HOME"].nil?
dir=ENV["USERPROFILE"].gsub('\\','/') unless ENV["USERPROFILE"].nil?
return dir
end
def self.dev_root
["DEV_HOME","DEV_ROOT"].each {|v|
return ENV[v].gsub('\\','/') unless ENV[v].nil?
}
dir=home
#dir=ENV["DEV_ROOT"].gsub('\\','/') unless ENV["DEV_ROOT"].nil?
return dir
end
def self.exit_code command
cmd = Command.new(command)
cmd[:ignore_failure]=true
cmd[:quiet]=true
cmd.execute
cmd[:exit_code]
end
def self.output command
cmd = Command.new(command)
cmd[:ignore_failure]=true
cmd[:quiet]=true
cmd.execute
cmd[:output]
end
def getFormattedTimeSpan timespan
seconds = timespan
seconds.to_s + " sec"
end
def self.execute command
cmd - Command.new(command)
cmd.execute
cmd[:exit_code]
end
def summary
duration=""
duration=getFormattedTimeSpan(self[:end_time]-self[:start_time]) + " - " if(!self[:end_time].nil?)
duration + "#{self[:exit_code].to_s} #{self[:input]} (#{self[:directory]})"
end
def to_html
if self[:exit_code] == 0
[
''
].join
else
[
' | ',
self[:input],
' | ',
map { |k, v| ["#{k} | ", v.respond_to?(:to_html) ? v.to_html : "#{v} | "] },
' ',
' |
|
'
].join
end
end
end
#require_relative('hash.rb')
#require_relative('pull.rb')
#require_relative('update.rb')
#require_relative('setup.rb')
#require_relative('build.rb')
#require_relative('test.rb')
#require_relative('analyze.rb')
#require_relative('publish.rb')
#require_relative('doc.rb')
#require_relative('clean.rb')
#require_relative('clobber.rb')
#require_relative('add.rb')
#require_relative('commit.rb')
#require_relative('push.rb')
class Commands < Hash
def initialize directory=Dir.pwd
Dir.chdir(directory) do
self[:pull]=Pull.new
self[:update]=Update.new
self[:setup]=Setup.new
self[:build]=Build.new
self[:test]=Test.new
self[:analyze]=Analyze.new
self[:doc]=Doc.new
self[:clean]=Clean.new
self[:publish]=Publish.new
self[:clobber]=Clobber.new
self[:add]=Add.new
self[:commit]=Commit.new
self[:push]=Push.new
end
end
end
#require_relative('internet.rb')
class Commit < Array
def update
if(File.exists?('.git') && `git config --list`.include?('user.name='))
if(!`git status`.include?('nothing to commit') &&
!`git status`.include?('untracked files present'))
if(File.exists?('commit.message') && File.read('commit.message').gsub(/\s+/,"").length >0)
add "git commit -a -v -m \"#{File.read('commit.message')}\""
else
add "git commit -m'all'"
end
add "<%FileUtils.rm('commit.message')%>" if File.exists?('commit.message')
end
end
if(File.exists?('.svn') && Internet.available?)
add 'svn commit -m"commit all"'
end
end
end
class Doc < Array
def update
#cmd=Command.new ({ :input => 'yard --version', :ignore_failure => true})
#cmd.execute
if(Command.exit_code('yard --version'))
add 'yard doc - LICENSE' if File.exists?('README.md') && File.exists?('LICENSE')
end
end
end
class Gemspec
def self.update gemspec_file
Text.replace_in_file gemspec_file,
/('\d{4}-\d{2}-\d{2}')/,
"'#{Time.now.strftime('%Y-%m-%d')}'"
end
def self.gemfile gemspec_file
spec=Gem::Specification.load(gemspec_file)
return "#{spec.name}-#{spec.version}.gem" if !spec.nil?
return ""
end
def self.version gemspec_file
spec=Gem::Specification.load(gemspec_file)
return spec.version.to_s
end
def self.published_version gemspec_file
published_version=''
spec=Gem::Specification.load(gemspec_file)
begin
published_version = `gem list -r #{spec.name}`.scan(/\((\d+.\d+.\d+)\)/)[0][0]
rescue
published_version=''
end
published_version
end
def self.published? gemspec_file
published_version(gemspec_file)==version(gemspec_file) ? true : false
end
def self.normalize gemspec_file
spec=Gem::Specification.load(gemspec_file)
File.open(gemspec_file,'w'){|f|f.write(spec.to_ruby)}
end
def self.upgrade gemspec_file
end
end
class Git
def self.branch
begin
`git branch`.scan(/\* ([.\w-]+)/)[0][0]
rescue
''
end
end
def self.remote_origin directory=''
url=''
directory=Dir.pwd if directory.length == 0
Dir.chdir(directory) do
begin
url=`git remote show origin`.scan(/Fetch URL: ([\.\-:\/\w\d]+)/)[0]
rescue
url=''
end
end
url
end
end
class Hash
def execute value=nil
self.each{|k,v|
v.update if v.respond_to?(:update)
if(v.is_a?(Array) && v.length==0)
self.delete k
else
#puts "executing #{k}"
v.execute(value) if v.respond_to?(:execute)
end
}
end
def to_html
[
'',
map { |k, v| ["
#{k}", v.respond_to?(:to_html) ? v.to_html : "#{v}
"] },
'
'
].join
end
end
#require_relative 'array.rb'
INFO=Array.new
require 'open-uri'
#require 'net/http'
require 'timeout'
class Internet
@@available=true
def self.available?
return @@available if !@@available.nil?
begin
index=open('http://www.google.com').read
if index.include?('Google')
@@available = true
else
puts "open('http://www.google.com') returned false"
end
rescue Exception => e
puts "open('http://www.google.com') raised an exception: #{e.to_s}"
@@available = false
end
@@available
end
end
# Visual Studio 2008 version 9.0, solution format version 10.00
# Visual Studio 2010 version 10.0, solution format version 11.00
# Visual Studio 2012 version 11.0, solution format version 12.00
# Visual Studio 2013 version 12.0, solution format version 12.00
class MSBuild < Hash
def initialize
self[:vs9]="C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\msbuild.exe" if(File.exists?("C:\\Windows\\Microsoft.NET\\Framework\\v3.5\\msbuild.exe"))
self[:vs12]="C:\\Program Files (x86)\\MSBuild\\12.0\\bin\\msbuild.exe" if(File.exists?("C:\\Program Files (x86)\\MSBuild\\12.0\\bin\\msbuild.exe"))
end
def self.has_version? version
if(defined?(MSBUILD))
MSBUILD.has_key?(version)
else
msb=MSBuild.new
return msb.has_key? version
end
end
def self.get_version version
if(defined?(MSBUILD))
MSBUILD[version]
else
msb=MSBuild.new
return msb[version]
end
end
def self.get_vs_version(sln_filename)
sln_text=File.read(sln_filename,:encoding=>'UTF-8')
return :vs9 if sln_text.include?('Format Version 10.00')
return :vs12
end
def self.get_configurations(sln_filename)
configs=Array.new
sln_text=File.read(sln_filename,:encoding=>'UTF-8')
sln_text.scan( /= ([\w]+)\|/ ).each{|m|
c=m.first.to_s
configs << c if !configs.include?(c)
}
return configs
end
def self.get_platforms(sln_filename)
platforms=Array.new
sln_text=File.read(sln_filename,:encoding=>"UTF-8")
sln_text.scan( /= [\w]+\|([\w ]+)/ ).each{|m|
p=m.first.to_s
platforms << p if !platforms.include?(p)
}
return platforms
end
end
#require_relative('internet.rb')
class Publish < Array
def update
if(Internet.available?)
if(File.exists?('.git'))
if(`git branch`.include?('* master'))
Dir.glob('*.gemspec').each{|gemspec_file|
add "gem push #{Gemspec.gemfile(gemspec_file)}" if !Gemspec.published? gemspec_file
}
end
end
if(File.exists?('.svn'))
if(`svn info`.include?('/trunk'))
Dir.glob('*.gemspec').each{|gemspec_file|
add "gem push #{Gemspec.gemfile(gemspec_file)}" if !Gemspec.published? gemspec_file
}
end
end
end
end
end
#require_relative('git.rb')
#require_relative('internet.rb')
class Pull < Array
def update
if(Internet.available?)
if(File.exists?('.git') && `git config --list`.include?('user.name='))
self << 'git pull' if Git.branch != 'develop'
end
end
end
end
class Push < Array
def update
if(File.exists?('.git') && `git config --list`.include?('user.name='))
self << 'git push'
self << 'git push --tags'# if Git.branch != 'develop' && Internet.available?
end
end
end
#
# use the SVN_EXPORTS hash to define svn exports destined for DEV_ROOT/dep
#
# SVN_EXPORT={ 'System.Data.SQLite/1.0.93.0' => 'https://third-party.googlecode.com/svn/trunk/System.Data.SQLite/1.0.93.0' }
#
class Setup < Array
def update
add 'bundle install' if(File.exists?('Gemfile'))
Dir.glob('*.gemspec').each{|gemspec_file|
add "<%Gemspec.update('#{gemspec_file}')%>"
}
if(defined?(SVN_EXPORTS))
SVN_EXPORTS.each{|k,v|
if(!File.exists?("#{Command.dev_root}/dep/#{k}"))
FileUtils.mkdir_p(File.dirname("#{Command.dev_root}/dep/#{k}")) if !File.exists?("#{Command.dev_root}/dep/#{k}")
dest="#{Command.dev_root}/dep/#{k}"
add "svn export #{v} #{Command.dev_root}/dep/#{k}" if !dest.include?("@")
add "svn export #{v} #{Command.dev_root}/dep/#{k}@" if dest.include?("@")
end
}
end
end
end
class Tag < Array
end
#
# nunit dlls may be specified with
# NUNIT=FileList.new('**/*.Test.dll')
#
# for nunit dlls that must be run in x86 mode,
# NUNIT_x86=FileList.new('**/*.x86.Test.dll')
#
class Test < Array
def update
add 'rspec' if File.exists?('spec')
if(defined?(NUNIT))
NUNIT.each{|nunit_dll|
add "\"#{Test.nunit_console}\" \"#{Rake.application.original_dir}\\#{nunit_dll}\" /xml:\"#{nunit_dll}.TestResults.xml\""
}
end
if(defined?(NUNIT_X86))
NUNIT_X86.each{|nunit_dll|
add "\"#{Test.nunit_console_x86}\" \"#{Rake.application.original_dir}\\#{nunit_dll}\" /xml:\"#{nunit_dll}.TestResults.xml\""
}
end
if(defined?(TESTS))
TEST.each{|t| add t}
end
end
@@nunit_console=''
def self.nunit_console
if(!File.exists?(@@nunit_console))
if(defined?(NUNIT_CONSOLE))
@@nunit_console = NUNIT_CONSOLE
end
@@nunit_console = "C:\\Program Files (x86)\\NUnit 2.6.4\\bin\\nunit-console.exe" if(!File.exists?(@@nunit_console))
@@nunit_console = "C:\\Program Files (x86)\\NUnit 2.6.3\\bin\\nunit-console.exe" if(!File.exists?(@@nunit_console))
end
if(!File.exists?(@@nunit_console))
raise "unable to locate nunit-console.exe, assign NUNIT_CONSOLE to the correct location."
end
@@nunit_console
end
@@nunit_console_x86=''
def self.nunit_console_x86
if(!File.exists?(@@nunit_console_x86))
if(defined?(NUNIT_CONSOLE_X86))
@@nunit_console_x86 = NUNIT_CONSOLE_X86
end
@@nunit_console_x86 = "C:\\Program Files (x86)\\NUnit 2.6.4\\bin\\nunit-console-x86.exe" if(!File.exists?(@@nunit_console_x86))
@@nunit_console_x86 = "C:\\Program Files (x86)\\NUnit 2.6.3\\bin\\nunit-console-x86.exe" if(!File.exists?(@@nunit_console_x86))
end
if(!File.exists?(@@nunit_console_x86))
raise "unable to locate nunit-console-x86.exe, assign NUNIT_CONSOLE_X86 to the correct location."
end
@@nunit_console_x86
end
end
NUNIT=FileList.new('bin/**/*.Test.dll')
class Text
def self.replace_in_glob(glob,search,replace)
Dir.glob(glob).each{ |f| replace_in_file(f,search,replace) }
end
def self.replace_in_file(filename,search,replace)
text1 = IO.read(filename)
text2 = text1.gsub(search) { |str| str=replace }
unless text1==text2
File.open(filename,"w") { |f| f.puts text2 }
return true
end
false
end
end
############################################################################
# The following code is based on code originally copied from
# https://gist.github.com/lpar/1032297
# Gist title: lpar/timeout.rb
############################################################################
# Runs a specified shell command in a separate thread.
# If it exceeds the given timeout in seconds, kills it.
# Returns any output produced by the command (stdout or stderr) as a String.
# Uses Kernel.select to wait up to the tick length (in seconds) between
# checks on the command's status
#
# If you've got a cleaner way of doing this, I'd be interested to see it.
# If you think you can do it with Ruby's Timeout module, think again.
def run_with_timeout(directory,command, timeout, tick)
output = ''
exit_code=1
begin
# Start task in another thread, which spawns a process
stdin, stderrout, thread = Open3.popen2e(command, :chdir=>directory)
# Get the pid of the spawned process
pid = thread[:pid]
start = Time.now
while (Time.now - start) < timeout and thread.alive?
# Wait up to `tick` seconds for output/error data
Kernel.select([stderrout], nil, nil, tick)
# Try to read the data
begin
output << stderrout.read_nonblock(BUFFER_SIZE)
rescue IO::WaitReadable
# A read would block, so loop around for another select
rescue EOFError
# Command has completed, not really an error...
break
end
end
# Give Ruby time to clean up the other thread
sleep 1
if thread.alive?
# We need to kill the process, because killing the thread leaves
# the process alive but detached, annoyingly enough.
Process.kill("TERM", pid)
else
exit_code=thread.value
sleep 1
end
ensure
stdin.close if stdin
stderrout.close if stderrout
end
return [output,exit_code]
end
require 'timeout'
def run_with_timeout2(directory,command,timeout)
# stdout, stderr pipes
rout, wout = IO.pipe
rerr, werr = IO.pipe
output=''
error=''
exit_code=1
pid = Process.spawn(command, :chdir => directory, :out => wout, :err => werr)
begin
Timeout.timeout(timeout) do
exit_code = Process.wait2(pid)
output = rout.readlines.join("\n")
error = rerr.readlines.join("\n")
end
rescue
Proces.kill('TERM',pid)
output = output + 'timeout occurred.'
ensure
rout.close
rerr.close
end
[output,exit_code]
end
class Timer
attr_accessor :start_time
def initialize
@start_time=Time.now
end
def elapsed # in seconds
return Time.now-@start_time
end
def elapsed_str
elapsed_str="[" + "%.0f" %(elapsed) + "s]"
end
def self.elapsed_exceeds?(name,duration_seconds)
if(Timer.get_elapsed(name).nil? || Timer.get_elapsed(name) > duration_seconds)
return true
end
return false
end
def self.get_elapsed(name)
timestamp=get_timestamp(name)
return Time.now-timestamp if(!timestamp.nil?)
nil
end
def self.get_timestamp(name)
dir=Rake.application.original_dir
if(File.exists?("#{DEV[:dev_root]}/log/#{name}.timestamp"))
return Time.parse(File.read("#{DEV[:dev_root]}/log/#{name}.timestamp").strip)
end
nil
end
def self.set_timestamp(name)
Dir.mkdir("#{DEV_TASKS[:dev_root]}/log") if(!Dir.exists?("#{DEV_TASKS[:dev_root]}/log"))
File.open("#{DEV_TASKS[:dev_root]}/log/#{name}.timestamp",'w'){|f|f.puts(Time.now.to_s)}
end
end
TIMER=Timer.new
class Update < Array
def update
self .add 'svn update' if File.exists?('.svn') && Internet.available?
end
end
class Upgrade < Array
def update
if(File.exists?('Gemfile'))
end
end
end
require 'json'
require 'rake/clean'
CLOBBER.include('*.gem')
SOURCE=FileList.new('LICENSE','README','README.md',"Gemfile")
SOURCE.include('*.{gitignore,yml,gemspec,rb}')
SOURCE.include('*.{cs}')
SOURCE.include('*.{c,h}')
SOURCE.include('*.{cpp,hpp}')
Dir.glob("#{File.dirname(__FILE__)}/commands/*.rb").each{|rb|
require(rb)
}
COMMANDS=Commands.new
MSBUILD=MSBuild.new