multiversal/make-multiverse.rb
2019-11-10 17:08:41 +01:00

200 lines
5.5 KiB
Ruby

require 'yaml'
require 'set'
require 'fileutils'
require 'optparse'
require './generator'
require './cincludes'
require './executor'
BUILTIN_NAMES=Set.new [
"uint8_t", "uint16_t", "uint32_t", "uint64_t",
"int8_t", "int16_t", "int32_t", "int64_t",
"char",
"ProcPtr",
"void",
"const",
"bool",
"sizeof",
"double"
]
def hexlit(thing, sz=16)
case thing
when Integer
digits = thing.to_s(16).upcase
return "0x" + "0" * [sz/4 - digits.length, 0].max + digits
else
return thing.to_s
end
end
def main_elem(item)
item.each do |key, value|
next if ["only-for", "not-for", "api"].include? key
return key, value
end
return nil
end
$global_name_map = {}
class HeaderFile
attr_reader :file, :name, :declared_names, :required_names, :included, :included_why, :data
def initialize(file, filter_key:nil)
@file = file
@name = File.basename(@file, ".yaml")
@data = YAML::load(File.read(@file))
@filter_key = filter_key.downcase
@data.reject! do |item|
onlyfor = item["only-for"]
notfor = item["not-for"]
onlyfor = [onlyfor] if onlyfor and not onlyfor.is_a? Array
notfor = [notfor] if notfor and not notfor.is_a? Array
next true if onlyfor and not onlyfor.map {|x| x.downcase}.index(filter_key)
next true if notfor and notfor.map {|x| x.downcase}.index(filter_key)
next false
end
@data.each do |item|
item.reject! { |k| k == "only-for" or k == "not-for" }
end
collect_dependencies
@included = nil
@included_why = nil
end
private
def collect_dep(str)
tmp = str.to_s.dup
tmp.gsub!(/'[^']+'/,"")
tmp.scan(/[a-zA-Z_][a-zA-Z0-9_]*/).each do |x|
@required_names << x unless BUILTIN_NAMES.member?(x)
end
end
def collect_members_dependencies(members)
members.each do |member|
collect_dep(member["type"]) if member["type"]
collect_members_dependencies(member["struct"]) if member["struct"]
collect_members_dependencies(member["union"]) if member["union"]
collect_dep(member["common"]) if member["common"]
end
end
def collect_dependencies
@declared_names = Set.new
@required_names = Set.new
@data.each do |item|
key, value = main_elem(item)
@declared_names << value["name"] if value["name"]
$global_name_map[value["name"]] = item if value["name"]
case key
when "enum"
value["values"].each do |val|
@declared_names << val["name"]
collect_dep(val["value"]) if val["value"]
end
when "typedef"
collect_dep(value["type"])
when "struct", "union"
collect_members_dependencies value["members"] if value["members"]
when "function", "funptr"
collect_dep(value["return"]) if value["return"]
(value ["args"] or []).each do |arg|
collect_dep(arg["type"])
end
@declared_names.merge value["variants"] if value["variants"]
end
end
@required_names = @required_names - @declared_names
end
public
def collect_includes(all_declared_names)
@included = Set.new
@included_why = {}
@required_names.each do |n|
where = all_declared_names[n]
if where then
@included << where
@included_why[where] = Set.new unless @included_why[where]
@included_why[where] << n
elsif @filter_key == "executor" and n == "Point" then
else
print "??????????????? Where is #{n}\n"
end
end
end
end
class Defs
attr_reader :headers, :declared_names, :topsort
def initialize(filter_key:nil)
@headers = {}
@declared_names = {}
print "Reading yaml files..."
Dir.glob('defs/*.yaml') do |file|
header = HeaderFile.new(file, filter_key: filter_key)
@headers[header.name] = header
header.declared_names.each { |n| @declared_names[n] = header.name }
end
print "#{headers.size} files.\n"
headers.each do |name, header|
header.collect_includes(declared_names)
end
@topsort = []
done = Set.new
headers.each do |name, header|
do_topsort(name, done)
end
end
private
def do_topsort(name, done, active=Set.new, stack=[])
print "INCLUDE CYCLE #{stack.join('=>')} => #{name}\n" if active.include?(name)
return if done.include?(name)
active << name
done << name
stack << name
@headers[name].included.each do |incname|
do_topsort(incname, done, active, stack)
end
@topsort << name
active.delete?(name)
stack.pop
end
end
Options = Struct.new(:output_dir, :generator)
$options = Options.new
$options.output_dir = "./out"
OptionParser.new do |op|
op.on('-o', '--output-dir=PATH', String) { |a| $options.output_dir = a }
op.on('-G', '--generator=GEN', {
'CIncludes'=>CIncludesGenerator,
'Executor'=>ExecutorGenerator
}) { |a| $options.generator = a }
end.parse!
defs = Defs.new(filter_key: $options.generator.filter_key)
$options.generator.new.generate(defs)