200 lines
5.5 KiB
Ruby
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)
|