Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

tsee (4409)

tsee
  reversethis-{gro.napc} {ta} {relleums}
http://steffen-mueller.net/

You can find most of my Open Source Perl software in my CPAN directory [cpan.org].

Journal of tsee (4409)

Sunday January 14, 2007
10:50 AM

Evil PAR tricks, issue 0: Binary including a binary

[ #32140 ]

In my journal entry from December 22, 2006, I said I'd used a few hacks to package parinstallppdgui into an executable binary using PAR::Packer. I'll explore that further here:

parinstallppdgui is mostly a GUI front-end to parinstallppd. It doesn't use PAR::Dist::InstallPPD internally, but uses the system to run parinstallppd so if the child process bombs out, the GUI process can show a friendly warning to the user (comprised of the STDERR and STDOUT of the child process) instead of crashing. That's all very simple if you have perl on your system, of course.

Now, if you want to package parinstallppdgui or any other Perl script that uses another Perl script into an .exe, you'll quickly find out that without a perl on the target system, these two scripts cannot share the same interpreter. The obvious "solution" is to package both of them up into an .exe separately and ship them both. This has several problems. First, you need to ship two executables instead of one. Second, the first executable won't necessarily know where to look for the second if the user doesn't put them in PATH. Adding a couple of FindBin hacks isn't great at solving this, either! Third, these two executables will have a lot in common - starting with all the core Perl modules.

So I took a slightly better, yet more complicated route. The process is as follows:

  1. Add a special case to the parent's source code for execution from inside a PAR binary:

    if (defined $ENV{PAR_TEMP}) {
            require Config;
            require File::Spec;
            $ENV{PATH} .= (defined $ENV{PATH} ? $Config::Config{path_sep} : '')
                                            . File::Spec->catdir($ENV{PAR_TEMP}, 'inc');
            $ENV{PAR_GLOBAL_TEMP} = $ENV{PAR_TEMP};
    }

    This forces all PAR'd executables that are started by this process to use the same cache area as this process. This might be a dangerous thing to do if both binaries contain different, incompatible versions of some files. But we control everything here, so it should be okay.
  2. Package parinstallppdgui, the parent script:
    pp -o parinstallppdgui.exe parinstallppdgui
  3. Package parinstallppd, the child script:
    pp -o parinstallppd.exe -l expat parinstallppd
    (We include the expat library.)
  4. Remove all files from the child script that are also available from the parent. This makes the child dependent on the parent. Use the pare utility from the PAR::Packer distribution's contrib/ directory for this:
    pare -u parinstallppdgui.exe parinstallppd.exe
  5. Package the parent script again, but this time include the child script as an extra file into the executable:
    pp -o parinstallppdgui.exe -a parinstallppd.exe -l expat parinstallppdgui
  6. Ship the combined binary only.

Cheers,

Steffen

P.S.: A third and even better solution might be to package both of the scripts into the same binary and symlink or copy that binary to parinstallppd.exe and parinstallppdgui.exe and let PAR figure out what to run. This is cool if you have symlinks and sucks if you don't.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.