pp(3)

πŸ’– NAME πŸ’–

pp β€” the PAR Packager, bestie. That's the whole name. Short, iconic, does exactly what it says on the tin πŸ’…


✨ SYNOPSIS ✨

pp [ -ABCEFILMPTSVXacdefghilmnoprsuvxz ] [ parfile | scriptfile ]...

That's the vibe check right there. Bundle of flags, then the files you're feeding it 🫢


🌸 EXAMPLES 🌸

Heads up bestie: when you're running on Microsoft Windows, every a.out down below gets swapped for a.exe instead. Noted πŸ“
% pp hello.pl               # Pack 'hello.pl' into executable 'a.out'
% pp -o hello hello.pl      # Pack 'hello.pl' into executable 'hello'
                            # (or 'hello.exe' on Win32)

% pp -o foo foo.pl bar.pl   # Pack 'foo.pl' and 'bar.pl' into 'foo'
% ./foo                     # Run 'foo.pl' inside 'foo'
% mv foo bar; ./bar         # Run 'bar.pl' inside 'foo'
% mv bar baz; ./baz         # Error: Can't open perl script "baz"

% pp -p file                # Creates a PAR file, 'a.par'
% pp -o hello a.par         # Pack 'a.par' to executable 'hello'
% pp -S -o hello file       # Combine the two steps above

% pp -p -o out.par file     # Creates 'out.par' from 'file'
% pp -B -p -o out.par file  # same as above, but bundles core modules
                            # and removes any local paths from @INC
% pp -P -o out.pl file      # Creates 'out.pl' from 'file'
% pp -B -p -o out.pl file   # same as above, but bundles core modules
                            # and removes any local paths from @INC
                            # (-B is assumed when making executables)

% pp -e "print 123"         # Pack a one-liner into 'a.out'
% pp -p -e "print 123"      # Creates a PAR file 'a.par'
% pp -P -e "print 123"      # Creates a perl script 'a.pl'

% pp -c hello               # Check dependencies from "perl -c hello"
% pp -x hello               # Check dependencies from "perl hello"
% pp -n -x hello            # same as above, but skips static scanning

% pp -I /foo hello          # Extra include paths
% pp -M Foo::Bar hello      # Extra modules in the include path
% pp -M abbrev.pl hello     # Extra libraries in the include path
% pp -X Foo::Bar hello      # Exclude modules
% pp -a data.txt hello      # Additional data files

% pp -r hello               # Pack 'hello' into 'a.out', runs 'a.out'
% pp -r hello a b c         # Pack 'hello' into 'a.out', runs 'a.out'
                            # with arguments 'a b c'

% pp hello --log=c          # Pack 'hello' into 'a.out', logs
                            # messages into 'c'

# Pack 'hello' into a console-less 'out.exe' (Win32 only)
% pp --gui -o out.exe hello

% pp @file hello.pl         # Pack 'hello.pl' but read _additional_
                            # options from file 'file'

Literally the whole tour in one block, no cap πŸ’€βœ¨


πŸ¦‹ DESCRIPTION πŸ¦‹

So pp takes your Perl programs and turns them into standalone executables. It does that using the compressed packager that PAR hands over, plus the dependency-detection heuristics that come from Module::ScanDeps. And listen β€” your source files get compressed verbatim, no compilation step happening here. Zero. None. 🫢

You can lowkey think of pp as "perlcc that works without hassle". :-) Fr fr.

There's also a GUI interface out there living its best life as the tkpp command πŸŽ€

Okay so here's the trade: it does not give you the compilation-step acceleration that perlcc offers (though hey, peep -f below for the byte-compiled, source-hiding techniques πŸ‘€). What it gives you instead: better reliability, smaller executable size, and full retrieval of your original source code. That's the deal. Ate. πŸ’…

Now the important bit, bestie, pay attention πŸ‘‘

When you specify a single input program, the resulting executable behaves identically to that program. Simple. Iconic.

