PAR::Intro - Introduction to Perl Archive Toolkit
# This is a presentation, not a module.
Do what JAR (Java Archive) does for Perl
Benefits of using PAR:
par@perl.org
You can also turn a PAR file into a self-contained script
pp to compile the script...
First, generate a PAR file with modules in it:
% zip foo.par Hello.pm
% zip -r foo.par lib/ # grab all modules in lib/
Using modules stored inside a PAR file:
% perl -MPAR=./foo.par -MHello
% perl -MPAR=./foo -MHello # the .par part is optional
Or put it in @INC and use it just like a directory:
% perl -MPAR -Ifoo.par -MHello
% perl -MPAR -Ifoo -MHello # ditto
Use pp to scan scripts and store dependencies as a PAR file:
% pp -p source.pl # makes 'source.par'
% pp -B -p source.pl # bundles core modules too
Use par.pl to run files from a Perl Archive:
% par.pl foo.par # looks for 'main.pl' by default
% par.pl foo.par test.pl # runs script/test.pl in foo.par
Use parl or parl.exe to run files from a Perl Archive:
% parl foo.par
% parl foo.par test.pl
The pp utility can also generate binary executables:
% pp -o packed.exe source.pl # self-contained .exe
% packed.exe # runs anywhere with the same OS
You can also bundle additional modules:
# packs CGI + its dependencies, too
% pp -o packed.exe -M CGI source.pl
Or pack one-liners:
# turns one-liner into executable
% pp -o packed.exe -e 'print "Hi!"'
Some notes:
pp are almost identical to perlcc's
Modules can reside in different directories in a PAR file:
/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
/ # casual packaging only
Scripts are stored in one of the two locations:
/script/ # standard location
/ # casual packaging only
Shared libraries are currently stored in a single location: /shlib/ # standard location =item *
Special files:
/MANIFEST # index of the PAR's contents
/SIGNATURE # digital signature(s)
/META.yml # dependency, license info, etc.
/Build.PL # self-contained installer
PAR::read_file($filename) to read file contents inside PAR
Apache::PAR
App::Packer::Backend::PAR
CPANPLUS::Dist::PAR
In httpd.conf:
<VirtualHost *>
<IfDefine MODPERL2>
PerlModule Apache::ServerUtil
</IfDefine>
PerlModule Apache::PAR
PARDir /opt/myapp
PARFile /opt/myapp/myapp.par
</VirtualHost>
In web.conf inside myapp.par:
Alias /myapp/static/ ##PARFILE##/
<Location /myapp/static>
SetHandler perl-script
PerlHandler Apache::PAR::Static
PerlAddVar PARStaticDirectoryIndex index.html
PerlSetVar PARStaticDefaultMIME text/html
</Location>
Alias /myapp/cgi-perl/ ##PARFILE##/
<Location /myapp/cgi-perl>
Options +ExecCGI
SetHandler perl-script
PerlHandler Apache::PAR::Registry
</Location>
Polish pp's features
Learning from JAR
Learning from FreeBSD Bento
Here begins the scary part
PAR invokes five areas of Perl arcana:
The first two only works on 5.6 or later
On 1999-07-19, Ken Fox submitted a patch to P5P
Code references in @INC may return a filehandle, or undef to 'pass':
push @INC, \&my_sub;
sub my_sub {
my ($coderef, $filename) = @_; # $coderef is \&my_sub
open my $fh, "wget http://example.com/$filename |";
return $fh; # using remote modules, indeed!
}
Perl 5.8 let you open a file handle to a string, so we just use that:
open my $fh, '<', \($zip->memberNamed($filename)->contents);
return $fh;
... Undocumented features to the rescue!
This is how Acme::use::strict::with::pride works:
# Force all modules used to use strict and warnings
open my $fh, "<", $filename or return;
my @lines = ("use strict; use warnings;\n", "#line 1 \"$full\"\n");
return ($fh, sub {
return 0 unless @lines;
push @lines, $_; $_ = shift @lines; return length $_;
});
But we don't really have a filehandle for anything
We can actually omit the first return value altogether:
# Return all contents line-by-line from the file inside PAR
my @lines = split /(?<=\n)/, $zip->memberNamed($filename)->contents;
return (sub { $_ = shift(@lines); return length $_ });
The @INC filter stops when it sees __END__ or __DATA__
eval the main.pl script
Therefore, we insert a line before the final token to fake *DATA
Here is what I came up with (but no longer needed in recent versions):
$DATACache{$file} = $1 if ($program =~ s/^__DATA__\n?(.*)//ms);
if (eval {require PerlIO::scalar; 1}) {
"use PerlIO::scalar".
" ( open(*DATA, '<:scalar', \\\$PAR::DATACache{'$key'}) ? () : () )";
}
elsif (eval {require IO::Scalar; 1}) {
# This will first load IO::Scalar, *then* tie the handles.
"use IO::Scalar".
" ( tie(*DATA, 'IO::Scalar', \\\$PAR::DATACache{'$key'}) ? () : () )";
}
else {
# only dies when it's used
"use PAR (tie(*DATA, 'PAR::_data') ? () : ())\n";
}
sub PAR::_data::TIEHANDLE { return bless({}, shift) }
sub PAR::_data::AUTOLOAD { die "Please install IO::Scalar first!\n" }
XS modules have dynamically loaded libraries (.so or .dll)
auto/ directories
Module names are passed to bootstrap for XS loading
dl_findfile to locate the file
So we wrap around both functions:
no strict 'refs'; no warnings 'redefine';
$bootstrap = \&DynaLoader::bootstrap;
$dl_findfile = \&DynaLoader::dl_findfile;
*{'DynaLoader::bootstrap'} = \&_bootstrap;
*{'DynaLoader::dl_findfile'} = \&_dl_findfile;
Our _bootstrap just checks if the library is in PARs
If yes, extract it to a File::Temp temp file
$bootstrap
_dl_findfile intercepts known filenames and return it
The par script ($0) itself
Any number of embedded files
One PAR file
"PK\003\004"
Ending section
"\012PAR.pm\012"
All we can expect is a working perl interpreter
Solution: bundle all module and object files needed by PAR.pm
FILE section in the previous slide is for
We want to minimize the amount of temporary files
First, try getting PerlIO::scalar loaded
tempfile()
This can be so much easier if we have a pure-perl inflate()
http://www.autrijus.org/par-intro/ (English version)
http://www.autrijus.org/par-intro.zh/ (Chinese version)
ex::lib::zip, Acme::use::strict::with::pride
App::Packer, Apache::PAR, CPANPLUS, Module::Install
Autrijus Tang <autrijus@autrijus.org>
PAR has a mailing list, <par@perl.org>, that you can write to; send an empty mail to <par-subscribe@perl.org> to join the list and participate in the discussion.
Please send bug reports to <bug-par@rt.cpan.org>.
Copyright 2002, 2003 by Autrijus Tang <autrijus@autrijus.org>.
This document is free documentation; you can redistribute it and/or modify it under the same terms as Perl itself.