1 package ExtUtils::ParseXS::Utilities;
7 use ExtUtils::ParseXS::Constants ();
9 our (@ISA, @EXPORT_OK);
12 standard_typemap_locations
22 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 require ExtUtils::Typemaps;
307 my $typemap = ExtUtils::Typemaps->new;
308 foreach my $typemap_loc (@tm) {
309 next unless -f $typemap_loc;
310 # skip directories, binary files etc.
311 warn("Warning: ignoring non-text typemap file '$typemap_loc'\n"), next
312 unless -T $typemap_loc;
314 $typemap->merge(file => $typemap_loc, replace => 1);
320 =head2 C<make_targetable()>
326 Populate C<%targetable>. This constitutes a refinement of the output of
327 C<process_typemaps()> with respect to its fourth output, C<$output_expr_ref>.
331 %targetable = make_targetable($output_expr_ref);
333 Single hash reference: the fourth such ref returned by C<process_typemaps()>.
343 sub make_targetable {
344 my $output_expr_ref = shift;
346 our $bal; # ()-balanced
355 # matches variations on (SV*)
358 \( \s* SV \s* \* \s* \) \s*
362 my $size = qr[ # Third arg (to setpvn)
367 foreach my $key (keys %{ $output_expr_ref }) {
368 # We can still bootstrap compile 're', because in code re.pm is
369 # available to miniperl, and does not attempt to load the XS code.
372 my ($type, $with_size, $arg, $sarg) =
373 ($output_expr_ref->{$key} =~
376 sv_set([iunp])v(n)? # Type, is_setpvn
379 $sv_cast \$arg \s* , \s*
380 ( (??{ $bal }) ) # Set from
381 ( (??{ $size }) )? # Possible sizeof set-from
385 $targetable{$key} = [$type, $with_size, $arg, $sarg] if $type;
396 Performs a mapping at several places inside C<PARAGRAPH> loop.
400 $type = map_type($self, $type, $varname);
402 List of three arguments.
406 String holding augmented version of second argument.
413 my ($self, $type, $varname) = @_;
415 # C++ has :: in types too so skip this
416 $type =~ tr/:/_/ unless $self->{hiertype};
417 $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s;
419 if ($type =~ / \( \s* \* (?= \s* \) ) /xg) {
420 (substr $type, pos $type, 0) = " $varname ";
423 $type .= "\t$varname";
429 =head2 C<standard_XS_defs()>
435 Writes to the C<.c> output file certain preprocessor directives and function
436 headers needed in all such files.
450 sub standard_XS_defs {
452 #ifndef PERL_UNUSED_VAR
453 # define PERL_UNUSED_VAR(var) if (0) var = var
459 #ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
460 #define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)
462 /* prototype to pass -Wmissing-prototypes */
464 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params);
467 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params)
469 const GV *const gv = CvGV(cv);
471 PERL_ARGS_ASSERT_CROAK_XS_USAGE;
474 const char *const gvname = GvNAME(gv);
475 const HV *const stash = GvSTASH(gv);
476 const char *const hvname = stash ? HvNAME(stash) : NULL;
479 Perl_croak(aTHX_ "Usage: %s::%s(%s)", hvname, gvname, params);
481 Perl_croak(aTHX_ "Usage: %s(%s)", gvname, params);
483 /* Pants. I don't think that it should be possible to get here. */
484 Perl_croak(aTHX_ "Usage: CODE(0x%"UVxf")(%s)", PTR2UV(cv), params);
487 #undef PERL_ARGS_ASSERT_CROAK_XS_USAGE
489 #ifdef PERL_IMPLICIT_CONTEXT
490 #define croak_xs_usage(a,b) S_croak_xs_usage(aTHX_ a,b)
492 #define croak_xs_usage S_croak_xs_usage
497 /* NOTE: the prototype of newXSproto() is different in versions of perls,
498 * so we define a portable version of newXSproto()
501 #define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
503 #define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
504 #endif /* !defined(newXS_flags) */
510 =head2 C<assign_func_args()>
516 Perform assignment to the C<func_args> attribute.
520 $string = assign_func_args($self, $argsref, $class);
522 List of three elements. Second is an array reference; third is a string.
532 sub assign_func_args {
533 my ($self, $argsref, $class) = @_;
534 my @func_args = @{$argsref};
535 shift @func_args if defined($class);
537 for my $arg (@func_args) {
538 $arg =~ s/^/&/ if $self->{in_out}->{$arg};
540 return join(", ", @func_args);
543 =head2 C<analyze_preprocessor_statements()>
549 Within each function inside each Xsub, print to the F<.c> output file certain
550 preprocessor statements.
554 ( $self, $XSS_work_idx, $BootCode_ref ) =
555 analyze_preprocessor_statements(
556 $self, $statement, $XSS_work_idx, $BootCode_ref
559 List of four elements.
563 Modifed values of three of the arguments passed to the function. In
564 particular, the C<XSStack> and C<InitFileCode> attributes are modified.
570 sub analyze_preprocessor_statements {
571 my ($self, $statement, $XSS_work_idx, $BootCode_ref) = @_;
573 if ($statement eq 'if') {
574 $XSS_work_idx = @{ $self->{XSStack} };
575 push(@{ $self->{XSStack} }, {type => 'if'});
578 $self->death("Error: `$statement' with no matching `if'")
579 if $self->{XSStack}->[-1]{type} ne 'if';
580 if ($self->{XSStack}->[-1]{varname}) {
581 push(@{ $self->{InitFileCode} }, "#endif\n");
582 push(@{ $BootCode_ref }, "#endif");
585 my(@fns) = keys %{$self->{XSStack}->[-1]{functions}};
586 if ($statement ne 'endif') {
587 # Hide the functions defined in other #if branches, and reset.
588 @{$self->{XSStack}->[-1]{other_functions}}{@fns} = (1) x @fns;
589 @{$self->{XSStack}->[-1]}{qw(varname functions)} = ('', {});
592 my($tmp) = pop(@{ $self->{XSStack} });
593 0 while (--$XSS_work_idx
594 && $self->{XSStack}->[$XSS_work_idx]{type} ne 'if');
595 # Keep all new defined functions
596 push(@fns, keys %{$tmp->{other_functions}});
597 @{$self->{XSStack}->[$XSS_work_idx]{functions}}{@fns} = (1) x @fns;
600 return ($self, $XSS_work_idx, $BootCode_ref);
618 my ($ellipsis, $min_args, $num_args) = @_;
621 $cond = ($min_args ? qq(items < $min_args) : 0);
623 elsif ($min_args == $num_args) {
624 $cond = qq(items != $min_args);
627 $cond = qq(items < $min_args || items > $num_args);
632 =head2 C<current_line_number()>
638 Figures out the current line number in the XS file.
646 The current line number.
652 sub current_line_number {
654 my $line_number = $self->{line_no}->[@{ $self->{line_no} } - @{ $self->{line} } -1];
674 my $warn_line_number = $self->current_line_number();
675 print STDERR "@_ in $self->{filename}, line $warn_line_number\n";
718 =head2 C<check_conditional_preprocessor_statements()>
732 sub check_conditional_preprocessor_statements {
734 my @cpp = grep(/^\#\s*(?:if|e\w+)/, @{ $self->{line} });
738 if ($cpp =~ /^\#\s*if/) {
742 $self->Warn("Warning: #else/elif/endif without #if in this function");
743 print STDERR " (precede it with a blank line if the matching #if is outside the function)\n"
744 if $self->{XSStack}->[-1]{type} eq 'if';
747 elsif ($cpp =~ /^\#\s*endif/) {
751 $self->Warn("Warning: #if without #endif in this function") if $cpplevel;