Skip to content

Commit

Permalink
Incorporated code from the 'pl2bat' utility distributed with Perl to …
Browse files Browse the repository at this point in the history
…avoid shell quoting insufficiencies and differences in various flavors of Windows.

Updated documentation to give examples of invoking builds under Windows.


git-svn-id: http://svn.perl.org/modules/Module-Build/trunk@5798 50811bd7-b8ce-0310-adc1-d9db26280581
  • Loading branch information
Randy Sims committed Mar 4, 2006
1 parent 45eecc3 commit 26d8eb9
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 92 deletions.
6 changes: 6 additions & 0 deletions Changes
Expand Up @@ -2,6 +2,12 @@ Revision history for Perl extension Module::Build.


0.27_08 0.27_08


- Due to shell quoting issues and differences in syntax between
various flavors of Windows, the code for the 'pl2bat' utility
distributed with Perl has been incorporated into M::B::P::Windows.
[Thanks to Dr Bean and Ron Savage for help testing and isolating
the problem.]

- Modify add_build_element() so that it only adds elements if they - Modify add_build_element() so that it only adds elements if they
don't already exist. [David Wheeler] don't already exist. [David Wheeler]


Expand Down
10 changes: 6 additions & 4 deletions INSTALL
Expand Up @@ -3,15 +3,17 @@
To install this module, just do: To install this module, just do:


perl Build.PL perl Build.PL
./Build
./Build test ./Build test
./Build install (this step may need to be done as the superuser) ./Build install (this step may need to be done as the superuser)


For other operating systems that don't like the "./" syntax, like Or, if you're on a platform (like DOS or Windows) that doesn't require
Windows-based systems, you can do: the "./" notation, you can do this:


perl Build.PL perl Build.PL
perl Build test Build
perl Build install Build test
Build install


The important thing is that the "Build" script gets executed and that The important thing is that the "Build" script gets executed and that
you pass it the "test", "install", etc. arguments. you pass it the "test", "install", etc. arguments.
Expand Down
8 changes: 4 additions & 4 deletions lib/Module/Build.pm
Expand Up @@ -112,13 +112,13 @@ Standard process for building & installing modules:
./Build test ./Build test
./Build install ./Build install
Or, if you're on a platform (like DOS or Windows) that doesn't like Or, if you're on a platform (like DOS or Windows) that doesn't require
the "./" notation, you can do this: the "./" notation, you can do this:
perl Build.PL perl Build.PL
perl Build Build
perl Build test Build test
perl Build install Build install
=head1 DESCRIPTION =head1 DESCRIPTION
Expand Down
11 changes: 6 additions & 5 deletions lib/Module/Build/Cookbook.pm
Expand Up @@ -44,12 +44,13 @@ directories like so:
./Build test ./Build test
./Build install ./Build install
If you're on Windows, you'll probably do something like this: If you're on Windows where the current directory is always searched
first for scripts, you'll probably do something like this:
perl Build.PL perl Build.PL
.\Build Build
.\Build test Build test
.\Build install Build install
On the old Mac OS (version 9 or lower) using MacPerl, you can On the old Mac OS (version 9 or lower) using MacPerl, you can
double-click on the F<Build.PL> script to create the F<Build> script, double-click on the F<Build.PL> script to create the F<Build> script,
Expand Down Expand Up @@ -83,7 +84,7 @@ If the end-user might not have C<Module::Build> installed, it's
probably best to supply a "traditional" F<Makefile.PL>. The probably best to supply a "traditional" F<Makefile.PL>. The
C<Module::Build::Compat> module has some very helpful tools for C<Module::Build::Compat> module has some very helpful tools for
keeping a F<Makefile.PL> in sync with a F<Build.PL>. See its keeping a F<Makefile.PL> in sync with a F<Build.PL>. See its
documentation, and also the C<create_makefile_pl> parameter to the documentation, and also the C<create_makefile_pl> parameter to the
C<< Module::Build->new() >> method. C<< Module::Build->new() >> method.
Expand Down
190 changes: 111 additions & 79 deletions lib/Module/Build/Platform/Windows.pm
Expand Up @@ -2,6 +2,7 @@ package Module::Build::Platform::Windows;


use strict; use strict;


use Config;
use File::Basename; use File::Basename;
use File::Spec; use File::Spec;
use IO::File; use IO::File;
Expand All @@ -11,83 +12,9 @@ use Module::Build::Base;
use vars qw(@ISA); use vars qw(@ISA);
@ISA = qw(Module::Build::Base); @ISA = qw(Module::Build::Base);


sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);


$self->_find_pl2bat(); sub manpage_separator {
return $self; return '.';
}


sub _find_pl2bat {
my $self = shift;
my $cf = $self->{config};

# Find 'pl2bat.bat' utility used for installing perl scripts.
# This search is probably overkill, as I've never met a MSWin32 perl
# where these locations differed from each other.

my @potential_dirs;

if ( $ENV{PERL_CORE} ) {

require ExtUtils::CBuilder;
@potential_dirs = File::Spec->catdir( ExtUtils::CBuilder->new()->perl_src(),
qw/win32 bin/ );
} else {
@potential_dirs = map { File::Spec->canonpath($_) }
@${cf}{qw(installscript installbin installsitebin installvendorbin)},
File::Basename::dirname($self->perl);
}

foreach my $dir (@potential_dirs) {
my $potential_file = File::Spec->catfile($dir, 'pl2bat.bat');
if ( -f $potential_file && !-d _ ) {
$cf->{pl2bat} = $potential_file;
last;
}
}
}

