Home / Alt manpages / perlhacktips(1)

  • perlhacktips(1)
  • User command
  • linux

A Practical Perl Core C Hacking Checklist

Use this checklist before sending a change to the Perl core C code. You will finish with a change that has been considered against threaded and debugging builds, the core's limited C99 dialect, symbol visibility, macro expansion and basic profiling tools. The local reference is perlhacktips(1) from Perl 5.38.2. Allow an hour for a small change, longer if you need to build Perl on more than one platform.

Prerequisites

You need a Perl source checkout, a C compiler and the normal Perl build tools. This guide does not turn an installed Perl binary into a development tree. Replace /path/to/perl below with the root of your checkout. Do not run build or test commands as root: a source build belongs in a directory you own.

First record the interpreter on the machine where you are working:

perl -V:version -V:useithreads -V:cc -V:ccflags

On this system the result identifies Perl 5.38.2, GCC as the compiler, and useithreads='define'. Your values may differ. This output is a checkpoint, not a promise that the source tree will use exactly the same configuration.

1. Establish a clean baseline

Build the checkout before editing it. A clean baseline separates an existing toolchain problem from your patch. The exact configure options are project and platform decisions, so use the options already chosen for your checkout rather than copying a guessed command line.

cd /path/to/perl
git status --short
make test

An empty status output means there are no uncommitted changes. A successful test run gives you a useful comparison point. If the tree is already dirty, record which files belong to you before proceeding; do not discard someone else's work with a reset or checkout command.

2. Exercise the build configurations that expose mistakes

Threading changes Perl function prototypes and affects how interpreter context is passed. Code using a context-aware form such as Perl_sv_setiv(aTHX_ ...) must not be casually mixed with the implicit-context form sv_setiv(...). If a function does not receive aTHX_, the document says to initialise context with dTHX first. Compile and test with -Duseithreads, even if your first build was unthreaded.

Also compile with -DDEBUGGING. This exposes more code to the compiler and therefore catches problems hidden by an ordinary build. These are build configurations, not runtime switches to add to a random Perl invocation. Use the source tree's Configure and build procedure to select them, then run its tests.

Checkpoint

Record the two results separately: an ordinary test run, then a threaded and debugging run. A failure in only one configuration is valuable evidence about the change, not a reason to delete the configuration.

3. Keep C99 use inside the supported boundary

Perl core C source permits a defined subset of C99 from version 5.35.5 onwards. The local manual lists mixed declarations and code, 64-bit integer types through I64 and U64, variadic macros, declarations in for loops, member initialisers in C and XS code, flexible array members, and // comments. Headers still need to compile as C++, so do not put member structure initialisers in headers.

Do not assume that all of C99 is available. Variable length arrays are explicitly unsuitable, and the manual says to use Perl's PERL_INT_FAST8_T-style types rather than the C99 types from <stdint.h>. C99 format strings from <inttypes.h> also have portability problems. Code in dual-life extensions must remain C89-compatible because it may build against older Perls.

Do not add a new -std=c99 flag just to force the issue. Perl's Configure and cflags.SH select compiler flags for the platform. The manual warns that this flag can affect declarations from <unistd.h>, while the core normally uses as much of -std=gnu99, -pedantic and warning flags as the platform supports.

4. Check names, globals and visibility

Choose names that will survive both C and C++ compilation. Do not begin a symbol with an underscore, and do not use two consecutive underscores. Perl reserves names beginning with Perl, perl and PL_; that is useful context when reading existing code, but it does not make arbitrary header macros safe for XS consumers.

A modifiable global, including a file-static one, complicates interpreters and concurrency. Prefer a new interpreter variable, following the layout and compatibility guidance in intrpvar.h. A genuinely read-only global can be checked with nm. For a BSD-style nm, the manual gives this focused check:

nm libperl.a | egrep -v ' [TURtr] '

Run it from the directory containing the archive and inspect the result. A constant you added should not appear in that filtered output. The command is a diagnostic, not a substitute for checking the declaration and the link on every target.

Make a function static when it is used in one source file. If several Perl source files use it but it is internal, keep it out of the public API. Export only functions that genuinely belong in the shared library. Some platforms need explicit exports for public API functions, so consult the embed.pl discussion in perlguts before changing visibility.

5. Treat macros as an interface

Prefer an inline function for non-trivial work. It avoids name collisions and does not evaluate expression arguments in surprising ways. When a macro is necessary, give every value it depends on an explicit parameter. An unqualified temporary such as foo can collide with a caller's definition, including a later #define foo bar, and the resulting error can appear far from the macro definition.

Use a small preprocessor expansion test for macros that accept expressions or create local variables. Check that each argument is evaluated once, that a caller's names cannot rename a macro temporary, and that the macro behaves correctly inside an if statement. Then compile the test under the threaded and debugging configurations from step 2.

6. Verify before handing it over

Run the relevant tests again, inspect the diff, and keep the configuration output with the review notes. If you introduced an unlisted C99 feature, the manual requires either a Configure probe with a fallback in the headers or test evidence from every platform that must support it. Do not claim portability from one Linux compiler.

If your change fails, return to the last checkpoint rather than broadening the patch. Restore only files you own, preserve the failing test output, and explain whether the failure is specific to threads, DEBUGGING, a compiler dialect, symbol visibility or macro expansion. No service restart or elevated privilege is required for this workflow.

Done means

  • The clean baseline and final test results are recorded.
  • Threaded and -DDEBUGGING builds have been attempted.
  • Only the documented C99 subset is used, with fallbacks or probes for anything else.
  • Globals, symbol names and exports match the intended scope.
  • Macros have explicit inputs and no accidental caller-name dependencies.
  • The final diff contains no unrelated edits.