1 package ExtUtils::ParseXS::Utilities;
7 use ExtUtils::ParseXS::Constants ();
8 our (@ISA, @EXPORT_OK);
11 standard_typemap_locations
17 process_single_typemap
24 ExtUtils::ParseXS::Utilities - Subroutines used with ExtUtils::ParseXS
28 use ExtUtils::ParseXS::Utilities qw(
29 standard_typemap_locations
40 The following functions are not considered to be part of the public interface.
41 They are documented here for the benefit of future maintainers of this module.
43 =head2 C<standard_typemap_locations()>
49 Provide a list of filepaths where F<typemap> files may be found. The
50 filepaths -- relative paths to files (not just directory paths) -- appear in this list in lowest-to-highest priority.
52 The highest priority is to look in the current directory.
56 The second and third highest priorities are to look in the parent of the
57 current directory and a directory called F<lib/ExtUtils> underneath the parent
61 '../lib/ExtUtils/typemap',
63 The fourth through ninth highest priorities are to look in the corresponding
64 grandparent, great-grandparent and great-great-grandparent directories.
67 '../../lib/ExtUtils/typemap',
69 '../../../lib/ExtUtils/typemap',
70 '../../../../typemap',
71 '../../../../lib/ExtUtils/typemap',
73 The tenth and subsequent priorities are to look in directories named
74 F<ExtUtils> which are subdirectories of directories found in C<@INC> --
75 I<provided> a file named F<typemap> actually exists in such a directory.
78 '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap',
80 However, these filepaths appear in the list returned by
81 C<standard_typemap_locations()> in reverse order, I<i.e.>, lowest-to-highest.
83 '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap',
84 '../../../../lib/ExtUtils/typemap',
85 '../../../../typemap',
86 '../../../lib/ExtUtils/typemap',
88 '../../lib/ExtUtils/typemap',
90 '../lib/ExtUtils/typemap',
96 my @stl = standard_typemap_locations( \@INC );
102 Array holding list of directories to be searched for F<typemap> files.
108 sub standard_typemap_locations {
109 my $include_ref = shift;
110 my @tm = qw(typemap);
112 my $updir = File::Spec->updir();
114 File::Spec->catdir(($updir) x 1),
115 File::Spec->catdir(($updir) x 2),
116 File::Spec->catdir(($updir) x 3),
117 File::Spec->catdir(($updir) x 4),
119 unshift @tm, File::Spec->catfile($dir, 'typemap');
120 unshift @tm, File::Spec->catfile($dir, lib => ExtUtils => 'typemap');
122 foreach my $dir (@{ $include_ref}) {
123 my $file = File::Spec->catfile($dir, ExtUtils => 'typemap');
124 unshift @tm, $file if -e $file;
129 =head2 C<trim_whitespace()>
135 Perform an in-place trimming of leading and trailing whitespace from the
136 first argument provided to the function.
140 trim_whitespace($arg);
144 None. Remember: this is an I<in-place> modification of the argument.
150 sub trim_whitespace {
151 $_[0] =~ s/^\s+|\s+$//go;
154 =head2 C<tidy_type()>
160 Rationalize any asterisks (C<*>) by joining them into bunches, removing
161 interior whitespace, then trimming leading and trailing whitespace.
165 ($ret_type) = tidy_type($_);
167 String to be cleaned up.
180 # rationalise any '*' by joining them into bunches and removing whitespace
184 # change multiple whitespace into a single space
187 # trim leading & trailing whitespace
199 Escape backslashes (C<\>) in prototype strings.
203 $ProtoThisXSUB = C_string($_);
205 String needing escaping.
209 Properly escaped string.
218 $string =~ s[\\][\\\\]g;
222 =head2 C<valid_proto_string()>
228 Validate prototype string.
232 String needing checking.
236 Upon success, returns the same string passed as argument.
238 Upon failure, returns C<0>.
244 sub valid_proto_string {
247 if ( $string =~ /^$ExtUtils::ParseXS::Constants::proto_re+$/ ) {
254 =head2 C<process_typemaps()>
260 Process all typemap files.
264 my ($type_kind_ref, $proto_letter_ref, $input_expr_ref, $output_expr_ref) =
265 process_typemaps( $args{typemap}, $pwd );
267 List of two elements: C<typemap> element from C<%args>; current working
272 Upon success, returns a list of four hash references. (This will probably be
279 sub process_typemaps {
280 my ($tmap, $pwd) = @_;
282 my @tm = ref $tmap ? @{$tmap} : ($tmap);
284 foreach my $typemap (@tm) {
285 die "Can't find $typemap in $pwd\n" unless -r $typemap;
288 push @tm, standard_typemap_locations( \@INC );
290 my ($type_kind_ref, $proto_letter_ref, $input_expr_ref, $output_expr_ref)
291 = ( {}, {}, {}, {} );
293 foreach my $typemap (@tm) {
294 next unless -f $typemap;
295 # skip directories, binary files etc.
296 warn("Warning: ignoring non-text typemap file '$typemap'\n"), next
298 ($type_kind_ref, $proto_letter_ref, $input_expr_ref, $output_expr_ref) =
299 process_single_typemap( $typemap,
300 $type_kind_ref, $proto_letter_ref, $input_expr_ref, $output_expr_ref);
302 return ($type_kind_ref, $proto_letter_ref, $input_expr_ref, $output_expr_ref);
305 sub process_single_typemap {
307 $type_kind_ref, $proto_letter_ref, $input_expr_ref, $output_expr_ref) = @_;
308 open my $TYPEMAP, '<', $typemap
309 or warn ("Warning: could not open typemap file '$typemap': $!\n"), next;
310 my $mode = 'Typemap';
312 my $current = \$junk;
317 $mode = 'Input'; $current = \$junk; next;
320 $mode = 'Output'; $current = \$junk; next;
322 if (/^TYPEMAP\s*$/) {
323 $mode = 'Typemap'; $current = \$junk; next;
325 if ($mode eq 'Typemap') {
327 my $logged_line = $_;
331 my($type,$kind, $proto) =
332 m/^\s*(.*?\S)\s+(\S+)\s*($ExtUtils::ParseXS::Constants::proto_re*)\s*$/
334 "Warning: File '$typemap' Line $. '$logged_line' " .
335 "TYPEMAP entry needs 2 or 3 columns\n"
338 $type = tidy_type($type);
339 $type_kind_ref->{$type} = $kind;
340 # prototype defaults to '$'
341 $proto = "\$" unless $proto;
343 # "Warning: File '$typemap' Line $. '$logged_line' " .
344 # "Invalid prototype '$proto'\n"
345 # ) unless valid_proto_string($proto);
346 $proto_letter_ref->{$type} = C_string($proto);
351 elsif ($mode eq 'Input') {
353 $input_expr_ref->{$_} = '';
354 $current = \$input_expr_ref->{$_};
358 $output_expr_ref->{$_} = '';
359 $current = \$output_expr_ref->{$_};
363 return ($type_kind_ref, $proto_letter_ref, $input_expr_ref, $output_expr_ref);
366 =head2 C<make_targetable()>
372 Populate C<%targetable>.
376 %targetable = make_targetable(\%output_expr);
378 Reference to C<%output_expr>.
388 sub make_targetable {
389 my $output_expr_ref = shift;
392 $bal = qr[(?:(?>[^()]+)|\((??{ $bal })\))*]; # ()-balanced
393 $cast = qr[(?:\(\s*SV\s*\*\s*\)\s*)?]; # Optional (SV*) cast
394 $size = qr[,\s* (??{ $bal }) ]x; # Third arg (to setpvn)
397 foreach my $key (keys %{ $output_expr_ref }) {
398 # We can still bootstrap compile 're', because in code re.pm is
399 # available to miniperl, and does not attempt to load the XS code.
402 my ($t, $with_size, $arg, $sarg) =
403 ($output_expr_ref->{$key} =~
404 m[^ \s+ sv_set ( [iunp] ) v (n)? # Type, is_setpvn
405 \s* \( \s* $cast \$arg \s* ,
406 \s* ( (??{ $bal }) ) # Set from
407 ( (??{ $size }) )? # Possible sizeof set-from
411 $targetable{$key} = [$t, $with_size, $arg, $sarg] if $t;
417 my ($type, $varname, $hiertype) = @_;
419 # C++ has :: in types too so skip this
420 $type =~ tr/:/_/ unless $hiertype;
421 $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s;
423 if ($type =~ / \( \s* \* (?= \s* \) ) /xg) {
424 (substr $type, pos $type, 0) = " $varname ";
427 $type .= "\t$varname";