2011-12-24 03:37:00 -07:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
2011-12-29 05:30:55 -07:00
|
|
|
BEGIN {
|
|
|
|
our $script_root = '.';
|
|
|
|
if ($0 =~ /^(.*)[\\\/][^\\\/]*$/) {
|
|
|
|
$script_root = $1;
|
|
|
|
unshift @INC, $1;
|
2011-12-24 05:22:10 -07:00
|
|
|
}
|
2011-12-29 05:30:55 -07:00
|
|
|
};
|
2011-12-24 03:37:00 -07:00
|
|
|
|
2011-12-29 05:30:55 -07:00
|
|
|
use XML::LibXML;
|
|
|
|
use XML::LibXSLT;
|
2011-12-24 03:37:00 -07:00
|
|
|
|
2011-12-29 05:30:55 -07:00
|
|
|
use Common;
|
2011-12-24 03:37:00 -07:00
|
|
|
|
2011-12-29 05:30:55 -07:00
|
|
|
use Enum;
|
|
|
|
use Bitfield;
|
|
|
|
use StructType;
|
2011-12-24 03:37:00 -07:00
|
|
|
|
2011-12-29 05:30:55 -07:00
|
|
|
my $input_dir = $ARGV[0] || '.';
|
|
|
|
my $output_dir = $ARGV[1] || 'codegen';
|
2011-12-24 03:37:00 -07:00
|
|
|
|
2011-12-29 05:30:55 -07:00
|
|
|
$main_namespace = $ARGV[2] || 'df';
|
|
|
|
$export_prefix = 'DFHACK_EXPORT ';
|
2011-12-24 03:37:00 -07:00
|
|
|
|
|
|
|
# Collect all type definitions from XML files
|
|
|
|
|
2011-12-29 05:30:55 -07:00
|
|
|
our $script_root;
|
|
|
|
my $parser = XML::LibXML->new();
|
|
|
|
my $xslt = XML::LibXSLT->new();
|
|
|
|
my @transforms =
|
|
|
|
map { $xslt->parse_stylesheet_file("$script_root/$_"); }
|
|
|
|
('lower-1.xslt', 'lower-2.xslt');
|
|
|
|
my @documents;
|
2011-12-24 03:37:00 -07:00
|
|
|
|
2011-12-29 05:30:55 -07:00
|
|
|
for my $fn (sort { $a cmp $b } glob "$input_dir/*.xml") {
|
2011-12-24 03:37:00 -07:00
|
|
|
local $filename = $fn;
|
2011-12-29 05:30:55 -07:00
|
|
|
my $doc = $parser->parse_file($filename);
|
|
|
|
$doc = $_->transform($doc) for @transforms;
|
|
|
|
|
|
|
|
push @documents, $doc;
|
|
|
|
add_type_to_hash $_ foreach $doc->findnodes('/ld:data-definition/ld:global-type');
|
2011-12-24 03:37:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
# Generate text representations
|
|
|
|
|
|
|
|
my %type_handlers = (
|
|
|
|
'enum-type' => \&render_enum_type,
|
|
|
|
'bitfield-type' => \&render_bitfield_type,
|
|
|
|
'class-type' => \&render_struct_type,
|
|
|
|
'struct-type' => \&render_struct_type,
|
|
|
|
);
|
|
|
|
|
|
|
|
my %type_data;
|
|
|
|
|
|
|
|
for my $name (sort { $a cmp $b } keys %types) {
|
|
|
|
local $typename = $name;
|
|
|
|
local $filename = $type_files{$typename};
|
|
|
|
local %weak_refs;
|
|
|
|
local %strong_refs;
|
|
|
|
|
|
|
|
eval {
|
|
|
|
my $type = $types{$typename};
|
2011-12-29 05:30:55 -07:00
|
|
|
my $meta = $type->getAttribute('ld:meta') or die "Null meta";
|
2011-12-24 03:37:00 -07:00
|
|
|
|
|
|
|
# Emit the actual type definition
|
|
|
|
my @code = with_emit {
|
|
|
|
with_anon {
|
2011-12-29 05:30:55 -07:00
|
|
|
my $handler = $type_handlers{$meta} or die "Unknown type meta: $meta\n";
|
|
|
|
$handler->($type);
|
2011-12-24 03:37:00 -07:00
|
|
|
};
|
|
|
|
} 2;
|
2011-12-29 05:30:55 -07:00
|
|
|
|
2011-12-24 03:37:00 -07:00
|
|
|
delete $weak_refs{$name};
|
|
|
|
delete $strong_refs{$name};
|
|
|
|
|
|
|
|
# Add wrapping
|
|
|
|
my @all = with_emit {
|
|
|
|
my $def = type_header_def($typename);
|
|
|
|
emit "#ifndef $def";
|
|
|
|
emit "#define $def";
|
|
|
|
|
|
|
|
for my $strong (sort { $a cmp $b } keys %strong_refs) {
|
|
|
|
my $sdef = type_header_def($strong);
|
|
|
|
emit "#ifndef $sdef";
|
|
|
|
emit "#include \"$strong.h\"";
|
|
|
|
emit "#endif";
|
|
|
|
}
|
|
|
|
|
|
|
|
emit_block {
|
|
|
|
for my $weak (sort { $a cmp $b } keys %weak_refs) {
|
|
|
|
next if $strong_refs{$weak};
|
|
|
|
my $ttype = $types{$weak};
|
|
|
|
my $tstr = 'struct';
|
|
|
|
$tstr = 'enum' if $ttype->nodeName eq 'enum-type';
|
|
|
|
$tstr = 'union' if $ttype->nodeName eq 'bitfield-type';
|
|
|
|
$tstr = 'union' if ($ttype->nodeName eq 'struct-type' && is_attr_true($ttype,'is-union'));
|
|
|
|
emit $tstr, ' ', $weak, ';';
|
|
|
|
}
|
|
|
|
|
|
|
|
push @lines, @code;
|
|
|
|
} "namespace $main_namespace ";
|
|
|
|
|
|
|
|
emit "#endif";
|
|
|
|
};
|
|
|
|
|
|
|
|
$type_data{$typename} = \@all;
|
|
|
|
};
|
|
|
|
if ($@) {
|
|
|
|
print 'Error: '.$@."Type $typename in $filename ignored\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write output files
|
|
|
|
|
|
|
|
mkdir $output_dir;
|
|
|
|
|
|
|
|
{
|
|
|
|
# Delete the old files
|
|
|
|
for my $name (glob "$output_dir/*.h") {
|
|
|
|
unlink $name;
|
|
|
|
}
|
2011-12-29 05:30:55 -07:00
|
|
|
for my $name (glob "$output_dir/*.inc") {
|
|
|
|
unlink $name;
|
|
|
|
}
|
|
|
|
unlink "$output_dir/codegen.out.xml";
|
2011-12-24 03:37:00 -07:00
|
|
|
|
|
|
|
# Write out the headers
|
|
|
|
local $, = "\n";
|
|
|
|
local $\ = "\n";
|
|
|
|
|
|
|
|
for my $name (keys %type_data) {
|
|
|
|
open FH, ">$output_dir/$name.h";
|
|
|
|
print FH "/* THIS FILE WAS GENERATED. DO NOT EDIT. */";
|
|
|
|
print FH @{$type_data{$name}};
|
|
|
|
close FH;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write out the static file
|
2011-12-29 05:30:55 -07:00
|
|
|
for my $tag (keys %static_lines) {
|
|
|
|
my $name = $output_dir.'/static'.($tag?'.'.$tag:'').'.inc';
|
|
|
|
open FH, ">$name";
|
|
|
|
print FH "/* THIS FILE WAS GENERATED. DO NOT EDIT. */";
|
|
|
|
for my $name (sort { $a cmp $b } keys %{$static_includes{$tag}}) {
|
|
|
|
print FH "#include \"$name.h\"";
|
|
|
|
}
|
|
|
|
print FH "namespace $main_namespace {";
|
|
|
|
print FH @{$static_lines{$tag}};
|
|
|
|
print FH '}';
|
|
|
|
close FH;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Write an xml file with all types
|
|
|
|
open FH, ">$output_dir/codegen.out.xml";
|
|
|
|
print FH '<ld:data-definition xmlns:ld="http://github.com/peterix/dfhack/lowered-data-definition">';
|
|
|
|
for my $doc (@documents) {
|
|
|
|
for my $node ($doc->documentElement()->findnodes('*')) {
|
|
|
|
print FH ' '.$node->toString();
|
|
|
|
}
|
2011-12-24 03:37:00 -07:00
|
|
|
}
|
2011-12-29 05:30:55 -07:00
|
|
|
print FH '</ld:data-definition>';
|
2011-12-24 03:37:00 -07:00
|
|
|
close FH;
|
|
|
|
}
|