1 ################################################################################
3 # PPPort_pm.PL -- generate PPPort.pm
5 # Set the environment variable DPPP_CHECK_LEVEL to more than zero for some
6 # extra checking. 1 or 2 currently
8 ################################################################################
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.
15 # This program is free software; you can redistribute it and/or
16 # modify it under the same terms as Perl itself.
18 ################################################################################
22 require "./parts/ppptools.pl";
24 my $INCLUDE = 'parts/inc';
27 my %embed = map { ( $_->{name} => $_ ) }
28 parse_embed(qw(parts/embed.fnc parts/apidoc.fnc parts/ppport.fnc));
30 my(%provides, %prototypes, %explicit);
32 my $data = do { local $/; <DATA> };
34 # Call include(file, params) for every line that begins with %include
35 $data =~ s{^\%(include)\s+(\w+)((?:[^\S\r\n]+.*?)?)\s*$}
36 {eval "$1('$2', $3)" or die $@}gem;
38 $data = expand($data);
40 my @api = sort { lc $a cmp lc $b or $a cmp $b } keys %provides;
42 $data =~ s{^(.*)__PROVIDED_API__(\s*?)^}
43 {join '', map "$1$_\n", @api}gem;
47 for (keys %explicit) {
48 length > $len and $len = length;
50 my $format = sprintf '%%-%ds %%-%ds %%s', $len+2, $len+5;
53 $data =~ s!^(.*)__EXPLICIT_API__(\s*?)^!
54 sprintf("$1$format\n", 'Function / Variable', 'Static Request', 'Global Request') .
55 $1 . '-'x$len . "\n" .
56 join('', map { sprintf "$1$format\n", $explicit{$_} eq 'var' ? $_ : "$_()", "NEED_$_", "NEED_${_}_GLOBAL" }
61 my %raw_base = %{&parse_todo('parts/base')};
62 my %raw_todo = %{&parse_todo('parts/todo')};
65 for (keys %raw_todo) {
66 push @{$todo{$raw_todo{$_}}}, $_;
71 if (exists $raw_todo{$_} and exists $raw_base{$_}) {
72 if ($raw_base{$_} eq $raw_todo{$_}) {
73 warn "$INCLUDE/$provides{$_} provides $_, which is still marked "
74 . "todo for " . format_version($raw_todo{$_}) . "\n";
77 check(2, "$_ was ported back to " . format_version($raw_todo{$_}) .
78 " (baseline revision: " . format_version($raw_base{$_}) . ").");
84 for (keys %provides) {
85 next if /^Perl_(.*)/ && exists $embed{$1};
86 next if exists $embed{$_};
88 check(2, "No API definition for provided element $_ found.");
91 push @perl_api, keys %embed;
94 if (exists $provides{$_} && !exists $raw_base{$_}) {
95 check(2, "Mmmh, $_ doesn't seem to need backporting.");
97 my $line = "$_|" . (exists $provides{$_} && exists $raw_base{$_} ? $raw_base{$_} : '') . '|';
98 $line .= ($raw_todo{$_} || '') . '|';
99 $line .= 'p' if exists $provides{$_};
100 if (exists $embed{$_}) {
102 if (exists $e->{flags}{p}) { # Has 'Perl_' prefix
103 my $args = $e->{args};
104 $line .= 'v' if @$args && $args->[-1][0] eq '...';
106 $line .= 'n' if exists $e->{flags}{T}; # No thread context parameter
111 $data =~ s/^([\t ]*)__PERL_API__(\s*?)$/
112 join "\n", map "$1$_", sort @perl_api
116 for (reverse sort keys %todo) {
117 my $ver = format_version($_);
118 my $todo = "=item perl $ver\n\n";
119 for (sort @{$todo{$_}}) {
125 $data =~ s{^__UNSUPPORTED_API__(\s*?)^}
126 {join "\n", @todo}gem;
128 $data =~ s{__MIN_PERL__}{5.003}g;
129 $data =~ s{__MAX_PERL__}{5.30}g;
131 open FH, ">PPPort.pm" or die "PPPort.pm: $!\n";
139 my($file, $opt) = @_;
141 print "including $file\n";
143 my $data = parse_partspec("$INCLUDE/$file");
145 for (@{$data->{provides}}) {
146 if (exists $provides{$_}) {
147 if ($provides{$_} ne $file) {
148 warn "$file: $_ already provided by $provides{$_}\n";
152 $provides{$_} = $file;
156 for (keys %{$data->{prototypes}}) {
157 $prototypes{$_} = $data->{prototypes}{$_};
158 $prototypes{$_} = normalize_prototype($data->{prototypes}{$_});
159 $data->{implementation} =~ s/^$_(?=\s*\()/$DPPP(my_$_)/mg;
162 my $out = $data->{implementation};
164 if (exists $opt->{indent}) {
165 $out =~ s/^/$opt->{indent}/gm;
174 $code =~ s{^(\s*#\s*(?:el)?if\s+)(.*)$}{$1.expand_pp_expressions($2)}gem;
184 (?:[^\r\n\\]|\\[^\r\n])*
188 (?:[^\r\n\\]|\\[^\r\n])*
192 {expand_undefined($2, $1, $3)}gemx;
193 $code =~ s{^([^\S\r\n]*)__NEED_VAR__\s+(.*?)\s+(\w+)(?:\s*=\s*([^;]+?))?\s*;\s*$}
194 {expand_need_var($1, $3, $2, $4)}gem;
195 $code =~ s{^([^\S\r\n]*)__NEED_DUMMY_VAR__\s+(.*?)\s+(\w+)(?:\s*=\s*([^;]+?))?\s*;\s*$}
196 {expand_need_dummy_var($1, $3, $2, $4)}gem;
202 my($indent, $var, $type, $init) = @_;
204 $explicit{$var} = 'var';
206 my $myvar = "$DPPP(my_$var)";
207 $init = defined $init ? " = $init" : "";
209 my $code = <<ENDCODE;
210 #if defined(NEED_$var)
211 static $type $myvar$init;
212 #elif defined(NEED_${var}_GLOBAL)
220 $code =~ s/^/$indent/mg;
225 sub expand_need_dummy_var
227 my($indent, $var, $type, $init) = @_;
229 $explicit{$var} = 'var';
231 my $myvar = "$DPPP(dummy_$var)";
232 $init = defined $init ? " = $init" : "";
234 my $code = <<ENDCODE;
235 #if defined(NEED_$var)
236 static $type $myvar$init;
237 #elif defined(NEED_${var}_GLOBAL)
244 $code =~ s/^/$indent/mg;
251 my($macro, $withargs, $def) = @_;
252 my $rv = "#ifndef $macro\n# define ";
254 if (defined $def && $def =~ /\S/) {
255 $rv .= sprintf "%-30s %s", $withargs, $def;
266 sub expand_pp_expressions
269 $pp =~ s/\{([^\}]+)\}/expand_pp_expr($1)/ge;
277 if ($expr =~ /^\s*need\s+(\w+)\s*$/i) {
279 my $e = $embed{$func} or die "unknown API function '$func' in NEED\n";
280 my $proto = make_prototype($e);
281 if (exists $prototypes{$func}) {
282 if (compare_prototypes($proto, $prototypes{$func})) {
283 check(1, "differing prototypes for $func:\n API: $proto\n PPP: $prototypes{$func}");
284 $proto = $prototypes{$func};
288 warn "found no prototype for $func\n";;
291 $explicit{$func} = 'func';
293 $proto =~ s/\b$func(?=\s*\()/$DPPP(my_$func)/;
294 my $embed = make_embed($e);
296 return "defined(NEED_$func)\n"
303 . "#if defined(NEED_$func) || defined(NEED_${func}_GLOBAL)\n"
308 die "cannot expand preprocessor expression '$expr'\n";
315 my $a = do { my $x = 'a'; join ',', map { $x++ } 1 .. @{$f->{args}} };
316 my $lastarg = ${$f->{args}}[-1];
318 if ($f->{flags}{T}) {
319 if ($f->{flags}{p}) {
320 return "#define $n $DPPP(my_$n)\n" .
321 "#define Perl_$n $DPPP(my_$n)";
324 return "#define $n $DPPP(my_$n)";
333 if ($f->{flags}{p}) {
334 if ($f->{flags}{f}) {
335 return "#define Perl_$n $DPPP(my_$n)";
337 elsif (@$lastarg && $lastarg->[0] =~ /\.\.\./) {
338 return $undef . "#define $n $DPPP(my_$n)\n" .
339 "#define Perl_$n $DPPP(my_$n)";
342 return $undef . "#define $n($a) $DPPP(my_$n)(aTHX_ $a)\n" .
343 "#define Perl_$n $DPPP(my_$n)";
347 return $undef . "#define $n($a) $DPPP(my_$n)(aTHX_ $a)";
356 if (exists $ENV{DPPP_CHECK_LEVEL} and $ENV{DPPP_CHECK_LEVEL} >= $level) {
357 print STDERR @_, "\n";
362 ################################################################################
364 # !!!!! Do NOT edit this file directly! -- Edit PPPort_pm.PL instead. !!!!!
366 # This file was automatically generated from the definition files in the
367 # parts/inc/ subdirectory by PPPort_pm.PL. To learn more about how all this
368 # works, please read the F<HACKERS> file that came with this distribution.
370 ################################################################################
372 # Perl/Pollution/Portability
374 ################################################################################
376 # Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
377 # Copyright (C) 2018, The perl5 porters
378 # Version 2.x, Copyright (C) 2001, Paul Marquess.
379 # Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
381 # This program is free software; you can redistribute it and/or
382 # modify it under the same terms as Perl itself.
384 ################################################################################
388 Devel::PPPort - Perl/Pollution/Portability
392 Devel::PPPort::WriteFile(); # defaults to ./ppport.h
393 Devel::PPPort::WriteFile('someheader.h');
395 # Same as above but retrieve contents rather than write file
396 my $contents = Devel::PPPort::GetFileContents();
397 my $contents = Devel::PPPort::GetFileContents('someheader.h');
399 =head1 Start using Devel::PPPort for XS projects
402 $ perl -MDevel::PPPort -e'Devel::PPPort::WriteFile'
403 $ perl ppport.h --compat-version=5.6.1 --patch=diff.patch *.xs
404 $ patch -p0 < diff.patch
405 $ echo ppport.h >>MANIFEST
409 Perl's API has changed over time, gaining new features, new functions,
410 increasing its flexibility, and reducing the impact on the C namespace
411 environment (reduced pollution). The header file written by this module,
412 typically F<ppport.h>, attempts to bring some of the newer Perl API
413 features to older versions of Perl, so that you can worry less about
414 keeping track of old releases, but users can still reap the benefit.
416 C<Devel::PPPort> contains two functions, C<WriteFile> and C<GetFileContents>.
417 C<WriteFile>'s only purpose is to write the F<ppport.h> C header file.
418 This file contains a series of macros and, if explicitly requested, functions
419 that allow XS modules to be built using older versions of Perl. Currently,
420 Perl versions from __MIN_PERL__ to __MAX_PERL__ are supported.
422 C<GetFileContents> can be used to retrieve the file contents rather than
425 This module is used by C<h2xs> to write the file F<ppport.h>.
427 =head2 Why use ppport.h?
429 You should use F<ppport.h> in modern code so that your code will work
430 with the widest range of Perl interpreters possible, without significant
433 You should attempt to get older code to fully use F<ppport.h>, because the
434 reduced pollution of newer Perl versions is an important thing. It's so
435 important that the old polluting ways of original Perl modules will not be
436 supported very far into the future, and your module will almost certainly
437 break! By adapting to it now, you'll gain compatibility and a sense of
438 having done the electronic ecology some good.
440 =head2 How to use ppport.h
442 Don't direct the users of your module to download C<Devel::PPPort>.
443 They are most probably not XS writers. Also, don't make F<ppport.h>
444 optional. Rather, just take the most recent copy of F<ppport.h> that
445 you can find (e.g. by generating it with the latest C<Devel::PPPort>
446 release from CPAN), copy it into your project, adjust your project to
447 use it, and distribute the header along with your module.
449 =head2 Running ppport.h
451 But F<ppport.h> is more than just a C header. It's also a Perl script
452 that can check your source code. It will suggest hints and portability
453 notes, and can even make suggestions on how to change your code. You
454 can run it like any other Perl program:
456 perl ppport.h [options] [files]
458 It also has embedded documentation, so you can use
462 to find out more about how to use it.
468 C<WriteFile> takes one optional argument. When called with one
469 argument, it expects to be passed a filename. When called with
470 no arguments, it defaults to the filename F<ppport.h>.
472 The function returns a true value if the file was written successfully.
473 Otherwise it returns a false value.
475 =head2 GetFileContents
477 C<GetFileContents> behaves like C<WriteFile> above, but returns the contents
478 of the would-be file rather than writing it out.
482 F<ppport.h> supports Perl versions from __MIN_PERL__ to __MAX_PERL__
483 in threaded and non-threaded configurations.
485 =head2 Provided Perl compatibility API
487 The header file written by this module, typically F<ppport.h>, provides
488 access to the following elements of the Perl API that are not otherwise
489 available in older Perl releases:
493 =head2 Perl API not supported by ppport.h
495 There is still a big part of the API not supported by F<ppport.h>.
496 Either because it doesn't make sense to back-port that part of the API,
497 or simply because it hasn't been implemented yet. Patches welcome!
499 Here's a list of the currently unsupported API, and also the version of
500 Perl below which it is unsupported:
510 If you find any bugs, C<Devel::PPPort> doesn't seem to build on your
511 system, or any of its tests fail, please send a bug report to
512 L<perlbug@perl.org|mailto:perlbug@perl.org>.
520 Version 1.x of Devel::PPPort was written by Kenneth Albanowski.
524 Version 2.x was ported to the Perl core by Paul Marquess.
528 Version 3.x was ported back to CPAN by Marcus Holland-Moritz.
532 Versions >= 3.22 are maintained by perl5 porters
538 Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
540 Copyright (C) 2018, The perl5 porters
542 Version 2.x, Copyright (C) 2001, Paul Marquess.
544 Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
546 This program is free software; you can redistribute it and/or
547 modify it under the same terms as Perl itself.
551 See L<h2xs>, L<ppport.h>.
555 package Devel::PPPort;
558 use vars qw($VERSION $data);
564 $data = do { local $/; <DATA> };
565 my $pkg = 'Devel::PPPort';
566 $data =~ s/__PERL_VERSION__/$]/g;
567 $data =~ s/__VERSION__/$VERSION/g;
568 $data =~ s/__PKG__/$pkg/g;
572 sub GetFileContents {
573 my $file = shift || 'ppport.h';
574 defined $data or _init_data();
576 $copy =~ s/\bppport\.h\b/$file/g;
583 my $file = shift || 'ppport.h';
584 my $data = GetFileContents($file);
585 open F, ">$file" or return undef;
599 ----------------------------------------------------------------------
601 ppport.h -- Perl/Pollution/Portability Version __VERSION__
603 Automatically created by __PKG__ running under perl __PERL_VERSION__.
605 Do NOT edit this file directly! -- Edit PPPort_pm.PL and the
606 includes in parts/inc/ instead.
608 Use 'perldoc ppport.h' to view the documentation below.
610 ----------------------------------------------------------------------
614 %include ppphdoc { indent => '|>' }
621 #ifndef _P_P_PORTABILITY_H_
622 #define _P_P_PORTABILITY_H_
624 #ifndef DPPP_NAMESPACE
625 # define DPPP_NAMESPACE DPPP_
628 #define DPPP_CAT2(x,y) CAT2(x,y)
629 #define DPPP_(name) DPPP_CAT2(DPPP_NAMESPACE, name)
701 #endif /* _P_P_PORTABILITY_H_ */
703 /* End of File ppport.h */