# 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 (); 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 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') ." ". $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); 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', '--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-2.0 -lgtop_sysdeps-2.0 -lgtop_common-2.0 $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"; } # 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; #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 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(<{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 < $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 <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 $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, < \$*.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\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}; 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); # 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 (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 *.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 { 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 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 argument to C. 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 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 argument to C. 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