Perl developers have almost always faced a dependency problem: how do you ensure a CPAN module can find, download, and build a non-Perl library? Whether it’s openssl, libpng, or nuklear, the traditional solution has been a complex dance of shell commands, pkg-config probes, and precarious make invocations.
Today, we have two primary philosophies for solving this. On one side stands Alien::Build, the mature, flexible Swiss Army knife of the Perl ecosystem. On the other stands Alien::Xrepo::Base, a new contender that leverages the cross-platform power of the Xmake/Xrepo ecosystem and Perl 5.40's native class features.
In this article, we’ll compare these two approaches and see why moving from "Recipes" to "Registries" might be the biggest productivity boost for Alien authors in years.
1. Alien::Build: The Recipe-Based Approach
Alien::Build (and its companion alienfile) is built on the idea of a recipe. As an author, you write a DSL that describes exactly how to fetch, build, and probe a library.
Example: A typical alienfile for nuklear
Nuklear is a single-header library, which sounds easy, but to use it with FFI, you must create a C wrapper to compile it into a shared library. (Worth noting: because nuklear is header-only — Xrepo declares it set_kind("library", { headeronly = true }) — there is no prebuilt nuklear.so/nuklear.dll anywhere; you always have to synthesize one for FFI, in Alien::Build or in a registry.)
use alienfile;
share {
start_url => 'https://github.com/vurtun/nuklear/archive/refs/tags/v4.13.2.tar.gz';
# We have to manually write a C file to 'inject' the implementation
# so it can be compiled into a .so / .dll
patch [
'echo "#define NK_IMPLEMENTATION\n#include \"nuklear.h\"" > nuklear_wrapper.c'
];
build [
[ '%{cc}', '-shared', '-fPIC', '-o', 'libnuklear.%{so}', 'nuklear_wrapper.c' ],
[ 'mkdir', '-p', '%{.install.prefix}/lib', '%{.install.prefix}/include' ],
[ 'cp', 'libnuklear.%{so}', '%{.install.prefix}/lib/' ],
[ 'cp', 'nuklear.h', '%{.install.prefix}/include/' ]
];
};
gather sub {
my($build) = @_;
my $prefix = $build->install_prop->{prefix};
$build->runtime_prop->{cflags} = "-I$prefix/include";
$build->runtime_prop->{libs} = "-L$prefix/lib -lnuklear";
};
2. Alien::Xrepo::Base: The Registry-Based Approach
Alien::Xrepo::Base takes a different path. Instead of teaching Perl how to build a specific library, it delegates that responsibility to Xrepo, which already knows how to turn a package like zlib into a usable shared library.
Why alienfile steps are redundant with Xrepo
When you use Alien::Xrepo::Base, the three pillars of alienfile effectively disappear:
- No Download Step: Xrepo handles mirrors, checksums, and git submodules.
- No Build Step: Xrepo knows if a package needs
Autoconf,CMake,Meson, orBazel. It handles compiler flags, cross-compilation, and dependency trees automatically. - No Gather Step: This is perhaps the biggest win. Xrepo returns a structured JSON object containing the exact include directories, library paths, and linker flags. There is no "searching" for files; the registry provides the map.
Example: The "Quick & Easy" Alien::Xrepo::Base subclass
Here is the equivalent Alien::Zlib using the new Perl 5.40 syntax:
use v5.40;
use experimental 'class';
use Alien::Xrepo::Base;
class Alien::Zlib : isa(Alien::Xrepo::Base) {
method package_name { 'zlib' }
# Ask Xrepo for a shared library so we can FFI into it with Affix.
method install_opts {
return ( kind => 'shared' );
}
}
That's it. You don't need to know where the source code lives, how to run make, or how
to find zlib1.dll / libz.so. You simply ask for the package by name, and the registry
hands you a shared library ready for FFI.
3. Comparing the Developer Experience
| Feature | Alien::Build / alienfile | Alien::Xrepo::Base |
|---|---|---|
| Logic Location | Inside your Perl distribution. | Inside the global Xrepo registry. |
| Maintenance | You must update URLs and patches. | Community maintainers update Xrepo. |
| Cross-Platform | Requires manual logic for Win32/Unix. | Handled by Xmake's abstraction layer. |
| Dependencies | Hard to chain multiple Aliens. | Xrepo handles the full dependency tree. |
| Perl Version | Works on older Perls. | Requires Perl 5.40+ (for class). |
4. Integration with Distribution Builders
Whether you use Module::Build or ExtUtils::MakeMaker (EUMM), an Alien module needs to hook into the install process to trigger the native build.
Using Module::Build (with Alien::Xrepo::Base)
Alien::Xrepo::Base provides a helper that makes Build.PL trivial. It essentially generates a tiny Module::Build::Tiny clone that understands the Xrepo lifecycle.
use v5.40;
use lib 'lib';
use Alien::Xrepo::Base;
# This triggers the "Registry-to-Dist" bridge
Build_PL('Alien::Zlib');
When a user runs ./Build, Alien::Xrepo::Base will:
- Call
xrepo install zlibinto theblibshare directory. - Generate a
ConfigData.pmcontaining the resolved metadata from Xrepo.
Note: Build_PL reads the distribution's META.json to name the generated Build
script, so that file must exist before you run Build_PL('Alien::Zlib').
Using ExtUtils::MakeMaker
If you prefer the classic Makefile.PL approach, you can still drive the Xmake/Xrepo
engine directly. The cflags/libs accessors (and the root-isolated install) are
provided by Alien::Xrepo::Base; instantiate that instead of the lower-level
Alien::Xrepo, whose install returns a raw PackageInfo with no such methods.
use ExtUtils::MakeMaker;
use Alien::Xrepo::Base;
my $package = Alien::Xrepo::Base->new(
package_name => 'zlib', # or subclass it and let it provide install_opts
root => 'blib/lib/share', # isolated, project-local install
);
$package->install( kind => 'shared' ); # also available via $package->package_info
WriteMakefile(
NAME => 'My::Module',
VERSION_FROM => 'lib/My/Module.pm',
CONFIGURE_REQUIRES => {
'Alien::Xrepo' => '0.09'
},
INC => $package->cflags, # "-I/path/to/zlib/include"
LIBS => [ $package->libs ] # "-L/path/to/zlib/lib -lzlib"
);
5. The "Zero-Recipe" Advantage
The true strength of Alien::Xrepo::Base is revealed when a library has complex dependencies of its own.
Imagine building libwebp. It might require libjpeg, libpng, and zlib. In the Alien::Build world, you would need to create four separate Alien distributions and coordinate their alienfiles so they find each other.
In the Alien::Xrepo::Base world, you just ask for libwebp. Xrepo resolves the tree, builds all four libraries in the correct order, links them together, and hands you a single PackageInfo object.
By basing your work on Alien::Xrepo::Base, you aren't just writing a wrapper; you are tapping into a global effort to make C/C++ libraries as easy to install as CPAN modules.
Summary
Alien::Build remains the right choice if:
- You need to support Perls older than 5.40.
- The library you are wrapping is extremely obscure and doesn't exist in any package manager.
- You need highly bespoke patching logic that only Perl can provide.
Alien::Xrepo::Base is the superior choice if:
- You want to leverage modern Perl 5.40 features.
- You want to support Windows, macOS, and Linux without writing three different sets of build logic.
- You want to spend your time writing FFI bindings, not debugging
Makefileerrors.
By moving the "Recipe" out of your Perl code and into a managed registry like Xrepo, you reduce the surface area for bugs and ensure your Alien module stays working long after you've stopped updating the download URLs.
Comments