17 "html|h=s" => \$SOURCE_DIR,
18 "target|t=s" => \$TARGET_DIR,
19 "warnings|W" => \$WARNINGS,
22 pod2usage(0) if $HELP;
32 my $stylesheet = load_stylesheet($SOURCE_DIR);
33 load_templates($SOURCE_DIR, \%templates);
34 process_source_files(\%docs);
35 merge(\%docs, \%templates, \$stylesheet);
39 # Load CSS stylesheet.
43 my $file_path = "$dir_path/api-style.css";
44 open (my $file, '<', $file_path) or die "Could not open $file_path";
46 my $contents = <$file>;
52 # Load HTML templates.
55 my ($dir_path, $templates) = @_;
56 opendir (my $dir, "$dir_path/sources/") or die "Could not open $dir_path";
57 while (my $file_name = readdir ($dir)) {
58 next if $file_name !~ /mono-api-.*\.html$/;
59 open (my $file, "$dir_path/sources/$file_name") or die "Could not open $file_name";
64 if (/name="api:(.*?)"/) {
65 s/.*name="api:(\w+?)".*/$1/;
70 $templates->{$file_name}->{contents} = $contents;
71 $templates->{$file_name}->{api} = \@api;
77 # Extract documentation from all source files.
79 sub process_source_files {
81 for my $file_path (@ARGV) {
82 process_source_file($file_path, $docs);
87 # Extract documentation from a single source file.
89 sub process_source_file {
90 my ($file_path, $docs) = @_;
91 open (my $file, '<', $file_path) or die "Could not open $file_path";
93 next if (!/\/\*\* *\n/);
94 process_function($file, $file_path, $docs);
100 # Extract documentation from a single function.
102 sub process_function {
104 my ($file, $file_path, $docs) = @_;
106 my $PARAMETER_SECTION = 0;
107 my $BODY_SECTION = 1;
108 my $RETURN_SECTION = 2;
109 my $section = $PARAMETER_SECTION;
119 # Ignore irrelevant functions, and those with the wrong doc format.
120 return if $name !~ /^mono_\w+$/;
130 # We've reached the last line in the documentation block.
133 # Grab function prototype.
139 # Clean up prototype.
142 # Strip braces and trailing whitespace.
145 # Turn "Type * xxx" into "Type* xxx"
150 # Process formatting within sections.
151 for my $parameter (@parameters) {
152 process_formatting(\$parameter->{description});
154 process_formatting(\$returns);
155 process_formatting(\$body);
158 if (exists($docs->{body}->{$name})) {
159 my $origin = $docs->{origin}->{$name};
162 "$file_path:$.: Redundant documentation for $name\n",
163 "$origin->{file}:$origin->{line}: Previously defined here\n";
166 $docs->{origin}->{$name} = { file => $file_path, line => $. };
167 $docs->{body}->{$name} = $body;
168 $docs->{parameters}->{$name} = \@parameters;
169 $docs->{deprecated}->{$name} = $deprecated if defined $deprecated;
170 $docs->{return}->{$name} = $returns;
171 $docs->{prototype}->{$name} = $prototype;
176 # Strip newlines and asterisk prefix.
180 # Replace blank lines with paragraph breaks.
181 $_ = '<p>' if /^\s*$/;
183 if ($section == $PARAMETER_SECTION) {
184 if (/\s*(\w+):(.*)/) {
185 if ($1 eq 'deprecated') {
188 push @parameters, { name => $1, description => $2 };
192 $section = $BODY_SECTION;
194 } elsif ($section == $BODY_SECTION) {
198 $section = $RETURN_SECTION;
202 } elsif ($section == $RETURN_SECTION) {
203 $returns .= "\n\t$_";
205 die "Invalid section $section\n";
211 # Substitute formatting within documentation text.
213 sub process_formatting {
218 s{NULL}{<code>NULL</code>}g;
219 s{TRUE}{<code>TRUE</code>}g;
220 s{FALSE}{<code>FALSE</code>}g;
223 s{@(\w+)}{<i>$1</i>}g;
226 s{#(\w+)}{<code>$1</code>}g;
227 s{\`([:.\w\*]+)\`}{<code>$1</code>}g;
233 # Merge templates with stylesheet and documentation extracted from sources.
236 my ($docs, $templates, $stylesheet) = @_;
238 for my $name (keys %$templates) {
239 open (my $output_file, '>', "$TARGET_DIR/html/$name")
240 or die "Could not create $TARGET_DIR/html/$name";
241 print "Merging: $name\n";
242 print $output_file <<EOF;
243 <?xml version="1.0" encoding="utf-8"?>
244 <html xmlns="http://www.w3.org/1999/xhtml">
247 <style type="text/css">
252 <div class="mapi-docs">
254 my @a = split (/\n/, $templates->{$name}->{contents});
256 my $strikeextra = '';
258 for (my $ai = 0; $ai < $#a; $ai++) {
260 if (my ($api, $caption) = ($line =~ /<h4><a name=\"api:(\w+)\">(\w+)<\/a><\/h4>/)) {
261 if ($api_shown == 1) {
262 print $output_file "</div> <!-- class=mapi -->\n\n";
263 if ($docs->{deprecated}->{$api}) {
264 $strike = "mapi-strike";
265 $strikeextra = "</div><br><div class='mapi-deprecated'><b>Deprecated:</b> " . $docs->{deprecated}->{$api};
272 my $proto = $docs->{prototype}->{$api} // $api;
274 print $output_file <<EOF;
275 <a name="api:$api"></a>
277 <div class="mapi-entry $strike"><code>$api$strikeextra</code></div>
278 <div class="mapi-height-container">
279 <div class="mapi-ptr-container"></div>
280 <div class="mapi-description">
281 <div class="mapi-ptr"></div>
283 <div class="mapi-declaration mapi-section">Syntax</div>
284 <div class="mapi-prototype">$proto</div>
287 if (exists ($docs->{parameters}->{$api})) {
288 my $ppars = $docs->{parameters}->{$api};
291 " <div class=\"mapi-section\">Parameters</div>\n",
292 " <table class=\"mapi-parameters\"><tbody>",
293 render_parameters($ppars),
298 opt_print ($output_file, "Return value", $docs->{return}->{$api});
299 opt_print ($output_file, "Description", $docs->{body}->{$api});
300 print $output_file " </div><!--mapi-description-->\n </div><!--height container-->\n";
302 if ($line =~ /\@API_IDX\@/) {
303 my $apis_toc = create_toc ($docs, $templates->{$name}->{api});
304 $line =~ s/\@API_IDX\@/$apis_toc/;
306 if ($line =~ /^<h4/) {
307 print $output_file "</div>\n";
312 print $output_file "$line\n";
320 system ("$ENV{runtimedir}/mono-wrapper convert.exe $TARGET_DIR/html/$name $TARGET_DIR/html/x-$name");
322 # Clean up the mess that AgilityPack makes (it CDATAs our CSS).
323 open (my $hack_input, '<', "$TARGET_DIR/html/x-$name")
324 or die "Could not open $TARGET_DIR/html/x-$name";
325 open (my $hack_output, '>', "$TARGET_DIR/deploy/$name")
326 or die "Could not open output";
330 while (<$hack_input>) {
331 print $hack_output $last if ($doprint);
333 s/^\/\/<!\[CDATA\[//;
336 # Remove the junk <span> wrapper generated by AgilityPack.
341 # Replace the CSS in the XHTML output with the original CSS.
342 print $hack_output $_;
343 print $hack_output $$stylesheet;
344 while (<$hack_input>) {
345 last if (/<\/style>/);
351 if (!($last =~ /span/)) {
352 print $hack_output $last;
354 # system ("cp.exe $TARGET_DIR/html/$name $TARGET_DIR/deploy/$name");
359 my ($docs, $apis_listed) = @_;
362 my ($ret, $xname, $args);
365 # Try to align things; compute type size, method size, and arguments.
366 foreach my $line (split /\n/, $apis_listed) {
367 if (exists ($docs->{prototype}->{$line})) {
368 my $p = $docs->{prototype}->{$line};
369 if (my ($ret, $xname, $args) = ($p =~ /(.*)\n(\w+)[ \t](.*)/)) {
370 my $tl = length ($ret);
371 my $pl = length ($xname);
372 $type_size = $tl if ($tl > $type_size);
373 $name_size = $pl if ($pl > $name_size);
381 foreach my $line (split /\n/, $apis_listed) {
383 if (exists($docs->{prototype}->{$line})) {
384 my $p = $docs->{prototype}->{$line};
385 if (my ($ret, $xname, $args) = ($p =~ /(.*)\n(\w+)[ \t](.*)/)) {
386 $xname = $line if $xname eq "";
387 my $rspace = " " x ($type_size - length ($ret));
388 my $nspace = " " x ($name_size - length ($xname));
389 $args = wrap ($args, length ($ret . $rspace . $xname . $nspace), 60);
390 $apis_toc .= "$ret$rspace<a href=\"\#api:$line\">$xname</a>$nspace$args\n";
398 my ($args, $size, $limit) = @_;
401 # return $args if ((length (args) + size) < $limit);
403 my $remain = $limit - $size;
404 my @sa = split /,/, $args;
406 foreach my $arg (@sa) {
409 $linelen += length ($sret);
411 if ($linelen + length ($arg) < $limit) {
412 $sret .= "FITS" . $arg . ", ";
414 my $newline = " " x ($size) . $arg . ", ";
415 my $linelen = length ($newline);
416 $sret .= "\n" . $newline;
425 # Print a section if non-empty.
428 my ($output, $caption, $opttext) = @_;
429 if (defined($opttext) && $opttext ne '' && $opttext !~ /^[ \t]+$/) {
431 " <div class=\"mapi-section\">$caption</div>\n",
432 " <div>$opttext</div>\n";
437 # Render parameter information as table.
439 sub render_parameters {
440 my ($parameters) = @_;
442 for my $parameter (@$parameters) {
443 $result .= "<tr><td><i>$parameter->{name}</i></td><td>$parameter->{description}</td></tr>";
452 exdoc - Compiles API docs from Mono sources and HTML templates.
456 exdoc [OPTIONS] [FILE...]
464 Print this help message.
466 =item B<--html> I<DIR>, B<-h> I<DIR>
468 Use I<DIR> as the input path for HTML sources.
470 =item B<--target> I<DIR>, B<-t> I<DIR>
472 Use I<DIR> as the target path for output.
474 =item B<--warnings>, B<-W>
476 Enable warnings about documentation errors.
482 Reads HTML templates and C sources, extracting documentation from the sources and splicing it into the templates.