📄 hackers
字号:
=head1 NAMEHACKERS - Devel::PPPort internals for hackers=head1 SYNOPSISSo you probably want to hack C<Devel::PPPort>?Well, here's some information to get you started with what'slying around in this distribution.=head1 DESCRIPTION=head2 How to build 111 versions of PerlC<Devel::PPPort> supports Perl versions between 5.003 and bleadperl.To guarantee this support, I need some of these versions on mymachine. I currently have 111 different Perl version/configurationcombinations installed on my laptop.As many of the old Perl distributions need patching to compilecleanly on newer systems (and because building 111 Perls by handjust isn't fun), I wrote a tool to build all the differentversions 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=head2 Fully automatic API checksKnowing which parts of the API are not backwards compatible andprobably need C<Devel::PPPort> support is another problem that'snot easy to deal with manually. If you run perl Makefile.PL --with-apichecka C file is generated by F<parts/apicheck.pl> that is compiledand linked with C<Devel::PPPort>. This C file has the purpose ofusing each of the public API functions/macros once.The required information is derived from C<parts/embed.fnc> (justa copy of bleadperl's C<embed.fnc>), C<parts/apidoc.fnc> (whichis generated by F<devel/mkapidoc.sh> and simply collects the restof the apidoc entries spread over the Perl source code) andC<parts/ppport.fnc> (which lists all API provided purely byDevel::PPPort).The generated C file C<apicheck.c> is currently about 500k in sizeand takes quite a while to compile.Usually, C<apicheck.c> won't compile with older perls. And even ifit compiles, there's still a good chance of the dynamic linkerfailing 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 throughall different versions of perl. Scanning the output of the compilerand the dynamic linker for errors, the files in F<parts/todo/> aregenerated. These files list all parts of the public API that don'twork with less than a certain version of Perl.This information is in turn used by F<parts/apicheck.pl> to maskAPI calls in the generated C file for these versions, so theprocess can be stopped by the time F<apicheck.c> compiles cleanlyand the dynamic linker is happy. (Actually, this process may generatefalse positives, so by default each API call is checked once moreafterwards.)Running C<devel/mktodo> takes about an hour, depending of courseon the machine you're running it on. If you run it withthe C<--nocheck> option, it won't recheck the API calls that failedin 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 willgenerate the I<baseline> todo files by disabling all functionalityprovided by C<Devel::PPPort>. These are required for implementingthe C<--compat-version> option of the C<ppport.h> script. Thebaseline todo files hold the information about which version ofPerl 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 beaffected by C<--compat-version>. It's the job of F<devel/scanprov>to figure out the baseline information for all remaining providedmacros by scanning the include files in the F<CORE> directory ofvarious Perl versions.The whole process isn't platform independent. It has currently beentested only under Linux, and it definitely requires at least C<gcc> andthe C<nm> utility.It's not very often that one has to regenerate the baseline and todofiles. If you have to, you can either run F<devel/regenerate> or justexecute 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 perlsin 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 underexactly this name. (The name of the executable is currently hardcodedin F<devel/mktodo> and F<devel/scanprov>.)=item *Remove all existing todo files in the F<parts/base> andF<parts/todo> directories.=item *Update the API information. Copy the latest F<embed.fnc> file frombleadperl to the F<parts> directory and run F<devel/mkapidoc.sh> tocollect the remaining information in F<parts/apidoc.fnc>.=item *Build the new baseline by running perl devel/mktodo --basein 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/mktodoin 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 ImplementationResiding in F<parts/inc/> is the "heart" of C<Devel::PPPort>. Eachof the files implements a part of the supported API, along withhints, dependency information, XS code and tests.The files are in a POD-like format that is parsed using thefunctions in F<parts/ppptools.pl>.The scripts F<PPPort_pm.PL>, F<PPPort_xs.PL> and F<mktests.PL> alluse the information in F<parts/inc/> to generate the main moduleF<PPPort.pm>, the XS code in F<RealPPPort.xs> and various test filesin F<t/>.All of these files could be generated on the fly while buildingC<Devel::PPPort>, but not having the tests in C<t/> will confuseTEST/harness in the core. Not having F<PPPort.pm> will be bad forviewing the docs on C<search.cpan.org>. So unfortunately, it'sunavoidable to put some redundancy into the package.=head2 Adding stuff to Devel::PPPortFirst, check if the code you plan to add fits into one of theexisting files in F<parts/inc/>. If not, just start a new one andremember to include it from within F<PPPort_pm.PL>.Each file holds all relevant data for implementing a certain partof 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 bottomof F<PPPort_xs.PL> to see where the code ends up.=item *The tests in the C<=tests> section. Remember not to use any fancymodules or syntax elements, as the test code should be able to runwith Perl 5.003, which, for example, doesn't support C<my> inC<for>-loops: for my $x (1, 2, 3) { } # won't work with 5.003You 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.=backIt's usually the best approach to just copy an existing file anduse it as a template.=head2 Implementation HintsIn the C<=implementation> section, you can use __UNDEFINED__ macro some definitioninstead of #ifndef macro # define macro some definition #endifThe macro can have optional arguments and the definition can evenspan multiple lines, like in __UNDEFINED__ SvMAGIC_set(sv, val) \ STMT_START { assert(SvTYPE(sv) >= SVt_PVMG); \ (((XPVMG*) SvANY(sv))->xmg_magic = (val)); } STMT_ENDThis usually makes the code more compact and readable. And youonly 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 olderform C<5.00x_yy>. Both are translated into the correct preprocessorstatements. 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 */ #endifThis not only works in the C<=implementation> section, but also inthe C<=xsubs>, C<=xsinit>, C<=xsmisc>, C<=xshead> and C<=xsboot> sections.=head2 TestingTo automatically test C<Devel::PPPort> with lots of different Perlversions, you can use the F<soak> script. Just pass it a list ofall Perl binaries you want to test.=head2 Special Makefile targetsYou can use make regento regenerate all of the autogenerated files. To get rid of allgenerated files (except for F<parts/todo/*> and F<parts/base/*>),use make purge_allThat's it.=head2 Submitting PatchesIf you've added some functionality to C<Devel::PPPort>, pleaseconsider submitting a patch with your work to either the author(E<lt>mhx@cpan.orgE<gt>) or to the CPAN Request Tracker atL<http://rt.cpan.org>.When submitting patches, please only add the relevant changesand don't include the differences of the generated files. Youcan use the C<purge_all> target to delete all autogeneratedfiles.=head2 Integrating into the Perl coreWhen integrating this module into the Perl core, be sure toremove the following files from the distribution. They areeither not needed or generated on the fly when building thismodule in the core: MANIFEST META.yml PPPort.pm=head1 COPYRIGHTVersion 3.x, Copyright (C) 2004-2007, 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/ormodify it under the same terms as Perl itself.=head1 SEE ALSOSee L<ppport.h> and L<devel/regenerate>.=cut
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -