feat: ✨ begin the repo a bit late but better than never
This commit is contained in:
commit
607159334b
22 changed files with 898 additions and 0 deletions
12
lib/comfpile.rb
Normal file
12
lib/comfpile.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "comfpile/version"
|
||||
|
||||
require_relative 'comfpile/core.rb'
|
||||
|
||||
require_relative 'comfpile/engines/filesource_engine.rb'
|
||||
|
||||
module Comfpile
|
||||
class Error < StandardError; end
|
||||
# Your code goes here...
|
||||
end
|
216
lib/comfpile/artefact.rb
Normal file
216
lib/comfpile/artefact.rb
Normal file
|
@ -0,0 +1,216 @@
|
|||
|
||||
require 'debug'
|
||||
|
||||
module Comfpile
|
||||
class ArtefactExecSkipError < StandardError
|
||||
end
|
||||
|
||||
class Artefact
|
||||
attr_reader :core, :engine
|
||||
|
||||
attr_reader :exit_state
|
||||
attr_reader :stage, :target
|
||||
|
||||
attr_reader :linked_artefacts
|
||||
|
||||
# ARTEFACT STATES
|
||||
#
|
||||
# The following states are known to the system:
|
||||
# - blocked: The Artefact is blocked and waiting on other artefacts
|
||||
# - waiting: The Artefact is idle and ready to be queued
|
||||
# - running: The Artefact is currently being run
|
||||
# Note that this is not a state, but is determined by the
|
||||
# @running flag
|
||||
# - succeeded: it has finished its work without issue
|
||||
# - skipped: it didn't run/won't run because of failed dependencies
|
||||
# - failed: it has failed due to a requirement not being met
|
||||
#
|
||||
# Meta-States exist:
|
||||
# - in_progress/completed: Anything but/Only succeeded, skipped, failed
|
||||
|
||||
def initialize(core, engine, stage, target)
|
||||
@core = core
|
||||
@engine = engine
|
||||
|
||||
@stage = stage
|
||||
@target = target
|
||||
|
||||
@age = Time.at(0)
|
||||
|
||||
@parent_artefact = nil
|
||||
|
||||
@required_artefacts = nil
|
||||
@linked_artefacts = nil
|
||||
|
||||
@steps = []
|
||||
@step_additions = nil
|
||||
|
||||
@waitlist = []
|
||||
|
||||
@steps_done_ctr = 0
|
||||
|
||||
@parameters = {}
|
||||
|
||||
@exit_state = nil
|
||||
@running = false
|
||||
end
|
||||
|
||||
def [](key)
|
||||
v = @parameters[key]
|
||||
return v unless v.nil?
|
||||
|
||||
if @parent_artefact
|
||||
return @parent_artefact[key]
|
||||
end
|
||||
end
|
||||
def []=(key, value)
|
||||
@parameters[key] = value
|
||||
end
|
||||
|
||||
private def add_step_data(data)
|
||||
@step_additions ||= []
|
||||
|
||||
@step_additions << data
|
||||
end
|
||||
private def process_additional_step_data
|
||||
unless @step_additions.nil?
|
||||
@steps.insert(@steps_done_ctr, @step_additions)
|
||||
@steps.flatten!
|
||||
@step_additions = nil
|
||||
end
|
||||
end
|
||||
|
||||
def add_step(&block)
|
||||
add_step_data({
|
||||
type: :block,
|
||||
executed: false,
|
||||
block: block
|
||||
})
|
||||
end
|
||||
|
||||
def parent_artefact(stage, target)
|
||||
@parent_artefact = require_artefact(stage, target)
|
||||
end
|
||||
|
||||
def require_artefact(stage, target)
|
||||
artefact = @core.craft_artefact(stage, target)
|
||||
|
||||
if(artefact.nil?)
|
||||
fail! "Missing artefact dependency for #{stage} #{target}!"
|
||||
else
|
||||
@waitlist << {
|
||||
artefact: artefact,
|
||||
required: true
|
||||
}
|
||||
end
|
||||
|
||||
@required_artefacts ||= {}
|
||||
@required_artefacts[stage] ||= {}
|
||||
@required_artefacts[stage][target] = artefact
|
||||
|
||||
artefact
|
||||
end
|
||||
|
||||
def craft_artefact(stage, target)
|
||||
artefact = @core.craft_artefact(stage, target)
|
||||
|
||||
artefact
|
||||
end
|
||||
|
||||
def waitlist_empty?
|
||||
return true if completed?
|
||||
|
||||
loop do
|
||||
return true if @waitlist.empty?
|
||||
|
||||
item = @waitlist[-1]
|
||||
|
||||
return false if item[:artefact].in_progress?
|
||||
|
||||
if not item[:required]
|
||||
@waitlist.pop
|
||||
elsif item[:artefact].succeeded?
|
||||
@waitlist.pop
|
||||
else
|
||||
skip! skip! "Failed artefact dependency: #{item[:artefact]}"
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def state
|
||||
return :blocked unless waitlist_empty?
|
||||
return @exit_state unless @exit_state.nil?
|
||||
|
||||
return :running if @running
|
||||
|
||||
return :waiting
|
||||
end
|
||||
|
||||
def completed?
|
||||
not @exit_state.nil?
|
||||
end
|
||||
|
||||
def succeeded?
|
||||
@exit_state == :succeeded
|
||||
end
|
||||
|
||||
def in_progress?
|
||||
not completed?
|
||||
end
|
||||
|
||||
def waiting?
|
||||
self.state == :waiting
|
||||
end
|
||||
|
||||
private def mark_state_change(state, reason, abort: false)
|
||||
puts "#{@stage} #{target}: Reached state #{state}: #{reason}"
|
||||
@exit_state = state
|
||||
@reason = reason
|
||||
|
||||
abort_step! if abort
|
||||
end
|
||||
|
||||
def skip!(reason, **opts)
|
||||
mark_state_change(:skipped, reason, **opts)
|
||||
end
|
||||
def fail!(reason, **opts)
|
||||
mark_state_change(:failed, reason, **opts)
|
||||
end
|
||||
def succeed!(reason, **opts)
|
||||
mark_state_change(:succeeded, reason, **opts)
|
||||
end
|
||||
|
||||
def abort_step!
|
||||
raise ArtefactExecSkipError
|
||||
end
|
||||
|
||||
def execute_step
|
||||
return unless waiting?
|
||||
@running = true
|
||||
|
||||
process_additional_step_data
|
||||
|
||||
next_step = @steps[@steps_done_ctr]
|
||||
succeed! "All done", abort: true if next_step.nil?
|
||||
|
||||
case next_step[:type]
|
||||
when :block
|
||||
instance_exec &next_step[:block]
|
||||
else
|
||||
fail! "Unknown artefact step taken!", abort: true
|
||||
end
|
||||
|
||||
@steps_done_ctr += 1
|
||||
succeed! "All done", abort: true if @steps_done_ctr >= @steps.length
|
||||
|
||||
ensure
|
||||
@running = false
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#<Compfile::Artefact #{@stage} #{@target}>"
|
||||
end
|
||||
end
|
||||
end
|
60
lib/comfpile/artefact_engine.rb
Normal file
60
lib/comfpile/artefact_engine.rb
Normal file
|
@ -0,0 +1,60 @@
|
|||
|
||||
require_relative 'artefact.rb'
|
||||
|
||||
module Comfpile
|
||||
class ArtefactEngine
|
||||
|
||||
attr_accessor :priority, :subpriority
|
||||
|
||||
def initialize(core, **options)
|
||||
@core = core
|
||||
|
||||
@priority = options[:priority] || 0
|
||||
@subpriority = options[:subpriority] || 0
|
||||
|
||||
@recipes = []
|
||||
end
|
||||
|
||||
def craft(stage, target)
|
||||
@recipes.each do |recipe|
|
||||
match = target
|
||||
|
||||
if recipe[:stage]
|
||||
next unless stage == recipe[:stage]
|
||||
end
|
||||
|
||||
if r = recipe[:regex]
|
||||
if r.is_a? String
|
||||
next unless target == r
|
||||
else
|
||||
match = r.match target
|
||||
next if match.nil?
|
||||
end
|
||||
end
|
||||
|
||||
new_artefact = Artefact.new(@core, self, stage, target)
|
||||
|
||||
item = recipe[:block].call(match, new_artefact)
|
||||
|
||||
return new_artefact if item
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
prio = other.priority <=> self.priority
|
||||
return prio unless prio == 0
|
||||
|
||||
other.subpriority <=> self.subpriority
|
||||
end
|
||||
|
||||
def add_recipe(stage = nil, target_regex, &block)
|
||||
@recipes << {
|
||||
regex: target_regex,
|
||||
stage: stage,
|
||||
block: block
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
113
lib/comfpile/core.rb
Normal file
113
lib/comfpile/core.rb
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
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 = {}
|
||||
|
||||
# Stack-style processing queue for item processing
|
||||
@processing_stack = []
|
||||
end
|
||||
|
||||
def find_artefact(stage, file = :none)
|
||||
@artefacts.dig(stage, file)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def add_artefact_engine(engine_class = Comfpile::ArtefactEngine, **options)
|
||||
new_engine = engine_class.new(self,
|
||||
subpriority: @artefact_prio_counter, **options)
|
||||
@artefact_prio_counter += 1
|
||||
|
||||
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()
|
||||
@processing_stack.reverse_each do |a|
|
||||
return a if a.waiting?
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
end
|
25
lib/comfpile/engines/filesource_engine.rb
Normal file
25
lib/comfpile/engines/filesource_engine.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
|
||||
module Comfpile
|
||||
class FilesourceEngine < ArtefactEngine
|
||||
def initialize(core, **options)
|
||||
super(core, **options)
|
||||
|
||||
@root_path = options[:root_path]
|
||||
end
|
||||
|
||||
def craft(stage, target)
|
||||
return nil unless stage == :sourcefile
|
||||
|
||||
full_path = File.join(@root_path, target)
|
||||
|
||||
return nil unless File.exists? full_path
|
||||
|
||||
a = Artefact.new(@core, self, stage, target);
|
||||
a[:file] = full_path
|
||||
a[:filepath] = full_path
|
||||
|
||||
a
|
||||
end
|
||||
end
|
||||
end
|
88
lib/comfpile/engines/parser_engine.rb
Normal file
88
lib/comfpile/engines/parser_engine.rb
Normal file
|
@ -0,0 +1,88 @@
|
|||
|
||||
module Compfile
|
||||
class ParserArtefact < Artefact
|
||||
attr_reader :included_files
|
||||
attr_reader :required_files
|
||||
|
||||
def initialize(*args)
|
||||
super(*args)
|
||||
|
||||
@included_files = []
|
||||
@required_files = []
|
||||
|
||||
parent_artefact :sourcefile, @target
|
||||
|
||||
add_step do
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ParserEngine < ArtefactEngine
|
||||
def initialize(core, **options)
|
||||
super(core, **options)
|
||||
|
||||
@input_file_regex = options[:allowed_files]
|
||||
|
||||
@require_regex = options[:require_reg]
|
||||
@include_regex = options[:include_reg]
|
||||
end
|
||||
|
||||
def generate_parser_artefact(stage, target)
|
||||
match = @input_file_regex.match target
|
||||
return nil if match.nil?
|
||||
|
||||
a = Artefact.new(@core, self, stage, target)
|
||||
|
||||
a.parent_artefact(:sourcefile, target)
|
||||
|
||||
a.add_step do
|
||||
|
||||
@parameters[:included_files] = []
|
||||
@parameters[:required_files] = []
|
||||
|
||||
File.readlines(@parent_artefact[:file]) do |l|
|
||||
case l
|
||||
when @require_regex
|
||||
filename = $~[:file]
|
||||
|
||||
own_dir = File.dirname(@target)
|
||||
relative_file = File.join(own_dir, filename)
|
||||
|
||||
unless craft_artefact(:sourcefile, relative_file).nil?
|
||||
@parameters[:required_files] << require_artefact(:parsed, relative_file)
|
||||
else
|
||||
@parameters[:required_files] << require_artefact(:parsed, filename)
|
||||
end
|
||||
|
||||
when @include_regex
|
||||
filename = $~[:file]
|
||||
|
||||
own_dir = File.dirname(@target)
|
||||
relative_file = File.join(own_dir, filename)
|
||||
|
||||
unless craft_artefact(:sourcefile, relative_file).nil?
|
||||
@parameters[:included_files] << craft_artefact(:parsed, relative_file)
|
||||
else
|
||||
@parameters[:included_files] << craft_artefact(:parsed, filename)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def generate_dependency_artefact(stage, target)
|
||||
|
||||
end
|
||||
|
||||
def craft(stage, target)
|
||||
case stage
|
||||
when :parsed
|
||||
generate_parser_artefact(stage, target)
|
||||
when :dependency_list
|
||||
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
49
lib/comfpile/resource_location.rb
Normal file
49
lib/comfpile/resource_location.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
|
||||
require_relative 'sourcefile.rb'
|
||||
|
||||
module Comfpile
|
||||
class ResourceLocation
|
||||
attr_reader :base_path
|
||||
attr_reader :files
|
||||
|
||||
attr_reader :priority, :subpriority
|
||||
|
||||
def initialize(core, base_path, **options)
|
||||
raise ArgumentError, "Resource path must be a string!" unless base_path.is_a? String
|
||||
raise ArgumentError, "Resource path must be a valid file or directory" unless File.exists?(base_path)
|
||||
@base_path = File.expand_path(base_path)
|
||||
|
||||
@name = options[:name] || @location
|
||||
@core = core
|
||||
|
||||
@priority = options[:priority] || 0;
|
||||
@subpriority = options[:subpriority] || 0;
|
||||
|
||||
@settings_map = Hash.new()
|
||||
|
||||
@known_files = Hash.new()
|
||||
end
|
||||
|
||||
def find_sourcefile(item_key)
|
||||
return @known_files[item_key] if @known_files.include? item_key
|
||||
|
||||
item_path = File.join(@base_path, item_key)
|
||||
new_item = nil
|
||||
|
||||
if File.exists? item_path
|
||||
new_item = Sourcefile.new(self, item_path, item_key)
|
||||
|
||||
@known_files[item_key] = new_item
|
||||
end
|
||||
|
||||
new_item
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
prio = other.priority <=> self.priority
|
||||
return prio unless prio == 0
|
||||
|
||||
other.subpriority <=> self.subpriority
|
||||
end
|
||||
end
|
||||
end
|
16
lib/comfpile/sourcefile.rb
Normal file
16
lib/comfpile/sourcefile.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
module Comfpile
|
||||
class Sourcefile
|
||||
attr_reader :full_path, :local_path, :resource_location
|
||||
|
||||
def initialize(resource_location, full_path, local_path)
|
||||
@resource_location = resource_location
|
||||
@full_path = full_path
|
||||
@local_path = local_path
|
||||
end
|
||||
|
||||
def to_s
|
||||
@full_path
|
||||
end
|
||||
end
|
||||
end
|
5
lib/comfpile/version.rb
Normal file
5
lib/comfpile/version.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Comfpile
|
||||
VERSION = "0.1.0"
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue