Today is: 9 January, 2012
Check todays hot topics

Win32 + PAR::Packer Installation Tips

Perl's PAR::Packer is a great way to write (albeit bloated) Win32 apps for clients in a short time. I find this useful for taking on jobs spec'd for Windows or a *nix project that you'd like obfuscated by virtue of distributing a binary.

PAR is actually neat for a lot of things. It creates a compressed archive of libraries for simple recall. The PAR that is generated is not all together too different then Java's notion of a JAR. PAR has surprisingly seamless integration with LWP::UserAgent, in that, you can include modules in a PAR from across the network by simply using it.

# Including a par in $INC
$ perl -MPAR=./foo -MHello
 
# Using PAR::Repository::Client to include a repo of PARS
use PAR { repository => 'http://example.com/foo/bar/' };
 
# Using PAR::Repository::Client to execute a PAR locally
use PAR { repository => 'http://example.com/', run => 'foo.par' }
 
# Include a specific PAR across the network. 
use PAR;
use lib ('http://example.com/test.par');

The Packer!!

This is my favorite part about PAR! The packer (albeit awfully named) is pretty much the coolest thing since... I got nothin.. It can be a pain in the ass to install, but is well worth it. Jump to "Tips" for installation tips and common issues.

Compiled on a Win32 platform we generate Win32 binaries! Compiled on a *nix platform we generate mostly-valid elf binaries! Observe..

$ cat par_test.pl 
#!/usr/bin/perl
 
print "Hello World!\n";
 
$ ./par_test.pl 
Hello World!
 
$ pp -o par_test par_test.pl 
 
$ file par_test
par_test: Mach-O executable i386
 
$ ./par_test
Hello World!

Note: YMMV, this example was generated on an Intel MAC. You will obviously recieve different `file` output based on your system.

The execution time is drastically increased based on the bloat of the executable. Notice the filesize diffs...

$ ls -lah par_test par_test.pl 
-rwxr-xr-x  1 ghettocode  staff   3.8M Sep  3 13:56 par_test
-rwxr-xr-x  1 ghettocode  staff    41B Sep  3 13:55 par_test.pl

The reason for this bloat is parpacker is including the entire interpretter any needed LIBS into the executable. Including a lot more libs will increase the size of your binary as it literally follows the dependancy tree and packs everything needed into the binary. If your application is very performance oriented this may not be the best solution for you.

I'm assuming it's possible to leak code by running binutils against the executable, but good luck sifting through Interpretter code and par code.

PAR Inclusion. How it Works.

Including a .par will prepend a coderef into @INC allowing you to include modules as if they were in @INC.

$ perl -MPAR=./Hello.par -Mscript::World -e '$, = " \n"; print @INC;'
CODE(0x8294e4) 
/opt/local/lib/perl5/site_perl/5.8.9/darwin-2level 
/opt/local/lib/perl5/site_perl/5.8.9 
/opt/local/lib/perl5/site_perl 
/opt/local/lib/perl5/vendor_perl/5.8.9/darwin-2level 
/opt/local/lib/perl5/vendor_perl/5.8.9 
/opt/local/lib/perl5/vendor_perl 
/opt/local/lib/perl5/5.8.9/darwin-2level 
/opt/local/lib/perl5/5.8.9 
. 

I'm still not entirely sure how this works. Dumping the code ref spits back a 'DUMMY' handler.

$ perl -MPAR=./Hello.par -MData::Dumper -e "print Dumper \@INC[0];"
$VAR1 = \sub { "DUMMY" };

A Brief Example

package Hello::World;
 
sub hello {
        print "Hello World\n";
}
 
1;

$ pp -p -o Hello.par Hello/World.pm 
 
$ ls
Hello     Hello.par
 
$ unzip -l Hello.par 
Archive:  Hello.par
  Length     Date   Time    Name
 --------    ----   ----    ----
      434  09-03-09 14:17   MANIFEST
      216  09-03-09 14:17   META.yml
       66  09-03-09 14:17   script/World.pm
      274  09-03-09 14:17   script/main.pl
 --------                   -------
      990                   4 files

The PAR's directory structure is influenced by the way you create the par. Supplying the dir Hello/ to `pp` yields a different directory structure. Observe.

$ perl -MPAR=./Hello.par -Mscript::World -e "print Hello::World->hello();"
Hello World

*NIX Tips

I've seen quite a few different error conditions while installing this module. Because I'm lazy, I'd rather fix up the problems and install via CPAN rather then installing by hand. The most common I've seen is supplying duplicate make options. I'm not entirely sure why this happens but if $CPAN::Config includes something tantamount to 'make_opts' => '-j3', proceeded by 'make_install_opts' => '-j3'

$ cat ~/.cpan/CPAN/MyConfig.pm
 
$CPAN::Config = {
  'foo' => 'bar',
  'make_opts' => '-j3',
  'make_install_opts' => '-j3'
}

This somehow translates into CPAN throwing make duplicate '-j3' opts, which results in epic failure.

  SMUELLER/PAR-Packer-0.991.tar.gz
  /usr/bin/make -j3 -j3 -- NOT OK
Running make test
  Can't test without successful make
Running make install
  Make had returned bad status, install seems impossible
Failed during this command:
 SMUELLER/PAR-Packer-0.991.tar.gz             : make NO

Removing these options from the file and restarting CPAN, and *NOT* supplying -j3 when prompted will result in smooth sailing for installing PAR::Packer.

I'm also not sure what this shit is about, but it doesn't seem to be fatal. I'm assuming it has to do with special compile time options to add things to @INC. In my case I'm running the osX ports version of perl which compiles a whole bunch of /opt/ dirs into @INC. I guess my understanding of this error message would be better if I knew wtf 'taint mode' was.

*** You have extra Perl library paths set in your environment.
    Please note that these paths (set with PERL5LIB or PERLLIB)
    are not honored by perl when running under taint mode, which
    may lead to problems. This is a limitation (by design) of
    Perl, not of PAR::Packer; but some of the problems may
    manifest here during installation.

Win32 Tips

This process *REALLY* sucks!!! I sat down for quite awhile trying to figure out how to do this, and here's what I've come up with.

1. Don't use ActiveState perl. 
    - Building this module requires Dmake, which does not ship 
      with Active State. As an alternative, Strawberry Perl 
      Ships with Dmake and various other useful things.
2. Don't use this on a system with Cygwin.
    - If you can get this working, you're god damn awesome.
      Cygwin takes the liberty of installing a second build
      of Perl in $PATH and completely fucks up your lib path.
      Uninstalling Cygwin is not really an adventure in
      awesome. Just don't do it.
3. Dmake is way hotter then Cygwin GCC.
    - See Above.

Thats it. I will revise this article as necessary.