blob: 19692a836ac55078a051590ebe509641821572cf [file] [log] [blame]
# please insert nothing before this line: -*- mode: cperl; cperl-indent-level: 4; cperl-continued-statement-offset: 4; indent-tabs-mode: nil -*-
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package Apache2::Build;
use 5.006;
use strict;
use warnings;
use Config;
use Cwd ();
use File::Spec::Functions qw(catfile catdir canonpath rel2abs devnull
catpath splitpath);
use File::Basename;
use ExtUtils::Embed ();
use File::Copy ();
BEGIN { # check for a sane ExtUtils::Embed
unless ($ENV{MP_USE_MY_EXTUTILS_EMBED}) {
my ($version, $path)=(ExtUtils::Embed->VERSION,
$INC{q{ExtUtils/Embed.pm}});
my $msg=<<"EOF";
I have found ExtUtils::Embed $version at
$path
This is probably not the right one for this perl version. Please make sure
there is only one version of this module installed and that it is the one
that comes with this perl version.
If you insist on using the ExtUtils::Embed as is set the environment
variable MP_USE_MY_EXTUTILS_EMBED=1 and try again.
EOF
if (eval {require Module::CoreList}) {
my $req=$Module::CoreList::version{$]}->{q/ExtUtils::Embed/};
die "Please repair your Module::CoreList" unless $req;
unless ($version eq $req) {
$msg.=("Details: expecting ExtUtils::Embed $req ".
"(according to Module::CoreList)\n\n");
die $msg;
}
}
else {
my $req=$Config{privlib}.'/ExtUtils/Embed.pm';
unless ($path eq $req) {
$msg.="Details: expecting ExtUtils::Embed at $req\n\n";
die $msg;
}
}
}
}
use constant IS_MOD_PERL_BUILD => grep
{ -e "$_/Makefile.PL" && -e "$_/lib/mod_perl2.pm" } qw(. ..);
use constant AIX => $^O eq 'aix';
use constant DARWIN => $^O eq 'darwin';
use constant CYGWIN => $^O eq 'cygwin';
use constant IRIX => $^O eq 'irix';
use constant HPUX => $^O eq 'hpux';
use constant OPENBSD => $^O eq 'openbsd';
use constant WIN32 => $^O eq 'MSWin32';
use constant MSVC => WIN32() && ($Config{cc} eq 'cl');
use constant DMAKE => WIN32() && ($Config{make} eq 'dmake');
use constant REQUIRE_ITHREADS => grep { $^O eq $_ } qw(MSWin32);
use constant PERL_HAS_ITHREADS =>
$Config{useithreads} && ($Config{useithreads} eq 'define');
use constant BUILD_APREXT => WIN32() || CYGWIN();
use ModPerl::Code ();
use ModPerl::BuildOptions ();
use Apache::TestTrace;
use Apache::TestConfig ();
our $VERSION = '0.01';
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
my $name = uc ((split '::', $AUTOLOAD)[-1]);
unless ($name =~ /^MP_/) {
die "no such method: $AUTOLOAD";
}
unless ($self->{$name}) {
return wantarray ? () : undef;
}
return wantarray ? (split /\s+/, $self->{$name}) : $self->{$name};
}
#--- apxs stuff ---
our $APXS;
my %apxs_query = (
INCLUDEDIR => 'include',
LIBEXECDIR => 'modules',
CFLAGS => undef,
PREFIX => '',
);
sub ap_prefix_invalid {
my $self = shift;
my $prefix = $self->{MP_AP_PREFIX};
unless (-d $prefix) {
return "$prefix: No such file or directory";
}
my $include_dir = $self->apxs(-q => 'INCLUDEDIR');
unless (-d $include_dir) {
return "include/ directory not found in $prefix";
}
return '';
}
sub httpd_is_source_tree {
my $self = shift;
return $self->{httpd_is_source_tree}
if exists $self->{httpd_is_source_tree};
my $prefix = $self->dir;
$self->{httpd_is_source_tree} =
defined $prefix && -d $prefix && -e "$prefix/CHANGES";
}
# try to find the apxs utility, set $self->{MP_APXS} to the path if found,
# otherwise to ''
sub find_apxs_util {
my $self = shift;
if (not defined $self->{MP_APXS}) {
$self->{MP_APXS} = ''; # not found
}
my @trys = ($Apache2::Build::APXS,
$self->{MP_APXS},
$ENV{MP_APXS});
push @trys, catfile $self->{MP_AP_PREFIX}, 'bin', 'apxs'
if exists $self->{MP_AP_PREFIX};
if (WIN32) {
my $ext = '.bat';
for (@trys) {
$_ .= $ext if ($_ and $_ !~ /$ext$/);
}
}
unless (IS_MOD_PERL_BUILD) {
#if we are building mod_perl via apxs, apxs should already be known
#these extra tries are for things built outside of mod_perl
#e.g. libapreq
# XXX: this may pick a wrong apxs version!
push @trys,
Apache::TestConfig::which('apxs'),
'/usr/local/apache/bin/apxs';
}
my $apxs_try;
for (@trys) {
next unless ($apxs_try = $_);
chomp $apxs_try;
if (-x $apxs_try) {
$self->{MP_APXS} = $apxs_try;
last;
}
}
}
# if MP_AP_DESTDIR was specified this sub will prepend this path to
# any Apache-specific installation path (that option is used only by
# package maintainers).
sub ap_destdir {
my $self = shift;
my $path = shift || '';
return $path unless $self->{MP_AP_DESTDIR};
if (WIN32) {
my ($dest_vol, $dest_dir) = splitpath $self->{MP_AP_DESTDIR}, 1;
my $real_dir = (splitpath $path)[1];
$path = catpath $dest_vol, catdir($dest_dir, $real_dir), '';
}
else {
$path = catdir $self->{MP_AP_DESTDIR}, $path;
}
return canonpath $path;
}
sub apxs {
my $self = shift;
$self->find_apxs_util() unless defined $self->{MP_APXS};
my $is_query = (@_ == 2) && ($_[0] eq '-q');
$self = $self->build_config unless ref $self;
my $query_key;
if ($is_query) {
$query_key = 'APXS_' . uc $_[1];
if (exists $self->{$query_key}) {
return $self->{$query_key};
}
}
unless ($self->{MP_APXS}) {
my $prefix = $self->{MP_AP_PREFIX} || "";
return '' unless -d $prefix and $is_query;
my $val = $apxs_query{$_[1]};
return defined $val ? ($val ? "$prefix/$val" : $prefix) : "";
}
my $devnull = devnull();
my $val = qx($self->{MP_APXS} @_ 2>$devnull);
chomp $val if defined $val;
unless ($val) {
# do we have an error or is it just an empty value?
my $error = qx($self->{MP_APXS} @_ 2>&1);
chomp $error if defined $error;
if ($error) {
error "'$self->{MP_APXS} @_' failed:";
error $error;
}
else {
$val = '';
}
}
$self->{$query_key} = $val;
}
sub apxs_cflags {
my $who = caller_package(shift);
my $cflags = $who->apxs('-q' => 'CFLAGS');
$cflags =~ s/\"/\\\"/g;
$cflags;
}
sub apxs_extra_cflags {
my $who = caller_package(shift);
my $flags = $who->apxs('-q' => 'EXTRA_CFLAGS');
$flags =~ s/\"/\\\"/g;
$flags;
}
sub apxs_extra_cppflags {
my $who = caller_package(shift);
my $flags = $who->apxs('-q' => 'EXTRA_CPPFLAGS') ." ".
$who->apxs('-q' => 'NOTEST_CPPFLAGS');
$flags =~ s/\"/\\\"/g;
$flags;
}
sub caller_package {
my $arg = shift;
return ($arg and ref($arg) eq __PACKAGE__) ? $arg : __PACKAGE__;
}
my %threaded_mpms = map { $_ => 1 }
qw(worker winnt beos mpmt_os2 netware leader perchild threadpool
dynamic);
sub mpm_is_threaded {
my $self = shift;
my $mpm_name = $self->mpm_name();
return exists $threaded_mpms{$mpm_name} ? 1 : 0;
}
sub mpm_name {
my $self = shift;
return $self->{mpm_name} if $self->{mpm_name};
if ($self->httpd_version =~ /^(\d+)\.(\d+)\.(\d+)/) {
delete $threaded_mpms{dynamic} if $self->mp_nonthreaded_ok;
return $self->{mpm_name} = 'dynamic' if ($1*1000+$2)*1000+$3>=2003000;
}
# XXX: hopefully apxs will work on win32 one day
return $self->{mpm_name} = 'winnt' if WIN32;
my $mpm_name;
# httpd >= 2.3
if ($self->httpd_version_as_int =~ m/^2[3-9]\d+/) {
$mpm_name = 'dynamic';
}
else {
$mpm_name = $self->apxs('-q' => 'MPM_NAME');
}
# building against the httpd source dir
unless (($mpm_name and $self->httpd_is_source_tree)) {
if ($self->dir) {
my $config_vars_file = catfile $self->dir,
"build", "config_vars.mk";
if (open my $fh, $config_vars_file) {
while (<$fh>) {
if (/MPM_NAME = (\w+)/) {
$mpm_name = $1;
last;
}
}
close $fh;
}
}
}
unless ($mpm_name) {
my $msg = 'Failed to obtain the MPM name.';
$msg .= " Please specify MP_APXS=/full/path/to/apxs to solve " .
"this problem." unless exists $self->{MP_APXS};
error $msg;
die "\n";
}
return $self->{mpm_name} = $mpm_name;
}
sub should_build_apache {
my ($self) = @_;
return $self->{MP_USE_STATIC} ? 1 : 0;
}
sub configure_apache {
my ($self) = @_;
unless ($self->{MP_AP_CONFIGURE}) {
error "You specified MP_USE_STATIC but did not specify the " .
"arguments to httpd's ./configure with MP_AP_CONFIGURE";
exit 1;
}
unless ($self->{MP_AP_PREFIX}) {
error "You specified MP_USE_STATIC but did not speficy the " .
"location of httpd's source tree with MP_AP_PREFIX";
exit 1;
}
debug "Configuring httpd in $self->{MP_AP_PREFIX}";
my $httpd = File::Spec->catfile($self->{MP_AP_PREFIX}, 'httpd');
$self->{'httpd'} ||= $httpd;
push @Apache::TestMM::Argv, ('httpd' => $self->{'httpd'});
my $mplibpath = '';
my $ldopts = $self->ldopts;
if (CYGWIN) {
# Cygwin's httpd port links its modules into httpd2core.dll,
# instead of httpd.exe. In this case, we have a problem,
# because libtool doesn't want to include static libs (.a)
# into a dynamic lib (.dll). Workaround this by setting
# mod_perl.a as a linker argument (including all other flags
# and libs).
my $mplib = "$self->{MP_LIBNAME}$Config{lib_ext}";
$ldopts = join ' ',
'--export-all-symbols',
'--enable-auto-image-base',
"$self->{cwd}/src/modules/perl/$mplib",
$ldopts;
$ldopts =~ s/(\S+)/-Wl,$1/g;
} else {
my $mplib = "$self->{MP_LIBNAME}$Config{lib_ext}";
$mplibpath = catfile($self->{cwd}, qw(src modules perl), $mplib);
}
local $ENV{BUILTIN_LIBS} = $mplibpath;
local $ENV{AP_LIBS} = $ldopts;
local $ENV{MODLIST} = 'perl';
# XXX: -Wall and/or -Werror at httpd configure time breaks things
local $ENV{CFLAGS} = join ' ', grep { ! /\-Wall|\-Werror/ }
split /\s+/, $ENV{CFLAGS} || '';
my $cd = qq(cd $self->{MP_AP_PREFIX});
# We need to clean the httpd tree before configuring it
if (-f File::Spec->catfile($self->{MP_AP_PREFIX}, 'Makefile')) {
my $cmd = qq(make clean);
debug "Running $cmd";
system("$cd && $cmd") == 0 or die "httpd: $cmd failed";
}
my $cmd = qq(./configure $self->{MP_AP_CONFIGURE});
debug "Running $cmd";
system("$cd && $cmd") == 0 or die "httpd: $cmd failed";
# Got to build in srclib/* early to have generated files present.
my $srclib = File::Spec->catfile($self->{MP_AP_PREFIX}, 'srclib');
$cd = qq(cd $srclib);
$cmd = qq(make);
debug "Building srclib in $srclib";
system("$cd && $cmd") == 0 or die "srclib: $cmd failed";
}
#--- Perl Config stuff ---
my %gtop_config = ();
sub find_gtop {
my $self = shift;
return %gtop_config if %gtop_config;
if (%gtop_config = find_gtop_config()) {
return %gtop_config;
}
if ($self->find_dlfile('gtop')) {
$gtop_config{ldopts} = $self->gtop_ldopts_old();
$gtop_config{ccopts} = '';
return %gtop_config;
}
return ();
}
sub find_gtop_config {
my %c = ();
my $ver_2_5_plus = 0;
if (system('pkg-config --exists libgtop-2.0') == 0) {
# 2.x
chomp($c{ccopts} = qx|pkg-config --cflags libgtop-2.0|);
chomp($c{ldopts} = qx|pkg-config --libs libgtop-2.0|);
# 2.0.0 bugfix
chomp(my $libdir = qx|pkg-config --variable=libdir libgtop-2.0|);
$c{ldopts} =~ s|\$\(libdir\)|$libdir|;
chomp($c{ver} = qx|pkg-config --modversion libgtop-2.0|);
($c{ver_maj}, $c{ver_min}) = split /\./, $c{ver};
$ver_2_5_plus++ if $c{ver_maj} == 2 && $c{ver_min} >= 5;
if ($ver_2_5_plus) {
# some headers were removed in libgtop 2.5.0 so we need to
# be able to exclude them at compile time
$c{ccopts} .= ' -DGTOP_2_5_PLUS';
}
}
elsif (system('gnome-config --libs libgtop') == 0) {
chomp($c{ccopts} = qx|gnome-config --cflags libgtop|);
chomp($c{ldopts} = qx|gnome-config --libs libgtop|);
# buggy ( < 1.0.9?) versions fixup
$c{ccopts} =~ s|^/|-I/|;
$c{ldopts} =~ s|^/|-L/|;
}
# starting from 2.5.0 'pkg-config --cflags libgtop-2.0' already
# gives us all the cflags that are needed
if ($c{ccopts} && !$ver_2_5_plus) {
chomp(my $ginc = `glib-config --cflags`);
$c{ccopts} .= " $ginc";
}
if (%c) {
$c{ccopts} = " $c{ccopts}";
$c{ldopts} = " $c{ldopts}";
}
return %c;
}
my @Xlib = qw(/usr/X11/lib /usr/X11R6/lib);
sub gtop_ldopts_old {
my $self = shift;
my $xlibs = "";
my ($path) = $self->find_dlfile('Xau', @Xlib);
if ($path) {
$xlibs = "-L$path -lXau";
}
if ($self->find_dlfile('intl')) {
$xlibs .= ' -lintl';
}
return " -lgtop -lgtop_sysdeps -lgtop_common $xlibs";
}
sub gtop_ldopts {
exists $gtop_config{ldopts} ? $gtop_config{ldopts} : '';
}
sub gtop_ccopts {
exists $gtop_config{ccopts} ? $gtop_config{ccopts} : '';
}
sub ldopts {
my ($self) = @_;
my $config = tied %Config;
my $ldflags = $config->{ldflags};
if (WIN32) {
$config->{ldflags} = ''; #same as lddlflags
}
elsif (DARWIN) {
#not sure how this can happen, but it shouldn't
my @bogus_flags = ('flat_namespace', 'bundle', 'undefined suppress');
for my $flag (@bogus_flags) {
$config->{ldflags} =~ s/-$flag\s*//;
}
}
my $ldopts = ExtUtils::Embed::ldopts();
chomp $ldopts;
my $ld = $self->perl_config('ld');
if (HPUX && $ld eq 'ld') {
while ($ldopts =~ s/-Wl,(\S+)/$1/) {
my $cp = $1;
(my $repl = $cp) =~ s/,/ /g;
$ldopts =~ s/\Q$cp/$repl/;
}
}
if ($self->{MP_USE_GTOP}) {
$ldopts .= $self->gtop_ldopts;
}
$config->{ldflags} = $ldflags; #reset
# on Irix mod_perl.so needs to see the libperl.so symbols, which
# requires the -exports option immediately before -lperl.
if (IRIX) {
($ldopts =~ s/-lperl\b/-exports -lperl/)
or warn "Failed to fix Irix symbol exporting\n";
}
$ldopts;
}
my $Wall =
"-Wall -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations";
# perl v5.6.1 and earlier produces lots of warnings, so we can't use
# -Werror with those versions.
$Wall .= " -Werror" if $] >= 5.006002;
sub ap_ccopts {
my ($self) = @_;
my $ccopts = "-DMOD_PERL";
if ($self->{MP_USE_GTOP}) {
$ccopts .= " -DMP_USE_GTOP";
$ccopts .= $self->gtop_ccopts;
}
if ($self->{MP_MAINTAINER}) {
$self->{MP_DEBUG} = 1;
if ($self->perl_config('gccversion')) {
#same as --with-maintainter-mode
$ccopts .= " $Wall";
}
if (!OPENBSD &&
$self->has_gcc_version('3.3.2') &&
$ccopts !~ /declaration-after-statement/) {
debug "Adding -Wdeclaration-after-statement to ccopts";
$ccopts .= " -Wdeclaration-after-statement";
}
}
if ($self->{MP_COMPAT_1X}) {
$ccopts .= " -DMP_COMPAT_1X";
}
if ($self->{MP_DEBUG}) {
$self->{MP_TRACE} = 1;
my $win32_flags = MSVC ? '-Od -MD -Zi' : '';
my $debug_flags = WIN32 ? $win32_flags : '-g';
$ccopts .= " $debug_flags" unless $Config{optimize} =~ /$debug_flags/;
$ccopts .= ' -DMP_DEBUG';
}
if ($self->{MP_CCOPTS}) {
$ccopts .= " $self->{MP_CCOPTS}";
}
if ($self->{MP_TRACE}) {
$ccopts .= " -DMP_TRACE";
}
if ($self->has_gcc_version('5.0.0') && $ccopts !~ /-fgnu89-inline/) {
$ccopts .= " -fgnu89-inline";
}
if ($self->has_clang && $ccopts !~ /-std=gnu89/) {
$ccopts .= " -std=gnu89";
}
# make sure apr.h can be safely included
# for example Perl's included -D_GNU_SOURCE implies
# -D_LARGEFILE64_SOURCE on linux, but this won't happen on
# Solaris, so we need apr flags living in apxs' EXTRA_CPPFLAGS
my $extra_cppflags = $self->apxs_extra_cppflags;
$ccopts .= " " . $extra_cppflags;
# Make sure the evil AP_DEBUG is not defined when building mod_perl
$ccopts =~ s/ ?-DAP_DEBUG\b//;
$ccopts;
}
sub has_gcc_version {
my $self = shift;
my $requested_version = shift;
my $has_version = $self->perl_config('gccversion');
return 0 unless $has_version;
#Only interested in leading version digits
$has_version =~ s/^([0-9.]+).*/$1/;
my @tuples = split /\./, $has_version, 3;
my @r_tuples = split /\./, $requested_version, 3;
return cmp_tuples(\@tuples, \@r_tuples) == 1;
}
sub has_clang {
my $self = shift;
my $has_version = $self->perl_config('gccversion');
return 0 unless $has_version;
return $has_version =~ m/Clang/;
}
sub cmp_tuples {
my ($num_a, $num_b) = @_;
while (@$num_a && @$num_b) {
my $cmp = shift @$num_a <=> shift @$num_b;
return $cmp if $cmp;
}
return @$num_a <=> @$num_b;
}
sub perl_ccopts {
my $self = shift;
my $cflags = $self->strip_lfs(" $Config{ccflags} ");
my $fixup = \&{"ccopts_$^O"};
if (defined &$fixup) {
$fixup->(\$cflags);
}
if (WIN32 and $self->{MP_DEBUG}) {
#only win32 has -DDEBUGGING in both optimize and ccflags
my $optim = $Config{optimize};
unless ($optim =~ /-DDEBUGGING/) {
$cflags =~ s/$optim//;
}
}
if (CYGWIN) {
$cflags .= " -DCYGWIN ";
}
$cflags;
}
sub ccopts_hpux {
my $cflags = shift;
return if $Config{cc} eq 'gcc'; #XXX?
return if $$cflags =~ /(-Ae|\+e)/;
$$cflags .= " -Ae ";
}
# XXX: there could be more, but this is just for cosmetics
my %cflags_dups = map { $_ => 1 } qw(-D_GNU_SOURCE -D_REENTRANT);
sub ccopts {
my ($self) = @_;
my $cflags = $self->perl_ccopts . ExtUtils::Embed::perl_inc() .
$self->ap_ccopts;
# remove duplicates of certain cflags coming from perl and ap/apr
my @cflags = ();
my %dups = ();
for (split /\s+/, $cflags) {
if ($cflags_dups{$_}) {
next if $dups{$_};
$dups{$_}++;
}
push @cflags, $_;
}
$cflags = "@cflags";
$cflags;
}
sub ldopts_prefix {
my $self = shift;
$self->perl_config('ld') eq 'ld' ? '' : "-Wl,";
}
sub perl_config_optimize {
my ($self, $val) = @_;
$val ||= $Config{optimize};
if ($self->{MP_DEBUG}) {
return ' ' unless $Config{ccflags} =~ /-DDEBUGGING/;
}
$val;
}
sub perl_config_ld {
my ($self, $val) = @_;
$val ||= $Config{ld};
basename $val; #bleedperl hpux value is /usr/bin/ld !
}
sub perl_config_lddlflags {
my ($self, $val) = @_;
if ($self->{MP_DEBUG}) {
if (MSVC) {
unless ($val =~ s/-release/-debug/) {
$val .= ' -debug';
}
}
}
if (AIX) {
my $Wl = $self->ldopts_prefix;
# it's useless to import symbols from libperl.so this way,
# because perl.exp is incomplete. a better way is to link
# against -lperl which has all the symbols
$val =~ s|${Wl}-bI:\$\(PERL_INC\)/perl\.exp||;
# also in the case of Makefile.modperl PERL_INC is defined
# this works with at least ld(1) on powerpc-ibm-aix5.1.0.0:
# -berok ignores symbols resolution problems (they will be
# resolved at run-time
# -brtl prepares the object for run-time loading
# LDFLAGS already inserts -brtl
$val .= " ${Wl}-berok";
# XXX: instead of -berok, could make sure that we have:
# -Lpath/to/CORE -lperl
# -bI:$path/apr.exp -bI:$path/aprutil.exp -bI:$path/httpd.exp
# -bI:$path/modperl_*.exp
# - don't import modperl_*.exp in Makefile.modperl which
# exports -bE:$path/modperl_*.exp
# - can't rely on -bI:$path/perl.exp, because it's incomplete,
# use -lperl instead
# - the issue with using apr/aprutil/httpd.exp is to pick the
# right path if httpd wasn't yet installed
}
$val;
}
sub perl_config {
my ($self, $key) = @_;
my $val = $Config{$key} || '';
my $method = \&{"perl_config_$key"};
if (defined &$method) {
return $method->($self, $val);
}
return $val;
}
sub find_in_inc {
my $name = shift;
for (@INC) {
my $file;
if (-e ($file = "$_/auto/Apache2/$name")) {
return $file;
}
}
}
sub libpth {
my $self = shift;
$self->{libpth} ||= [split /\s+/, $Config{libpth}];
return wantarray ? @{ $self->{libpth} } : $self->{libpth};
}
sub find_dlfile {
my ($self, $name) = (shift, shift);
require DynaLoader;
require AutoLoader; #eek
my $found = 0;
my $loc = "";
my (@path) = ($self->libpth, @_);
for (@path) {
if ($found = DynaLoader::dl_findfile($_, "-l$name")) {
$loc = $_;
last;
}
}
return wantarray ? ($loc, $found) : $found;
}
sub find_dlfile_maybe {
my ($self, $name) = @_;
my $path = $self->libpth;
my @maybe;
my $lib = 'lib' . $name;
for (@$path) {
push @maybe, grep { ! -l $_ } <$_/$lib.*>;
}
return \@maybe;
}
sub lib_check {
my ($self, $name) = @_;
return unless $self->perl_config('libs') =~ /$name/;
return if $self->find_dlfile($name);
my $maybe = $self->find_dlfile_maybe($name);
my $suggest = @$maybe ?
"You could just symlink it to $maybe->[0]" :
'You might need to install Perl from source';
$self->phat_warn(<<EOF);
Your Perl is configured to link against lib$name,
but lib$name.so was not found.
$suggest
EOF
}
#--- user interaction ---
sub prompt {
my ($self, $q, $default) = @_;
return $default if $self->{MP_PROMPT_DEFAULT};
require ExtUtils::MakeMaker;
ExtUtils::MakeMaker::prompt($q, $default);
}
sub prompt_y {
my ($self, $q) = @_;
$self->prompt($q, 'y') =~ /^y/i;
}
sub prompt_n {
my ($self, $q) = @_;
$self->prompt($q, 'n') =~ /^n/i;
}
sub phat_warn {
my ($self, $msg, $abort) = @_;
my $level = $abort ? 'ERROR' : 'WARNING';
warn <<EOF;
************* $level *************
$msg
************* $level *************
EOF
if ($abort) {
exit 1;
}
else {
sleep 5;
}
}
#--- constructors ---
my $bpm = 'Apache2/BuildConfig.pm';
sub build_config {
my $self = shift;
my $bpm_mtime = 0;
$bpm_mtime = (stat _)[9] if $INC{$bpm} && -e $INC{$bpm};
if (-e "lib/$bpm" and (stat _)[9] > $bpm_mtime) {
#reload if Makefile.PL has regenerated
unshift @INC, 'lib';
delete $INC{$bpm};
eval { require Apache2::BuildConfig; };
shift @INC;
}
else {
eval { require Apache2::BuildConfig; };
}
return bless {}, (ref($self) || $self) if $@;
return Apache2::BuildConfig->new;
}
sub new {
my $class = shift;
my $self = bless {
cwd => Cwd::fastcwd(),
MP_LIBNAME => 'mod_perl',
MP_APXS => undef, # so we know we haven't tried to set it yet
@_,
}, $class;
$self->{MP_APR_LIB} = 'aprext';
ModPerl::BuildOptions->init($self) if delete $self->{init};
$self;
}
sub DESTROY {}
my %default_files = (
'build_config' => 'lib/Apache2/BuildConfig.pm',
'ldopts' => 'src/modules/perl/ldopts',
'makefile' => 'src/modules/perl/Makefile',
);
sub clean_files {
my $self = shift;
[sort map { $self->default_file($_) } keys %default_files];
}
sub default_file {
my ($self, $name, $override) = @_;
my $key = join '_', 'file', $name;
$self->{$key} ||= ($override || $default_files{$name});
}
sub file_path {
my $self = shift;
# work around when Apache2::BuildConfig has not been created yet
return unless $self && $self->{cwd};
my @files = map { m:^/: ? $_ : join('/', $self->{cwd}, $_) } @_;
return wantarray ? @files : $files[0];
}
sub freeze {
require Data::Dumper;
local $Data::Dumper::Terse = 1;
local $Data::Dumper::Sortkeys = 1;
my $data = Data::Dumper::Dumper(shift);
chomp $data;
$data;
}
sub save_ldopts {
my ($self, $file) = @_;
$file ||= $self->default_file('ldopts', $file);
my $ldopts = $self->ldopts;
open my $fh, '>', $file or die "open $file: $!";
print $fh "#!/bin/sh\n\necho $ldopts\n";
close $fh;
chmod 0755, $file;
}
sub noedit_warning_hash {
ModPerl::Code::noedit_warning_hash(__PACKAGE__);
}
sub save {
my ($self, $file) = @_;
delete $INC{$bpm};
$file ||= $self->default_file('build_config');
$file = $self->file_path($file);
my $obj = $self->freeze;
$obj =~ s/^\s{9}//mg;
$obj =~ s/^/ /;
open my $fh, '>', $file or die "open $file: $!";
#work around autosplit braindeadness
my $package = 'package Apache2::BuildConfig';
print $fh noedit_warning_hash();
print $fh <<EOF;
$package;
use Apache2::Build ();
sub new {
$obj;
}
1;
EOF
close $fh or die "failed to write $file: $!";
}
sub rebuild {
my $self = __PACKAGE__->build_config;
my @opts = map { qq[$_='$self->{$_}'] } sort grep /^MP_/, keys %$self;
my $command = "perl Makefile.PL @opts";
print "Running: $command\n";
system $command;
}
# % perl -MApache2::Build -e rebuild
*main::rebuild = \&rebuild if $0 eq '-e';
#--- attribute access ---
sub is_dynamic { shift->{MP_USE_DSO} }
sub default_dir {
my $build = shift->build_config;
return $build->dir || '../apache_x.x/src';
}
sub dir {
my ($self, $dir) = @_;
if ($dir) {
for (qw(ap_includedir)) {
delete $self->{$_};
}
if ($dir =~ m:^\.\.[/\\]:) {
$dir = "$self->{cwd}/$dir";
}
$self->{dir} = $dir;
}
return $self->{dir} if $self->{dir};
# be careful with the guesswork, or may pick up some wrong headers
if (IS_MOD_PERL_BUILD && $self->{MP_AP_PREFIX}) {
my $build = $self->build_config;
if (my $bdir = $build->{'dir'}) {
for ($bdir, "../$bdir", "../../$bdir") {
if (-d $_) {
$dir = $_;
last;
}
}
}
}
$dir ||= $self->{MP_AP_PREFIX};
# we no longer install Apache headers, so don't bother looking in @INC
# might end up finding 1.x headers anyhow
# unless ($dir and -d $dir) {
# for (@INC) {
# last if -d ($dir = "$_/auto/Apache2/include");
# }
# }
return $self->{dir} = $dir ? canonpath(rel2abs $dir) : undef;
}
#--- finding apache *.h files ---
sub find {
my $self = shift;
my %seen = ();
my @dirs = ();
for my $src_dir ($self->dir,
$self->default_dir,
'../httpd-2.0')
{
next unless $src_dir;
next unless (-d $src_dir || -l $src_dir);
next if $seen{$src_dir}++;
push @dirs, $src_dir;
#$modified{$src_dir} = (stat($src_dir))[9];
}
return @dirs;
}
sub ap_includedir {
my ($self, $d) = @_;
return $self->{ap_includedir}
if $self->{ap_includedir} and -d $self->{ap_includedir};
return unless $d ||= $self->apxs('-q' => 'INCLUDEDIR') || $self->dir;
if (-e "$d/include/ap_release.h") {
return $self->{ap_includedir} = "$d/include";
}
$self->{ap_includedir} = $d;
}
# This is necessary for static builds that needs to make a
# difference between where the apache headers are (to build
# against) and where they will be installed (to install our
# own headers alongside)
#
# ap_exp_includedir is where apache is going to install its
# headers to
sub ap_exp_includedir {
my ($self) = @_;
return $self->{ap_exp_includedir} if $self->{ap_exp_includedir};
my $build_vars = File::Spec->catfile($self->{MP_AP_PREFIX},
qw(build config_vars.mk));
open my $vars, "<$build_vars" or die "Couldn't open $build_vars $!";
my $ap_exp_includedir;
while (<$vars>) {
if (/exp_includedir\s*=\s*(.*)/) {
$ap_exp_includedir = $1;
last;
}
}
$self->{ap_exp_includedir} = $ap_exp_includedir;
}
sub install_headers_dir {
my ($self) = @_;
if ($self->should_build_apache) {
return $self->ap_exp_includedir();
}
else {
return $self->ap_includedir();
}
}
# where apr-config and apu-config reside
sub apr_bindir {
my ($self) = @_;
$self->apr_config_path unless $self->{apr_bindir};
$self->{apr_bindir};
}
sub apr_generation {
my ($self) = @_;
return $self->httpd_version_as_int =~ m/2[1-9]\d+/ ? 1 : 0;
}
# returns an array of apr/apu linking flags (--link-ld --libs) if found
# an empty array otherwise
my @apru_link_flags = ();
sub apru_link_flags {
my ($self) = @_;
return @apru_link_flags if @apru_link_flags;
# first use apu_config_path and then apr_config_path in order to
# resolve the symbols right during linking
for ($self->apu_config_path, $self->apr_config_path) {
my $flags = '--link-ld --libs';
$flags .= ' --ldflags' unless (WIN32);
if (my $link = $_ && -x $_ && qx{$_ $flags}) {
chomp $link;
# Change '/path/to/libanything.la' to '-L/path/to -lanything'
if (CYGWIN) {
$link =~ s|(\S*)/lib([^.\s]+)\.\S+|-L$1 -l$2|g;
}
if ($self->httpd_is_source_tree) {
my @libs;
while ($link =~ m/-L(\S+)/g) {
my $dir = File::Spec->catfile($1, '.libs');
push @libs, $dir if -d $dir;
}
push @apru_link_flags, join ' ', map { "-L$_" } @libs;
}
push @apru_link_flags, $link;
}
}
return @apru_link_flags;
}
sub apr_config_path {
shift->apru_config_path("apr");
}
sub apu_config_path {
shift->apru_config_path("apu");
}
sub apru_config_path {
my ($self, $what) = @_;
my $key = "${what}_config_path"; # apr_config_path
my $mp_key = "MP_" . uc($what) . "_CONFIG"; # MP_APR_CONFIG
my $bindir = uc($what) . "_BINDIR"; # APR_BINDIR
return $self->{$key} if $self->{$key} and -x $self->{$key};
if (exists $self->{$mp_key} and -x $self->{$mp_key}) {
$self->{$key} = $self->{$mp_key};
}
my $config = $self->apr_generation ? "$what-1-config" : "$what-config";
if (!$self->{$key}) {
my @tries = ();
if ($self->httpd_is_source_tree) {
for my $base (grep defined $_, $self->dir) {
push @tries, grep -d $_,
map catdir($base, "srclib", $_), qw(apr apr-util);
}
# Check for MP_AP_CONFIGURE="--with-apr[-util]=DIR|FILE"
my $what_long = ($what eq 'apu') ? 'apr-util' : 'apr';
if ($self->{MP_AP_CONFIGURE} &&
$self->{MP_AP_CONFIGURE} =~ /--with-${what_long}=(\S+)/) {
my $dir = $1;
$dir = dirname $dir if -f $dir;
push @tries, grep -d $_, $dir, catdir $dir, 'bin';
}
}
else {
push @tries, grep length,
map $self->apxs(-q => $_), $bindir, "BINDIR";
push @tries, catdir $self->{MP_AP_PREFIX}, "bin"
if exists $self->{MP_AP_PREFIX} and -d $self->{MP_AP_PREFIX};
}
@tries = map { catfile $_, $config } @tries;
if (WIN32) {
my $ext = '.bat';
for (@tries) {
$_ .= $ext if ($_ and $_ !~ /$ext$/);
}
}
for my $try (@tries) {
next unless -x $try;
$self->{$key} = $try;
}
}
$self->{$key} ||= Apache::TestConfig::which($config);
# apr_bindir makes sense only if httpd/apr is installed, if we are
# building against the source tree we can't link against
# apr/aprutil libs
unless ($self->httpd_is_source_tree) {
$self->{apr_bindir} = $self->{$key}
? dirname $self->{$key}
: '';
}
$self->{$key};
}
sub apr_includedir {
my ($self) = @_;
return $self->{apr_includedir}
if $self->{apr_includedir} and -d $self->{apr_includedir};
my $incdir;
my $apr_config_path = $self->apr_config_path;
if ($apr_config_path) {
my $httpd_version = $self->httpd_version;
chomp($incdir = `$apr_config_path --includedir`);
}
unless ($incdir and -d $incdir) {
# falling back to the default when apr header files are in the
# same location as the httpd header files
$incdir = $self->ap_includedir;
}
my @tries = ($incdir);
if ($self->httpd_is_source_tree) {
my $path = catdir $self->dir, "srclib", "apr", "include";
push @tries, $path if -d $path;
}
for (@tries) {
next unless $_ && -e catfile $_, "apr.h";
$self->{apr_includedir} = $_;
last;
}
unless ($self->{apr_includedir}) {
error "Can't find apr include/ directory,",
"use MP_APR_CONFIG=/path/to/apr-config";
exit 1;
}
$self->{apr_includedir};
}
#--- parsing apache *.h files ---
sub mmn_eq {
my ($class, $dir) = @_;
return 1 if WIN32; #just assume, till Apache2::Build works under win32
my $instsrc;
{
local @INC = grep { !/blib/ } @INC;
my $instdir;
for (@INC) {
last if -d ($instdir = "$_/auto/Apache2/include");
}
$instsrc = $class->new(dir => $instdir);
}
my $targsrc = $class->new($dir ? (dir => $dir) : ());
my $inst_mmn = $instsrc->module_magic_number;
my $targ_mmn = $targsrc->module_magic_number;
unless ($inst_mmn && $targ_mmn) {
return 0;
}
if ($inst_mmn == $targ_mmn) {
return 1;
}
print "Installed MMN $inst_mmn does not match target $targ_mmn\n";
return 0;
}
sub module_magic_number {
my $self = shift;
return $self->{mmn} if $self->{mmn};
my $d = $self->ap_includedir;
return 0 unless $d;
#return $mcache{$d} if $mcache{$d};
my $fh;
for (qw(ap_mmn.h http_config.h)) {
last if open $fh, "$d/$_";
}
return 0 unless $fh;
my $n;
my $mmn_pat = join '|', qw(MODULE_MAGIC_NUMBER_MAJOR MODULE_MAGIC_NUMBER);
while(<$fh>) {
if(s/^\#define\s+($mmn_pat)\s+(\d+).*/$2/) {
chomp($n = $_);
last;
}
}
close $fh;
$self->{mmn} = $n
}
sub fold_dots {
my $v = shift;
$v =~ s/\.//g;
$v .= '0' if length $v < 3;
$v;
}
sub httpd_version_as_int {
my ($self, $dir) = @_;
my $v = $self->httpd_version($dir);
fold_dots($v);
}
sub httpd_version_cache {
my ($self, $dir, $v) = @_;
return '' unless $dir;
$self->{httpd_version}->{$dir} = $v if $v;
$self->{httpd_version}->{$dir};
}
sub httpd_version {
my ($self, $dir) = @_;
return unless $dir = $self->ap_includedir($dir);
if (my $v = $self->httpd_version_cache($dir)) {
return $v;
}
my $header = "$dir/ap_release.h";
open my $fh, $header or do {
error "Unable to open $header: $!";
return undef;
};
my $version;
while (<$fh>) {
#now taking bets on how many friggin times this will change
#over the course of apache 2.0. 1.3 changed it at least a half
#dozen times. hopefully it'll stay in the same file at least.
if (/^\#define\s+AP_SERVER_MAJORVERSION\s+\"(\d+)\"/) {
#XXX could be more careful here. whatever. see above.
my $major = $1;
my $minor = (split /\s+/, scalar(<$fh>))[-1];
my $patch = (split /\s+/, scalar(<$fh>))[-1];
$version = join '.', $major, $minor, $patch;
$version =~ s/\"//g;
last;
}
elsif (/^\#define\s+AP_SERVER_BASEREVISION\s+\"(.*)\"/) {
$version = $1;
last;
}
elsif(/^\#define\s+AP_SERVER_MAJORVERSION_NUMBER\s+(\d+)/) {
# new 2.1 config
my $major = $1;
my $minor = (split /\s+/, scalar(<$fh>))[-1];
my $patch = (split /\s+/, scalar(<$fh>))[-1];
my ($define, $macro, $dev) = (split /\s+/, scalar(<$fh>));
if ($macro =~ /AP_SERVER_DEVBUILD_BOOLEAN/ && $dev eq '1') {
$dev = "-dev";
}
else {
$dev = "";
}
$version = join '.', $major, $minor, "$patch$dev";
$version =~ s/\"//g;
last;
}
}
close $fh;
debug "parsed version $version from ap_release.h";
$self->httpd_version_cache($dir, $version);
}
my %wanted_apr_config = map { $_, 1} qw(
HAS_THREADS HAS_DSO HAS_MMAP HAS_RANDOM HAS_SENDFILE
HAS_LARGE_FILES HAS_INLINE HAS_FORK
);
sub get_apr_config {
my $self = shift;
return $self->{apr_config} if $self->{apr_config};
my $fh;
my $header = catfile $self->apr_includedir, "apr.h";
if (WIN32) {
open $fh, $header or do {
error "Unable to open $header: $!";
return undef;
};
}
else {
my @command = ($self->perl_config('cpp'), '-E', '-dM', $header);
open $fh, '-|', @command or do {
error "Unable to preprocess $header with @command: $!";
return undef;
};
}
my %cfg;
while (<$fh>) {
next unless s/^\#define\s+APR_((HAVE|HAS|USE)_\w+)/$1/;
chomp;
my ($name, $val) = split /\s+/, $_, 2;
next unless $wanted_apr_config{$name};
$val =~ s/\s+$//;
next unless $val =~ /^\d+$/;
$cfg{$name} = $val;
}
$self->{apr_config} = \%cfg;
}
#--- generate Makefile ---
sub canon_make_attr {
my ($self, $name) = (shift, shift);
my $attr = join '_', 'MODPERL', uc $name;
$self->{$attr} = "@_";
"$attr = $self->{$attr}\n\n";
}
sub xsubpp {
my $self = shift;
my $xsubpp = join ' ', '$(MODPERL_PERLPATH)',
'$(MODPERL_PRIVLIBEXP)/ExtUtils/xsubpp',
'-typemap', '$(MODPERL_PRIVLIBEXP)/ExtUtils/typemap';
my $typemap = $self->file_path('lib/typemap');
if (-e $typemap) {
$xsubpp .= join ' ',
' -typemap', $typemap;
}
$xsubpp;
}
sub make_xs {
my ($self, $fh) = @_;
print $fh $self->canon_make_attr(xsubpp => $self->xsubpp);
return [] unless $self->{XS};
my @files;
my @xs_targ;
foreach my $name (sort keys %{ $self->{XS} }) {
my $xs = $self->{XS}->{$name};
#Foo/Bar.xs => Foo_Bar.c
(my $c = $xs) =~ s:.*?WrapXS/::;
$c =~ s:/:_:g;
$c =~ s:\.xs$:.c:;
push @files, $c;
push @xs_targ, <<EOF;
$c: $xs
\t\$(MODPERL_XSUBPP) $xs > \$*.xsc && \$(MODPERL_MV) \$*.xsc \$@
EOF
}
my %o = (xs_o_files => 'o', xs_o_pic_files => 'lo');
for my $ext (qw(xs_o_files xs_o_pic_files)) {
print $fh $self->canon_make_attr($ext, map {
(my $file = $_) =~ s/c$/$o{$ext}/; $file;
} @files);
}
print $fh $self->canon_make_attr(xs_clean_files => @files);
\@xs_targ;
}
#when we use a bit of MakeMaker, make it use our values for these vars
my %perl_config_pm_alias = (
ABSPERL => 'perlpath',
ABSPERLRUN => 'perlpath',
PERL => 'perlpath',
PERLRUN => 'perlpath',
PERL_LIB => 'privlibexp',
PERL_ARCHLIB => 'archlibexp',
);
my $mm_replace = join '|', keys %perl_config_pm_alias;
# get rid of dups
my %perl_config_pm_alias_values = reverse %perl_config_pm_alias;
my @perl_config_pm_alias_values = keys %perl_config_pm_alias_values;
my @perl_config_pm = sort(@perl_config_pm_alias_values, qw(cc cpprun
rm ranlib lib_ext obj_ext cccdlflags lddlflags optimize));
sub mm_replace {
my $val = shift;
$$val =~ s/\(($mm_replace)\)/(MODPERL_\U$perl_config_pm_alias{$1})/g;
}
#help prevent warnings
my @mm_init_vars = (BASEEXT => '', NAME => '');
sub make_tools {
my ($self, $fh) = @_;
for (@perl_config_pm) {
print $fh $self->canon_make_attr($_, $self->perl_config($_));
}
require ExtUtils::MakeMaker;
my $mm = bless { @mm_init_vars }, 'MM';
# Fake initialize MakeMaker
foreach my $m (qw(init_main init_others init_tools)) {
$mm->$m() if $mm->can($m);
# init_main() undefines the BASEEXT member which we initialized
# to a blank string to prevent warnings; reset it now
$mm->{BASEEXT} = '' if not defined $mm->{BASEEXT};
}
for (qw(rm_f mv ld ar cp test_f)) {
my $val = $mm->{"\U$_"};
if ($val) {
mm_replace(\$val);
}
else {
$val = $Config{$_};
}
print $fh $self->canon_make_attr($_ => $val);
}
}
sub export_files_MSWin32 {
my $self = shift;
my $xs_dir = $self->file_path("xs");
"-def:$xs_dir/modperl.def";
}
sub export_files_aix {
my $self = shift;
my $Wl = $self->ldopts_prefix;
# there are several modperl_*.exp, not just $(BASEEXT).exp
# $(BASEEXT).exp resolves to modperl_global.exp
my $xs_dir = $self->file_path("xs");
join " ", map "${Wl}-bE:$xs_dir/modperl_$_.exp", qw(inline ithreads);
}
sub dynamic_link_header_default {
return <<'EOF';
$(MODPERL_LIBNAME).$(MODPERL_DLEXT): $(MODPERL_PIC_OBJS)
$(MODPERL_RM_F) $@
$(MODPERL_LD) $(MODPERL_LDDLFLAGS) \
$(MODPERL_AP_LIBS) \
$(MODPERL_PIC_OBJS) $(MODPERL_LDOPTS) \
EOF
}
sub dynamic_link_default {
my $self = shift;
my $link = $self->dynamic_link_header_default . "\t" . '-o $@';
my $ranlib = "\t" . '$(MODPERL_RANLIB) $@' . "\n";
$link .= "\n" . $ranlib unless (DARWIN or OPENBSD);
$link;
}
sub dynamic_link_MSWin32 {
my $self = shift;
my $defs = $self->export_files_MSWin32;
my $symbols = $self->modperl_symbols_MSWin32;
return $self->dynamic_link_header_default .
"\t$defs" .
($symbols ? ' \\' . "\n\t-pdb:$symbols" : '') .
' -out:$@' . "\n\t" .
'if exist $(MODPERL_MANIFEST_LOCATION)' . " \\\n\t" .
'mt /nologo /manifest $(MODPERL_MANIFEST_LOCATION)' . " \\\n\t" .
'/outputresource:$@;2' . "\n\n";
}
sub dynamic_link_aix {
my $self = shift;
my $link = $self->dynamic_link_header_default .
"\t" . $self->export_files_aix . " \\\n" .
"\t" . '-o $@' . " \n" .
"\t" . '$(MODPERL_RANLIB) $@';
}
sub dynamic_link_cygwin {
my $self = shift;
return <<'EOF';
$(MODPERL_LIBNAME).$(MODPERL_DLEXT): $(MODPERL_PIC_OBJS)
$(MODPERL_RM_F) $@
$(MODPERL_CC) -shared -o $@ \
-Wl,--out-implib=$(MODPERL_LIBNAME).dll.a \
-Wl,--export-all-symbols -Wl,--enable-auto-import \
-Wl,--enable-auto-image-base -Wl,--stack,8388608 \
$(MODPERL_PIC_OBJS) \
$(MODPERL_LDDLFLAGS) $(MODPERL_LDOPTS) \
$(MODPERL_AP_LIBS)
$(MODPERL_RANLIB) $@
EOF
}
sub dynamic_link {
my $self = shift;
my $link = \&{"dynamic_link_$^O"};
$link = \&dynamic_link_default unless defined &$link;
$link->($self);
}
# Returns the link flags for the apache shared core library
my $apache_corelib_cygwin;
sub apache_corelib_cygwin {
return $apache_corelib_cygwin if $apache_corelib_cygwin;
my $self = shift;
my $mp_src = "$self->{cwd}/src/modules/perl";
my $core = 'httpd2core';
# There's a problem with user-installed perl on cygwin.
# MakeMaker doesn't know about the .dll.a libs and warns
# about missing -lhttpd2core. "Fix" it by copying
# the lib and adding .a suffix.
# For the static build create a soft link, because libhttpd2core.dll.a
# doesn't exist at this time.
if ($self->is_dynamic) {
my $libpath = $self->apxs(-q => 'exp_libdir');
File::Copy::copy("$libpath/lib$core.dll.a", "$mp_src/lib$core.a");
} else {
my $libpath = catdir($self->{MP_AP_PREFIX}, '.libs');
mkdir $libpath unless -d $libpath;
qx{touch $libpath/lib$core.dll.a && \
ln -fs $libpath/lib$core.dll.a $mp_src/lib$core.a};
}
$apache_corelib_cygwin = "-L$mp_src -l$core";
}
sub apache_libs_MSWin32 {
my $self = shift;
my $prefix = $self->apxs(-q => 'PREFIX') || $self->dir;
my $lib = catdir $prefix, 'lib';
opendir(my $dir, $lib) or die qq{Cannot opendir $lib: $!};
my @libs = map {catfile($lib, $_)}
grep /^lib(apr|aprutil|httpd)\b\S*?\.lib$/, readdir $dir;
closedir $dir;
"@libs";
}
sub apache_libs_cygwin {
my $self = shift;
join ' ', $self->apache_corelib_cygwin, $self->apru_link_flags;
}
sub apache_libs {
my $self = shift;
my $libs = \&{"apache_libs_$^O"};
return "" unless defined &$libs;
$libs->($self);
}
sub modperl_libs_MSWin32 {
my $self = shift;
"$self->{cwd}/src/modules/perl/$self->{MP_LIBNAME}.lib";
}
sub modperl_libs_cygwin {
my $self = shift;
return '' unless $self->is_dynamic;
return "-L$self->{cwd}/src/modules/perl -l$self->{MP_LIBNAME}";
}
sub modperl_libs {
my $self = shift;
my $libs = \&{"modperl_libs_$^O"};
return "" unless defined &$libs;
$libs->($self);
}
sub modperl_libpath_MSWin32 {
my $self = shift;
# mod_perl.lib will be installed into MP_AP_PREFIX/lib
# for use by 3rd party xs modules
"$self->{cwd}/src/modules/perl/$self->{MP_LIBNAME}.lib";
}
sub modperl_libpath_cygwin {
my $self = shift;
"$self->{cwd}/src/modules/perl/$self->{MP_LIBNAME}.dll.a";
}
sub modperl_libpath {
my $self = shift;
my $libpath = \&{"modperl_libpath_$^O"};
return "" unless defined &$libpath;
$libpath->($self);
}
# returns the directory and name of the aprext lib built under blib/
sub mp_apr_blib {
my $self = shift;
return unless (my $mp_apr_lib = $self->{MP_APR_LIB});
my $lib_mp_apr_lib = 'lib' . $mp_apr_lib;
my @dirs = qw(blib arch auto);
my $apr_blib = catdir $self->{cwd}, @dirs, $lib_mp_apr_lib;
my $full_libname = $lib_mp_apr_lib . $Config{lib_ext};
return ($apr_blib, $full_libname);
}
sub mp_apr_lib_MSWin32 {
my $self = shift;
# The MP_APR_LIB will be installed into MP_AP_PREFIX/lib
# for use by 3rd party xs modules
my ($dir, $lib) = $self->mp_apr_blib();
$lib =~ s[^lib(\w+)$Config{lib_ext}$][$1];
$dir = Win32::GetShortPathName($dir);
return qq{ -L$dir -l$lib };
}
sub mp_apr_lib_cygwin {
my $self = shift;
my ($dir, $lib) = $self->mp_apr_blib();
$lib =~ s[^lib(\w+)$Config{lib_ext}$][$1];
my $libs = "-L$dir -l$lib";
# This is ugly, but is the only way to prevent the "undefined
# symbols" error
$libs .= join ' ', '',
'-L' . catdir($self->perl_config('archlibexp'), 'CORE'), '-lperl';
$libs;
}
# linking used for the aprext lib used to build APR/APR::*
sub mp_apr_lib {
my $self = shift;
my $libs = \&{"mp_apr_lib_$^O"};
return "" unless defined &$libs;
$libs->($self);
}
sub modperl_symbols_MSWin32 {
my $self = shift;
return "" unless $self->{MP_DEBUG};
"$self->{cwd}/src/modules/perl/$self->{MP_LIBNAME}.pdb";
}
sub modperl_symbols {
my $self = shift;
my $symbols = \&{"modperl_symbols_$^O"};
return "" unless defined &$symbols;
$symbols->($self);
}
sub write_src_makefile {
my $self = shift;
my $code = ModPerl::Code->new;
my $path = $code->path;
my $install = <<'EOI';
install:
EOI
if (!$self->should_build_apache) {
$install .= <<'EOI';
# install mod_perl.so
@$(MKPATH) $(DESTDIR)$(MODPERL_AP_LIBEXECDIR)
$(MODPERL_TEST_F) $(MODPERL_LIB_DSO) && \
$(MODPERL_CP) $(MODPERL_LIB_DSO) $(DESTDIR)$(MODPERL_AP_LIBEXECDIR)
EOI
}
$install .= <<'EOI';
# install mod_perl .h files
@$(MKPATH) $(DESTDIR)$(MODPERL_AP_INCLUDEDIR)
$(MODPERL_CP) $(MODPERL_H_FILES) $(DESTDIR)$(MODPERL_AP_INCLUDEDIR)
EOI
my $mf = $self->default_file('makefile');
open my $fh, '>', $mf or die "open $mf: $!";
print $fh noedit_warning_hash();
print $fh $self->canon_make_attr('makefile', basename $mf);
$self->make_tools($fh);
print $fh $self->canon_make_attr('ap_libs', $self->apache_libs);
print $fh $self->canon_make_attr('libname', $self->{MP_LIBNAME});
print $fh $self->canon_make_attr('dlext', 'so'); #always use .so
if (AIX) {
my $xs_dir = $self->file_path("xs");
print $fh "BASEEXT = $xs_dir/modperl_global\n\n";
}
my %libs = (
dso => "$self->{MP_LIBNAME}.$self->{MODPERL_DLEXT}",
static => "$self->{MP_LIBNAME}$self->{MODPERL_LIB_EXT}",
);
#XXX short-term compat for Apache::TestConfigPerl
$libs{shared} = $libs{dso};
foreach my $type (sort keys %libs) {
my $lib = $libs{$type};
print $fh $self->canon_make_attr("lib_$type", $libs{$type});
}
if (my $symbols = $self->modperl_symbols) {
print $fh $self->canon_make_attr('lib_symbols', $symbols);
$install .= <<'EOI';
# install mod_perl symbol file
@$(MKPATH) $(MODPERL_AP_LIBEXECDIR)
$(MODPERL_TEST_F) $(MODPERL_LIB_SYMBOLS) && \
$(MODPERL_CP) $(MODPERL_LIB_SYMBOLS) $(MODPERL_AP_LIBEXECDIR)
EOI
}
if ($self->is_dynamic && (my $libs = $self->modperl_libpath)) {
print $fh $self->canon_make_attr('lib_location', $libs);
# Visual Studio 8 on Win32 uses manifest files
if (WIN32) {
(my $manifest = $libs) =~ s/\.lib$/.so.manifest/;
print $fh $self->canon_make_attr('manifest_location', $manifest);
}
print $fh $self->canon_make_attr('ap_libdir',
$self->ap_destdir(catdir $self->{MP_AP_PREFIX}, 'lib')
);
$install .= <<'EOI';
# install mod_perl.lib
@$(MKPATH) $(MODPERL_AP_LIBDIR)
$(MODPERL_TEST_F) $(MODPERL_LIB_LOCATION) && \
$(MODPERL_CP) $(MODPERL_LIB_LOCATION) $(MODPERL_AP_LIBDIR)
EOI
}
my $libperl = join '/',
$self->perl_config('archlibexp'), 'CORE', $self->perl_config('libperl');
#this is only used for deps, if libperl has changed, relink mod_perl.so
#not all perl dists put libperl where it should be, so just leave this
#out if it isn't in the proper place
if (-e $libperl) {
print $fh $self->canon_make_attr('libperl', $libperl);
}
for my $method (qw(ccopts ldopts inc)) {
print $fh $self->canon_make_attr($method, $self->$method());
}
for my $method (qw(c_files o_files o_pic_files h_files)) {
print $fh $self->canon_make_attr($method, @{ $code->$method() });
}
my @libs;
for my $type (sort map { uc } keys %libs) {
next unless $self->{"MP_USE_$type"};
# on win32 mod_perl.lib must come after mod_perl.so
$type eq 'STATIC'
? push @libs, $self->{"MODPERL_LIB_$type"}
: unshift @libs, $self->{"MODPERL_LIB_$type"};
}
print $fh $self->canon_make_attr('lib', "@libs");
print $fh $self->canon_make_attr('AP_INCLUDEDIR',
$self->ap_destdir($self->install_headers_dir));
print $fh $self->canon_make_attr('AP_LIBEXECDIR',
$self->ap_destdir($self->apxs(-q => 'LIBEXECDIR')));
my $xs_targ = $self->make_xs($fh);
print $fh <<'EOF';
MODPERL_CCFLAGS = $(MODPERL_INC) $(MODPERL_CCOPTS) $(MODPERL_OPTIMIZE)
MODPERL_CCFLAGS_SHLIB = $(MODPERL_CCFLAGS) $(MODPERL_CCCDLFLAGS)
MODPERL_OBJS = $(MODPERL_O_FILES) $(MODPERL_XS_O_FILES)
MODPERL_PIC_OBJS = $(MODPERL_O_PIC_FILES) $(MODPERL_XS_O_PIC_FILES)
MKPATH = $(MODPERL_PERLPATH) "-MExtUtils::Command" -e mkpath
all: lib
lib: $(MODPERL_LIB)
EOF
print $fh $install;
print $fh <<'EOF' if DMAKE;
.USESHELL :
EOF
print $fh <<'EOF';
.SUFFIXES: .xs .c $(MODPERL_OBJ_EXT) .lo .i .s
.c.lo:
$(MODPERL_CC) $(MODPERL_CCFLAGS_SHLIB) \
-c $< && $(MODPERL_MV) $*$(MODPERL_OBJ_EXT) $*.lo
.c$(MODPERL_OBJ_EXT):
$(MODPERL_CC) $(MODPERL_CCFLAGS) -c $<
.c.i:
$(MODPERL_CPPRUN) $(MODPERL_CCFLAGS) -c $< > $*.i
.c.s:
$(MODPERL_CC) -O -S $(MODPERL_CCFLAGS) -c $<
.xs.c:
$(MODPERL_XSUBPP) $*.xs >$@
.xs$(MODPERL_OBJ_EXT):
$(MODPERL_XSUBPP) $*.xs >$*.c
$(MODPERL_CC) $(MODPERL_CCFLAGS) -c $*.c
.xs.lo:
$(MODPERL_XSUBPP) $*.xs >$*.c
$(MODPERL_CC) $(MODPERL_CCFLAGS_SHLIB) \
-c $*.c && $(MODPERL_MV) $*$(MODPERL_OBJ_EXT) $*.lo
clean:
$(MODPERL_RM_F) *.a *.so *.xsc \
$(MODPERL_LIBNAME).exp $(MODPERL_LIBNAME).lib \
*$(MODPERL_OBJ_EXT) *.lo *.i *.s *.pdb *.manifest \
$(MODPERL_CLEAN_FILES) \
$(MODPERL_XS_CLEAN_FILES)
$(MODPERL_OBJS): $(MODPERL_H_FILES) $(MODPERL_MAKEFILE)
$(MODPERL_PIC_OBJS): $(MODPERL_H_FILES) $(MODPERL_MAKEFILE)
$(MODPERL_LIB): $(MODPERL_LIBPERL)
$(MODPERL_LIBNAME)$(MODPERL_LIB_EXT): $(MODPERL_OBJS)
$(MODPERL_RM_F) $@
$(MODPERL_AR) crv $@ $(MODPERL_OBJS)
$(MODPERL_RANLIB) $@
EOF
print $fh $self->dynamic_link;
print $fh @$xs_targ;
print $fh "\n"; # Makefile must end with \n to avoid warnings
close $fh;
}
#--- generate MakeMaker parameter values ---
sub otherldflags_default {
my $self = shift;
# e.g. aix's V:ldflags feeds -brtl and other flags
$self->perl_config('ldflags');
}
sub otherldflags {
my $self = shift;
my $flags = \&{"otherldflags_$^O"};
return $self->otherldflags_default unless defined &$flags;
$flags->($self);
}
sub otherldflags_MSWin32 {
my $self = shift;
my $flags = $self->otherldflags_default;
$flags .= ' -pdb:$(INST_ARCHAUTODIR)\$(BASEEXT).pdb' if $self->{MP_DEBUG};
$flags;
}
sub typemaps {
my $self = shift;
my @typemaps = ();
# XXX: could move here the code from ModPerl::BuildMM
return [] if IS_MOD_PERL_BUILD;
# for post install use
for (@INC) {
# make sure not to pick mod_perl 1.0 typemap
my $file = "$_/auto/Apache2/typemap";
push @typemaps, $file if -e $file;
}
return \@typemaps;
}
sub includes {
my $self = shift;
my @inc = ();
unless (IS_MOD_PERL_BUILD) {
# XXX: what if apxs is not available? win32?
my $ap_inc = $self->apxs('-q' => 'INCLUDEDIR');
if ($ap_inc && -d $ap_inc) {
push @inc, $ap_inc;
return \@inc;
}
# this is fatal
my $reason = $ap_inc
? "path $ap_inc doesn't exist"
: "apxs -q INCLUDEDIR didn't return a value";
die "Can't find the mod_perl include dir (reason: $reason)";
}
my $os = WIN32 ? 'win32' : 'unix';
push @inc, $self->file_path("src/modules/perl", "xs");
push @inc, $self->mp_include_dir;
unless ($self->httpd_is_source_tree) {
push @inc, $self->apr_includedir;
my $apuc = $self->apu_config_path;
if ($apuc && -x $apuc) {
chomp(my $apuincs = qx($apuc --includes));
# win32: /Ipath, elsewhere -Ipath
$apuincs =~ s{^\s*(-|/)I}{};
push @inc, $apuincs;
}
my $ainc = $self->apxs('-q' => 'INCLUDEDIR');
if (-d $ainc) {
push @inc, $ainc;
return \@inc;
}
}
if ($self->{MP_AP_PREFIX}) {
my $src = $self->dir;
for ("$src/modules/perl", "$src/include",
"$src/srclib/apr/include",
"$src/srclib/apr-util/include",
"$src/os/$os")
{
push @inc, $_ if -d $_;
}
}
return \@inc;
}
sub inc {
local $_;
my @includes = map { "-I$_" } @{ shift->includes };
"@includes";
}
### Picking the right LFS support flags for mod_perl, by Joe Orton ###
#
# on Unix systems where by default off_t is a "long", a 32-bit integer,
# there are two different ways to get "large file" support, i.e. the
# ability to manipulate files bigger than 2Gb:
#
# 1) you compile using -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64. This
# makes sys/types.h expose off_t as a "long long", a 64-bit integer, and
# changes the size of a few other types too. The C library headers
# automatically arrange to expose a correct implementation of functions
# like lseek() which take off_t parameters.
#
# 2) you compile using -D_LARGEFILE64_SOURCE, and use what is called the
# "transitional" interface. This means that the system headers expose a
# new type, "off64_t", which is a long long, but the size of off_t is not
# changed. A bunch of new functions like lseek64() are exposed by the C
# library headers, which take off64_t parameters in place of off_t.
#
# Perl built with -Duselargefiles uses approach (1).
#
# APR HEAD uses (2) by default. APR 0.9 does not by default use either
# approach, but random users can take a httpd-2.0.49 tarball, and do:
#
# export CPPFLAGS="-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
# ./configure
#
# to build a copy of apr/httpd which uses approach (1), though this
# isn't really a supported configuration.
#
# The problem that mod_perl has to work around is when you take a
# package built with approach (1), i.e. Perl, and any package which was
# *not* built with (1), i.e. APR, and want to interface between
# them. [1]
#
# So what you want to know is whether APR was built using approach (1)
# or not. APR_HAS_LARGE_FILES in HEAD just tells you whether APR was
# built using approach (2) or not, which isn't useful in solving this
# problem.
#
# [1]: In some cases, it may be OK to interface between packages which
# use (1) and packages which use (2). APR HEAD is currently not such a
# case, since the size of apr_ino_t is still changing when
# _FILE_OFFSET_BITS is defined.
#
# If you want to see how this matters, get some httpd function to do at
# the very beginning of main():
#
# printf("sizeof(request_rec) = %lu, sizeof(apr_finfo_t) = %ul",
# sizeof(request_rec), sizeof(apr_finfo_t));
#
# and then put the same printf in mod_perl somewhere, and see the
# differences. This is why it is a really terribly silly idea to ever
# use approach (1) in anything other than an entirely self-contained
# application.
#
# there is no conflict if both libraries either have or don't have
# large files support enabled
sub has_large_files_conflict {
my $self = shift;
my $apxs_flags = join $self->apxs_extra_cflags, $self->apxs_extra_cppflags;
my $apr_lfs64 = $apxs_flags =~ /-D_FILE_OFFSET_BITS=64/;
my $perl_lfs64 = $Config{ccflags} =~ /-D_FILE_OFFSET_BITS=64/;
# XXX: we don't really deal with the case where APR was built with
# -D_FILE_OFFSET_BITS=64 but perl wasn't, since currently we strip
# only perl's ccflags, not apr's flags. the reason we don't deal
# with it is that we didn't have such a case yet, but may need to
# deal with it later
return 0;
# $perl_lfs64 ^ $apr_lfs64;
}
# if perl is built with uselargefiles, but apr not, the build won't
# work together as it uses two binary incompatible libraries, so
# reduce the functionality to the greatest common denominator (C code
# will have to make sure to prevent any operations that may rely on
# effects created by uselargefiles, e.g. Off_t=8 instead of Off_t=4)
sub strip_lfs {
my ($self, $cflags) = @_;
return $cflags unless $self->has_large_files_conflict();
my $lf = $Config{ccflags_uselargefiles}
|| '-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64';
$cflags =~ s/$lf//;
$cflags;
}
sub define {
my $self = shift;
return "";
}
1;
__END__
=head1 NAME
Apache2::Build - Methods for locating and parsing bits of Apache source code
=head1 SYNOPSIS
use Apache2::Build ();
my $build = Apache2::Build->new;
# rebuild mod_perl with build opts from the previous build
% cd modperl-2.0
% perl -MApache2::Build -e rebuild
=head1 DESCRIPTION
This module provides methods for locating and parsing bits of Apache
source code.
Since mod_perl remembers what build options were used to build it, you
can use this knowledge to rebuild it using the same options. Simply
chdir to the mod_perl source directory and run:
% cd modperl-2.0
% perl -MApache2::Build -e rebuild
If you want to rebuild not yet installed, but already built mod_perl,
run from its root directory:
% perl -Ilib -MApache2::Build -e rebuild
=head1 METHODS
=over 4
=item new
Create an object blessed into the B<Apache2::Build> class.
my $build = Apache2::Build->new;
=item dir
Top level directory where source files are located.
my $dir = $build->dir;
-d $dir or die "can't stat $dir $!\n";
=item find
Searches for apache source directories, return a list of those found.
Example:
for my $dir ($build->find) {
my $yn = prompt "Configure with $dir ?", "y";
...
}
=item inc
Print include paths for MakeMaker's B<INC> argument to
C<WriteMakefile>.
Example:
use ExtUtils::MakeMaker;
use Apache2::Build ();
WriteMakefile(
'NAME' => 'Apache2::Module',
'VERSION' => '0.01',
'INC' => Apache2::Build->new->inc,
);
=item module_magic_number
Return the B<MODULE_MAGIC_NUMBER> defined in the apache source.
Example:
my $mmn = $build->module_magic_number;
=item httpd_version
Return the server version.
Example:
my $v = $build->httpd_version;
=item otherldflags
Return other ld flags for MakeMaker's B<dynamic_lib> argument to
C<WriteMakefile>. This might be needed on systems like AIX that need
special flags to the linker to be able to reference mod_perl or httpd
symbols.
Example:
use ExtUtils::MakeMaker;
use Apache2::Build ();
WriteMakefile(
'NAME' => 'Apache2::Module',
'VERSION' => '0.01',
'INC' => Apache2::Build->new->inc,
'dynamic_lib' => {
'OTHERLDFLAGS' => Apache2::Build->new->otherldflags,
},
);
=back
=head1 AUTHOR
Doug MacEachern
=cut