Makefile.PL   [plain text]


use strict;
use warnings;

use Config;
use ExtUtils::MakeMaker;
use File::Basename qw(dirname);
use Getopt::Long;

eval "use ExtUtils::MakeMaker::Coverage";
$@ or print "Adding testcover target\n";

use vars qw($opt_default $opt_libpath $opt_static);

GetOptions(
    "default", \$opt_default,
    "lib=s",   \$opt_libpath,
    "static",  \$opt_static,
);

$opt_default ||= $ENV{CRYPT_SSLEAY_DEFAULT};

# FIND POSSIBLE SSL INSTALLATIONS

my @POSSIBLE_SSL_DIRS;
if ($opt_libpath) {
    # explicit from command-line
    @POSSIBLE_SSL_DIRS = ($opt_libpath);
    $opt_default       = 1;
}
elsif ($^O eq 'MSWin32') {
    @POSSIBLE_SSL_DIRS = 'c:\\openssl';
}
elsif ($^O eq 'VMS') {
    @POSSIBLE_SSL_DIRS = '/ssl$root';
}
else {
    # Unix and the rest
    @POSSIBLE_SSL_DIRS = qw(
        /local
        /local/ssl
        /opt/ssl
        /usr
        /usr/local
        /usr/local/ssl
        /usr/local/openssl
    );
}

my @CANDIDATE = do {
    my %seen;
    grep {!$seen{$_->{check}}++}
        map {Candidate($_)}
            @POSSIBLE_SSL_DIRS
};

if (@CANDIDATE == 0) {
    $opt_default = 0;
    my $bar = '=' x 72;
    print <<"INFO";
$bar
No installed SSL libraries found in any of the following places.
INFO
    print "    $_\n" for @POSSIBLE_SSL_DIRS;
    print <<"INFO";
You will have to either specify a directory location at the following
prompt, or rerun the Makefile.PL program and use the --lib switch
to specify the path. If the path in question is considered standard
on your platform, please consider filing a bug report in order to
have it taken into account in a subsequent version of Crypt::SSLeay.

INFO

    if (-f '/etc/debian_version') {
        print <<"DEBIAN_INFO";

This host looks like it is running Debian. Crypt::SSLeay needs to
be compiled with C headers that the libssl-dev package makes
available. Please install that package before trying to build this
module. (You can always deinstall the package afterwards, once
Crypt::SSLeay has been built).
DEBIAN_INFO
    }
}

my $SSL_DIR;
my $pkg_config;

if (@CANDIDATE == 1) {
    $pkg_config = $CANDIDATE[0];
    if ($opt_default) {
        $SSL_DIR = $pkg_config->{dir};
    }
    else {
        print <<"INFO";
=======================================================
Only one $pkg_config->{type} installation found at $pkg_config->{dir}
Consider running 'perl Makefile.PL --default' the next
time Crypt::SSLeay is upgraded to select this directory
automatically thereby avoiding the following prompt.
=======================================================
INFO
    }
}
elsif (@CANDIDATE > 1) {
    print "Found multiple possibilities for OpenSSL\n";
    for my $c (@CANDIDATE) {
        print "  $c->{dir} ($c->{type} $c->{ver})\n";
    }
}

if (not $SSL_DIR) {
    my %cand;
    for my $c (@CANDIDATE) {
        $cand{$c->{dir}} = {%$c};
    }

    $SSL_DIR = prompt "Which SSL install path do you want to use?",
        $CANDIDATE[0]->{dir};

    if (exists $cand{$SSL_DIR}) {
        # we've already determined that this directory is usable
        $pkg_config = $cand{$SSL_DIR};
    }
    else {
        # unknown directory, better check it out
        $pkg_config = Candidate($SSL_DIR);
        if (not $pkg_config) {
            die <<"INFO";
$SSL_DIR does not appear to be an SSL library installation, since
the required header files were not found. The build cannot proceed.
INFO
        }
    }
}

# note: $SSL_DIR is now sane at this point

my (@INC_FLAGS, @LIB_FLAGS);

