blob: 555214a87b71d5ab48749a7dfa2528b7b15654ba [file] [log] [blame]
use strict;
use ModPerl::MM;
use 5.005;
use Apache::Test5005compat;
use Apache::TestMM qw(test clean);
use Apache::TestReport ();
use Apache::TestSmoke ();
use Apache::TestRun ();
use Apache::TestConfigPerl ();
use Apache::TestSmokePerl ();
use Apache::TestReportPerl ();
use Config;
use File::Find qw(finddepth);
use File::Basename;
use Apache2::Build;
use constant WIN32 => Apache2::Build::WIN32;
use Cwd;
use ExtUtils::XSBuilder::ParseSource;
my $version = "2.XX-dev"; # DUMMY VALUE
my $cwd = WIN32 ?
Win32::GetLongPathName(cwd) : cwd;
$cwd =~ m{^(.+)/glue/perl$} or die "Can't find base directory";
my $base_dir = $1;
my $inc_dir = "$base_dir/include";
my $lib_dir = "$base_dir/library";
my $xs_dir = "$base_dir/glue/perl/xsbuilder";
sub slurp($$)
{
open my $file, $_[1] or die "Can't open $_[1]: $!";
read $file, $_[0], -s $file;
}
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 autoconf_foo {
my ($config, $re_start, $re_end, $re_match) = @_;
$$config =~ /^${re_start}APACHE2_INCLUDES${re_end}($re_match)/m or
die "Can't find apache include directory";
my $apache_includes = $1;
$$config =~ /^${re_start}APR_INCLUDES${re_end}($re_match)/m or
die "Can't find apache include directory";
$apache_includes .= " $1";
my $apr_libs ="";
$$config =~ m/^${re_start}APREQ_LIBNAME${re_end}($re_match)/m or
die "Can't find apreq libname";
## XXX: 2.60 bug/hack
my $apreq_libname = $1;
$$config =~ m/^${re_start}PACKAGE_VERSION${re_end}($re_match)/m or
die "Can't find package version";
my $version = $1;
## Code around an autoconf 2.60 bug
## http://lists.gnu.org/archive/html/bug-autoconf/2006-06/msg00127.html
## $ grep @PACKAGE_VERSION config.status-2.59 config.status-2.60
## config.status-2.59:s,@PACKAGE_VERSION@,2.09,;t t
## config.status-2.60:s,@PACKAGE_VERSION@,|#_!!_#|2.09,g
foreach ($apache_includes, $apreq_libname, $version) {
s/\|#_!!_#\|//g;
}
return ($apache_includes, $apr_libs, $apreq_libname, $version);
}
my ($apache_includes, $apache_dir, $apr_libs, $apreq_libname, $perl_lib);
if (WIN32) {
# XXX May need fixing, Randy!
slurp my $config => "$base_dir/configure.ac";
$config =~ /^AC_INIT[^,]+,\s*([^,\s]+)/m or
die "Can't find version string";
$version = $1;
slurp my $make => "$base_dir/Makefile";
$make =~ /^APACHE=(\S+)/m or
die "Cannot find top-level Apache directory";
($apache_dir = $1) =~ s!\\!/!g;
($apache_includes = "-I$apache_dir" . '/include') =~ s!\\!/!g;
($apr_libs = "-L$apache_dir" . '/lib') =~ s!\\!/!g;
$make =~ /^APR_LIB=(\S+)/m or
die "Cannot find apr lib";
$apr_libs .= ' -l' . basename($1, '.lib');
$make =~ /^APU_LIB=(\S+)/m or
die "Cannot find aprutil lib";
$apr_libs .= ' -l' . basename($1, '.lib');
$apreq_libname = 'apreq2';
$perl_lib = $Config{installsitelib} . '\auto\libaprext';
$perl_lib =~ s{\\}{\\\\}g;
}
else {
slurp my $config => "$base_dir/config.status";
$config =~ /GNU Autoconf (\d+\.\d+)/;
my $autoconf_ver = $1;
### XXX: Lord have mercy on us.....
if (cmp_tuples([split /\./, $autoconf_ver], [qw(2 61)]) > 0) {
### Autoconf >=2.62 changed the format of the file
### I.E.: S["APACHE2_INCLUDES"]="-I/usr/local/include/apache2"
($apache_includes, $apr_libs, $apreq_libname, $version) =
autoconf_foo(\$config, qr/S\[\"/, qr/\"\]=\"/, qr/[^\"]+/);
}
else {
### I.E.: s,@APACHE2_INCLUDES@,-I/usr/local/include/apache22,;t t
($apache_includes, $apr_libs, $apreq_libname, $version) =
autoconf_foo(\$config, qr/s,\@/, qr/\@,/, qr/[^,]+/);
}
}
my $apreq_libs;
if (WIN32) {
$apreq_libs = qq{-L$base_dir/win32/libs -llib$apreq_libname -lmod_apreq2 -L$perl_lib -llibaprext -L$apache_dir/lib -lmod_perl};
} else {
my $apreq2_config = "$base_dir/apreq2-config";
my $bindir = qx{$apreq2_config --bindir};
chomp $bindir;
$apreq2_config = "$bindir/apreq2-config" if $ENV{INSTALL};
$apreq_libs = qx{$apreq2_config --link-ld --ldflags --libs};
chomp $apreq_libs;
}
my $mp2_typemaps = Apache2::Build->new->typemaps;
package My::ParseSource;
use base qw/ExtUtils::XSBuilder::ParseSource/;
use constant WIN32 => ($^O =~ /Win32/i);
my @dirs = ("$base_dir/include", "$base_dir/module/apache2");
sub package {'APR::Request'}
sub unwanted_includes {[qw/apreq_config.h apreq_private_apache2.h/]}
# ParseSource.pm v 0.23 bug: line 214 should read
# my @dirs = @{$self->include_dirs};
# for now, we override it here just to work around the bug
sub find_includes {
my $self = shift;
return $self->{includes} if $self->{includes};
require File::Find;
my(@dirs) = @{$self->include_dirs};
unless (-d $dirs[0]) {
die "could not find include directory";
}
# print "Will search @dirs for include files...\n" if ($verbose) ;
my @includes;
my $unwanted = join '|', @{$self -> unwanted_includes} ;
for my $dir (@dirs) {
File::Find::finddepth({
wanted => sub {
return unless /\.h$/;
return if ($unwanted && (/^($unwanted)/o));
my $dir = $File::Find::dir;
push @includes, "$dir/$_";
},
follow => not WIN32,
}, $dir);
}
return $self->{includes} = $self -> sort_includes (\@includes) ;
}
sub include_dirs {\@dirs}
package My::WrapXS;
use base qw/ExtUtils::XSBuilder::WrapXS/;
our $VERSION = $version;
use constant WIN32 => ($^O =~ /Win32/i);
##################################################
# Finally, we get to the actual script...
__PACKAGE__ -> run;
my @scripts = ();
use File::Spec::Functions qw(catfile);
File::Find::finddepth(sub {
return unless /(.*?\.pl)\.PL$/;
push @scripts, "$File::Find::dir/$1";
}, '.');
Apache::TestMM::filter_args();
Apache::TestMM::generate_script("t/TEST");
Apache::TestSmokePerl->generate_script;
Apache::TestReportPerl->generate_script;
my %opts = (
NAME => 'libapreq2',
DIR => [qw(xs)],
clean => { FILES => "xs t/logs t/TEST @scripts" },
realclean => { FILES => "xsbuilder/tables" },
);
ModPerl::MM::WriteMakefile(%opts);
# That's the whole script - below is just a bunch of local overrides
##################################################
sub get_functions {
my $self = shift;
$self->{XS}->{"APR::Request::Error"} ||= [];
$self->SUPER::get_functions;
}
sub test_docs {
my ($pods, $tests) = @_;
require Config;
my $bin = $Config::Config{bin};
my $pod2test = catfile $bin, "pod2test";
$pod2test = Apache::TestConfig::which('pod2test')
unless -e $pod2test;
return "" unless $pod2test and -e $pod2test;
return join "", map <<EOT, 0..$#$pods;
$$tests[$_]: $$pods[$_]
\$(FULLPERLRUN) $pod2test $$pods[$_] $$tests[$_]
EOT
}
sub MY::postamble {
my @docs = (<xsbuilder/APR/Request/*/*.pod>, <xsbuilder/APR/Request/*.pod>);
my @tests = @docs;
s/pod$/t/ for @tests;
s/^xsbuilder/xs/ for @tests;
my $string = "";
my $test_docs = test_docs(\@docs, \@tests);
if ($test_docs) {
$string .= $test_docs;
$string .= <<EOT;
doc_test : @tests
\$(FULLPERLRUN) "-Mblib" "-MTest::Harness" "-e" "runtests(\@ARGV)" @tests
test :: doc_test
EOT
} else {
$string .= <<EOT;
test ::
\$(NOECHO) \$(ECHO) pod2test was not found, skipping inlined tests
EOT
}
return $string;
}
sub parsesource_objects {[My::ParseSource->new]}
sub new_typemap {My::TypeMap->new(shift)}
sub h_filename_prefix {'apreq_xs_'}
sub my_xs_prefix {'apreq_xs_'}
sub xs_include_dir { $xs_dir }
sub mod_xs {
my($self, $module, $complete) = @_;
my $dirname = $self->class_dirname($module);
my @parts = split '::', $module;
my $mod_xs = "$dirname/$parts[-1].xs";
for ($self -> xs_include_dir, @{ $self->{glue_dirs} }) {
my $file = "$_/$mod_xs";
$mod_xs = $file if $complete;
return $mod_xs if -e $file;
}
undef;
}
sub mod_pm {
my($self, $module, $complete) = @_;
my $dirname = $self->class_dirname($module);
my @parts = split '::', $module;
my $mod_pm = "$dirname/$parts[-1].pm";
for ($self -> xs_include_dir, @{ $self->{glue_dirs} }) {
my $file = "$_/$mod_pm";
$mod_pm = $file if $complete;
return $mod_pm if -e $file;
}
undef;
}
#inline mod_xs directly, so we can put XS directives there
sub write_xs {
my($self, $module, $functions) = @_;
my $fh = $self->open_class_file($module, '.xs');
print $fh "$self->{noedit_warning_c}\n";
my @includes = @{ $self->includes };
if (my $mod_h = $self->mod_h($module)) {
push @includes, $mod_h;
}
for (@includes) {
print $fh qq{\#include "$_"\n\n};
}
if (my $mod_xs = $self->mod_xs($module, 1)) {
open my $file, $mod_xs or die "can't open $mod_xs: $!";
print $fh $_ while <$file>;
print $fh "\n\n";
}
my $last_prefix = "";
my $fmap = $self -> typemap -> {function_map} ;
my $myprefix = $self -> my_xs_prefix ;
for my $func (@$functions) {
my $class = $func->{class};
if ($class)
{
my $prefix = $func->{prefix};
$last_prefix = $prefix if $prefix;
if ($func->{name} =~ /^$myprefix/o) {
#e.g. mpxs_Apache__RequestRec_
my $class_prefix = $fmap -> class_c_prefix($class);
if ($func->{name} =~ /$class_prefix/) {
$prefix = $fmap -> class_xs_prefix($class);
}
}
$prefix = $prefix ? " PREFIX = $prefix" : "";
print $fh "MODULE = $module PACKAGE = $class $prefix\n\n";
}
print $fh $func->{code};
}
if (my $destructor = $self->typemap->destructor($last_prefix)) {
my $arg = $destructor->{argspec}[0];
print $fh <<EOF;
void
$destructor->{name}($arg)
$destructor->{class} $arg
EOF
}
print $fh "PROTOTYPES: disabled\n\n";
print $fh "BOOT:\n";
print $fh $self->boot($module);
print $fh " items = items; /* -Wall */\n\n";
if (my $newxs = $self->{newXS}->{$module}) {
for my $xs (@$newxs) {
print $fh qq{ cv = newXS("$xs->[0]", $xs->[1], __FILE__);\n};
print $fh qq{ GvSHARED_on(CvGV(cv));\n} if ExtUtils::XSBuilder::WrapXS::GvSHARED();
}
}
close $fh;
}
sub mod_pod {
my($self, $module, $complete) = @_;
my $dirname = $self->class_dirname($module);
my @parts = split '::', $module;
my $mod_pod = "$dirname/$parts[-1].pod";
for ($self -> xs_incsrc_dir, @{ $self->{glue_dirs} }) {
my $file = "$_/$mod_pod";
$mod_pod = $file if $complete;
print "mod_pod $mod_pod $file $complete\n" ;
return $mod_pod if -e $file;
}
undef;
}
sub write_docs {
my ($self, $module, $functions) = @_;
my $podfile = $self->mod_pod($module, 1) or return;
my $fh = $self->open_class_file($module, '.pod');
open my $pod, "<", $podfile or die $!;
while (<$pod>) {
print $fh $_;
}
}
sub pm_text {
my($self, $module, $isa, $code) = @_;
my $text = <<"EOF";
$self->{noedit_warning_hash}
package $module;
require DynaLoader ;
use strict;
use warnings FATAL => 'all';
use vars qw{\$VERSION \@ISA} ;
$isa
push \@ISA, 'DynaLoader' ;
\$VERSION = '$version';
bootstrap $module \$VERSION ;
$code
1;
__END__
EOF
return $text;
}
sub makefilepl_text {
my($self, $class, $deps,$typemap) = @_;
my @parts = split (/::/, $class) ;
my $mmargspath = '../' x @parts ;
$mmargspath .= 'mmargs.pl' ;
my $txt = qq{
$self->{noedit_warning_hash}
use ModPerl::MM;
local \$MMARGS ;
if (-f '$mmargspath')
{
do '$mmargspath' ;
die \$\@ if (\$\@) ;
}
\$MMARGS ||= {} ;
ModPerl::MM::WriteMakefile(
'NAME' => '$class',
'VERSION' => '$version',
'TYPEMAPS' => [qw(@$mp2_typemaps $typemap)],
'INC' => "-I$base_dir/glue/perl/xs -I$inc_dir -I$xs_dir $apache_includes",
'LIBS' => "$apreq_libs $apr_libs",
} ;
$txt .= "'depend' => $deps,\n" if ($deps) ;
$txt .= qq{
\%\$MMARGS,
);
} ;
}
# For now, just copy the typemap file in xsbuilder til we
# can remove ExtUtils::XSBuilder.
sub write_typemap
{
my $self = shift;
my $typemap = $self->typemap;
my $map = $typemap->get;
my %seen;
my $fh = $self->open_class_file('', 'typemap');
print $fh "$self->{noedit_warning_hash}\n";
open my $tfh, "$xs_dir/typemap" or die $!;
print $fh $_ while <$tfh>;
}
package My::TypeMap;
use base 'ExtUtils::XSBuilder::TypeMap';
sub null_type {
my($self, $type) = @_;
my $t = $self->get->{$type};
my $class = $t -> {class} ;
if ($class =~ /APREQ_COOKIE_VERSION/) {
return 'APREQ_COOKIE_VERSION_DEFAULT';
}
else {
return $self->SUPER::null_type($type);
}
}
# XXX this needs serious work
sub typemap_code
{
{
T_SUBCLASS => {
INPUT => <<'EOT',
if (SvROK($arg) || !sv_derived_from($arg, \"$Package\"))
Perl_croak(aTHX_ \"Usage: argument is not a subclass of $Package\");
$var = SvPV_nolen($arg)
EOT
},
T_APREQ_COOKIE => {
INPUT => '$var = apreq_xs_sv2cookie(aTHX_ $arg)',
perl2c => 'apreq_xs_sv2cookie(aTHX_ sv)',
OUTPUT => '$arg = apreq_xs_cookie2sv(aTHX_ $var, class, parent);',
c2perl => 'apreq_xs_cookie2sv(aTHX_ ptr, class, parent)',
},
T_APREQ_PARAM => {
INPUT => '$var = apreq_xs_sv2param(aTHX_ $arg)',
perl2c => 'apreq_xs_sv2param(aTHX_ sv)',
OUTPUT => '$arg = apreq_xs_param2sv(aTHX_ $var, class, parent);',
c2perl => 'apreq_xs_param2sv(aTHX_ ptr, class, parent)',
},
T_APREQ_HANDLE => {
INPUT => '$var = apreq_xs_sv2handle(aTHX_ $arg)',
perl2c => 'apreq_xs_sv2handle(aTHX_ sv)',
c2perl => 'apreq_xs_handle2sv(aTHX_ ptr, class, parent)',
OUTPUT => '$arg = apreq_xs_handle2sv(aTHX_ $var, class, parent);',
},
T_APREQ_HANDLE_CGI => {
INPUT => '$var = apreq_xs_sv2handle(aTHX_ $arg)',
OUTPUT => '$arg = apreq_xs_handle2sv(aTHX_ $var, class, SvRV(ST(1)));'
},
T_APREQ_HANDLE_APACHE2 => {
INPUT => '$var = apreq_xs_sv2handle(aTHX_ $arg)',
OUTPUT => <<'EOT',
$arg = apreq_xs_handle2sv(aTHX_ $var, class, SvRV(ST(1)));
SvMAGIC(SvRV($arg))->mg_ptr = (void *)r;
EOT
},
T_APREQ_ERROR => {
INPUT => '$var = (HV *)SvRV($arg)',
OUTPUT => '$arg = sv_bless(newRV_noinc((SV*)$var), gv_stashpvn(\"${ntype}\", sizeof(\"${ntype}\") - 1, FALSE);'
},
T_HASHOBJ => {
INPUT => <<'EOT', # '$var = modperl_hash_tied_object(aTHX_ \"${ntype}\", $arg)'
if (sv_derived_from($arg, \"${ntype}\")) {
if (SVt_PVHV == SvTYPE(SvRV($arg))) {
SV *hv = SvRV($arg);
MAGIC *mg;
if (SvMAGICAL(hv)) {
if ((mg = mg_find(hv, PERL_MAGIC_tied))) {
$var = (void *)MgObjIV(mg);
}
else {
Perl_warn(aTHX_ \"Not a tied hash: (magic=%c)\", mg);
$var = NULL;
}
}
else {
Perl_warn(aTHX_ \"SV is not tied\");
$var = NULL;
}
}
else {
$var = (void *)SvObjIV($arg);
}
}
else {
Perl_croak(aTHX_
\"argument is not a blessed reference \"
\"(expecting an %s derived object)\", \"${ntype}\");
}
EOT
OUTPUT => <<'EOT', # '$arg = modperl_hash_tie(aTHX_ \"${ntype}\", $arg, $var);'
{
SV *hv = (SV*)newHV();
SV *rsv = $arg;
sv_setref_pv(rsv, \"${ntype}\", $var);
sv_magic(hv, rsv, PERL_MAGIC_tied, Nullch, 0);
$arg = SvREFCNT_inc(sv_bless(sv_2mortal(newRV_noinc(hv)),
gv_stashpv(\"${ntype}\", TRUE)));
}
EOT
},
}
}