tools/fsh-builddocs
changeset 0 7f656887cf89
child 2 68b921ce1944
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 #!perl
       
     2 # fsh-builddocs
       
     3 # 
       
     4 # Copyright (c) 2010 Accenture. All rights reserved.
       
     5 # This component and the accompanying materials are made available
       
     6 # under the terms of the "Eclipse Public License v1.0"
       
     7 # which accompanies this distribution, and is available
       
     8 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     9 # 
       
    10 # Initial Contributors:
       
    11 # Accenture - Initial contribution
       
    12 #
       
    13 # Description:
       
    14 # fsh-builddocs - A tool that generates HTML documentation from POD source files.
       
    15 
       
    16 use strict;
       
    17 use Cwd;
       
    18 use Pod::Html;
       
    19 use IO::File;
       
    20 use File::Basename;
       
    21 use File::Path;
       
    22 use File::Copy;
       
    23 use Getopt::Long;
       
    24 use FindBin;
       
    25 use lib "$FindBin::Bin";
       
    26 use fshu;
       
    27 use CommandInfoFile;
       
    28 
       
    29 
       
    30 #
       
    31 # Constants.
       
    32 #
       
    33 
       
    34 my $kCpp = "\\epoc32\\gcc\\bin\\cpp -I. ";
       
    35 
       
    36 
       
    37 #
       
    38 # Globals.
       
    39 #
       
    40 
       
    41 my %options = (
       
    42 	       verbose => 0,
       
    43 	       includes => []
       
    44 	      );
       
    45 
       
    46 
       
    47 #
       
    48 # Main.
       
    49 #
       
    50 
       
    51 my $podListFile = ProcessCommandLine();
       
    52 my $podListDirName = dirname($podListFile);
       
    53 my $cwd = cwd();
       
    54 chdir ($podListDirName) or die "Error: Couldn't chdir to '$podListDirName' - $!\n";
       
    55 my $spec;
       
    56 
       
    57 eval {
       
    58   $spec = ParsePodList($podListFile);
       
    59   CopyCss($spec);
       
    60   CopyPod($spec);
       
    61   IdentifyIndexPod($spec);
       
    62   IdentifyCifPod($spec);
       
    63   GenerateIndexPod($spec);
       
    64   GenerateCifPod($spec);
       
    65   BuildHtml($spec);
       
    66 };
       
    67 my $error = $@;
       
    68 
       
    69 chdir ($cwd) or die "Error: Couldn't chdir back to '$cwd': $!\n";
       
    70 die $error if ($error);
       
    71 
       
    72 
       
    73 #
       
    74 # Subs.
       
    75 #
       
    76 
       
    77 sub ProcessCommandLine {
       
    78   my $help;
       
    79   GetOptions('h|help' => \$help,
       
    80 	     'v|verbose' => \$options{verbose},
       
    81 	     'w|what' => \$options{what},
       
    82 	     'c|clean' => \$options{clean},
       
    83 	     'i|include=s' => $options{includes}) or DisplayHelp();
       
    84   DisplayHelp() if ($help);
       
    85   warn "Invalid arguments\n" and DisplayHelp() unless (@ARGV == 1);
       
    86   return shift @ARGV;
       
    87 }
       
    88 
       
    89 sub DisplayHelp {
       
    90   require Pod::Text;
       
    91   print "\n";
       
    92   my $parser = Pod::Text->new();
       
    93   $parser->parse_from_file($0);
       
    94   exit;
       
    95 }
       
    96 
       
    97 sub ParsePodList {
       
    98   my $podListFileName = shift;
       
    99 
       
   100   local $_;
       
   101   my $command = $kCpp . join (' ', map {"-I$_"} @{$options{includes}}) . " $podListFileName 2>&1 |";
       
   102   print "Running '$command'\n" if ($options{verbose});
       
   103   open (CPP, $command) or die "Error: Couldn't run 'cpp.exe': $!\n";
       
   104   my $spec;
       
   105 
       
   106   my $currentDir;
       
   107   my $currentFile;
       
   108   my $currentLine = 0;
       
   109   while (my $line = <CPP>) {
       
   110     ++$currentLine;
       
   111     $line =~ s/^\s+//;
       
   112     next if (!$line); # Blank lines.
       
   113 
       
   114     if ($line =~ /^#\s+(\d+)\s+"([^"]+)"/) {
       
   115       $currentLine = $1;
       
   116       $currentFile = fshu::TidyPath($2);
       
   117       $currentDir = dirname($currentFile);
       
   118     }
       
   119     elsif ($line =~ /^doc-root\s+(\S+)\s*$/) {
       
   120       $spec->{docRoot} = fshu::TidyPath($1);
       
   121     }
       
   122     elsif ($line =~ /^temp-root\s+(\S+)\s*$/) {
       
   123       $spec->{tempRoot} = fshu::TidyPath($1);
       
   124     }
       
   125     elsif ($line =~ /^css\s+(\S+)\s*$/) {
       
   126       $spec->{cssFile} = fshu::TidyPath("$currentDir\\" . fshu::TidyPath($1));
       
   127     }
       
   128     elsif ($line =~ /^pod\s+(\S+)\s+(\S+)\s*$/) {
       
   129       my $podFileName = fshu::TidyPath("$currentDir\\" . fshu::TidyPath($1));
       
   130       my $htmlFileName = fshu::TidyPath($2);
       
   131 
       
   132       # If this HTML file matches one we already have in the list, allow this one to override.
       
   133       my $found = 0;
       
   134       foreach my $podFile (@{$spec->{podFiles}}) {
       
   135 	if (lc($podFile->{htmlFileName}) eq lc($htmlFileName)) {
       
   136 	  $podFile->{podFileName} = $podFileName;
       
   137 	  $found = 1;
       
   138 	  last;
       
   139 	}
       
   140       }
       
   141       unless ($found) {
       
   142 	push (@{$spec->{podFiles}}, {
       
   143 				     podFileName => $podFileName,
       
   144 				     htmlFileName => $htmlFileName
       
   145 				    });
       
   146       }
       
   147     }
       
   148     elsif ($line =~ /^index\s+(\S+)/) {
       
   149       my $htmlDirName = fshu::TidyPath($1);
       
   150       my $podFileName;
       
   151       if ($line =~ /^index\s+\S+\s+(\S+)\s*$/) {
       
   152 	$podFileName = fshu::TidyPath("$currentDir\\" . fshu::TidyPath($1));
       
   153       }
       
   154       push (@{$spec->{indices}}, {
       
   155 				   podFileName => $podFileName,
       
   156 				   htmlDirName => $htmlDirName
       
   157 				  });
       
   158     }
       
   159     elsif ($line =~ /^cif\s+(\S+)\s+(\S+)/) {
       
   160       my $cifDirName = fshu::TidyPath($1);
       
   161       my $htmlDirName = fshu::TidyPath($2);
       
   162       my $podFileName;
       
   163       if ($line =~ /^cif\s+\S+\s+\S+\s+(\S+)\s*$/) {
       
   164 	$podFileName = fshu::TidyPath("$currentDir\\" . fshu::TidyPath($1));
       
   165       }
       
   166       push (@{$spec->{cifIndices}}, {
       
   167 				      podFileName => $podFileName,
       
   168 				      htmlDirName => $htmlDirName,
       
   169 				      cifDirName => $cifDirName
       
   170 				     });
       
   171     }
       
   172     else {
       
   173       die "Error: Invalid pod-list line at $currentFile($currentLine):\n$line";
       
   174     }
       
   175   }
       
   176 
       
   177   close (CPP);
       
   178 
       
   179   die "Error: No 'doc-root' in pod-list\n" unless ($spec->{docRoot});
       
   180   die "Error: No 'temp-root' in pod-list\n" unless ($spec->{tempRoot});
       
   181   return $spec;
       
   182 }
       
   183 
       
   184 sub CopyCss {
       
   185   my $spec = shift;
       
   186 
       
   187   if ($spec->{cssFile}) {
       
   188     my $target = "$spec->{docRoot}\\" . basename($spec->{cssFile});
       
   189     if ($options{what}) {
       
   190       print "$target\n";
       
   191     }
       
   192     elsif ($options{clean}) {
       
   193       unlink ($target);
       
   194     }
       
   195     else {
       
   196       fshu::CopyFile($spec->{cssFile}, $target, $options{verbose});
       
   197     }
       
   198   }
       
   199 }
       
   200 
       
   201 sub CopyPod {
       
   202   my $spec = shift;
       
   203 
       
   204   foreach my $podEntry (@{$spec->{podFiles}}) {
       
   205     my $targetFileName = "$spec->{tempRoot}\\$podEntry->{htmlFileName}";
       
   206     $targetFileName =~ s/\.[^\.]+$/\.pod/;
       
   207     $podEntry->{tempPodFileName} = $targetFileName;
       
   208     if ($options{what}) {
       
   209       # Do nothing.
       
   210     }
       
   211     elsif ($options{clean}) {
       
   212       unlink ($targetFileName);
       
   213     }
       
   214     else {
       
   215       fshu::CopyFile($podEntry->{podFileName}, $targetFileName, $options{verbose});
       
   216     }
       
   217   }
       
   218 }
       
   219 
       
   220 sub IdentifyIndexPod {
       
   221   my $spec = shift;
       
   222 
       
   223   # This sub-routine identifies the index POD files that will be generated later on.
       
   224   # This is done as a separate phase to allow indices to contain links generated POD.
       
   225 
       
   226   foreach my $indexEntry (@{$spec->{indices}}) {
       
   227     push (@{$spec->{podFiles}}, {
       
   228 				 podFileName => "$indexEntry->{htmlDirName}.pod",
       
   229 				 tempPodFileName => "$spec->{tempRoot}\\$indexEntry->{htmlDirName}.pod",
       
   230 				 htmlFileName => "$indexEntry->{htmlDirName}.html"
       
   231 				});
       
   232   }
       
   233 }
       
   234 
       
   235 sub GenerateIndexPod {
       
   236   my $spec = shift;
       
   237 
       
   238   foreach my $indexEntry (@{$spec->{indices}}) {
       
   239     my $tempDir = "$spec->{tempRoot}\\$indexEntry->{htmlDirName}";
       
   240     my $indexPodFileName = "$tempDir.pod";
       
   241 
       
   242     if ($options{what}) {
       
   243       # Do nothing.
       
   244     }
       
   245     elsif ($options{clean}) {
       
   246       unlink ($indexPodFileName);
       
   247     }
       
   248     else {
       
   249       print "Building $indexPodFileName\n" if ($options{verbose});
       
   250       fshu::MakePath($tempDir);
       
   251       open (INDEX, ">$indexPodFileName") or die "Error: Couldn't open '$indexPodFileName' for writing - $!\n";
       
   252 
       
   253       # Copy the contents of the introductory POD.
       
   254       if ($indexEntry->{podFileName}) {
       
   255 	open (POD, $indexEntry->{podFileName}) or die "Error: Couldn't open '$indexEntry->{podFileName}' for reading - $!\n";
       
   256 	while (my $line = <POD>) {
       
   257 	  print INDEX $line;
       
   258 	}
       
   259 	close (POD);
       
   260       }
       
   261       else {
       
   262 	print INDEX "=head1 $indexEntry->{htmlDirName}\n\n";
       
   263       }
       
   264 
       
   265       # Add the links.
       
   266       my $htmlDir = lc($indexEntry->{htmlDirName});
       
   267       foreach my $podEntry (@{$spec->{podFiles}}) {
       
   268 	if (lc(dirname($podEntry->{htmlFileName})) eq $htmlDir) {
       
   269 	  my $fileName = $podEntry->{htmlFileName};
       
   270 	  $fileName =~ s/\.[^\.]+$//;
       
   271 	  $fileName =~ s/\\/\//g;
       
   272 	  my $presentableFileName = basename($fileName);
       
   273 	  $presentableFileName = ucfirst($presentableFileName);
       
   274 	  $presentableFileName =~ s/_/ /g;
       
   275 	  $fileName =~ s/\//::/g;
       
   276 	  print INDEX "\nL<$presentableFileName|$fileName>\n";
       
   277 	}
       
   278       }
       
   279 
       
   280       close (INDEX);
       
   281     }
       
   282   }
       
   283 }
       
   284 
       
   285 sub IdentifyCifPod {
       
   286   my $spec = shift;
       
   287 
       
   288   # This sub-routine identifies the CIF index POD files that will be generated later on.
       
   289   # This is done as a separate phase to allow indices to contain links generated POD.
       
   290 
       
   291   foreach my $podEntry (@{$spec->{cifIndices}}) {
       
   292     push (@{$spec->{podFiles}}, {
       
   293 				 podFileName => "$podEntry->{htmlDirName}.pod",
       
   294 				 tempPodFileName => "$spec->{tempRoot}\\$podEntry->{htmlDirName}.pod",
       
   295 				 htmlFileName => "$podEntry->{htmlDirName}.html"
       
   296 				});
       
   297   }
       
   298 }
       
   299 
       
   300 sub GenerateCifPod {
       
   301   my $spec = shift;
       
   302 
       
   303   foreach my $podEntry (@{$spec->{cifIndices}}) {
       
   304     my $tempDir = "$spec->{tempRoot}\\$podEntry->{htmlDirName}";
       
   305     my $indexPodFileName = "$tempDir.pod";
       
   306     my $indexFile;
       
   307 
       
   308     if ($options{what}) {
       
   309       # Do nothing.
       
   310     }
       
   311     elsif ($options{clean}) {
       
   312       unlink ($indexPodFileName);
       
   313     }
       
   314     else {
       
   315       print "Building $indexPodFileName\n" if ($options{verbose});
       
   316       fshu::MakePath($tempDir);
       
   317       $indexFile = IO::File->new(">$indexPodFileName") or die "Error: Couldn't open '$indexPodFileName' for writing: $!\n";
       
   318 
       
   319       # Copy the contents of the introductory POD.
       
   320       if ($podEntry->{podFileName}) {
       
   321 	open (POD, $podEntry->{podFileName}) or die "Error: Couldn't open '$podEntry->{podFileName}' for reading - $!\n";
       
   322 	while (my $line = <POD>) {
       
   323 	  print $indexFile $line;
       
   324 	}
       
   325 	close (POD);
       
   326       }
       
   327       else {
       
   328 	print $indexFile "=head1 $podEntry->{htmlDirName}\n\n";
       
   329       }
       
   330 
       
   331       # Add an entry for the index .pod to cause the HTML to be generated later.
       
   332       push (@{$spec->{podFiles}}, {
       
   333 				   tempPodFileName => $indexPodFileName,
       
   334 				   htmlFileName => "$podEntry->{htmlDirName}.html"
       
   335 				  });
       
   336     }
       
   337 
       
   338     # Generate a POD file for each CIF and add a line for each to the index file.
       
   339     opendir(DIR, $podEntry->{cifDirName}) or die "Error: Couldn't opendir '$podEntry->{cifDirName}' - $!\n";
       
   340     while (defined(my $file = readdir(DIR))) {
       
   341       my $cifFileName = "$podEntry->{cifDirName}\\$file";
       
   342       if (-f $cifFileName and ($cifFileName =~ /\.cif$/i)) {
       
   343 	my $podFileName = "$tempDir\\$file";
       
   344 	$podFileName =~ s/\.cif$/\.pod/i;
       
   345 	my $htmlFileName = "$podEntry->{htmlDirName}\\$file";
       
   346 	$htmlFileName =~ s/\.cif$/\.html/i;
       
   347 	
       
   348 	print "Reading '$cifFileName'...\n" if ($options{verbose});
       
   349 	my $cif = CommandInfoFile->New($cifFileName);
       
   350 	Cif2Pod($cif, $podFileName, $htmlFileName, $indexFile, $spec->{podFiles});
       
   351       }
       
   352     }
       
   353     closedir(DIR);
       
   354 
       
   355     if ($indexFile) {
       
   356       $indexFile->close();
       
   357     }
       
   358   }
       
   359 }
       
   360 
       
   361 sub BuildHtml {
       
   362   my $spec = shift;
       
   363 
       
   364   my $cwd = cwd();
       
   365   unless ($options{what} or $options{clean}) {
       
   366     chdir ($spec->{tempRoot}) or die "Error: Couldn't chdir to '$spec->{tempRoot}' - $!\n";
       
   367   }
       
   368 
       
   369   eval {
       
   370     my $first = 1;
       
   371     foreach my $podEntry (@{$spec->{podFiles}}) {
       
   372       DoBuildHtml($podEntry->{tempPodFileName}, $podEntry->{htmlFileName}, $spec->{docRoot}, $spec->{cssFile}, $first);
       
   373       $first = 0 if ($first);
       
   374     }
       
   375   };
       
   376 
       
   377   my $error = $@;
       
   378   chdir $cwd;
       
   379   die $@ if $@;
       
   380 }
       
   381 
       
   382 sub DoBuildHtml {
       
   383   my $podFileName = shift;
       
   384   my $htmlFileName = shift;
       
   385   my $docRoot = shift;
       
   386   my $css = shift;
       
   387   my $first = shift;
       
   388 
       
   389   my $outputFileName = "$docRoot\\$htmlFileName";
       
   390 
       
   391   if ($options{what}) {
       
   392     print "$outputFileName\n";
       
   393   }
       
   394   elsif ($options{clean}) {
       
   395     unlink ($outputFileName);
       
   396   }
       
   397   else {
       
   398     print "Building $outputFileName\n" if ($options{verbose});
       
   399     fshu::MakePath(dirname($outputFileName));
       
   400 
       
   401     my @elements = split /\\/, $htmlFileName;
       
   402     my $numElements = @elements;
       
   403     my $pathRelativeToDocRoot = '.';
       
   404     for (my $i = 0; $i < ($numElements - 1); ++$i) {
       
   405       if ($pathRelativeToDocRoot eq '.') {
       
   406 	$pathRelativeToDocRoot = '..';
       
   407       }
       
   408       else {
       
   409 	$pathRelativeToDocRoot = "..\\$pathRelativeToDocRoot";
       
   410       }
       
   411     }
       
   412 
       
   413     my @args = ("--podpath=.",
       
   414 		"--podroot=.",
       
   415 		"--htmlroot=$pathRelativeToDocRoot",
       
   416 		"--recurse",
       
   417 		"--infile=$podFileName",
       
   418 		"--outfile=$outputFileName");
       
   419     #  push (@args, '--verbose') if ($options{verbose});
       
   420     if ($css) {
       
   421       my $relativeCssName = "$pathRelativeToDocRoot\\" . basename($css);
       
   422       push(@args, "--css=$relativeCssName");
       
   423     }
       
   424     if ($first) {
       
   425       push (@args, '--flush');
       
   426     }
       
   427 
       
   428     print "Calling pod2html - ", join(' ', @args), "\n" if ($options{verbose});
       
   429     pod2html(@args);
       
   430   }
       
   431 }
       
   432 
       
   433 sub Cif2Pod {
       
   434   my $cif = shift;
       
   435   my $podFileName = shift;
       
   436   my $htmlFileName = shift;
       
   437   my $indexFile = shift;
       
   438   my $podFileList = shift;
       
   439 
       
   440   # Add an entry for the CIF .pod to cause the HTML to be generated later.
       
   441   push (@$podFileList, {
       
   442 			tempPodFileName => $podFileName,
       
   443 			htmlFileName => $htmlFileName
       
   444 		       });
       
   445 
       
   446   if ($options{what}) {
       
   447     # Do nothing.
       
   448   }
       
   449   elsif ($options{clean}) {
       
   450     unlink ($podFileName);
       
   451   }
       
   452   else {
       
   453     open (OUTPUT, ">$podFileName") or die "Error: Couldn't open '$podFileName' for writing: $!\n";
       
   454 
       
   455     print OUTPUT "=head1 SYNTAX\n\n";
       
   456     my $fullName = $cif->FullName();
       
   457     $fullName =~ s/^\S+\s//; # Remove the root command name if this is a sub-command.
       
   458     print OUTPUT '    ', $fullName;
       
   459 
       
   460     my $options = $cif->Options();
       
   461     if ($options and (@$options > 0)) {
       
   462       print OUTPUT ' [options]';
       
   463     }
       
   464 
       
   465     my $arguments = $cif->Arguments();
       
   466     foreach my $argument (@$arguments) {
       
   467       if ($argument->{flags}->{optional}) {
       
   468 	print OUTPUT ' [<';
       
   469       }
       
   470       else {
       
   471 	print OUTPUT ' <';
       
   472       }
       
   473       print OUTPUT $argument->{name};
       
   474       if ($argument->{flags}->{optional}) {
       
   475 	print OUTPUT '>]';
       
   476       }
       
   477       else {
       
   478 	print OUTPUT '>';
       
   479       }
       
   480       if ($argument->{flags}->{multiple}) {
       
   481 	print OUTPUT ' ...';
       
   482       }
       
   483     }
       
   484     print OUTPUT "\n\n";
       
   485 
       
   486     if ($options and (@$options > 0)) {
       
   487       print OUTPUT "=head1 OPTIONS\n\n";
       
   488       print OUTPUT "=over5\n\n";
       
   489       foreach my $option (@$options) {
       
   490 	print OUTPUT "=item -$option->{short_name} (--$option->{long_name}) $option->{type}\n\n$option->{description}";
       
   491 	if ($option->{type} eq 'enum') {
       
   492 	  FormatEnum($option->{enum_values});
       
   493 	}
       
   494 	if ($option->{flags}->{multiple}) {
       
   495 	  print OUTPUT " Can be specified more than once.";
       
   496 	}
       
   497 	if ($option->{env_var}) {
       
   498 	  print OUTPUT " Can also be specified by defining the environment variable '$option->{env_var}'.";
       
   499 	}
       
   500 	print OUTPUT "\n\n";
       
   501       }
       
   502       print OUTPUT "\n\n=back\n\n";
       
   503     }
       
   504 
       
   505     if ($arguments and (@$arguments > 0)) {
       
   506       print OUTPUT "=head1 ARGUMENTS\n\n\n\n";
       
   507       print OUTPUT "=over5\n\n";
       
   508       foreach my $argument (@$arguments) {
       
   509 	print OUTPUT '=item ';
       
   510 	if ($argument->{flags}->{optional}) {
       
   511 	  print OUTPUT '[<';
       
   512 	}
       
   513 	else {
       
   514 	  print OUTPUT '<';
       
   515 	}
       
   516 	print OUTPUT $argument->{name};
       
   517 	if ($argument->{flags}->{optional}) {
       
   518 	  print OUTPUT ">]\n\n";
       
   519 	}
       
   520 	else {
       
   521 	  print OUTPUT ">\n\n";
       
   522 	}
       
   523 	print OUTPUT $argument->{description};
       
   524 	if ($argument->{flags}->{multiple}) {
       
   525 	  print OUTPUT ' Can be specified more than once.';
       
   526 	}
       
   527 	if ($argument->{env_var}) {
       
   528 	  print OUTPUT " Can also be specified by defining the environment variable '$argument->{env_var}'.";
       
   529 	}
       
   530 	if ($argument->{flags}->{last}) {
       
   531 	  print OUTPUT ' Any further arguments or options will be coalesced into this one.';
       
   532 	}
       
   533 	
       
   534 	if ($argument->{type} eq 'enum') {
       
   535 	  FormatEnum($argument->{enum_values});
       
   536 	}
       
   537 	elsif ($argument->{flags}->{multiple}) {
       
   538 	  print OUTPUT " [$argument->{type}(s)]";
       
   539 	}
       
   540 	else {
       
   541 	  print OUTPUT " [$argument->{type}]";
       
   542 	}
       
   543 	print OUTPUT "\n\n";
       
   544       }
       
   545       print OUTPUT "\n\n=back\n\n";
       
   546     }
       
   547 
       
   548     print OUTPUT "=head1 DESCRIPTION\n\n";
       
   549     print OUTPUT $cif->ShortDescription(), "\n\n";
       
   550     if ($cif->LongDescription()) {
       
   551       print OUTPUT $cif->LongDescription(), "\n\n";
       
   552     }
       
   553     if ($cif->SeeAlso()) {
       
   554       print OUTPUT "=head1 SEE ALSO\n\n";
       
   555       print OUTPUT $cif->SeeAlso(), "\n\n";
       
   556     }
       
   557 
       
   558     if ($cif->NumSubCommands() > 0) {
       
   559       print OUTPUT "=head1 SUB-COMMANDS\n\n";
       
   560       my $numSubCommands = $cif->NumSubCommands();
       
   561       for (my $i = 0; $i < $numSubCommands; ++$i) {
       
   562 	my $subCommandCif = $cif->SubCommand($i);
       
   563 	my $subCommandName = $subCommandCif->Name();
       
   564 	my $subCommandPodName = $subCommandCif->FullName();
       
   565 	$subCommandPodName =~ s/ /-/g;
       
   566 	print OUTPUT "L<$subCommandName|$subCommandPodName>\n\n";
       
   567       }
       
   568     }
       
   569 
       
   570     if ($cif->Copyright()) {
       
   571       print OUTPUT "=head1 COPYRIGHT\n\n";
       
   572       print OUTPUT $cif->Copyright(), "\n\n";
       
   573     }
       
   574 
       
   575     close (OUTPUT);
       
   576   }
       
   577 
       
   578   if ($indexFile) {
       
   579     my $description = $cif->ShortDescription();
       
   580     $description =~ s/^\s*//;
       
   581     $description =~ s/\s*$//;
       
   582     print $indexFile "\nL<$cif->{name}|$cif->{name}> - $description\n\n";
       
   583   }
       
   584 
       
   585   if ($cif->NumSubCommands() > 0) {
       
   586     # Recurse through sub-commands.
       
   587     my $numSubCommands = $cif->NumSubCommands();
       
   588     for (my $i = 0; $i < $numSubCommands; ++$i) {
       
   589       my $thisSubCommandCif = $cif->SubCommand($i);
       
   590       my $fullSubCommandName = $thisSubCommandCif->FullName();
       
   591       $fullSubCommandName =~ s/ /-/g;
       
   592       my $podName = $podFileName;
       
   593       $podName =~ s/[^\\]+$/$fullSubCommandName\.pod/;
       
   594       my $htmlName = $htmlFileName;
       
   595       $htmlName =~ s/[^\\]+$/$fullSubCommandName\.html/;
       
   596       Cif2Pod($cif->SubCommand($i), $podName, $htmlName, undef, $podFileList);
       
   597     }
       
   598   }
       
   599 }
       
   600 
       
   601 sub FormatEnum {
       
   602   my $enumValues = shift;
       
   603 
       
   604   my $descriptionsPresent = 0;
       
   605   foreach my $enumValue (@$enumValues) {
       
   606     if ($enumValue->{description} !~ /^\s*$/) {
       
   607       $descriptionsPresent= 1;
       
   608       last;
       
   609     }
       
   610   }
       
   611   if ($descriptionsPresent) {
       
   612     print OUTPUT "\n\n=over 5\n\n";
       
   613     foreach my $enumValue (@$enumValues) {
       
   614       print OUTPUT "=item $enumValue->{value}\n\n";
       
   615       if ($enumValue->{description}) {
       
   616 	print OUTPUT $enumValue->{description};
       
   617       }
       
   618     }
       
   619     print OUTPUT "\n\n=back\n\n";
       
   620   }
       
   621   else {
       
   622     local $_;
       
   623     print OUTPUT ' [', join (', ', map {$_->{value}} @$enumValues), ']';
       
   624   }
       
   625 }
       
   626 
       
   627 __END__
       
   628 
       
   629 =head1 NAME
       
   630 
       
   631 fsh-builddocs - A tool that generates HTML documentation from POD source files.
       
   632 
       
   633 =head1 SYNOPSIS
       
   634 
       
   635 fsh-builddocs <pod-list-file>
       
   636 
       
   637 options:
       
   638 
       
   639   -h (--help)        Display this help page.
       
   640   -v (--verbose)     Verbose output.
       
   641   -w (--what)        Do nothing but print the names of the files that would
       
   642                      be generated.
       
   643   -c (--clean)       Delete target files, rather than build them.
       
   644 
       
   645 =head1 DESCRIPTION
       
   646 
       
   647 This tool accepts a list of POD files (and indices - more on them later) and generates a corresponding set of HTML files within the specified output directory. The directory structure of the POD source files is mirrored in the output directory.
       
   648 
       
   649 The C<pod-list> file is preprocessed using F<cpp.exe> and so macro directives can be used to modify the list to suit a particular build configuration.
       
   650 
       
   651 The following syntax is supported for C<pod-list> files:
       
   652 
       
   653 =over 5
       
   654 
       
   655 =item C<doc-root E<lt>documentation_root_dirE<gt>>
       
   656 
       
   657 The directory into which the documentation HTML files will be generated.
       
   658 
       
   659 =item C<temp-root E<lt>temporary_file_root_dirE<gt>>
       
   660 
       
   661 The directory into any temporary files that are needed will be written.
       
   662 
       
   663 =item C<css E<lt>css_file_nameE<gt>>
       
   664 
       
   665 The name of a cascaded style sheet file that each of the generated HTML files should refer to. If specified, the file will be coped into the documentation root directory.
       
   666 
       
   667 =item C<pod E<lt>pod_file_nameE<gt> E<lt>html_file_nameE<gt>>
       
   668 
       
   669 Specifies a POD source file and a target HTML. The HTML file names are expected to be relative to the documentation root directory (specified with C<doc-root>).
       
   670 
       
   671 =item C<index E<lt>html_dir_nameE<gt>> [E<lt>index_pod_file_nameE<gt>]
       
   672 
       
   673 Generates a file named C<html_dir_name>F<.html>. This is produced by taking the contents of F<index_pod_file_name> (if specfied) and appending to that a links to each other HTML files that are present in the specified HTML directory. Note, because C<pod-list> files are preprocessed, it is possible to vary the contents of the index according to the build configuration by using #ifdef directives to prevent certain C<pod> lines from being active. For example:
       
   674 
       
   675   pod my_pod_dir\some_pod_that_is_always_build.pod  my_html_dir\some_pod_that_is_always_built.html
       
   676   #ifdef SOME_BUILD CONFIG
       
   677   pod my_pod_dir\some_pod_that_is_only_built_sometimes.pod  my_html_dir\some_pod_that_is_only_built_sometimes.html
       
   678   #endif
       
   679   index my_html_dir my_pod_dir\index.pod
       
   680 
       
   681 =item C<cif E<lt>cif_dir_nameE<gt> E<lt>html_dir_nameE<gt> [E<lt>index_pod_file_nameE<gt>]>
       
   682 
       
   683 Specifies a directory containing a set of Command Information Files (CIFs). The CIFs are converted to POD and in the temporary directory and then to HTML in the HTML directory (which is expected to be relative to the documentation root directory). In addition a page containing links to each of the CIF HTML files is generated. This is named C<html_dir_name>F<.html> and is headed with the POD from C<index_pod_file_name> if that has been specified.
       
   684 
       
   685 =back
       
   686 
       
   687 =head1 KNOWN BUGS
       
   688 
       
   689 None known.
       
   690 
       
   691 =head1 COPYRIGHT
       
   692 
       
   693 Copyright (c) 2010 Accenture. All rights reserved.
       
   694 
       
   695 =cut