blob: 4122048d6e765415a3f36f6019b9b7a540becfda [file] [log] [blame]
# Copyright 2000-2005 The Apache Software Foundation
#
# Licensed 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 ();
use constant IS_MOD_PERL_BUILD => grep { -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 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 $apxs to the path if found,
# otherwise to ''
my $apxs; # undef so we know we haven't tried to set it yet
sub find_apxs_util {
my $self = shift;
$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) {
$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 $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 ($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($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($apxs @_ 2>&1);
chomp $error if defined $error;
if ($error) {
error "'$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');
$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);
sub mpm_is_threaded {
my $self = shift;
my $mpm_name = $self->mpm_name();
return $threaded_mpms{$mpm_name} || 0;
}
sub mpm_name {
my $self = shift;
return $self->{mpm_name} if $self->{mpm_name};
# XXX: hopefully apxs will work on win32 one day
return $self->{mpm_name} = 'winnt' if WIN32;
my $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',
"$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 -DAP_DEBUG";
$ccopts .= " -DAP_HAVE_DESIGNATED_INITIALIZER";
}
if ($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";
}
# 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;
$ccopts;
}
sub has_gcc_version {
my $self = shift;
my $requested_version = shift;
my $has_version = $self->perl_config('gccversion');
return 0 unless $has_version;
my @tuples = split /\./, $has_version, 3;
my @r_tuples = split /\./, $requested_version, 3;
return cmp_tuples(\@tuples, \@r_tuples) == 1;
}
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',
@_,
}, $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;
[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;
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) {
if (my $link = $_ && -x $_ && qx{$_ --link-ld --libs}) {
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 $string = (split /\s+/, scalar(<$fh>))[-1];
$version = join '.', $major, $minor, "$patch$string";
$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 $header = catfile $self->apr_includedir, "apr.h";
open my $fh, $header or do {
error "Unable to open $header: $!";
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;
while (my ($name, $xs) = each %{ $self->{XS} }) {
#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 = (@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 => '');
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';
$mm->init_main;
$mm->init_others;
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\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,--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 @libs = map { "$prefix/lib/lib$_.lib" } qw(apr aprutil httpd);
"@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};
while (my ($type, $lib) = each %libs) {
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);
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 (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';
.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 \
$(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 {
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 $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