require_relative 'artefact_engine.rb' module Comfpile class Core attr_reader :processing_stack def initialize() @artefact_engines = [] @artefact_prio_counter = 0 # Artefacts are arranged in a double-hash. # First query is stage, second filename. @artefacts = {} @valid_contexts = {} # Stack-style processing queue for item processing @processing_stack = [] end # Sanitize and unify context tags. # This function will take either an array of tags, or a symbol, # and sanitize and unify it to match a predictable context tag # format. # # Context tags are symbols containing a list of unique configs # types to use, separated by underscore ('_') # # @param [Array, Symbol] context The context to # sanitize # # @return [Symbol] Sanitized context. Unique, sorted list # of tags, joined into a Symbol. # def sanitize_context(context) if context.nil? return :default elsif context.is_a? Array new_context = context.uniq.sort.join('_').to_sym @valid_contexts[new_context] = true return new_context elsif context.is_a? Symbol return context if @valid_contexts[context] return sanitize_context(context.to_s.split('_')) else raise ArgumentError, "Unknown context key type supplied!" end end end # Create or find a new artefact. # # This will first see if a given artefact matching # the stage and target descriptions already exists, and # will return it, if found. Otherwise it will # consult all given artefact crafting engines to # see if a new artefact can be created, which will be returned. # # If no artefact can be made, nil is returned # # @param [Symbol, String] stage State that shall be created (e.g. :parsed, :compiled, etc.) # @param [String] file File or other target that shall be looked at (e.g. "main.cpp") # # @return [Comfpile::Artefact, nil] Found or created artefact, or nil if nothing could be done # def craft_artefact(stage, file = :none) artefact = find_artefact(stage, file) return artefact unless artefact.nil? @artefact_engines.each do |engine| artefact = engine.craft stage, file if artefact @artefacts[stage] ||= {} @artefacts[stage][file] = artefact @processing_stack.push artefact return artefact end end nil end def add_artefact(stage, file = :none, engine: nil, artefact_class: Comfpile::Artefact) return unless find_artefact(stage, file).nil? a = Artefact.new(self, engine, stage, file); @artefacts[stage] ||= {} @artefacts[stage][file] = a yield(a) if block_given? nil end # @yieldparam [Comfpile::ArtefactEngine] Engine that was newly created def add_artefact_engine(engine_class = Comfpile::ArtefactEngine, **options) new_engine = if(engine_class.is_a? Comfpile::ArtefactEngine) engine_class else engine = engine_class.new(self, subpriority: @artefact_prio_counter, **options) @artefact_prio_counter += 1 engine end yield(new_engine) if block_given? @artefact_engines << new_engine @artefact_engines.sort! new_engine end def processing_stack_prune() loop do return if @processing_stack.empty? if @processing_stack[-1].completed? @processing_stack.pop else break end end nil end def processing_stack_find_next() return nil if @processing_stack.empty? i = 0 @processing_stack.reverse_each do |a| i += 1 if a.waiting? # puts "DBG - Found item after #{i} checks" return a end end # TODO: Fix up later-on for parallel runner capabilities raise "Deadlock found, no item was willing to run!" nil end def execute_step return if @processing_stack.empty? # puts "Got #{@processing_stack.length} items..." processing_stack_prune artefact = processing_stack_find_next return nil if artefact.nil? # puts "Processing artefact #{artefact.stage} #{artefact.target}" begin artefact.execute_step rescue ArtefactExecSkipError end nil end def execute_all step_count = 0 until @processing_stack.empty? execute_step() step_count += 1 end puts "Done after #{step_count} exec loops..." end def craft_and_complete(stage, target) artefact = craft_artefact(stage, target) return nil if artefact.nil? while artefact.in_progress? execute_step end artefact end end end