# This forces PL files to create target in same directory as PL file.
# This is so that make depend always knows where to find PL derivatives.
-chdir(dirname($0));
-($file = basename($0)) =~ s/\.PL$//;
-$file =~ s/\.pl$//
- if ($^O eq 'VMS' or $^O eq 'os2'); # "case-forgiving"
+chdir dirname($0);
+$file = basename($0, '.PL');
+$file .= '.com' if $^O eq 'VMS';
open OUT,">$file" or die "Can't create $file: $!";
# You can use $Config{...} to use Configure variables.
print OUT <<"!GROK!THIS!";
-$Config{'startperl'}
- eval 'exec perl -S \$0 "\$@"'
- if 0;
+$Config{startperl}
+ eval 'exec $Config{perlpath} -S \$0 \${1+"\$@"}'
+ if \$running_under_some_shell;
!GROK!THIS!
# In the following, perl variables are not expanded during extraction.
print OUT <<'!NO!SUBS!';
+
=head1 NAME
h2xs - convert .h C header files to Perl extensions
=head1 SYNOPSIS
-B<h2xs> [B<-AOPXcf>] [B<-v> version] [B<-n> module_name] [B<-p> prefix] [B<-s> sub] [headerfile [extra_libraries]]
+B<h2xs> [B<-AOPXcdf>] [B<-v> version] [B<-n> module_name] [B<-p> prefix] [B<-s> sub] [headerfile ... [extra_libraries]]
B<h2xs> B<-h>
=head1 DESCRIPTION
-I<h2xs> builds a Perl extension from any C header file. The extension will
-include functions which can be used to retrieve the value of any #define
-statement which was in the C header.
+I<h2xs> builds a Perl extension from C header files. The extension
+will include functions which can be used to retrieve the value of any
+#define statement which was in the C header files.
The I<module_name> will be used for the name of the extension. If
-module_name is not supplied then the name of the header file will be used,
-with the first character capitalized.
+module_name is not supplied then the name of the first header file
+will be used, with the first character capitalized.
If the extension might need extra libraries, they should be included
here. The extension Makefile.PL will take care of checking whether
Omit all autoload facilities. This is the same as B<-c> but also removes the
S<C<require AutoLoader>> statement from the .pm file.
+=item B<-F>
+
+Additional flags to specify to C preprocessor when scanning header for
+function declarations. Should not be used without B<-x>.
+
=item B<-O>
Allows a pre-existing extension directory to be overwritten.
Omit the autogenerated stub POD section.
+=item B<-X>
+
+Omit the XS portion. Used to generate templates for a module which is not
+XS-based.
+
=item B<-c>
Omit C<constant()> from the .xs file and corresponding specialised
C<AUTOLOAD> from the .pm file.
+=item B<-d>
+
+Turn on debugging messages.
+
=item B<-f>
Allows an extension to be created for a header even if that header is
Specify a version number for this extension. This version number is added
to the templates. The default is 0.01.
-=item B<-X>
-
-Omit the XS portion. Used to generate templates for a module which is not
-XS-based.
-
=item B<-x>
Automatically generate XSUBs basing on function declarations in the
header file. The package C<C::Scan> should be installed. If this
option is specified, the name of the header file may look like
C<NAME1,NAME2>. In this case NAME1 is used instead of the specified string,
-but XSUBS are emited only for the declarations included from file NAME2.
+but XSUBs are emitted only for the declarations included from file NAME2.
-=item B<-F>
-
-Additional flags to specify to C preprocessor when scanning header for
-function declarations. Should not be used without B<-x>.
+Note that some types of arguments/return-values for functions may
+result in XSUB-declarations/typemap-entries which need
+hand-editing. Such may be objects which cannot be converted from/to a
+pointer (like C<long long>), pointers to functions, or arrays.
=back
h2xs -n DCE::rgynbase -p sec_rgy_ \
-s sec_rgy_wildcard_name,sec_rgy_wildcard_sid dce/rgynbase
- # Make XS with defines in perl.h, and function declarations
+ # Make XS without defines in perl.h, but with function declarations
# visible from perl.h. Name of the extension is perl1.
# When scanning perl.h, define -DEXT=extern -DdEXT= -DINIT(x)=
# Extra backslashes below because the string is passed to shell.
- h2xs -xn perl1 -F "-DEXT=extern -DdEXT= -DINIT\(x\)=" \
- ../perl5_003_01/perl.h
+ # Note that a directory with perl header files would
+ # be added automatically to include path.
+ h2xs -xAn perl1 -F "-DEXT=extern -DdEXT= -DINIT\(x\)=" perl.h
# Same with function declaration in proto.h as visible from perl.h.
- perl H:\get\perl\perl5_003_01.try\utils\h2xs -xn perl1 \
- ../perl5_003_01/perl.h,proto.h
+ h2xs -xAn perl2 perl.h,proto.h
=head1 ENVIRONMENT
=cut
-my( $H2XS_VERSION ) = ' $Revision: 1.16 $ ' =~ /\$Revision:\s+([^\s]+)/;
+my( $H2XS_VERSION ) = ' $Revision: 1.18 $ ' =~ /\$Revision:\s+([^\s]+)/;
my $TEMPLATE_VERSION = '0.01';
use Getopt::Std;
sub usage{
warn "@_\n" if @_;
- die "h2xs [-AOPXcfh] [-v version] [-n module_name] [-p prefix] [-s subs] [headerfile [extra_libraries]]
+ die "h2xs [-AOPXcdfh] [-v version] [-n module_name] [-p prefix] [-s subs] [headerfile [extra_libraries]]
version: $H2XS_VERSION
- -f Force creation of the extension even if the C header does not exist.
- -n Specify a name to use for the extension (recommended).
- -c Omit the constant() function and specialised AUTOLOAD from the XS file.
- -p Specify a prefix which should be removed from the Perl function names.
- -s Create subroutines for specified macros.
-A Omit all autoloading facilities (implies -c).
+ -F Additional flags for C preprocessor (used with -x).
-O Allow overwriting of a pre-existing extension directory.
-P Omit the stub POD section.
-X Omit the XS portion.
+ -c Omit the constant() function and specialised AUTOLOAD from the XS file.
+ -d Turn on debugging messages.
+ -f Force creation of the extension even if the C header does not exist.
+ -h Display this help message
+ -n Specify a name to use for the extension (recommended).
+ -p Specify a prefix which should be removed from the Perl function names.
+ -s Create subroutines for specified macros.
-v Specify a version number for this extension.
-x Autogenerate XSUBs using C::Scan.
- -F Additional flags for C preprocessor (used with -x).
- -h Display this help message
extra_libraries
are any libraries that might be needed for loading the
extension, e.g. -lm would try to link in the math library.
}
-getopts("AOPXcfhxv:n:p:s:F:") || usage;
+getopts("AF:OPXcdfhn:p:s:v:x") || usage;
usage if $opt_h;
$opt_c = 1 if $opt_A;
%const_xsub = map { $_,1 } split(/,+/, $opt_s) if $opt_s;
-$path_h = shift;
-$extralibs = "@ARGV";
+while (my $arg = shift) {
+ if ($arg =~ /^-l/i) {
+ $extralibs = "$arg @ARGV";
+ last;
+ }
+ push(@path_h, $arg);
+}
usage "Must supply header file or module name\n"
- unless ($path_h or $opt_n);
+ unless (@path_h or $opt_n);
-if( $path_h ){
- $name = $path_h;
+if( @path_h ){
+ foreach my $path_h (@path_h) {
+ $name ||= $path_h;
if( $path_h =~ s#::#/#g && $opt_n ){
warn "Nesting of headerfile ignored with -n\n";
}
}
}
elsif ($^O eq 'os2') {
- $path_h = "/usr/include/$path_h" unless $path_h =~ m#^([a-z]:)?[./]#i;
+ $path_h = "/usr/include/$path_h"
+ if $path_h !~ m#^([a-z]:)?[./]#i and -r "/usr/include/$path_h";
+ }
+ else {
+ $path_h = "/usr/include/$path_h"
+ if $path_h !~ m#^[./]# and -r "/usr/include/$path_h";
}
- else { $path_h = "/usr/include/$path_h" unless $path_h =~ m#^[./]#; }
- die "Can't find $path_h\n" if ( ! $opt_f && ! -f $path_h );
-
- # Scan the header file (we should deal with nested header files)
- # Record the names of simple #define constants into const_names
- # Function prototypes are not (currently) processed.
- open(CH, "<$path_h") || die "Can't open $path_h: $!\n";
- while (<CH>) {
+
+ if (!$opt_c) {
+ die "Can't find $path_h\n" if ( ! $opt_f && ! -f $path_h );
+ # Scan the header file (we should deal with nested header files)
+ # Record the names of simple #define constants into const_names
+ # Function prototypes are processed below.
+ open(CH, "<$path_h") || die "Can't open $path_h: $!\n";
+ while (<CH>) {
if (/^#[ \t]*define\s+([\$\w]+)\b\s*[^("]/) {
- print "Matched $_ ($1)\n";
+ print "Matched $_ ($1)\n" if $opt_d;
$_ = $1;
next if /^_.*_h_*$/i; # special case, but for what?
if (defined $opt_p) {
- if (!/^$opt_p(\d)/) {
- ++$prefix{$_} if s/^$opt_p//;
- }
- else {
- warn "can't remove $opt_p prefix from '$_'!\n";
- }
+ if (!/^$opt_p(\d)/) {
+ ++$prefix{$_} if s/^$opt_p//;
+ }
+ else {
+ warn "can't remove $opt_p prefix from '$_'!\n";
+ }
}
$const_names{$_}++;
- }
+ }
+ }
+ close(CH);
+ }
}
- close(CH);
@const_names = sort keys %const_names;
}
mkdir($modpname, 0777);
chdir($modpname) || die "Can't chdir $ext$modpname: $!\n";
+my %types_seen;
+my %std_types;
+my $fdecls;
+my $fdecls_parsed;
+
if( ! $opt_X ){ # use XS, unless it was disabled
open(XS, ">$modfname.xs") || die "Can't create $ext$modpname/$modfname.xs: $!\n";
+ if ($opt_x) {
+ require C::Scan; # Run-time directive
+ require Config; # Run-time directive
+ warn "Scanning typemaps...\n";
+ get_typemap();
+ my $c;
+ my $filter;
+ my @fdecls;
+ foreach my $filename (@path_h) {
+ my $addflags = $opt_F || '';
+ if ($fullpath =~ /,/) {
+ $filename = $`;
+ $filter = $';
+ }
+ warn "Scanning $filename for functions...\n";
+ $c = new C::Scan 'filename' => $filename, 'filename_filter' => $filter,
+ 'add_cppflags' => $addflags;
+ $c->set('includeDirs' => ["$Config::Config{archlib}/CORE"]);
+
+ $fdecls_parsed = $c->get('parsed_fdecls');
+ push(@fdecls, @{$c->get('fdecls')});
+ }
+ $fdecls = [ @fdecls ];
+ }
}
+
open(PM, ">$modfname.pm") || die "Can't create $ext$modpname/$modfname.pm: $!\n";
$" = "\n\t";
if( $opt_X || $opt_c || $opt_A ){
# we won't have our own AUTOLOAD(), so won't have $AUTOLOAD
print PM <<'END';
-use vars qw($VERSION @ISA @EXPORT);
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
END
}
else{
# will want Carp.
print PM <<'END';
use Carp;
-use vars qw($VERSION @ISA @EXPORT $AUTOLOAD);
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD);
END
}
# require autoloader if XS is disabled.
# if XS is enabled, require autoloader unless autoloading is disabled.
-if( $opt_X || (! $opt_A) ){
+if( ($opt_X && (! $opt_A)) || (!$opt_X) ) {
print PM <<"END";
require AutoLoader;
END
my \$constname;
(\$constname = \$AUTOLOAD) =~ s/.*:://;
+ croak "&$module::constant not defined" if \$constname eq 'constant';
my \$val = constant(\$constname, \@_ ? \$_[0] : 0);
if (\$! != 0) {
if (\$! =~ /Invalid/) {
croak "Your vendor has not defined $module macro \$constname";
}
}
- eval "sub \$AUTOLOAD { \$val }";
+ *\$AUTOLOAD = sub () { \$val };
goto &\$AUTOLOAD;
}
$author = "A. U. Thor";
$email = 'a.u.thor@a.galaxy.far.far.away';
+my $const_doc = '';
+my $fdecl_doc = '';
+if (@const_names and not $opt_P) {
+ $const_doc = <<EOD;
+\n=head1 Exported constants
+
+ @{[join "\n ", @const_names]}
+
+EOD
+}
+if (defined $fdecls and @$fdecls and not $opt_P) {
+ $fdecl_doc = <<EOD;
+\n=head1 Exported functions
+
+ @{[join "\n ", @$fdecls]}
+
+EOD
+}
+
$pod = <<"END" unless $opt_P;
## Below is the stub of documentation for your module. You better edit it!
#
#unedited.
#
#Blah blah blah.
-#
+#$const_doc$fdecl_doc
#=head1 AUTHOR
#
#$author, $email
#endif
END
-if( $path_h ){
+if( @path_h ){
+ foreach my $path_h (@path_h) {
my($h) = $path_h;
$h =~ s#^/usr/include/##;
if ($^O eq 'VMS') { $h =~ s#.*vms\]#sys/# or $h =~ s#.*[:>\]]##; }
-print XS <<"END";
-#include <$h>
-
-END
+ print XS qq{#include <$h>\n};
+ }
+ print XS "\n";
}
if( ! $opt_c ){
END
+my %seen_decl;
+
+
sub print_decl {
my $fh = shift;
my $decl = shift;
my ($type, $name, $args) = @$decl;
+ return if $seen_decl{$name}++; # Need to do the same for docs as well?
+
my @argnames = map {$_->[1]} @$args;
my @argtypes = map { normalize_type( $_->[0] ) } @$args;
+ my @argarrays = map { $_->[4] || '' } @$args;
my $numargs = @$args;
if ($numargs and $argtypes[-1] eq '...') {
$numargs--;
for $arg (0 .. $numargs - 1) {
print $fh <<"EOP";
- $argtypes[$arg] $argnames[$arg]
+ $argtypes[$arg] $argnames[$arg]$argarrays[$arg]
EOP
}
}
-my $ignore_mods = '(?:\b(?:__const__|static|inline|__inline__)\b\s*)*';
+# Should be called before any actual call to normalize_type().
+sub get_typemap {
+ # We do not want to read ./typemap by obvios reasons.
+ my @tm = qw(../../../typemap ../../typemap ../typemap);
+ my $stdtypemap = "$Config::Config{privlib}/ExtUtils/typemap";
+ unshift @tm, $stdtypemap;
+ my $proto_re = "[" . quotemeta('\$%&*@;') . "]" ;
+ my $image;
+
+ foreach $typemap (@tm) {
+ next unless -e $typemap ;
+ # skip directories, binary files etc.
+ warn " Scanning $typemap\n";
+ warn("Warning: ignoring non-text typemap file '$typemap'\n"), next
+ unless -T $typemap ;
+ open(TYPEMAP, $typemap)
+ or warn ("Warning: could not open typemap file '$typemap': $!\n"), next;
+ my $mode = 'Typemap';
+ while (<TYPEMAP>) {
+ next if /^\s*\#/;
+ if (/^INPUT\s*$/) { $mode = 'Input'; next; }
+ elsif (/^OUTPUT\s*$/) { $mode = 'Output'; next; }
+ elsif (/^TYPEMAP\s*$/) { $mode = 'Typemap'; next; }
+ elsif ($mode eq 'Typemap') {
+ next if /^\s*($|\#)/ ;
+ if ( ($type, $image) =
+ /^\s*(.*?\S)\s+(\S+)\s*($proto_re*)\s*$/o
+ # This may reference undefined functions:
+ and not ($image eq 'T_PACKED' and $typemap eq $stdtypemap)) {
+ normalize_type($type);
+ }
+ }
+ }
+ close(TYPEMAP) or die "Cannot close $typemap: $!";
+ }
+ %std_types = %types_seen;
+ %types_seen = ();
+}
+
sub normalize_type {
+ my $ignore_mods = '(?:\b(?:__const__|static|inline|__inline__)\b\s*)*';
my $type = shift;
$type =~ s/$ignore_mods//go;
+ $type =~ s/([\]\[()])/ \1 /g;
$type =~ s/\s+/ /g;
$type =~ s/\s+$//;
$type =~ s/^\s+//;
$type =~ s/\b\*/ */g;
$type =~ s/\*\b/* /g;
$type =~ s/\*\s+(?=\*)/*/g;
+ $types_seen{$type}++
+ unless $type eq '...' or $type eq 'void' or $std_types{$type};
$type;
}
if ($opt_x) {
- require C::Scan; # Run-time directive
- require Config; # Run-time directive
- my $c;
- my $filter;
- my $filename = $path_h;
- my $addflags = $opt_F || '';
- if ($fullpath =~ /,/) {
- $filename = $`;
- $filter = $';
- }
- $c = new C::Scan 'filename' => $filename, 'filename_filter' => $filter,
- 'add_cppflags' => $addflags;
- $c->set('includeDirs' => [$Config::Config{shrpdir}]);
-
- my $fdec = $c->get('parsed_fdecls');
-
- for $decl (@$fdec) { print_decl(\*XS, $decl) }
+ for $decl (@$fdecls_parsed) { print_decl(\*XS, $decl) }
}
close XS;
+
+if (%types_seen) {
+ my $type;
+ warn "Writing $ext$modpname/typemap\n";
+ open TM, ">typemap" or die "Cannot open typemap file for write: $!";
+
+ for $type (keys %types_seen) {
+ print TM $type, "\t" x (6 - int((length $type)/8)), "T_PTROBJ\n"
+ }
+
+ close TM or die "Cannot close typemap file for write: $!";
+}
+
} # if( ! $opt_X )
warn "Writing $ext$modpname/Makefile.PL\n";
unless ($@) { @files = readdir(D); closedir(D); }
}
if (!@files) { @files = map {chomp && $_} `ls`; }
-print MANI join("\n",@files);
+if ($^O eq 'VMS') {
+ foreach (@files) {
+ # Clip trailing '.' for portability -- non-VMS OSs don't expect it
+ s%\.$%%;
+ # Fix up for case-sensitive file systems
+ s/$modfname/$modfname/i && next;
+ $_ = "\U$_" if $_ eq 'manifest' or $_ eq 'changes';
+ $_ = 'Makefile.PL' if $_ eq 'makefile.pl';
+ }
+}
+print MANI join("\n",@files), "\n";
close MANI;
!NO!SUBS!