—package
Devel::FindPerl;
$Devel::FindPerl::VERSION
=
'0.016'
;
use
strict;
use
warnings;
our
@EXPORT_OK
=
qw/find_perl_interpreter perl_is_same/
;
our
%EXPORT_TAGS
= (
all
=> \
@EXPORT_OK
);
use
Config;
use
File::Spec::Functions
qw/catfile catdir rel2abs file_name_is_absolute updir curdir path splitpath/
;
my
%perl_for
;
sub
find_perl_interpreter {
my
$config
=
shift
||
'Devel::FindPerl::Config'
;
my
$key
=
$config
->can(
'serialize'
) ?
$config
->serialize :
''
;
$perl_for
{
$key
} ||= _discover_perl_interpreter(
$config
);
return
wantarray
? @{
$perl_for
{
$key
} } :
$perl_for
{
$key
}[0];
}
sub
_discover_perl_interpreter {
my
$config
=
shift
;
return
VMS::Filespec::vmsify($^X)
if
$^O eq
'VMS'
;
my
$perl_basename
= basename($^X);
my
@potential_perls
;
# Try 1, Check $^X for absolute and relative path
push
@potential_perls
, file_name_is_absolute($^X) ? [ $^X ] :
length
+(splitpath($^X))[1] ? [ rel2abs($^X) ] : ();
# Try 2, Last ditch effort: These two option use hackery to try to locate
# a suitable perl. The hack varies depending on whether we are running
# from an installed perl or an uninstalled perl in the perl source dist.
if
(
$ENV
{PERL_CORE}) {
# Try 3.A, If we are in a perl source tree, running an uninstalled
# perl, we can keep moving up the directory tree until we find our
# binary. We wouldn't do this under any other circumstances.
my
$perl_src
= _perl_src();
if
(
defined
(
$perl_src
) &&
length
(
$perl_src
)) {
my
$uninstperl
= catfile(
$perl_src
,
$perl_basename
);
# When run from the perl core, @INC will include the directories
# where perl is yet to be installed. We need to reference the
# absolute path within the source distribution where it can find
# it's Config.pm This also prevents us from picking up a Config.pm
# from a different configuration that happens to be already
# installed in @INC.
push
@potential_perls
, [
$uninstperl
,
'-I'
. catdir(
$perl_src
,
'lib'
) ];
}
}
else
{
# Try 2.B, First look in $Config{perlpath}, then search the user's
# PATH. We do not want to do either if we are running from an
# uninstalled perl in a perl source tree.
push
@potential_perls
, [
$config
->get(
'perlpath'
) ];
push
@potential_perls
,
map
{ [ catfile(
$_
,
$perl_basename
) ] } path();
}
@potential_perls
=
grep
{ !tainted(
$_
->[0]) }
@potential_perls
;
# Now that we've enumerated the potential perls, it's time to test
# them to see if any of them match our configuration, returning the
# absolute path of the first successful match.
my
$exe
=
$config
->get(
'exe_ext'
);
foreach
my
$thisperl
(
@potential_perls
) {
$thisperl
->[0] .=
$exe
if
length
$exe
and
$thisperl
->[0] !~ m/\Q
$exe
\E$/i;
return
$thisperl
if
-f
$thisperl
->[0] && perl_is_same(@{
$thisperl
});
}
# We've tried all alternatives, and didn't find a perl that matches
# our configuration. Throw an exception, and list alternatives we tried.
my
@paths
=
map
{ dirname(
$_
->[0]) }
@potential_perls
;
die
"Can't locate the perl binary used to run this script in (@paths)\n"
;
}
# if building perl, perl's main source directory
sub
_perl_src {
# N.B. makemaker actually searches regardless of PERL_CORE, but
# only squawks at not finding it if PERL_CORE is set
return
unless
$ENV
{PERL_CORE};
my
$updir
= updir;
my
$dir
= curdir;
# Try up to 10 levels upwards
for
(0..10) {
if
(
-f catfile(
$dir
,
"config_h.SH"
)
&&
-f catfile(
$dir
,
"perl.h"
)
&&
-f catfile(
$dir
,
"lib"
,
"Exporter.pm"
)
) {
return
realpath(
$dir
);
}
$dir
= catdir(
$dir
,
$updir
);
}
carp
"PERL_CORE is set but I can't find your perl source!\n"
;
return
;
}
sub
perl_is_same {
my
@perl
=
@_
;
return
lc
_capture_command(
@perl
,
qw(-MConfig=myconfig -e print -e myconfig)
) eq
lc
Config->myconfig;
}
sub
_capture_command {
my
(
@command
) =
@_
;
local
@ENV
{
qw/PATH IFS CDPATH ENV BASH_ENV/
};
delete
@ENV
{
qw/PATH IFS CDPATH ENV BASH_ENV/
};
my
$pid
= open2(
my
(
$in
,
$out
),
@command
);
binmode
$in
,
':crlf'
if
$^O eq
'MSWin32'
;
my
$ret
=
do
{
local
$/; <
$in
> };
waitpid
$pid
, 0;
return
$ret
;
}
sub
Devel::FindPerl::Config::get {
my
(
$self
,
$key
) =
@_
;
return
$Config
{
$key
};
}
1;
#ABSTRACT: Find the path to your perl
__END__
=pod
=encoding UTF-8
=head1 NAME
Devel::FindPerl - Find the path to your perl
=head1 VERSION
version 0.016
=head1 SYNOPSIS
use Devel::FindPerl 'find_perl_interpreter';
system find_perl_interpreter, '-e', '...';
=head1 DESCRIPTION
This module tries to find the path to the currently running perl. It (optionally) exports the following functions:
=head1 FUNCTIONS
=head2 find_perl_interpreter($config = ExtUtils::Config->new)
This function will try really really hard to find the path to the perl running your program. I should be able to find it in most circumstances. Note that the result of this function will be cached for any serialized value of C<$config>. It will return a list that usually but not necessarily is containing one element; additional elements are arguments that must be passed to that perl for correct functioning.
=head2 perl_is_same($path, @arguments)
Tests if the perl in C<$path> is the same perl as the currently running one.
=head1 SECURITY
This module by default does things that are not particularly secure (run programs based on external input). In tainted mode, it will try to avoid any insecure action, but that may affect its ability to find the perl executable.
=head1 SEE ALSO
=over 4
=item * Probe::Perl
This module has much the same purpose as Probe::Perl, in fact the algorithm is mostly the same as both are extracted from L<Module::Build> at different points in time. If I had known about it when I extracted it myself, I probably wouldn't have bothered, but now that I do have it there are a number of reasons for me to prefer Devel::FindPerl over Probe::Perl
=over 4
=item * Separation of concerns. P::P does 4 completely different things (finding perl, managing configuration, categorizing platorms and formatting a perl version. Devel::FindPerl is instead complemented by modules such as L<ExtUtils::Config> and L<Perl::OSType>.
=item * It handles tainting better. In particular, C<find_perl_interpreter> never returns a tainted value, even in tainted mode.
=item * It was written with inclusion in core in mind, though the removal of Module::Build from core after perl 5.20 may make this point moot.
=back
=back
=head1 AUTHOR
Leon Timmermans <leont@cpan.org>, Randy Sims <randys@thepierianspring.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2012 by Randy Sims, Leon Timmermans.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut