use strict;
use File::Spec;
use Config;
+use Cwd ();
+
+our $VERSION = "1.006";
my @alt_transfer = qw/altname incsuffix libbase/;
$req->{altname} ||= "main";
$result = _probe_check($req);
}
+
+ if ($result && $req->{testcode}) {
+ $result = _probe_test($req, $result);
+ }
+
if (!$result && $req->{alternatives}) {
ALTCHECK:
my $index = 1;
for my $key (@alt_transfer) {
exists $alt->{$key} and $work{$key} = $alt->{$key};
}
- $result = _probe_check(\%work)
+ $result = _probe_check(\%work);
+
+ if ($result && $req->{testcode}) {
+ $result = _probe_test(\%work, $result);
+ }
+
+ $result
and last;
+
++$index;
}
}
if (!$result && $req->{testcode}) {
$result = _probe_fake($req);
- }
- $result or return;
- if ($req->{testcode}) {
+ $result or return;
+
$result = _probe_test($req, $result);
}
chomp $cflags;
chomp $lflags;
print "$req->{name}: Found via pkg-config $pkg\n";
+ print <<EOS if $req->{verbose};
+ cflags: $cflags
+ defines: $defines
+ lflags: $lflags
+EOS
+ # rt 75869
+ # if Win32 doesn't provide this information, too bad
+ if (!grep(/^-L/, split " ", $lflags)
+ && $^O ne 'MSWin32') {
+ # pkg-config told us about the library, make sure it's
+ # somewhere EU::MM can find it
+ print "Checking if EU::MM can find $lflags\n" if $req->{verbose};
+ my ($extra, $bs_load, $ld_load, $ld_run_path) =
+ ExtUtils::Liblist->ext($lflags, $req->{verbose});
+ unless ($ld_run_path) {
+ # search our standard places
+ $lflags = _resolve_libs($req, $lflags);
+ }
+ }
+
return
{
INC => $cflags,
return;
}
+sub _is_msvc {
+ return $Config{cc} eq "cl";
+}
+
+sub _lib_basename {
+ my ($base) = @_;
+
+ if (_is_msvc()) {
+ return $base;
+ }
+ else {
+ return "lib$base";
+ }
+}
+
+sub _lib_option {
+ my ($base) = @_;
+
+ if (_is_msvc()) {
+ return $base . $Config{_a};
+ }
+ else {
+ return "-l$base";
+ }
+}
+
+sub _quotearg {
+ my ($opt) = @_;
+
+ return $opt =~ /\s/ ? qq("$opt") : $opt;
+}
+
sub _probe_check {
my ($req) = @_;
- my $libcheck = $req->{libcheck};
- my $libbase = $req->{libbase};
- if (!$libcheck && $req->{libbase}) {
- # synthesize a libcheck
+ my @libcheck;
+ my @libbase;
+ if ($req->{libcheck}) {
+ if (ref $req->{libcheck} eq "ARRAY") {
+ push @libcheck, @{$req->{libcheck}};
+ }
+ else {
+ push @libcheck, $req->{libcheck};
+ }
+ }
+ elsif ($req->{libbase}) {
+ @libbase = ref $req->{libbase} ? @{$req->{libbase}} : $req->{libbase};
+
my $lext=$Config{'so'}; # Get extensions of libraries
my $aext=$Config{'_a'};
- $libcheck = sub {
- -e File::Spec->catfile($_[0], "lib$libbase$aext")
- || -e File::Spec->catfile($_[0], "lib$libbase.$lext")
- };
+
+ for my $libbase (@libbase) {
+ my $basename = _lib_basename($libbase);
+ push @libcheck, sub {
+ -e File::Spec->catfile($_[0], "$basename$aext")
+ || -e File::Spec->catfile($_[0], "$basename.$lext")
+ };
+ }
+ }
+ else {
+ print "$req->{name}: No libcheck or libbase, nothing to search for\n"
+ if $req->{verbose};
+ return;
}
- my $found_libpath;
+ my @found_libpath;
my @lib_search = _lib_paths($req);
print "$req->{name}: Searching directories for libraries:\n"
if $req->{verbose};
- for my $path (@lib_search) {
- print "$req->{name}: $path\n" if $req->{verbose};
- if ($libcheck->($path)) {
- print "$req->{name}: Found!\n" if $req->{verbose};
- $found_libpath = $path;
- last;
+ for my $libcheck (@libcheck) {
+ for my $path (@lib_search) {
+ print "$req->{name}: $path\n" if $req->{verbose};
+ if ($libcheck->($path)) {
+ print "$req->{name}: Found!\n" if $req->{verbose};
+ push @found_libpath, $path;
+ last;
+ }
}
}
$alt = " $req->{altname}:";
}
print "$req->{name}:$alt includes ", $found_incpath ? "" : "not ",
- "found - libraries ", $found_libpath ? "" : "not ", "found\n";
+ "found - libraries ", @found_libpath == @libcheck ? "" : "not ", "found\n";
- $found_libpath && $found_incpath
+ @found_libpath == @libcheck && $found_incpath
or return;
- my @libs = "-L$found_libpath";
+ my @libs = map "-L$_", @found_libpath;
if ($req->{libopts}) {
push @libs, $req->{libopts};
}
- elsif ($libbase) {
- push @libs, "-l$libbase";
+ elsif (@libbase) {
+ push @libs, map _lib_option($_), @libbase;
}
else {
die "$req->{altname}: inccheck but no libbase or libopts";
return
{
- INC => "-I$found_incpath",
- LIBS => "@libs",
+ INC => _quotearg("-I$found_incpath"),
+ LIBS => join(" ", map _quotearg($_), @libs),
DEFINE => "",
};
}
elsif (defined $req->{libbase}) {
# might not need extra libraries, eg. Win32 perl already links
# everything
- $lopts = $req->{libbase} ? "-l$req->{libbase}" : "";
+ my @libs = $req->{libbase}
+ ? ( ref $req->{libbase} ? @{$req->{libbase}} : $req->{libbase} )
+ : ();
+ $lopts = join " ", map _lib_option($_), @libs;
}
if (defined $lopts) {
print "$req->{name}: Checking if the compiler can find them on its own\n";
require Devel::CheckLib;
# setup LD_RUN_PATH to match link time
+ print "Asking liblist for LD_RUN_PATH:\n" if $req->{verbose};
my ($extra, $bs_load, $ld_load, $ld_run_path) =
ExtUtils::Liblist->ext($result->{LIBS}, $req->{verbose});
local $ENV{LD_RUN_PATH};
print "Setting LD_RUN_PATH=$ld_run_path for $req->{name} probe\n"
if $req->{verbose};
$ENV{LD_RUN_PATH} = $ld_run_path;
+ if ($Config{lddlflags} =~ /([^ ]*-(?:rpath|R)[,=]?)([^ ]+)/
+ && -d $2) {
+ # hackety, hackety
+ # LD_RUN_PATH is ignored when there's already an -rpath option
+ # so provide one
+ my $prefix = $1;
+ $result->{LDDLFLAGS} = $Config{lddlflags} . " " .
+ join " ", map "$prefix$_", split $Config{path_sep}, $ld_run_path;
+ }
}
my $good =
Devel::CheckLib::check_lib
return $result;
}
+sub _resolve_libs {
+ my ($req, $lflags) = @_;
+
+ my @libs = grep /^-l/, split ' ', $lflags;
+ my %paths;
+ my @paths = _lib_paths($req);
+ my $so = $Config{so};
+ my $libext = $Config{_a};
+ for my $lib (@libs) {
+ $lib =~ s/^-l/lib/;
+
+ for my $path (@paths) {
+ if (-e "$path/$lib.$so" || -e "$path/$lib$libext") {
+ $paths{$path} = 1;
+ }
+ }
+ }
+
+ return join(" ", ( map "-L$_", keys %paths ), $lflags );
+}
+
sub _lib_paths {
my ($req) = @_;
+ print "$req->{name} IM_LIBPATH: $ENV{IM_LIBPATH}\n"
+ if $req->{verbose} && defined $ENV{IM_LIBPATH};
+ print "$req->{name} LIB: $ENV{IM_LIBPATH}\n"
+ if $req->{verbose} && defined $ENV{LIB} && $^O eq "MSWin32";
+ my $lp = $req->{libpath};
+ print "$req->{name} libpath: ", ref $lp ? join($Config{path_sep}, @$lp) : $lp, "\n"
+ if $req->{verbose} && defined $lp;
+
return _paths
(
$ENV{IM_LIBPATH},
$^O eq "cygwin" ? "/usr/lib/w32api" : "",
"/usr/lib",
"/usr/local/lib",
+ _gcc_lib_paths(),
+ _dyn_lib_paths(),
);
}
+sub _gcc_lib_paths {
+ $Config{gccversion}
+ or return;
+
+ my ($base_version) = $Config{gccversion} =~ /^([0-9]+)/
+ or return;
+
+ $base_version >= 4
+ or return;
+
+ local $ENV{LANG} = "C";
+ local $ENV{LC_ALL} = "C";
+ my ($lib_line) = grep /^libraries:/, `$Config{cc} -print-search-dirs`
+ or return;
+ $lib_line =~ s/^libraries: =//;
+ chomp $lib_line;
+
+ return grep !/gcc/ && -d, split /:/, $lib_line;
+}
+
+sub _dyn_lib_paths {
+ return map { defined() ? split /\Q$Config{path_sep}/ : () }
+ map $ENV{$_},
+ qw(LD_RUN_PATH LD_LIBRARY_PATH DYLD_LIBRARY_PATH LIBRARY_PATH);
+}
+
sub _inc_paths {
my ($req) = @_;
+ print "$req->{name} IM_INCPATH: $ENV{IM_INCPATH}\n"
+ if $req->{verbose} && defined $ENV{IM_INCPATH};
+ print "$req->{name} INCLUDE: $ENV{INCLUDE}\n"
+ if $req->{verbose} && defined $ENV{INCLUDE} && $^O eq "MSWin32";
+ my $ip = $req->{incpath};
+ print "$req->{name} incpath: ", ref $ip ? join($Config{path_sep}, @$ip) : $ip, "\n"
+ if $req->{verbose} && defined $req->{incpath};
+
my @paths = _paths
(
$ENV{IM_INCPATH},
),
"/usr/include",
"/usr/local/include",
+ _gcc_inc_paths(),
+ _dyn_inc_paths(),
);
if ($req->{incsuffix}) {
return @paths;
}
+sub _gcc_inc_paths {
+ $Config{gccversion}
+ or return;
+
+ my ($base_version) = $Config{gccversion} =~ /^([0-9]+)/
+ or return;
+
+ $base_version >= 4
+ or return;
+
+ local $ENV{LANG} = "C";
+ local $ENV{LC_ALL} = "C";
+ my $devnull = File::Spec->devnull;
+ my @spam = `$Config{cc} -E -v - <$devnull 2>&1`;
+ # output includes lines like:
+ # ...
+ # ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../x86_64-linux-gnu/include"
+ # #include "..." search starts here:
+ # #include <...> search starts here:
+ # /usr/lib/gcc/x86_64-linux-gnu/4.9/include
+ # /usr/local/include
+ # /usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed
+ # /usr/include/x86_64-linux-gnu
+ # /usr/include
+ # End of search list.
+ # # 1 "<stdin>"
+ # # 1 "<built-in>"
+ # ...
+
+ while (@spam && $spam[0] !~ /^#include /) {
+ shift @spam;
+ }
+ my @inc;
+ while (@spam && $spam[0] !~ /^End of search/) {
+ my $line = shift @spam;
+ chomp $line;
+ next if $line =~ /^#include /;
+ next unless $line =~ s/^\s+//;
+ push @inc, $line;
+ }
+ return @inc;
+}
+
+sub _dyn_inc_paths {
+ return map {
+ my $tmp = $_;
+ $tmp =~ s/\blib$/include/ ? $tmp : ()
+ } _dyn_lib_paths();
+}
+
sub _paths {
my (@in) = @_;
push @out, grep -d $_, split /\Q$Config{path_sep}/, $path;
}
+ @out = map Cwd::realpath($_), @out;
+
+ my %seen;
+ @out = grep !$seen{$_}++, @out;
+
return @out;
}
C<libcheck> - a code reference that checks if the supplied library
directory contains the required library files. Note: the
F<Makefile.PL> version of this was supplied all of the library file
-names instead.
+names instead. C<libcheck> can also be an arrayref of library check
+code references, all of which must find a match for the library to be
+considered "found".
=item *
C<libbase> - if C<inccheck> is supplied, but C<libcheck> isn't, then a
C<libcheck> that checks for C<lib>I<libbase>I<$Config{_a}> and
C<lib>I<libbase>.I<$Config{so}> is created. If C<libopts> isn't
-supplied then that can be synthesized as C<-l>C<<I<libbase>>>.
+supplied then that can be synthesized as C<< -lI<libbase>
+>>. C<libbase> can also be an arrayref of library base names to search
+for, in which case all of the libraries mentioned must be found for
+the probe to succeed.
=item *
C<libpath> - C<$Config{path_sep}> separated list of library file
directories to check, or a reference to an array of such.
+=item *
+
+C<alternatives> - an optional array reference of alternate
+configurations (as hash references) to test if the primary
+configuration isn't successful. Each alternative should include an
+C<altname> key describing the alternative. Any key not mentioned in
+an alternative defaults to the value from the main configuration.
+
=back
+=head1 AUTHOR
+
+Tony Cook <tonyc@cpan.org>, Arnar M. Hrafnkelsson
+
=cut