diff --git a/.gitignore b/.gitignore index 9106b2a..5b202f0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /pkg/ /spec/reports/ /tmp/ +/build/ \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 6791cc1..79db816 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,51 +10,54 @@ GEM backport (1.2.0) benchmark (0.2.1) coderay (1.1.3) - debug (1.7.2) + debug (1.8.0) irb (>= 1.5.0) reline (>= 0.3.1) diff-lcs (1.5.0) e2mmap (0.1.0) io-console (0.6.0) - irb (1.6.4) - reline (>= 0.3.0) - jaro_winkler (1.5.4) + irb (1.7.4) + reline (>= 0.3.6) + jaro_winkler (1.5.6) json (2.6.3) kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) + language_server-protocol (3.17.0.3) method_source (1.0.0) - minitest (5.18.0) - nokogiri (1.14.3-x86_64-linux) + minitest (5.18.1) + nokogiri (1.15.3-x86_64-linux) racc (~> 1.4) - parallel (1.22.1) - parser (3.2.2.0) + parallel (1.23.0) + parser (3.2.2.3) ast (~> 2.4.1) + racc pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) - racc (1.6.2) + racc (1.7.1) rainbow (3.1.1) rake (13.0.6) rbs (2.8.4) - regexp_parser (2.7.0) - reline (0.3.3) + regexp_parser (2.8.1) + reline (0.3.6) io-console (~> 0.5) reverse_markdown (2.1.1) nokogiri rexml (3.2.5) - rubocop (1.50.1) + rubocop (1.54.2) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.0.0) + parser (>= 3.2.2.3) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) rubocop-ast (>= 1.28.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.28.0) + rubocop-ast (1.29.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) solargraph (0.49.0) @@ -73,10 +76,10 @@ GEM thor (~> 1.0) tilt (~> 2.0) yard (~> 0.9, >= 0.9.24) - thor (1.2.1) - tilt (2.1.0) + thor (1.2.2) + tilt (2.2.0) unicode-display_width (2.4.2) - yard (0.9.33) + yard (0.9.34) PLATFORMS x86_64-linux diff --git a/bin/console b/bin/console index db8daa8..6926412 100755 --- a/bin/console +++ b/bin/console @@ -12,145 +12,20 @@ $core = Comfpile::Core.new() $core.add_artefact_engine Comfpile::FilesourceEngine, root_path: File.join(File.dirname(__FILE__), '../test/faux_build_dir') $core.add_artefact_engine Comfpile::ConfigLoaderEngine -$core.add_artefact_engine Comfpile::ParserEngine, - file_regex: /^(.+)\.(h|c|cpp)$/, - search_regexes: [ - { - regex: /^#include\s*[<"](?.+)[>"]/, - key: 'include' - }, - { - regex: /\/\/\s*comf\.(?\w+)[=:]\s*(?.+)/ - } -] -$core.add_artefact_engine Comfpile::ParserEngine, - file_regex: /^(.+)\.vhd$/, - search_regexes: [ - { - regex: /--+\s*comf\.(?[^:]+)[=:]\s*(?.+)/ - } -] - -$core.add_artefact_engine do |engine| - engine.add_recipe(:ghdl_analysed, /^(.+)\.vhd/) do |match, a| - a.parent_artefact :dependency_analysis_include, a.target - - a.add_step do - @parent_artefact.dependencies.each do |dependency| - next if dependency.target == a.target - - log "Adding dependency for ghdl analysis of #{dependency.target}..." - - require_artefact :ghdl_analysed, dependency.target - end - end - - a.add_step do - work_library = find_parsed_parameter('vhdl.work') - if work_library.nil? - work_library = '' - else - work_library = '--work=' + work_library - end - - cmd = "ghdl -a -fsynopsys --std=08 #{work_library} #{self.file}" - log "Executing: #{cmd}" - - `#{cmd}` - end - end - - engine.add_recipe(:ghdl_elaborated, /^(.+)\.vhd/) do |match, a| - a.parent_artefact :dependency_analysis, a.target - - a.add_step do - dependencies.each do |dep| - require_artefact :ghdl_analysed, dep.target - end - end - - a.add_step do - work_library = find_parsed_parameter('vhdl.work') - if work_library.nil? - work_library = '' - else - work_library = '--work=' + work_library - end - - elaborate_arch = find_parsed_parameter('vhdl.elaborate') || File.basename(@target).chomp(File.extname(@target)) - - cmd = "ghdl -e -fsynopsys --std=08 #{work_library} #{elaborate_arch}" - log "Executing: #{cmd}" - - `#{cmd}` - end - end - - engine.add_recipe :ghdl_run, /^(.+)\.vhd/ do |match, a| - a.parent_artefact :ghdl_elaborated, a.target - - a.add_step do - work_library = find_parsed_parameter('vhdl.work') - if work_library.nil? - work_library = '' - else - work_library = '--work=' + work_library - end - - elaborate_arch = find_parsed_parameter('vhdl.elaborate') || File.basename(@target).chomp(File.extname(@target)) - - @parameters[:ghdl_arch] = elaborate_arch - - cmd = "ghdl -r -fsynopsys --std=08 #{work_library} #{elaborate_arch} --wave=#{elaborate_arch}.ghw" - log "Executing: #{cmd}" - - `#{cmd}` - end - end - - engine.add_recipe(:gtkwave_output, /^(.+)\.vhd/) do |match, a| - a.parent_artefact :ghdl_run, a.target - a.add_step do - `gtkwave #{@parent_artefact[:ghdl_arch]}.ghw` - end - end -end - -$core.add_artefact_engine do |engine| - - engine.add_recipe(:dependency_list, /^(.+)\.(h|c|cpp)$/) do |match, a| - - a.require_artefact :dependency_analysis, a.target - a.require_artefact :dependency_analysis_include, a.target - - a.add_step do - deps_artefact = find_artefact(:dependency_analysis, @target) - @parameters[:dependency_list] = deps_artefact.dependencies - end - end - - engine.add_recipe(:x86_debug, /^run (.+)/) do |match, a| - - a.require_artefact :parsed, "#{match[1]}.cpp" - end - - engine.add_recipe(:x86_debug, /(.+)\.o$/) do |match, a| - a.require_artefact :sourcefile, "#{match[1]}.cpp" - - a.add_step do - - end - - true - end -end +require_relative 'ghdl_engine.rb' +require_relative 'cpp_engine.rb' t_start = Time.now() -dep_art = $core.craft_and_complete(:gtkwave_output, "spi_master_tb.vhd") +dep_art = $core.craft_and_complete(:execute, "cpp/main.cpp") +dep_art = $core.craft_and_complete(:execute, "cpp/main2.cpp") t_end = Time.now() -puts "Full dependency list is: #{dep_art.dependencies.map(&:target)} (took #{t_end - t_start})" -puts "Includes of all source files:" +# t_start = Time.now() +# dep_art = $core.craft_and_complete(:gtkwave_output, "vhdl/spi_master_tb.vhd") +# t_end = Time.now() + +# puts "Full dependency list is: #{dep_art.dependencies.map(&:target)} (took #{t_end - t_start})" +# puts "Includes of all source files:" # (If you use this, don't forget to add pry to your Gemfile!) require "pry" diff --git a/bin/cpp-engine_old.rb b/bin/cpp-engine_old.rb new file mode 100644 index 0000000..278b2b9 --- /dev/null +++ b/bin/cpp-engine_old.rb @@ -0,0 +1,48 @@ + + +$core.add_artefact_engine Comfpile::ParserEngine, + file_regex: /^(.+)\.(h|c|cpp)$/, + search_regexes: [ + { + regex: /^#include\s*[<"](?.+)[>"]/, + key: 'include' + }, + { + regex: /\/\/\s*comf\.(?\w+)[=:]\s*(?.+)/ + } +] + +$core.add_artefact_engine do |engine| + + engine.add_recipe(:dependency_list, /^(.+)\.(h|c|cpp)$/) do |match, a| + + a.require_artefact :dependency_analysis, a.target + a.require_artefact :dependency_analysis_include, a.target + + a.add_step do + deps_artefact = find_artefact(:dependency_analysis, @target) + @parameters[:dependency_list] = deps_artefact.dependencies + end + end + + engine.add_recipe(:x86_debug, /^run (.+)/) do |match, a| + + a.require_artefact :parsed, "#{match[1]}.cpp" + end + + engine.add_recipe(:x86_debug, /(.+)\.o$/) do |match, a| + a.require_artefact :sourcefile, "#{match[1]}.cpp" + + a.add_step do + + end + end + + engine.add_recipe(:execute, /(.*)/) do |match, a| + a.parent_artefact :executable, match[0] + + a.add_step do + @parent_artefact + end + end +end \ No newline at end of file diff --git a/bin/cpp_engine.rb b/bin/cpp_engine.rb new file mode 100644 index 0000000..2fbfb13 --- /dev/null +++ b/bin/cpp_engine.rb @@ -0,0 +1,72 @@ + + +$core.add_artefact_engine Comfpile::ParserEngine, + file_regex: /^(.+)\.(h|c|cpp)$/, + search_regexes: [ + { + regex: /^#include\s*[<"](?.+)[>"]/, + key: 'include' + }, + { + regex: /\/\/\s*comf\.(?\w+)[=:]?\s*(?.+)/ + } +] + +$core.add_artefact_engine do |engine| + engine.add_recipe(:object, /^(.*)\.(h|c|cpp)$/) do |match, a| + a.parent_artefact :sourcefile, match[0] + + a.add_step do + `g++ -I #{@build_dir} -c #{@target} -o #{@target}.o` + end + end + + engine.add_recipe(:main_object, /^(.*)\.(h|c|cpp)$/) do |match, a| + a.parent_artefact :sourcefile, match[0] + + a.add_step do + `g++ -DCOMF_MAIN -I #{@build_dir} -c #{@target} -o #{@target}.main.o` + end + end + + engine.add_recipe(:executable) do |match, a| + a.parent_artefact :dependency_analysis, match + + a.add_step do + @object_files = [] + + @parent_artefact.dependencies.each do |dep| + next unless ['.c', '.cpp'].include? File.extname(dep.target) + next if dep.target == @target + + @object_files << require_artefact(:object, dep.target) + end + + @main_object_file = require_artefact(:main_object, @target) + end + + a.add_step do + link_objects = [] + @object_files.each do |obj| + link_objects << obj.target + '.o' + end + link_objects << @main_object_file.target + '.main.o' + + cmd = "g++ #{link_objects.join(' ')} -o #{@target.sub(/\.[^.\/]*\Z/, '')}" + log cmd + + `#{cmd}` + end + end + + engine.add_recipe(:execute) do |match, a| + + a.parent_artefact :executable, match + + a.add_step do + print "\n\nPROGRAM OUTPUT FOR #{match}:\n" + print `./#{@target.sub(/\.[^.\/]*\Z/, '')}` + print "\n\n" + end + end +end \ No newline at end of file diff --git a/bin/ghdl_engine.rb b/bin/ghdl_engine.rb new file mode 100644 index 0000000..637c0a2 --- /dev/null +++ b/bin/ghdl_engine.rb @@ -0,0 +1,94 @@ + + +$core.add_artefact_engine Comfpile::ParserEngine, + file_regex: /^(.+)\.vhd$/, + search_regexes: [ + { + regex: /--+\s*comf\.(?[^:]+)[=:]\s*(?.+)/ + } +] + +$core.add_artefact_engine do |engine| + engine.add_recipe(:ghdl_analysed, /^(.+)\.vhd/) do |match, a| + a.parent_artefact :dependency_analysis_include, a.target + + a.add_step do + @parent_artefact.dependencies.each do |dependency| + next if dependency.target == a.target + + log "Adding dependency for ghdl analysis of #{dependency.target}..." + + require_artefact :ghdl_analysed, dependency.target + end + end + + a.add_step do + work_library = find_parsed_parameter('vhdl.work') + if work_library.nil? + work_library = '' + else + work_library = '--work=' + work_library + end + + cmd = "ghdl -a -fsynopsys --std=08 #{work_library} #{self.file}" + log "Executing: #{cmd}" + + `#{cmd}` + end + end + + engine.add_recipe(:ghdl_elaborated, /^(.+)\.vhd/) do |match, a| + a.parent_artefact :dependency_analysis, a.target + + a.add_step do + dependencies.each do |dep| + require_artefact :ghdl_analysed, dep.target + end + end + + a.add_step do + work_library = find_parsed_parameter('vhdl.work') + if work_library.nil? + work_library = '' + else + work_library = '--work=' + work_library + end + + elaborate_arch = find_parsed_parameter('vhdl.elaborate') || File.basename(@target).chomp(File.extname(@target)) + + cmd = "ghdl -e -fsynopsys --std=08 #{work_library} #{elaborate_arch}" + log "Executing: #{cmd}" + + `#{cmd}` + end + end + + engine.add_recipe :ghdl_run, /^(.+)\.vhd/ do |match, a| + a.parent_artefact :ghdl_elaborated, a.target + + a.add_step do + work_library = find_parsed_parameter('vhdl.work') + if work_library.nil? + work_library = '' + else + work_library = '--work=' + work_library + end + + elaborate_arch = find_parsed_parameter('vhdl.elaborate') || File.basename(@target).chomp(File.extname(@target)) + + @parameters[:ghdl_arch] = elaborate_arch + + cmd = "ghdl -r -fsynopsys --std=08 #{work_library} #{elaborate_arch} --wave=#{elaborate_arch}.ghw" + log "Executing: #{cmd}" + + `#{cmd}` + end + end + + engine.add_recipe(:gtkwave_output, /^(.+)\.vhd/) do |match, a| + a.parent_artefact :ghdl_run, a.target + a.add_step do + `gtkwave #{@parent_artefact[:ghdl_arch]}.ghw` + end + end +end \ No newline at end of file diff --git a/comfpile.code-workspace b/comfpile.code-workspace index 876a149..acc38e0 100644 --- a/comfpile.code-workspace +++ b/comfpile.code-workspace @@ -4,5 +4,9 @@ "path": "." } ], - "settings": {} + "settings": { + "conventionalCommits.scopes": [ + "context" + ] + } } \ No newline at end of file diff --git a/lib/comfpile.rb b/lib/comfpile.rb index 2ea0c64..08bb219 100644 --- a/lib/comfpile.rb +++ b/lib/comfpile.rb @@ -6,4 +6,6 @@ require_relative 'comfpile/core.rb' require_relative 'comfpile/engines/filesource_engine.rb' require_relative 'comfpile/engines/parser_engine.rb' -require_relative 'comfpile/engines/config_engine.rb' \ No newline at end of file +require_relative 'comfpile/engines/config_engine.rb' + +require_relative 'comfpile/context.rb' \ No newline at end of file diff --git a/lib/comfpile/artefact.rb b/lib/comfpile/artefact.rb index 37ed45f..1b0d3a3 100644 --- a/lib/comfpile/artefact.rb +++ b/lib/comfpile/artefact.rb @@ -8,6 +8,9 @@ module Comfpile class Artefact attr_reader :core, :engine + # @return [Comfpile::ContextKey] The context key of this artefact + attr_reader :context + attr_reader :exit_state attr_reader :stage, :target @@ -32,6 +35,8 @@ module Comfpile # linking steps, but the objects can be built separately. attr_reader :referenced_artefacts + attr_reader :build_dir + # ARTEFACT STATES # # The following states are known to the system: @@ -47,7 +52,7 @@ module Comfpile # Meta-States exist: # - in_progress/completed: Anything but/Only succeeded, skipped, failed - def initialize(core, engine, stage, target, **opts) + def initialize(core, engine, stage, target, context, **opts) raise ArgumentError, "Unknown options supplied!" unless opts.empty? @core = core @@ -55,8 +60,7 @@ module Comfpile @stage = stage @target = target - - @age = Time.at(0) + @context = @core.get_context_key context @parent_artefact = nil @@ -77,6 +81,8 @@ module Comfpile @exit_state = nil @running = false + + @build_dir = opts[:build_dir] || @core.get_context_build_dir_path(self) end def [](key) @@ -110,7 +116,7 @@ module Comfpile @log_current_line = text @log_current_level = level - puts "> %-30s %s:\n %s" % [@stage, @target, text] + print "\033[2K> %-30s %s: %s\r" % [@stage, @target, text] nil end @@ -136,26 +142,26 @@ module Comfpile }) end - def parent_artefact(stage, target) - @parent_artefact = require_artefact(stage, target) + def parent_artefact(stage, target, context = @context) + @parent_artefact = require_artefact(stage, target, context) end - def include_artefact(stage, target, **opts) - artefact = require_artefact(stage, target, **opts) + def include_artefact(stage, target, context = @context, **opts) + artefact = require_artefact(stage, target, context, **opts) @included_artefacts << artefact artefact end - def require_artefact(stage, target, **opts) - artefact = reference_artefact(stage, target, required: true, **opts) + def require_artefact(stage, target, context = @context, **opts) + artefact = reference_artefact(stage, target, context, required: true, **opts) @required_artefacts << artefact artefact end - def reference_artefact(stage, target, required: false) - artefact = craft_artefact(stage, target) + def reference_artefact(stage, target, context = @context, required: false) + artefact = craft_artefact(stage, target, context) wait_on(artefact) if required @@ -207,12 +213,12 @@ module Comfpile # found that can craft this, else returns the # created or looked-up artefact. # - def craft_artefact(stage, target) - @core.craft_artefact(stage, target) + def craft_artefact(stage, target, context = @context) + @core.craft_artefact(stage, target, context) end - def find_artefact(stage, target) - @core.find_artefact(stage, target) + def find_artefact(stage, target, context = @context) + @core.find_artefact(stage, target, context) end def waitlist_empty? diff --git a/lib/comfpile/artefact_engine.rb b/lib/comfpile/artefact_engine.rb index 5f83db9..199d9de 100644 --- a/lib/comfpile/artefact_engine.rb +++ b/lib/comfpile/artefact_engine.rb @@ -15,7 +15,9 @@ module Comfpile @recipes = [] end - def craft(stage, target) + def craft(stage, target, context) + context = @core.get_context_key context + @recipes.each do |recipe| match = target @@ -23,16 +25,16 @@ module Comfpile next unless stage == recipe[:stage] end - if r = recipe[:regex] + if not (r = recipe[:regex]).nil? if r.is_a? String next unless target == r - else + elsif r.is_a? Regexp match = r.match target next if match.nil? end end - new_artefact = Artefact.new(@core, self, stage, target) + new_artefact = Artefact.new(@core, self, stage, target, context) item = recipe[:block].call(match, new_artefact) @@ -49,7 +51,7 @@ module Comfpile other.subpriority <=> self.subpriority end - def add_recipe(stage = nil, target_regex, &block) + def add_recipe(stage, target_regex = nil, &block) @recipes << { regex: target_regex, stage: stage, diff --git a/lib/comfpile/context.rb b/lib/comfpile/context.rb new file mode 100644 index 0000000..bfab9e7 --- /dev/null +++ b/lib/comfpile/context.rb @@ -0,0 +1,156 @@ + + +module Comfpile + class ContextKey + attr_reader :provider, :context_hash, :context_symbol + + def initialize(provider, ctx_hash, ctx_symbol) + @provider = provider + + @context_hash = ctx_hash + @context_symbol = ctx_symbol + end + + def to_s + "CTX[" + @context_symbol.to_s + "]" + end + end + + # + # This class represents the configuration of a specific context. + # It has two main jobs: + # To provide functions to set up and merge configuration options + # from the various tags given to it, and + # to give reader options for the various config options. + # + # Most importantly, it contains mechanisms for safely setting up + # configurations and flagging issues. + # + class ContextConfig + attr_reader :context_tag + + attr_reader :config_options + + def initialize(context_tag, parent) + @context_tag = context_tag + + @seeder_blocks = [] + + @config_options = nil + end + + def add_seeder_block(priority, &block) + @seeder_blocks << { + priority: priority, + block: block + } + end + + def resolve_key(key) + if key.is_a? Array + return key.map {|k| k.to_sym } + elsif key.is_a? String + return key.split('.').map {|k| k.to_sym } + elsif key.is_a? Symbol + return resolve_key(key.to_s) + end + end + + def [](key) + @config_options&.dig(*resolve_key(key)) + end + def []=(key, value) + key = resolve_key(key) + + current_hash = (@config_options ||= {}) + key[0..-2].each do |key| + current_hash = (current_hash[key] ||= {}) + next if current_hash.is_a? Hash + raise ArgumentError, "Attempted to set child-parameter of another parameter!" + end + + return @provider.provide_context(new_keys) + end + + def to_s + "CTX[" + @context_symbol.to_s + "]" + end + alias inspect to_s + end + + class ContextKeyProvider + def initialize + @known_context_keys = Hash.new + end + + def symbol_to_keys(symbol) + raise ArgumentError, 'Input symbol needs to be a Symbol!' unless symbol.is_a? Symbol + + out_keys = Hash.new + + symbol.to_s.split(';').each do |key| + if key =~ /(?[^=]+)=(?[^=]+)/ + out_keys[$~['key'].to_sym] = $~['value'].to_s + else + out_keys[key.to_sym] = true + end + end + + out_keys + end + + def sanitize_keys(keys) + out_keys = Hash.new + + keys.each do |key, value| + if value.is_a? TrueClass + out_keys[key.to_sym] = true + elsif (not value.nil?) and (not value.is_a? FalseClass) + out_keys[key.to_sym] = value.to_s + end + end + + out_keys + end + + def keys_to_symbol(keys) + raise ArgumentError, 'Keys needs to be a Hash!' unless keys.is_a? Hash + + keys.keys.sort.map do |key| + value = keys[key] + + if value.is_a? TrueClass + key.to_s + elsif value.is_a? FalseClass + nil + else + "#{key}=#{value}" + end + end.compact.join(';').to_sym + end + + def provide_context(ctx) + return ctx if ctx.is_a? ContextKey + + orig_ctx = ctx + + return @known_context_keys[ctx] if @known_context_keys.include? ctx + + ctx = symbol_to_keys(ctx) if ctx.is_a? Symbol + ctx = sanitize_keys(ctx) + + ctx_symbol = keys_to_symbol(ctx) + + if @known_context_keys.include? ctx + existing_ctx = @known_context_keys[ctx] + @known_context_keys[orig_ctx] = existing_ctx + return existing_ctx + end + + new_ctx = ContextKey.new(self, ctx, ctx_symbol) + + @known_context_keys[ctx] = new_ctx + @known_context_keys[ctx_symbol] = new_ctx + end + end +end \ No newline at end of file diff --git a/lib/comfpile/core.rb b/lib/comfpile/core.rb index f697634..92cacae 100644 --- a/lib/comfpile/core.rb +++ b/lib/comfpile/core.rb @@ -1,11 +1,17 @@ +require 'digest' + require_relative 'artefact_engine.rb' +require_relative 'context.rb' + module Comfpile class Core attr_reader :processing_stack - def initialize() + def initialize(build_dir_path: File.join('/tmp/comfpile/', Digest::MD5.hexdigest(Dir.pwd))) + @build_dir_path = File.expand_path(build_dir_path) + @artefact_engines = [] @artefact_prio_counter = 0 @@ -15,10 +21,18 @@ module Comfpile # Stack-style processing queue for item processing @processing_stack = [] + + @context_key_provider = ContextKeyProvider.new() end - def find_artefact(stage, file = :none) - @artefacts.dig(stage, file) + def get_context_key(key) + return @context_key_provider.provide_context(key) + end + + def find_artefact(stage, file = :none, context = :default) + context = get_context_key(context) + + @artefacts.dig(context, stage, file) end # Create or find a new artefact. @@ -36,17 +50,20 @@ module Comfpile # # @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) + def craft_artefact(stage, file = :none, context = :default) + context = get_context_key(context) + + artefact = find_artefact(stage, file, context) return artefact unless artefact.nil? @artefact_engines.each do |engine| - artefact = engine.craft stage, file + artefact = engine.craft stage, file, context if artefact - @artefacts[stage] ||= {} - @artefacts[stage][file] = artefact + @artefacts[context] ||= {} + @artefacts[context][stage] ||= {} + @artefacts[context][stage][file] = artefact @processing_stack.push artefact @@ -57,13 +74,16 @@ module Comfpile nil end - def add_artefact(stage, file = :none, engine: nil, artefact_class: Comfpile::Artefact) - return unless find_artefact(stage, file).nil? + def add_artefact(stage, file = :none, context = :default, engine: nil, artefact_class: Comfpile::Artefact) + context = get_context_key context - a = Artefact.new(self, engine, stage, file); + return unless find_artefact(stage, file, context).nil? + + a = Artefact.new(self, engine, stage, file, context); - @artefacts[stage] ||= {} - @artefacts[stage][file] = a + @artefacts[context] ||= {} + @artefacts[context][stage] ||= {} + @artefacts[context][stage][file] = a yield(a) if block_given? @@ -123,6 +143,12 @@ module Comfpile nil end + def get_context_build_dir_path(context) + context = context.context if context.is_a? Artefact + + File.expand_path(File.join(@build_dir_path, context.to_s)) + end + def execute_step return if @processing_stack.empty? # puts "Got #{@processing_stack.length} items..." @@ -135,9 +161,14 @@ module Comfpile # puts "Processing artefact #{artefact.stage} #{artefact.target}" - begin - artefact.execute_step - rescue ArtefactExecSkipError + build_path = get_context_build_dir_path(artefact) + FileUtils.mkpath build_path + + Dir.chdir build_path do + begin + artefact.execute_step + rescue ArtefactExecSkipError + end end nil diff --git a/lib/comfpile/engines/config_engine.rb b/lib/comfpile/engines/config_engine.rb index 7c76013..ec98a56 100644 --- a/lib/comfpile/engines/config_engine.rb +++ b/lib/comfpile/engines/config_engine.rb @@ -25,7 +25,9 @@ module Comfpile end @config_file_artefact = craft_artefact(:sourcefile, File.join(@target, "comf.yml")) - wait_on @config_file_artefact + return if @config_file_artefact.nil? + + wait_on @config_file_artefact add_step do unless @config_file_artefact.nil? @@ -61,10 +63,10 @@ module Comfpile end - def craft(stage, target) + def craft(stage, target, context) return unless stage == :config_file - ConfigArtefact.new(@core, self, stage, target) + ConfigArtefact.new(@core, self, stage, target, context) end end end \ No newline at end of file diff --git a/lib/comfpile/engines/filesource_engine.rb b/lib/comfpile/engines/filesource_engine.rb index 81d8482..eef059f 100644 --- a/lib/comfpile/engines/filesource_engine.rb +++ b/lib/comfpile/engines/filesource_engine.rb @@ -6,18 +6,23 @@ module Comfpile alias path filename alias file filename - def initialize(*args, file: filename, **opts) + def initialize(*args, file: nil, **opts) super(*args, **opts) - @filename = file + @original_filename = file - unless File.exist? @filename + unless File.exist? @original_filename fail! "File could not be loaded!" return end - # TODO add actual file reading/copying to build dir + @filename = @target + + add_step do + FileUtils.mkpath File.dirname(@filename) + FileUtils.cp @original_filename, @filename + end end def mtime @@ -32,14 +37,14 @@ module Comfpile @root_path = options[:root_path] end - def craft(stage, target) + def craft(stage, target, context) return nil unless stage == :sourcefile - full_path = File.join(@root_path, target) + full_path = File.expand_path(File.join(@root_path, target)) return nil unless File.exists? full_path - FilesourceArtefact.new(@core, self, stage, target, + FilesourceArtefact.new(@core, self, stage, target, context, file: full_path); end end diff --git a/lib/comfpile/engines/parser_engine.rb b/lib/comfpile/engines/parser_engine.rb index 3893d5f..7197829 100644 --- a/lib/comfpile/engines/parser_engine.rb +++ b/lib/comfpile/engines/parser_engine.rb @@ -117,7 +117,7 @@ module Comfpile end def sourcefile_dependency_step - ['include', 'require', 'reference'].map do |key| + ['include', 'require', 'reference'].each do |key| key_artefacts = resolve_sourcefile(find_parsed_parameters(key), optional: key == 'include') @parsed_dependencies[key] = key_artefacts.map { |key| craft_artefact(:parsed, key) } @@ -205,28 +205,28 @@ module Comfpile raise ArgumentError, "Missing regex parsing array!" unless @search_regexes.is_a? Array end - def generate_parser_artefact(stage, target) + def generate_parser_artefact(stage, target, context) match = @input_file_regex.match target return nil if match.nil? - ParserArtefact.new(@core, self, stage, target, + ParserArtefact.new(@core, self, stage, target, context, search_regexes: @search_regexes) end - def generate_dependency_analysis_artefact(stage, target, **opts) + def generate_dependency_analysis_artefact(stage, target, context, **opts) match = @input_file_regex.match target return nil if match.nil? - DependencyAnalysisArtefact.new(@core, self, stage, target, **opts) + DependencyAnalysisArtefact.new(@core, self, stage, target, context, **opts) end - def craft(stage, target) + def craft(stage, target, context) case stage when :parsed - generate_parser_artefact(stage, target) + generate_parser_artefact(stage, target, context) when /^dependency_analysis(?:_(.+))?$/ traverse = $1&.split('_') - generate_dependency_analysis_artefact(stage, target, traverse: traverse) + generate_dependency_analysis_artefact(stage, target, context, traverse: traverse) else nil end diff --git a/lib/comfpile/old_context.rb b/lib/comfpile/old_context.rb new file mode 100644 index 0000000..cbc70a0 --- /dev/null +++ b/lib/comfpile/old_context.rb @@ -0,0 +1,171 @@ + + +module Comfpile + class ContextTag + def initialize(tag) + @@known_tags ||= {} + + case tag + when Symbol, String + precompiled = @@known_tags[tag.to_sym] + + if not precompiled.nil? + @tag = precompiled[:tag] + @tag_array = precompiled[:array] + else + @tag_array = tag.to_s.split('_').uniq.sort + + end + when Array + @tag_array = tag.map(&:to_s).uniq.sort + @tag = @tag_array + end + + @tag ||= @tag_array.join('_').to_sym + + tag_data = { + tag: @tag, + array: @ŧag_array + } + + @@known_tags[@tag] ||= tag_data + @@known_tags[tag] ||= tag_data + end + + + def self.tagify(tag) + @known_tags ||= {} + + + end + + end + + # + # This class represents the configuration of a specific context. + # It has two main jobs: + # To provide functions to set up and merge configuration options + # from the various tags given to it, and + # to give reader options for the various config options. + # + # Most importantly, it contains mechanisms for safely setting up + # configurations and flagging issues. + # + class ContextConfig + attr_reader :context_tag + + attr_reader :config_options + + def initialize(context_tag, parent) + @context_tag = context_tag + + @seeder_blocks = [] + + @config_options = nil + end + + def add_seeder_block(priority, &block) + @seeder_blocks << { + priority: priority, + block: block + } + end + + def resolve_key(key) + if key.is_a? Array + return key.map {|k| k.to_sym } + elsif key.is_a? String + return key.split('.').map {|k| k.to_sym } + elsif key.is_a? Symbol + return resolve_key(key.to_s) + end + end + + def [](key) + @config_options&.dig(*resolve_key(key)) + end + def []=(key, value) + key = resolve_key(key) + + current_hash = (@config_options ||= {}) + key[0..-2].each do |key| + current_hash = (current_hash[key] ||= {}) + next if current_hash.is_a? Hash + raise ArgumentError, "Attempted to set child-parameter of another parameter!" + end + + current_hash[key[-1]] = value + + value + end + alias set []= + + def set_flag(key, value) + set resolve_key(key).append(value), true + end + def get_flags(key) + (@config_options&.dig(*resolve_key(key)) || {}).keys + end + + def dig(arg0, *args) + @config_options&.dig(arg0, *args) + end + + def append(key, value) + key = resolve_key key + array = @config_options&.dig(*key) + + if array.nil? + array = set key, [] + end + + raise ArgumentError, "Attempted to append to a non-list parameter!" unless array.is_a? Array + + array << value + end + alias append_to append + + def assert(orig_key, value) + key = resolve_key orig_key + current_value = @config_options&.dig(*key) + + if current_value.nil? + set key, value + elsif current_value != value + raise ArgumentError, "Config key assertion failed! #{orig_key} should be #{value}, is #{current_value}!" + end + end + + def build_config + @config_options = nil + + @seeder_blocks.sort { |block| block[:priority] }.each do |block| + self.instance_exec(block, &block[:block]) + end + + self.freeze + end + end + + class ContextConfigContainer + def initialize + @context_configs_seeds = [] + end + + def find_seeds_for(context_tag) + out = [] + + @context_configs_seeds.each do |seed| + seed_tags = seed[:context_tags] + + seed_tags.each do |tag| + + end + end + end + + def add_to_config(context_config) + raise ArgumentError, "No context supplied!" unless context_config.is_a? ContextConfig + end + end +end \ No newline at end of file diff --git a/test/faux_build_dir/spi_master_tb.vhd b/spi_master_tb.vhd similarity index 100% rename from test/faux_build_dir/spi_master_tb.vhd rename to spi_master_tb.vhd diff --git a/test/faux_build_dir/lib_test/comf.yml b/test/faux_build_dir/cpp/lib_test/comf.yml similarity index 100% rename from test/faux_build_dir/lib_test/comf.yml rename to test/faux_build_dir/cpp/lib_test/comf.yml diff --git a/test/faux_build_dir/lib_test/test.cpp b/test/faux_build_dir/cpp/lib_test/test.cpp similarity index 100% rename from test/faux_build_dir/lib_test/test.cpp rename to test/faux_build_dir/cpp/lib_test/test.cpp diff --git a/test/faux_build_dir/lib_test/test.h b/test/faux_build_dir/cpp/lib_test/test.h similarity index 100% rename from test/faux_build_dir/lib_test/test.h rename to test/faux_build_dir/cpp/lib_test/test.h diff --git a/test/faux_build_dir/lib_test/test_settings.h b/test/faux_build_dir/cpp/lib_test/test_settings.h similarity index 100% rename from test/faux_build_dir/lib_test/test_settings.h rename to test/faux_build_dir/cpp/lib_test/test_settings.h diff --git a/test/faux_build_dir/cpp/main.cpp b/test/faux_build_dir/cpp/main.cpp new file mode 100644 index 0000000..b9976e1 --- /dev/null +++ b/test/faux_build_dir/cpp/main.cpp @@ -0,0 +1,23 @@ + +// Your First C++ Program + +#include + +#include "main.h" + +void print_bar() { + std::cout << "Bar"; +} + +#ifdef COMF_MAIN + +int main() { + + print_bar(); + print_foo(); + + std::cout << "Hello World!"; + return 0; +} + +#endif \ No newline at end of file diff --git a/test/faux_build_dir/cpp/main.h b/test/faux_build_dir/cpp/main.h new file mode 100644 index 0000000..773e68f --- /dev/null +++ b/test/faux_build_dir/cpp/main.h @@ -0,0 +1,9 @@ + + +#pragma once + +// comf.reference main.cpp +// comf.reference main2.cpp + +void print_foo(); +void print_bar(); \ No newline at end of file diff --git a/test/faux_build_dir/cpp/main2.cpp b/test/faux_build_dir/cpp/main2.cpp new file mode 100644 index 0000000..20bc337 --- /dev/null +++ b/test/faux_build_dir/cpp/main2.cpp @@ -0,0 +1,22 @@ + +// Your First C++ Program + +#include + +#include "main.h" + +void print_foo() { + std::cout << "Foo"; +} + +#ifdef COMF_MAIN + +int main() { + print_foo(); + print_bar(); + + std::cout << "Hello World!"; + return 0; +} + +#endif \ No newline at end of file diff --git a/test/faux_build_dir/main.cpp b/test/faux_build_dir/main.cpp deleted file mode 100644 index 90e1541..0000000 --- a/test/faux_build_dir/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ - - -#include "main.h" -#include - -int main() { - return 0; -} \ No newline at end of file diff --git a/test/faux_build_dir/main.h b/test/faux_build_dir/main.h deleted file mode 100644 index 5c55a78..0000000 --- a/test/faux_build_dir/main.h +++ /dev/null @@ -1,5 +0,0 @@ - - -#pragma once - -#include "lib_test/test.h" \ No newline at end of file diff --git a/test/faux_build_dir/UQDS_specifics_pkg.vhd b/test/faux_build_dir/vhdl/UQDS_specifics_pkg.vhd similarity index 100% rename from test/faux_build_dir/UQDS_specifics_pkg.vhd rename to test/faux_build_dir/vhdl/UQDS_specifics_pkg.vhd diff --git a/test/faux_build_dir/vhdl/comf.yml b/test/faux_build_dir/vhdl/comf.yml new file mode 100644 index 0000000..2646a61 --- /dev/null +++ b/test/faux_build_dir/vhdl/comf.yml @@ -0,0 +1 @@ +test: Hellooooo diff --git a/test/faux_build_dir/spi_master.vhd b/test/faux_build_dir/vhdl/spi_master.vhd similarity index 100% rename from test/faux_build_dir/spi_master.vhd rename to test/faux_build_dir/vhdl/spi_master.vhd diff --git a/test/faux_build_dir/vhdl/spi_master_tb.vhd b/test/faux_build_dir/vhdl/spi_master_tb.vhd new file mode 100644 index 0000000..e299bcb --- /dev/null +++ b/test/faux_build_dir/vhdl/spi_master_tb.vhd @@ -0,0 +1,108 @@ + +-- comf.include: UQDS_specifics_pkg.vhd + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library uqdslib; +use uqdslib.UQDS_Specifics_pkg.all; + +entity spi_master_tb is +end; + +architecture bench of spi_master_tb is + -- Clock period + constant clk_period : time := 5 ns; + -- Generics + constant clkdiv : integer := 40; + constant clk_idle : std_logic := '1'; + + -- Ports + signal rst : std_logic := '0'; + signal clk : std_logic; + signal data_tx : std_logic_vector(7 downto 0); + signal data_rx : std_logic_vector(7 downto 0); + signal send_request : std_logic; + signal byte_tx_complete : std_logic; + signal byte_rx_complete : std_logic; + signal pin_mosi : std_logic; + signal pin_miso : std_logic; + signal pin_clk : std_logic; + + signal test_done : std_logic := '0'; + +begin + + spi_master_inst : uqdslib.UQDS_Specifics_pkg.spi_master + generic map ( + clkdiv => clkdiv, + clk_idle => clk_idle + ) + port map ( + rst => rst, + clk => clk, + spi_control_to.data_tx => data_tx, + spi_control_to.send_request => send_request, + spi_control_from.data_rx => data_rx, + spi_control_from.byte_tx_complete => byte_tx_complete, + spi_control_from.byte_rx_complete => byte_rx_complete, + pin_mosi => pin_mosi, + pin_miso => pin_miso, + pin_clk => pin_clk + ); + + pin_miso <= pin_mosi; + + clk_process : process + begin + clk <= '1'; + wait for clk_period/2; + clk <= '0'; + wait for clk_period/2; + + if(test_done) then wait; end if; + end process clk_process; + + data_process : process + begin + + rst <= '1'; + wait for 100 ns; + rst <= '0'; + + for i in 0 to 4 loop + data_tx <= std_logic_vector(to_unsigned(i, data_tx'length)); + send_request <= '1'; + wait until falling_edge(byte_tx_complete) for 1 ms; + send_request <= '0'; + end loop; + + for i in 0 to 4 loop + data_tx <= std_logic_vector(to_unsigned(i, data_tx'length)); + send_request <= '1'; + wait until falling_edge(byte_tx_complete) for 1 ms; + send_request <= '0'; + + wait for 1.5 us; + end loop; + + data_tx <= std_logic_vector(to_unsigned(69, data_tx'length)); + send_request <= '1'; + wait until falling_edge(byte_tx_complete) for 1 ms; + send_request <= '0'; + + wait for 400 ns; + rst <= '1'; + wait for 100 ns; + rst <= '0'; + wait for 3 us; + + test_done <= '1'; + + wait until falling_edge(byte_rx_complete) for 1 us; + + wait; + + end process data_process; +end;