1 package ExtUtils::ParseXS::Utilities;
7 use ExtUtils::ParseXS::Constants ();
8 require ExtUtils::Typemaps;
10 our (@ISA, @EXPORT_OK);
13 standard_typemap_locations
23 analyze_preprocessor_statements
28 check_conditional_preprocessor_statements
33 ExtUtils::ParseXS::Utilities - Subroutines used with ExtUtils::ParseXS
37 use ExtUtils::ParseXS::Utilities qw(
38 standard_typemap_locations
48 analyze_preprocessor_statements
53 check_conditional_preprocessor_statements
58 The following functions are not considered to be part of the public interface.
59 They are documented here for the benefit of future maintainers of this module.
61 =head2 C<standard_typemap_locations()>
67 Provide a list of filepaths where F<typemap> files may be found. The
68 filepaths -- relative paths to files (not just directory paths) -- appear in this list in lowest-to-highest priority.
70 The highest priority is to look in the current directory.
74 The second and third highest priorities are to look in the parent of the
75 current directory and a directory called F<lib/ExtUtils> underneath the parent
79 '../lib/ExtUtils/typemap',
81 The fourth through ninth highest priorities are to look in the corresponding
82 grandparent, great-grandparent and great-great-grandparent directories.
85 '../../lib/ExtUtils/typemap',
87 '../../../lib/ExtUtils/typemap',
88 '../../../../typemap',
89 '../../../../lib/ExtUtils/typemap',
91 The tenth and subsequent priorities are to look in directories named
92 F<ExtUtils> which are subdirectories of directories found in C<@INC> --
93 I<provided> a file named F<typemap> actually exists in such a directory.
96 '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap',
98 However, these filepaths appear in the list returned by
99 C<standard_typemap_locations()> in reverse order, I<i.e.>, lowest-to-highest.
101 '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap',
102 '../../../../lib/ExtUtils/typemap',
103 '../../../../typemap',
104 '../../../lib/ExtUtils/typemap',
106 '../../lib/ExtUtils/typemap',
108 '../lib/ExtUtils/typemap',
114 my @stl = standard_typemap_locations( \@INC );
116 Reference to C<@INC>.
120 Array holding list of directories to be searched for F<typemap> files.
126 sub standard_typemap_locations {
127 my $include_ref = shift;
128 my @tm = qw(typemap);
130 my $updir = File::Spec->updir();
132 File::Spec->catdir(($updir) x 1),
133 File::Spec->catdir(($updir) x 2),
134 File::Spec->catdir(($updir) x 3),
135 File::Spec->catdir(($updir) x 4),
137 unshift @tm, File::Spec->catfile($dir, 'typemap');
138 unshift @tm, File::Spec->catfile($dir, lib => ExtUtils => 'typemap');
140 foreach my $dir (@{ $include_ref}) {
141 my $file = File::Spec->catfile($dir, ExtUtils => 'typemap');
142 unshift @tm, $file if -e $file;
147 =head2 C<trim_whitespace()>
153 Perform an in-place trimming of leading and trailing whitespace from the
154 first argument provided to the function.
158 trim_whitespace($arg);
162 None. Remember: this is an I<in-place> modification of the argument.
168 sub trim_whitespace {
169 $_[0] =~ s/^\s+|\s+$//go;
172 =head2 C<tidy_type()>
178 Rationalize any asterisks (C<*>) by joining them into bunches, removing
179 interior whitespace, then trimming leading and trailing whitespace.
183 ($ret_type) = tidy_type($_);
185 String to be cleaned up.
198 # rationalise any '*' by joining them into bunches and removing whitespace
202 # change multiple whitespace into a single space
205 # trim leading & trailing whitespace
217 Escape backslashes (C<\>) in prototype strings.
221 $ProtoThisXSUB = C_string($_);
223 String needing escaping.
227 Properly escaped string.
236 $string =~ s[\\][\\\\]g;
240 =head2 C<valid_proto_string()>
246 Validate prototype string.
250 String needing checking.
254 Upon success, returns the same string passed as argument.
256 Upon failure, returns C<0>.
262 sub valid_proto_string {
265 if ( $string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/ ) {
272 =head2 C<process_typemaps()>
278 Process all typemap files.
282 my $typemaps_object = process_typemaps( $args{typemap}, $pwd );
284 List of two elements: C<typemap> element from C<%args>; current working
289 Upon success, returns an L<ExtUtils::Typemaps> object.
295 sub process_typemaps {
296 my ($tmap, $pwd) = @_;
298 my @tm = ref $tmap ? @{$tmap} : ($tmap);
300 foreach my $typemap (@tm) {
301 die "Can't find $typemap in $pwd\n" unless -r $typemap;
304 push @tm, standard_typemap_locations( \@INC );
306 my $typemap = ExtUtils::Typemaps->new;
307 foreach my $typemap_loc (@tm) {
308 next unless -f $typemap_loc;
309 # skip directories, binary files etc.
310 warn("Warning: ignoring non-text typemap file '$typemap_loc'\n"), next
311 unless -T $typemap_loc;
313 $typemap->merge(file => $typemap_loc, replace => 1);
319 =head2 C<make_targetable()>
325 Populate C<%targetable>. This constitutes a refinement of the output of
326 C<process_typemaps()> with respect to its fourth output, C<$output_expr_ref>.
330 %targetable = make_targetable($output_expr_ref);
332 Single hash reference: the fourth such ref returned by C<process_typemaps()>.
342 sub make_targetable {
343 my $output_expr_ref = shift;
345 our $bal; # ()-balanced
354 # matches variations on (SV*)
357 \( \s* SV \s* \* \s* \) \s*
361 my $size = qr[ # Third arg (to setpvn)
366 foreach my $key (keys %{ $output_expr_ref }) {
367 # We can still bootstrap compile 're', because in code re.pm is
368 # available to miniperl, and does not attempt to load the XS code.
371 my ($type, $with_size, $arg, $sarg) =
372 ($output_expr_ref->{$key} =~
375 sv_set([iunp])v(n)? # Type, is_setpvn
378 $sv_cast \$arg \s* , \s*
379 ( (??{ $bal }) ) # Set from
380 ( (??{ $size }) )? # Possible sizeof set-from
384 $targetable{$key} = [$type, $with_size, $arg, $sarg] if $type;
395 Performs a mapping at several places inside C<PARAGRAPH> loop.
399 $type = map_type($self, $type, $varname);
401 List of three arguments.
405 String holding augmented version of second argument.
412 my ($self, $type, $varname) = @_;
414 # C++ has :: in types too so skip this
415 $type =~ tr/:/_/ unless $self->{hiertype};
416 $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s;
418 if ($type =~ / \( \s* \* (?= \s* \) ) /xg) {
419 (substr $type, pos $type, 0) = " $varname ";
422 $type .= "\t$varname";
428 =head2 C<standard_XS_defs()>
434 Writes to the C<.c> output file certain preprocessor directives and function
435 headers needed in all such files.
449 sub standard_XS_defs {
451 #ifndef PERL_UNUSED_VAR
452 # define PERL_UNUSED_VAR(var) if (0) var = var
458 #ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
459 #define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)
461 /* prototype to pass -Wmissing-prototypes */
463 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params);
466 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params)
468 const GV *const gv = CvGV(cv);
470 PERL_ARGS_ASSERT_CROAK_XS_USAGE;
473 const char *const gvname = GvNAME(gv);
474 const HV *const stash = GvSTASH(gv);
475 const char *const hvname = stash ? HvNAME(stash) : NULL;
478 Perl_croak(aTHX_ "Usage: %s::%s(%s)", hvname, gvname, params);
480 Perl_croak(aTHX_ "Usage: %s(%s)", gvname, params);
482 /* Pants. I don't think that it should be possible to get here. */
483 Perl_croak(aTHX_ "Usage: CODE(0x%"UVxf")(%s)", PTR2UV(cv), params);
486 #undef PERL_ARGS_ASSERT_CROAK_XS_USAGE
488 #ifdef PERL_IMPLICIT_CONTEXT
489 #define croak_xs_usage(a,b) S_croak_xs_usage(aTHX_ a,b)
491 #define croak_xs_usage S_croak_xs_usage
496 /* NOTE: the prototype of newXSproto() is different in versions of perls,
497 * so we define a portable version of newXSproto()
500 #define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
502 #define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
503 #endif /* !defined(newXS_flags) */
509 =head2 C<assign_func_args()>
515 Perform assignment to the C<func_args> attribute.
519 $string = assign_func_args($self, $argsref, $class);
521 List of three elements. Second is an array reference; third is a string.
531 sub assign_func_args {
532 my ($self, $argsref, $class) = @_;
533 my @func_args = @{$argsref};
534 shift @func_args if defined($class);
536 for my $arg (@func_args) {
537 $arg =~ s/^/&/ if $self->{in_out}->{$arg};
539 return join(", ", @func_args);
542 =head2 C<analyze_preprocessor_statements()>
548 Within each function inside each Xsub, print to the F<.c> output file certain
549 preprocessor statements.
553 ( $self, $XSS_work_idx, $BootCode_ref ) =
554 analyze_preprocessor_statements(
555 $self, $statement, $XSS_work_idx, $BootCode_ref
558 List of four elements.
562 Modifed values of three of the arguments passed to the function. In
563 particular, the C<XSStack> and C<InitFileCode> attributes are modified.
569 sub analyze_preprocessor_statements {
570 my ($self, $statement, $XSS_work_idx, $BootCode_ref) = @_;
572 if ($statement eq 'if') {
573 $XSS_work_idx = @{ $self->{XSStack} };
574 push(@{ $self->{XSStack} }, {type => 'if'});
577 death ("Error: `$statement' with no matching `if'")
578 if $self->{XSStack}->[-1]{type} ne 'if';
579 if ($self->{XSStack}->[-1]{varname}) {
580 push(@{ $self->{InitFileCode} }, "#endif\n");
581 push(@{ $BootCode_ref }, "#endif");
584 my(@fns) = keys %{$self->{XSStack}->[-1]{functions}};
585 if ($statement ne 'endif') {
586 # Hide the functions defined in other #if branches, and reset.
587 @{$self->{XSStack}->[-1]{other_functions}}{@fns} = (1) x @fns;
588 @{$self->{XSStack}->[-1]}{qw(varname functions)} = ('', {});
591 my($tmp) = pop(@{ $self->{XSStack} });
592 0 while (--$XSS_work_idx
593 && $self->{XSStack}->[$XSS_work_idx]{type} ne 'if');
594 # Keep all new defined functions
595 push(@fns, keys %{$tmp->{other_functions}});
596 @{$self->{XSStack}->[$XSS_work_idx]{functions}}{@fns} = (1) x @fns;
599 return ($self, $XSS_work_idx, $BootCode_ref);
617 my ($ellipsis, $min_args, $num_args) = @_;
620 $cond = ($min_args ? qq(items < $min_args) : 0);
622 elsif ($min_args == $num_args) {
623 $cond = qq(items != $min_args);
626 $cond = qq(items < $min_args || items > $num_args);
647 # work out the line number
648 my $warn_line_number = $self->{line_no}->[@{ $self->{line_no} } - @{ $self->{line} } -1];
650 print STDERR "@_ in $self->{filename}, line $warn_line_number\n";
693 =head2 C<check_conditional_preprocessor_statements()>
707 sub check_conditional_preprocessor_statements {
709 my @cpp = grep(/^\#\s*(?:if|e\w+)/, @{ $self->{line} });
713 if ($cpp =~ /^\#\s*if/) {
717 Warn( $self, "Warning: #else/elif/endif without #if in this function");
718 print STDERR " (precede it with a blank line if the matching #if is outside the function)\n"
719 if $self->{XSStack}->[-1]{type} eq 'if';
722 elsif ($cpp =~ /^\#\s*endif/) {
726 Warn( $self, "Warning: #if without #endif in this function") if $cpplevel;