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
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
/.bundle/
|
||||||
|
/.yardoc
|
||||||
|
/_yardoc/
|
||||||
|
/coverage/
|
||||||
|
/doc/
|
||||||
|
/pkg/
|
||||||
|
/spec/reports/
|
||||||
|
/tmp/
|
13
.rubocop.yml
Normal file
13
.rubocop.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
AllCops:
|
||||||
|
TargetRubyVersion: 2.6
|
||||||
|
|
||||||
|
Style/StringLiterals:
|
||||||
|
Enabled: true
|
||||||
|
EnforcedStyle: double_quotes
|
||||||
|
|
||||||
|
Style/StringLiteralsInInterpolation:
|
||||||
|
Enabled: true
|
||||||
|
EnforcedStyle: double_quotes
|
||||||
|
|
||||||
|
Layout/LineLength:
|
||||||
|
Max: 120
|
14
Gemfile
Normal file
14
Gemfile
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
source "https://rubygems.org"
|
||||||
|
|
||||||
|
# Specify your gem's dependencies in comfpile.gemspec
|
||||||
|
gemspec
|
||||||
|
|
||||||
|
gem "rake", "~> 13.0"
|
||||||
|
gem "pry"
|
||||||
|
|
||||||
|
gem "minitest", "~> 5.0"
|
||||||
|
gem "debug", "~> 1.0"
|
||||||
|
|
||||||
|
gem "rubocop", "~> 1.21"
|
59
Gemfile.lock
Normal file
59
Gemfile.lock
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
PATH
|
||||||
|
remote: .
|
||||||
|
specs:
|
||||||
|
comfpile (0.1.0)
|
||||||
|
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
ast (2.4.2)
|
||||||
|
coderay (1.1.3)
|
||||||
|
debug (1.6.2)
|
||||||
|
irb (>= 1.3.6)
|
||||||
|
reline (>= 0.3.1)
|
||||||
|
io-console (0.5.11)
|
||||||
|
irb (1.4.1)
|
||||||
|
reline (>= 0.3.0)
|
||||||
|
json (2.6.3)
|
||||||
|
method_source (1.0.0)
|
||||||
|
minitest (5.18.0)
|
||||||
|
parallel (1.22.1)
|
||||||
|
parser (3.2.1.1)
|
||||||
|
ast (~> 2.4.1)
|
||||||
|
pry (0.14.1)
|
||||||
|
coderay (~> 1.1)
|
||||||
|
method_source (~> 1.0)
|
||||||
|
rainbow (3.1.1)
|
||||||
|
rake (13.0.6)
|
||||||
|
regexp_parser (2.7.0)
|
||||||
|
reline (0.3.1)
|
||||||
|
io-console (~> 0.5)
|
||||||
|
rexml (3.2.5)
|
||||||
|
rubocop (1.48.0)
|
||||||
|
json (~> 2.3)
|
||||||
|
parallel (~> 1.10)
|
||||||
|
parser (>= 3.2.0.0)
|
||||||
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
regexp_parser (>= 1.8, < 3.0)
|
||||||
|
rexml (>= 3.2.5, < 4.0)
|
||||||
|
rubocop-ast (>= 1.26.0, < 2.0)
|
||||||
|
ruby-progressbar (~> 1.7)
|
||||||
|
unicode-display_width (>= 2.4.0, < 3.0)
|
||||||
|
rubocop-ast (1.27.0)
|
||||||
|
parser (>= 3.2.1.0)
|
||||||
|
ruby-progressbar (1.13.0)
|
||||||
|
unicode-display_width (2.4.2)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
x86_64-linux
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
comfpile!
|
||||||
|
debug (~> 1.0)
|
||||||
|
minitest (~> 5.0)
|
||||||
|
pry
|
||||||
|
rake (~> 13.0)
|
||||||
|
rubocop (~> 1.21)
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.3.22
|
29
README.md
Normal file
29
README.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# Comfpile
|
||||||
|
|
||||||
|
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/comfpile`. To experiment with that code, run `bin/console` for an interactive prompt.
|
||||||
|
|
||||||
|
TODO: Delete this and the text above, and describe your gem
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install the gem and add to the application's Gemfile by executing:
|
||||||
|
|
||||||
|
$ bundle add comfpile
|
||||||
|
|
||||||
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
||||||
|
|
||||||
|
$ gem install comfpile
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
TODO: Write usage instructions here
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
||||||
|
|
||||||
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/comfpile.
|
16
Rakefile
Normal file
16
Rakefile
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "bundler/gem_tasks"
|
||||||
|
require "rake/testtask"
|
||||||
|
|
||||||
|
Rake::TestTask.new(:test) do |t|
|
||||||
|
t.libs << "test"
|
||||||
|
t.libs << "lib"
|
||||||
|
t.test_files = FileList["test/**/test_*.rb"]
|
||||||
|
end
|
||||||
|
|
||||||
|
require "rubocop/rake_task"
|
||||||
|
|
||||||
|
RuboCop::RakeTask.new
|
||||||
|
|
||||||
|
task default: %i[test rubocop]
|
101
bin/console
Executable file
101
bin/console
Executable file
|
@ -0,0 +1,101 @@
|
||||||
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "bundler/setup"
|
||||||
|
require "comfpile"
|
||||||
|
|
||||||
|
# You can add fixtures and/or initialization code here to make experimenting
|
||||||
|
# with your gem easier. You can also use a different console, if you like.
|
||||||
|
|
||||||
|
$core = Comfpile::Core.new()
|
||||||
|
|
||||||
|
`mkdir /tmp/test`
|
||||||
|
`touch /tmp/test/main.cpp`
|
||||||
|
|
||||||
|
`mkdir /tmp/test2/`
|
||||||
|
`touch /tmp/test2/main.cpp`
|
||||||
|
|
||||||
|
$core.add_artefact_engine Comfpile::FilesourceEngine, root_path: "/tmp/test2"
|
||||||
|
$core.add_artefact_engine Comfpile::FilesourceEngine, root_path: "/tmp/test"
|
||||||
|
|
||||||
|
|
||||||
|
$core.add_artefact_engine do |engine|
|
||||||
|
engine.add_recipe(:parsed, /^(.+)\.(h|c|cpp)$/) do |match, a|
|
||||||
|
|
||||||
|
a.parent_artefact :sourcefile, a.target
|
||||||
|
|
||||||
|
a.add_step do
|
||||||
|
puts "Parsing file #{@target}..."
|
||||||
|
|
||||||
|
@linked_artefacts = []
|
||||||
|
|
||||||
|
File.readlines(@required_artefacts[:sourcefile][@target][:file]).each do |l|
|
||||||
|
case l
|
||||||
|
when /^#include\s*[<"](.+)[>"]/
|
||||||
|
|
||||||
|
puts "Got include for file #{$1}!"
|
||||||
|
@linked_artefacts << craft_artefact(:parsed, $1)
|
||||||
|
|
||||||
|
when /\/\/+\s*require\s*[<"]((?:.+)\.(?:c|cpp))[>"]/
|
||||||
|
puts "Got require for file #{$1}!"
|
||||||
|
@linked_artefacts << require_artefact(:parsed, $1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
engine.add_recipe(:dependency_list, /^(.+)\.(h|c|cpp)$/) do |match, a|
|
||||||
|
a.parent_artefact :parsed, a.target
|
||||||
|
|
||||||
|
a.add_step do
|
||||||
|
puts "Generating dependency list for #{@target}..."
|
||||||
|
|
||||||
|
parsed_marker = {}
|
||||||
|
parsing_list = [@parent_artefact]
|
||||||
|
|
||||||
|
loop do
|
||||||
|
break if parsing_list.empty?
|
||||||
|
a = parsing_list.pop
|
||||||
|
|
||||||
|
next if a.nil?
|
||||||
|
next unless a.stage == :parsed
|
||||||
|
next unless a.succeeded?
|
||||||
|
|
||||||
|
next if parsed_marker[a.target]
|
||||||
|
parsed_marker[a.target] = true
|
||||||
|
|
||||||
|
parsing_list += a.linked_artefacts
|
||||||
|
end
|
||||||
|
|
||||||
|
@parameters[:dependency_list] = parsed_marker.keys
|
||||||
|
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
|
||||||
|
|
||||||
|
$tst = $core.craft_artefact(:dependency_list, "main.cpp");
|
||||||
|
|
||||||
|
50.times do
|
||||||
|
$core.execute_step
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "Dependency list is: #{$tst[:dependency_list]}"
|
||||||
|
|
||||||
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
||||||
|
require "pry"
|
||||||
|
Pry.start
|
||||||
|
|
8
bin/setup
Executable file
8
bin/setup
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
set -vx
|
||||||
|
|
||||||
|
bundle install
|
||||||
|
|
||||||
|
# Do any other automated setup that you need to do here
|
8
comfpile.code-workspace
Normal file
8
comfpile.code-workspace
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": ".."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {}
|
||||||
|
}
|
35
comfpile.gemspec
Normal file
35
comfpile.gemspec
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative "lib/comfpile/version"
|
||||||
|
|
||||||
|
Gem::Specification.new do |spec|
|
||||||
|
spec.name = "comfpile"
|
||||||
|
spec.version = Comfpile::VERSION
|
||||||
|
spec.authors = ["David Bailey"]
|
||||||
|
spec.email = ["davidbailey.2889@gmail.com"]
|
||||||
|
|
||||||
|
spec.summary = "Comfortably compile source code together"
|
||||||
|
spec.description = "Compile based on simple, understandable source dependencies in-sourcecode, rather than a makefile or whatever other hot mess."
|
||||||
|
spec.homepage = "https://github.com/lucidergs/comfpile"
|
||||||
|
spec.required_ruby_version = ">= 2.6.0"
|
||||||
|
|
||||||
|
spec.metadata["homepage_uri"] = spec.homepage
|
||||||
|
spec.metadata["source_code_uri"] = spec.homepage
|
||||||
|
|
||||||
|
# Specify which files should be added to the gem when it is released.
|
||||||
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
||||||
|
spec.files = Dir.chdir(__dir__) do
|
||||||
|
`git ls-files -z`.split("\x0").reject do |f|
|
||||||
|
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
spec.bindir = "exe"
|
||||||
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
||||||
|
spec.require_paths = ["lib"]
|
||||||
|
|
||||||
|
# Uncomment to register a new dependency of your gem
|
||||||
|
# spec.add_dependency "example-gem", "~> 1.0"
|
||||||
|
|
||||||
|
# For more information and examples about making a new gem, check out our
|
||||||
|
# guide at: https://bundler.io/guides/creating_gem.html
|
||||||
|
end
|
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
|
4
sig/comfpile.rbs
Normal file
4
sig/comfpile.rbs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module Comfpile
|
||||||
|
VERSION: String
|
||||||
|
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
|
||||||
|
end
|
13
test/test_comfpile.rb
Normal file
13
test/test_comfpile.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class TestComfpile < Minitest::Test
|
||||||
|
def test_that_it_has_a_version_number
|
||||||
|
refute_nil ::Comfpile::VERSION
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_it_does_something_useful
|
||||||
|
assert false
|
||||||
|
end
|
||||||
|
end
|
6
test/test_helper.rb
Normal file
6
test/test_helper.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
|
||||||
|
require "comfpile"
|
||||||
|
|
||||||
|
require "minitest/autorun"
|
Loading…
Add table
Add a link
Reference in a new issue