Build and Test a Perl XSUB Extension with h2xs
You will finish with a small Perl extension skeleton, a compiled shared library, and a repeatable test command. XSUBs let Perl code call routines written in C, with xsubpp generating the C glue between the two.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 20 minutes for the first run. You need Perl, h2xs, a C compiler, and make. This guide uses the installed Perl 5.38.2 and perl-doc 5.38.2-3.2ubuntu0.6. It follows the Unix workflow documented by perlxstut(1).
1. Check the build tools
Run these ordinary, read-only checks in the shell:
$ perl -v
$ perl -V:make
$ command -v h2xs
$ command -v make
$ command -v cc
The version output should identify Perl 5.38.2 on this machine, and perl -V:make should report make='make';. The tutorial uses the build program Perl was configured to use. If your output names another program, use that program in later commands.
Checkpoint
Do not continue until the required commands resolve. A missing compiler or build tool is a package-management issue; this workflow does not require sudo and does not install anything.
2. Generate a clean extension directory
Choose a new working directory. The name passed to h2xs becomes both the directory and the initial Perl package name:
$ mkdir -p ~/src/perl-xsubs
$ cd ~/src/perl-xsubs
$ h2xs -A -n Mytest
-A asks for a module-only skeleton rather than wrapping an existing C header. -n Mytest names the extension. The command creates Mytest/, including Makefile.PL, Mytest.xs, lib/Mytest.pm, t/Mytest.t, MANIFEST, and supporting files. Recent h2xs versions also warn that they are defaulting to backwards compatibility with the installed Perl unless a minimum version is supplied.
This command changes the current directory by adding files. If you generated the skeleton in the wrong place, stop and inspect it before deleting anything. To abandon an unused experiment, remove only the exact Mytest directory after checking its path with pwd and find; do not use a broad recursive deletion command.
Verify the important inputs before editing:
$ cd Mytest
$ find . -maxdepth 2 -type f | sort
$ sed -n '1,80p' Makefile.PL
$ sed -n '1,80p' Mytest.xs
3. Generate the Makefile
Makefile.PL is a Perl program that uses ExtUtils::MakeMaker to write the real Makefile. Run it from the extension directory:
$ perl Makefile.PL
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for Mytest
The exact wording can vary with the installed ExtUtils::MakeMaker release. The useful result is a new Makefile and no fatal error. The command also creates metadata files such as MYMETA.json and MYMETA.yml. They describe the generated distribution; they are not the compiled extension.
Checkpoint
Confirm that the Makefile exists:
$ test -f Makefile && echo 'Makefile ready'
Makefile ready
4. Compile the XSUB
Run the configured build program without elevated privileges:
$ make
The build runs xsubpp to translate Mytest.xs into Mytest.c, compiles that C file, and places the shared library under blib/arch/auto/Mytest/. It also copies the Perl module into blib/lib/ and may generate documentation.
You may see a message asking for prototyping behaviour. The tutorial identifies this as an informational prompt for this basic example. Read the complete build output if the command fails: a missing header, compiler error, or linker error must be fixed before testing.
Check that the build artefacts exist:
$ test -f Mytest.c && echo 'C glue generated'
$ find blib -type f -maxdepth 6 | sort
C glue generated
5. Run the generated tests
Use the Makefile's test target, not the test script by itself:
$ make test
t/Mytest.t .. ok
All tests successful.
Files=1, Tests=1
Result: PASS
The exact timing and harness lines vary. The important evidence is that the test file reports ok, the harness says all tests are successful, and the final result is PASS. make test supplies blib/lib and blib/arch to Perl, so the test loads the extension you just built rather than an older installed copy.
Running perl t/Mytest.t directly is a common trap. It can fail to find the module, or it can accidentally test a different version already present in @INC. Keep using the Makefile target while the extension is under development.
6. Add a small XSUB
The generated .xs file is the source for the C-facing part of the extension. A function declaration is followed by its parameters and directives such as CODE: and OUTPUT:. For example, the tutorial's first routine has this shape:
void
hello()
CODE:
printf("Hello, world!\n");
Append that routine to Mytest.xs, then rebuild and retest:
$ make
$ make test
To call it from a small script, use the build tree explicitly:
$ perl -Iblib/lib -Iblib/arch -MMytest -e 'Mytest::hello()'
Hello, world!
The function is not installed system-wide by these commands. The -I options point Perl at the local build output, which keeps the experiment reversible. If you later change the XS source, rerun make before testing and keep the module version aligned with the shared library.
7. Understand the next boundary
Adding an integer result uses RETVAL and an OUTPUT: section. The typemap converts between Perl values and C types. Standard rules live in Perl's ExtUtils typemap, and modern Perl versions can also use inline typemaps. You do not need to edit that file for the basic extension above.
Do not treat the generated skeleton as ready for distribution. Add focused tests under t/, choose exports deliberately, and check the minimum Perl version your code needs. If you link another library, declare it in Makefile.PL and verify the linker command before distributing the result. Installing a module into a system Perl changes shared state and may affect applications, so use a deliberate packaging or local-library plan rather than copying files into system directories.
Done means
h2xs -A -n Mytestcreated an isolated module skeleton.perl Makefile.PLgenerated a usable Makefile.makegenerated C glue and a local shared library.make testloaded the build-tree extension and finished withPASS.- You know that
blibis temporary build output and that installation is a separate, state-changing step.