#!perl
# This file is part of the build tools for Win32::GUI
# It expects to be run in the same directory as the make
# command is run from, and performs the following functions:
# (1) Parses the source files for documentation
# (2) Prepares the macros needed for generating the templated
# POD documentation.
# (3) Takes every POD document from the docs directory tree
# and does the macro substitution, copying it into the
# same relative location in the blib directory tree.
# (4) Generates the per-package(class) POD documentation
# directly in the blib directory tree.
# it is typically invoked as
# make poddocs
# or automatically as part of the distribution build
# process
#
# Author: Robert May , rmay@popeslane.clara.co.uk, 20 June 2005
# $Id: doPodDocs.pl,v 1.5 2006/03/16 21:11:12 robertemay Exp $
use strict;
use warnings;
use SrcParser;
use BuildTools;
$SrcParser::DEBUG = 0;
######################################################################
# (1) Parse the source fies for documentation.
{
my $src_dir = "."; # directory where the source files are
# Set up the files that we are going to parse for embedded documentation:
my @files;
# GUI.pm
push @files, "$src_dir/GUI.pm";
# GUI_MessageLoops.cpp
push @files, "$src_dir/GUI_MessageLoops.cpp";
# and all the XS files
opendir(my $DIR, $src_dir) || die "Can't open directory $src_dir: $!";
while(my $file = readdir($DIR)) {
push @files, "$src_dir/$file" if $file =~ /\.xs$/;
}
closedir($DIR);
# Parse the files
SrcParser::parse(@files);
}
# (2) Set up the macros that we need;
{
my $pkgtpl_file = "docs/GUI/Reference/Packages_package.tpl";
my $postamble_file = "docs/pod_postamble.tpl";
# set up the pod POSTAMBLE
BuildTools::macro_set_file("POSTAMBLE", $postamble_file);
# set up the PKGTPL per package template
BuildTools::macro_set_file("PKGTPL", $pkgtpl_file);
# set up the PACKLIST macro
my $packages = "";
for my $package (SrcParser::get_package_list()) {
(my $link = $package) =~ s/^Win32::GUI$/Win32::GUI::Reference::Methods/; # special case for Win32::GUI
BuildTools::macro_set("PKGNAME", $package);
BuildTools::macro_set("PKGLINK", $link);
$packages .= BuildTools::macro_subst("__W32G_PKGTPL__");
}
$packages =~ s/\s*$//;
BuildTools::macro_set("PACKLIST", $packages);
my $evttpl_file = "docs/GUI/Reference/Events_event.tpl";
# set up the EVTTPL per package template
BuildTools::macro_set_file("EVTTPL", $evttpl_file);
# set up the EVENTLIST macro
my $events = "";
for my $event (SrcParser::get_common_event_list()) {
BuildTools::macro_set("EVENTNAME", $event);
BuildTools::macro_set("EVENTPROTO", SrcParser::get_common_event_prototype($event));
BuildTools::macro_set("EVENTDESCR",
fix_description( "Win32::GUI::Reference::Events", SrcParser::get_common_event_description($event) )
);
$events .= BuildTools::macro_subst("__W32G_EVTTPL__");
}
$events =~ s/\s*$//;
BuildTools::macro_set("EVENTLIST", $events);
}
# (3) Copy the static POD documentation
{
my $static_pod_root = "docs"; # the starting point to find POD sources
my $blib_pod_root = "blib/lib/Win32"; # the matching root for placing processed POD docs
print BuildTools::macro_subst(
"Copying static POD from $static_pod_root for Win32::GUI v__W32G_VERSION__ on __W32G_DATE__\n"
);
# recurvively traverse the POD directories, building the output POD docs
cp_pod($static_pod_root, $blib_pod_root);
}
# (4) Generate the per package documentation
{
my $blib_pod_root = "blib/lib";
my $package_template_file = "docs/per_package.tpl";
my $package_methodsec_file = "docs/per_package_method_section.tpl";
my $pp_method_file = "docs/per_package_method.tpl";
my $package_eventsec_file = "docs/per_package_event_section.tpl";
my $pp_event_file = "docs/per_package_event.tpl";
# load the templates:
BuildTools::macro_set_file("PKGMETHODSEC", $package_methodsec_file);
BuildTools::macro_set_file("PKGEVENTSEC", $package_eventsec_file);
BuildTools::macro_set_file("METHOD", $pp_method_file);
BuildTools::macro_set_file("EVENT", $pp_event_file);
print BuildTools::macro_subst(
"Creating per package POD for Win32::GUI v__W32G_VERSION__ on __W32G_DATE__\n"
);
for my $package (SrcParser::get_package_list()) {
(my $pkg = $package) =~ s/^Win32::GUI$/Win32::GUI::Reference::Methods/; # special case for Win32::GUI
# set up PKGNAME macro
BuildTools::macro_set("PKGNAME", $pkg);
# set up PKGABSTRACT macro
BuildTools::macro_set("PKGABSTRACT", fix_abstract( SrcParser::get_package_abstract($package) ));
# set up PKGDESCR macro
BuildTools::macro_set("PKGDESCR", fix_description( $package, SrcParser::get_package_description($package) ));
# set up the PP_METHODS macro
my $pkgmethods = "";
for my $method (SrcParser::get_package_method_list($package)) {
BuildTools::macro_set("METHODNAME", $method);
BuildTools::macro_set("METHODPROTO", SrcParser::get_package_method_prototype($package, $method));
BuildTools::macro_set("METHODDESCR",
fix_description( $package, SrcParser::get_package_method_description($package, $method) )
);
$pkgmethods .= BuildTools::macro_subst("__W32G_METHOD__");;
# Move this to a template somehow
$pkgmethods .= "See also the L<common options|Win32::GUI::Reference::Options>.\n\n" if $method eq "new";
}
$pkgmethods =~ s/\s*//;
BuildTools::macro_set("PP_METHODS", $pkgmethods);
# TODO set up the PP_EVENTS macro
my $pkgevents = "";
for my $event (SrcParser::get_package_event_list($package)) {
BuildTools::macro_set("EVENTTITLE", $event);
BuildTools::macro_set("EVENTNAME", SrcParser::get_package_event_name($package, $event));
BuildTools::macro_set("EVENTPROTO", SrcParser::get_package_event_prototype($package, $event));
BuildTools::macro_set("EVENTDESCR",
fix_description( $package, SrcParser::get_package_event_description($package, $event) )
);
$pkgevents .= BuildTools::macro_subst("__W32G_EVENT__");;
}
$pkgevents =~ s/\s*//;
BuildTools::macro_set("PP_EVENTS", $pkgevents);
my $podfile = $pkg;
$podfile =~ s/::/\//g;
$podfile = "$blib_pod_root/$podfile.pod";
# copy over the template, doing substitution
BuildTools::macro_subst_cp($package_template_file, $podfile);
}
}
exit(0);
################################################################################
# cp_pod: recursively traverses source directory for *.pod documents. Each one
# found is copyied into the corresponing position below the destination directory
# and macro substitution is performed.
sub cp_pod
{
my $src_dir = shift;
my $dest_dir = shift;
# open the directory
opendir(my $DIR, $src_dir) || die "Can't open directory $src_dir for reading: $!";
while(my $file = readdir($DIR)) {
# process POD files
if($file =~ /\.pod$/) {
# perform the file copy and macro substitution
BuildTools::macro_subst_cp("$src_dir/$file", "$dest_dir/$file");
}
# resurse into directories
elsif (-d "$src_dir/$file" ) {
# ignore '.' and '..'
if ($file !~ /^\.{1,2}$/) {
cp_pod("$src_dir/$file", "$dest_dir/$file");
}
}
# ignore anything else
else {
}
}
closedir($DIR);
return 1;
}
################################################################################
# fix_description(descr): fixes a raw description, as returned by the parser
# - remove blank lines at the start
# - trim whitespace from the end of the lines
# - throw away multiple blank line
# - ensure that there are blank lines around blocks of indented text
# - parse and resolve links
# - remove all whitespace (includes trailing \n) from end of text
sub fix_description
{
my $package = shift;
my $text = shift;
my $indented = 0;
my $lastlineblank = 1; # causes removal of blank lines from the start
my $outtext = '';
# add a return if there isn't one, so that the split later
# doesn't cause any undefined warnings
$text .= "\n" if($text !~ /\n/);
# split into lines
my @lines = split("\n", $text);
for my $line (@lines) {
# remove whitespace from end of line
$line =~ s/\s*$//;
if ( $line eq '' ) {
# throw away multiple sets of blank lines, and
# blank lines at the start
next if $lastlineblank == 1;
$lastlineblank = 1;
$outtext .= "\n";
next;
}
if($lastlineblank) { # this is the first line in a new block
$lastlineblank = 0;
$indented = ($line =~ /^\s/);
}
else {
# add lines around indented text, if necessary
if( ($indented and $line !~ /^\s/) or
(not $indented and $line =~ /^\s/) ) {
$indented = not $indented;
$outtext .= "\n";
}
}
# parse the line for any links and add to the output
$outtext .= parse_links($package, $line). "\n";
}
# remove any remaining whitespace from the end
$outtext =~ s/\s*$//;
# if we have no description left, return [TBD]
return $outtext ? $outtext : '[TBD]';
}
################################################################################
# parse_links(text): find links to other docs
# looks for links of the form:
# - [sS]ee [also] func()
# - [sS]ee [also] new Win32::GUI::Package();
sub parse_links
{
my $pack = shift;
my $text = shift;
return $text unless defined $pack; # defensive
while ($text =~ /[sS]ee (also )?/g) {
$text =~ s/\G(new )?([\w:]+)\(\)/create_link($pack, $2, $1)/e;
}
return $text;
}
sub create_link
{
my $pack = shift;
my $method = shift;
my $new = shift;
my $text;
if($new) {
$pack = $method;
$method = "new";
$text = "new $pack()";
}
else {
#($pack = $method) =~ s/(.*::).*/$1/ if($method =~ /::/);
$text = "$method()";
if( $method =~ /(.*)::(.*?)$/ ) {
$pack = $1;
$method = $2;
}
}
# special case for Win32::GUI
$pack = "Win32::GUI::Reference::Methods" if $pack =~ /^Win32::GUI$/;
return "L<$text|$pack/$method>";
}
################################################################################
# fix_abstract(descr): fixes an abstract, as returned by the parser
# - remove whitesapce at tart and end
sub fix_abstract
{
my $text = shift;
$text =~ s/\s*(.*)\s*/$1/;
# if we have no description left, return [TBD]
return $text ? $text : 'A Win32::GUI package';
}