if ($^O eq 'VMS') {
    push @INC_FLAGS, "-I$pkg_config->{inc}";
    push @LIB_FLAGS, qw(-L/SYS$SHARE -lSSL$LIBSSL_SHR32 -lSSL$LIBCRYPTO_SHR32);
}
elsif ($^O eq 'MSWin32') {
    # external tools probably expect \ and not / for path separators
    $SSL_DIR =~ tr{/}{\\};

    # default to drive C: if no drive or relative path
    $SSL_DIR = "c:$SSL_DIR" if $SSL_DIR !~ m{\A(?:[a-z]:|\.\.[\\/])}i;

    my $inc = $pkg_config->{inc};
    $inc =~ tr{/}{\\};
    $inc = "c:$inc" if $inc !~ m{\A(?:[a-z]:|\.\.[\\/])}i;
    push @INC_FLAGS, "-I$inc";
    push @INC_FLAGS, "-I$SSL_DIR\\inc32" if -d "$SSL_DIR/inc32";

    my $vanilla = $Config{cc} eq 'gcc' ? 1 : 0;
    $vanilla and print "Assuming Vanilla/Strawberry Perl installation\n";
    
    if ($vanilla and -d "$SSL_DIR\\lib\\MinGW") {
        push @LIB_FLAGS, "-L$SSL_DIR\\lib\\MinGW";
    }
    elsif(-d "$SSL_DIR/lib") {
        push @LIB_FLAGS, "-L$SSL_DIR\\lib";
    }
    else {
        my $dir = $opt_static ? "$SSL_DIR\\out32" : "$SSL_DIR\\out32dll";
        if (-d $dir) {
            push @LIB_FLAGS, "-L$dir";
        }
        else {
            # Allow developers to point at OpenSSL source...
            push @LIB_FLAGS, "-L$SSL_DIR";
        }
    }

    push @LIB_FLAGS, qw(-lssleay32 -llibeay32);
    push @LIB_FLAGS, qw(-lRSAglue -lrsaref) if $pkg_config->{type} ne 'OpenSSL';
}
else {
    push @INC_FLAGS, "-I$pkg_config->{inc}";

    push @LIB_FLAGS, "-L$pkg_config->{lib}", qw(-lssl -lcrypto -lgcc);
    push @LIB_FLAGS, qw(-lRSAglue -lrsaref) if $pkg_config->{type} ne 'OpenSSL';

    # ccc on alpha support
    if ($^O eq 'linux'
        and `uname -m` =~ /alpha/
        and !(system("nm $SSL_DIR/lib/libssl.a|grep -q 'U _Ots'")>>8)
    ) {
        push @LIB_FLAGS, '-lots'; 
    }

    # this fix was suggested for building on RedHat 9
    push @INC_FLAGS, '-I/usr/kerberos/include' if -d '/usr/kerberos/include';
}

# write include file that determines ssl support
# we need to include crypto.h for SSLeay so the version gets picked up in SSLeay.xs

open(INCLUDE, ">crypt_ssleay_version.h") || die("can't open crypt_ssleay_version.h for writing: $!");
print INCLUDE <<"INFO";
#include "$pkg_config->{prefix}ssl.h"
#include "$pkg_config->{prefix}crypto.h"
#include "$pkg_config->{prefix}err.h"
#include "$pkg_config->{prefix}rand.h"
#include "$pkg_config->{prefix}pkcs12.h"

INFO

if ($] < 5.005) {
    print "adding PL_sv_undef symbol for this ancient perl installation";
    print INCLUDE <<"INFO";
/* defining PL_sv_undef for very old perls ($]) */
#ifndef PL_sv_undef
#define PL_sv_undef sv_undef
#endif

INFO
}

if ($pkg_config->{type} eq 'OpenSSL') {
    # OPENSSL_free defined in OpenSSL 0.9.6 and higher
    if ($pkg_config->{ver} =~ /\b0\.9\.[2-5]/) {
        print INCLUDE "#define CRYPT_SSLEAY_free free\n";
    }
    else {
        print INCLUDE "#define CRYPT_SSLEAY_free OPENSSL_free\n";
    }
}
else {
    print INCLUDE "#define CRYPT_SSLEAY_free free\n";
}

close INCLUDE or die "Cannot close crypt_ssleay_version.h for output: $!\n";

print <<"INFO";

BUILD INFORMATION
================================================
ssl library: $pkg_config->{type} $pkg_config->{ver} in $SSL_DIR
ssl header:  $pkg_config->{prefix}ssl.h
libraries:   @LIB_FLAGS
include dir: @INC_FLAGS
================================================
INFO

my @license =
    do {
        my $version = $ExtUtils::MakeMaker::VERSION;
        $version =~ tr/_//d;
        $version} > 6.30
    ? qw(LICENSE perl)
    : ();

