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