1 package ExtUtils::ParseXS::Utilities;
7 use ExtUtils::ParseXS::Constants ();
9 our $VERSION = '3.04_01';
11 our (@ISA, @EXPORT_OK);
14 standard_typemap_locations
24 analyze_preprocessor_statements
30 check_conditional_preprocessor_statements
35 ExtUtils::ParseXS::Utilities - Subroutines used with ExtUtils::ParseXS
39 use ExtUtils::ParseXS::Utilities qw(
40 standard_typemap_locations
50 analyze_preprocessor_statements
55 check_conditional_preprocessor_statements
60 The following functions are not considered to be part of the public interface.
61 They are documented here for the benefit of future maintainers of this module.
63 =head2 C<standard_typemap_locations()>
69 Provide a list of filepaths where F<typemap> files may be found. The
70 filepaths -- relative paths to files (not just directory paths) -- appear in this list in lowest-to-highest priority.
72 The highest priority is to look in the current directory.
76 The second and third highest priorities are to look in the parent of the
77 current directory and a directory called F<lib/ExtUtils> underneath the parent
81 '../lib/ExtUtils/typemap',
83 The fourth through ninth highest priorities are to look in the corresponding
84 grandparent, great-grandparent and great-great-grandparent directories.
87 '../../lib/ExtUtils/typemap',
89 '../../../lib/ExtUtils/typemap',
90 '../../../../typemap',
91 '../../../../lib/ExtUtils/typemap',
93 The tenth and subsequent priorities are to look in directories named
94 F<ExtUtils> which are subdirectories of directories found in C<@INC> --
95 I<provided> a file named F<typemap> actually exists in such a directory.
98 '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap',
100 However, these filepaths appear in the list returned by
101 C<standard_typemap_locations()> in reverse order, I<i.e.>, lowest-to-highest.
103 '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap',
104 '../../../../lib/ExtUtils/typemap',
105 '../../../../typemap',
106 '../../../lib/ExtUtils/typemap',
108 '../../lib/ExtUtils/typemap',
110 '../lib/ExtUtils/typemap',
116 my @stl = standard_typemap_locations( \@INC );
118 Reference to C<@INC>.
122 Array holding list of directories to be searched for F<typemap> files.
128 sub standard_typemap_locations {
129 my $include_ref = shift;
130 my @tm = qw(typemap);
132 my $updir = File::Spec->updir();
134 File::Spec->catdir(($updir) x 1),
135 File::Spec->catdir(($updir) x 2),
136 File::Spec->catdir(($updir) x 3),
137 File::Spec->catdir(($updir) x 4),
139 unshift @tm, File::Spec->catfile($dir, 'typemap');
140 unshift @tm, File::Spec->catfile($dir, lib => ExtUtils => 'typemap');
142 foreach my $dir (@{ $include_ref}) {
143 my $file = File::Spec->catfile($dir, ExtUtils => 'typemap');
144 unshift @tm, $file if -e $file;
149 =head2 C<trim_whitespace()>
155 Perform an in-place trimming of leading and trailing whitespace from the
156 first argument provided to the function.
160 trim_whitespace($arg);
164 None. Remember: this is an I<in-place> modification of the argument.
170 sub trim_whitespace {
171 $_[0] =~ s/^\s+|\s+$//go;
174 =head2 C<tidy_type()>
180 Rationalize any asterisks (C<*>) by joining them into bunches, removing
181 interior whitespace, then trimming leading and trailing whitespace.
185 ($ret_type) = tidy_type($_);
187 String to be cleaned up.
200 # rationalise any '*' by joining them into bunches and removing whitespace
204 # change multiple whitespace into a single space
207 # trim leading & trailing whitespace
219 Escape backslashes (C<\>) in prototype strings.
223 $ProtoThisXSUB = C_string($_);
225 String needing escaping.
229 Properly escaped string.
238 $string =~ s[\\][\\\\]g;
242 =head2 C<valid_proto_string()>
248 Validate prototype string.
252 String needing checking.
256 Upon success, returns the same string passed as argument.
258 Upon failure, returns C<0>.
264 sub valid_proto_string {
267 if ( $string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/ ) {
274 =head2 C<process_typemaps()>
280 Process all typemap files.
284 my $typemaps_object = process_typemaps( $args{typemap}, $pwd );
286 List of two elements: C<typemap> element from C<%args>; current working
291 Upon success, returns an L<ExtUtils::Typemaps> object.
297 sub process_typemaps {
298 my ($tmap, $pwd) = @_;
300 my @tm = ref $tmap ? @{$tmap} : ($tmap);
302 foreach my $typemap (@tm) {
303 die "Can't find $typemap in $pwd\n" unless -r $typemap;
306 push @tm, standard_typemap_locations( \@INC );
308 require ExtUtils::Typemaps;
309 my $typemap = ExtUtils::Typemaps->new;
310 foreach my $typemap_loc (@tm) {
311 next unless -f $typemap_loc;
312 # skip directories, binary files etc.
313 warn("Warning: ignoring non-text typemap file '$typemap_loc'\n"), next
314 unless -T $typemap_loc;
316 $typemap->merge(file => $typemap_loc, replace => 1);
322 =head2 C<make_targetable()>
328 Populate C<%targetable>. This constitutes a refinement of the output of
329 C<process_typemaps()> with respect to its fourth output, C<$output_expr_ref>.
333 %targetable = make_targetable($output_expr_ref);
335 Single hash reference: the fourth such ref returned by C<process_typemaps()>.
345 sub make_targetable {
346 my $output_expr_ref = shift;
348 our $bal; # ()-balanced
357 # matches variations on (SV*)
360 \( \s* SV \s* \* \s* \) \s*
364 my $size = qr[ # Third arg (to setpvn)
369 foreach my $key (keys %{ $output_expr_ref }) {
370 # We can still bootstrap compile 're', because in code re.pm is
371 # available to miniperl, and does not attempt to load the XS code.
374 my ($type, $with_size, $arg, $sarg) =
375 ($output_expr_ref->{$key} =~
378 sv_set([iunp])v(n)? # Type, is_setpvn
381 $sv_cast \$arg \s* , \s*
382 ( (??{ $bal }) ) # Set from
383 ( (??{ $size }) )? # Possible sizeof set-from
387 $targetable{$key} = [$type, $with_size, $arg, $sarg] if $type;
398 Performs a mapping at several places inside C<PARAGRAPH> loop.
402 $type = map_type($self, $type, $varname);
404 List of three arguments.
408 String holding augmented version of second argument.
415 my ($self, $type, $varname) = @_;
417 # C++ has :: in types too so skip this
418 $type =~ tr/:/_/ unless $self->{hiertype};
419 $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s;
421 if ($type =~ / \( \s* \* (?= \s* \) ) /xg) {
422 (substr $type, pos $type, 0) = " $varname ";
425 $type .= "\t$varname";
431 =head2 C<standard_XS_defs()>
437 Writes to the C<.c> output file certain preprocessor directives and function
438 headers needed in all such files.
452 sub standard_XS_defs {
454 #ifndef PERL_UNUSED_VAR
455 # define PERL_UNUSED_VAR(var) if (0) var = var
459 /* This stuff is not part of the API! You have been warned. */
460 #ifndef PERL_VERSION_DECIMAL
461 # define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
463 #ifndef PERL_DECIMAL_VERSION
464 # define PERL_DECIMAL_VERSION \\
465 PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
467 #ifndef PERL_VERSION_GE
468 # define PERL_VERSION_GE(r,v,s) \\
469 (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
471 #ifndef PERL_VERSION_LE
472 # define PERL_VERSION_LE(r,v,s) \\
473 (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
476 /* XS_INTERNAL is the explicit static-linkage variant of the default
479 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
480 * "STATIC", ie. it exports XSUB symbols. You probably don't want that
481 * for anything but the BOOT XSUB.
483 * See XSUB.h in core!
487 /* TODO: This might be compatible further back than 5.10.0. */
488 #if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
491 # if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
492 # define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
493 # define XS_INTERNAL(name) __declspec(dllexport) STATIC XSPROTO(name)
495 # if defined(__SYMBIAN32__)
496 # define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
497 # define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
500 # if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
501 # define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
502 # define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
505 # define XS_EXTERNAL(name) extern "C" XSPROTO(name)
506 # define XS_INTERNAL(name) static XSPROTO(name)
508 # define XS_EXTERNAL(name) XSPROTO(name)
509 # define XS_INTERNAL(name) STATIC XSPROTO(name)
515 /* perl >= 5.10.0 && perl <= 5.15.1 */
518 /* The XS_EXTERNAL macro is used for functions that must not be static
519 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
520 * macro defined, the best we can do is assume XS is the same.
521 * Dito for XS_INTERNAL.
524 # define XS_EXTERNAL(name) XS(name)
527 # define XS_INTERNAL(name) XS(name)
530 /* Now, finally, after all this mess, we want an ExtUtils::ParseXS
531 * internal macro that we're free to redefine for varying linkage due
532 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
533 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
536 /* default to internal */
538 #define XS_EUPXS(name) XS_INTERNAL(name)
543 #ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
544 #define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)
546 /* prototype to pass -Wmissing-prototypes */
548 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params);
551 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params)
553 const GV *const gv = CvGV(cv);
555 PERL_ARGS_ASSERT_CROAK_XS_USAGE;
558 const char *const gvname = GvNAME(gv);
559 const HV *const stash = GvSTASH(gv);
560 const char *const hvname = stash ? HvNAME(stash) : NULL;
563 Perl_croak(aTHX_ "Usage: %s::%s(%s)", hvname, gvname, params);
565 Perl_croak(aTHX_ "Usage: %s(%s)", gvname, params);
567 /* Pants. I don't think that it should be possible to get here. */
568 Perl_croak(aTHX_ "Usage: CODE(0x%"UVxf")(%s)", PTR2UV(cv), params);
571 #undef PERL_ARGS_ASSERT_CROAK_XS_USAGE
573 #ifdef PERL_IMPLICIT_CONTEXT
574 #define croak_xs_usage(a,b) S_croak_xs_usage(aTHX_ a,b)
576 #define croak_xs_usage S_croak_xs_usage
581 /* NOTE: the prototype of newXSproto() is different in versions of perls,
582 * so we define a portable version of newXSproto()
585 #define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
587 #define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
588 #endif /* !defined(newXS_flags) */
594 =head2 C<assign_func_args()>
600 Perform assignment to the C<func_args> attribute.
604 $string = assign_func_args($self, $argsref, $class);
606 List of three elements. Second is an array reference; third is a string.
616 sub assign_func_args {
617 my ($self, $argsref, $class) = @_;
618 my @func_args = @{$argsref};
619 shift @func_args if defined($class);
621 for my $arg (@func_args) {
622 $arg =~ s/^/&/ if $self->{in_out}->{$arg};
624 return join(", ", @func_args);
627 =head2 C<analyze_preprocessor_statements()>
633 Within each function inside each Xsub, print to the F<.c> output file certain
634 preprocessor statements.
638 ( $self, $XSS_work_idx, $BootCode_ref ) =
639 analyze_preprocessor_statements(
640 $self, $statement, $XSS_work_idx, $BootCode_ref
643 List of four elements.
647 Modifed values of three of the arguments passed to the function. In
648 particular, the C<XSStack> and C<InitFileCode> attributes are modified.
654 sub analyze_preprocessor_statements {
655 my ($self, $statement, $XSS_work_idx, $BootCode_ref) = @_;
657 if ($statement eq 'if') {
658 $XSS_work_idx = @{ $self->{XSStack} };
659 push(@{ $self->{XSStack} }, {type => 'if'});
662 $self->death("Error: `$statement' with no matching `if'")
663 if $self->{XSStack}->[-1]{type} ne 'if';
664 if ($self->{XSStack}->[-1]{varname}) {
665 push(@{ $self->{InitFileCode} }, "#endif\n");
666 push(@{ $BootCode_ref }, "#endif");
669 my(@fns) = keys %{$self->{XSStack}->[-1]{functions}};
670 if ($statement ne 'endif') {
671 # Hide the functions defined in other #if branches, and reset.
672 @{$self->{XSStack}->[-1]{other_functions}}{@fns} = (1) x @fns;
673 @{$self->{XSStack}->[-1]}{qw(varname functions)} = ('', {});
676 my($tmp) = pop(@{ $self->{XSStack} });
677 0 while (--$XSS_work_idx
678 && $self->{XSStack}->[$XSS_work_idx]{type} ne 'if');
679 # Keep all new defined functions
680 push(@fns, keys %{$tmp->{other_functions}});
681 @{$self->{XSStack}->[$XSS_work_idx]{functions}}{@fns} = (1) x @fns;
684 return ($self, $XSS_work_idx, $BootCode_ref);
702 my ($ellipsis, $min_args, $num_args) = @_;
705 $cond = ($min_args ? qq(items < $min_args) : 0);
707 elsif ($min_args == $num_args) {
708 $cond = qq(items != $min_args);
711 $cond = qq(items < $min_args || items > $num_args);
716 =head2 C<current_line_number()>
722 Figures out the current line number in the XS file.
730 The current line number.
736 sub current_line_number {
738 my $line_number = $self->{line_no}->[@{ $self->{line_no} } - @{ $self->{line} } -1];
758 my $warn_line_number = $self->current_line_number();
759 print STDERR "@_ in $self->{filename}, line $warn_line_number\n";
802 =head2 C<check_conditional_preprocessor_statements()>
816 sub check_conditional_preprocessor_statements {
818 my @cpp = grep(/^\#\s*(?:if|e\w+)/, @{ $self->{line} });
822 if ($cpp =~ /^\#\s*if/) {
826 $self->Warn("Warning: #else/elif/endif without #if in this function");
827 print STDERR " (precede it with a blank line if the matching #if is outside the function)\n"
828 if $self->{XSStack}->[-1]{type} eq 'if';
831 elsif ($cpp =~ /^\#\s*endif/) {
835 $self->Warn("Warning: #if without #endif in this function") if $cpplevel;