Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Re^4: dll not embedded in PAR::Packer's output

by afoken (Chancellor)
on Dec 03, 2015 at 00:24 UTC ( [id://1149244]=note: print w/replies, xml ) Need Help??


in reply to Re^3: dll not embedded in PAR::Packer's output
in thread dll not embedded in PAR::Packer's output

I discovered that 7-zip files can be converted into installers. Basically, it's a variant of a self-extracting archive that auto-runs a cmd file (like a bat file) after the extraction.

Yes, that's exactly what my old AP+app installer did using WinZIPs SFX function. Perl can quite easily be embedded into a batch script, and as far as I remember, that was how the installer script worked. A batch file with embedded code that first did some minimal sanity checks using the batch interpreter, then executed perl -x -S %0, letting perl search and execute the embedded perl script in the batch file.

This is a quite minimal example showing the trick:

@echo off echo Hello from the DOS/Windows command interpreter perl -x -S %0 goto batchend #!perl use strict; use warnings; print "Hello from Perl\n"; exit; __END__ :batchend echo Back from perl in the command interpreter

When installing from an unpacked ZIP, that batch usually runs from a temporary directory (not a big problem), and the perl executable from the ZIP is not in $ENV{PATH}. That can be a problem. Just starting the installer script as shown above will either run some other perl installed by some other application (Oracle delivered REALLY old perls at that time), or perl won't simply run.

You really don't want to mix two or more perls on a Windows machine, so the first thing you do (or better: I did years ago) was to try to run the "resident" perl, expecting it to fail:

rem -- somewhere inside the big batch file -- perl -e "exit 42" if errorlevel 43 goto noperl if errorlevel 42 goto badperl goto noperl :badperl echo Sorry, there is a foreign perl installed. Can't install. goto fail :noperl rem ... rem ... much code omitted, explained in the next step rem ... :fail pause :end

DOS is strange: if errorlevel 43 does NOT check that the exit code of the last program is exactly 43, but if the exit code is at least 43. So the first if errorlevel 43 catches exit codes from 43 to 255, the next if errorlevel 42 can catch only exit code 42 (all above were alredy handled), and the final goto noperl catches all exit codes below 42. The check for 42 is quite arbitary, usually the exit code is 0, 255, or something much larger on NT. But it is very unlikely that some program named perl.com or perl.exe exits with exit code 42, given the parameters, and can't interpret perl.

Note that the batch doesn't yet use the embedded script trick, because there can be only one embedded script.

Another step is to make sure that the script finds itself and the other files extracted from the ZIP file. SFX modules should run the embedded script with the temporary directory as the current directory.

Quite easy:

rem ... rem (assuming this script is named setup.bat) if exists setup.bat goto havesetup echo Can't find myself. Can't install. goto fail :havesetup rem (assuming our perl.exe is in a bin subdirectory) if not exists perl.exe goto dirsok echo ZIP file was unpacked without directories. Can't install. goto fail :dirsok if exists bin\perl.exe goto haveperl echo Missing my perl.exe. Archive damaged? Can't install. goto fail :haveperl rem ... rem ... use perl to bootstrap itself rem ... :fail pause :end

Quite easy: Check that the batch file exists in the current directory, and that perl.exe was extracted to the bin subdirectroy, not to the current directory. At that point, the unpacked perl should be usable.

Perhaps one last paranoia check?

rem ... bin\perl.exe -e "exit($]!=5.014002)" if not errorlevel 1 goto perlok echo Unexpected perl interpreter found. Won't install. goto fail :perlok rem ... :fail pause :end

Just a version check. perl.exe will exit with exit code 0 only if it has exactly the right version. Else, it will exit with exit code 1, maybe larger if something strange has happened (crashing perl).

And now, the embedded script. perl -x will search the entire file for the first line starting with #! and containing the word "perl". From there, it will start interpreting perl. The -S parameter will make perl search $ENV{PATH} for the script. Not strictly required.

Errors should be reported. Perl has die, so that's not a big problem. But the command window should stay open on error. The batch file has a fail label that does exactly that, and die sets the exit code to non-zero. Easily combined:

rem ... perl -x -S %0 if errorlevel 1 goto fail goto end rem -- command shell will never try to interpret this or the following + lines up to the fail label. #!perl use strict; use warnings; use lib 'lib'; use My::Custom::Setup; My::Custom::Setup->run(); __END__ rem ^-- perl stops interpreting at __END__ :fail pause end

The embedded script here is quite minimal, it just loads a custom module that does the actual work. You can also combine a batch skeleton and a larger perl script, perhaps using a primitive template system, or just by concatenating batch header, perl script, and batch footer into the final setup.bat. But that makes it a little bit harder to use an editor with syntax highlighting. Most won't correctly display both languages at the same time.

Another option is to setup or load a custom error handler that displays a message box instead of just writing into the DOS box. Carp can be quite inspiring, but a simple eval may be sufficient. The string eval prevents compile time effects of use, so even compile errors in lib and My::Custom::Setup are trapped by eval.

#!perl use strict; use warnings; if (eval q[ use lib 'lib'; use My::Custom::Setup; My::Custom::Setup->run(); 1; ]) { exit(0); } else { my $err=$@; Win32::MsgBox($err,MB_ICONSTOP,"Error"); die $err; } __END__

Alexander

--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1149244]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (4)
As of 2024-03-19 03:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found