| 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 |
| |
| }, |
| } |
| } |