| Current File : //usr/local/share/man/man3/PAR::Tutorial.3 |
.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.30)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{
. if \nF \{
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\"
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
.\" Fear. Run. Save yourself. No user-serviceable parts.
. \" fudge factors for nroff and troff
.if n \{\
. ds #H 0
. ds #V .8m
. ds #F .3m
. ds #[ \f1
. ds #] \fP
.\}
.if t \{\
. ds #H ((1u-(\\\\n(.fu%2u))*.13m)
. ds #V .6m
. ds #F 0
. ds #[ \&
. ds #] \&
.\}
. \" simple accents for nroff and troff
.if n \{\
. ds ' \&
. ds ` \&
. ds ^ \&
. ds , \&
. ds ~ ~
. ds /
.\}
.if t \{\
. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u"
. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u'
. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u'
. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u'
. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u'
. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u'
.\}
. \" troff and (daisy-wheel) nroff accents
.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V'
.ds 8 \h'\*(#H'\(*b\h'-\*(#H'
.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#]
.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H'
.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u'
.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#]
.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#]
.ds ae a\h'-(\w'a'u*4/10)'e
.ds Ae A\h'-(\w'A'u*4/10)'E
. \" corrections for vroff
.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u'
.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u'
. \" for low resolution devices (crt and lpr)
.if \n(.H>23 .if \n(.V>19 \
\{\
. ds : e
. ds 8 ss
. ds o a
. ds d- d\h'-1'\(ga
. ds D- D\h'-1'\(hy
. ds th \o'bp'
. ds Th \o'LP'
. ds ae ae
. ds Ae AE
.\}
.rm #[ #] #H #V #F C
.\" ========================================================================
.\"
.IX Title "PAR::Tutorial 3"
.TH PAR::Tutorial 3 "2011-12-28" "perl v5.20.0" "User Contributed Perl Documentation"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "NAME"
PAR::Tutorial \- Cross\-Platform Packaging and Deployment with PAR
.SH "SYNOPSIS"
.IX Header "SYNOPSIS"
This is a tutorial on \s-1PAR,\s0 first appeared at the 7th Perl Conference.
The \s-1HTML\s0 version of this tutorial is available online as
<http://search.cpan.org/perldoc?PAR::Tutorial>
.SH "DESCRIPTION"
.IX Header "DESCRIPTION"
.SS "On Deploying Perl Applications"
.IX Subsection "On Deploying Perl Applications"
.Vb 3
\& % sshnuke.pl 10.2.2.2 \-rootpw="Z1ON0101"
\& Perl v5.6.1 required\-\-this is only v5.6.0, stopped at sshnuke.pl line 1.
\& BEGIN failed\-\-compilation aborted at sshnuke.pl line 1.
.Ve
.IP "\(bu" 4
Q: \*(L"Help! I can't run your program!\*(R"
.IP "\(bu" 4
A1: Install Perl & \f(CW\*(C`perl \-MCPAN \-e\*(Aqinstall(...)\*(Aq\*(C'\fR
.RS 4
.IP "\(bu" 4
How do we know which modules are needed?
.IP "\(bu" 4
New versions of \s-1CPAN\s0 modules may break \f(CW\*(C`sshnuke.pl\*(C'\fR
.RE
.RS 4
.RE
.IP "\(bu" 4
A2: Install Perl & \f(CW\*(C`tar zxf my_perllib.tgz\*(C'\fR
.RS 4
.IP "\(bu" 4
Possibly overwriting existing modules; not cross-platform at all
.RE
.RS 4
.RE
.IP "\(bu" 4
A3: Use the executable generated by \f(CW\*(C`perlcc sshnuke.pl\*(C'\fR
.RS 4
.IP "\(bu" 4
Impossible to debug; \f(CW\*(C`perlcc\*(C'\fR usually does not work anyway
.RE
.RS 4
.RE
.SS "\s-1PAR,\s0 the Perl Archive Toolkit"
.IX Subsection "PAR, the Perl Archive Toolkit"
.IP "\(bu" 4
Do what \s-1JAR \s0(Java Archive) does for Perl
.RS 4
.IP "\(bu" 4
Aggregates modules, scripts and other files into a Zip file
.IP "\(bu" 4
Easy to generate, update and extract
.IP "\(bu" 4
Version consistency: solves forward-compatibility problems
.IP "\(bu" 4
Developed by community: \f(CW\*(C`par@perl.org\*(C'\fR
.RE
.RS 4
.RE
.IP "\(bu" 4
\&\s-1PAR\s0 files can be packed into self-contained scripts
.RS 4
.IP "\(bu" 4
Automatically scans perl script for dependencies
.IP "\(bu" 4
Bundles all necessary 3rd\-party modules with it
.IP "\(bu" 4
Requires only core Perl to run on the target machine
.IP "\(bu" 4
\&\s-1PAR\s0 also comes with \f(CW\*(C`pp\*(C'\fR, the Perl Packager:
.Sp
.Vb 1
\& % pp \-o sshnuke.exe sshnuke.pl # stand\-alone executable!
.Ve
.RE
.RS 4
.RE
.SS "Simple Packaging"
.IX Subsection "Simple Packaging"
.IP "\(bu" 4
\&\s-1PAR\s0 files are just Zip files with modules in it
.IP "\(bu" 4
Any Zip tools can generate them:
.Sp
.Vb 2
\& % zip foo.par Hello.pm World.pm # pack two modules
\& % zip \-r bar.par lib/ # grab all modules in lib/
.Ve
.IP "\(bu" 4
To load modules from \s-1PAR\s0 files:
.Sp
.Vb 3
\& use PAR;
\& use lib "foo.par"; # the .par part is optional
\& use Hello;
.Ve
.IP "\(bu" 4
This also works:
.Sp
.Vb 2
\& use PAR "/home/mylibs/*.par"; # put all of them into @INC
\& use Hello;
.Ve
.SS "\s-1PAR\s0 Loaders"
.IX Subsection "PAR Loaders"
.IP "\(bu" 4
Use \f(CW\*(C`par.pl\*(C'\fR to run files inside a \s-1PAR\s0 archive:
.Sp
.Vb 2
\& % par.pl foo.par # looks for \*(Aqmain.pl\*(Aq by default
\& % par.pl foo.par test.pl # runs script/test.pl in foo.par
.Ve
.IP "\(bu" 4
Same thing, with the stand-alone \f(CW\*(C`parl\*(C'\fR or \f(CW\*(C`parl.exe\*(C'\fR:
.Sp
.Vb 2
\& % parl foo.par # no perl or PAR.pm needed!
\& % parl foo.par test.pl # ditto
.Ve
.IP "\(bu" 4
The \s-1PAR\s0 loader can prepend itself to a \s-1PAR\s0 file:
.RS 4
.IP "\(bu" 4
\&\f(CW\*(C`\-b\*(C'\fR bundles non-core modules needed by \f(CW\*(C`PAR.pm\*(C'\fR:
.Sp
.Vb 1
\& % par.pl \-b \-O./foo.pl foo.par # self\-contained script
.Ve
.IP "\(bu" 4
\&\f(CW\*(C`\-B\*(C'\fR bundles core modules in addition to \f(CW\*(C`\-b\*(C'\fR:
.Sp
.Vb 1
\& % parl \-B \-O./foo.exe foo.par # self\-contained binary
.Ve
.RE
.RS 4
.RE
.SS "Dependency Scanning"
.IX Subsection "Dependency Scanning"
.IP "\(bu" 4
Recursively scan dependencies with \f(CW\*(C`scandeps.pl\*(C'\fR:
.Sp
.Vb 7
\& % scandeps.pl sshnuke.pl
\& # Legend: [C]ore [X]ternal [S]ubmodule [?]NotOnCPAN
\& \*(AqCrypt::SSLeay\*(Aq => \*(Aq0\*(Aq, # X #
\& \*(AqNet::HTTP\*(Aq => \*(Aq0\*(Aq, # #
\& \*(AqCrypt::SSLeay::X509\*(Aq => \*(Aq0\*(Aq, # S # Crypt::SSLeay
\& \*(AqNet::HTTP::Methods\*(Aq => \*(Aq0\*(Aq, # S # Net::HTTP
\& \*(AqCompress::Zlib\*(Aq => \*(Aq0\*(Aq, # X # Net::HTTP::Methods
.Ve
.IP "\(bu" 4
Scan an one-liner, list all involved files:
.Sp
.Vb 7
\& % scandeps.pl \-V \-e "use Dynaloader;"
\& ...
\& # auto/DynaLoader/dl_findfile.al [autoload]
\& # auto/DynaLoader/extralibs.ld [autoload]
\& # auto/File/Glob/Glob.bs [data]
\& # auto/File/Glob/Glob.so [shared]
\& ...
.Ve
.ie n .SS "Perl Packager: ""pp"""
.el .SS "Perl Packager: \f(CWpp\fP"
.IX Subsection "Perl Packager: pp"
.IP "\(bu" 4
Combines scanning, zipping and loader-embedding:
.Sp
.Vb 2
\& % pp \-o out.exe src.pl # self\-contained .exe
\& % out.exe # runs anywhere on the same OS
.Ve
.IP "\(bu" 4
Bundle additional modules:
.Sp
.Vb 1
\& % pp \-o out.exe \-M CGI src.pl # pack CGI + its dependencies, too
.Ve
.IP "\(bu" 4
Pack one-liners:
.Sp
.Vb 1
\& % pp \-o out.exe \-e \*(Aqprint "Hi!"\*(Aq # turns one\-liner into executable
.Ve
.IP "\(bu" 4
Generate \s-1PAR\s0 files instead of executables:
.Sp
.Vb 2
\& % pp \-p src.pl # makes \*(Aqsource.par\*(Aq
\& % pp \-B \-p src.pl # include core modules
.Ve
.SS "How it works"
.IX Subsection "How it works"
.IP "\(bu" 4
Command-line options are almost identical to \f(CW\*(C`perlcc\*(C'\fR's
.RS 4
.IP "\(bu" 4
Also supports \f(CW\*(C`gcc\*(C'\fR\-style long options:
.Sp
.Vb 1
\& % pp \-\-gui \-\-verbose \-\-output=out.exe src.pl
.Ve
.RE
.RS 4
.RE
.IP "\(bu" 4
Small initial overhead; no runtime overhead
.IP "\(bu" 4
Dependencies are POD-stripped before packing
.IP "\(bu" 4
Loads modules directly into memory on demand
.IP "\(bu" 4
Shared libraries (DLLs) are extracted with File::Temp
.IP "\(bu" 4
Works on Perl 5.6.0 or above
.IP "\(bu" 4
Tested on Win32 (\s-1VC++\s0 and MinGW), FreeBSD, NetBSD, Linux, MacOSX, Cygwin, \s-1AIX,\s0 Solaris, HP-UX, Tru64...
.SS "Aggregating multiple programs"
.IX Subsection "Aggregating multiple programs"
.IP "\(bu" 4
A common question:
.Sp
.Vb 3
\& > I have used pp to make several standalone applications which work
\& > great, the only problem is that for each executable that I make, I am
\& > assuming the parl.exe is somehow bundled into the resulting exe.
.Ve
.IP "\(bu" 4
The obvious workaround:
.Sp
.Vb 2
\& You can ship parl.exe by itself, along with .par files built
\& by "pp \-p", and run those PAR files by associating them to parl.exe.
.Ve
.IP "\(bu" 4
On platforms that have \f(CW\*(C`ln\*(C'\fR, there is a better solution:
.Sp
.Vb 4
\& % pp \-\-output=a.out a.pl b.pl # two scripts in one!
\& % ln a.out b.out # symlink also works
\& % ./a.out # runs a.pl
\& % ./b.out # runs b.pl
.Ve
.SS "Cross-platform Packages"
.IX Subsection "Cross-platform Packages"
.IP "\(bu" 4
Of course, there is no cross-platform binary format
.IP "\(bu" 4
Pure-perl \s-1PAR\s0 packages are cross-platform by default
.RS 4
.IP "\(bu" 4
However, \s-1XS\s0 modules are specific to Perl version and platform
.IP "\(bu" 4
Multiple versions of a \s-1XS\s0 module can co-exist in a \s-1PAR\s0 file
.RE
.RS 4
.RE
.IP "\(bu" 4
Suppose we need \f(CW\*(C`out.par\*(C'\fR on both Win32 and Finix:
.Sp
.Vb 3
\& C:\e> pp \-\-multiarch \-\-output=out.par src.pl
\& ...copy src.pl and out.par to a Finix machine...
\& % pp \-\-multiarch \-\-output=out.par src.pl
.Ve
.IP "\(bu" 4
Now it works on both platforms:
.Sp
.Vb 2
\& % parl out.par # runs src.pl
\& % perl \-MPAR=out.par \-e \*(Aq...\*(Aq # uses modules inside out.par
.Ve
.SS "The Anatomy of a \s-1PAR\s0 file"
.IX Subsection "The Anatomy of a PAR file"
.IP "\(bu" 4
Modules can reside in several directories:
.Sp
.Vb 6
\& / # casual packaging only
\& /lib/ # standard location
\& /arch/ # for creating from blib/
\& /i386\-freebsd/ # i.e. $Config{archname}
\& /5.8.0/ # i.e. Perl version number
\& /5.8.0/i386\-freebsd/ # combination of the two above
.Ve
.IP "\(bu" 4
Scripts are stored in one of the two locations:
.Sp
.Vb 2
\& / # casual packaging only
\& /script/ # standard location
.Ve
.IP "\(bu" 4
Shared libraries may be architecture\- or perl-version-specific:
.Sp
.Vb 1
\& /shlib/(5.8.0/)?(i386\-freebsd/)?
.Ve
.IP "\(bu" 4
\&\s-1PAR\s0 files may recursively contain other \s-1PAR\s0 files:
.Sp
.Vb 1
\& /par/(5.8.0/)?(i386\-freebsd/)?
.Ve
.SS "Special files"
.IX Subsection "Special files"
.IP "\(bu" 4
\&\s-1MANIFEST\s0
.RS 4
.IP "\(bu" 4
Index of all files inside \s-1PAR\s0
.IP "\(bu" 4
Can be parsed with \f(CW\*(C`ExtUtils::Manifest\*(C'\fR
.RE
.RS 4
.RE
.IP "\(bu" 4
\&\s-1META\s0.yml
.RS 4
.IP "\(bu" 4
Dependency, license, runtime options
.IP "\(bu" 4
Can be parsed with \f(CW\*(C`YAML\*(C'\fR
.RE
.RS 4
.RE
.IP "\(bu" 4
\&\s-1SIGNATURE\s0
.RS 4
.IP "\(bu" 4
OpenPGP-signed digital signature
.IP "\(bu" 4
Can be parsed and verified with \f(CW\*(C`Module::Signature\*(C'\fR
.RE
.RS 4
.RE
.SS "Advantages over perlcc, PerlApp and Perl2exe"
.IX Subsection "Advantages over perlcc, PerlApp and Perl2exe"
.IP "\(bu" 4
This is not meant to be a flame
.RS 4
.IP "\(bu" 4
All three maintainers have contributed to \s-1PAR\s0 directly; I'm grateful
.RE
.RS 4
.RE
.IP "\(bu" 4
perlcc
.RS 4
.IP "\(bu" 4
\&\*(L"The code generated in this way is not guaranteed to work... Use for production purposes is strongly discouraged.\*(R" (from perldoc perlcc)
.IP "\(bu" 4
\&\fIGuaranteed to not work\fR is more like it
.RE
.RS 4
.RE
.IP "\(bu" 4
PerlApp / Perl2exe
.RS 4
.IP "\(bu" 4
Expensive: Need to pay for each upgrade
.IP "\(bu" 4
Non-portable: Only available for limited platforms
.IP "\(bu" 4
Proprietary: Cannot extend its features or fix bugs
.IP "\(bu" 4
Obfuscated: Vendor and black-hats can see your code, but you can't
.IP "\(bu" 4
Inflexible: Does not work with existing Perl installations
.RE
.RS 4
.RE
.SS "\s-1MANIFEST:\s0 Best viewed with Mozilla"
.IX Subsection "MANIFEST: Best viewed with Mozilla"
.IP "\(bu" 4
The \s-1URL\s0 of \f(CW\*(C`MANIFEST\*(C'\fR inside \f(CW\*(C`/home/autrijus/foo.par\*(C'\fR:
.Sp
.Vb 1
\& jar:file:///home/autrijus/foo.par!/MANIFEST
.Ve
.IP "\(bu" 4
Open it in a Gecko browser (e.g. Netscape 6+) with Javascript enabled:
.IP "\(bu" 4
No needed to unzip anything; just click on files to view them
.SS "\s-1META\s0.yml: Metadata galore"
.IX Subsection "META.yml: Metadata galore"
.IP "\(bu" 4
Static, machine-readable distribution metadata
.RS 4
.IP "\(bu" 4
Supported by \f(CW\*(C`Module::Build\*(C'\fR, \f(CW\*(C`ExtUtils::MakeMaker\*(C'\fR, \f(CW\*(C`Module::Install\*(C'\fR
.RE
.RS 4
.RE
.IP "\(bu" 4
A typical \f(CW\*(C`pp\*(C'\fR\-generated \f(CW\*(C`META.yml\*(C'\fR looks like this:
.Sp
.Vb 12
\& build_requires: {}
\& conflicts: {}
\& dist_name: out.par
\& distribution_type: par
\& dynamic_config: 0
\& generated_by: \*(AqPerl Packager version 0.03\*(Aq
\& license: unknown
\& par:
\& clean: 0
\& signature: \*(Aq\*(Aq
\& verbatim: 0
\& version: 0.68
.Ve
.IP "\(bu" 4
The \f(CW\*(C`par:\*(C'\fR settings controls its runtime behavior
.SS "\s-1SIGNATURE:\s0 Signing and verifying packages"
.IX Subsection "SIGNATURE: Signing and verifying packages"
.IP "\(bu" 4
OpenPGP clear-signed manifest with \s-1SHA1\s0 digests
.RS 4
.IP "\(bu" 4
Supported by \f(CW\*(C`Module::Signature\*(C'\fR, \f(CW\*(C`CPANPLUS\*(C'\fR and \f(CW\*(C`Module::Build\*(C'\fR
.RE
.RS 4
.RE
.IP "\(bu" 4
A typical \f(CW\*(C`SIGNATURE\*(C'\fR looks like this:
.Sp
.Vb 2
\& \-\-\-\-\-BEGIN PGP SIGNED MESSAGE\-\-\-\-\-
\& Hash: SHA1
\&
\& SHA1 8a014cd6d0f6775552a01d1e6354a69eb6826046 AUTHORS
\& ...
\& \-\-\-\-\-BEGIN PGP SIGNATURE\-\-\-\-\-
\& ...
\& \-\-\-\-\-END PGP SIGNATURE\-\-\-\-\-
.Ve
.IP "\(bu" 4
Use \f(CW\*(C`pp\*(C'\fR and \f(CW\*(C`cpansign\*(C'\fR to work with signatures:
.Sp
.Vb 3
\& % pp \-s \-o foo.par bar.pl # make and sign foo.par from bar.pl
\& % cpansign \-s foo.par # sign this PAR file
\& % cpansign \-v foo.par # verify this PAR file
.Ve
.SS "Perl Servlets with Apache::PAR"
.IX Subsection "Perl Servlets with Apache::PAR"
.IP "\(bu" 4
Framework for self-contained Web applications
.RS 4
.IP "\(bu" 4
Similar to Java's \*(L"Web Application Archive\*(R" (\s-1WAR\s0) files
.IP "\(bu" 4
Works with mod_perl 1.x or 2.x
.RE
.RS 4
.RE
.IP "\(bu" 4
A complete web application inside a \f(CW\*(C`.par\*(C'\fR file
.RS 4
.IP "\(bu" 4
Apache configuration, static files, Perl modules...
.IP "\(bu" 4
Supports Static, Registry and PerlRun handlers
.IP "\(bu" 4
Can also load all PARs under a directory
.RE
.RS 4
.RE
.IP "\(bu" 4
One additional special file: \f(CW\*(C`web.conf\*(C'\fR
.Sp
.Vb 6
\& Alias /myapp/cgi\-perl/ ##PARFILE##/
\& <Location /myapp/cgi\-perl>
\& Options +ExecCGI
\& SetHandler perl\-script
\& PerlHandler Apache::PAR::Registry
\& </Location>
.Ve
.SS "Hon Dah, A\-par-che!"
.IX Subsection "Hon Dah, A-par-che!"
.IP "\(bu" 4
First, make a \f(CW\*(C`hondah.par\*(C'\fR from an one-liner:
.Sp
.Vb 4
\& # use the "web.conf" from the previous slide
\& % pp \-p \-o hondah.par \-e \*(Aqprint "Hon Dah!\en"\*(Aq \e
\& \-\-add web.conf
\& % chmod a+x hondah.par
.Ve
.IP "\(bu" 4
Add this to \f(CW\*(C`httpd.conf\*(C'\fR, then restart apache:
.Sp
.Vb 5
\& <IfDefine MODPERL2>
\& PerlModule Apache2
\& </IfDefine>
\& PerlAddVar PARInclude /home/autrijus/hondah.par
\& PerlModule Apache::PAR
.Ve
.IP "\(bu" 4
Test it out:
.Sp
.Vb 2
\& % GET http://localhost/myapp/cgi\-perl/main.pl
\& Hon Dah!
.Ve
.IP "\(bu" 4
Instant one-liner web application that works!
.SS "On-demand library fetching"
.IX Subsection "On-demand library fetching"
.IP "\(bu" 4
With \s-1LWP\s0 installed, your can use remote \s-1PAR\s0 files:
.Sp
.Vb 3
\& use PAR;
\& use lib \*(Aqhttp://aut.dyndns.org/par/DBI\-latest.par\*(Aq;
\& use DBI; # always up to date!
.Ve
.IP "\(bu" 4
Modules are now cached under \f(CW$ENV{PAR_GLOBAL_TEMP}\fR
.IP "\(bu" 4
Auto-updates with \f(CW\*(C`LWP::Simple::mirror\*(C'\fR
.RS 4
.IP "\(bu" 4
Download only if modified
.IP "\(bu" 4
Safe for offline use after the first time
.IP "\(bu" 4
May use \f(CW\*(C`SIGNATURE\*(C'\fR to prevent DNS-spoofing
.RE
.RS 4
.RE
.IP "\(bu" 4
Makes large-scale deployment a breeze
.RS 4
.IP "\(bu" 4
Upgrades from a central location
.IP "\(bu" 4
No installers needed
.RE
.RS 4
.RE
.SS "Code Obfuscation"
.IX Subsection "Code Obfuscation"
.IP "\(bu" 4
Also known as \fIsource-hiding\fR techniques
.RS 4
.IP "\(bu" 4
It is \fInot\fR encryption
.IP "\(bu" 4
Offered by PerlApp, Perl2Exe, Stunnix...
.RE
.RS 4
.RE
.IP "\(bu" 4
Usually easy to defeat
.RS 4
.IP "\(bu" 4
Take optree dump from memory, feed to \f(CW\*(C`B::Deparse\*(C'\fR
.IP "\(bu" 4
If you just want to stop a casual \f(CW\*(C`grep\*(C'\fR, \*(L"deflate\*(R" already works
.RE
.RS 4
.RE
.IP "\(bu" 4
\&\s-1PAR\s0 now supports pluggable \fIinput filters\fR with \f(CW\*(C`pp \-f\*(C'\fR
.RS 4
.IP "\(bu" 4
Bundled examples: Bleach, PodStrip and PatchContent
.IP "\(bu" 4
True encryption using \f(CW\*(C`Crypt::*\*(C'\fR
.IP "\(bu" 4
Or even _product activation_ over the internet
.RE
.RS 4
.RE
.IP "\(bu" 4
Alternatively, just keep core logic in your server and use \s-1RPC\s0
.SS "Accessing packed files"
.IX Subsection "Accessing packed files"
.IP "\(bu" 4
To get the host archive from a packed program:
.Sp
.Vb 2
\& my $zip = PAR::par_handle($0); # an Archive::Zip object
\& my $content = $zip\->contents(\*(AqMANIFEST\*(Aq);
.Ve
.IP "\(bu" 4
Same thing, but with \f(CW\*(C`read_file()\*(C'\fR:
.Sp
.Vb 1
\& my $content = PAR::read_file(\*(AqMANIFEST\*(Aq);
.Ve
.IP "\(bu" 4
Loaded \s-1PAR\s0 files are stored in \f(CW%PAR::LibCache\fR:
.Sp
.Vb 5
\& use PAR \*(Aq/home/mylibs/*.par\*(Aq;
\& while (my ($filename, $zip) = each %PAR::LibCache) {
\& print "[$filename \- MANIFEST]\en";
\& print $zip\->contents(\*(AqMANIFEST\*(Aq);
\& }
.Ve
.SS "Packing \s-1GUI\s0 applications"
.IX Subsection "Packing GUI applications"
.IP "\(bu" 4
\&\s-1GUI\s0 toolkits often need to link with shared libraries:
.Sp
.Vb 2
\& # search for libncurses under library paths and pack it
\& % pp \-l ncurses curses_app.pl # same for Tk, Wx, Gtk, Qt...
.Ve
.IP "\(bu" 4
Use \f(CW\*(C`pp \-\-gui\*(C'\fR on Win32 to eliminate the console window:
.Sp
.Vb 2
\& # pack \*(Aqsrc.pl\*(Aq into a console\-less \*(Aqout.exe\*(Aq (Win32 only)
\& % pp \-\-gui \-o out.exe src.pl
.Ve
.IP "\(bu" 4
\&\*(L"Can't locate Foo/Widget/Bar.pm in \f(CW@INC\fR\*(R"?
.RS 4
.IP "\(bu" 4
Some toolkits (notably Tk) autoloads modules without \f(CW\*(C`use\*(C'\fR or \f(CW\*(C`require\*(C'\fR
.IP "\(bu" 4
Hence \f(CW\*(C`pp\*(C'\fR and \f(CW\*(C`Module::ScanDeps\*(C'\fR may fail to detect them
.IP "\(bu" 4
Tk problems mostly fixed by now, but other toolkits may still break
.IP "\(bu" 4
You can work around it with \f(CW\*(C`pp \-M\*(C'\fR or an explicit \f(CW\*(C`require\*(C'\fR
.IP "\(bu" 4
Or better, send a short test-case to \f(CW\*(C`par@perl.org\*(C'\fR so we can fix it
.RE
.RS 4
.RE
.SS "Precompiled \s-1CPAN\s0 distributions"
.IX Subsection "Precompiled CPAN distributions"
.IP "\(bu" 4
Installing \s-1XS\s0 extensions from \s-1CPAN\s0 was difficult
.RS 4
.IP "\(bu" 4
Some platforms do not come with a compiler (Win32, MacOSX...)
.IP "\(bu" 4
Some headers or libraries may be missing
.IP "\(bu" 4
\&\s-1PAR\s0.pm itself used to suffer from both problems
.RE
.RS 4
.RE
.IP "\(bu" 4
\&...but not anymore \*(-- \f(CW\*(C`Module::Install\*(C'\fR to the rescue!
.Sp
.Vb 6
\& # same old Makefile.PL, with a few changes
\& use inc::Module::Install; # was "use ExtUtils::MakeMaker;"
\& WriteMakefile( ... ); # same as the original
\& check_nmake(); # make sure the user have nmake
\& par_base(\*(AqAUTRIJUS\*(Aq); # your CPAN ID or a URL
\& fetch_par() unless can_cc(); # use precompiled PAR only if necessary
.Ve
.IP "\(bu" 4
Users will not notice anything, except now it works
.RS 4
.IP "\(bu" 4
Of course, you still need to type \f(CW\*(C`make par\*(C'\fR and upload the precompiled package
.IP "\(bu" 4
\&\s-1PAR\s0 users can also install it directly with \f(CW\*(C`parl \-i\*(C'\fR
.RE
.RS 4
.RE
.SS "Platform-specific Tips"
.IX Subsection "Platform-specific Tips"
.IP "\(bu" 4
Win32 and other icon-savvy platforms
.RS 4
.IP "\(bu" 4
Needs 3rd\-party tools to add icons to \f(CW\*(C`pp\*(C'\fR\-generated executables
.IP "\(bu" 4
\&\s-1PE\s0 Header manipulation in Perl \*(-- volunteers wanted!
.RE
.RS 4
.RE
.IP "\(bu" 4
Linux and other libc-based platforms
.RS 4
.IP "\(bu" 4
Try to avoid running \f(CW\*(C`pp\*(C'\fR on a bleeding-edge version of the \s-1OS\s0
.IP "\(bu" 4
Older versions with an earlier libc won't work with new ones
.RE
.RS 4
.RE
.IP "\(bu" 4
Solaris and other zlib-lacking platforms (but not Win32)
.RS 4
.IP "\(bu" 4
You need a static-linked \f(CW\*(C`Compress::Zlib\*(C'\fR before installing \s-1PAR\s0
.IP "\(bu" 4
In the future, \s-1PAR\s0 may depend on \f(CW\*(C`Compress::Zlib::Static\*(C'\fR instead
.RE
.RS 4
.RE
.IP "\(bu" 4
Any platform with limited bandwidth or disk space
.RS 4
.IP "\(bu" 4
Use \s-1UPX\s0 to minimize the executable size
.RE
.RS 4
.RE
.SS "Thank you!"
.IX Subsection "Thank you!"
.IP "\(bu" 4
Additional resources
.RS 4
.IP "\(bu" 4
Mailing list: \f(CW\*(C`par@perl.org\*(C'\fR
.IP "\(bu" 4
Subscribe: Send a blank email to \f(CW\*(C`par\-subscribe@perl.org\*(C'\fR
.IP "\(bu" 4
List archive: <http://nntp.x.perl.org/group/perl.par>
.IP "\(bu" 4
PAR::Intro: <http://search.cpan.org/dist/PAR/lib/PAR/Intro.pod>
.IP "\(bu" 4
Apache::PAR: <http://search.cpan.org/dist/Apache\-PAR/>
.IP "\(bu" 4
Module::Install: <http://search.cpan.org/dist/Module\-Install/>
.RE
.RS 4
.RE
.IP "\(bu" 4
Any questions?
.SS "Bonus Slides: \s-1PAR\s0 Internals"
.IX Subsection "Bonus Slides: PAR Internals"
.SS "Overview of \s-1PAR\s0.pm's Implementation"
.IX Subsection "Overview of PAR.pm's Implementation"
.IP "\(bu" 4
Here begins the scary part
.RS 4
.IP "\(bu" 4
Grues, Dragons and Jabberwocks abound...
.IP "\(bu" 4
You are going to learn weird things about Perl internals
.RE
.RS 4
.RE
.IP "\(bu" 4
\&\s-1PAR\s0 invokes four areas of Perl arcana:
.RS 4
.IP "\(bu" 4
\&\f(CW@INC\fR code references
.IP "\(bu" 4
On-the-fly source filtering
.IP "\(bu" 4
Overriding \f(CW\*(C`DynaLoader::bootstrap()\*(C'\fR to handle \s-1XS\s0 modules
.IP "\(bu" 4
Making self-bootstrapping binary executables
.RE
.RS 4
.RE
.IP "\(bu" 4
The first two only works on 5.6 or later
.RS 4
.IP "\(bu" 4
DynaLoader and \f(CW%INC\fR are there since Perl 5 was born
.IP "\(bu" 4
\&\s-1PAR\s0 currently needs 5.6, but a 5.005 port is possible
.RE
.RS 4
.RE
.ie n .SS "Code References in @INC"
.el .SS "Code References in \f(CW@INC\fP"
.IX Subsection "Code References in @INC"
.IP "\(bu" 4
On 1999\-07\-19, Ken Fox submitted a patch to P5P
.RS 4
.IP "\(bu" 4
To _enable using remote modules_ by putting hooks in \f(CW@INC\fR
.IP "\(bu" 4
It's accepted to come in Perl 5.6, but undocumented until 5.8
.IP "\(bu" 4
Type \f(CW\*(C`perldoc \-f require\*(C'\fR to read the nitty-gritty details
.RE
.RS 4
.RE
.IP "\(bu" 4
Coderefs in \f(CW@INC\fR may return a fh, or undef to 'pass':
.Sp
.Vb 5
\& push @INC, sub {
\& my ($coderef, $filename) = @_; # $coderef is \e&my_sub
\& open my $fh, "wget ftp://example.com/$filename |";
\& return $fh; # using remote modules, indeed!
\& };
.Ve
.IP "\(bu" 4
Perl 5.8 let you open a file handle to a string, so we just use that:
.Sp
.Vb 2
\& open my $fh, \*(Aq<\*(Aq, \e($zip\->memberNamed($filename)\->contents);
\& return $fh;
.Ve
.IP "\(bu" 4
But Perl 5.6 does not have that, and I don't want to use temp files...
.SS "Source Filtering without Filter::* Modules"
.IX Subsection "Source Filtering without Filter::* Modules"
.IP "\(bu" 4
\&... Undocumented features to the rescue!
.RS 4
.IP "\(bu" 4
It turns out that \f(CW@INC\fR hooks can return \fBtwo\fR values
.IP "\(bu" 4
The first is still the file handle
.IP "\(bu" 4
The second is a code reference for line-by-line source filtering!
.RE
.RS 4
.RE
.IP "\(bu" 4
This is how \f(CW\*(C`Acme::use::strict::with::pride\*(C'\fR works:
.Sp
.Vb 7
\& # Force all modules used to use strict and warnings
\& open my $fh, "<", $filename or return;
\& my @lines = ("use strict; use warnings;\en", "#line 1 \e"$full\e"\en");
\& return ($fh, sub {
\& return 0 unless @lines;
\& push @lines, $_; $_ = shift @lines; return length $_;
\& });
.Ve
.SS "Source Filtering without Filter::* Modules (cont.)"
.IX Subsection "Source Filtering without Filter::* Modules (cont.)"
.IP "\(bu" 4
But we don't really have a filehandle for anything
.IP "\(bu" 4
Another undocumented feature saves the day!
.IP "\(bu" 4
We can actually omit the first return value altogether:
.Sp
.Vb 9
\& # Return all contents line\-by\-line from the file inside PAR
\& my @lines = split(
\& /(?<=\en)/,
\& $zip\->memberNamed($filename)\->contents
\& );
\& return (sub {
\& $_ = shift(@lines);
\& return length $_;
\& });
.Ve
.SS "Overriding DynaLoader::bootstrap"
.IX Subsection "Overriding DynaLoader::bootstrap"
.IP "\(bu" 4
\&\s-1XS\s0 modules have dynamically loaded libraries
.RS 4
.IP "\(bu" 4
They cannot be loaded as part of a zip file, so we extract them out
.IP "\(bu" 4
Must intercept DynaLoader's library-finding process
.RE
.RS 4
.RE
.IP "\(bu" 4
Module names are passed to \f(CW\*(C`bootstrap\*(C'\fR for \s-1XS\s0 loading
.RS 4
.IP "\(bu" 4
During the process, it calls \f(CW\*(C`dl_findfile\*(C'\fR to locate the file
.IP "\(bu" 4
So we install pre-hooks around both functions
.RE
.RS 4
.RE
.IP "\(bu" 4
Our \f(CW\*(C`_bootstrap\*(C'\fR just checks if the library is in PARs
.RS 4
.IP "\(bu" 4
If yes, extract it to a \f(CW\*(C`File::Temp\*(C'\fR temp file
.RS 4
.IP "\(bu" 4
The file will be automatically cleaned up when the program ends
.RE
.RS 4
.RE
.IP "\(bu" 4
It then pass the arguments to the original \f(CW\*(C`bootstrap\*(C'\fR
.IP "\(bu" 4
Finally, our \f(CW\*(C`dl_findfile\*(C'\fR intercepts known filenames and return it
.RE
.RS 4
.RE
.SS "Anatomy of a Self-Contained \s-1PAR\s0 executable"
.IX Subsection "Anatomy of a Self-Contained PAR executable"
.IP "\(bu" 4
The par script ($0) itself
.RS 4
.IP "\(bu" 4
May be in plain-text or native executable format
.RE
.RS 4
.RE
.IP "\(bu" 4
Any number of embedded files
.RS 4
.IP "\(bu" 4
Typically used to bootstrap \s-1PAR\s0's various dependencies
.IP "\(bu" 4
Each section begins with the magic string \*(L"\s-1FILE\*(R"\s0
.IP "\(bu" 4
Length of filename in pack('N') format and the filename (auto/.../)
.IP "\(bu" 4
File length in pack('N') and the file's content (not compressed)
.RE
.RS 4
.RE
.IP "\(bu" 4
One \s-1PAR\s0 file
.RS 4
.IP "\(bu" 4
Just a regular zip file with the magic string \f(CW"PK\e003\e004"\fR
.RE
.RS 4
.RE
.IP "\(bu" 4
Ending section
.RS 4
.IP "\(bu" 4
A pack('N') number of the total length of \s-1FILE\s0 and \s-1PAR\s0 sections
.IP "\(bu" 4
Finally, there must be a 8\-bytes magic string: \f(CW"\e012PAR.pm\e012"\fR
.RE
.RS 4
.RE
.SS "Self-Bootstrapping Tricks"
.IX Subsection "Self-Bootstrapping Tricks"
.IP "\(bu" 4
All we can expect is a working perl interpreter
.RS 4
.IP "\(bu" 4
The self-contained script *must not* use any modules at all
.IP "\(bu" 4
But to process \s-1PAR\s0 files, we need \s-1XS\s0 modules like Compress::Zlib
.RE
.RS 4
.RE
.IP "\(bu" 4
Answer: bundle all modules + libraries used by \s-1PAR\s0.pm
.RS 4
.IP "\(bu" 4
That's what the \f(CW\*(C`FILE\*(C'\fR section in the previous slide is for
.IP "\(bu" 4
Load modules to memory, and write object files to disk
.IP "\(bu" 4
Then use a local \f(CW@INC\fR hook to load them on demand
.RE
.RS 4
.RE
.IP "\(bu" 4
Minimizing the amount of temporary files
.RS 4
.IP "\(bu" 4
First, try to load PerlIO::scalar and File::Temp
.IP "\(bu" 4
Set up an \s-1END\s0 hook to unlink all temp files up to this point
.IP "\(bu" 4
Load other bundled files, and look in the compressed \s-1PAR\s0 section
.IP "\(bu" 4
This can be much easier with a pure-perl \f(CW\*(C`inflate()\*(C'\fR; patches welcome!
.RE
.RS 4
.RE
.SS "Thank you (again)!"
.IX Subsection "Thank you (again)!"
.IP "\(bu" 4
Any questions, \fIplease\fR?
.SH "SEE ALSO"
.IX Header "SEE ALSO"
\&\s-1PAR\s0, pp, par.pl, parl
.PP
ex::lib::zip, Acme::use::strict::with::pride
.PP
App::Packer, Apache::PAR, \s-1CPANPLUS\s0, Module::Install
.SH "AUTHORS"
.IX Header "AUTHORS"
Audrey Tang <cpan@audreyt.org>
.PP
<http://par.perl.org/> is the official \s-1PAR\s0 website. 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.
.PP
Please submit bug reports to <bug\-par@rt.cpan.org>.
.SH "COPYRIGHT"
.IX Header "COPYRIGHT"
Copyright 2003, 2004, 2005, 2006 by Audrey Tang <cpan@audreyt.org>.
.PP
This document is free documentation; you can redistribute it and/or
modify it under the same terms as Perl itself.
.PP
See <http://www.perl.com/perl/misc/Artistic.html>