1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
=head1 NAME
HACKERS - Devel::PPPort internals for hackers
=head1 SYNOPSIS
So you probably want to hack C<Devel::PPPort>?
Well, here's some information to get you started with what's
lying around in this distribution.
=head1 DESCRIPTION
=head2 How to build 114 versions of Perl
C<Devel::PPPort> supports Perl versions between 5.003 and bleadperl.
To guarantee this support, I need some of these versions on my
machine. I currently have 114 different Perl version/configuration
combinations installed on my laptop.
As many of the old Perl distributions need patching to compile
cleanly on newer systems (and because building 114 Perls by hand
just isn't fun), I wrote a tool to build all the different
versions and configurations. You can find it in F<devel/buildperl.pl>.
It can currently build the following Perl releases:
5.003
5.004 - 5.004_05
5.005 - 5.005_04
5.6.x
5.7.x
5.8.x
5.9.x
5.10.x
=head2 Fully automatic API checks
Knowing which parts of the API are not backwards compatible and
probably need C<Devel::PPPort> support is another problem that's
not easy to deal with manually. If you run
perl Makefile.PL --with-apicheck
a C file is generated by F<parts/apicheck.pl> that is compiled
and linked with C<Devel::PPPort>. This C file has the purpose of
using each of the public API functions/macros once.
The required information is derived from C<parts/embed.fnc> (just
a copy of bleadperl's C<embed.fnc>), C<parts/apidoc.fnc> (which
is generated by F<devel/mkapidoc.sh> and simply collects the rest
of the apidoc entries spread over the Perl source code) and
C<parts/ppport.fnc> (which lists all API provided purely by
Devel::PPPort).
The generated C file C<apicheck.c> is currently about 500k in size
and takes quite a while to compile.
Usually, C<apicheck.c> won't compile with older perls. And even if
it compiles, there's still a good chance of the dynamic linker
failing at C<make test> time. But that's on purpose!
We can use these failures to find changes in the API automatically.
The two Perl scripts F<devel/mktodo> and F<devel/mktodo.pl>
repeatedly run C<Devel::PPPort> with the apicheck code through
all different versions of perl. Scanning the output of the compiler
and the dynamic linker for errors, the files in F<parts/todo/> are
generated. These files list all parts of the public API that don't
work with less than a certain version of Perl.
This information is in turn used by F<parts/apicheck.pl> to mask
API calls in the generated C file for these versions, so the
process can be stopped by the time F<apicheck.c> compiles cleanly
and the dynamic linker is happy. (Actually, this process may generate
false positives, so by default each API call is checked once more
afterwards.)
Running C<devel/mktodo> takes about an hour, depending of course
on the machine you're running it on. If you run it with
the C<--nocheck> option, it won't recheck the API calls that failed
in the compilation stage and it'll take significantly less time.
Running with C<--nocheck> should usually be safe.
When running C<devel/mktodo> with the C<--base> option, it will
generate the I<baseline> todo files by disabling all functionality
provided by C<Devel::PPPort>. These are required for implementing
the C<--compat-version> option of the C<ppport.h> script. The
baseline todo files hold the information about which version of
Perl lacks a certain part of the API.
However, only the documented public API can be checked this way.
And since C<Devel::PPPort> provides more macros, these would not be
affected by C<--compat-version>. It's the job of F<devel/scanprov>
to figure out the baseline information for all remaining provided
macros by scanning the include files in the F<CORE> directory of
various Perl versions.
The whole process isn't platform independent. It has currently been
tested only under Linux, and it definitely requires at least C<gcc> and
the C<nm> utility.
It's not very often that one has to regenerate the baseline and todo
files. If you have to, you can either run F<devel/regenerate> or just
execute the following steps by hand:
=over 4
=item *
You need a whole bunch of different Perls. The more, the better.
You can use F<devel/buildperl.pl> to build them. I keep my perls
in F</tmp/perl>, so most of the tools take this as a default.
=item *
You also need a freshly built bleadperl that is in the path under
exactly this name. (The name of the executable is currently hardcoded
in F<devel/mktodo> and F<devel/scanprov>.)
=item *
Remove all existing todo files in the F<parts/base> and
F<parts/todo> directories.
=item *
Update the API information. Copy the latest F<embed.fnc> file from
bleadperl to the F<parts> directory and run F<devel/mkapidoc.sh> to
collect the remaining information in F<parts/apidoc.fnc>.
=item *
Build the new baseline by running
perl devel/mktodo --base
in the root directory of the distribution. When it's finished,
move all files from the F<parts/todo> directory to F<parts/base>.
=item *
Build the new todo files by running
perl devel/mktodo
in the root directory of the distribution.
=item *
Finally, add the remaining baseline information by running
perl Makefile.PL && make
perl devel/scanprov write
=back
=head2 Implementation
Residing in F<parts/inc/> is the "heart" of C<Devel::PPPort>. Each
of the files implements a part of the supported API, along with
hints, dependency information, XS code and tests.
The files are in a POD-like format that is parsed using the
functions in F<parts/ppptools.pl>.
The scripts F<PPPort_pm.PL>, F<PPPort_xs.PL> and F<mktests.PL> all
use the information in F<parts/inc/> to generate the main module
F<PPPort.pm>, the XS code in F<RealPPPort.xs> and various test files
in F<t/>.
All of these files could be generated on the fly while building
C<Devel::PPPort>, but not having the tests in C<t/> will confuse
TEST/harness in the core. Not having F<PPPort.pm> will be bad for
viewing the docs on C<search.cpan.org>. So unfortunately, it's
unavoidable to put some redundancy into the package.
=head2 Adding stuff to Devel::PPPort
First, check if the code you plan to add fits into one of the
existing files in F<parts/inc/>. If not, just start a new one and
remember to include it from within F<PPPort_pm.PL>.
Each file holds all relevant data for implementing a certain part
of the API:
=over 2
=item *
A list of the provided API in the C<=provides> section.
=item *
The implementation to add to F<ppport.h> in the C<=implementation>
section.
=item *
The code required to add to PPPort.xs for testing the implementation.
This code goes into the C<=xshead>, C<=xsinit>, C<=xsmisc>, C<=xsboot>
and C<=xsubs> section. Have a look at the template at the bottom
of F<PPPort_xs.PL> to see where the code ends up.
=item *
The tests in the C<=tests> section. Remember not to use any fancy
modules or syntax elements, as the test code should be able to run
with Perl 5.003, which, for example, doesn't support C<my> in
C<for>-loops:
for my $x (1, 2, 3) { } # won't work with 5.003
You can use C<ok()> to report success or failure:
ok($got == 42);
ok($got, $expected);
Regular expressions are not supported as the second argument to C<ok>,
because older perls do not support the C<qr> operator.
=back
It's usually the best approach to just copy an existing file and
use it as a template.
=head2 Implementation Hints
In the C<=implementation> section, you can use
__UNDEFINED__ macro some definition
instead of
#ifndef macro
# define macro some definition
#endif
The macro can have optional arguments and the definition can even
span multiple lines, like in
__UNDEFINED__ SvMAGIC_set(sv, val) \
STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \
(((XPVMG*) SvANY(sv))->xmg_magic = (val)); } STMT_END
This usually makes the code more compact and readable. And you
only have to add C<__UNDEFINED__> to the C<=provided> section.
Version checking can be tricky if you want to do it correct.
You can use
#if { VERSION < 5.9.3 }
instead of
#if ((PERL_VERSION < 9) || (PERL_VERSION == 9 && PERL_SUBVERSION < 3))
The version number can be either of the new form C<5.x.x> or of the older
form C<5.00x_yy>. Both are translated into the correct preprocessor
statements. It is also possible to combine this with other statements:
#if { VERSION >= 5.004 } && !defined(sv_vcatpvf)
/* a */
#elif { VERSION < 5.004_63 } && { VERSION != 5.004_05 }
/* b */
#endif
This not only works in the C<=implementation> section, but also in
the C<=xsubs>, C<=xsinit>, C<=xsmisc>, C<=xshead> and C<=xsboot> sections.
=head2 Testing
To automatically test C<Devel::PPPort> with lots of different Perl
versions, you can use the F<soak> script. Just pass it a list of
all Perl binaries you want to test.
=head2 Special Makefile targets
You can use
make regen
to regenerate all of the autogenerated files. To get rid of all
generated files (except for F<parts/todo/*> and F<parts/base/*>),
use
make purge_all
That's it.
=head2 Submitting Patches
If you've added some functionality to C<Devel::PPPort>, please
consider submitting a patch with your work to either the author
(E<lt>mhx@cpan.orgE<gt>) or to the CPAN Request Tracker at
L<http://rt.cpan.org>.
When submitting patches, please only add the relevant changes
and don't include the differences of the generated files. You
can use the C<purge_all> target to delete all autogenerated
files.
=head2 Integrating into the Perl core
When integrating this module into the Perl core, be sure to
remove the following files from the distribution. They are
either not needed or generated on the fly when building this
module in the core:
MANIFEST
META.yml
PPPort.pm
=head1 COPYRIGHT
Version 3.x, Copyright (C) 2004-2009, Marcus Holland-Moritz.
Version 2.x, Copyright (C) 2001, Paul Marquess.
Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
See L<ppport.h> and L<devel/regenerate>.
=cut
|