1 package ExtUtils::ParseXS::Utilities;
7 use ExtUtils::ParseXS::Constants ();
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
463 /* This stuff is not part of the API! You have been warned. */
464 #ifndef PERL_VERSION_DECIMAL
465 # define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
467 #ifndef PERL_DECIMAL_VERSION
468 # define PERL_DECIMAL_VERSION \\
469 PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
471 #ifndef PERL_VERSION_GE
472 # define PERL_VERSION_GE(r,v,s) \\
473 (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
475 #ifndef PERL_VERSION_LE
476 # define PERL_VERSION_LE(r,v,s) \\
477 (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
480 /* XS_INTERNAL is the explicit static-linkage variant of the default
483 * XS_EXTERNAL is the same as XS_INTERNAL except it does not include
484 * "STATIC", ie. it exports XSUB symbols. You probably don't want that
485 * for anything but the BOOT XSUB.
487 * See XSUB.h in core!
491 /* TODO: This might be compatible further back than 5.10.0. */
492 #if PERL_VERSION_GE(5, 10, 0) && PERL_VERSION_LE(5, 15, 1)
495 # if defined(__CYGWIN__) && defined(USE_DYNAMIC_LOADING)
496 # define XS_EXTERNAL(name) __declspec(dllexport) XSPROTO(name)
497 # define XS_INTERNAL(name) __declspec(dllexport) STATIC XSPROTO(name)
499 # if defined(__SYMBIAN32__)
500 # define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
501 # define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
504 # if defined(HASATTRIBUTE_UNUSED) && !defined(__cplusplus)
505 # define XS_EXTERNAL(name) void name(pTHX_ CV* cv __attribute__unused__)
506 # define XS_INTERNAL(name) STATIC void name(pTHX_ CV* cv __attribute__unused__)
509 # define XS_EXTERNAL(name) extern "C" XSPROTO(name)
510 # define XS_INTERNAL(name) static XSPROTO(name)
512 # define XS_EXTERNAL(name) XSPROTO(name)
513 # define XS_INTERNAL(name) STATIC XSPROTO(name)
519 /* perl >= 5.10.0 && perl <= 5.15.1 */
522 /* The XS_EXTERNAL macro is used for functions that must not be static
523 * like the boot XSUB of a module. If perl didn't have an XS_EXTERNAL
524 * macro defined, the best we can do is assume XS is the same.
525 * Dito for XS_INTERNAL.
528 # define XS_EXTERNAL(name) XS(name)
531 # define XS_INTERNAL(name) XS(name)
534 /* Now, finally, after all this mess, we want an ExtUtils::ParseXS
535 * internal macro that we're free to redefine for varying linkage due
536 * to the EXPORT_XSUB_SYMBOLS XS keyword. This is internal, use
537 * XS_EXTERNAL(name) or XS_INTERNAL(name) in your code if you need to!
541 #if defined(PERL_EUPXS_ALWAYS_EXPORT)
542 # define XS_EUPXS(name) XS_EXTERNAL(name)
544 /* default to internal */
545 # define XS_EUPXS(name) XS_INTERNAL(name)
551 #ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
552 #define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)
554 /* prototype to pass -Wmissing-prototypes */
556 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params);
559 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params)
561 const GV *const gv = CvGV(cv);
563 PERL_ARGS_ASSERT_CROAK_XS_USAGE;
566 const char *const gvname = GvNAME(gv);
567 const HV *const stash = GvSTASH(gv);
568 const char *const hvname = stash ? HvNAME(stash) : NULL;
571 Perl_croak(aTHX_ "Usage: %s::%s(%s)", hvname, gvname, params);
573 Perl_croak(aTHX_ "Usage: %s(%s)", gvname, params);
575 /* Pants. I don't think that it should be possible to get here. */
576 Perl_croak(aTHX_ "Usage: CODE(0x%"UVxf")(%s)", PTR2UV(cv), params);
579 #undef PERL_ARGS_ASSERT_CROAK_XS_USAGE
581 #ifdef PERL_IMPLICIT_CONTEXT
582 #define croak_xs_usage(a,b) S_croak_xs_usage(aTHX_ a,b)
584 #define croak_xs_usage S_croak_xs_usage
589 /* NOTE: the prototype of newXSproto() is different in versions of perls,
590 * so we define a portable version of newXSproto()
593 #define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
595 #define newXSproto_portable(name, c_impl, file, proto) (PL_Sv=(SV*)newXS(name, c_impl, file), sv_setpv(PL_Sv, proto), (CV*)PL_Sv)
596 #endif /* !defined(newXS_flags) */
602 =head2 C<assign_func_args()>
608 Perform assignment to the C<func_args> attribute.
612 $string = assign_func_args($self, $argsref, $class);
614 List of three elements. Second is an array reference; third is a string.
624 sub assign_func_args {
625 my ($self, $argsref, $class) = @_;
626 my @func_args = @{$argsref};
627 shift @func_args if defined($class);
629 for my $arg (@func_args) {
630 $arg =~ s/^/&/ if $self->{in_out}->{$arg};
632 return join(", ", @func_args);
635 =head2 C<analyze_preprocessor_statements()>
641 Within each function inside each Xsub, print to the F<.c> output file certain
642 preprocessor statements.
646 ( $self, $XSS_work_idx, $BootCode_ref ) =
647 analyze_preprocessor_statements(
648 $self, $statement, $XSS_work_idx, $BootCode_ref
651 List of four elements.
655 Modifed values of three of the arguments passed to the function. In
656 particular, the C<XSStack> and C<InitFileCode> attributes are modified.
662 sub analyze_preprocessor_statements {
663 my ($self, $statement, $XSS_work_idx, $BootCode_ref) = @_;
665 if ($statement eq 'if') {
666 $XSS_work_idx = @{ $self->{XSStack} };
667 push(@{ $self->{XSStack} }, {type => 'if'});
670 $self->death("Error: '$statement' with no matching 'if'")
671 if $self->{XSStack}->[-1]{type} ne 'if';
672 if ($self->{XSStack}->[-1]{varname}) {
673 push(@{ $self->{InitFileCode} }, "#endif\n");
674 push(@{ $BootCode_ref }, "#endif");
677 my(@fns) = keys %{$self->{XSStack}->[-1]{functions}};
678 if ($statement ne 'endif') {
679 # Hide the functions defined in other #if branches, and reset.
680 @{$self->{XSStack}->[-1]{other_functions}}{@fns} = (1) x @fns;
681 @{$self->{XSStack}->[-1]}{qw(varname functions)} = ('', {});
684 my($tmp) = pop(@{ $self->{XSStack} });
685 0 while (--$XSS_work_idx
686 && $self->{XSStack}->[$XSS_work_idx]{type} ne 'if');
687 # Keep all new defined functions
688 push(@fns, keys %{$tmp->{other_functions}});
689 @{$self->{XSStack}->[$XSS_work_idx]{functions}}{@fns} = (1) x @fns;
692 return ($self, $XSS_work_idx, $BootCode_ref);
710 my ($ellipsis, $min_args, $num_args) = @_;
713 $cond = ($min_args ? qq(items < $min_args) : 0);
715 elsif ($min_args == $num_args) {
716 $cond = qq(items != $min_args);
719 $cond = qq(items < $min_args || items > $num_args);
724 =head2 C<current_line_number()>
730 Figures out the current line number in the XS file.
738 The current line number.
744 sub current_line_number {
746 my $line_number = $self->{line_no}->[@{ $self->{line_no} } - @{ $self->{line} } -1];
766 my $warn_line_number = $self->current_line_number();
767 print STDERR "@_ in $self->{filename}, line $warn_line_number\n";
810 =head2 C<check_conditional_preprocessor_statements()>
824 sub check_conditional_preprocessor_statements {
826 my @cpp = grep(/^\#\s*(?:if|e\w+)/, @{ $self->{line} });
830 if ($cpp =~ /^\#\s*if/) {
834 $self->Warn("Warning: #else/elif/endif without #if in this function");
835 print STDERR " (precede it with a blank line if the matching #if is outside the function)\n"
836 if $self->{XSStack}->[-1]{type} eq 'if';
839 elsif ($cpp =~ /^\#\s*endif/) {
843 $self->Warn("Warning: #if without #endif in this function") if $cpplevel;