php IHDR w Q )Ba pHYs sRGB gAMA a IDATxMk\U s&uo,mD )Xw+e?tw.oWp;QHZnw`gaiJ9̟灙a=nl[ ʨ G;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ y H@E7j 1j+OFRg}ܫ;@Ea~ j`u'o> j- $_q?qS XzG'ay
files >> /opt/lampp/lib/perl5/site_perl/5.16.3/ExtUtils/XSBuilder/ |
files >> //opt/lampp/lib/perl5/site_perl/5.16.3/ExtUtils/XSBuilder/WrapXS.pm |
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__y~or5J={Eeu磝Qk ᯘG{?+]ן?wM3X^歌>{7پK>on\jy Rg/=fOroNVv~Y+ NGuÝHWyw[eQʨSb> >}Gmx[o[<{Ϯ_qFvM IENDB`