Author image Raphael Manfredi


Pod::PP - POD pre-processor


 # normally used via the podpp script

 require Pod::PP;

 my $pp = Pod::PP->make(
     -incpath   => ['h', 'h/sys'],
     -symbols   => { DIR => "/var/www", TMPDIR => "/var/tmp" },



The Pod::PP module is a POD pre-processor built on top of Pod::Parser. The helper script podpp provides a pre-processor command for POD, whose interface is very much like cpp, the C pre-processor. However, unlike C, the Pod::PP processing is not normally invoked when parsing POD.

If you wish to automate the pre-processing for every POD file, you need to write .pp files (say) instead of .pod files, and add the following make rules to your Makefile:

    PODPP = podpp
    PP_FLAGS =

    .SUFFIXES: .pp .pod

        $(PODPP) $(PP_FLAGS) $< >$*.pod

Those teach make how to derive a .pod from a .pp file using the podpp pre-processor.

Pod::PP uses the P<> notation to request symbol expansion. Since it processes text, you need to tag the symbols to be expanded explicitely. Expansion is done recursively, until there is no more expansion possible.

If you are familiar with cpp, most directives will be easy to grasp. For instance, using the == prefix to make shorter commands:

    ==pp include "common.pp"

    ==pp define DIR     /var/www
    ==pp define TMP     /tmp

    ==pp ifdef SOME_COMMON_SYMBOL
    ==pp define FOO     common foo
    ==pp else
    ==pp define FOO     P<DIR>
    ==pp endif

The == notation is not standard POD, but it is understood by Pod::Parser and very convenient when it comes to writing things like the above block, because there's no need to separate commands by blank lines. Since the code is going to be processed by podpp anyway, there's no problem, and podpp will always emit legitimate POD. That is, given the following:

    ==head1 NAME
    Some data

it will re-emit:

    =head1 NAME

    Some data

thereby normalizing the output. It is guaranteed that after a podpp pass, the output is regular POD. If you make errors in writing the Pod::PP directives, you will not get the expected output, but it will be regular POD.


The pre-processing directives can be given in two forms, depending on whether you wish to process your POD files containing Pod::PP directives with the usual POD tools before or after having run podpp on them:

  • By using the =for pp form before all commands, you ensure that regular POD tools will simply ignore those. This might result in incorrect processing though, if you depend on the definition of some symbols to produce different outputs (i.e. you would need a podpp pass anyway).

  • By using the =pp form before all commands, you require that podpp be run on your file to produce regular POD that can be then processed via regular POD tools.

Here are the supported directives, in alphabetical order:

=pp comment comment

A comment. Will be stripped out upon reading.

When Pod::PP encounters an error whilst processing a directive, e.g. an include with a file not found, it will leave a comment in the output, albeit using the =for pp form so that it is properly ignored by standard POD tools.

=pp define symbol [value]

Defines symbol to be value. If there's no value, the symbol is simply defined to an empty value. There may be an arbitrary amount of spaces or tabs between symbol and value.

A symbol can be tested for defined-ness via =pp ifdef, used in expressions via =pp if, or expanded via P<sym>.

=pp elif expr

Alternate condition. There may be as many =pp elif as needed, but they must precede any =pp else directive, and follow a leading =pp if test. See =pp if below for the expr definiton.

Naturally, within an =pp if test, the expression expr is evaluated only if the if condition was false.

=pp else

The else clause of the =pp if or =pp ifdef test.

=pp endif

Closes the testing sequence opened by last =pp if or =pp ifdef.

=pp if expr

Starts a conditional text sequence. If expr evaluates to true, the remaining up to the matching =pp elif or =pp else or =pp endif is included in the output, otherwise it is stripped.

Within an expression, you may include any symbol, verbatim, and form any legal Perl expression. For instance:

    ==pp define X 4
    ==pp define LIMIT 100

    =pp if X*X < LIMIT

    Include this portion if X*X < LIMIT

    =pp else

    Include this portion if X*X >= LIMIT

    =pp endif

would yield, when processed by podpp:

    Include this portion if X*X < LIMIT

since the condition is true with the current symbol values.

You may also use the defined() operator in tests, as in cpp:

    =pp if defined(X) || !defined(LIMIT)

A bad expression will result in an error message, but you must know that your expressions are converted into Perl, and then are evaluated within a Safe compartment: the errors will be reported relative to the translated Perl expressions, not to your original expressions.

=pp ifdef symbol

Tests whether a symbol is defined. This is equivalent to:

    =pp if defined(symbol)

only it is shorter to say.

=pp ifndef symbol

Tests whether a symbol is not defined. This is equivalent to:

    =pp if !defined(symbol)

but it is shorter to say.

=pp image [<center>] "path"

This directive is not a regular pre-processing directive in that it is highly specialized. It's there because I historically implemented Pod::PP to pre-process that command in my PODs.

It's a high-level macro, that is hardwired because Pod::PP is not rich enough yet to be able to support the definition of that kind of macro.

It is expanded into two POD directives: one for HTML, one for text. An example will be better than a lengthy description:

    =pp image <center> "logo.png"

will expand into:

    =for html <P ALIGN="center"><IMG SRC="logo.png" ALT="logo"></P>

    =begin text

     [image "logo.png" not rendered]

    =end text

The <center> tag is optional, and you may use <right> instead to right-justify your image in HTML.

=pp include "file"

Includes "file" at the present location, through Pod::PP. That is, the included file may itself use Pod::PP directives.

The algorithm to find the file is as follows:

1. The file is first looked for from the location of the current file being processed.

2. If not found there, the search path is traversed. You may supply a search path with the -I flag in podpp or via the -incpath of the creation routine for Pod::PP.

3. If still not found, an error is reported.

=pp require "file"

Same as an =pp include directive, but the "file" is included only once. The absolute path of the file is used to determine whether it has already been included. For example, assuming we're in file dir/foo, and that dir/foo/file.pp exists, the following:

    ==pp require "file.pp"
    ==pp require "../dir/file.pp"

will result in only one inclusion of "file.pp", since both require statements end up requesting the inclusion of the same path.

=pp undef symbol

Undefines the target symbol.


Pod::PP uses Log::Agent to emit its diagnostics. The podpp script leaves Log::Agent in its default configuration, thereby redirecting all the errors to STDERR. If you use the Pod::PP interface directly in a script, you can look at configuring alternatives for the logs in Log::Agent.

Whenever possible, Pod::PP leaves a trail in the output marking the error. For instance, feeding the following to podpp:

    =pp include "no-such-file"

would print the following error to STDERR:

    podpp: error in Pod::PP directive 'include' at "example", line 17:
        cannot find "no-such-file"

and leave the following trail:

    =for pp comment (at "example", line 17):
        Following "=pp" directive failed: cannot find "no-such-file"
        =pp include "no-such-file"

which will be ignored by all POD tools.


You will normally don't care, since you will be mostly interfacing with Pod::PP via the podpp script. This section is therefore only useful for people wishing to use Pod::PP from within a program.

Since Pod::PP inherits from Pod::Parser, it conforms to its interface, in particular for the parse_from_filehandle() and parse_from_file() routines. See Pod::Parser for more information.

The creation routine make() takes the following mandatory arguments:

-incpath => array_ref

The additional include search path ("." is always part of the search path, and always the first thing looked at). The array_ref provides a list of directories to look. For instance:

    -incpath    => ["h", "/home/ram/usr/podpp"]

would add the two directories, in the order given.

-symbols => hash_ref

Provides the intial set of defined symbols. Each key from the hash_ref is a symbol for the pre-processor:

    -symbols    => {
        DIR     => "/var/tmp"
        TMP     => "/tmp"

Given the above, the following input:

    dir is "P<DIR>" and tmp is "P<TMP>"

would become after processing:

    dir is "/var/tmp" and tmp is "/tmp"

as expected.


The =pp image directive is a hack. It should not be implemented at this level, but it was convenient to do so.


Raphael Manfredi <>

This software is currently unmaintained. Please look at:

if you wish to take over maintenance. I would appreciate being notified, so that I can transfer the PAUSE (CPAN) ownership to you.