1 ################################################################################
3 # PPPort_pm.PL -- generate PPPort.pm
5 ################################################################################
7 # Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
8 # Copyright (C) 2018, The perl5 porters
9 # Version 2.x, Copyright (C) 2001, Paul Marquess.
10 # Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
12 # This program is free software; you can redistribute it and/or
13 # modify it under the same terms as Perl itself.
15 ################################################################################
19 require "./parts/ppptools.pl";
21 my $INCLUDE = 'parts/inc';
24 my %embed = map { ( $_->{name} => $_ ) }
25 parse_embed(qw(parts/embed.fnc parts/apidoc.fnc parts/ppport.fnc));
27 my(%provides, %prototypes, %explicit);
29 my $data = do { local $/; <DATA> };
30 $data =~ s{^\%(include)\s+(\w+)((?:[^\S\r\n]+.*?)?)\s*$}
31 {eval "$1('$2', $3)" or die $@}gem;
33 $data = expand($data);
35 my @api = sort { lc $a cmp lc $b or $a cmp $b } keys %provides;
37 $data =~ s{^(.*)__PROVIDED_API__(\s*?)^}
38 {join '', map "$1$_\n", @api}gem;
42 for (keys %explicit) {
43 length > $len and $len = length;
45 my $format = sprintf '%%-%ds %%-%ds %%s', $len+2, $len+5;
48 $data =~ s!^(.*)__EXPLICIT_API__(\s*?)^!
49 sprintf("$1$format\n", 'Function / Variable', 'Static Request', 'Global Request') .
50 $1 . '-'x$len . "\n" .
51 join('', map { sprintf "$1$format\n", $explicit{$_} eq 'var' ? $_ : "$_()", "NEED_$_", "NEED_${_}_GLOBAL" }
56 my %raw_base = %{&parse_todo('parts/base')};
57 my %raw_todo = %{&parse_todo('parts/todo')};
60 for (keys %raw_todo) {
61 push @{$todo{$raw_todo{$_}}}, $_;
66 if (exists $raw_todo{$_} and exists $raw_base{$_}) {
67 if ($raw_base{$_} eq $raw_todo{$_}) {
68 warn "$INCLUDE/$provides{$_} provides $_, which is still marked "
69 . "todo for " . format_version($raw_todo{$_}) . "\n";
72 check(2, "$_ was ported back to " . format_version($raw_todo{$_}) .
73 " (baseline revision: " . format_version($raw_base{$_}) . ").");
79 for (keys %provides) {
80 next if /^Perl_(.*)/ && exists $embed{$1};
81 next if exists $embed{$_};
83 check(2, "No API definition for provided element $_ found.");
86 push @perl_api, keys %embed;
89 if (exists $provides{$_} && !exists $raw_base{$_}) {
90 check(2, "Mmmh, $_ doesn't seem to need backporting.");
92 my $line = "$_|" . (exists $provides{$_} && exists $raw_base{$_} ? $raw_base{$_} : '') . '|';
93 $line .= ($raw_todo{$_} || '') . '|';
94 $line .= 'p' if exists $provides{$_};
95 if (exists $embed{$_}) {
97 if (exists $e->{flags}{p}) { # Has 'Perl_' prefix
98 my $args = $e->{args};
99 $line .= 'v' if @$args && $args->[-1][0] eq '...';
101 $line .= 'n' if exists $e->{flags}{T}; # No thread context parameter
106 $data =~ s/^([\t ]*)__PERL_API__(\s*?)$/
107 join "\n", map "$1$_", sort @perl_api
111 for (reverse sort keys %todo) {
112 my $ver = format_version($_);
113 my $todo = "=item perl $ver\n\n";
114 for (sort @{$todo{$_}}) {
120 $data =~ s{^__UNSUPPORTED_API__(\s*?)^}
121 {join "\n", @todo}gem;
123 $data =~ s{__MIN_PERL__}{5.003}g;
124 $data =~ s{__MAX_PERL__}{5.30}g;
126 open FH, ">PPPort.pm" or die "PPPort.pm: $!\n";
134 my($file, $opt) = @_;
136 print "including $file\n";
138 my $data = parse_partspec("$INCLUDE/$file");
140 for (@{$data->{provides}}) {
141 if (exists $provides{$_}) {
142 if ($provides{$_} ne $file) {
143 warn "$file: $_ already provided by $provides{$_}\n";
147 $provides{$_} = $file;
151 for (keys %{$data->{prototypes}}) {
152 $prototypes{$_} = $data->{prototypes}{$_};
153 $data->{implementation} =~ s/^$_(?=\s*\()/$DPPP(my_$_)/mg;
156 my $out = $data->{implementation};
158 if (exists $opt->{indent}) {
159 $out =~ s/^/$opt->{indent}/gm;
168 $code =~ s{^(\s*#\s*(?:el)?if\s+)(.*)$}{$1.expand_pp_expressions($2)}gem;
178 (?:[^\r\n\\]|\\[^\r\n])*
182 (?:[^\r\n\\]|\\[^\r\n])*
186 {expand_undefined($2, $1, $3)}gemx;
187 $code =~ s{^([^\S\r\n]*)__NEED_VAR__\s+(.*?)\s+(\w+)(?:\s*=\s*([^;]+?))?\s*;\s*$}
188 {expand_need_var($1, $3, $2, $4)}gem;
189 $code =~ s{^([^\S\r\n]*)__NEED_DUMMY_VAR__\s+(.*?)\s+(\w+)(?:\s*=\s*([^;]+?))?\s*;\s*$}
190 {expand_need_dummy_var($1, $3, $2, $4)}gem;
196 my($indent, $var, $type, $init) = @_;
198 $explicit{$var} = 'var';
200 my $myvar = "$DPPP(my_$var)";
201 $init = defined $init ? " = $init" : "";
203 my $code = <<ENDCODE;
204 #if defined(NEED_$var)
205 static $type $myvar$init;
206 #elif defined(NEED_${var}_GLOBAL)
214 $code =~ s/^/$indent/mg;
219 sub expand_need_dummy_var
221 my($indent, $var, $type, $init) = @_;
223 $explicit{$var} = 'var';
225 my $myvar = "$DPPP(dummy_$var)";
226 $init = defined $init ? " = $init" : "";
228 my $code = <<ENDCODE;
229 #if defined(NEED_$var)
230 static $type $myvar$init;
231 #elif defined(NEED_${var}_GLOBAL)
238 $code =~ s/^/$indent/mg;
245 my($macro, $withargs, $def) = @_;
246 my $rv = "#ifndef $macro\n# define ";
248 if (defined $def && $def =~ /\S/) {
249 $rv .= sprintf "%-30s %s", $withargs, $def;
260 sub expand_pp_expressions
263 $pp =~ s/\{([^\}]+)\}/expand_pp_expr($1)/ge;
271 if ($expr =~ /^\s*need\s+(\w+)\s*$/i) {
273 my $e = $embed{$func} or die "unknown API function '$func' in NEED\n";
274 my $proto = make_prototype($e);
275 if (exists $prototypes{$func}) {
276 if (compare_prototypes($proto, $prototypes{$func})) {
277 check(1, "differing prototypes for $func:\n API: $proto\n PPP: $prototypes{$func}");
278 $proto = $prototypes{$func};
282 warn "found no prototype for $func\n";;
285 $explicit{$func} = 'func';
287 $proto =~ s/\b$func(?=\s*\()/$DPPP(my_$func)/;
288 my $embed = make_embed($e);
290 return "defined(NEED_$func)\n"
297 . "#if defined(NEED_$func) || defined(NEED_${func}_GLOBAL)\n"
302 die "cannot expand preprocessor expression '$expr'\n";
309 my $a = do { my $x = 'a'; join ',', map { $x++ } 1 .. @{$f->{args}} };
310 my $lastarg = ${$f->{args}}[-1];
312 if ($f->{flags}{T}) {
313 if ($f->{flags}{p}) {
314 return "#define $n $DPPP(my_$n)\n" .
315 "#define Perl_$n $DPPP(my_$n)";
318 return "#define $n $DPPP(my_$n)";
327 if ($f->{flags}{p}) {
328 if ($f->{flags}{f}) {
329 return "#define Perl_$n $DPPP(my_$n)";
331 elsif (@$lastarg && $lastarg->[0] =~ /\.\.\./) {
332 return $undef . "#define $n $DPPP(my_$n)\n" .
333 "#define Perl_$n $DPPP(my_$n)";
336 return $undef . "#define $n($a) $DPPP(my_$n)(aTHX_ $a)\n" .
337 "#define Perl_$n $DPPP(my_$n)";
341 return $undef . "#define $n($a) $DPPP(my_$n)(aTHX_ $a)";
350 if (exists $ENV{DPPP_CHECK_LEVEL} and $ENV{DPPP_CHECK_LEVEL} >= $level) {
351 print STDERR @_, "\n";
356 ################################################################################
358 # !!!!! Do NOT edit this file directly! -- Edit PPPort_pm.PL instead. !!!!!
360 # This file was automatically generated from the definition files in the
361 # parts/inc/ subdirectory by PPPort_pm.PL. To learn more about how all this
362 # works, please read the F<HACKERS> file that came with this distribution.
364 ################################################################################
366 # Perl/Pollution/Portability
368 ################################################################################
370 # Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
371 # Copyright (C) 2018, The perl5 porters
372 # Version 2.x, Copyright (C) 2001, Paul Marquess.
373 # Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
375 # This program is free software; you can redistribute it and/or
376 # modify it under the same terms as Perl itself.
378 ################################################################################
382 Devel::PPPort - Perl/Pollution/Portability
386 Devel::PPPort::WriteFile(); # defaults to ./ppport.h
387 Devel::PPPort::WriteFile('someheader.h');
389 # Same as above but retrieve contents rather than write file
390 my $contents = Devel::PPPort::GetFileContents();
391 my $contents = Devel::PPPort::GetFileContents('someheader.h');
393 =head1 Start using Devel::PPPort for XS projects
396 $ perl -MDevel::PPPort -e'Devel::PPPort::WriteFile'
397 $ perl ppport.h --compat-version=5.6.1 --patch=diff.patch *.xs
398 $ patch -p0 < diff.patch
399 $ echo ppport.h >>MANIFEST
403 Perl's API has changed over time, gaining new features, new functions,
404 increasing its flexibility, and reducing the impact on the C namespace
405 environment (reduced pollution). The header file written by this module,
406 typically F<ppport.h>, attempts to bring some of the newer Perl API
407 features to older versions of Perl, so that you can worry less about
408 keeping track of old releases, but users can still reap the benefit.
410 C<Devel::PPPort> contains two functions, C<WriteFile> and C<GetFileContents>.
411 C<WriteFile>'s only purpose is to write the F<ppport.h> C header file.
412 This file contains a series of macros and, if explicitly requested, functions
413 that allow XS modules to be built using older versions of Perl. Currently,
414 Perl versions from __MIN_PERL__ to __MAX_PERL__ are supported.
416 C<GetFileContents> can be used to retrieve the file contents rather than
419 This module is used by C<h2xs> to write the file F<ppport.h>.
421 =head2 Why use ppport.h?
423 You should use F<ppport.h> in modern code so that your code will work
424 with the widest range of Perl interpreters possible, without significant
427 You should attempt older code to fully use F<ppport.h>, because the
428 reduced pollution of newer Perl versions is an important thing. It's so
429 important that the old polluting ways of original Perl modules will not be
430 supported very far into the future, and your module will almost certainly
431 break! By adapting to it now, you'll gain compatibility and a sense of
432 having done the electronic ecology some good.
434 =head2 How to use ppport.h
436 Don't direct the users of your module to download C<Devel::PPPort>.
437 They are most probably no XS writers. Also, don't make F<ppport.h>
438 optional. Rather, just take the most recent copy of F<ppport.h> that
439 you can find (e.g. by generating it with the latest C<Devel::PPPort>
440 release from CPAN), copy it into your project, adjust your project to
441 use it, and distribute the header along with your module.
443 =head2 Running ppport.h
445 But F<ppport.h> is more than just a C header. It's also a Perl script
446 that can check your source code. It will suggest hints and portability
447 notes, and can even make suggestions on how to change your code. You
448 can run it like any other Perl program:
450 perl ppport.h [options] [files]
452 It also has embedded documentation, so you can use
456 to find out more about how to use it.
462 C<WriteFile> takes one optional argument. When called with one
463 argument, it expects to be passed a filename. When called with
464 no arguments, it defaults to the filename F<ppport.h>.
466 The function returns a true value if the file was written successfully.
467 Otherwise it returns a false value.
469 =head2 GetFileContents
471 C<GetFileContents> behaves like C<WriteFile> above, but returns the contents
472 of the would-be file rather than writing it out.
476 F<ppport.h> supports Perl versions from __MIN_PERL__ to __MAX_PERL__
477 in threaded and non-threaded configurations.
479 =head2 Provided Perl compatibility API
481 The header file written by this module, typically F<ppport.h>, provides
482 access to the following elements of the Perl API that is not available
483 in older Perl releases:
487 =head2 Perl API not supported by ppport.h
489 There is still a big part of the API not supported by F<ppport.h>.
490 Either because it doesn't make sense to back-port that part of the API,
491 or simply because it hasn't been implemented yet. Patches welcome!
493 Here's a list of the currently unsupported API, and also the version of
494 Perl below which it is unsupported:
504 If you find any bugs, C<Devel::PPPort> doesn't seem to build on your
505 system, or any of its tests fail, please send a bug report to
506 L<perlbug@perl.org|mailto:perlbug@perl.org>.
514 Version 1.x of Devel::PPPort was written by Kenneth Albanowski.
518 Version 2.x was ported to the Perl core by Paul Marquess.
522 Version 3.x was ported back to CPAN by Marcus Holland-Moritz.
526 Versions >= 3.22 are maintained with support from Matthew Horsfall (alh).
532 Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
534 Copyright (C) 2018, The perl5 porters
536 Version 2.x, Copyright (C) 2001, Paul Marquess.
538 Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
540 This program is free software; you can redistribute it and/or
541 modify it under the same terms as Perl itself.
545 See L<h2xs>, L<ppport.h>.
549 package Devel::PPPort;
552 use vars qw($VERSION $data);
558 $data = do { local $/; <DATA> };
559 my $pkg = 'Devel::PPPort';
560 $data =~ s/__PERL_VERSION__/$]/g;
561 $data =~ s/__VERSION__/$VERSION/g;
562 $data =~ s/__PKG__/$pkg/g;
566 sub GetFileContents {
567 my $file = shift || 'ppport.h';
568 defined $data or _init_data();
570 $copy =~ s/\bppport\.h\b/$file/g;
577 my $file = shift || 'ppport.h';
578 my $data = GetFileContents($file);
579 open F, ">$file" or return undef;
593 ----------------------------------------------------------------------
595 ppport.h -- Perl/Pollution/Portability Version __VERSION__
597 Automatically created by __PKG__ running under perl __PERL_VERSION__.
599 Do NOT edit this file directly! -- Edit PPPort_pm.PL and the
600 includes in parts/inc/ instead.
602 Use 'perldoc ppport.h' to view the documentation below.
604 ----------------------------------------------------------------------
608 %include ppphdoc { indent => '|>' }
615 #ifndef _P_P_PORTABILITY_H_
616 #define _P_P_PORTABILITY_H_
618 #ifndef DPPP_NAMESPACE
619 # define DPPP_NAMESPACE DPPP_
622 #define DPPP_CAT2(x,y) CAT2(x,y)
623 #define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name)
693 #endif /* _P_P_PORTABILITY_H_ */
695 /* End of File ppport.h */