shell bypass 403
package ExtUtils::XSBuilder::WrapXS;
use strict;
use warnings FATAL => 'all';
use constant GvSHARED => 0; #$^V gt v5.7.0;
use File::Spec ;
use ExtUtils::XSBuilder::TypeMap ();
use ExtUtils::XSBuilder::MapUtil qw(function_table structure_table callback_table);
use ExtUtils::XSBuilder::PODTemplate ;
use File::Path qw(rmtree mkpath);
use Cwd qw(fastcwd);
use Data::Dumper;
use Carp qw(confess) ;
our $VERSION = '0.03';
my %warnings;
my $verbose = 0 ;
=pod
=head1 NAME
ExtUtils::XSBuilder::WrapXS - create perl XS wrappers for C functions
=head2 DESCRIPTION
For more information, see L<ExtUtils::XSBuilder>
=cut
# ============================================================================
sub new {
my $class = shift;
my $self = bless {
}, $class;
$self -> {glue_dirs} = [$self -> xs_glue_dirs()] ;
$self -> {typemap} = $self -> new_typemap ;
$self -> {parsesource} = $self -> new_parsesource ;
$self -> {xs_includes} = $self -> xs_includes ;
$self -> {callbackno} = 1 ;
for (qw(c hash)) {
my $w = "noedit_warning_$_";
my $method = $w ;
$self->{$w} = $self->$method();
}
$self->typemap->get;
$self;
}
# ============================================================================
sub classname {
my $self = shift || __PACKAGE__;
ref($self) || $self;
}
# ============================================================================
sub calls_trace {
my $frame = 1;
my $trace = '';
while (1) {
my($package, $filename, $line) = caller($frame);
last unless $filename;
$trace .= "$frame. $filename:$line\n";
$frame++;
}
return $trace;
}
# ============================================================================
sub noedit_warning_c {
my $class = classname(shift);
my $warning = \$warnings{C}->{$class};
return $$warning if $$warning;
my $v = join '/', $class, $class->VERSION;
my $trace = calls_trace();
$trace =~ s/^/ * /mg;
$$warning = <<EOF;
/*
* *********** WARNING **************
* This file generated by $v
* Any changes made here will be lost
* ***********************************
$trace */
EOF
}
# ============================================================================
#this is named hash after the `#' character
#rather than named perl, since #comments are used
#non-Perl files, e.g. Makefile, typemap, etc.
sub noedit_warning_hash {
my $class = classname(shift);
my $warning = \$warnings{hash}->{$class};
return $$warning if $$warning;
($$warning = noedit_warning_c($class)) =~ s/^/\# /mg;
$$warning;
}
# ============================================================================
=pod
=head2 new_parsesource (o)
Returns an array ref of new ParseSource objects for all source files that
should be used to generate XS files
=cut
sub new_parsesource { [ ExtUtils::XSBuilder::ParseSource->new ] }
# ============================================================================
=pod
=head2 new_typemap (o)
Returns a new typemap object
=cut
sub new_typemap { ExtUtils::XSBuilder::TypeMap->new (shift) }
# ============================================================================
=pod
=head2 new_podtemplate (o)
Returns a new podtemplate object
=cut
sub new_podtemplate { ExtUtils::XSBuilder::PODTemplate->new }
# ============================================================================
=pod
=head2 xs_includes (o)
Returns a list of XS include files.
Default: use all include files that C<ParseSource::find_includes> returns, but
strip path info
=cut
sub xs_includes
{
my $self = shift ;
my $parsesource = $self -> parsesource_objects ;
my @includes ;
my @paths ;
foreach my $src (@$parsesource) {
push @includes, @{ $src -> find_includes } ;
push @paths, @{ $src -> include_paths } ;
}
foreach (@paths)
{
s#(\\|/)$## ;
s#\\#/# ;
}
foreach (@includes)
{
s#\\#/# ;
}
# strip include paths
foreach my $file (@includes)
{
foreach my $path (@paths)
{
if ($file =~ /^\Q$path\E(\/|\\)(.*?)$/i)
{
$file = $2 ;
last ;
}
}
}
my %includes = map { $_ => 1 } @includes ;
my $fixup1 = $self -> h_filename_prefix . 'preperl.h' ;
my $fixup2 = $self -> h_filename_prefix . 'postperl.h' ;
return [
keys %includes,
-f $self -> xs_include_dir . '/'. $fixup1?$fixup1:(),
'EXTERN.h', 'perl.h', 'XSUB.h',
-f $self -> xs_include_dir . '/'. $fixup2?$fixup2:(),
$self -> h_filename_prefix . 'sv_convert.h',
$self -> h_filename_prefix . 'typedefs.h',
] ;
}
# ============================================================================
=pod
=head2 xs_glue_dirs (o)
Returns a list of additional XS glue directories to seach for maps in.
=cut
sub xs_glue_dirs {
() ;
}
# ============================================================================
=pod
=head2 xs_base_dir (o)
Returns a directory which serves as a base for other directories.
Default: C<'.'>
=cut
sub xs_base_dir { '.' } ;
# ============================================================================
=pod
=head2 xs_map_dir (o)
Returns the directory to search for map files in
Default: C<<xs_base_dir>/xsbuilder/maps>
=cut
sub xs_map_dir { File::Spec -> catdir ($_[0] -> xs_base_dir, 'xsbuilder', 'maps') } ;
# ============================================================================
=pod
=head2 xs_incsrc_dir (o)
Returns the directory to search for files to include into the source. For
example, C<<xs_incsrc_dir>/Apache/DAV/Resource/Resource_pm> will be included into
the C<Apache::DAV::Resource> module.
Default: C<<xs_base_dir>/xsbuilder>
=cut
sub xs_incsrc_dir { File::Spec -> catdir ($_[0] -> xs_base_dir, 'xsbuilder') ; } ;
# ============================================================================
=pod
=head2 xs_include_dir (o)
Returns a directory to search for include files for pm and XS
Default: C<<xs_base_dir>/xsinclude>
=cut
sub xs_include_dir { File::Spec -> catdir ($_[0] -> xs_base_dir, 'xsinclude') ; } ;
# ============================================================================
=pod
=head2 xs_target_dir (o)
Returns the directory to write generated XS and header files in
Default: C<<xs_base_dir>/xs>
=cut
sub xs_target_dir { File::Spec -> catdir ($_[0] -> xs_base_dir, 'xs') ; }
# ============================================================================
sub typemap { shift->{typemap} }
# ============================================================================
sub includes { shift->{xs_includes} || [] }
# ============================================================================
sub parsesource_objects { shift->{parsesource} }
# ============================================================================
sub function_list {
my $self = shift;
my(@list) = @{ function_table($self) };
while (my($name, $val) = each %{ $self->typemap->function_map }) {
#entries that do not exist in C::Scan generated tables
next unless $name =~ /^DEFINE_/;
push @list, $val;
}
return \@list;
}
# ============================================================================
sub callback_list {
my $self = shift;
my(@list) = @{ callback_table($self) };
while (my($name, $val) = each %{ $self->typemap->callback_map }) {
#entries that do not exist in C::Scan generated tables
next unless $name =~ /^DEFINE_/;
push @list, $val;
}
return \@list;
}
# ============================================================================
sub get_callback_function {
my ($self, $func, $struct, $elt) = @_ ;
my $myprefix = $self -> my_xs_prefix ;
my $n ;
$elt -> {callbackno} = $n = $self -> {callbackno}++ ;
my $structelt = $elt -> {name} ;
my $class = $struct -> {class} ;
my $cclass = $self -> cname($class) ;
my($name, $args, $retargs, $return_type, $orig_args, $userdataarg) =
@{ $func } { qw(perl_name args retargs return_type orig_args userdataarg) };
$struct -> {staticcnt} ||= 4 ;
my $staticcnt = $struct -> {staticcnt} ;
#print "get_callback_function: ", Data::Dumper -> Dump([$func]), "\n" ;
my $code = "\n/* --- $class -> $structelt --- */\n\n" ;
my $cbname = "${myprefix}cb_${cclass}__$structelt" ;
my %retargs = map { $_->{name} => $_ } @$retargs ;
my %args = map { $_->{name} => $_ } @$args ;
my @args = map { my $name = /^(?:\*|&)(.*?)$/?$1:$_ ; ($args{$name}{rtype} || $retargs{$name}{rtype}) . (/^&/?" * $name":" $name") } @$orig_args ;
$return_type = $self -> cname($return_type) ;
my $return_class = $self -> typemap -> map_class ($return_type) || $return_type;
if ($return_class =~ / /)
{
print "ERROR: return class '$return_class' contains spaces" ;
}
my $desttype = 'CV' ;
if ($structelt)
{
$desttype = 'SV' ;
}
my $numret = $return_type eq 'void'?0:1 ;
$numret += @$retargs ;
my $callflags = $numret == 0?'G_VOID':$numret == 1?'G_SCALAR':'G_ARRAY' ;
$code .= qq[
static $return_type $cbname (] . join (',', "$desttype * __cbdest", @args) . qq[)
{
] ;
$code .= " $return_type __retval ;\n" if ($return_type && $return_type ne 'void') ;
$code .= " SV * __retsv ;\n" if ($numret) ;
$code .= qq[
int __cnt ;
dSP ;
ENTER ;
SAVETMPS ;
PUSHMARK(SP) ;
];
if ($structelt)
{
$code .= " PUSHs(__cbdest) ;\n" ;
}
foreach (@$orig_args) {
my $type = /^(?:\*|\&)(.*?)$/?$1:$_ ;
my $name = /^\*(.*?)$/?"&$1":$_ ;
next if ($retargs{$type}{class}) ;
if (!$args{$type}{class} && !$args{$type}{type})
{
print "WARNING: unknown type for argument '$name' in struct member '$structelt'\n" ;
print Dumper ($args) ;
next ;
}
my $class = $args{$type}{class} || $args{$type}{type} ;
if ($class =~/\s/)
{
print "WARNING: type '$class' for argument '$name' in struct member '$structelt' contains spaces\n" ;
print Dumper ($args) ;
next ;
}
$code .= ' PUSHs(' . $self -> convert_2obj ($class, $name) . ") ;\n" ;
}
$code .= qq[
PUTBACK ;
] ;
if ($structelt)
{
$code .= " __cnt = perl_call_method(\"cb_$structelt\", $callflags) ;\n" ;
}
else
{
$code .= " __cnt = perl_call_sv(__cbdest, $callflags) ;\n" ;
}
$code .= qq[
if (__cnt != $numret)
croak (\"$cbname expected $numret return values\") ;
] if ($numret > 0) ;
$code .= qq[
SPAGAIN ;
] ;
if ($return_type && $return_type ne 'void')
{
$code .= " __retsv = POPs;\n" ;
$code .= ' __retval = ' . $self -> convert_sv2 ($return_type, $return_class, '__retsv') . ";\n"
}
foreach (@$retargs) {
$code .= " __retsv = POPs;\n" ;
$code .= " *$_->{name} = " . $self -> convert_sv2 ($_->{rtype}, $_->{class}, '__retsv') . ";\n" ;
}
$code .= qq[
PUTBACK ;
FREETMPS ;
LEAVE ;
] ;
$code .= " return __retval ;\n" if ($return_type && $return_type ne 'void') ;
$code .= qq[
}
] ;
if (!$userdataarg) {
$staticcnt ||= 4 ;
for (my $i = 0 ; $i < $staticcnt; $i++) {
$code .= qq[
static $return_type ${cbname}_obj$i (] . join (',', @args) . qq[)
{
] . ($return_type eq 'void'?'':'return') . qq[ ${cbname} (] .
join (',', "${myprefix}${cclass}_obj[$i]", map { /^(?:\*|\&)?(.*?)$/ } @$orig_args) . qq[) ;
}
] ;
}
$code .= "typedef $return_type (*t${cbname}_func)(" . join (',', @args) . qq") ;\n" ;
$code .= "static t${cbname}_func ${myprefix}${cbname}_func [$staticcnt] = {\n " .
join (",\n ", map { "${cbname}_obj$_" } (0..$staticcnt-1)) . "\n } ;\n\n\n" ;
}
unshift @{ $self->{XS}->{ $func->{module} } }, {
code => $code,
class => '',
name => $name,
};
}
# ============================================================================
sub get_function {
my ($self, $func) = @_ ;
my $myprefix = $self -> my_xs_prefix ;
my($name, $module, $class, $args, $retargs) =
@{ $func } { qw(perl_name module class args retargs) };
my %retargs = map { $_->{name} => $_ } @$retargs ;
print "get_function: ", Data::Dumper -> Dump([$func]), "\n" if ($verbose);
#eg ap_fputs()
if ($name =~ s/^DEFINE_//) {
$func->{name} =~ s/^DEFINE_//;
if (needs_prefix($func->{name})) {
#e.g. DEFINE_add_output_filter
$func->{name} = make_prefix($func->{name}, $class);
}
}
my $xs_parms = join ', ',
map { defined $_->{default} ?
"$_->{name}=$_->{default}" : $_->{name} } @$args;
my $parms ;
if ($func -> {dispatch_argspec})
{
$parms = $func -> {dispatch_argspec} ;
}
else
{
($parms = join (',', $xs_parms,
map { "\&$_->{name}" } @$retargs)) =~
s/=[^,]+//g; #strip defaults
}
my $proto = join "\n",
(map " $_->{type} $_->{name}", @$args) ;
my $return_type =
$name =~ /^DESTROY$/ ? 'void' : $func->{return_type};
my $retdecl = @$retargs?(join "\n",
(map { my $type = $self -> cname($_->{class}) ; $type =~ s/\*$//; ' ' . $type . " $_->{name};"} @$retargs),
#' ' . $self -> cname($return_type) . ' RETVAL',
''):'';
my($dispatch, $orig_args) =
@{ $func } {qw(dispatch orig_args)};
if ($dispatch =~ /^$myprefix/io) {
$name =~ s/^$myprefix//;
$name =~ s/^$func->{prefix}//;
push @{ $self->{newXS}->{ $module } },
["$class\::$name", $dispatch];
return;
}
my $passthru = @$args && $args->[0]->{name} eq '...';
if ($passthru) {
$parms = '...';
$proto = '';
}
my $attrs = $self->attrs($name);
my $code = <<EOF;
$return_type
$name($xs_parms)
EOF
$code .= "$proto\n" if ($proto) ;
$code .= "$attrs\n" if ($attrs) ;
$code .= "PREINIT:\n$retdecl" if ($retdecl) ;
if ($dispatch || $orig_args) {
my $thx = "";
if ($dispatch) {
$thx = 'aTHX_ ' if $dispatch =~ /^$myprefix/i;
if ($orig_args && !$func -> {dispatch_argspec}) {
$parms = join ', ', map { $retargs{$_}?"&$_":$_} @$orig_args;
}
}
else {
### ??? gr ### if ($orig_args and @$orig_args == @$args) {
if ($orig_args && @$orig_args) {
#args were reordered
$parms = join ', ', map { $retargs{$_}?"&$_":$_} @$orig_args;
}
$dispatch = $func->{name};
}
if ($passthru) {
$thx ||= 'aTHX_ ';
$parms = 'items, MARK+1, SP';
}
my $retval = $return_type eq 'void' ?
["", ""] : ["RETVAL = ", "OUTPUT:\n RETVAL\n"];
my $retnum = $retdecl?scalar(@$retargs) + ($return_type eq 'void' ?0:1):0 ;
$code .= $retdecl?"PPCODE:":"CODE:" ;
$code .= "\n $retval->[0]$dispatch($thx$parms);\n" ;
if ($retdecl) {
my $retclass = $self -> typemap -> map_class ($return_type) || $return_type ;
if ($retclass =~ / /)
{
print "ERROR: return class '$retclass' contains spaces" ;
}
$code .= " XSprePUSH;\n" ;
$code .= " EXTEND(SP, $retnum) ;\n" ;
$code .= ' PUSHs(' . $self -> convert_2obj ($retclass, 'RETVAL') . ") ;\n" ;
foreach (@$retargs) {
if ($_->{class} =~ / /)
{
print "ERROR: $_->{class} contains spaces; retargs = ", Dumper ($_) ;
}
$code .= ' PUSHs(' . $self -> convert_2obj ($_->{class}, $_->{name}) . ") ;\n" ;
}
}
else {
$code .= "$retval->[1]\n" ;
}
}
$code .= "\n" ;
$func->{code} = $code;
push @{ $self->{XS}->{ $module } }, $func;
}
# ============================================================================
sub get_functions {
my $self = shift;
my $typemap = $self->typemap;
my %seen ;
for my $entry (@{ $self->function_list() }) {
#print "get_func ", Dumper ($entry) ;
my $func = $typemap->map_function($entry);
#print "FAILED to map $entry->{name}\n" unless $func;
next unless $func;
print "WARNING: Duplicate function: $entry->{name}\n" if ($seen{$entry->{name}}++) ;
$self -> get_function ($func) ;
}
}
# ============================================================================
sub get_value {
my $e = shift;
my $val = 'val';
if ($e->{class} eq 'PV') {
if (my $pool = $e->{pool}) {
$pool .= '(obj)';
$val = "((ST(1) == &PL_sv_undef) ? NULL :
apr_pstrndup($pool, val, val_len))"
}
}
return $val;
}
# ============================================================================
sub get_structure_callback_init {
my ($self, $class, $struct) = @_ ;
my $cclass = $self -> cname($class) ;
my $myprefix = $self -> my_xs_prefix ;
my $staticcnt = $struct -> {staticcnt} ;
my $cnv = $self -> convert_sv2 ($cclass, $class, 'obj') ;
my $code = qq[
void
init_callbacks (obj, val=NULL)
SV * obj
SV * val
PREINIT:
int n = -1 ;
int i ;
$cclass cobj = $cnv ;
SV * ref ;
SV * perl_obj ;
CODE:
if (items > 1)
obj = val ;
perl_obj = SvRV(obj) ;
ref = newRV_noinc(perl_obj) ;
for (i=0;i < $staticcnt;i++)
{
if ($myprefix${cclass}_obj[i] == ref)
{
n = i ;
break ;
}
}
if (n < 0)
for (i=0;i < $staticcnt;i++)
{
if ($myprefix${cclass}_obj[i] == NULL)
{
n = i ;
break ;
}
}
if (n < 0)
croak ("Limit for concurrent object callbacks reached for $class. Limit is $staticcnt") ;
$myprefix${cclass}_obj[n] = ref ;
] ;
foreach my $e (@{ $struct->{elts} }) {
if ($e -> {callback}) {
my $cbname = "${myprefix}cb_${cclass}__$e->{name}" ;
$code .= " cobj -> $e->{name} = ${myprefix}${cbname}_func[n] ;\n" ;
}
}
$code .= qq[
] ;
my $ccode = "static SV * ${myprefix}${cclass}_obj[$staticcnt] ;\n\n" ;
push @{ $self->{XS}->{ $struct->{module} } }, {
code => $code,
class => $class,
name => 'init_callbacks',
};
unshift @{ $self->{XS}->{ $struct->{module} } }, {
code => $ccode,
class => '',
name => 'init_callbacks',
};
}
# ============================================================================
sub get_structure_new {
my ($self, $class, $struct) = @_ ;
my $cclass = $self -> cname($class) ;
my $cnvprefix = $self -> my_cnv_prefix ;
my $alloc = $struct -> {alloc} || 'malloc(sizeof(*cobj))' ;
my $code = qq[
SV *
new (class,initializer=NULL)
char * class
SV * initializer
PREINIT:
SV * svobj ;
$cclass cobj ;
SV * tmpsv ;
CODE:
${cnvprefix}${cclass}_create_obj(cobj,svobj,RETVAL,$alloc) ;
if (initializer) {
if (!SvROK(initializer) || !(tmpsv = SvRV(initializer)))
croak ("initializer for ${class}::new is not a reference") ;
if (SvTYPE(tmpsv) == SVt_PVHV || SvTYPE(tmpsv) == SVt_PVMG)
${cclass}_new_init (aTHX_ cobj, tmpsv, 0) ;
else if (SvTYPE(tmpsv) == SVt_PVAV) {
int i ;
SvGROW(svobj, sizeof (*cobj) * av_len((AV *)tmpsv)) ;
for (i = 0; i <= av_len((AV *)tmpsv); i++) {
SV * * itemrv = av_fetch((AV *)tmpsv, i, 0) ;
SV * item ;
if (!itemrv || !*itemrv || !SvROK(*itemrv) || !(item = SvRV(*itemrv)))
croak ("array element of initializer for ${class}::new is not a reference") ;
${cclass}_new_init (aTHX_ &cobj[i], item, 1) ;
}
}
else {
croak ("initializer for ${class}::new is not a hash/array/object reference") ;
}
}
OUTPUT:
RETVAL
] ;
my $c_code = qq[
void ${cclass}_new_init (pTHX_ $cclass obj, SV * item, int overwrite) {
SV * * tmpsv ;
if (SvTYPE(item) == SVt_PVMG)
memcpy (obj, (void *)SvIVX(item), sizeof (*obj)) ;
else if (SvTYPE(item) == SVt_PVHV) {
] ;
foreach my $e (@{ $struct->{elts} }) {
if ($e -> {name} =~ /^(.*?)\[(.*?)\]$/) {
my $strncpy = $2 ;
my $name = $1 ;
my $perl_name ;
($perl_name = $e -> {perl_name}) =~ s/\[.*?\]$// ;
$c_code .= " if ((tmpsv = hv_fetch((HV *)item, \"$perl_name\", sizeof(\"$perl_name\") - 1, 0)) || overwrite) {\n" ;
$c_code .= " STRLEN l = 0;\n" ;
$c_code .= " if (tmpsv) {\n" ;
$c_code .= " char * s = SvPV(*tmpsv,l) ;\n" ;
$c_code .= " if (l > ($strncpy)-1) l = ($strncpy) - 1 ;\n" ;
$c_code .= " strncpy(obj->$name, s, l) ;\n" ;
$c_code .= " }\n" ;
$c_code .= " obj->$name\[l] = '\\0';\n" ;
$c_code .= " }\n" ;
} elsif (($e -> {class} !~ /::/) || ($e -> {rtype} =~ /\*$/)) {
$c_code .= " if ((tmpsv = hv_fetch((HV *)item, \"$e->{perl_name}\", sizeof(\"$e->{perl_name}\") - 1, 0)) || overwrite) {\n" ;
if ($e -> {malloc}) {
my $type = $e->{rtype} ;
my $dest = "obj -> $e->{name}" ;
my $src = 'tmpobj' ;
my $expr = eval ('"' . $e -> {malloc} . '"') ;
print $@ if ($@) ;
$c_code .= " $type tmpobj = (" . $self -> convert_sv2 ($e->{rtype}, $e->{class}, '(tmpsv && *tmpsv?*tmpsv:&PL_sv_undef)') . ");\n" ;
$c_code .= " if (tmpobj)\n" ;
$c_code .= " $expr;\n" ;
$c_code .= " else\n" ;
$c_code .= " $dest = NULL ;\n" ;
}
else {
$c_code .= ' ' . "obj -> $e->{name} = " . $self -> convert_sv2 ($e->{rtype}, $e->{class}, '(tmpsv && *tmpsv?*tmpsv:&PL_sv_undef)') . " ;\n" ;
}
$c_code .= " }\n" ;
}
}
$c_code .= qq[ ; }
else
croak ("initializer for ${class}::new is not a hash or object reference") ;
} ;
] ;
push @{ $self->{XS}->{ $struct->{module} } }, {
code => $code,
class => $class,
name => 'new',
};
unshift @{ $self->{XS}->{ $struct->{module} } }, {
code => $c_code,
class => '',
name => 'new',
};
}
# ============================================================================
sub get_structure_destroy {
my ($self, $class, $struct) = @_ ;
my $cclass = $self -> cname($class) ;
my $cnvprefix = $self -> my_cnv_prefix ;
my $code = qq[
void
DESTROY (obj)
$class obj
CODE:
${cclass}_destroy (aTHX_ obj) ;
] ;
my $numfree = 0 ;
my $c_code = qq[
void ${cclass}_destroy (pTHX_ $cclass obj) {
];
foreach my $e (@{ $struct->{elts} }) {
if (($e -> {class} !~ /::/) || ($e -> {rtype} =~ /\*$/)) {
if ($e -> {free}) {
my $src = "obj -> $e->{name}" ;
my $type = $e->{rtype} ;
my $expr = eval ('"' . $e -> {free} . '"') ;
print $@ if ($@) ;
$c_code .= " if (obj -> $e->{name})\n" ;
$c_code .= ' ' . $expr . ";\n" ;
$numfree++ ;
}
}
}
$c_code .= "\n};\n\n" ;
if ($numfree) {
push @{ $self->{XS}->{ $struct->{module} } }, {
code => $code,
class => $class,
name => 'destroy',
};
unshift @{ $self->{XS}->{ $struct->{module} } }, {
code => $c_code,
class => '',
name => 'destroy',
};
}
}
# ============================================================================
sub get_structures {
my $self = shift;
my $typemap = $self->typemap;
my $has_callbacks = 0 ;
for my $entry (@{ structure_table($self) }) {
print 'struct ', $entry->{type} || '???', "...\n" ;
my $struct = $typemap->map_structure($entry);
print Data::Dumper -> Dump ([$entry, $struct], ['Table Entry', 'Mapfile Entry']) if ($verbose) ;
if (!$struct)
{
print "WARNING: Struture '$entry->{type}' not found in map file\n" ;
next ;
}
my $class = $struct->{class};
$has_callbacks = 0 ;
for my $e (@{ $struct->{elts} }) {
my($name, $default, $type, $perl_name ) =
@{$e}{qw(name default type perl_name)};
print " $name...\n" ;
if ($e -> {callback}) {
#print "callback < ", Dumper ($e) , "\n" ;
$self -> get_function ($e -> {func}) ;
$self -> get_callback_function ($e -> {func}, $struct, $e) ;
$has_callbacks++ ;
}
else {
(my $cast = $type) =~ s/:/_/g;
my $val = get_value($e);
my $type_in = $type;
my $preinit = "/*nada*/";
my $address = '' ;
my $rdonly = 0 ;
my $strncpy ;
if ($e->{class} eq 'PV' and $val ne 'val') {
$type_in =~ s/char/char_len/;
$preinit = "STRLEN val_len;";
} elsif (($e->{class} =~ /::/) && ($e -> {rtype} !~ /\*\s*$/)) {
# an inlined struct is read only
$rdonly = 1 ;
$address = '&' ;
} elsif ($name =~ /^(.*?)\[(.*?)\]$/) {
$strncpy = $2 ;
$name = $1 ;
$perl_name =~ s/\[.*?\]$// ;
$type = 'char *' ;
$type_in = 'char *' ;
$cast = 'char *' ;
}
my $attrs = $self->attrs($name);
my $code = <<EOF;
$type
$perl_name(obj, val=$default)
$class obj
$type_in val
PREINIT:
$preinit
$attrs
CODE:
RETVAL = ($cast) $address obj->$name;
EOF
if ($rdonly) {
$code .= <<EOF
if (items > 1) {
croak (\"$name is read only\") ;
}
EOF
}
else {
$code .= "\n if (items > 1) {\n" ;
if ($e -> {malloc}) {
my $dest = "obj->$name" ;
my $src = $val ;
my $type = $cast ;
my $expr = eval ('"' . $e -> {malloc} . '"') ;
print $@ if ($@) ;
$code .= ' ' . $expr . ";\n" ;
}
elsif ($strncpy) {
$code .= " strncpy(obj->$name, ($cast) $val, ($strncpy) - 1) ;\n" ;
$code .= " obj->$name\[($strncpy)-1] = '\\0';\n" ;
}
else {
$code .= " obj->$name = ($cast) $val;\n" ;
}
$code .= " }\n" ;
}
$code .= <<EOF;
OUTPUT:
RETVAL
EOF
push @{ $self->{XS}->{ $struct->{module} } }, {
code => $code,
class => $class,
name => $name,
perl_name => $e -> {perl_name},
comment => $e -> {comment},
struct_member => $e,
};
}
}
$self -> get_structure_new($class, $struct) if ($struct->{has_new}) ;
$self -> get_structure_destroy($class, $struct) if ($struct->{has_new}) ;
$self -> get_structure_callback_init ($class, $struct) if ($has_callbacks);
}
}
# ============================================================================
sub prepare {
my $self = shift;
$self->{DIR} = $self -> xs_target_dir;
$self->{XS_DIR} = $self -> xs_target_dir ;
if (-e $self->{DIR}) {
rmtree([$self->{DIR}], 1, 1);
}
mkpath [$self->{DIR}], 1, 0755;
}
# ============================================================================
sub class_dirname {
my($self, $class) = @_;
# my($base, $sub) = split '::', $class;
# return "$self->{DIR}/$base" unless $sub; #Apache | APR
# return $sub if $sub eq $self->{DIR}; #WrapXS
# return "$base/$sub";
$class =~ s/::/\//g ;
return $class ;
}
# ============================================================================
sub class_dir {
my($self, $class) = @_;
my $dirname = $self->class_dirname($class);
#my $dir = ($dirname =~ m:/: and $dirname !~ m:^$self->{DIR}:) ?
# join('/', $self->{DIR}, $dirname) : $dirname;
my $dir = join('/', $self->{DIR}, $dirname) ;
mkpath [$dir], 1, 0755 unless -d $dir;
$dir;
}
# ============================================================================
sub class_file {
my($self, $class, $file) = @_;
join '/', $self->class_dir($class), $file;
}
# ============================================================================
sub cname {
my($self, $class) = @_;
confess ('ERROR: class is undefined in cname') if (!defined ($class)) ;
$class =~ s/::$// ;
$class =~ s/:/_/g;
$class;
}
# ============================================================================
sub convert_2obj {
my($self, $class, $name) = @_;
$self -> my_cnv_prefix . $self -> cname($class) . "_2obj($name)" ;
}
# ============================================================================
sub convert_sv2 {
my($self, $rtype, $class, $name) = @_;
$class =~ s/^const\s+// ;
$class =~ s/char\s*\*/PV/ ;
$class =~ s/SV\s*\*/SV/ ;
return "($rtype)" . $self -> my_cnv_prefix . 'sv2_' . $self -> cname($class) . "($name)" ;
}
# ============================================================================
sub open_class_file {
my($self, $class, $file) = @_;
if ($file =~ /^\./) {
my $sub = (split '::', $class)[-1];
$file = $sub . $file;
}
my $name = $self->class_file($class, $file);
open my $fh, '>', $name or die "open $name: $!";
print "writing...$name\n";
return $fh;
}
# ============================================================================
=pod
=head2 makefilepl_text (o)
Returns text for Makefile.PL
=cut
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 ExtUtils::MakeMaker ();
local \$MMARGS ;
if (-f '$mmargspath')
{
do '$mmargspath' ;
die \$\@ if (\$\@) ;
}
\$MMARGS ||= {} ;
ExtUtils::MakeMaker::WriteMakefile(
'NAME' => '$class',
'VERSION' => '0.01',
'TYPEMAPS' => ['$typemap'],
} ;
$txt .= "'depend' => $deps,\n" if ($deps) ;
$txt .= qq{
\%\$MMARGS,
);
} ;
}
# ============================================================================
sub write_makefilepl {
my($self, $class) = @_;
$self -> {makefilepls}{$class} = 1 ;
my $fh = $self->open_class_file($class, 'Makefile.PL');
my $includes = $self->includes;
my @parts = split '::', $class ;
my $xs = @parts?$parts[-1] . '.c':'' ;
my $deps = {$xs => ""};
if (my $mod_h = $self->mod_h($class, 1)) {
my $abs = File::Spec -> rel2abs ($mod_h) ;
my $rel = File::Spec -> abs2rel ($abs, $self -> class_dir ($class)) ;
$deps->{$xs} .= " $rel";
}
local $Data::Dumper::Terse = 1;
$deps = Dumper $deps;
$deps = undef if (!$class) ;
$class ||= 'WrapXS' ;
print $fh $self -> makefilepl_text ($class, $deps, ('../' x @parts) . 'typemap') ;
close $fh;
}
# ============================================================================
sub write_missing_makefilepls {
my($self, $class) = @_;
my %classes = ('' => 1) ;
foreach (keys %{$self -> {makefilepls}})
{
my @parts = split (/::/, $_) ;
my $i ;
for ($i = 0; $i < @parts; $i++)
{
$classes{join('::', @parts[0..$i])} = 1 ;
}
}
foreach my $class (keys %classes)
{
next if ($self -> {makefilepls}{$class}) ;
$self -> write_makefilepl ($class) ;
}
}
# ============================================================================
sub mod_h {
my($self, $module, $complete) = @_;
my $dirname = $self->class_dirname($module);
my $cname = $self->cname($module);
my $mod_h = "$dirname/$cname.h";
for ($self -> xs_include_dir, @{ $self->{glue_dirs} }) {
my $file = "$_/$mod_h";
$mod_h = $file if $complete;
return $mod_h 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_incsrc_dir, @{ $self->{glue_dirs} }) {
my $file = "$_/$mod_pm";
$mod_pm = $file if $complete;
print "mod_pm $mod_pm $file $complete\n" ;
return $mod_pm if -e $file;
}
undef;
}
# ============================================================================
=pod
=head2 h_filename_prefix (o)
Defines a prefix for generated header files
Default: C<'xs_'>
=cut
sub h_filename_prefix { 'xs_' }
# ============================================================================
=pod
=head2 my_xs_prefix (o)
Defines a prefix used for all XS functions
Default: C<'xs_'>
=cut
sub my_xs_prefix { 'xs_' }
# ============================================================================
=pod
=head2 my_cnv_prefix (o)
Defines a prefix used for all conversion functions/macros.
Default: C<my_xs_prefix>
=cut
sub my_cnv_prefix { $_[0] -> my_xs_prefix }
# ============================================================================
=pod
=head2 needs_prefix (o, name)
Returns true if the passed name should be prefixed
=cut
sub needs_prefix {
return 0 if (!$_[1]) ;
my $pf = $_[0] -> my_xs_prefix ;
return $_[1] !~ /^$pf/i;
}
# ============================================================================
sub isa_str {
my($self, $module) = @_;
my $str = "";
if (my $isa = $self->typemap->{function_map}->{isa}->{$module}) {
while (my($sub, $base) = each %$isa) {
#XXX cannot set isa in the BOOT: section because XSLoader local-ises
#ISA during bootstrap
# $str .= qq{ av_push(get_av("$sub\::ISA", TRUE),
# newSVpv("$base",0));}
$str .= qq{\@$sub\::ISA = '$base';\n}
}
}
$str;
}
# ============================================================================
sub boot {
my($self, $module) = @_;
my $str = "";
if (my $boot = $self->typemap->{function_map}->{boot}->{$module}) {
$str = ' ' . $self -> my_xs_prefix . $self->cname($module) . "_BOOT(aTHXo);\n";
}
$str;
}
# ============================================================================
my $notshared = join '|', qw(TIEHANDLE); #not sure why yet
sub attrs {
my($self, $name) = @_;
my $str = "";
return $str if $name =~ /$notshared$/o;
$str = " ATTRS: shared\n" if GvSHARED;
$str;
}
# ============================================================================
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};
}
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 GvSHARED;
}
}
close $fh;
}
# ============================================================================
=pod
=head2 pm_text (o, module, isa, code)
Returns the text of a C<.pm> file, or undef if no C<.pm> file should be
written.
Default: Create a C<.pm> file which bootstraps the XS code
=cut
sub pm_text {
my($self, $module, $isa, $code) = @_;
return <<EOF;
$self->{noedit_warning_hash}
package $module;
require DynaLoader ;
use strict ;
use vars qw{\$VERSION \@ISA} ;
$isa
push \@ISA, 'DynaLoader' ;
\$VERSION = '0.01';
bootstrap $module \$VERSION ;
$code
1;
__END__
EOF
}
# ============================================================================
sub write_pm {
my($self, $module) = @_;
my $isa = $self->isa_str($module);
my $code = "";
if (my $mod_pm = $self->mod_pm($module, 1)) {
open my $fh, '<', $mod_pm;
local $/;
$code = <$fh>;
close $fh;
}
my $base = (split '::', $module)[0];
my $loader = join '::', $base, 'XSLoader';
my $text = $self -> pm_text ($module, $isa, $code) ;
return if (!$text) ;
my $fh = $self->open_class_file($module, '.pm');
print $fh $text ;
}
# ============================================================================
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";
while (my($type, $t) = each %$map) {
my $class = $t -> {class} ;
$class ||= $type;
next if $seen{$type}++ || $typemap->special($class);
my $typemap = $t -> {typemapid} ;
if ($class =~ /::/) {
next if $seen{$class}++ ;
$class =~ s/::$// ;
print $fh "$class\t$typemap\n";
}
else {
print $fh "$type\t$typemap\n";
}
}
my $cnvprefix = $self -> my_cnv_prefix ;
my $typemap_code = $typemap -> typemap_code ($cnvprefix);
foreach my $dir ('INPUT', 'OUTPUT') {
print $fh "\n$dir\n" ;
while (my($type, $code) = each %{$typemap_code}) {
print $fh "$type\n$code->{$dir}\n\n" if ($code->{$dir}) ;
}
}
close $fh;
}
# ============================================================================
sub write_typemap_h_file {
my($self, $method) = @_;
$method = $method . '_code';
my($h, $code) = $self->typemap->$method();
my $file = join '/', $self->{XS_DIR}, $h;
open my $fh, '>', $file or die "open $file: $!";
print $fh "$self->{noedit_warning_c}\n";
print $fh $code;
close $fh;
}
# ============================================================================
sub _pod_gen_siglet {
my $class = shift || '' ;
return '\%' if $class eq 'HV';
return '\@' if $class eq 'AV';
return '$';
}
# ============================================================================
# Determine if the name is that of a function or an object
sub _pod_is_function {
my $class = shift || '';
#print "_pod_is_function($class)\n";
my %func_class = (
SV => 1,
IV => 1,
NV => 1,
PV => 1,
UV => 1,
PTR => 1,
);
exists $func_class{$class};
}
# ============================================================================
sub generate_pod {
my $self = shift ;
my $fh = shift;
my $pdd = shift;
my $templ = $self -> new_podtemplate ;
my $since = $templ -> since_default ;
print $fh $templ -> gen_pod_head ($pdd->{module}) ;
my $detail = $pdd->{functions_detailed};
unless ( ref($detail) eq 'ARRAY') {
warn "No functions listed in pdd structure for $pdd->{module}";
return;
}
foreach my $f (@$detail) {
# Generate the function or method name
my $method = $f->{perl_name};
$method = $1 if ($f->{prefix} && ($method =~ /^$f->{prefix}(.*?)$/)) ;
$method = $1 if ($f->{class_xs_prefix} && ($method =~ /^(?:DEFINE_)?$f->{class_xs_prefix}(.*?)$/)) ;
if (!$method) {
warn "Cannot determinate method name for '$f->{name}'" ;
next ;
}
my $comment = $f->{comment_parsed};
my $commenttext = ($comment->{func_desc} || '') . "\n\n" . ($comment->{doxygen_remark} || '') ;
my $member = $f -> {struct_member};
if ($member)
{
print $fh $templ -> gen_pod_struct_member ($f->{class}, '$obj', $f->{struct_member}->{class}, $f->{perl_name}, $commenttext, $since) ;
}
else
{
my $args = $f->{args};
if ($args && @$args)
{
my @param_nm = map { $_ -> {name} } @$args ; # Parameter names
my $obj_nm;
my $obj_sym;
my $offset = 0;
my $first_param = $f->{args}[0];
unless (_pod_is_function($first_param->{class})) {
$obj_nm = $param_nm[0]; # Object Name
$obj_sym = &_pod_gen_siglet($first_param->{class}). $obj_nm;
$offset++;
}
my $retclass ;
my $retcomment = $comment -> {doxygen_return} || '' ;
if ($f -> {return_type} && $f -> {return_type} ne 'void') {
my $rettype = $self -> typemap->get->{$f -> {return_type}} ;
$retclass = $rettype?$rettype->{class}:$f -> {return_type};
}
my @param;
my $i = 0 ;
for my $param_nm (@param_nm) {
my $arg = $args->[$i++];
push @param, { class => $arg->{class}, name => &_pod_gen_siglet($arg->{class}) . $param_nm,
comment => ($comment->{doxygen_param_desc}{$param_nm} || '') } ;
}
print $fh $templ -> gen_pod_func ($obj_sym, $obj_sym, $method, \@param, $retclass, $retcomment, $commenttext, $since) ;
}
}
}
}
# ============================================================================
# pdd = PERL Data Dumper
sub write_docs {
my($self, $module, $functions) = @_;
my $fh = $self->open_class_file($module, '.pdd');
print $fh "$self->{noedit_warning_hash}\n";
# Includes
my @includes = @{ $self->includes };
if (my $mod_h = $self->mod_h($module)) {
push @includes, $mod_h;
}
my $last_prefix = "";
my $fmap = $self->typemap->{function_map} ;
my $myprefix = $self->my_xs_prefix ;
# Finding doxygen- and other data inside the comments
# This code only knows the syntax for @ingroup, @param, @remark,
# @return and @warning. At the moment all other doxygen commands
# are treated as multiple-occurance, no-parameter commands.
# Note: Nor does @deffunc exist in the doxygen specification,
# neither does @remark (but @remarks), @tip and @see. So we treat
# @remark like @remarks, but we don't do any speacial treating for
# @deffunc. Ideas or suggestions anyone?
# --Axel Beckert
foreach my $details (@$functions) {
#print "Comment: ", $details->{name} || '?', ': ', $details->{comment} || '-', "\n" ;
#print "----> ", Dumper ($details) ;# if (!$details->{comment}) ;
if (defined $details->{comment} and
my $comment = $details->{comment}) {
$details->{comment_parsed} = {};
# Source file
if ($comment =~ s/^\s*(\S*\.c)\s+\*\n//s) {
$details->{comment_parsed}{source_file} = $1;
}
# Initialize several fields
$details->{comment_parsed}{func_desc} = "";
my $doxygen = 0; # flag indicating that we already have
# seen doxygen fields in this comment
my $type = 0; # name of doxygen field
my $pre = 0; # if we should recognize leading
# spaces. Example see apr_table_overlap
# Setting some regexps
my $ordinary_line = qr/^\s*?\*(\s*(.*?))\s*$/;
my $pre_begin = qr(<PRE>)i;
my $pre_end = qr(</PRE>)i;
# Parse the rest of the comment line by line, because
# doxygen fields can appear more than once
foreach my $line (split /\n/, $comment) {
# Yesss! This looks like doxygen data.
if ($line =~ /^\s*\*\s+[\\@](\w+)\s+(.*)\s*$/) {
$type = $doxygen = $1;
my $info = $2;
# setting the recognizing of leading spaces
$pre = ($info =~ $pre_begin ? 1 : $pre);
$pre = ($info =~ $pre_end ? 0 : $pre);
# Already had a doxygen element of this type for this func.
if (defined $details->{comment_parsed}{"doxygen_$type"}) {
push(@{ $details->{comment_parsed}{"doxygen_$type"} },
$info);
}
# Hey, hadn't seen this doxygen type in this function yet!
else {
$details->{comment_parsed}{"doxygen_$type"} = [ $info ];
}
}
# Further line belonging to doxygen field of the last line
elsif ($doxygen) {
# An empty line ends a doxygen paragraph
if ($line =~ /^\s*$/) {
$doxygen = 0;
next;
}
# Those two situations should never appear. But we
# better double check those things.
croak("There already was a doxygen comment, but it didn't set an type.\nStrange things happen")
unless defined $details->{comment_parsed}{"doxygen_$type"};
croak("This ($line) maybe an syntactic incorrect doxygen line.\nStrange things happen")
unless $line =~ $ordinary_line;
my $info = $2;
$info = $1 if $pre;
# setting the recognizing of leading spaces
$pre = ($info =~ $pre_begin ? 1 : $pre);
$pre = ($info =~ $pre_end ? 0 : $pre);
$info =~ s(^\s+</PRE>)(</PRE>)i;
# Ok, get me the last line of documentation.
my $lastline =
pop @{ $details->{comment_parsed}{"doxygen_$type"} };
# Concatenate that line and the actual line with a newline
$info = "$lastline\n$info";
# Strip empty lines at the end and beginning
# unless there was a <PRE> before.
unless ($pre) {
$info =~ s/[\n\s]+$//s;
$info =~ s/^[\n\s]+//s;
}
# Push the back into the array
push(@{ $details->{comment_parsed}{"doxygen_$type"} },
$info);
}
# Booooh! Just an ordinary comment
elsif ($line =~ $ordinary_line) {
my $info = $2;
$info = $1 if $pre;
# setting the recognizing of leading spaces
$pre = ($info =~ $pre_begin ? 1 : $pre);
$pre = ($info =~ $pre_end ? 0 : $pre);
$info =~ s(^\s+(</PRE>))($1)i;
# Only add if not an empty line at the beginning
$details->{comment_parsed}{func_desc} .= "$info\n"
unless ($info =~ /^\s*$/ and
$details->{comment_parsed}{func_desc} eq "");
} else {
if (defined $details->{comment_parsed}{unidentified}) {
push(@{ $details->{comment_parsed}{unidentified} },
$line);
} else {
$details->{comment_parsed}{unidentified} = [ $line ];
}
}
}
# Unnecessary linebreaks at the end of the function description
$details->{comment_parsed}{func_desc} =~ s/[\n\s]+$//s
if defined $details->{comment_parsed}{func_desc};
if (defined $details->{comment_parsed}{doxygen_param}) {
# Remove the description from the doxygen_param and
# move into an hash. A sole hash doesn't work, because
# it usually screws up the parameter order
my %param; my @param;
foreach (@{ $details->{comment_parsed}{doxygen_param} }) {
my ($var, $desc) = split(" ",$_,2);
$param{$var} = $desc;
push(@param, $var);
}
$details->{comment_parsed}{doxygen_param} = [ @param ];
$details->{comment_parsed}{doxygen_param_desc} = { %param };
}
if (defined $details->{comment_parsed}{doxygen_defgroup}) {
# Change doxygen_defgroup from array to hash
my %defgroup;
foreach (@{ $details->{comment_parsed}{doxygen_defgroup} }) {
my ($var, $desc) = split(" ",$_,2);
$defgroup{$var} = $desc;
}
$details->{comment_parsed}{doxygen_defgroup} = { %defgroup };
}
if (defined $details->{comment_parsed}{doxygen_ingroup}) {
# There should be a list of all parameters
my @ingroup = ();
foreach (@{ $details->{comment_parsed}{doxygen_ingroup} }) {
push(@ingroup, split());
}
$details->{comment_parsed}{doxygen_ingroup} = [ @ingroup ];
}
foreach (qw(return warning remark)) {
if (defined $details->{comment_parsed}{"doxygen_$_"}) {
# Multiple adjacent @$_ should be concatenated, so
# we can make an scalar out of it. Although we
# actually still disregard the case, that there
# are several non-adjacent @$_s.
$details->{comment_parsed}{"doxygen_$_"} =
join("\n",
@{ $details->{comment_parsed}{"doxygen_$_"} });
}
}
# Dump the output for debugging purposes
# print STDERR "### $details->{perl_name}:\n".
# Dumper $details->{comment_parsed};
# print STDERR "### Original Comment:\n".
# Dumper $details->{comment};
}
# Some more per function information, used in the XS files
my $class = $details->{class};
if ($class) {
my $prefix = $details->{prefix};
$last_prefix = $prefix if $prefix;
if ($details->{name} =~ /^$myprefix/o) {
#e.g. mpxs_Apache__RequestRec_
my $class_prefix = $fmap -> class_c_prefix($class);
if ($details->{name} =~ /$class_prefix/) {
$details->{class_xs_prefix} =
$fmap->class_xs_prefix($class);
}
$details->{class_c_prefix} = $class_prefix;
}
}
}
# Some more information, used in the XS files
my $destructor = $self->typemap->destructor($last_prefix);
my $boot = $self->boot($module);
if ($boot) {
chomp($boot);
$boot =~ s/(\s+$|^\s+)//;
}
my $newxs = $self->{newXS}->{$module};
# Finally do the PDD Dump
my $pdd = {
module => $module,
functions => [ map $$_{perl_name}, @$functions ],
functions_detailed => [ @$functions ],
includes => [ @includes ],
my_xs_prefix => $myprefix,
destructor => $destructor,
boot => $boot,
newXS => $newxs
};
print $fh Dumper $pdd;
close $fh;
$fh = $self->open_class_file($module, '.pod');
$self -> generate_pod($fh, $pdd);
close $fh;
}
# ============================================================================
sub generate {
my $self = shift;
$self->prepare;
# now done by write_missing_makefilepls
#for (qw(ModPerl::WrapXS Apache APR)) {
# $self->write_makefilepl($_);
#}
$self->write_typemap;
for (qw(typedefs sv_convert)) {
$self->write_typemap_h_file($_);
}
$self->get_functions;
$self->get_structures;
while (my($module, $functions) = each %{ $self->{XS} }) {
# my($root, $sub) = split '::', $module;
# if (-e "$self->{XS_DIR}/$root/$sub/$sub.xs") {
# $module = join '::', $root, "Wrap$sub";
# }
if (!$module)
{
print "WARNING: empty module\n" ;
next ;
}
print "mod $module\n" ;
$self->write_makefilepl($module);
$self->write_xs($module, $functions);
$self->write_pm($module);
$self->write_docs($module, $functions);
}
$self -> write_missing_makefilepls ;
}
# ============================================================================
sub stats {
my $self = shift;
$self->get_functions;
$self->get_structures;
my %stats;
while (my($module, $functions) = each %{ $self->{XS} }) {
$stats{$module} += @$functions;
if (my $newxs = $self->{newXS}->{$module}) {
$stats{$module} += @$newxs;
}
}
return \%stats;
}
# ============================================================================
=pod
=head2 mapline_elem (o, elem)
Called for each structure element that is written to the map file by
checkmaps. Allows the user to change the element name, for example
adding a different perl name.
Default: returns the element unmodified
=cut
sub mapline_elem { return $_[1] } ;
# ============================================================================
=pod
=head2 mapline_func (o)
Called for each function that is written to the map file by checkmaps. Allows
the user to change the function name, for example adding a different perl
name.
Default: returns the element unmodified
=cut
sub mapline_func { return $_[1] } ;
# ============================================================================
sub checkmaps {
my $self = shift;
my $prefix = shift;
$self = $self -> new if (!ref $self) ;
my $result = $self -> {typemap} -> checkmaps ;
$self -> {typemap} -> writemaps ($result, $prefix) if ($prefix) ;
return $result ;
}
# ============================================================================
sub run {
my $class = shift ;
my $xs = $class -> new;
$xs->generate;
}
1;
__END__