#!/usr/bin/perl use strict; # See https://rt.cpan.org/Public/Bug/Display.html?id=57341 use Getopt::Long; use GCC::TranslationUnit; use C::DynaLib::Parse qw(pack_types process_struct process_func); my ($cc, $inc, $code, $file, $type); GetOptions( "cc=s" => \$cc, "I=s" => \$inc, "code=s" => \$code, "file=s", => \$file, "t=s" => \$type ); $inc = "-I$inc" if $inc; sub declare_func { my $decl = shift; if ($type eq 'c_dynalib') { print "DeclareSub($decl->{name}, ", pack_types($decl->{retn}),", ", pack_types($decl->{parms}),");\n"; } else { my $parms = join ", ", @{$decl->{parms}}; $parms =~ s/, $//; print "$decl->{retn} $decl->{name}( $parms );\n"; } } sub declare_struct { my $decl = shift; if ($type eq 'c_dynalib') { my $names = join '","', @{$decl->{names}}; print qq(Define C::DynaLib::Struct("$decl->{name}", "$decl->{packnames}", ["$names"}]); ); } else { print "\n$decl->{type} $decl->{name} {"; for (0..@$decl->{names}) { print $decl->{types}->[$_]; print "\t",$decl->{names}->[$_]; print ":",$decl->{sizees}->[$_],"\n"; } print "}\n;" } } my @post; my %records; my $filter; my $header = shift; if ($type) { my %FFIs = map { $_ => 1 } qw( C::DynaLib FFI Win32::API P5NCI CTypes ); die "Error: Unknown -t $type. Valid: ".join(", ",keys(%FFIs)) ."\n" unless $FFIs{$type}; print "use $type;\n"; print "# generated by hparse.pl $header;\n"; $type =~ s/::/_/g; $type = lc($type); # c_dynalib ffi win32_api p5nci ctypes } my $node; # .h or .hpp or .hh $header .= ".h" if !$file and !$code and $header and $header !~ /\.h/; if ($header and $header =~ /\.h(h|pp)$/ and !$cc) { # for c++ support with newer g++ see # https://rt.cpan.org/Public/Bug/Display.html?id=57349 (v1.01) $cc = "g++"; } $cc = 'gcc' unless $cc; # check Config? if ($code) { $filter = $header; $node = C::DynaLib::Parse::GCC_prepare($code, "$cc $inc"); } elsif ($file) { $filter = $header; $node = C::DynaLib::Parse::GCC_prepare("#include \"$file\"\n", "$cc $inc"); } else { $header = "stdlib.h" unless $header; $filter = shift; $node = C::DynaLib::Parse::GCC_prepare("#include <$header>\n", "$cc $inc"); } while ($node) { if ($node->isa('GCC::Node::function_decl') and ($filter ? $node->name->identifier =~ /$filter/ : $node->name->identifier !~ /^_/)) { declare_func process_func($node); } if ($node->isa('GCC::Node::record_type') and ($filter ? $node->name->identifier =~ /$filter/ : $node->name->identifier !~ /^_/)) { declare_struct process_struct($node); } } continue { $node = $node->chain; } POST: while ($node = shift @C::DynaLib::Parse::post) { #print "\n(", ref $node, ")"; if ($node->isa('GCC::Node::record_type')) { declare_struct process_struct($node); } } __END__ =pod =head1 NAME hparse =head1 DESCRIPTION Parse function signatures for FFI from gcc4 -fdump-translation-unit Also parses record types (union, struct) if used as arguments of the used functions. Note that the output should be compiler independent. So you CAN use gcc for creating FFI signatures for shared libraries compiled with other compilers. Theoretically. =head1 SYNOPSIS hparse.pl [OPTIONS] [header] [function-regex] hparse.pl stdio.h '^fprintf$' hparse.pl --code "int __cdecl ioctl (int __fd, int __cmd, ...)" =head1 OPTIONS -t FFI-TYPE - dump in the given FFI format (todo) --cc gcc - use given gcc -I - use given include path --code string - parse string, not any header --file file - parse file, not any header =head2 FFI-TYPES (todo) * C::DynaLib * FFI * Win32::API * P5NCI * Ctypes =head1 EXAMPLES =head2 hparse.pl stdio.h '^fr' frexp return=double parms=double, *int, void align=8, return-align=64 frexpf return=float parms=float, *int, void align=8, return-align=32 frexpl return=long double parms=long double, *int, void align=8, return-align=32 free return=void parms=*void, void align=8, return-align=8 freopen return=*FILE parms=const *char, const *char, *FILE, void align=8, return-align=32 fread return=size_t parms=*void, size_t, size_t, *FILE, void align=8, return-align=32 =head2 hparse.pl poll.h 'poll' poll return=int parms=struct pollfd *, nfds_t, int, void align=8, return-align=32 struct pollfd (align=32) { int fd (align=32) short int events (align=16) short int revents (align=16) } =head1 TODO Resolve size_t, nfds_t => integer_type Calling convention _stdcall, _cdecl, _fastcall Align syntax for the FFI's? Varargs ... not detected ./hparse.pl --code "int __cdecl ioctl (int __fd, int __cmd, ...);" ioctl ioctl return=int parms=int, int align=8, return-align=32 =cut