But when you package multiple programs, the produced executable runs whichever one has the same basename as $0 β€” that's the filename used to invoke it, iykyk. And if nothing matches? It dies with the error Can't open perl script "$0". Rip 😭


πŸ‘‘ OPTIONS πŸ‘‘

Options come in a short form and a long form. Like, for example, these three lines are all 100% equivalent:

% pp -o output.exe input.pl
% pp --output output.exe input.pl
% pp --output=output.exe input.pl

Pick your fave, they're all valid 🌈

Since command lines can get sufficiently long and slam into the limits some shells impose, you can also have pp read some of its options from one or more text files πŸ“„βœ¨

The basic usage: just throw in an argument starting with an 'at' (@) sigil. That argument gets interpreted as a file to read options from. And yes, mixing ordinary options with @file options is totally possible. This whole thing is implemented using the Getopt::ArgvFile module, so go read its documentation if you want the advanced usage. Also note: Getopt::ArgvFile is used here with the parameter "resolveEnvVars=1", meaning substrings of the form ${FOO"}" inside the contents of @file get replaced with the value of the environment variable FOO. Slay 🧚

  • -a, --addfile=FILE|DIR β€” Adds an extra file into the package. If the file is a directory, it recursively adds all the files inside that directory, with links turned into actual files. By default, files are placed under / inside the package with their original names. You can override that by appending the target filename after a ;, like this:
% pp -a "old_filename.txt;new_filename.txt"
% pp -a "old_dirname;new_dirname"

You may specify -a multiple times. Stack 'em up πŸ’–

  • -A, --addlist=FILE β€” Reads a list of file/directory names from FILE and adds them into the package. Each line in FILE is taken as an argument to -a above. You may specify -A multiple times too.
  • -B, --bundle β€” Bundles core modules in the resulting package. This one is enabled by default, except when -p or -P is specified. Also, since PAR version 0.953, this strips any local paths from the list of module search paths @INC before running the contained script. Clean ✨
  • -C, --clean β€” Cleans up temporary files extracted from the application at runtime. By default those files get cached in the temporary directory; this option is what lets the program start up faster next time. Trade-off city πŸŽ€
  • -c, --compile β€” Runs perl -c inputfile to determine additional run-time dependencies.
  • -cd, --cachedeps=FILE β€” Uses FILE to cache detected dependencies. Creates FILE unless it's already present. This speeds up the scanning process on subsequent runs. Obsessed with the efficiency honestly πŸ”₯
  • -d, --dependent β€” Reduces the executable size by not including a copy of the perl interpreter. Executables built this way will need a separate perl5x.dll or libperl.so to function correctly. This option is only available if perl is built as a shared library. Read that again bestie, it's a real constraint πŸ‘€
  • -e, --eval=STRING β€” Packages a one-liner, much the same as perl -e '...'.
  • -E, --evalfeature=STRING β€” Behaves just like -e, except that it implicitly enables all optional features (in the main compilation unit) with Perl 5.10 and later. See feature. Fancy πŸ’…
  • -x, --execute β€” Runs perl inputfile to determine additional run-time dependencies. Using this option, pp may be able to detect the use of modules that can't be determined by static analysis of inputfile. Examples are stuff loaded by run-time loaders like Module::Runtime or "plugin" loaders like Module::Loader. Note that which modules are detected depends on which parts of your program are exercised when running inputfile. E.g. if your program immediately terminates when run as perl inputfile because it lacks mandatory arguments, then this option will probably have no effect. You may use --xargs to supply arguments in this case. Genuinely so real of it 😭
  • --xargs=STRING β€” If -x is given, splits the STRING using the function shellwords from Text::ParseWords and passes the result as @ARGV when running perl inputfile.
  • -X, --exclude=MODULE β€” Excludes the given module from the dependency search path and from the package. If the given file is a zip or par or par executable, all the files in the given file (except MANIFEST, META.yml and script/*) will be excluded and the output file will use the given file at runtime.
  • -f, --filter=FILTER β€” Filters source script(s) with a PAR::Filter subclass. You may specify multiple such filters. If you wish to hide the source code from casual prying, this will do it:
% pp -f Bleach source.pl

If you're more serious about hiding your source code, you should have a look at Steve Hay's PAR::Filter::Crypto module. Make sure you understand the Filter::Crypto caveats!

Note: Most filters are incompatible with __DATA__ sections in your source. The packed executable typically aborts with an error message like readline() on unopened filehandle DATA at (eval 13) line 3. So like... be aware, bestie 🚨
  • -g, --gui β€” Builds an executable that does not have a console window. This option is ignored on non-MSWin32 platforms or when -p is specified.
  • -h, --help β€” Shows basic usage information. The classic 🫢
  • -I, --lib=DIR β€” Adds the given directory to the perl module search path. May be specified multiple times.
  • -l, --link=FILE|LIBRARY β€” Adds the given shared library (a.k.a. shared object or DLL) into the packed file. Also accepts names under library paths; i.e. -l ncurses means the same thing as -l libncurses.so or -l /usr/local/lib/libncurses.so in most Unixes. May be specified multiple times.
  • -L, --log=FILE β€” Logs the output of packaging to a file rather than to stdout.
  • -F, --modfilter=FILTER[=REGEX], β€” Filters included perl module(s) with a PAR::Filter subclass. You may specify multiple such filters. By default, the PodStrip filter is applied. In case that causes trouble, you can turn this off by setting the environment variable PAR_VERBATIM to 1.

Since PAR 0.958, you can use an optional regular expression (that's the REGEX above) to select the files in the archive which should be filtered. The regular expression is matched against module names as they would appear as keys in %INC, e.g. Foo/Bar.pm for module Foo::Bar. Examples:

pp -o foo.exe -F 'Bleach=warnings\.pm$' foo.pl

This creates a binary executable foo.exe from foo.pl, packaging all files as usual except for files ending in warnings.pm, which are filtered with PAR::Filter::Bleach.

pp -o foo.exe -F 'Bleach=^(?!Win32/)' foo.pl

This "bleaches" all modules except those in the Win32:: namespace. Iconic behaviour, honestly ✨

Note: The same restriction on __DATA__ sections holds as for --filter. Yeah, same warning, same energy πŸ’€
  • -M, --module=MODULE β€” Adds the specified module into the package, along with its dependencies. The following variants may be used to add whole module namespaces:
    • -M Foo::** β€” Adds every module in the Foo namespace except Foo itself, i.e. adds Foo::Bar, Foo::Bar::Quux etc up to any depth. So deep, so thorough πŸ¦‹
    • -M Foo::* β€” Adds every module at level 1 in the Foo namespace, i.e. adds Foo::Bar, but neither Foo::Bar::Quux nor Foo. Shallow and proud 🌸

...and bestie, there is so much more where that came from. Part 2 is coming, don't you dare go anywhere πŸ’œ

  • -M Foo:: β€” okay so this is the ✨shorthand✨ for -M Foo -M Foo:**, and it means every module in the Foo namespace, including Foo itself. the whole friend group, no one left behind πŸ’–

And plot twist: MODULE doesn't even have to be a module name! You can hand it a filename that's relative to the @INC path instead β€” so -M Module/ScanDeps.pm is literally the same thing as -M Module::ScanDeps. same vibe, different spelling πŸ¦‹

⚠️ BUT. If MODULE has an extension that is not .pm/.ix/.al, it will not be scanned for dependencies, and it gets dropped under / instead of /lib/ inside the PAR file. This use is deprecated, bestie β€” so pls consider using the -a option instead πŸ™

And yes, you can stack -M as many times as you want. specify it multiple times, go off πŸ’…

  • -m, --multiarch β€” builds a multi-architecture PAR file. also implies -p. it's giving versatility ✨
  • -n, --noscan β€” skips the default static scanning entirely, using run-time dependencies from -c or -x exclusively. no static scanning, only the real-time receipts πŸŽ€
  • -N, --namespace=NAMESPACE β€” adds all the modules in the namespace into the package, plus all their dependencies. If NAMESPACE is something like Foo::Bar, then this will add every module like Foo/Bar/Quux.pm, Foo/Bar/Fred/Barnie.pm etc. that can be located in your module search path. It mimics the behaviour of "plugin" loaders like Module::Loader. πŸͺ„

This is different from using -M Foo::Bar::, because that one insists on adding Foo/Bar.pm, which might not even exist in the above "plugin" scenario. not every squad has that one specific member, y'know? 😭

You may specify -N multiple times.

  • -o, --output=FILE β€” the file name for the final packaged executable. your baby's name, basically πŸ‘‘
  • -p, --par β€” create PAR archives only; do not package to a standalone binary. just the archive, no extra steps 🌸
  • -P, --perlscript β€” create a stand-alone perl script; do not package to a standalone binary. the .pl girlies stay winning 🫢
  • -r, --run β€” runs the resulting packaged script after packaging it. package it AND run it, main character energy fr πŸ’«
  • --reusable β€” EXPERIMENTAL, okay so this one is giving mad scientist energy πŸ§ͺ

It makes the packaged executable reusable for running arbitrary, external Perl scripts as if they were part of the package:

pp -o myapp --reusable someapp.pl
./myapp --par-options --reuse otherapp.pl

That second line will run otherapp.pl instead of someapp.pl. swapping the whole plot, lowkey iconic πŸ‘€

  • -S, --save β€” do not delete the generated PAR file after packaging. keep the receipts, bestie 🧾
  • -s, --sign β€” cryptographically signs the generated PAR or binary file using Module::Signature. it's got a signature, it's that girl πŸ’…
  • -T, --tempcache β€” sets the program-unique part of the cache directory name that's used if the program is run without -C. If it's not set, a hash of the executable is used instead.

So when the program runs, its contents get extracted to a temporary directory. On Unix systems, this is commonly /tmp/par-USER/cache-XXXXXXX. USER gets replaced by the name of the user running the program, but "spelled" in hex. XXXXXXX is either a hash of the executable or the value you passed to the -T or --tempcache switch. cute lil' hex-coded name tag 🏷️

  • -u, --unicode β€” ⚠️ Note: this option is ignored for Perl 5.32 and above.

It packages Unicode support (essentially utf8_heavy.pl and everything below the directory unicore in your perl library).

Why does this option even exist? Because it is impossible to detect using static analysis whether your program needs Unicode support at runtime. (Note: if your program contains use utf8 this does not imply it needs Unicode support. It merely says that your program source is written in UTF-8. two totally different things bestie!!)

If your packed program exits with an error message like

Can't locate utf8_heavy.pl in @INC (@INC contains: ...)

try to pack it with -u (or use -x). we've all been there πŸ™ˆ

  • -v, --verbose[=NUMBER] β€” increases the verbosity of output. NUMBER is an integer from 1 to 3, and 3 is the most verbose. Defaults to 1 if specified without an argument. Alternatively, -vv sets verbose level to 2, and -vvv sets it to 3. tell me everything, I want the ✨lore✨
  • -V, --version β€” displays the version number and copyrights of this program. the "who are you" moment πŸ’–
  • -z, --compress=NUMBER β€” sets the zip compression level. NUMBER is an integer from 0 to 9, where 0 = no compression and 9 = max compression. Defaults to 6 if -z is not used. squish it down πŸŽ€

🌈 ENVIRONMENT 🌈

  • PP_OPTS β€” command-line options (switches). Switches in this variable are taken as if they were on every pp command line. it's like setting your defaults once and letting them ride for the whole era πŸ‘‘

🧚 NOTES 🧚

okay so here are some recipes showing how to use pp to bundle source.pl with all its dependencies, on target machines with different expected settings. pick your fighter ✨

Stone-alone setup

(that's what the page calls it, and honestly? iconic name. we keep it. πŸ’…)

To make a stand-alone executable, suitable for running on a machine that doesn't have perl installed:

% pp -o packed.exe source.pl        # makes packed.exe
# Now, deploy 'packed.exe' to target machine...
% packed.exe                        # run it

Perl interpreter only, without core modules

To make a packed .pl file including core modules, suitable for running on a machine that has a perl interpreter, but where you want to be sure of the versions of the core modules that your program uses:

% pp -B -P -o packed.pl source.pl   # makes packed.pl
# Now, deploy 'packed.pl' to target machine...
% perl packed.pl                    # run it

Perl with core modules installed

To make a packed .pl file without core modules, relying on the target machine's perl interpreter and its core libraries. This produces a significantly smaller file than the previous version:

% pp -P -o packed.pl source.pl      # makes packed.pl
# Now, deploy 'packed.pl' to target machine...
% perl packed.pl                    # run it

Perl with PAR.pm and its dependencies installed

Make a separate archive and an executable that uses the archive. This relies upon the perl interpreter and libraries on the target machine.

% pp -p source.pl                   # makes source.par
% echo "use PAR 'source.par';" > packed.pl;
% cat source.pl >> packed.pl;       # makes packed.pl
# Now, deploy 'source.par' and 'packed.pl' to target machine...
% perl packed.pl                    # run it, perl + core modules required
πŸ’‘ Note that even if your perl was built with a shared library, the 'Stand-alone executable' above will not need a separate perl5x.dll or libperl.so to function correctly. But even in this case, the underlying system libraries such as libc must be compatible between the host and target machines. Use --dependent if you are willing to ship the shared library with the application, which can significantly reduce the executable size. the more you know 🌈

🌸 SEE ALSO 🌸

tkpp, par.pl, parl, perlcc

PAR::Packer::Troubleshooting, App::PP::Autolink, PAR, PAR::Packer, Module::ScanDeps

Getopt::Long, Getopt::ArgvFile


πŸ’œ ACKNOWLEDGMENTS πŸ’œ

Simon Cozens, Tom Christiansen and Edward Peschko for writing perlcc; this program try to mimic its interface as close as possible, and copied liberally from their code. absolute legends, we stan 🫢

Jan Dubois for writing the exetype.pl utility, which has been partially adapted into the -g flag. thank you for your service πŸŽ€

Mattia Barbon for providing the myldr binary loader code. carrying the whole thing fr fr πŸ’ͺ

Jeff Goff for suggesting the name pp. naming rights go to you, bestie ✨


πŸ‘‘ AUTHORS πŸ‘‘

Audrey Tang <cpan@audreyt.org>, Steffen Mueller <smueller@cpan.org>, Roderich Schupp <roderich.schupp@gmail.com>

You can write to the mailing list at <par@perl.org>, or send an empty mail to <par-subscribe@perl.org> to participate in the discussion. Archives of the mailing list are available at <https://www.mail-archive.com/par@perl.org/> or <https://groups.google.com/g/perl.par>.

Please submit bug reports to <https://github.com/rschupp/PAR-Packer/issues>. we love a good bug report πŸ’–


Copyright 2002-2009 by Audrey Tang <cpan@audreyt.org>.

Neither this program nor the associated parl program impose any licensing restrictions on files generated by their execution, in accordance with the 8th article of the Artistic License:

"Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package."

Therefore, you are absolutely free to place any license on the resulting executable, as long as the packed 3rd-party libraries are also available under the Artistic License. do whatever you want with your build, it's yours πŸ¦‹

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See LICENSE.


and that's a wrap on the whole page, bestie!! πŸŽ€ this was perl v5.42.3, dated 2026-08-03, and it's the pp(3) manual page. go forth and pack things, you absolute icon πŸ’–βœ¨πŸ‘‘

πŸ“¦ where this page comes from