sub make_executable {
my $self = shift;
$self->SUPER::make_executable(@_);

my $perl = $self->perl;

my $pl2bat = $self->{config}{pl2bat};
my $pl2bat_args = '';

if ( defined($pl2bat) && length($pl2bat) ) {

foreach my $script (@_) {
next if $script =~ /\.(bat|cmd)$/i; # already a script; nothing to do

(my $script_bat = $script) =~ s/\.plx?$//i;
$script_bat .= '.bat'; # MSWin32 executable batch script file extension

my $quiet_state = $self->{properties}{quiet}; # keep quiet
if ( $script eq $self->build_script ) {
$self->{properties}{quiet} = 1;
$pl2bat_args =
q(-n "-x -S """%0""" "--build_bat" %*" ) .
q(-o "-x -S """%0""" "--build_bat" %1 %2 %3 %4 %5 %6 %7 %8 %9");
}

my $status = $self->do_system("$perl $pl2bat $pl2bat_args " .
"< $script > $script_bat");
$self->SUPER::make_executable($script_bat);

$self->{properties}{quiet} = $quiet_state; # restore quiet
}

} else {
warn <<"EOF";
Could not find 'pl2bat.bat' utility needed to make scripts executable.
Unable to convert scripts ( @{[join(', ', @_)]} ) to executables.
EOF
}
} }


sub ACTION_realclean { sub ACTION_realclean {
Expand All @@ -103,9 +30,10 @@ sub ACTION_realclean {
my $full_progname = $0; my $full_progname = $0;
$full_progname =~ s/(?:\.bat)?$/.bat/i; $full_progname =~ s/(?:\.bat)?$/.bat/i;


# Vodoo required to have a batch file delete itself without error;
# Syntax differs between 9x & NT: the later requires a null arg (???) # Syntax differs between 9x & NT: the later requires a null arg (???)
require Win32; require Win32;
my $null_arg = (Win32::GetOSVersion() == 2) ? '""' : ''; my $null_arg = (Win32::IsWinNT()) ? '""' : '';
my $cmd = qq(start $null_arg /min "\%comspec\%" /c del "$full_progname"); my $cmd = qq(start $null_arg /min "\%comspec\%" /c del "$full_progname");


my $fh = IO::File->new(">> $basename.bat") my $fh = IO::File->new(">> $basename.bat")
Expand All @@ -118,10 +46,114 @@ sub ACTION_realclean {
} }
} }


sub manpage_separator { sub make_executable {
return '.'; my $self = shift;

$self->SUPER::make_executable(@_);

foreach my $script (@_) {
my %opts = ();
if ( $script eq $self->build_script ) {
$opts{ntargs} = q(-x -S %0 --build_bat %*);
$opts{otherargs} = q(-x -S "%0" --build_bat %1 %2 %3 %4 %5 %6 %7 %8 %9);
}

my $out = eval {$self->pl2bat(in => $script, update => 1, %opts)};
if ( $@ ) {
$self->log_warn("WARNING: Unable to convert file '$script' to an executable script:\n$@");
} else {
$self->SUPER::make_executable($out);
}
}
}

# This routine was copied almost verbatim from the 'pl2bat' utility
# distributed with perl. It requires too much vodoo with shell quoting
# differences and shortcomings between the various flavors of Windows
# to reliably shell out
sub pl2bat {
my $self = shift;
my %opts = @_;

# NOTE: %0 is already enclosed in doublequotes by cmd.exe, as appropriate
$opts{ntargs} = '-x -S %0 %*' unless exists $opts{ntargs};
$opts{otherargs} = '-x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9' unless exists $opts{otherargs};

$opts{stripsuffix} = '/\\.plx?/' unless exists $opts{stripsuffix};
$opts{stripsuffix} = ($opts{stripsuffix} =~ m{^/([^/]*[^/\$]|)\$?/?$} ? $1 : "\Q$opts{stripsuffix}\E");

unless (exists $opts{out}) {
$opts{out} = $opts{in};
$opts{out} =~ s/$opts{stripsuffix}$//oi;
$opts{out} .= '.bat' unless $opts{in} =~ /\.bat$/i or $opts{in} =~ /^-$/;
}

my $head = <<EOT;
\@rem = '--*-Perl-*--
\@echo off
if "%OS%" == "Windows_NT" goto WinNT
perl $opts{otherargs}
goto endofperl
:WinNT
perl $opts{ntargs}
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
\@rem ';
EOT

$head =~ s/^\s+//gm;
my $headlines = 2 + ($head =~ tr/\n/\n/);
my $tail = "\n__END__\n:endofperl\n";

my $linedone = 0;
my $taildone = 0;
my $linenum = 0;
my $skiplines = 0;

my $start = $Config{startperl};
$start = "#!perl" unless $start =~ /^#!.*perl/;

my $in = IO::File->new("< $opts{in}") or die "Can't open $opts{in}: $!";
my @file = <$in>;
$in->close;

foreach my $line ( @file ) {
$linenum++;
if ( $line =~ /^:endofperl\b/ ) {
if (!exists $opts{update}) {
warn "$opts{in} has already been converted to a batch file!\n";
return;
}
$taildone++;
}
if ( not $linedone and $line =~ /^#!.*perl/ ) {
if (exists $opts{update}) {
$skiplines = $linenum - 1;
$line .= "#line ".(1+$headlines)."\n";
} else {
$line .= "#line ".($linenum+$headlines)."\n";
}
$linedone++;
}
if ( $line =~ /^#\s*line\b/ and $linenum == 2 + $skiplines ) {
$line = "";
}
}

my $out = IO::File->new("> $opts{out}") or die "Can't open $opts{out}: $!";
print $out $head;
print $out $start, ( $opts{usewarnings} ? " -w" : "" ),
"\n#line ", ($headlines+1), "\n" unless $linedone;
print $out @file[$skiplines..$#file];
print $out $tail unless $taildone;
$out->close;

return $opts{out};
} }



sub split_like_shell { sub split_like_shell {
# As it turns out, Windows command-parsing is very different from # As it turns out, Windows command-parsing is very different from
# Unix command-parsing. Double-quotes mean different things, # Unix command-parsing. Double-quotes mean different things,
Expand Down

0 comments on commit 26d8eb9

Please sign in to comment.