WriteMakefile(
    NAME          => 'Crypt::SSLeay',
    AUTHOR        => 'David Landgren',
    ABSTRACT_FROM => 'SSLeay.pm',
    VERSION_FROM  => 'SSLeay.pm',
    LIBS          => ["@LIB_FLAGS"],
    INC           => "@INC_FLAGS",
    NEEDS_LINKING => 1,
    clean         => {
        FILES => 'crypt_ssleay_version.h test.config',
    },
    @license,
);

if (open OUT, '> test.config') {
    print OUT <<"INFO";
ssl $pkg_config->{type} $pkg_config->{ver} in $SSL_DIR
lib @LIB_FLAGS
inc @INC_FLAGS
cc $Config{cc}
INFO

    print <<"INFO";
The test suite can attempt to connect to public servers
to ensure that the code is working properly. If you are
behind a strict firewall or have no network connectivity,
these tests may fail (through no fault of the code).

INFO
    my $network_tests = prompt
        "Do you want to run the live tests (y/N) ?",
        'N';
    print OUT "network_tests ", ($network_tests =~ /y/i) ? 1 : 0, "\n";
    close OUT;
}

## HELPERS

sub Candidate {
    my $dir = shift;
    return unless -d $dir;

    my $inc_dir;
    my $version_file;
    for (
         "$dir/inc32/openssl/opensslv.h", # old win32 builds
         "$dir/crypto/opensslv.h", # cygwin32 builds
         "$dir/include/openssl/opensslv.h", 
         "$dir/../include/openssl/opensslv.h", # Solaris
         "$dir/include/opensslv.h", 
         "$dir/include/crypto.h"
    ) {
        if (-e $_) {
            $version_file = $_;
            last;
        } 
    }
    return unless defined $version_file;
    my $fingerprint = join(':', (stat $version_file)[0,1]);

    my $open_ssl = ($version_file =~ /openssl/i) ? 1 : 0;
    $inc_dir = dirname($version_file);
    return unless -e "$inc_dir/ssl.h";

    my $prefix;
    if ($^O eq 'MSWin32') {
        $inc_dir =~ s{[\\/]openssl\z}{};
        $prefix  = 'openssl/';
    }
    elsif (index($inc_dir, '/../') > -1) {
        # OpenSSL include directory is in a sibling directory
        $inc_dir =~ s{\/openssl\z}{};
        $prefix  = 'openssl/';
    }
    else {
        $prefix = ($inc_dir =~ /\bopenssl/i) ? 'openssl/' : '';
    }

    open(VERSION_FILE, $version_file) or return;
    my $version_match = $open_ssl ? "OPENSSL_VERSION_NUMBER" : "SSLEAY_VERSION_NUMBER";
    my $version;
    my $type;
    while (<VERSION_FILE>) {
        if (/^#define\s+$version_match\s+0x0+(\d\d\d)/) {
            $version = $1;
            $version =~ s/(\d)0(\d)/$1$2/;            
            $type = ($version > 92) ? "OpenSSL" : "SSLeay";
            $version = join('.', split(//, "0$version"));
            last;
        }
    }
    close(VERSION_FILE);

    # Silly test to look for the library files
    my $found_lib;
    my $libd;
    my $subdir = $opt_static ? 'out32' : 'out32dll';
    if (-d "$dir/$subdir") {
        $libd = [$subdir];
    }
    elsif ($^O eq 'MSWin32' and $Config{cc} eq 'gcc') {
        $libd = ['lib/MinGW'];
    }
    else {
        # second attempt is for Solaris, like the include directory, the
        # library directory may be in a sibling directory
        $libd = ['lib', '../lib'];
    }

    SCAN:
    for my $d (@$libd) {
        my $lib_dir = "$dir/$d";
        if (opendir(LIBDIR, $lib_dir)) {
            while (defined($_ = readdir(LIBDIR))) {
                if (/\A(?:lib(?:crypto|eay32|ssl)|ssleay32)/) {
                    $found_lib = $lib_dir;
                    last SCAN;
                }
            }
            closedir(LIBDIR);
        }
    }

    if (!$found_lib) {
        my @tried = join( ',' => map {"$dir/$_"} @$libd);
        print "Did not locate expected SSL library files in @tried\n";
    }

    return {
        dir    => $dir,
        inc    => $inc_dir,
        lib    => $found_lib,
        ver    => $version,
        type   => $type,
        prefix => $prefix,
        check  => $fingerprint,
    };
}