lib/blocks/block.rb in bake-toolkit-2.32.0 vs lib/blocks/block.rb in bake-toolkit-2.33.0
- old
+ new
@@ -1,8 +1,10 @@
require 'bake/libElement'
require 'bake/model/metamodel'
require 'common/abortException'
+require "multithread/job"
+require "thwait"
module Bake
BUILD_PASSED = 0
BUILD_FAILED = 1
@@ -14,10 +16,14 @@
ALL_BLOCKS = {}
ALL_COMPILE_BLOCKS = {}
class Block
+ @@block_counter = 0
+ @@delayed_result = true
+ @@threads = []
+
attr_reader :lib_elements, :projectDir, :library, :config, :projectName, :prebuild, :output_dir, :tcs
attr_accessor :visited, :inDeps, :result
def startupSteps
@startupSteps ||= []
@@ -71,11 +77,10 @@
@config = config
@referencedConfigs = referencedConfigs
@projectName = config.parent.name
@configName = config.name
@projectDir = config.get_project_dir
- @@block_counter = 0
@result = true
@tcs = tcs
@lib_elements = Bake::LibElements.calcLibElements(self)
@@ -97,11 +102,13 @@
return d if Bake.options.no_autodir
inc = d.split("/")
if (inc[0] == "..") # very simple check, but should be okay for 99.9 % of the cases
if elem and Bake.options.verbose >= 2
- Bake.formatter.printInfo("path starts with \"..\"", elem)
+ SyncOut.mutex.synchronize do
+ Bake.formatter.printInfo("path starts with \"..\"", elem)
+ end
end
end
res = []
@@ -136,13 +143,15 @@
res = res.map{ |r| Pathname.new(r).cleanpath.to_s }.uniq
if warnIfLocal && res.length > 1
if elem and Bake.options.verbose >= 2
- Bake.formatter.printInfo("#{d} matches several paths:", elem)
- puts " #{res[0]} (chosen)"
- res[1..-1].each { |r| puts " #{r}" }
+ SyncOut.mutex.synchronize do
+ Bake.formatter.printInfo("#{d} matches several paths:", elem)
+ puts " #{res[0]} (chosen)"
+ res[1..-1].each { |r| puts " #{r}" }
+ end
end
end
res[0]
end
@@ -157,10 +166,22 @@
def self.reset_block_counter
@@block_counter = 0
end
+ def self.reset_delayed_result
+ @@delayed_result = true
+ end
+
+ def self.delayed_result
+ @@delayed_result
+ end
+
+ def self.threads
+ @@threads
+ end
+
def calcIsBuildBlock
@startupSteps ||= []
return true if Metamodel::ExecutableConfig === @config
if Metamodel::CustomConfig === @config
@@ -205,12 +226,14 @@
@result = false
ProcessHelper.killProcess(true)
rescue Exception => ex1
@result = false
if not Bake::IDEInterface.instance.get_abort
- Bake.formatter.printError("Error: #{ex1.message}")
- puts ex1.backtrace if Bake.options.debug
+ SyncOut.mutex.synchronize do
+ Bake.formatter.printError("Error: #{ex1.message}")
+ puts ex1.backtrace if Bake.options.debug
+ end
end
end
if Bake::IDEInterface.instance.get_abort
raise AbortException.new
@@ -246,64 +269,130 @@
break if not depResult and Bake.options.stopOnFirstError
end
return depResult
end
+ def execute_in_thread(method)
+ if method == :execute
+ @@mutex.synchronize do
+ if @@threads.length == Bake.options.threads
+ endedThread = ThreadsWait.new(@@threads).next_wait
+ @@threads.delete(endedThread)
+ end
+ end
+ thread = Thread.new() {
+
+ exceptionOccured = false
+ begin
+ yield
+ exceptionOccured = true
+ rescue Bake::SystemCommandFailed => scf # normal compilation error
+ rescue SystemExit => exSys
+ rescue Exception => ex1
+ if not Bake::IDEInterface.instance.get_abort
+ SyncOut.mutex.synchronize do
+ Bake.formatter.printError("Error: #{ex1.message}")
+ puts ex1.backtrace if Bake.options.debug
+ end
+ end
+ end
+ if !exceptionOccured
+ @result = false
+ @@delayed_result = false
+ end
+ }
+ @@mutex.synchronize do
+ @@threads << thread
+ end
+ else
+ yield
+ end
+ raise AbortException.new if Bake::IDEInterface.instance.get_abort
+ end
+
+ def blockAbort?(res)
+ ((not res) || !@@delayed_result) and Bake.options.stopOnFirstError or Bake::IDEInterface.instance.get_abort
+ end
+
def callSteps(method)
@config.writeEnvVars()
Thread.current[:lastCommand] = nil
preSteps.each do |step|
+ ThreadsWait.all_waits(Blocks::Block::threads)
@result = executeStep(step, method) if @result
- return false if not @result and Bake.options.stopOnFirstError
+ return false if blockAbort?(@result)
end unless @prebuild
- mainSteps.each do |step|
+ threadableSteps = mainSteps.select { |step| method == :execute && (Library === step || Compile === step) }
+ nonThreadableSteps = mainSteps.select { |step| method != :execute || !(Library === step || Compile === step) }
+
+ execute_in_thread(method) {
+ threadableSteps.each do |step|
+ if !@prebuild || (Library === step)
+ Multithread::Jobs.incThread() if Library === step
+ begin
+ @result = executeStep(step, method) if @result
+ ensure
+ Multithread::Jobs.decThread() if Library === step
+ end
+ @@delayed_result &&= @result
+ end
+ break if blockAbort?(@result)
+ end
+ } if !threadableSteps.empty?
+ nonThreadableSteps.each do |step|
if !@prebuild || (Library === step)
+ ThreadsWait.all_waits(Blocks::Block::threads)
@result = executeStep(step, method) if @result
end
- return false if not @result and Bake.options.stopOnFirstError
+ return false if blockAbort?(@result)
end
postSteps.each do |step|
+ ThreadsWait.all_waits(Blocks::Block::threads)
@result = executeStep(step, method) if @result
- return false if not @result and Bake.options.stopOnFirstError
+ return false if blockAbort?(@result)
end unless @prebuild
return @result
end
def execute
if (@inDeps)
if Bake.options.verbose >= 3
- Bake.formatter.printWarning("While calculating next config, a circular dependency was found including project #{@projectName} with config #{@configName}", @config)
+ SyncOut.mutex.synchronize do
+ Bake.formatter.printWarning("While calculating next config, a circular dependency was found including project #{@projectName} with config #{@configName}", @config)
+ end
end
return true
end
return true if (@visited)
@visited = true
@inDeps = true
depResult = callDeps(:execute)
@inDeps = false
- return false if not depResult and Bake.options.stopOnFirstError
+ return false if blockAbort?(depResult)
Bake::IDEInterface.instance.set_build_info(@projectName, @configName)
- if Bake.options.verbose >= 2 || isBuildBlock? || @prebuild
- typeStr = "Building"
- if @prebuild
- typeStr = "Using"
- elsif not isBuildBlock?
- typeStr = "Applying"
+ SyncOut.mutex.synchronize do
+ if Bake.options.verbose >= 2 || isBuildBlock? || @prebuild
+ typeStr = "Building"
+ if @prebuild
+ typeStr = "Using"
+ elsif not isBuildBlock?
+ typeStr = "Applying"
+ end
+ Block.inc_block_counter()
+ Bake.formatter.printAdditionalInfo "**** #{typeStr} #{Block.block_counter} of #{@@num_projects}: #{@projectName} (#{@configName}) ****"
end
- Block.inc_block_counter()
- Bake.formatter.printAdditionalInfo "**** #{typeStr} #{Block.block_counter} of #{@@num_projects}: #{@projectName} (#{@configName}) ****"
+ puts "Project path: #{@projectDir}" if Bake.options.projectPaths
end
- puts "Project path: #{@projectDir}" if Bake.options.projectPaths
@result = callSteps(:execute)
return (depResult && @result)
end
@@ -313,10 +402,14 @@
depResult = callDeps(:clean)
return false if not depResult and Bake.options.stopOnFirstError
if Bake.options.verbose >= 2 || isBuildBlock? || @prebuild
+ Block.inc_block_counter()
+ end
+
+ if Bake.options.verbose >= 2
typeStr = "Cleaning"
if @prebuild
typeStr = "Checking"
elsif not isBuildBlock?
typeStr = "Skipping"
@@ -342,9 +435,15 @@
@result = executeStep(step, :cleanStep) if @result
return false if not @result and Bake.options.stopOnFirstError
end unless @prebuild
return (depResult && @result)
+ end
+
+ def self.init_threads()
+ @@threads = []
+ @@result = true
+ @@mutex = Mutex.new
end
def startup
return true if (@visited)
@visited = true
\ No newline at end of file