This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
parts/inc/inctools: Rewrite parse_version
[perl5.git] / dist / Devel-PPPort / PPPort_pm.PL
1 ################################################################################
2 #
3 #  PPPort_pm.PL -- generate PPPort.pm
4 #
5 # Set the environment variable DPPP_CHECK_LEVEL to more than zero for some
6 # extra checking. 1 or 2 currently
7
8 ################################################################################
9 #
10 #  Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
11 #               Copyright (C) 2018, The perl5 porters
12 #  Version 2.x, Copyright (C) 2001, Paul Marquess.
13 #  Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
14 #
15 #  This program is free software; you can redistribute it and/or
16 #  modify it under the same terms as Perl itself.
17 #
18 ################################################################################
19
20 use strict;
21 BEGIN { $^W = 1; }
22 require "./parts/ppptools.pl";
23 require "./parts/inc/inctools";
24
25 my $INCLUDE = 'parts/inc';
26 my $DPPP = 'DPPP_';
27
28 my %embed = map { ( $_->{name} => $_ ) }
29             parse_embed(qw(parts/embed.fnc parts/apidoc.fnc parts/ppport.fnc));
30
31 my(%provides, %prototypes, %explicit);
32
33 my $data = do { local $/; <DATA> };
34
35 # Call include(file, params) for every line that begins with %include
36 $data =~ s{^\%(include)\s+(\w+)((?:[^\S\r\n]+.*?)?)\s*$}
37           {eval "$1('$2', $3)" or die $@}gem;
38
39 $data = expand($data);
40
41 my @api = sort dictionary_order keys %provides;
42
43 $data =~ s{^(.*)__PROVIDED_API__(\s*?)^}
44           {join '', map "$1$_\n", @api}gem;
45
46 {
47   my $len = 0;
48   for (keys %explicit) {
49     length > $len and $len = length;
50   }
51   my $format = sprintf '%%-%ds  %%-%ds  %%s', $len+2, $len+5;
52   $len = 3*$len + 23;
53
54 $data =~ s!^(.*)__EXPLICIT_API__(\s*?)^!
55            sprintf("$1$format\n", 'Function / Variable', 'Static Request', 'Global Request') .
56            $1 . '-'x$len . "\n" .
57            join('', map { sprintf "$1$format\n", $explicit{$_} eq 'var' ? $_ : "$_()", "NEED_$_", "NEED_${_}_GLOBAL" }
58                     sort dictionary_order keys %explicit)
59           !gem;
60 }
61
62 my %raw_base = %{&parse_todo('parts/base')};
63 my %raw_todo = %{&parse_todo('parts/todo')};
64
65 my %todo;
66 for (keys %raw_todo) {
67   push @{$todo{$raw_todo{$_}}}, $_;
68 }
69
70 # check consistency
71 for (@api) {
72   if (exists $raw_todo{$_} and exists $raw_base{$_}) {
73     if ($raw_base{$_} eq $raw_todo{$_}) {
74       warn "$INCLUDE/$provides{$_} provides $_, which is still marked "
75            . "todo for " . format_version($raw_todo{$_}) . "\n";
76     }
77     else {
78       check(2, "$_ was ported back to " . format_version($raw_todo{$_}) .
79                " (baseline revision: " . format_version($raw_base{$_}) . ").");
80     }
81   }
82 }
83
84 my @perl_api;
85 for (keys %provides) {
86   next if /^Perl_(.*)/ && exists $embed{$1};
87   next if exists $embed{$_};
88   push @perl_api, $_;
89   check(2, "No API definition for provided element $_ found.");
90 }
91
92 push @perl_api, keys %embed;
93 @perl_api = sort dictionary_order @perl_api;
94
95 for (@perl_api) {
96   if (exists $provides{$_} && !exists $raw_base{$_}) {
97     check(2, "Mmmh, $_ doesn't seem to need backporting.");
98   }
99   my $line = "$_|" . (exists $provides{$_} && exists $raw_base{$_} ? $raw_base{$_} : '') . '|';
100   $line .= ($raw_todo{$_} || '') . '|';
101   $line .= 'p' if exists $provides{$_};
102   if (exists $embed{$_}) {
103     my $e = $embed{$_};
104     if (exists $e->{flags}{p}) {    # Has 'Perl_' prefix
105       my $args = $e->{args};
106       $line .= 'v' if @$args && $args->[-1][0] eq '...';
107     }
108     $line .= 'n' if exists $e->{flags}{T};  # No thread context parameter
109   }
110   $_ = $line;
111 }
112
113 $data =~ s/^([\t ]*)__PERL_API__(\s*?)$/
114            join "\n", map "$1$_", sort dictionary_order @perl_api
115           /gem;
116
117 my $undocumented = "(undocumented)";
118
119 my @todo;
120 for (reverse sort keys %todo) {
121   my $ver = format_version($_);
122   my $todo = "=item perl $ver\n\n";
123   for (sort dictionary_order @{$todo{$_}}) {
124     $todo .= "  $_";
125     $todo .= "  (DEPRECATED)" if  $embed{$_}->{flags}{D};
126     $todo .= "  (marked experimental)" if $embed{$_}->{flags}{x};
127     $todo .= "  $undocumented" unless $embed{$_}->{flags}{d};
128     $todo .= "\n";
129   }
130   push @todo, $todo;
131 }
132
133 $data =~ s{^__UNSUPPORTED_API__(\s*?)^}
134           {join "\n", @todo}gem;
135
136 $data =~ s{__MIN_PERL__}{5.003_07}g;
137 $data =~ s{__MAX_PERL__}{5.30}g;
138
139 open FH, ">PPPort.pm" or die "PPPort.pm: $!\n";
140 print FH $data;
141 close FH;
142
143 exit 0;
144
145 sub include
146 {
147   my($file, $opt) = @_;
148
149   print "including $file\n";
150
151   my $data = parse_partspec("$INCLUDE/$file");
152
153   for (@{$data->{provides}}) {
154     if (exists $provides{$_}) {
155       if ($provides{$_} ne $file) {
156         warn "$file: $_ already provided by $provides{$_}\n";
157       }
158     }
159     else {
160       $provides{$_} = $file;
161     }
162   }
163
164   for (keys %{$data->{prototypes}}) {
165     $prototypes{$_} = $data->{prototypes}{$_};
166     $prototypes{$_} = normalize_prototype($data->{prototypes}{$_});
167     $data->{implementation} =~ s/^$_(?=\s*\()/$DPPP(my_$_)/mg;
168   }
169
170   my $out = $data->{implementation};
171
172   if (exists $opt->{indent}) {
173     $out =~ s/^/$opt->{indent}/gm;
174   }
175
176   return $out;
177 }
178
179 sub expand
180 {
181   my $code = shift;
182   $code =~ s{^(\s*#\s*(?:el)?if\s+)(.*)$}{$1.expand_pp_expressions($2)}gem;
183   $code =~ s{^\s*
184               __UNDEFINED__
185               \s+
186               (
187                 ( \w+ )
188                 (?: \( [^)]* \) )?
189               )
190               [^\r\n\S]*
191               (
192                 (?:[^\r\n\\]|\\[^\r\n])*
193                 (?:
194                   \\
195                   (?:\r\n|[\r\n])
196                   (?:[^\r\n\\]|\\[^\r\n])*
197                 )*
198               )
199             \s*$}
200             {expand_undefined($2, $1, $3)}gemx;
201   $code =~ s{^([^\S\r\n]*)__NEED_VAR__\s+(.*?)\s+(\w+)(?:\s*=\s*([^;]+?))?\s*;\s*$}
202             {expand_need_var($1, $3, $2, $4)}gem;
203   $code =~ s{^([^\S\r\n]*)__NEED_DUMMY_VAR__\s+(.*?)\s+(\w+)(?:\s*=\s*([^;]+?))?\s*;\s*$}
204             {expand_need_dummy_var($1, $3, $2, $4)}gem;
205   return $code;
206 }
207
208 sub expand_need_var
209 {
210   my($indent, $var, $type, $init) = @_;
211
212   $explicit{$var} = 'var';
213
214   my $myvar = "$DPPP(my_$var)";
215   $init = defined $init ? " = $init" : "";
216
217   my $code = <<ENDCODE;
218 #if defined(NEED_$var)
219 static $type $myvar$init;
220 #elif defined(NEED_${var}_GLOBAL)
221 $type $myvar$init;
222 #else
223 extern $type $myvar;
224 #endif
225 #define $var $myvar
226 ENDCODE
227
228   $code =~ s/^/$indent/mg;
229
230   return $code;
231 }
232
233 sub expand_need_dummy_var
234 {
235   my($indent, $var, $type, $init) = @_;
236
237   $explicit{$var} = 'var';
238
239   my $myvar = "$DPPP(dummy_$var)";
240   $init = defined $init ? " = $init" : "";
241
242   my $code = <<ENDCODE;
243 #if defined(NEED_$var)
244 static $type $myvar$init;
245 #elif defined(NEED_${var}_GLOBAL)
246 $type $myvar$init;
247 #else
248 extern $type $myvar;
249 #endif
250 ENDCODE
251
252   $code =~ s/^/$indent/mg;
253
254   return $code;
255 }
256
257 sub expand_undefined
258 {
259   my($macro, $withargs, $def) = @_;
260   my $rv = "#ifndef $macro\n#  define ";
261
262   if (defined $def && $def =~ /\S/) {
263     $rv .= sprintf "%-30s %s", $withargs, $def;
264   }
265   else {
266     $rv .= $withargs;
267   }
268
269   $rv .= "\n#endif\n";
270
271   return $rv;
272 }
273
274 sub expand_pp_expressions
275 {
276   my $pp = shift;
277   $pp =~ s/\{([^\}]+)\}/expand_pp_expr($1)/ge;
278   return $pp;
279 }
280
281 sub expand_pp_expr
282 {
283   my $expr = shift;
284
285   if ($expr =~ /^\s*need\s+(\w+)\s*$/i) {
286     my $func = $1;
287     my $e = $embed{$func} or die "unknown API function '$func' in NEED\n";
288     my $proto = make_prototype($e);
289     if (exists $prototypes{$func}) {
290       if (compare_prototypes($proto, $prototypes{$func})) {
291         my $proto_no_pTHX = $proto;
292         $proto_no_pTHX =~ s/pTHX_\s*//;
293         if (compare_prototypes($proto_no_pTHX, $prototypes{$func})) {
294             check(1, "differing prototypes for $func:\n  API: $proto\n  PPP: $prototypes{$func}");
295         }
296         else {
297             check(1, "prototypes differ in pTHX_ for $func:\n  API: $proto\n  PPP: $prototypes{$func}");
298         }
299         $proto = $prototypes{$func};
300       }
301     }
302     else {
303       warn "found no prototype for $func\n";;
304     }
305
306     $explicit{$func} = 'func';
307
308     $proto =~ s/\b$func(?=\s*\()/$DPPP(my_$func)/;
309     my $embed = make_embed($e);
310
311     return "defined(NEED_$func)\n"
312          . "static $proto;\n"
313          . "static\n"
314          . "#else\n"
315          . "extern $proto;\n"
316          . "#endif\n"
317          . "\n"
318          . "#if defined(NEED_$func) || defined(NEED_${func}_GLOBAL)\n"
319          . "\n"
320          . "$embed\n";
321   }
322
323   die "cannot expand preprocessor expression '$expr'\n";
324 }
325
326 sub make_embed
327 {
328   my $f = shift;
329   my $n = $f->{name};
330   my $a = do { my $x = 'a'; join ',', map { $x++ } 1 .. @{$f->{args}} };
331   my $lastarg = ${$f->{args}}[-1];
332
333   if ($f->{flags}{T}) {
334     if ($f->{flags}{p}) {
335       return "#define $n $DPPP(my_$n)\n" .
336              "#define Perl_$n $DPPP(my_$n)";
337     }
338     else {
339       return "#define $n $DPPP(my_$n)";
340     }
341   }
342   else {
343     my $undef = <<UNDEF;
344 #ifdef $n
345 #  undef $n
346 #endif
347 UNDEF
348     if ($f->{flags}{p}) {
349       if ($f->{flags}{f}) {
350         return "#define Perl_$n $DPPP(my_$n)";
351       }
352       elsif (@$lastarg && $lastarg->[0] =~ /\.\.\./) {
353         return $undef . "#define $n $DPPP(my_$n)\n" .
354                         "#define Perl_$n $DPPP(my_$n)";
355       }
356       else {
357         return $undef . "#define $n($a) $DPPP(my_$n)(aTHX_ $a)\n" .
358                         "#define Perl_$n $DPPP(my_$n)";
359       }
360     }
361     else {
362       return $undef . "#define $n($a) $DPPP(my_$n)(aTHX_ $a)";
363     }
364   }
365 }
366
367 sub check
368 {
369   my $level = shift;
370
371   if (exists $ENV{DPPP_CHECK_LEVEL} and $ENV{DPPP_CHECK_LEVEL} >= $level) {
372     print STDERR @_, "\n";
373   }
374 }
375
376 __DATA__
377 ################################################################################
378 #
379 #  !!!!! Do NOT edit this file directly! -- Edit PPPort_pm.PL instead. !!!!!
380 #
381 #  This file was automatically generated from the definition files in the
382 #  parts/inc/ subdirectory by PPPort_pm.PL. To learn more about how all this
383 #  works, please read the F<HACKERS> file that came with this distribution.
384 #
385 ################################################################################
386 #
387 #  Perl/Pollution/Portability
388 #
389 ################################################################################
390 #
391 #  Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
392 #               Copyright (C) 2018, The perl5 porters
393 #  Version 2.x, Copyright (C) 2001, Paul Marquess.
394 #  Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
395 #
396 #  This program is free software; you can redistribute it and/or
397 #  modify it under the same terms as Perl itself.
398 #
399 ################################################################################
400
401 =head1 NAME
402
403 Devel::PPPort - Perl/Pollution/Portability
404
405 =head1 SYNOPSIS
406
407   Devel::PPPort::WriteFile();   # defaults to ./ppport.h
408   Devel::PPPort::WriteFile('someheader.h');
409
410   # Same as above but retrieve contents rather than write file
411   my $contents = Devel::PPPort::GetFileContents();
412   my $contents = Devel::PPPort::GetFileContents('someheader.h');
413
414 =head1 Start using Devel::PPPort for XS projects
415
416   $ cpan Devel::PPPort
417   $ perl -MDevel::PPPort -e'Devel::PPPort::WriteFile'
418   $ perl ppport.h --compat-version=5.6.1 --patch=diff.patch *.xs
419   $ patch -p0 < diff.patch
420   $ echo ppport.h >>MANIFEST
421
422 =head1 DESCRIPTION
423
424 Perl's API has changed over time, gaining new features, new functions,
425 increasing its flexibility, and reducing the impact on the C namespace
426 environment (reduced pollution). The header file written by this module,
427 typically F<ppport.h>, attempts to bring some of the newer Perl API
428 features to older versions of Perl, so that you can worry less about
429 keeping track of old releases, but users can still reap the benefit.
430
431 C<Devel::PPPort> contains two functions, C<WriteFile> and C<GetFileContents>.
432 C<WriteFile>'s only purpose is to write the F<ppport.h> C header file.
433 This file contains a series of macros and, if explicitly requested, functions
434 that allow XS modules to be built using older versions of Perl. Currently,
435 Perl versions from __MIN_PERL__ to __MAX_PERL__ are supported.
436
437 C<GetFileContents> can be used to retrieve the file contents rather than
438 writing it out.
439
440 This module is used by C<h2xs> to write the file F<ppport.h>.
441
442 =head2 Why use ppport.h?
443
444 You should use F<ppport.h> in modern code so that your code will work
445 with the widest range of Perl interpreters possible, without significant
446 additional work.
447
448 You should attempt to get older code to fully use F<ppport.h>, because the
449 reduced pollution of newer Perl versions is an important thing. It's so
450 important that the old polluting ways of original Perl modules will not be
451 supported very far into the future, and your module will almost certainly
452 break! By adapting to it now, you'll gain compatibility and a sense of
453 having done the electronic ecology some good.
454
455 =head2 How to use ppport.h
456
457 Don't direct the users of your module to download C<Devel::PPPort>.
458 They are most probably not XS writers. Also, don't make F<ppport.h>
459 optional. Rather, just take the most recent copy of F<ppport.h> that
460 you can find (e.g. by generating it with the latest C<Devel::PPPort>
461 release from CPAN), copy it into your project, adjust your project to
462 use it, and distribute the header along with your module.
463
464 =head2 Running ppport.h
465
466 But F<ppport.h> is more than just a C header. It's also a Perl script
467 that can check your source code. It will suggest hints and portability
468 notes, and can even make suggestions on how to change your code. You
469 can run it like any other Perl program:
470
471     perl ppport.h [options] [files]
472
473 It also has embedded documentation, so you can use
474
475     perldoc ppport.h
476
477 to find out more about how to use it.
478
479 =head1 FUNCTIONS
480
481 =head2 WriteFile
482
483 C<WriteFile> takes one optional argument. When called with one
484 argument, it expects to be passed a filename. When called with
485 no arguments, it defaults to the filename F<ppport.h>.
486
487 The function returns a true value if the file was written successfully.
488 Otherwise it returns a false value.
489
490 =head2 GetFileContents
491
492 C<GetFileContents> behaves like C<WriteFile> above, but returns the contents
493 of the would-be file rather than writing it out.
494
495 =head1 COMPATIBILITY
496
497 F<ppport.h> supports Perl versions from __MIN_PERL__ to __MAX_PERL__
498 in threaded and non-threaded configurations.
499
500 =head2 Provided Perl compatibility API
501
502 The header file written by this module, typically F<ppport.h>, provides
503 access to the following elements of the Perl API that are not otherwise
504 available in Perl releases older than when the elements were first introduced.
505 (Many of these are not supported all the way back to __MIN_PERL__;
506 see L</Perl API not supported by ppport.h back to __MIN_PERL__> for details.)
507
508     __PROVIDED_API__
509
510 =head2 Perl API not supported by ppport.h back to __MIN_PERL__
511
512 There is still a big part of the API not fully supported by F<ppport.h>.
513 This can be because it doesn't make sense to back-port that part of the API,
514 or simply because it hasn't been implemented yet. Patches welcome!  Some
515 elements are ported backward for some releases, but not all the way to
516 __MIN_PERL__.
517
518 Below is a list of the API that isn't currently supported back to
519 __MIN_PERL__, sorted by the version of Perl below which it is unsupported.
520 Only things you should be using are included in the list, so not listed are
521 deprecated and experimental functions.
522
523 Some of the entries are marked as "undocumented".  This means that they
524 aren't necessarily considered stable, and could be changed or removed in some
525 future release without warning.  It is therefore a bad idea to use them
526 without further checking.  It could be that these are considered to be for
527 perl core use only; or it could be, though, that C<Devel::PPPort> doesn't know
528 where to find their documentation, or that it's just an oversight that they
529 haven't been documented.  If you want to use one, and potentially have it
530 backported, first send mail to L<mailto:perl5-porters@perl.org>.
531
532 =over 4
533
534 __UNSUPPORTED_API__
535
536 =back
537
538 =head1 BUGS
539
540 If you find any bugs, C<Devel::PPPort> doesn't seem to build on your
541 system, or any of its tests fail, please send a bug report to
542 L<perlbug@perl.org|mailto:perlbug@perl.org>.
543
544 =head1 AUTHORS
545
546 =over 2
547
548 =item *
549
550 Version 1.x of Devel::PPPort was written by Kenneth Albanowski.
551
552 =item *
553
554 Version 2.x was ported to the Perl core by Paul Marquess.
555
556 =item *
557
558 Version 3.x was ported back to CPAN by Marcus Holland-Moritz.
559
560 =item * 
561
562 Versions >= 3.22 are maintained by perl5 porters
563
564 =back
565
566 =head1 COPYRIGHT
567
568 Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
569
570              Copyright (C) 2018, The perl5 porters
571
572 Version 2.x, Copyright (C) 2001, Paul Marquess.
573
574 Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
575
576 This program is free software; you can redistribute it and/or
577 modify it under the same terms as Perl itself.
578
579 =head1 SEE ALSO
580
581 See L<h2xs>, L<ppport.h>.
582
583 =cut
584
585 package Devel::PPPort;
586
587 use strict;
588 use vars qw($VERSION $data);
589
590 $VERSION = '3.55';
591
592 sub _init_data
593 {
594   $data = do { local $/; <DATA> };
595   my $pkg = 'Devel::PPPort';
596   $data =~ s/__PERL_VERSION__/$]/g;
597   $data =~ s/__VERSION__/$VERSION/g;
598   $data =~ s/__PKG__/$pkg/g;
599   $data =~ s/^\|>//gm;
600 }
601
602 sub GetFileContents {
603   my $file = shift || 'ppport.h';
604   defined $data or _init_data();
605   my $copy = $data;
606   $copy =~ s/\bppport\.h\b/$file/g;
607
608   return $copy;
609 }
610
611 sub WriteFile
612 {
613   my $file = shift || 'ppport.h';
614   my $data = GetFileContents($file);
615   open F, ">$file" or return undef;
616   print F $data;
617   close F;
618
619   return 1;
620 }
621
622 1;
623
624 __DATA__
625 #if 0
626 <<'SKIP';
627 #endif
628 /*
629 ----------------------------------------------------------------------
630
631     ppport.h -- Perl/Pollution/Portability Version __VERSION__
632
633     Automatically created by __PKG__ running under perl __PERL_VERSION__.
634
635     Do NOT edit this file directly! -- Edit PPPort_pm.PL and the
636     includes in parts/inc/ instead.
637
638     Use 'perldoc ppport.h' to view the documentation below.
639
640 ----------------------------------------------------------------------
641
642 SKIP
643
644 %include ppphdoc { indent => '|>' }
645
646 %include inctools
647
648 %include ppphbin
649
650 __DATA__
651 */
652
653 #ifndef _P_P_PORTABILITY_H_
654 #define _P_P_PORTABILITY_H_
655
656 #ifndef DPPP_NAMESPACE
657 #  define DPPP_NAMESPACE DPPP_
658 #endif
659
660 #define DPPP_CAT2(x,y) CAT2(x,y)
661 #define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name)
662
663 %include version
664
665 %include threads
666
667 %include limits
668
669 %include variables
670
671 %include newCONSTSUB
672
673 %include magic_defs
674
675 %include misc
676
677 %include sv_xpvf
678
679 %include SvPV
680
681 %include warn
682
683 %include format
684
685 %include uv
686
687 %include memory
688
689 %include mess
690
691 %include mPUSH
692
693 %include call
694
695 %include newRV
696
697 %include MY_CXT
698
699 %include SvREFCNT
700
701 %include newSV_type
702
703 %include newSVpv
704
705 %include Sv_set
706
707 %include shared_pv
708
709 %include HvNAME
710
711 %include gv
712
713 %include pvs
714
715 %include magic
716
717 %include cop
718
719 %include grok
720
721 %include snprintf
722
723 %include sprintf
724
725 %include exception
726
727 %include strlfuncs
728
729 %include utf8
730
731 %include pv_tools
732
733 #endif /* _P_P_PORTABILITY_H_ */
734
735 /* End of File ppport.h */