This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
ExtUtils::ParseXS: Allow users to enforce linkage of XSUBs
[perl5.git] / dist / ExtUtils-ParseXS / lib / ExtUtils / ParseXS / Utilities.pm
1 package ExtUtils::ParseXS::Utilities;
2 use strict;
3 use warnings;
4 use Exporter;
5 use File::Spec;
6 use lib qw( lib );
7 use ExtUtils::ParseXS::Constants ();
8
9 our $VERSION = '3.04_03';
10
11 our (@ISA, @EXPORT_OK);
12 @ISA = qw(Exporter);
13 @EXPORT_OK = qw(
14   standard_typemap_locations
15   trim_whitespace
16   tidy_type
17   C_string
18   valid_proto_string
19   process_typemaps
20   make_targetable
21   map_type
22   standard_XS_defs
23   assign_func_args
24   analyze_preprocessor_statements
25   set_cond
26   Warn
27   current_line_number
28   blurt
29   death
30   check_conditional_preprocessor_statements
31 );
32
33 =head1 NAME
34
35 ExtUtils::ParseXS::Utilities - Subroutines used with ExtUtils::ParseXS
36
37 =head1 SYNOPSIS
38
39   use ExtUtils::ParseXS::Utilities qw(
40     standard_typemap_locations
41     trim_whitespace
42     tidy_type
43     C_string
44     valid_proto_string
45     process_typemaps
46     make_targetable
47     map_type
48     standard_XS_defs
49     assign_func_args
50     analyze_preprocessor_statements
51     set_cond
52     Warn
53     blurt
54     death
55     check_conditional_preprocessor_statements
56   );
57
58 =head1 SUBROUTINES
59
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.
62
63 =head2 C<standard_typemap_locations()>
64
65 =over 4
66
67 =item * Purpose
68
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.
71
72 The highest priority is to look in the current directory.  
73
74   'typemap'
75
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
78 directory.
79
80   '../typemap',
81   '../lib/ExtUtils/typemap',
82
83 The fourth through ninth highest priorities are to look in the corresponding
84 grandparent, great-grandparent and great-great-grandparent directories.
85
86   '../../typemap',
87   '../../lib/ExtUtils/typemap',
88   '../../../typemap',
89   '../../../lib/ExtUtils/typemap',
90   '../../../../typemap',
91   '../../../../lib/ExtUtils/typemap',
92
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.
96 Example:
97
98   '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap',
99
100 However, these filepaths appear in the list returned by
101 C<standard_typemap_locations()> in reverse order, I<i.e.>, lowest-to-highest.
102
103   '/usr/local/lib/perl5/5.10.1/ExtUtils/typemap',
104   '../../../../lib/ExtUtils/typemap',
105   '../../../../typemap',
106   '../../../lib/ExtUtils/typemap',
107   '../../../typemap',
108   '../../lib/ExtUtils/typemap',
109   '../../typemap',
110   '../lib/ExtUtils/typemap',
111   '../typemap',
112   'typemap'
113
114 =item * Arguments
115
116   my @stl = standard_typemap_locations( \@INC );
117
118 Reference to C<@INC>.
119
120 =item * Return Value
121
122 Array holding list of directories to be searched for F<typemap> files.
123
124 =back
125
126 =cut
127
128 sub standard_typemap_locations {
129   my $include_ref = shift;
130   my @tm = qw(typemap);
131
132   my $updir = File::Spec->updir();
133   foreach my $dir (
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),
138   ) {
139     unshift @tm, File::Spec->catfile($dir, 'typemap');
140     unshift @tm, File::Spec->catfile($dir, lib => ExtUtils => 'typemap');
141   }
142   foreach my $dir (@{ $include_ref}) {
143     my $file = File::Spec->catfile($dir, ExtUtils => 'typemap');
144     unshift @tm, $file if -e $file;
145   }
146   return @tm;
147 }
148
149 =head2 C<trim_whitespace()>
150
151 =over 4
152
153 =item * Purpose
154
155 Perform an in-place trimming of leading and trailing whitespace from the
156 first argument provided to the function.
157
158 =item * Argument
159
160   trim_whitespace($arg);
161
162 =item * Return Value
163
164 None.  Remember:  this is an I<in-place> modification of the argument.
165
166 =back
167
168 =cut
169
170 sub trim_whitespace {
171   $_[0] =~ s/^\s+|\s+$//go;
172 }
173
174 =head2 C<tidy_type()>
175
176 =over 4
177
178 =item * Purpose
179
180 Rationalize any asterisks (C<*>) by joining them into bunches, removing
181 interior whitespace, then trimming leading and trailing whitespace.
182
183 =item * Arguments
184
185     ($ret_type) = tidy_type($_);
186
187 String to be cleaned up.
188
189 =item * Return Value
190
191 String cleaned up.
192
193 =back
194
195 =cut
196
197 sub tidy_type {
198   local ($_) = @_;
199
200   # rationalise any '*' by joining them into bunches and removing whitespace
201   s#\s*(\*+)\s*#$1#g;
202   s#(\*+)# $1 #g;
203
204   # change multiple whitespace into a single space
205   s/\s+/ /g;
206
207   # trim leading & trailing whitespace
208   trim_whitespace($_);
209
210   $_;
211 }
212
213 =head2 C<C_string()>
214
215 =over 4
216
217 =item * Purpose
218
219 Escape backslashes (C<\>) in prototype strings.
220
221 =item * Arguments
222
223       $ProtoThisXSUB = C_string($_);
224
225 String needing escaping.
226
227 =item * Return Value
228
229 Properly escaped string.
230
231 =back
232
233 =cut
234
235 sub C_string {
236   my($string) = @_;
237
238   $string =~ s[\\][\\\\]g;
239   $string;
240 }
241
242 =head2 C<valid_proto_string()>
243
244 =over 4
245
246 =item * Purpose
247
248 Validate prototype string.
249
250 =item * Arguments
251
252 String needing checking.
253
254 =item * Return Value
255
256 Upon success, returns the same string passed as argument.
257
258 Upon failure, returns C<0>.
259
260 =back
261
262 =cut
263
264 sub valid_proto_string {
265   my($string) = @_;
266
267   if ( $string =~ /^$ExtUtils::ParseXS::Constants::PrototypeRegexp+$/ ) {
268     return $string;
269   }
270
271   return 0;
272 }
273
274 =head2 C<process_typemaps()>
275
276 =over 4
277
278 =item * Purpose
279
280 Process all typemap files.
281
282 =item * Arguments
283
284   my $typemaps_object = process_typemaps( $args{typemap}, $pwd );
285       
286 List of two elements:  C<typemap> element from C<%args>; current working
287 directory.
288
289 =item * Return Value
290
291 Upon success, returns an L<ExtUtils::Typemaps> object.
292
293 =back
294
295 =cut
296
297 sub process_typemaps {
298   my ($tmap, $pwd) = @_;
299
300   my @tm = ref $tmap ? @{$tmap} : ($tmap);
301
302   foreach my $typemap (@tm) {
303     die "Can't find $typemap in $pwd\n" unless -r $typemap;
304   }
305
306   push @tm, standard_typemap_locations( \@INC );
307
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;
315
316     $typemap->merge(file => $typemap_loc, replace => 1);
317   }
318
319   return $typemap;
320 }
321
322 =head2 C<make_targetable()>
323
324 =over 4
325
326 =item * Purpose
327
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>.
330
331 =item * Arguments
332
333   %targetable = make_targetable($output_expr_ref);
334       
335 Single hash reference:  the fourth such ref returned by C<process_typemaps()>.
336
337 =item * Return Value
338
339 Hash.
340
341 =back
342
343 =cut
344
345 sub make_targetable {
346   my $output_expr_ref = shift;
347
348   our $bal; # ()-balanced
349   $bal = qr[
350     (?:
351       (?>[^()]+)
352       |
353       \( (??{ $bal }) \)
354     )*
355   ]x;
356
357   # matches variations on (SV*)
358   my $sv_cast = qr[
359     (?:
360       \( \s* SV \s* \* \s* \) \s*
361     )?
362   ]x;
363
364   my $size = qr[ # Third arg (to setpvn)
365     , \s* (??{ $bal })
366   ]x;
367
368   my %targetable;
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.
372     use re 'eval';
373
374     my ($type, $with_size, $arg, $sarg) =
375       ($output_expr_ref->{$key} =~
376         m[^
377           \s+
378           sv_set([iunp])v(n)?    # Type, is_setpvn
379           \s*
380           \( \s*
381             $sv_cast \$arg \s* , \s*
382             ( (??{ $bal }) )    # Set from
383           ( (??{ $size }) )?    # Possible sizeof set-from
384           \) \s* ; \s* $
385         ]x
386     );
387     $targetable{$key} = [$type, $with_size, $arg, $sarg] if $type;
388   }
389   return %targetable;
390 }
391
392 =head2 C<map_type()>
393
394 =over 4
395
396 =item * Purpose
397
398 Performs a mapping at several places inside C<PARAGRAPH> loop.
399
400 =item * Arguments
401
402   $type = map_type($self, $type, $varname);
403
404 List of three arguments.
405
406 =item * Return Value
407
408 String holding augmented version of second argument.
409
410 =back
411
412 =cut
413
414 sub map_type {
415   my ($self, $type, $varname) = @_;
416
417   # C++ has :: in types too so skip this
418   $type =~ tr/:/_/ unless $self->{hiertype};
419   $type =~ s/^array\(([^,]*),(.*)\).*/$1 */s;
420   if ($varname) {
421     if ($type =~ / \( \s* \* (?= \s* \) ) /xg) {
422       (substr $type, pos $type, 0) = " $varname ";
423     }
424     else {
425       $type .= "\t$varname";
426     }
427   }
428   return $type;
429 }
430
431 =head2 C<standard_XS_defs()>
432
433 =over 4
434
435 =item * Purpose
436
437 Writes to the C<.c> output file certain preprocessor directives and function
438 headers needed in all such files.
439
440 =item * Arguments
441
442 None.
443
444 =item * Return Value
445
446 Returns true.
447
448 =back
449
450 =cut
451
452 sub standard_XS_defs {
453   print <<"EOF";
454 #ifndef PERL_UNUSED_VAR
455 #  define PERL_UNUSED_VAR(var) if (0) var = var
456 #endif
457
458
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)
462 #endif
463 #ifndef PERL_DECIMAL_VERSION
464 #  define PERL_DECIMAL_VERSION \\
465           PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
466 #endif
467 #ifndef PERL_VERSION_GE
468 #  define PERL_VERSION_GE(r,v,s) \\
469           (PERL_DECIMAL_VERSION >= PERL_VERSION_DECIMAL(r,v,s))
470 #endif
471 #ifndef PERL_VERSION_LE
472 #  define PERL_VERSION_LE(r,v,s) \\
473           (PERL_DECIMAL_VERSION <= PERL_VERSION_DECIMAL(r,v,s))
474 #endif
475
476 /* XS_INTERNAL is the explicit static-linkage variant of the default
477  * XS macro.
478  *
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.
482  *
483  * See XSUB.h in core!
484  */
485
486
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)
489 #  undef XS_EXTERNAL
490 #  undef XS_INTERNAL
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)
494 #  endif
495 #  if defined(__SYMBIAN32__)
496 #    define XS_EXTERNAL(name) EXPORT_C XSPROTO(name)
497 #    define XS_INTERNAL(name) EXPORT_C STATIC XSPROTO(name)
498 #  endif
499 #  ifndef XS_EXTERNAL
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__)
503 #    else
504 #      ifdef __cplusplus
505 #        define XS_EXTERNAL(name) extern "C" XSPROTO(name)
506 #        define XS_INTERNAL(name) static XSPROTO(name)
507 #      else
508 #        define XS_EXTERNAL(name) XSPROTO(name)
509 #        define XS_INTERNAL(name) STATIC XSPROTO(name)
510 #      endif
511 #    endif
512 #  endif
513 #endif
514
515 /* perl >= 5.10.0 && perl <= 5.15.1 */
516
517
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.
522  */
523 #ifndef XS_EXTERNAL
524 #  define XS_EXTERNAL(name) XS(name)
525 #endif
526 #ifndef XS_INTERNAL
527 #  define XS_INTERNAL(name) XS(name)
528 #endif
529
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!
534  */
535
536 #undef XS_EUPXS
537 #if defined(PERL_EUPXS_ALWAYS_EXPORT)
538 #  define XS_EUPXS(name) XS_EXTERNAL(name)
539 #else
540    /* default to internal */
541 #  define XS_EUPXS(name) XS_INTERNAL(name)
542 #endif
543
544 EOF
545
546   print <<"EOF";
547 #ifndef PERL_ARGS_ASSERT_CROAK_XS_USAGE
548 #define PERL_ARGS_ASSERT_CROAK_XS_USAGE assert(cv); assert(params)
549
550 /* prototype to pass -Wmissing-prototypes */
551 STATIC void
552 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params);
553
554 STATIC void
555 S_croak_xs_usage(pTHX_ const CV *const cv, const char *const params)
556 {
557     const GV *const gv = CvGV(cv);
558
559     PERL_ARGS_ASSERT_CROAK_XS_USAGE;
560
561     if (gv) {
562         const char *const gvname = GvNAME(gv);
563         const HV *const stash = GvSTASH(gv);
564         const char *const hvname = stash ? HvNAME(stash) : NULL;
565
566         if (hvname)
567             Perl_croak(aTHX_ "Usage: %s::%s(%s)", hvname, gvname, params);
568         else
569             Perl_croak(aTHX_ "Usage: %s(%s)", gvname, params);
570     } else {
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);
573     }
574 }
575 #undef  PERL_ARGS_ASSERT_CROAK_XS_USAGE
576
577 #ifdef PERL_IMPLICIT_CONTEXT
578 #define croak_xs_usage(a,b)    S_croak_xs_usage(aTHX_ a,b)
579 #else
580 #define croak_xs_usage        S_croak_xs_usage
581 #endif
582
583 #endif
584
585 /* NOTE: the prototype of newXSproto() is different in versions of perls,
586  * so we define a portable version of newXSproto()
587  */
588 #ifdef newXS_flags
589 #define newXSproto_portable(name, c_impl, file, proto) newXS_flags(name, c_impl, file, proto, 0)
590 #else
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) */
593
594 EOF
595   return 1;
596 }
597
598 =head2 C<assign_func_args()>
599
600 =over 4
601
602 =item * Purpose
603
604 Perform assignment to the C<func_args> attribute.
605
606 =item * Arguments
607
608   $string = assign_func_args($self, $argsref, $class);
609
610 List of three elements.  Second is an array reference; third is a string.
611
612 =item * Return Value
613
614 String.
615
616 =back
617
618 =cut
619
620 sub assign_func_args {
621   my ($self, $argsref, $class) = @_;
622   my @func_args = @{$argsref};
623   shift @func_args if defined($class);
624
625   for my $arg (@func_args) {
626     $arg =~ s/^/&/ if $self->{in_out}->{$arg};
627   }
628   return join(", ", @func_args);
629 }
630
631 =head2 C<analyze_preprocessor_statements()>
632
633 =over 4
634
635 =item * Purpose
636
637 Within each function inside each Xsub, print to the F<.c> output file certain
638 preprocessor statements.
639
640 =item * Arguments
641
642       ( $self, $XSS_work_idx, $BootCode_ref ) =
643         analyze_preprocessor_statements(
644           $self, $statement, $XSS_work_idx, $BootCode_ref
645         );
646
647 List of four elements.
648
649 =item * Return Value
650
651 Modifed values of three of the arguments passed to the function.  In
652 particular, the C<XSStack> and C<InitFileCode> attributes are modified.
653
654 =back
655
656 =cut
657
658 sub analyze_preprocessor_statements {
659   my ($self, $statement, $XSS_work_idx, $BootCode_ref) = @_;
660
661   if ($statement eq 'if') {
662     $XSS_work_idx = @{ $self->{XSStack} };
663     push(@{ $self->{XSStack} }, {type => 'if'});
664   }
665   else {
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");
671     }
672
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)} = ('', {});
678     }
679     else {
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;
686     }
687   }
688   return ($self, $XSS_work_idx, $BootCode_ref);
689 }
690
691 =head2 C<set_cond()>
692
693 =over 4
694
695 =item * Purpose
696
697 =item * Arguments
698
699 =item * Return Value
700
701 =back
702
703 =cut
704
705 sub set_cond {
706   my ($ellipsis, $min_args, $num_args) = @_;
707   my $cond;
708   if ($ellipsis) {
709     $cond = ($min_args ? qq(items < $min_args) : 0);
710   }
711   elsif ($min_args == $num_args) {
712     $cond = qq(items != $min_args);
713   }
714   else {
715     $cond = qq(items < $min_args || items > $num_args);
716   }
717   return $cond;
718 }
719
720 =head2 C<current_line_number()>
721
722 =over 4
723
724 =item * Purpose
725
726 Figures out the current line number in the XS file.
727
728 =item * Arguments
729
730 C<$self>
731
732 =item * Return Value
733
734 The current line number.
735
736 =back
737
738 =cut
739
740 sub current_line_number {
741   my $self = shift;
742   my $line_number = $self->{line_no}->[@{ $self->{line_no} } - @{ $self->{line} } -1];
743   return $line_number;
744 }
745
746 =head2 C<Warn()>
747
748 =over 4
749
750 =item * Purpose
751
752 =item * Arguments
753
754 =item * Return Value
755
756 =back
757
758 =cut
759
760 sub Warn {
761   my $self = shift;
762   my $warn_line_number = $self->current_line_number();
763   print STDERR "@_ in $self->{filename}, line $warn_line_number\n";
764 }
765
766 =head2 C<blurt()>
767
768 =over 4
769
770 =item * Purpose
771
772 =item * Arguments
773
774 =item * Return Value
775
776 =back
777
778 =cut
779
780 sub blurt {
781   my $self = shift;
782   $self->Warn(@_);
783   $self->{errors}++
784 }
785
786 =head2 C<death()>
787
788 =over 4
789
790 =item * Purpose
791
792 =item * Arguments
793
794 =item * Return Value
795
796 =back
797
798 =cut
799
800 sub death {
801   my $self = shift;
802   $self->Warn(@_);
803   exit 1;
804 }
805
806 =head2 C<check_conditional_preprocessor_statements()>
807
808 =over 4
809
810 =item * Purpose
811
812 =item * Arguments
813
814 =item * Return Value
815
816 =back
817
818 =cut
819
820 sub check_conditional_preprocessor_statements {
821   my ($self) = @_;
822   my @cpp = grep(/^\#\s*(?:if|e\w+)/, @{ $self->{line} });
823   if (@cpp) {
824     my $cpplevel;
825     for my $cpp (@cpp) {
826       if ($cpp =~ /^\#\s*if/) {
827         $cpplevel++;
828       }
829       elsif (!$cpplevel) {
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';
833         return;
834       }
835       elsif ($cpp =~ /^\#\s*endif/) {
836         $cpplevel--;
837       }
838     }
839     $self->Warn("Warning: #if without #endif in this function") if $cpplevel;
840   }
841 }
842
843 1;
844
845 # vim: ts=2 sw=2 et: