If your Perl distribution contains C code that needs to be compiled (an .xs extension, a helper tool, or a shared library for FFI) you have traditionally reached for ExtUtils::MakeMaker, Module::Build, or Alien::Build. Each drives make or cmake through its own abstraction.
Alien::Xmake takes a different approach: it is xmake, directly callable from Perl. If your C code already has (or could have) an xmake.lua build description, you can skip the MakeMaker layer entirely and let xmake handle the build, test, install, and packaging all invoked from a Perl driver.
What is xmake?
xmake is a cross-platform, Lua-scriptable build system. It supports C, C++, Rust, Go, and more; runs on Windows, macOS, Linux, and BSDs; and integrates directly with xrepo (its package manager). A single xmake.lua describes your targets, dependencies, and build rules.
Driving xmake from Perl
Alien::Xmake gives you the xmake binary, a convenience constructor, and a one-to-one method for every xmake action:
use v5.40;
use Alien::Xmake;
my $xmake = Alien::Xmake->new( verbose => 1 );
# The raw xmake binary path
say 'xmake: ' . $xmake->exe;
# Version
say 'version: ' . $xmake->version;
# Build, test, install
$xmake->build;
$xmake->test;
$xmake->install;
Every method corresponds to a command-line invocation (xmake build, xmake test, etc.). The verbose flag prints the commands as they run, which is invaluable during development.
The task() escape hatch
If a method doesn't exist for a particular xmake command, task() is the escape hatch to run the command manually. task() forwards arguments two ways: a target list via targets => [...], and raw command-line arguments via args => [...]:
$xmake->task('run', targets => ['main'], args => ['config=release']);
$xmake->task('package');
You can also write to xmake's persistent global configuration:
$xmake->global(vs => '2022');
$xmake->global(ndk => '/path/to/android-ndk');
A practical example: the nuklear single-header library
Nuklear is a single-header GUI library. There is no prebuilt nuklear.so anywhere, you have to synthesize one. An xmake.lua that compiles a wrapper translation unit into a shared library is just a few lines:
target("nuklear")
set_kind("shared") -- build a DLL/SO so FFI can load it
add_files("nuklear_wrapper.c") -- one TU: #define NK_IMPLEMENTATION, then #include "nuklear.h"
add_includedirs(".")
Driving this from Perl looks like this:
use Alien::Xmake;
use Path::Tiny;
my $proj = path('eg/nuklear');
my $xmake = Alien::Xmake->new( verbose => 1 );
# Build
chdir $proj;
$xmake->build;
# Install to a local directory
$xmake->install( installdir => path('dist')->absolute );
No Makefile.PL, no alienfile, no ExtUtils::CBuilder, just a three-line Perl driver on top of a few-line xmake.lua.
Dependency graphs
If your xmake.lua declares xrepo dependencies, you can ask xrepo (via Alien::Xrepo) to print the full dependency graph in Graphviz DOT format. Alien::Xrepo exposes an info method that renders the graph that xmake itself produces:
use Alien::Xrepo;
use Path::Tiny;
my $repo = Alien::Xrepo->new;
my $dot = $repo->info( 'libsdl3_ttf', depgraph => 1, format => 'dot' );
path('deps.dot')->spew($dot);
# Render: dot -Tpng deps.dot -o deps.png
info returns the raw DOT text (digraph { "ninja" ... }), or a parsed data structure when you pass format => 'json'. The depgraph flag works directly against the registry, so you can inspect the native dependency tree of any package without leaving Perl. If you hand it to Graphvis, you get something like this:
When xmake makes sense
- Your C code already uses xmake: If you maintain an xmake project, driving it from Perl is a no-brainer.
- You need cross-compilation without MakeMaker pain: xmake's
-p,-a,--toolchain,--ndkflags map directly toAlien::Xmake->build()/configure(). - You want packaging:
xmake packagecreates a distributable archive;xmake packproduces.deb/.rpm/.nsiinstallers all callable via$xmake->package(). - You're tired of Makefile generation: xmake's Lua config is a quarter the size of an equivalent
Makefile.PLfor anything beyond trivial C code.
For the right project, xmake replaces an entire build toolchain with a single Lua file and a two-line Perl driver.
Comments