Multiple cohabitating versions of Perl on Windows

I just built my shiny new Perl 5.22.0 on my 64-bit Windows 8.1 system using Visual Studio 2013 Community Edition.

First, and foremost, thank you to all who contributes to the development of Perl. Also, thanks to Microsoft for making Visual Studio Community editions available.

While going through the motions of installing the various CPAN modules, the old issue of multiple cohabitating native Windows Perl distributions reared its head again.

See, as the ExtUtils::MakeMaker documentation explains, on all other platforms, if a module installs a script, it will be executed using the perl which was used to build it:

EXE_FILES

Ref to array of executable files …

If your executables start with something like #!perl or #!/usr/bin/perl MakeMaker will change this to the path of the perl ‘Makefile.PL’ was invoked with so the programs will be sure to run properly even if perl is not in /usr/bin/perl.

This is also the case under Cygwin For example, consider a simple module My::Dummy which bundles a simple script called mydummy by including the following bit among the arguments to WriteMakefile:

EXE_FILES       => [ 'script/mydummy' ],

In the script, we have:

$ head script/mydummy
#!perl

use strict;
use warnings;

use My::Dummy;
My::Dummy->run;

Let’s build it with the latest using my latest Cygwin perl:

 $ /opt/perl-5.22.0/bin/perl Makefile.PL
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for My::Dummy
Writing MYMETA.yml and MYMETA.json

$ make
cp lib/My/Dummy.pm blib/lib/My/Dummy.pm
cp script/mydummy blib/script/mydummy
"/opt/perl-5.22.0/bin/perl.exe" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/mydummy
Manifying 1 pod document

$ head blib/script/mydummy
#!/opt/perl-5.22.0/bin/perl
…

On the other hand, if we use the native build, we get:

C:\...\My-Dummy> c:\opt\perl-5.20.2\bin\perl Makefile.PL
Generating a nmake-style Makefile
Writing Makefile for My::Dummy
Writing MYMETA.yml and MYMETA.json

C:\...\My-Dummy> nmake

C:\...\My-Dummy> type blib\script\mydummy
@rem = '--*-Perl-*--
@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
:WinNT
perl -x -S %0 %*
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endofperl
if %errorlevel% == 9009 echo You do not have Perl in your PATH.
if errorlevel 1 goto script_failed_so_exit_with_non_zero_val 2>nul
goto endofperl

which, of course, croaks if perl is not in the %PATH%.

So, after years of setting up batch files for each Perl distribution I wanted to use, the question occurred to me: Is this just a matter of editing a few lines in pl2bat.bat so it uses the value of $^X to generate these batch files?

I fired off an email to Perl5 porters list, and proceeded to make my edits to the 5.22.0 version of pl2bat I had just installed.

My first attempt at installing cpanm without perl in the %PATH% failed because ExtUtils::MM_Win32 expects pl2bat.bat to be found via the %PATH%. After replacing that with "$Config{bin}\\pl2bat.bat", the install went fine. Since then, I installed a number of modules using this method, and everything went well. File::Which failed one of its tests because it expected perl to be in the %PATH%.

In the mean time, Leon Timmermans pointed out that the logic is duplicated in Module::Build as well as in ExtUtils::Helpers. He also raised the question of whether the logic should be factored out to somewhere else.

I tend to favor small changes to big changes, but a discussion of the costs and benefits of either approach is worthwhile.

In addition, he pointed out that relocatable distributions such as Strawberry Perl portable would need pl2bat.bat, and the generated batch files, to look for perl in the %PATH%.

Now, I am far from a neutral party in this, but with that caveat aside, it seems to me that this could be incorporated to the process of generating such a distribution by patching it.

Or, maybe something like the following could address that concern:

IF EXIST "$^X" (
        "$^X" $OPT{'o'}
) ELSE (
        perl $OPT{'o'}
)

Update: As kmx showed that:

Strawberry Perl Portable uses a hack like this:

>     IF EXIST "%~dp0perl.exe" (

> "%dp0perl.exe" -x -S %0 %* > ) ELSE IF EXIST "%dp0....\bin\perl.exe" ( > "%~dp0....\bin\perl.exe" -x -S %0 %* > ) ELSE ( > perl -x -S %0 %* > ) >
I had forgotten about %~dp0: Within a batch file, %0 is the path to the current batch file, and %~dp0 gives the directory portion of that full path.

Fixing this will allow multiple Perl distributions on Windows to be used in the same way they can be used on *nix systems. There can be one designated “system” or “default” perl. Command line tools for other versions (including module installers) can be run using their absolute paths without constantly worrying about whether the wrong perl is being invoked.

I would like that.

PS: I’ll link to the Perl5 porters thread once it appears in the online archive.

PPS: You can also discuss this post on /r/perl.