#!/usr/bin/perl -w =pod =head1 Syntax doxygen2hdr full_path_name_to/DoxyDocs.pm > header.h This is a post-processor to doxygen(1) which extracts function and struct prototypes from the perl variant of the source code parser. The difference to cextract(1) and to the -aux-info option of gcc(1) is that also the definitions of structures and enumerations are listed, not only functions. =head1 Usage =over 4 =item 1 Edit the doxygen parameter file, which by default is called "C", and set GENERATE_PERLMOD there to YES. This could be achieved by a sequence of commands like doxygen -s -g - > .doxy echo "GENERATE_PERLMOD = YES" >> .doxy A more fancy scripting is not needed since the syntactically last setting of the parameter actually overwrites earlier mentions in the "C" (here: .doxy). =item 2 Run doxygen... doxygen .doxy =item 3 Run doxygen2hdr(1) with the first argument being the file generated in the directory defined by OUTPUT_DIRECTORY in the "C", and the second argument a single string to be used with the typical #ifdef ... #endif brackets for C header files. The header file will be printed to the standard output. doxygen2hdr full_path_name_to/DoxyDocs.pm =back =head2 Example 1: doxygen2hdr perlmod/DoxyDocs.pm MYFILE_HDR_H > myfile_hdr.h =head2 Example 2: The output header contains the common superset of the documentation parsed, which is in essence controlled by some keywords in the "C". Usually, one would like to generate one header per source file, and therefore loop over these with varying settings of FILE_PATTERNS. One might also keep track of these files in another header file, called "C" in the following bash script example: doxygen -s -g - > .doxy echo "EXTRACT_ALL = YES" >> .doxy echo "GENERATE_PERLMOD = YES" >> .doxy echo "GENERATE_LATEX = NO" >> .doxy echo "GENERATE_HTML = NO" >> .doxy echo "QUIET = YES" >> .doxy echo '#ifndef SUPER_H' > super.h echo '#define SUPER_H' >> super.h # the double tic mark avoids that in this source here the Id is filled in by cvs... echo '/* $I''d:$ */' >> super.h for c in prima_dfs prima_pfits prima_utils ; do \ echo "FILE_PATTERNS = $c.c" >> .doxy ; doxygen .doxy ;\ up=`echo $c | tr [a-z] [A-Z]` ; \ mv $c.h $c.h.bak ;\ doxygen2hdr perlmod/DoxyDocs.pm ${up}_H > $c.h ;\ echo '#include' \"$c.h\" >> super.h \ ; done echo '#endif' >> super.h rm -rf .doxy perlmod =head1 see also: http://www.doxygen.org http://www.stack.nl/~dimitri/doxygen/ http://www.netsw.org/softeng/lang/c/tools/cextract/ http://public.www.planetmirror.com/pub/hpfreeware/Misc/cextract-1.7/ =head1 ToDo =over 4 =item 1 Check how we can prevent that "C" gets truncated to "C" by doxygen(1). =item 2 Does it make sense to export also typedef's and are these tagged in the output of the doxygen(1) parser? =item 3 Check http://bugzilla.gnome.org, entry 306189 on the product doxygen, whether doxygen2hdr(1) is superfluous because the same functionality is already build into doxygen(1). =item 4 The output uses "C" where C++ would demand "C", and generally speaking one would need either some automatism depending on the file suffix to switch between C and C++ syntax, or some command-line option to toggle this behaviour. =back =head1 Bugs In rare cases, the file "C" may contain invalid perl assignment, which means doxygen2hdr(1) complains with s.th. like syntax error at DoxyDocs.pm line ..., near "[," Compilation failed in require at ...doxygen2hdr line ... For the only known cases of this to occurr, one can simply edit the syntax error in DoxyDocs.pm at the given line, because there is only a superfluous comma after an opening bracket. =head1 Author Richard J. Mathar, 2005-06-02, http://www.strw.leidenuniv.nl/~mathar =cut # print a usage message sub usag { print "Usage: ",__FILE__," ...../DoxyDocs.pm cpp-Def-string\n" ; } # generate the header of the output sub hdr { my @hdrDef = @_ ; my $tod=localtime() ; print "#ifndef ",$hdrDef[1],"\n" ; print "#define ",$hdrDef[1],"\n\n" ; print("/* File automatically generated by ",__FILE__,"\n * from ",$hdrDef[0]) ; print("\n * ",$tod," Do not edit manually.\n") ; print(" * \$Id\$ \n") ; print "*/\n\n" ; } # Generate the trailer of the output # On the stack of this subroutine: only the cpp macro string. sub tralr { @hdrDef = @_ ; print "\n#endif /* ",@hdrDef," */\n" ; } # on the stack is one reference of the kind # { name => '', # includes => [], # included_by => [], # defines => {}, # functions => {}, # brief => {}, # detailed => {} # } # # Extract the includes and defines. sub onefilePass1 { my @chF = @_ ; print "\n" ; foreach $el ( @chF ) { #@con = keys (%$el) ; # these are detailed, functions, included_by, name, brief, includes #print "@con\n" ; my $finame ; foreach $keyc ( keys ( %$el ) ) { # print "$keyc\n" ; if ( $keyc eq 'name' ) { # print "$el->{$keyc}\n" ; $finame = $el->{$keyc} ; } } # precedence: first the includes foreach $keyc ( keys ( %$el ) ) { if ( $keyc eq 'includes' ) { my $membsar = $el->{$keyc} ; # the reference to a hash which has a 'members' variable foreach $kvarp ( @$membsar ) { # these are name #@con = keys ( %$kvarp ) ; #print "@con\n" ; my @Parlis=() ; foreach $ktyp ( keys ( %$kvarp) ) { # skip header files if ( $ktyp eq 'name' && ( substr( $finame,-2) ne '.h' ) ) { $Nam = $kvarp->{$ktyp} ; print "#include <",$Nam,">\n" ; } } } } } print "\n" ; # precedence: second the defines foreach $keyc ( keys ( %$el ) ) { if ( $keyc eq 'defines' ) { my $membh = $el->{$keyc} ; # the reference to a hash which has a 'members' variable $membsar = $membh->{'members'} ; # a reference to an array foreach $kvarp ( @$membsar ) { # these are kind, name, virtualness, protection, static, brief, detailed, initializer #@con = keys ( %$kvarp ) ; #print "@con\n" ; my ($Kin,$Nam,$Stat,$Ints) ; my @Parlis=() ; foreach $ktyp ( keys ( %$kvarp) ) { if ( $ktyp eq 'name' ) { $Nam = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'static' ) { $Stat = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'kind' ) { $Kin = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'initializer' ) { $Ints = $kvarp->{$ktyp} ; } } # skip header files if ( ( $Kin eq 'define' ) && ( $Stat eq 'no' ) && ( substr( $finame,-2) ne '.h' ) ) { # print "extern " ; print "#define ",$Nam," ",$Ints,"\n" ; } } } } } } # on the stack is one reference of the kind # { name => '', # includes => [], # included_by => [], # defines => {}, # functions => {}, # brief => {}, # detailed => {} # } # # Extract the functions sub onefilePass2 { my @chF = @_ ; print "\n" ; foreach $el ( @chF ) { #@con = keys (%$el) ; # these are detailed, functions, included_by, name, brief, includes #print "@con\n" ; my $finame ; foreach $keyc ( keys ( %$el ) ) { # print "$keyc\n" ; if ( $keyc eq 'name' ) { # print "$el->{$keyc}\n" ; $finame = $el->{$keyc} ; } } # precedence: first the enums, then the functions... foreach $keyc ( keys ( %$el ) ) { if ( $keyc eq 'enums' ) { my $membh = $el->{$keyc} ; # the reference to a hash which has a 'members' variable $membsar = $membh->{'members'} ; # a reference to an array foreach $kvarp ( @$membsar ) { # these are kind, name, virtualness, protection, static, detailed, values #@con = keys ( %$kvarp ) ; #print "@con\n" ; my ($Nam,$Stat,$Vals) ; my @Parlis=() ; foreach $ktyp ( keys ( %$kvarp) ) { if ( $ktyp eq 'name' ) { $Nam = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'static' ) { $Stat = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'values' ) { $Pars = $kvarp->{$ktyp} ; foreach $pinPar ( @$Pars ) { my $decl='' ; foreach $pinPav ( keys ( %$pinPar ) ) { $decl = $pinPar->{$pinPav} if ( $pinPav eq 'name' ) ; } push(@Parlis,$decl) ; } } } # skip header files if ( ( $Stat eq 'no' ) && ( substr( $finame,-2) ne '.h' ) ) { # print "extern " ; print "enum ",$Nam," { ",join(', ',@Parlis),"} ;\n" ; } } } } print "\n" ; foreach $keyc ( keys ( %$el ) ) { if ( $keyc eq 'functions' ) { my $membh = $el->{$keyc} ; # the reference to a hash which has a 'members' variable $membsar = $membh->{'members'} ; # a reference to an array foreach $kvarp ( @$membsar ) { # these are kind, name, virtualness, protection, static, detailed, type #@con = keys ( %$kvarp ) ; #print "@con\n" ; my ($Kin,$Nam,$Stat,$Typ,$Pars,$Cons) ; my @Parlis=() ; foreach $ktyp ( keys ( %$kvarp) ) { if ( $ktyp eq 'type' ) { $Typ = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'name' ) { $Nam = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'static' ) { $Stat = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'const' ) { $Cons = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'kind' ) { $Kin = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'parameters' ) { my ($decl,$ty)=('','') ; $Pars = $kvarp->{$ktyp} ; foreach $pinPar ( @$Pars ) { foreach $pinPav ( keys ( %$pinPar ) ) { $decl = $pinPar->{$pinPav} if ( $pinPav eq 'declaration_name' ) ; $ty = $pinPar->{$pinPav} if ( $pinPav eq 'type' ) ; } my $tmp = $ty." ".$decl ; push(@Parlis,$tmp) ; } } } # skip header files if ( ( $Kin eq 'function' ) && ( $Stat eq 'no' ) && ( substr( $finame,-2) ne '.h' ) ) { # print "extern " ; if ( $Cons eq 'yes' ) { print "const " ; } print $Typ," ",$Nam,"(",join(', ',@Parlis),") ;\n" ; } } } elsif ( $keyc eq 'variables' ) { my $membh = $el->{$keyc} ; # the reference to a hash which has a 'members' variable $membsar = $membh->{'members'} ; # a reference to an array foreach $kvarp ( @$membsar ) { # these are kind, name, virtualness, protection, static, detailed, type #@con = keys ( %$kvarp ) ; #print "@con\n" ; my ($Kin,$Nam,$Stat,$Typ) ; foreach $ktyp ( keys ( %$kvarp) ) { if ( $ktyp eq 'type' ) { $Typ = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'name' ) { $Nam = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'static' ) { $Stat = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'kind' ) { $Kin = $kvarp->{$ktyp} ; } } # skip header files if ( ( $Kin eq 'variable' ) && ( $Stat eq 'no' ) && ( substr( $finame,-2) ne '.h' ) ) { # print "extern " ; print $Typ," ",$Nam," ;\n" ; } } } } } } # on the stack is one reference of the kind # { name => '', # inner => [], # includes => [], # all_members => [], # public__members => {}, # brief => {}, # detailed => {} # } # # On the output we construct s.th. like # struct { # # } ; sub oneclass { # print "@_\n" ; my ($ch) = @_ ; # @con = keys (%$ch) ; # these are: detailed, public_members, inner, name, brief, includes, all_members # print "@con\n" ; print "struct " ; foreach $keyc ( keys ( %$ch ) ) { # print "$keyc\n" ; if ( $keyc eq 'name' ) { print "$ch->{$keyc} {\n" ; } } foreach $keyc ( keys ( %$ch ) ) { # print "$keyc\n" ; if ( $keyc eq 'public_members' ) { my $membh = $ch->{$keyc} ; # the reference to a hash which has a 'members' variable $membsar = $membh->{'members'} ; # a reference to an array foreach $kvarp ( @$membsar ) { my ($Typ,$Nam,$Stat,$Kin) = ('','','','') ; #@con = keys ( %$kvarp ) ;# these are kind, name, virtualness, protection, static, detailed, type #print "@con\n" ; foreach $ktyp ( keys ( %$kvarp) ) { if ( $ktyp eq 'type' ) { $Typ = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'name' ) { $Nam = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'static' ) { $Stat = $kvarp->{$ktyp} ; } elsif ( $ktyp eq 'kind' ) { $Kin = $kvarp->{$ktyp} ; } } if ( $Kin eq 'variable') { print "\t",$Typ," ",$Nam," ;\n" ; } } } } print "};\n" ; } # check that all arguments are present if ( scalar(@ARGV) < 2 ) { usag ; die "Insufficient number of arguments" ; } # read the output of the doxygen .doxyfile command # which assigns an anonymous reference to a list to $doxydocs. require "$ARGV[0]" ; # now we have $doxydocs = { # classes => [], # namespaces => [], # files => [], # groups => [], # pages => [] # } hdr($ARGV[0],$ARGV[1]); # Order is probably important: # 1. over the files to get the "include" and "define" statements # 2. a loop over the classes, which may be refered to by the files, # 3. then the files again with the functions, # instead of running with one foreach-loop over both. foreach $key ( keys (%$doxydocs)) { if ( $key eq 'files' ) { my $allfiles = $doxydocs->{$key} ; # reference to array of hashes foreach $keyf ( @$allfiles ) { onefilePass1 $keyf ; } } } foreach $key ( keys (%$doxydocs)) { if ( $key eq 'classes' ) { my $allclasses = $doxydocs->{$key} ; # reference to array of hashes foreach $keyc ( @$allclasses ) # keyc an element of the array, ie, a reference to a hash { oneclass $keyc ; } } } foreach $key ( keys (%$doxydocs)) { if ( $key eq 'files' ) { my $allfiles = $doxydocs->{$key} ; # reference to array of hashes foreach $keyf ( @$allfiles ) { onefilePass2 $keyf ; } } } tralr $ARGV[1] ;