Why can't I get Dancer's version when invoking perl from the command line on Windows?

Recently, I decided to upgrade Dancer on my Windows XP laptop (hint, hint) to the most recent version. The ppm repositories I was using did not have the latest. Not to worry, though, because I had installed the mingw package to be able to build and install CPAN packages by hand.

From that point on, it was simply a matter of:

perl Makefile.PL
dmake test
dmake install

However, I was bothered by some messages along the lines of “unable to determine version information from Dancer.pm”.

I decided to investigate.

C:\> perl -MDancer -e "print $Dancer::VERSION"
-e: No such file or directory at c:/opt/Perl/site/lib/Dancer.pm line 161
BEGIN failed--compilation aborted at c:/opt/Perl/site/lib/Dancer.pm line 161,
 line 16.
Compilation failed in require,  line 16.
BEGIN failed--compilation aborted,  line 16.

Hmmmm … What is that all about? After all, my programs using Dancer still worked fine.

Here is the offending piece of code:

sub load_app {
    for my $app (@_) {
        Dancer::Logger->core("loading application $app");

        use lib path(dirname(abs_path($0)), 'lib');

The use lib pragma is trying to add the directory in which the script lives to @INC during the compile phase.

Now, when you invoke perl with the -e switch, $0 contains the string -e. I thought “A-ha!” and filed a lousy report.

The response I got from xsawyerx made me realize that there was a difference between perl on Windows and perl on Linux. Indeed, testing on one of the ancient desktops I have with ArchLinux installed confirmed it:

[[email protected] ~]$ perl -MDancer -e 'print "$Dancer::VERSION\n"'
1.1803

My attention quickly focused on Cwd::abs_path. Here is what Cwd::abs_path does on Windows. First, abs_path is aliased to fast_abs_path. Second, fast_abs_path contains:

my $Curdir;
sub fast_abs_path {
    local $ENV{PWD} = $ENV{PWD} || ''; # Guard against clobberage
    my $cwd = getcwd();
    require File::Spec;
    my $path = @_ ? shift : ($Curdir ||= File::Spec->curdir);

    # …

    unless (-e $path) {
  _croak("$path: No such file or directory");
    }

As you can see from the debugger output, that's where the problem is:

-e: No such file or directory at c:/opt/Perl/site/lib/Dancer.pm line 161
 at c:/opt/Perl/lib/Cwd.pm line 357
Cwd::_croak('-e: No such file or directory') called at c:/opt/Perl/lib/Cwd.pm
line 633
Cwd::fast_abs_path('-e') called at c:/opt/Perl/site/lib/Dancer.pm line 161

On Unix, abs_path is an XS routine. It calls bsd_realpath in Cwd.xs. Something, somewhere, in that execution path results in something that works.

Now, one might say, this does not matter if it only happens on Windows. On the other hand, command line tools rely on being able to require a module and deduce version information from the module.

As I thought about this more, I realized the delayed loading fix I recommended in the original bug report was the wrong solution. IMHO, the right solution would be to change Dancer::load_app like so:

require File::Spec;
use lib path(dirname(File::Spec->rel2abs($0)), 'lib');

Update: The patch will be included in the next release of Dancer. Now, that's what I call responsiveness.