# please insert nothing before this line: -*- mode: cperl; cperl-indent-level: 4; cperl-continued-statement-offset: 4; indent-tabs-mode: nil -*-
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package ModPerl::MM;
use strict;
use warnings;
use ExtUtils::MakeMaker ();
use ExtUtils::Install ();
use Cwd ();
use Carp;
our %PM; #add files to installation
# MM methods that this package overrides
no strict 'refs';
my $stash = \%{__PACKAGE__ . '::MY::'};
my @methods = grep *{$stash->{$_}}{CODE}, keys %$stash;
my $eu_mm_mv_all_methods_overriden = 0;
use strict 'refs';
sub override_eu_mm_mv_all_methods {
my @methods = @_;
my $orig_sub = \&ExtUtils::MakeMaker::mv_all_methods;
no warnings 'redefine';
*ExtUtils::MakeMaker::mv_all_methods = sub {
# do the normal move
$orig_sub->(@_);
# for all the overloaded methods mv_all_method installs a stab
# eval "package MY; sub $method { shift->SUPER::$method(\@_); }";
# therefore we undefine our methods so on the recursive invocation of
# Makefile.PL they will be undef, unless defined in Makefile.PL
# and my_import will override these methods properly
for my $sym (@methods) {
my $name = "MY::$sym";
undef &$name if defined &$name;
}
};
}
sub add_dep {
my ($string, $targ, $add) = @_;
$$string =~ s/($targ\s+::)/$1 $add/;
}
sub add_dep_before {
my ($string, $targ, $before_targ, $add) = @_;
$$string =~ s/($targ\s+::.*?) ($before_targ)/$1 $add $2/;
}
sub add_dep_after {
my ($string, $targ, $after_targ, $add) = @_;
$$string =~ s/($targ\s+::.*?$after_targ)/$1 $add/;
}
my $build;
sub build_config {
my $key = shift;
require Apache2::Build;
$build ||= Apache2::Build->build_config;
return $build unless $key;
$build->{$key};
}
#the parent WriteMakefile moves MY:: methods into a different class
#so alias them each time WriteMakefile is called in a subdir
sub my_import {
my $package = shift;
no strict 'refs';
my $stash = \%{$package . '::MY::'};
for my $sym (keys %$stash) {
next unless *{$stash->{$sym}}{CODE};
my $name = "MY::$sym";
# the method is defined in Makefile.PL
next if defined &$name;
# do the override behind the scenes
*$name = *{$stash->{$sym}}{CODE};
}
}
my @default_opts = qw(CCFLAGS LIBS INC OPTIMIZE LDDLFLAGS TYPEMAPS);
my @default_dlib_opts = qw(OTHERLDFLAGS);
my @default_macro_opts = ();
my %opts = (
CCFLAGS => sub { $build->{MODPERL_CCOPTS} },
LIBS => sub { join ' ', $build->apache_libs, $build->modperl_libs },
INC => sub { $build->inc; },
OPTIMIZE => sub { $build->perl_config('optimize'); },
LDDLFLAGS => sub { $build->perl_config('lddlflags'); },
TYPEMAPS => sub { $build->typemaps; },
OTHERLDFLAGS => sub { $build->otherldflags; },
);
sub get_def_opt {
my $opt = shift;
return $opts{$opt}->() if exists $opts{$opt};
# handle cases when Makefile.PL wants an option we don't have a
# default for. XXX: some options expect [] rather than scalar.
Carp::carp("!!! no default argument defined for argument: $opt");
return '';
}
sub WriteMakefile {
my %args = @_;
# override ExtUtils::MakeMaker::mv_all_methods
# can't do that on loading since ModPerl::MM is also use()'d
# by ModPerl::BuildMM which itself overrides it
unless ($eu_mm_mv_all_methods_overriden) {
override_eu_mm_mv_all_methods(@methods);
$eu_mm_mv_all_methods_overriden++;
}
$build ||= build_config();
my_import(__PACKAGE__);
# set top-level WriteMakefile's values if weren't set already
for my $o (@default_opts) {
$args{$o} = get_def_opt($o) unless exists $args{$o}; # already defined
}
# set dynamic_lib-level WriteMakefile's values if weren't set already
$args{dynamic_lib} ||= {};
my $dlib = $args{dynamic_lib};
for my $o (@default_dlib_opts) {
$dlib->{$o} = get_def_opt($o) unless exists $dlib->{$o};
}
# set macro-level WriteMakefile's values if weren't set already
$args{macro} ||= {};
my $macro = $args{macro};
for my $o (@default_macro_opts) {
$macro->{$o} = get_def_opt($o) unless exists $macro->{$o};
}
ExtUtils::MakeMaker::WriteMakefile(%args);
}
#### MM overrides ####
sub ModPerl::MM::MY::post_initialize {
my $self = shift;
$build ||= build_config();
my $pm = $self->{PM};
while (my ($k, $v) = each %PM) {
if (-e $k) {
$pm->{$k} = $v;
}
}
'';
}
1;
=head1 NAME
ModPerl::MM -- A "subclass" of ExtUtils::MakeMaker for mod_perl 2.0
=head1 Synopsis
use ModPerl::MM;
# ModPerl::MM takes care of doing all the dirty job of overriding
ModPerl::MM::WriteMakefile(...);
# if there is a need to extend the default methods
sub MY::constants {
my $self = shift;
$self->ModPerl::MM::MY::constants;
# do something else;
}
# or prevent overriding completely
sub MY::constants { shift->MM::constants(@_); }";
# override the default value of WriteMakefile's attribute
my $extra_inc = "/foo/include";
ModPerl::MM::WriteMakefile(
...
INC => $extra_inc,
...
);
# extend the default value of WriteMakefile's attribute
my $extra_inc = "/foo/include";
ModPerl::MM::WriteMakefile(
...
INC => join " ", $extra_inc, ModPerl::MM::get_def_opt('INC'),
...
);
=head1 Description
C<ModPerl::MM> is a "subclass" of C<ExtUtils::MakeMaker> for mod_perl
2.0, to a degree of sub-classability of C<ExtUtils::MakeMaker>.
When C<ModPerl::MM::WriteMakefile()> is used instead of
C<ExtUtils::MakeMaker::WriteMakefile()>, C<ModPerl::MM> overrides
several C<ExtUtils::MakeMaker> methods behind the scenes and supplies
default C<WriteMakefile()> arguments adjusted for mod_perl 2.0
build. It's written in such a way so that normally 3rd party module
developers for mod_perl 2.0, don't need to mess with I<Makefile.PL> at
all.
=head1 C<MY::> Default Methods
C<ModPerl::MM> overrides method I<foo> as long as I<Makefile.PL>
hasn't already specified a method I<MY::foo>. If the latter happens,
C<ModPerl::MM> will DWIM and do nothing.
In case the functionality of C<ModPerl::MM> methods needs to be
extended, rather than completely overridden, the C<ModPerl::MM> methods
can be called internally. For example if you need to modify constants
in addition to the modifications applied by
C<ModPerl::MM::MY::constants>, call the C<ModPerl::MM::MY::constants>
method (notice that it resides in the package C<ModPerl::MM::MY> and
not C<ModPerl::MM>), then do your extra manipulations on constants:
# if there is a need to extend the methods
sub MY::constants {
my $self = shift;
$self->ModPerl::MM::MY::constants;
# do something else;
}
In certain cases a developers may want to prevent from C<ModPerl::MM>
to override certain methods. In that case an explicit override in
I<Makefile.PL> will do the job. For example if you don't want the
C<constants()> method to be overridden by C<ModPerl::MM>, add to your
I<Makefile.PL>:
sub MY::constants { shift->MM::constants(@_); }";
C<ModPerl::MM> overrides the following methods:
=head2 C<ModPerl::MM::MY::post_initialize>
This method is deprecated.
=head1 C<WriteMakefile()> Default Arguments
C<ModPerl::MM::WriteMakefile> supplies default arguments such as
C<INC> and C<TYPEMAPS> unless they weren't passed to
C<ModPerl::MM::WriteMakefile> from I<Makefile.PL>.
If the default values aren't satisfying these should be overridden in
I<Makefile.PL>. For example to supply an empty INC, explicitly set the
argument in I<Makefile.PL>.
ModPerl::MM::WriteMakefile(
...
INC => '',
...
);
If instead of fully overriding the default arguments, you want to
extend or modify them, they can be retrieved using the
C<ModPerl::MM::get_def_opt()> function. The following example appends
an extra value to the default C<INC> attribute:
my $extra_inc = "/foo/include";
ModPerl::MM::WriteMakefile(
...
INC => join " ", $extra_inc, ModPerl::MM::get_def_opt('INC'),
...
);
C<ModPerl::MM> supplies default values for the following
C<ModPerl::MM::WriteMakefile> attributes:
=head2 C<CCFLAGS>
=head2 C<LIBS>
=head2 C<INC>
=head2 C<OPTIMIZE>
=head2 C<LDDLFLAGS>
=head2 C<TYPEMAPS>
=head2 C<dynamic_lib>
=head3 C<OTHERLDFLAGS>
dynamic_lib => { OTHERLDFLAGS => ... }
=head2 C<macro>
=head3 C<MOD_INSTALL>
macro => { MOD_INSTALL => ... }
makes sure that Apache-Test/ is added to @INC.
=head1 Public API
The following functions are a part of the public API. They are
described elsewhere in this document.
=head2 C<WriteMakefile()>
ModPerl::MM::WriteMakefile(...);
=head2 C<get_def_opt()>
my $def_val = ModPerl::MM::get_def_opt($key);
=cut