joes        2003/05/27 03:04:36

  Added:       build    xsbuilder.pl
  Log:
  Add xsbuilder.pl, an ExtUtils::XSBuilder script to make the perl glue.
  
  Revision  Changes    Path
  1.1                  httpd-apreq-2/build/xsbuilder.pl
  
  Index: xsbuilder.pl
  ===================================================================
  #!/usr/bin/perl
  # requires successful ./configure && make
  #
  # expected usage: cd glue/perl; ../../build/xsbuilder.pl run run
  #
  
  use strict;
  use warnings FATAL => 'all';
  use Apache2;
  use Apache::Build;
  
  use Cwd;
  cwd =~ m{^(.+httpd-apreq-2)} or die "Can't find base cvs directory";
  my $base_dir = $1;
  my $src_dir = "$base_dir/src";
  
  sub slurp($$)
  {
      open my $file, $_[1] or die $!;
      read $file, $_[0], -s $file;
  }
  
  slurp my $config => "$base_dir/config.status";
  $config =~ /^s,[EMAIL PROTECTED]@,([^,]+)/m && -d $1 or
      die "Can't find apache include directory";
  my $apache_includes = $1;
  $config =~ m/^s,[EMAIL PROTECTED]@,([^,]+)/m && -d $1 or
      die "Can't find apr lib directory";
  my $apr_libs = $1;
  
  my $mp2_typemaps = Apache::Build->new->typemaps;
  read DATA, my $grammar, -s DATA;
  
  my %c_macro_cache;
  sub c_macro
  {
      return $c_macro_cache{"@_"} if exists $c_macro_cache{"@_"};
  
      my ($name, $header) = @_;
      my $src;
      if (defined $header) {
          slurp local $_ => "$src_dir/$header";
          /^#define $name\s*\(([^)]+)\)\s+(.+?[^\\])$/ms
              or die "Can't find definition for '$name': $_";
          my $def = $2;
          my @args = split /\s*,\s*/, $1;
          for ([EMAIL PROTECTED]) {
              $def =~ s/\b$args[$_-1]\b/ \$$_ /g;
          }
          my $args = join ',' => ('([^,)]+)') x @args;
          $src = "sub { /^#define $name.+?[^\\\\]\$/gms +
                        s{$name\\s*\\($args\\)}{$def}g}";
      }
      else {
          $src = "sub { /^#define $name.+?[^\\\\]\$/gms +
                        s{$name\\s*\\(([^)]+)\\)}{\$1}g}";
      }
      return $c_macro_cache{"@_"} = eval $src;
  }
  
  
  
  package Apache::Request::ParseSource;
  use base qw/ExtUtils::XSBuilder::ParseSource/;
  
  __PACKAGE__->$_ for shift || ();
  
  sub package {'Apache::Request'}
  
  # ParseSource.pm v 0.23 bug: line 214 should read
  # my @dirs = @{$self->include_dirs};
  sub include_dirs {["$base_dir/src"]}
  
  sub preprocess
  {
      # need to macro-expand APREQ_DECLARE et.al. so P::RD can DTRT with
      # ExtUtils::XSBuilder::C::grammar
  
      for ($_[1]) {
          ::c_macro("APREQ_DECLARE", "apreq.h")->();
          ::c_macro("APREQ_DECLARE_HOOK", "apreq_parsers.h")->();
          ::c_macro("APREQ_DECLARE_PARSER", "apreq_parsers.h")->();
          ::c_macro("APREQ_DECLARE_LOG", "apreq_env.h")->();
          ::c_macro("APR_DECLARE")->();
      }
  }
  sub parse {
      my $self = shift;
  
      $self -> find_includes ;
      my $c = $self -> {c} = {} ;
      print "Initialize parser\n" if ($__SUPER__::verbose) ;
  
      $::RD_HINT++;
  
      my $parser = $self -> {parser} = Parse::RecDescent->new($grammar);
  
      $parser -> {data} = $c ;
      $parser -> {srcobj} = $self ;
  
      $self -> extent_parser ($parser) ;
  
      foreach my $inc (@{$self->{includes}}) {
          print "scan $inc ...\n" if ($__SUPER__::verbose) ;
          $self->scan ($inc) ;
      }
  
  }
  
  
  
  package Apache::Request::WrapXS;
  use base qw/ExtUtils::XSBuilder::WrapXS/;
  our $VERSION = '0.1';
  __PACKAGE__ -> $_ for @ARGV;
  
  sub parsesource_objects {[Apache::Request::ParseSource->new]}
  sub new_typemap {Apache::Request::TypeMap->new(shift)}
  sub h_filename_prefix {'apreq_'}
  sub my_xs_prefix {'apreq_'}
  
  sub makefilepl_text {
      my($self, $class, $deps,$typemap) = @_;
  
      my @parts = split (/::/, $class) ;
      my $mmargspath = '../' x @parts ;
      $mmargspath .= 'mmargs.pl' ;
  
      # XXX probably should gut EU::MM and use MP::MM instead
      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' => [qw(@$mp2_typemaps $typemap)],
      'INC'      => "-I.. -I../.. -I../../.. -I$src_dir -I$apache_includes",
      'LIBS'     => "-L$src_dir/.libs -L$apr_libs -lapreq -lapr-0 -laprutil-0",
  } ;
  $txt .= "'depend'  => $deps,\n" if ($deps) ;
  $txt .= qq{    
      \%\$MMARGS,
  );
  
  } ;
  
  }
  
  
  package Apache::Request::TypeMap;
  use base 'ExtUtils::XSBuilder::TypeMap';
  
  
  # XXX This needs serious work
  sub typemap_code
  {
      {
          'T_MAGICHASH_SV' => 
           {
               OUTPUT => 'if ($var -> _perlsv) $arg = $var -> _perlsv; else 
$arg = &sv_undef;',
  
               c2perl => '(ptr->_perlsv?ptr->_perlsv:&sv_undef)',
  
               INPUT =>  <<'EOT',
  do {
      MAGIC *mg;
      if (mg = mg_find (SvRV($arg), '~'))
          $var = *(($type *)(mg -> mg_ptr));
      else
          croak (\"$var is not of type $type\");
  } while(0)
  EOT
  
               perl2c => <<'EOT',
  (SvOK(sv) ?                                                        \\
              ((SvROK(sv) && SvMAGICAL(SvRV(sv))) ||                 \\
               (Perl_croak(aTHX_ "$croak ($expect)"),0) ?            \\
                   *(($ctype **)(mg_find(SvRV(sv),'~')->mg_ptr)) :   \\
                    ($ctype *)NULL)                                  \\
            : ($ctype *)NULL)
  EOT
  
               create => <<'EOT',
  do {                                                               \\
      sv = (SV *)newHV ();                                           \\
      p = alloc;                                                     \\
      memset (p, 0, sizeof($ctype));                                 \\
      sv_magic ((SV *)sv, NULL, '~', (char *)&p, sizeof (p));        \\
      rv = p -> _perlsv = newRV_noinc ((SV *)sv);                    \\
      sv_bless (rv, gv_stashpv ("$class", 0));                       \\
  } while (0)
  EOT
              destroy => '    free(ptr)',
           },
  
  
          'T_PTROBJ' => 
              {
              'c2perl' => '    sv_setref_pv(sv_newmortal(), "$class", 
(void*)ptr)',
  
              'perl2c' =>
  q[(SvOK(sv)?((SvROK(sv) && (SvTYPE(SvRV(sv)) == SVt_PVMG)) \\\\
  || (Perl_croak(aTHX_ "$croak ($expect)"),0) ? \\\\
  ($ctype *)SvIV((SV*)SvRV(sv)) : ($ctype *)NULL):($ctype *)NULL)
  ],
  
              'create' => 
  q[   rv = newSViv(0) ; \\\\
      sv = newSVrv (rv, "$class") ; \\\\
      SvUPGRADE(sv, SVt_PVIV) ; \\\\
      SvGROW(sv, sizeof (*p)) ;  \\\\
      p = ($ctype *)SvPVX(sv) ;\\\\
      memset(p, 0, sizeof (*p)) ;\\\\
      SvIVX(sv) = (IV)p ;\\\\
      SvIOK_on(sv) ;\\\\
      SvPOK_on(sv) ;
  ],
  
              },
          'T_AVREF' => 
              {
              'OUTPUT' => '        $arg = SvREFCNT_inc 
(epxs_AVREF_2obj($var));',
              'INPUT'  => '        $var = epxs_sv2_AVREF($arg)',
              },
          'T_HVREF' => 
              {
              'OUTPUT' => '        $arg = SvREFCNT_inc 
(epxs_HVREF_2obj($var));',
              'INPUT'  => '        $var = epxs_sv2_HVREF($arg)',
              },
          'T_SVPTR' => 
              {
              'OUTPUT' => '        $arg = SvREFCNT_inc 
(epxs_SVPTR_2obj($var));',
              'INPUT'  => '        $var = epxs_sv2_SVPTR($arg)',
              },
          }
  }
  
  # force DATA into main package
  package main;
  1;
  
  __DATA__
  { 
  use ExtUtils::XSBuilder::C::grammar ; # import cdef_xxx functions 
  }
  
  code: comment_part(s) {1}
  
  comment_part:
      comment(s?) part
          { 
          #print "comment: ", Data::Dumper::Dumper([EMAIL PROTECTED]) ;
          $item[2] -> {comment} = "@{$item[1]}" if (ref $item[1] && @{$item[1]} 
&& ref $item[2]) ;
          1 ;
          }
      | comment
  
  part:   
      prepart 
      | stdpart
          {
          if ($thisparser -> {my_neednewline}) 
              {
              print "\n" ;
              $thisparser -> {my_neednewline} = 0 ;
              }
          $return = $item[1] ;
          }
  
  # prepart can be used to extent the parser (for default it always fails)
  
  prepart:  '?' 
          {0}
  
             
  stdpart:   
      define
          {
          $return = cdef_define ($thisparser, $item[1][0], $item[1][1]) ;
          }
      | struct
          {
          $return = cdef_struct ($thisparser, @{$item[1]}) ;
          }
      | enum
          {
          $return = cdef_enum ($thisparser, $item[1][1]) ;
          }
      | function_declaration
          {
          $return = cdef_function_declaration ($thisparser, @{$item[1]}) ;
          }
      | struct_typedef
          {
          my ($type,$alias) = @{$item[1]}[0,1];
          $return = cdef_struct ($thisparser, undef, $type, undef, $alias) ;
          }
      | comment
      | anything_else
  
  comment:
      m{\s* // \s* ([^\n]*) \s*? \n }x
          { $1 }
      | m{\s* /\* \s* ([^*]+|\*(?!/))* \s*? \*/  ([ \t]*)? }x
          { $item[1] =~ m#/\*\s*?(.*?)\s*?\*/#s ; $1 }
  
  semi_linecomment:
      m{;\s*\n}x
          {
          $return = [] ;
          1 ;
          }
      | ';' comment(s?)
          {
          $item[2]
          }
  
  function_definition:
      rtype IDENTIFIER '(' <leftop: arg ',' arg>(s?) ')' '{'
          [EMAIL PROTECTED],1], $item[4]]}
  
  pTHX:
      'pTHX_'
  
  function_declaration:
      type_identifier '(' pTHX(?) <leftop: arg_decl ',' arg_decl>(s?) ')' 
