Cloned library of VTK-5.0.0 with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

593 lines
20 KiB

#!/usr/bin/env perl
# Time-stamp: <2005-06-23 17:26:59 barre>
#
# Convert VTK headers to doxygen format
#
# roeim : Vetle Roeim <vetler@ifi.uio.no>
# barre : Sebastien Barre <sebastien@barre.nom.fr>
#
# 0.9 (barre) :
# - add --conds : add \cond...\endcond around public:, private:, protected:
#
# 0.83 (barre) :
# - add --stdout : print converted file to standard output
#
# 0.82 (barre) :
# - add --relativeto path : each file/directory to document is considered
# relative to 'path', where --to and --relativeto should be absolute
#
# 0.81 (barre) :
# - fix pb if both --to and path to the file to document were absolute
# - remove warning when date or revision not found
#
# 0.8 (barre) :
# - update to match the new VTK 4.0 tree
# - change default --dirs so that it can be launched from Utilities/Doxygen
# - change default --to so that it can be launched from Utilities/Doxygen
# - handle more .SECTION syntax
# - add group support (at last)
#
# 0.76 (barre) :
# - add 'parallel' to the default set of directories
#
# 0.75 (barre) :
# - change default --to to '../vtk-doxygen' to comply with Kitware's doxyfile
#
# 0.74 (barre) :
# - as doxygen now handles RCS/CVS tags of the form $word:text$, use them
#
# 0.73 (barre) :
# - change doxygen command style from \ to @ to match javadoc, autodoc, etc.
#
# 0.72 (barre) :
# - change default --to to '../vtk-dox'
#
# 0.71 (barre) :
# - fix O_TEXT flag problem
# - switch to Unix CR/LF format
#
# 0.7 (barre) :
# - change name
# - remove -c option
#
# 0.6 (barre) :
# - change (warning) default --to to '../vtk2' because I ruined my own
# VTK distrib too many times :(
# - add automatic creation of missing directory trees
# - add check for current OS (if Windows, do not perform tests based
# on stat()/idev/ino features)
#
# 0.5 (barre) :
# - better .SECTION handling
# - add support for empty lines in documentation block
# - fix problem with headers not corresponding to classes
# - change name to doc_header2doxygen (removed vtk_)
# - change '-s' (silent) to '-v' (verbose)
# - add function description reformatting
#
# 0.4 (barre) :
# - change /*! ... */ position upon request
# - add 'Date:' support as @date
# - add 'Version:' support as @version
# - add 'Thanks:' support as @par Thanks
#
# 0.3 (barre) :
# - fix various " // Description" spelling problems :)
#
# 0.2 (barre) :
# - fix problem with classes with no brief documentation
#
# 0.1 (barre) :
# - add Perl syntactic sugar, options...
# - add standard output (filter) mode (-c)
# - add silent mode (-s)
# - add update mode, convert only if newer (-u)
# - add conversion to another directory (--to)
# - add '.SECTION Caveats' support as @warning
# - add/fix '.SECTION whatever' support as @par
# - add default directories to process
#
# 0.0 (roeim)
# - first release (thanks to V. Roeim !)
use Carp;
use Cwd 'abs_path';
use Getopt::Long;
use Fcntl;
use File::Basename;
use File::Find;
use File::Path;
use Text::Wrap;
use strict;
my ($VERSION, $PROGNAME, $AUTHOR) = (0.9, $0, "Sebastien Barre et al.");
$PROGNAME =~ s/^.*[\\\/]//;
# -------------------------------------------------------------------------
# Defaults (add options as you want: "verbose" => 1 for default verbose mode)
my %default =
(
dirs => ["../../Common",
"../../Filtering",
"../../GenericFiltering",
"../../GenericFiltering/Testing/Cxx",
"../../Graphics",
"../../GUISupport/MFC",
"../../GUISupport/Qt",
"../../Hybrid",
"../../Imaging",
"../../IO",
"../../Parallel",
"../../Patented",
"../../Rendering",
"../../VolumeRendering"],
relativeto => "",
temp => "doc_header2doxygen.tmp",
to => "../../../VTK-doxygen"
);
# -------------------------------------------------------------------------
# Parse options
my %args;
Getopt::Long::Configure("bundling");
GetOptions (\%args, "help", "verbose|v", "update|u", "conds|c", "force|f", "temp=s", "to=s", "stdout", "relativeto=s");
print "$PROGNAME $VERSION, by $AUTHOR\n" if ! exists $args{"stdout"};
if (exists $args{"help"}) {
print <<"EOT";
Usage : $PROGNAME [--help] [--verbose|-v] [--update|-u] [--conds|-c] [--force|-f] [--temp file] [--to path] [--relativeto path] [files|directories...]
--help : this message
--verbose|-v : verbose (display filenames while processing)
--update|-u : update (convert only if newer, requires --to)
--force|-f : force conversion for all files (overrides --update)
--stdout : print converted file to standard output
--temp file : use 'file' as temporary file (default: $default{temp})
--to path : use 'path' as destination directory (default: $default{to})
--relativeto path : each file/directory to document is considered relative to 'path', where --to and --relativeto should be absolute (default: $default{relativeto})
--conds|-c : use \cond sections around public, protected, private
Example:
$PROGNAME --to ../vtk-doxygen
$PROGNAME contrib
EOT
exit;
}
$args{"verbose"} = 1 if exists $default{"verbose"};
$args{"update"} = 1 if exists $default{"update"};
$args{"conds"} = 1 if exists $default{"conds"};
$args{"force"} = 1 if exists $default{"force"};
$args{"temp"} = $default{temp} if ! exists $args{"temp"};
$args{"to"} = $default{"to"} if ! exists $args{"to"};
$args{"to"} =~ s/[\\\/]*$// if exists $args{"to"};
$args{"relativeto"} = $default{"relativeto"} if ! exists $args{"relativeto"};
$args{"relativeto"} =~ s/[\\\/]*$// if exists $args{"relativeto"};
croak "$PROGNAME: --update requires --to\n"
if exists $args{"update"} && ! exists $args{"to"};
my $os_is_win = ($^O =~ m/(MSWin32|Cygwin)/i);
my $open_file_as_text = $os_is_win ? O_TEXT : 0;
my $start_time = time();
# -------------------------------------------------------------------------
# Collect all files and directories
push @ARGV, @{$default{dirs}} if !@ARGV;
print "Collecting...\n" if ! exists $args{"stdout"};
my @files;
foreach my $file (@ARGV) {
if (-f $file) {
push @files, $file;
} elsif (-d $file) {
find sub { push @files, $File::Find::name; }, $file;
}
}
# -------------------------------------------------------------------------
# Process files corresponding to headers
print "Converting...\n" if ! exists $args{"stdout"};
my $intermediate_time = time();
my $nb_file = 0;
foreach my $source (@files) {
next if $source !~ /vtk[^\\\/]*\.h\Z/;
# Figure out destination file now
my $dest;
if (! exists $args{"to"}) {
$dest = $args{"temp"};
} else {
# if source has absolute path, just use the basename, unless a
# relativeto path has been set
if ($source =~ m/^(\/|[a-zA-W]\:[\/\\])/) {
if ($args{"relativeto"}) {
my ($dir, $absrel) = (abs_path(dirname($source)),
abs_path($args{"relativeto"}));
$dir =~ s/$absrel//;
$dest = $args{"to"} . $dir . '/' . basename($source);
} else {
$dest = $args{"to"} . '/' . basename($source);
}
} else {
my $source2 = $source;
# let's remove the ../ component before the source filename, so
# that it might be appended to the "to" directory
$source2 =~ s/^(\.\.[\/\\])*//;
$dest = $args{"to"} . '/' . $source2;
}
# Ensure both source and target are different
if (!$os_is_win) {
my ($i_dev, $i_ino) = stat $source;
my ($o_dev, $o_ino) = stat $dest;
croak "$PROGNAME: sorry, $source and $dest are the same file\n"
if ($i_dev == $o_dev && $i_ino == $o_ino);
}
}
# Update mode : skip the file if it is not newer than the
# previously converted target
if (exists $args{"update"} && ! exists $args{"force"}) {
next if -e $dest && (stat $source)[9] < (stat $dest)[9];
}
++$nb_file;
print " $source\n" if exists $args{"verbose"};
# Open file, feed it entirely to an array
sysopen(HEADERFILE, $source, O_RDONLY|$open_file_as_text)
or croak "$PROGNAME: unable to open $source\n";
my @headerfile = <HEADERFILE>;
close(HEADERFILE);
my ($date, $revision) = ("", "");
my @converted = ();
my @thanks = ();
# Parse the file until the beginning of the documentation block
# is found. The copyright and disclaimer sections are parsed to
# extract the 'Date', 'Version' and 'Thanks' values.
my $line;
while ($line = shift @headerfile) {
# Quit if the beginning of the documentation block has been reached.
# It is supposed to start with:
# // .NAME vtkFooBar - foo bar class
last if $line =~ /\/\/ \.NAME/;
# Date. Example:
# Date: $Date: 2005/06/23 21:48:34 $
if ($line =~ /^\s*Date:\s*(.*)$/) {
$date = $1;
# Version. Example:
# Version: $Revision: 1.13 $
} elsif ($line =~ /^\s*Version:\s*(.*)$/) {
$revision = $1;
# Thanks (maybe multi-lines). Example:
# Thanks: Thanks to Sebastien Barre who developed this class.
} elsif ($line =~ /^\s*Thanks:\s*(.*)$/) {
push @thanks, " ", $1, "\n";
# Handle multi-line thanks
while ($line = shift @headerfile) {
last if $line =~ /^\s*$/;
$line =~ s/^(\s*)//;
push @thanks, " ", $line;
}
push @converted, $line;
# Everything else goes to the converted file
} else {
push @converted, $line;
}
}
# Process the documentation block
# Extract the name of the class and its short description
# // .NAME vtkFooBar - foo bar class
if (defined($line) && $line =~ /\/\/ \.NAME (\w*)( \- (.*))?/) {
my ($class_name, $class_short_description) = ($1, $3);
$class_name =~ s/\.h//;
# Insert class description, date, revision, thanks
push @converted, "/*! \@class $class_name\n";
push @converted, " \@brief $class_short_description\n"
if $class_short_description;
if ($date) {
push @converted, "\n $date\n";
}
# WARNING : need a blank line between RCS tags and previous dox tag
if ($revision) {
push @converted, "\n" if (!$date);
push @converted, " $revision\n";
}
# Do not add thanks anymore. Will be done externally.
# push @converted, " \@par Thanks:\n", @thanks if @thanks;
# Read until the end of the documentation block is reached
# Translate 'See Also', 'Caveats' and whatever .SECTION
# As of 24 sep 2001, there are:
# 137 // .SECTION Caveats
# 1 // .SECTION Credits
# 702 // .SECTION Description
# 3 // .SECTION Note
# 1 // .SECTION note
# 329 // .SECTION See Also
# 4 // .SECTION See also
# 70 // .SECTION see also
# 1 // .SECTION Warning
# find . -name vtk\*.h -exec grep "\.SECTION" {} \; | sort | uniq -c
# Let's provide support for bugs too:
# // .SECTION Bug
# // .SECTION Bugs
# // .SECTION Todo
my ($tag, $inblock) = ("", 0);
while ($line = shift @headerfile) {
# Quit if the end of the documentation block has been reached.
# Let'say that it is supposed to end as soon as the usual
# inclusion directives are found, for example:
# #ifndef __vtkAbstractTransform_h
# #define __vtkAbstractTransform_h
last if $line =~ /^\#/;
# Process and recognize a .SECTION command and convert it to
# the corresponding doxygen tag ($tag)
if ($line =~ /^\/\/\s+\.SECTION\s+(.+)\s*$/i) {
my $type = $1;
# Bugs (@bugs). Starts with:
# // .SECTION Bug
# // .SECTION Bugs
if ($type =~ /Bugs?/i) {
$tag = "\@bug";
}
# Caveats or Warnings (@warning). Starts with:
# // .SECTION Caveats
# // .SECTION Warning
# // .SECTION Warnings
elsif ($type =~ /(Caveats|Warnings?)/i) {
$tag = "\@warning";
}
# Description. Starts with:
# // .SECTION Description
elsif ($type =~ /Description/i) {
$tag = "";
push @converted, "\n";
}
# Note (@attention). Starts with:
# // .SECTION Note
elsif ($type =~ /Note/i) {
$tag = "\@attention";
}
# See also (@sa). Starts with:
# // .SECTION See Also
elsif ($type =~ /See Also/i) {
$tag = "\@sa";
}
# Todo (@todo). Starts with:
# // .SECTION Todo
elsif ($type =~ /Todo/i) {
$tag = "\@todo";
}
# Any other .SECTION (@par). Starts with:
# // .SECTION whatever
else {
$tag = "\@par " . $type . ":";
}
$inblock = 0;
}
# If the line starts with '//', we are still within the tag block.
# Remove '//' for non empty lines, eventually put or duplicate
# the tag name if an empty comment is found (meaning that a new
# 'paragraph' is requested but with the same tag type)
# Example:
# // .SECTION Caveats
# // blabla1q
# // blabla1b
# //
# // blabla2
# Gets translated into:
# @warning
# blabla1q
# blabla1b
#
# @warning
# blabla2
elsif ($line =~ /^\/\/(.*)/) {
my $remaining = $1;
if ($remaining =~ /\S/) {
push @converted, " $tag\n"
if $tag ne "" && ! $inblock;
push @converted, $remaining, "\n";
$inblock = 1;
} else {
push @converted, "\n";
$inblock = 0;
}
} else {
# Does not starts with // but still within block or just
# before the end (#). Probably an empty line.
# Hack : let's have a look at the next line... if it begins
# with // then the current line is included (was a space).
if (my $next_line = shift @headerfile) {
push @converted, $line if $next_line =~ /^\/\//;
unshift @headerfile, $next_line;
}
}
}
# Close the doxygen documentation block describing the class
push @converted, "*/\n\n", $line;
}
# Read until the end of the header and translate the description of
# each function provided that it is located in a C++ comment
# containing the 'Description:' keyword.
# Example:
# // Description:
# // Construct with automatic computation of divisions, averaging
# // 25 points per bucket.
# static vtkPointLocator2D *New();
my $in_section = "";
while ($line = shift @headerfile) {
# Track the public:, protected: and private: sections and put them
# between \cond... \endcond so that they can be removed from the
# documentation conditionally. Add them to ENABLED_SECTION
# to show them.
# IMPORTANT: *no* spaces are allowed between the beginning of the
# line and the qualifier. This is mandatory to solve issues
# with nested class definitions, since it is non-trivial to
# track the fact that we are leaving a class definition to
# re-enter the parent class definition, etc.
if (exists $args{"conds"}) {
if ($line =~ /^(public|protected|private):/) {
if ($in_section ne "") {
push @converted, "// \@endcond\n";
}
$in_section = $1;
push @converted, "// \@cond section_$in_section\n";
}
}
if ($line =~ /^(\s*)\/\/\s*De(s|c)(s|c)?ription/) {
my $indent = $1;
$Text::Wrap::columns = 76;
# While there are still lines beginning with '//' append them to
# the function's description and trim spaces.
my @description = ();
while ($line = shift @headerfile) {
last if $line !~ /^\s*\/\//;
chop $line;
$line =~ s/^\s*\/\/\s*//;
$line =~ s/\s*$//;
push @description, $line;
}
# While there are non-empty lines add these lines to the
# list of declarations (and/or inline definitions)
# pertaining to the same description.
my @declarations = ();
while ($line && $line =~ /\s+\S/) {
push @declarations, $line;
$line = shift @headerfile
}
# If there is more than one declaration or at least a macro,
# enclose in a group (actually a single multiline declaration will
# be enclosed too, but who cares :)...
my $enclose =
(scalar @declarations > 1 || $declarations[0] =~ /vtk.+Macro/);
push @converted, "$indent//@\{\n" if $enclose;
push @converted,
wrap("$indent/*! ", "$indent ", @description), " */\n"
if @description;
push @converted, @declarations;
push @converted, "$indent//@\}\n" if $enclose;
}
push @converted, $line;
}
if (exists $args{"conds"}) {
if ($in_section ne "") {
push @converted, "// \@endcond";
}
}
# Write the converted header to its destination
# or to standard output.
if (exists $args{"stdout"}) {
print @converted;
} else {
# Open the target and create the missing directory if any
if (!sysopen(DEST_FILE,
$dest,
O_WRONLY|O_TRUNC|O_CREAT|$open_file_as_text)) {
my $dir = dirname($dest);
mkpath($dir);
sysopen(DEST_FILE,
$dest,
O_WRONLY|O_TRUNC|O_CREAT|$open_file_as_text)
or croak "$PROGNAME: unable to open destination file $dest\n";
}
print DEST_FILE @converted;
close(DEST_FILE);
# If in-place conversion was requested, remove source and rename target
# (or temp file) to source
if (! exists $args{"to"}) {
unlink($source)
or carp "$PROGNAME: unable to delete original file $source\n";
rename($args{"temp"}, $source)
or carp "$PROGNAME: unable to rename ", $args{"temp"}, " to $source\n";
}
}
}
if (! exists $args{"stdout"}) {
print " => $nb_file files converted in ", time() - $intermediate_time, " s. \n";
print "Finished in ", time() - $start_time, " s.\n";
}