1 package ExtUtils::ParseXS::Utilities;
7 use ExtUtils::ParseXS::Constants ();
9 our $VERSION = '3.04_03';
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!
537 #if defined(PERL_EUPXS_ALWAYS_EXPORT)
538 # define XS_EUPXS(name) XS_EXTERNAL(name)
540 /* default to internal */
541 # define XS_EUPXS(name) XS_INTERNAL(name)
547 #ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
548 #define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)
550 /* prototype to pass -Wmissing-prototypes */
552 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params);
555 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params)
557 const GV *const gv = CvGV(cv);
559 PERL_ARGS_ASSERT_CROAK_XS_USAGE;
562 const char *const gvname = GvNAME(gv);
563 const HV *const stash = GvSTASH(gv);
564 const char *const hvname = stash ? HvNAME(stash) : NULL;
567 Perl_croak(aTHX_ "Usage: %s::%s(%s)", hvname, gvname, params);
569 Perl_croak(aTHX_ "Usage: %s(%s)", gvname, params);
571 /* Pants. I don't think that it should be possible to get here. */
572 Perl_croak(aTHX_ "Usage: CODE(0x%"UVxf")(%s)", PTR2UV(cv), params);
575 #undef PERL_ARGS_ASSERT_CROAK_XS_USAGE
577 #ifdef PERL_IMPLICIT_CONTEXT
578 #define croak_xs_usage(a,b) S_croak_xs_usage(aTHX_ a,b)
580 #define croak_xs_usage S_croak_xs_usage
585 /* NOTE: the prototype of newXSproto() is different in versions of perls,
586 * so we define a portable version of newXSproto()
589 #define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
591 #define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
592 #endif /* !defined(newXS_flags) */
598 =head2 C<assign_func_args()>
604 Perform assignment to the C<func_args> attribute.
608 $string = assign_func_args($self, $argsref, $class);
610 List of three elements. Second is an array reference; third is a string.
620 sub assign_func_args {
621 my ($self, $argsref, $class) = @_;
622 my @func_args = @{$argsref};
623 shift @func_args if defined($class);
625 for my $arg (@func_args) {
626 $arg =~ s/^/&/ if $self->{in_out}->{$arg};
628 return join(", ", @func_args);
631 =head2 C<analyze_preprocessor_statements()>
637 Within each function inside each Xsub, print to the F<.c> output file certain
638 preprocessor statements.
642 ( $self, $XSS_work_idx, $BootCode_ref ) =
643 analyze_preprocessor_statements(
644 $self, $statement, $XSS_work_idx, $BootCode_ref
647 List of four elements.
651 Modifed values of three of the arguments passed to the function. In
652 particular, the C<XSStack> and C<InitFileCode> attributes are modified.
658 sub analyze_preprocessor_statements {
659 my ($self, $statement, $XSS_work_idx, $BootCode_ref) = @_;
661 if ($statement eq 'if') {
662 $XSS_work_idx = @{ $self->{XSStack} };
663 push(@{ $self->{XSStack} }, {type => 'if'});
666 $self->death("Error: `$statement' with no matching `if'")
667 if $self->{XSStack}->[-1]{type} ne 'if';
668 if ($self->{XSStack}->[-1]{varname}) {
669 push(@{ $self->{InitFileCode} }, "#endif\n");
670 push(@{ $BootCode_ref }, "#endif");
673 my(@fns) = keys %{$self->{XSStack}->[-1]{functions}};
674 if ($statement ne 'endif') {
675 # Hide the functions defined in other #if branches, and reset.
676 @{$self->{XSStack}->[-1]{other_functions}}{@fns} = (1) x @fns;
677 @{$self->{XSStack}->[-1]}{qw(varname functions)} = ('', {});
680 my($tmp) = pop(@{ $self->{XSStack} });
681 0 while (--$XSS_work_idx
682 && $self->{XSStack}->[$XSS_work_idx]{type} ne 'if');
683 # Keep all new defined functions
684 push(@fns, keys %{$tmp->{other_functions}});
685 @{$self->{XSStack}->[$XSS_work_idx]{functions}}{@fns} = (1) x @fns;
688 return ($self, $XSS_work_idx, $BootCode_ref);
706 my ($ellipsis, $min_args, $num_args) = @_;
709 $cond = ($min_args ? qq(items < $min_args) : 0);
711 elsif ($min_args == $num_args) {
712 $cond = qq(items != $min_args);
715 $cond = qq(items < $min_args || items > $num_args);
720 =head2 C<current_line_number()>
726 Figures out the current line number in the XS file.
734 The current line number.
740 sub current_line_number {
742 my $line_number = $self->{line_no}->[@{ $self->{line_no} } - @{ $self->{line} } -1];
762 my $warn_line_number = $self->current_line_number();
763 print STDERR "@_ in $self->{filename}, line $warn_line_number\n";
806 =head2 C<check_conditional_preprocessor_statements()>
820 sub check_conditional_preprocessor_statements {
822 my @cpp = grep(/^\#\s*(?:if|e\w+)/, @{ $self->{line} });
826 if ($cpp =~ /^\#\s*if/) {
830 $self->Warn("Warning: #else/elif/endif without #if in this function");
831 print STDERR " (precede it with a blank line if the matching #if is outside the function)\n"
832 if $self->{XSStack}->[-1]{type} eq 'if';
835 elsif ($cpp =~ /^\#\s*endif/) {
839 $self->Warn("Warning: #if without #endif in this function") if $cpplevel;