Marcus Holland-Moritz > Devel-PPPort-3.21 > HACKERS

Download:
Devel-PPPort-3.21.tar.gz

Annotate this POD

CPAN RT

New  1
Open  1
Stalled  5
View/Report Bugs
Source   Latest Release: Devel-PPPort-3.25

NAME ^

HACKERS - Devel::PPPort internals for hackers

SYNOPSIS ^

So you probably want to hack Devel::PPPort?

Well, here's some information to get you started with what's lying around in this distribution.

DESCRIPTION ^

How to build 366 versions of Perl

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 366 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 366 Perls by hand just isn't fun), I wrote a tool to build all the different versions and configurations. You can find it in 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.1x.x

Fully automatic API checks

Knowing which parts of the API are not backwards compatible and probably need 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 parts/apicheck.pl that is compiled and linked with Devel::PPPort. This C file has the purpose of using each of the public API functions/macros once.

The required information is derived from parts/embed.fnc (just a copy of bleadperl's embed.fnc), parts/apidoc.fnc (which is generated by devel/mkapidoc.sh and simply collects the rest of the apidoc entries spread over the Perl source code) and parts/ppport.fnc (which lists all API provided purely by Devel::PPPort). The generated C file apicheck.c is currently about 500k in size and takes quite a while to compile.

Usually, 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 make test time. But that's on purpose!

We can use these failures to find changes in the API automatically. The two Perl scripts devel/mktodo and devel/mktodo.pl repeatedly run 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 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 parts/apicheck.pl to mask API calls in the generated C file for these versions, so the process can be stopped by the time 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 devel/mktodo takes about an hour, depending of course on the machine you're running it on. If you run it with the --nocheck option, it won't recheck the API calls that failed in the compilation stage and it'll take significantly less time. Running with --nocheck should usually be safe.

When running devel/mktodo with the --base option, it will generate the baseline todo files by disabling all functionality provided by Devel::PPPort. These are required for implementing the --compat-version option of the 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 Devel::PPPort provides more macros, these would not be affected by --compat-version. It's the job of devel/scanprov to figure out the baseline information for all remaining provided macros by scanning the include files in the 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 gcc and the 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 devel/regenerate or just execute the following steps by hand:

Implementation

Residing in parts/inc/ is the "heart" of 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 parts/ppptools.pl.

The scripts PPPort_pm.PL, PPPort_xs.PL and mktests.PL all use the information in parts/inc/ to generate the main module PPPort.pm, the XS code in RealPPPort.xs and various test files in t/.

All of these files could be generated on the fly while building Devel::PPPort, but not having the tests in t/ will confuse TEST/harness in the core. Not having PPPort.pm will be bad for viewing the docs on search.cpan.org. So unfortunately, it's unavoidable to put some redundancy into the package.

Adding stuff to Devel::PPPort

First, check if the code you plan to add fits into one of the existing files in parts/inc/. If not, just start a new one and remember to include it from within PPPort_pm.PL.

Each file holds all relevant data for implementing a certain part of the API:

It's usually the best approach to just copy an existing file and use it as a template.

Implementation Hints

In the =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 __UNDEFINED__ to the =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 5.x.x or of the older form 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 =implementation section, but also in the =xsubs, =xsinit, =xsmisc, =xshead and =xsboot sections.

Testing

To automatically test Devel::PPPort with lots of different Perl versions, you can use the soak script. Just pass it a list of all Perl binaries you want to test.

Special Makefile targets

You can use

    make regen

to regenerate all of the autogenerated files. To get rid of all generated files (except for parts/todo/* and parts/base/*), use

    make purge_all

That's it.

Submitting Patches

If you've added some functionality to Devel::PPPort, please consider submitting a patch with your work to either the author (<mhx@cpan.org>) or to the CPAN Request Tracker at 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 purge_all target to delete all autogenerated files.

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

COPYRIGHT ^

Version 3.x, Copyright (C) 2004-2013, 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.

SEE ALSO ^

See ppport.h and "regenerate" in devel.

syntax highlighting: