OSDN Git Service

FIRST REPOSITORY
[eos/hostdependOTHERS.git] / I686LINUX / util / I686LINUX / lib / vtk / doxygen / doc_header2doxygen.pl
1 #!/usr/bin/env perl
2 # Time-stamp: <2002-01-28 11:56:19 barre>
3 #
4 # Convert VTK headers to doxygen format
5 #
6 # roeim : Vetle Roeim <vetler@ifi.uio.no>
7 # barre : Sebastien Barre <sebastien@barre.nom.fr>
8 #
9 # 0.83 (barre) :
10 #   - add --stdout          : print converted file to standard output
11 #
12 # 0.82 (barre) :
13 #   - add --relativeto path : each file/directory to document is considered
14 #     relative to 'path', where --to and --relativeto should be absolute
15 #
16 # 0.81 (barre) :
17 #   - fix pb if both --to and path to the file to document were absolute
18 #   - remove warning when date or revision not found
19 #
20 # 0.8 (barre) :
21 #   - update to match the new VTK 4.0 tree
22 #   - change default --dirs so that it can be launched from Utilities/Doxygen
23 #   - change default --to so that it can be launched from Utilities/Doxygen
24 #   - handle more .SECTION syntax
25 #   - add group support (at last)
26 #
27 # 0.76 (barre) :
28 #   - add 'parallel' to the default set of directories
29 #
30 # 0.75 (barre) :
31 #   - change default --to to '../vtk-doxygen' to comply with Kitware's doxyfile
32 #
33 # 0.74 (barre) :
34 #   - as doxygen now handles RCS/CVS tags of the form $word:text$, use them 
35 #
36 # 0.73 (barre) :
37 #   - change doxygen command style from \ to @ to match javadoc, autodoc, etc.
38 #
39 # 0.72 (barre) :
40 #   - change default --to to '../vtk-dox'
41 #
42 # 0.71 (barre) :
43 #   - fix O_TEXT flag problem
44 #   - switch to Unix CR/LF format
45 #
46 # 0.7 (barre) :
47 #   - change name
48 #   - remove -c option
49 #
50 # 0.6 (barre) :
51 #   - change (warning) default --to to '../vtk2' because I ruined my own
52 #     VTK distrib too many times :(
53 #   - add automatic creation of missing directory trees
54 #   - add check for current OS (if Windows, do not perform tests based 
55 #     on stat()/idev/ino features)
56 #
57 # 0.5 (barre) :
58 #   - better .SECTION handling
59 #   - add support for empty lines in documentation block
60 #   - fix problem with headers not corresponding to classes
61 #   - change name to doc_header2doxygen (removed vtk_)
62 #   - change '-s' (silent) to '-v' (verbose)
63 #   - add function description reformatting
64 #
65 # 0.4 (barre) :
66 #   - change /*! ... */ position upon request
67 #   - add 'Date:' support as @date
68 #   - add 'Version:' support as @version
69 #   - add 'Thanks:' support as @par Thanks
70 #
71 # 0.3 (barre) :
72 #   - fix various " // Description" spelling problems :)
73 #
74 # 0.2 (barre) :
75 #   - fix problem with classes with no brief documentation
76 #
77 # 0.1 (barre) :
78 #   - add Perl syntactic sugar, options...
79 #   - add standard output (filter) mode (-c)
80 #   - add silent mode (-s)
81 #   - add update mode, convert only if newer (-u)
82 #   - add conversion to another directory (--to)
83 #   - add '.SECTION Caveats' support as @warning
84 #   - add/fix '.SECTION whatever' support as @par
85 #   - add default directories to process
86 #
87 # 0.0 (roeim)
88 #   - first release (thanks to V. Roeim !)
89
90
91 use Carp;
92 use Cwd 'abs_path';
93 use Getopt::Long;
94 use Fcntl;
95 use File::Basename;
96 use File::Find;
97 use File::Path;
98 use Text::Wrap;
99 use strict;
100
101 my ($VERSION, $PROGNAME, $AUTHOR) = (0.83, $0, "Sebastien Barre et al.");
102 $PROGNAME =~ s/^.*[\\\/]//;
103
104 # -------------------------------------------------------------------------
105 # Defaults  (add options as you want: "verbose" => 1 for default verbose mode)
106
107 my %default = 
108   (
109    dirs => ["../../Common", 
110             "../../Filtering", 
111             "../../Graphics", 
112             "../../Hybrid", 
113             "../../Imaging", 
114             "../../IO", 
115             "../../Parallel", 
116             "../../Patented", 
117             "../../Rendering"],
118    relativeto => "",
119    temp => "doc_header2doxygen.tmp",
120    to => "../../../VTK-doxygen"
121   );
122
123 # -------------------------------------------------------------------------
124 # Parse options
125
126 my %args;
127 Getopt::Long::Configure("bundling");
128 GetOptions (\%args, "help", "verbose|v", "update|u", "force|f", "temp=s", "to=s", "stdout", "relativeto=s");
129
130 print "$PROGNAME $VERSION, by $AUTHOR\n" if ! exists $args{"stdout"};
131
132 if (exists $args{"help"}) {
133     print <<"EOT";
134 Usage : $PROGNAME [--help] [--verbose|-v] [--update|-u] [--force|-f] [--temp file] [--to path] [--relativeto path] [files|directories...]
135   --help            : this message
136   --verbose|-v      : verbose (display filenames while processing)
137   --update|-u       : update (convert only if newer, requires --to)
138   --force|-f        : force conversion for all files (overrides --update)
139   --stdout          : print converted file to standard output
140   --temp file       : use 'file' as temporary file (default: $default{temp})
141   --to path         : use 'path' as destination directory (default: $default{to})
142   --relativeto path : each file/directory to document is considered relative to 'path', where --to and --relativeto should be absolute (default: $default{relativeto})
143
144 Example:
145   $PROGNAME --to ../vtk-doxygen
146   $PROGNAME contrib
147 EOT
148     exit;
149 }
150
151 $args{"verbose"} = 1 if exists $default{"verbose"};
152 $args{"update"} = 1 if exists $default{"update"};
153 $args{"force"} = 1 if exists $default{"force"};
154 $args{"temp"} = $default{temp} if ! exists $args{"temp"};
155 $args{"to"} = $default{"to"} if ! exists $args{"to"};
156 $args{"to"} =~ s/[\\\/]*$// if exists $args{"to"};
157 $args{"relativeto"} = $default{"relativeto"} if ! exists $args{"relativeto"};
158 $args{"relativeto"} =~ s/[\\\/]*$// if exists $args{"relativeto"};
159
160 croak "$PROGNAME: --update requires --to\n" 
161   if exists $args{"update"} && ! exists $args{"to"};
162
163 my $os_is_win = ($^O =~ m/(MSWin32|Cygwin)/i);
164 my $open_file_as_text = $os_is_win ? O_TEXT : 0;
165 my $start_time = time();
166     
167 # -------------------------------------------------------------------------
168 # Collect all files and directories
169
170 push @ARGV, @{$default{dirs}} if !@ARGV;
171
172 print "Collecting...\n" if ! exists $args{"stdout"};
173 my @files;
174 foreach my $file (@ARGV) {
175     if (-f $file) {
176         push @files, $file;
177     } elsif (-d $file) {
178         find sub { push @files, $File::Find::name; }, $file;
179     }
180 }
181
182 # -------------------------------------------------------------------------
183 # Process files corresponding to headers
184
185 print "Converting...\n" if ! exists $args{"stdout"};
186 my $intermediate_time = time();
187 my $nb_file = 0;
188
189 foreach my $source (@files) {
190     
191     next if $source !~ /vtk[^\\\/]*\.h\Z/;
192
193     # Figure out destination file now
194
195     my $dest;
196     if (! exists $args{"to"}) {
197         $dest = $args{"temp"};
198     } else {
199         # if source has absolute path, just use the basename, unless a
200         # relativeto path has been set
201         if ($source =~ m/^(\/|[a-zA-W]\:[\/\\])/) {
202             if ($args{"relativeto"}) {
203                 my ($dir, $absrel) = (abs_path(dirname($source)), 
204                                       abs_path($args{"relativeto"}));
205                 $dir =~ s/$absrel//;
206                 $dest = $args{"to"} . $dir . '/' . basename($source);
207             } else {
208                 $dest = $args{"to"} . '/' . basename($source);
209             }
210         } else {
211             my $source2 = $source;
212             # let's remove the ../ component before the source filename, so 
213             # that it might be appended to the "to" directory
214             $source2 =~ s/^(\.\.[\/\\])*//;
215             $dest = $args{"to"} . '/' . $source2;
216         }
217         # Ensure both source and target are different
218         if (!$os_is_win) {
219             my ($i_dev, $i_ino) = stat $source;
220             my ($o_dev, $o_ino) = stat $dest;
221             croak "$PROGNAME: sorry, $source and $dest are the same file\n"
222               if ($i_dev == $o_dev && $i_ino == $o_ino);
223         }
224     }
225
226     # Update mode : skip the file if it is not newer than the 
227     # previously converted target
228     
229     if (exists $args{"update"} && ! exists $args{"force"}) {
230         next if -e $dest && (stat $source)[9] < (stat $dest)[9];
231     }
232     
233     ++$nb_file;
234     print "  $source\n" if exists $args{"verbose"};
235
236     # Open file, feed it entirely to an array
237
238     sysopen(HEADERFILE, $source, O_RDONLY|$open_file_as_text)
239       or croak "$PROGNAME: unable to open $source\n";
240     my @headerfile = <HEADERFILE>;
241     close(HEADERFILE);
242     
243     my ($date, $revision) = ("", "");
244     my @converted = ();
245     my @thanks = ();
246     
247     # Parse the file until the beginning of the documentation block
248     # is found. The copyright and disclaimer sections are parsed to 
249     # extract the 'Date', 'Version' and 'Thanks' values.
250     
251     my $line;
252     while ($line = shift @headerfile) {
253
254         # Quit if the beginning of the documentation block has been reached. 
255         # It is supposed to start with:
256         # // .NAME vtkFooBar - foo bar class
257
258         last if $line =~ /\/\/ \.NAME/;
259
260         # Date. Example:
261         # Date:      $Date: 2002/01/29 23:29:28 $
262
263         if ($line =~ /^\s*Date:\s*(.*)$/) {
264             $date = $1;
265
266         # Version. Example:
267         # Version:   $Revision: 1.7 $
268
269         } elsif ($line =~ /^\s*Version:\s*(.*)$/) {
270             $revision = $1;
271
272         # Thanks (maybe multi-lines). Example:
273         # Thanks:    Thanks to Sebastien Barre who developed this class.
274          
275         } elsif ($line =~ /^\s*Thanks:\s*(.*)$/) {
276             push @thanks, "             ", $1, "\n";
277             # Handle multi-line thanks
278             while ($line = shift @headerfile) {
279                 last if $line =~ /^\s*$/;
280                 $line =~ s/^(\s*)//;
281                 push @thanks, "             ", $line;
282             }
283             push @converted, $line;
284
285         # Everything else goes to the converted file
286
287         } else {
288             push @converted, $line;
289         }
290     }
291     
292     # Process the documentation block
293     # Extract the name of the class and its short description
294     # // .NAME vtkFooBar - foo bar class
295     
296     if (defined($line) && $line =~ /\/\/ \.NAME (\w*)( \- (.*))?/) {
297         
298         my ($class_name, $class_short_description) = ($1, $3);
299         $class_name =~ s/\.h//;
300         
301         # Insert class description, date, revision, thanks
302         
303         push @converted, "/*! \@class   $class_name\n";
304         push @converted, "    \@brief   $class_short_description\n"
305           if $class_short_description;
306
307         if ($date) {
308             push @converted, "\n    $date\n";
309         }
310
311         # WARNING : need a blank line between RCS tags and previous dox tag
312
313         if ($revision) {
314             push @converted, "\n" if (!$date);
315             push @converted, "    $revision\n";
316         }
317
318         # Do not add thanks anymore. Will be done externally.
319         # push @converted, "    \@par     Thanks:\n", @thanks if @thanks;
320         
321         # Read until the end of the documentation block is reached
322         # Translate 'See Also', 'Caveats' and whatever .SECTION
323         # As of 24 sep 2001, there are:
324         #      137      // .SECTION Caveats
325         #        1      // .SECTION Credits
326         #      702      // .SECTION Description
327         #        3      // .SECTION Note
328         #        1      // .SECTION note
329         #      329      // .SECTION See Also
330         #        4      // .SECTION See also
331         #       70      // .SECTION see also
332         #        1      // .SECTION Warning
333         # find . -name vtk\*.h -exec grep "\.SECTION" {} \; | sort | uniq -c
334         # Let's provide support for bugs too:
335         #               // .SECTION Bug
336         #               // .SECTION Bugs
337         #               // .SECTION Todo
338
339         my ($tag, $inblock) = ("", 0);
340         while ($line = shift @headerfile) {
341             
342             # Quit if the end of the documentation block has been reached. 
343             # Let'say that it is supposed to end as soon as the usual
344             # inclusion directives are found, for example:
345             # #ifndef __vtkAbstractTransform_h
346             # #define __vtkAbstractTransform_h
347             
348             last if $line =~ /^\#/;
349             
350             # Process and recognize a .SECTION command and convert it to
351             # the corresponding doxygen tag ($tag)
352
353             if ($line =~ /^\/\/\s+\.SECTION\s+(.+)\s*$/i) {
354                 
355                 my $type = $1;
356
357                 # Bugs (@bugs). Starts with:
358                 # // .SECTION Bug
359                 # // .SECTION Bugs
360             
361                 if ($type =~ /Bugs?/i) {
362                     $tag = "\@bug";
363                 }
364
365                 # Caveats or Warnings (@warning). Starts with:
366                 # // .SECTION Caveats
367                 # // .SECTION Warning
368                 # // .SECTION Warnings
369             
370                 elsif ($type =~ /(Caveats|Warnings?)/i) {
371                     $tag = "\@warning";
372                 }
373
374                 # Description. Starts with:
375                 # // .SECTION Description
376             
377                 elsif ($type =~ /Description/i) {
378                     $tag = "";
379                     push @converted, "\n";
380                 }
381
382                 # Note (@attention). Starts with:
383                 # // .SECTION Note
384
385                 elsif ($type =~ /Note/i) {
386                     $tag = "\@attention";
387                 }
388
389                 # See also (@sa). Starts with:
390                 # // .SECTION See Also
391                 
392                 elsif ($type =~ /See Also/i) {
393                     $tag = "\@sa";
394                 }
395
396                 # Todo (@todo). Starts with:
397                 # // .SECTION Todo
398
399                 elsif ($type =~ /Todo/i) {
400                     $tag = "\@todo";
401                 }
402
403                 # Any other .SECTION (@par). Starts with:
404                 # // .SECTION whatever
405                 
406                 else {
407                     $tag =  "\@par " . $type . ":";
408                 }
409
410                 $inblock = 0;
411             }
412             
413             # If the line starts with '//', we are still within the tag block.
414             # Remove '//' for non empty lines, eventually put or duplicate 
415             # the tag name if an empty comment is found (meaning that a new 
416             # 'paragraph' is requested but with the same tag type)
417             # Example:
418             #  // .SECTION Caveats
419             #  // blabla1q
420             #  // blabla1b
421             #  //
422             #  // blabla2
423             # Gets translated into:
424             #      @warning
425             #   blabla1q
426             #   blabla1b
427             #
428             #      @warning
429             #   blabla2
430             
431             elsif ($line =~ /^\/\/(.*)/) {
432                 my $remaining = $1;
433                 if ($remaining =~ /\S/) {
434                     push @converted, "    $tag\n" 
435                       if $tag ne "" && ! $inblock;
436                     push @converted, $remaining, "\n";
437                     $inblock = 1;
438                 } else {
439                     push @converted, "\n";
440                     $inblock = 0;
441                 }    
442             } else {
443                 # Does not starts with // but still within block or just
444                 # before the end (#). Probably an empty line.
445                 # Hack : let's have a look at the next line... if it begins
446                 # with // then the current line is included (was a space).
447                 
448                 if (my $next_line = shift @headerfile) {
449                     push @converted, $line if $next_line =~ /^\/\//;
450                     unshift @headerfile, $next_line;
451                 }
452             }
453         }
454         
455         # Close the doxygen documentation block describing the class
456         
457         push @converted, "*/\n\n", $line;
458     }
459     
460     # Read until the end of the header and translate the description of
461     # each function provided that it is located in a C++ comment
462     # containing the 'Description:' keyword.
463     # Example:
464     #  // Description:
465     #  // Construct with automatic computation of divisions, averaging
466     #  // 25 points per bucket.
467     #  static vtkPointLocator2D *New();
468     
469     while ($line = shift @headerfile) {
470         
471         if ($line =~ /^(\s*)\/\/\s*De(s|c)(s|c)?ription/) {
472             
473             my $indent = $1;
474             $Text::Wrap::columns = 76;
475             
476             # While there are still lines beginning with '//' append them to
477             # the function's description and trim spaces.
478             
479             my @description = ();
480             while ($line = shift @headerfile) {
481                 last if $line !~ /^\s*\/\//;
482                 chop $line;
483                 $line =~ s/^\s*\/\/\s*//;
484                 $line =~ s/\s*$//;
485                 push @description, $line;
486             }
487
488             # While there are non-empty lines add these lines to the
489             # list of declarations (and/or inline definitions)
490             # pertaining to the same description.
491
492             my @declarations = ();
493             while ($line && $line =~ /\s+\S/) {
494                 push @declarations, $line;
495                 $line = shift @headerfile
496             }
497
498             # If there is more than one declaration or at least a macro,
499             # enclose in a group (actually a single multiline declaration will
500             # be enclosed too, but who cares :)...
501
502             my $enclose =
503               (scalar @declarations > 1 || $declarations[0] =~ /vtk.+Macro/);
504             
505             push @converted, "$indent//@\{\n" if $enclose;
506             push @converted, 
507             wrap("$indent/*! ", "$indent    ", @description), " */\n"
508               if @description;
509             push @converted, @declarations;
510             push @converted, "$indent//@\}\n" if $enclose;
511         }
512         
513         push @converted, $line;
514     }
515     
516     # Write the converted header to its destination
517     # or to standard output.
518
519     if (exists $args{"stdout"}) {
520
521         print  @converted;
522
523     } else {
524
525         # Open the target and create the missing directory if any
526
527         if (!sysopen(DEST_FILE, 
528                      $dest, 
529                      O_WRONLY|O_TRUNC|O_CREAT|$open_file_as_text)) {
530             my $dir = dirname($dest);
531             mkpath($dir);
532             sysopen(DEST_FILE, 
533                     $dest, 
534                     O_WRONLY|O_TRUNC|O_CREAT|$open_file_as_text)
535               or croak "$PROGNAME: unable to open destination file $dest\n";
536         }
537         print DEST_FILE @converted;
538         close(DEST_FILE);
539         
540         # If in-place conversion was requested, remove source and rename target
541         # (or temp file) to source
542
543         if (! exists $args{"to"}) {
544             unlink($source)
545               or carp "$PROGNAME: unable to delete original file $source\n";
546             rename($args{"temp"}, $source)
547               or carp "$PROGNAME: unable to rename ", $args{"temp"}, " to $source\n";
548         }
549     }
550 }
551
552 if (! exists $args{"stdout"}) {
553     print " => $nb_file files converted in ", time() - $intermediate_time, " s. \n";
554     print "Finished in ", time() - $start_time, " s.\n";
555 }