function_declaration_attr ( ';' | '{' )
          {
          #print Data::Dumper::Dumper ([EMAIL PROTECTED]) ;
              [
              $item[1][1], 
              $item[1][0], 
              @{$item[3]}?[['pTHX', 'aTHX' ], @{$item[4]}]:$item[4] 
              ]
          }
  
  define:
      '#define' IDENTIFIER /.*?\n/
          {
          $item[3] =~ m{(?:/\*\s*(.*?)\s*\*/|//\s*(.*?)\s*$)} ; [$item[2], $1] 
          }
  
  ignore_cpp:
      '#' /.*?\n/
  
  struct: 
      'struct' IDENTIFIER '{' field(s) '}' ';'
          {
          # [perlname, cname, fields]
          [$item[2], "@item[1,2]", $item[4]]
          }
      | 'typedef' 'struct' '{' field(s) '}' IDENTIFIER ';'
          {
          # [perlname, cname, fields]
          [$item[6], undef, $item[4], $item[6]]
          }
      | 'typedef' 'struct' IDENTIFIER '{' field(s) '}' IDENTIFIER ';'
          {
          # [perlname, cname, fields, alias]
          [$item[3], "@item[2,3]", $item[5], $item[7]]
          }
  
  struct_typedef: 
      'typedef' 'struct' IDENTIFIER IDENTIFIER ';'
          {
        ["@item[2,3]", $item[4]]
        }
  
  enum: 
      'enum' IDENTIFIER '{' enumfield(s) '}' ';'
          {
          [$item[2], $item[4]]
          }
      | 'typedef' 'enum' '{' enumfield(s) '}' IDENTIFIER ';'
          {
          [undef, $item[4], $item[6]]
          }
      | 'typedef' 'enum' IDENTIFIER '{' enumfield(s) '}' IDENTIFIER ';'
          {
          [$item[3], $item[5], $item[7]]
          }
  
  field: 
      comment 
      | define
        {
          $return = cdef_define ($thisparser, $item[1][0], $item[1][1]) ;
        }
      | valuefield 
      | callbackfield
      | ignore_cpp
  
  valuefield: 
      type_identifier comment(s?) semi_linecomment
          {
          $thisparser -> {my_neednewline} = 1 ;
          print "  valuefield: $item[1][0] : $item[1][1]\n" ;
        [$item[1][0], $item[1][1], [EMAIL PROTECTED]:() , [EMAIL PROTECTED]:()] 
]
          }
  
  
  callbackfield: 
      rtype '(' '*' IDENTIFIER ')' '(' <leftop: arg_decl ',' arg_decl>(s?) ')' 
comment(s?) semi_linecomment
          {
          my $type = "$item[1](*)(" . join(',', map { "$_->[0] $_->[1]" } 
@{$item[7]}) . ')' ;
          my $dummy = 'arg0' ;
          my @args ;
          for (@{$item[7]})
              {
              if (ref $_) 
                  {
                  push @args, { 
                      'type' => $_->[0], 
                      'name' => $_->[1], 
                      } if ($_->[0] ne 'void') ; 
                  }
              }
          my $s = { 'name' => $type, 'return_type' => $item[1], args => [EMAIL 
PROTECTED] } ;
          push @{$thisparser->{data}{callbacks}}, $s  if 
($thisparser->{srcobj}->handle_callback($s)) ;
  
          $thisparser -> {my_neednewline} = 1 ;
          print "  callbackfield: $type : $item[4]\n" ;
          [$type, $item[4], [EMAIL PROTECTED]:() , [EMAIL PROTECTED]:()]] ;
          }
  
  
  enumfield: 
      comment
      | IDENTIFIER  comment(s?) /,?/ comment(s?)
          {
          [$item[1], [EMAIL PROTECTED]:() , [EMAIL PROTECTED]:()] ] ;
          }
  
  rtype:  
      modmodifier(s) TYPE star(s?)
          {
          my @modifier = @{$item[1]} ;
          shift @modifier if ($modifier[0] eq 'extern' || $modifier[0] eq 
'static') ;
  
          $return = join ' ',@modifier, $item[2] ;
          $return .= join '',' ',@{$item[3]} if @{$item[3]};
          1 ;
        }
      | TYPE(s) star(s?)
          {
          $return = join (' ', @{$item[1]}) ;
          $return .= join '',' ',@{$item[2]} if @{$item[2]};
        #print "rtype $return \n" ;
          1 ;
          }
      modifier(s)  star(s?)
          {
          join ' ',@{$item[1]}, @{$item[2]} ;
        }
  
  arg:
      type_identifier 
          {[$item[1][0],$item[1][1]]}
      | '...'
          {['...']}
  
  arg_decl: 
      rtype '(' '*' IDENTIFIER ')' '(' <leftop: arg_decl ',' arg_decl>(s?) ')'
          {
          my $type = "$item[1](*)(" . join(',', map { "$_->[0] $_->[1]" } 
@{$item[7]}) . ')' ;
          my $dummy = 'arg0' ;
          my @args ;
          for (@{$item[7]})
              {
              if (ref $_) 
                  {
                  push @args, { 
                      'type' => $_->[0], 
                      'name' => $_->[1], 
                      } if ($_->[0] ne 'void') ; 
                  }
              }
          my $s = { 'name' => $type, 'return_type' => $item[1], args => [EMAIL 
PROTECTED] } ;
          push @{$thisparser->{data}{callbacks}}, $s  if 
($thisparser->{srcobj}->handle_callback($s)) ;
  
          [$type, $item[4], [EMAIL PROTECTED]:() , [EMAIL PROTECTED]:()]] ;
          }
      | 'pTHX'
        {
        ['pTHX', 'aTHX' ]
        }
      | type_identifier
        {
        [$item[1][0], $item[1][1] ]
        }
      | '...'
          {['...']}
  
  function_declaration_attr:
  
  type_identifier:
      type_varname 
          { 
          my $r ;
        my @type = @{$item[1]} ;
        #print "type = @type\n" ;
        my $name = pop @type ;
        if (@type && ($name !~ /\*/)) 
            {
              $r = [join (' ', @type), $name] 
            }
        else
            {
            $r = [join (' ', @{$item[1]})] ;
            }               
        #print "r = @$r\n" ;
          $r ;
          }
   
  type_varname:   
      attribute(s?) TYPE(s) star(s) varname(?)
          {
        [EMAIL PROTECTED], @{$item[2]}, @{$item[3]}, @{$item[4]}] ;     
        }
      | attribute(s?) varname(s)
          {
        $item[2] ;      
        }
  
  
  varname:
      ##IDENTIFIER '[' IDENTIFIER ']'
      IDENTIFIER '[' /[^]]+/ ']'
        {
        "$item[1]\[$item[3]\]" ;
        }
      | IDENTIFIER ':' IDENTIFIER
        {
        $item[1]
        }
      | IDENTIFIER
        {
        $item[1]
        }
  
  
  star: '*' | 'const' '*'
          
  modifier: 'const' | 'struct' | 'enum' | 'unsigned' | 'long' | 'extern' | 
'static' | 'short' | 'signed'
  
  modmodifier: 'const' | 'struct' | 'enum' | 'extern' | 'static'
  
  attribute: 'extern' | 'static' 
  
  # IDENTIFIER: /[a-z]\w*/i
  IDENTIFIER: /\w+/
  
  TYPE: /\w+/
  
  anything_else: /.*/
  
  
  

Reply via email to