Perl's Alien ecosystem has always been synonymous with one thing: fine grained access to shared libraries. You write an Alien module so that FFI::Platypus can call zlibVersion(), or so that Inline::C can link against libpng. The package metadata is full of cflags, libs, and linkdirs. Any tools you might need to build the libs will need their own Aliens which may be unmaintained or maybe not even exist.

What we really need is an Alien that can automatically provide tools: ninja to drive a build, cmake to configure one, python to run a code generator, meson, go, rust, node... Until now, a Perl distribution that depended on a tool had to either require the system administrator to install it or write a fragile Alien toolchain recipe.

Alien::Xrepo fixes this by treating binaries and libraries as first-class citizens of the same package store. Install libpng and ninja in the same line; run them both from Perl.

What is a "binary" package?

When you ask xrepo to install ninja, the registry hands you a package with a kind of binary rather than library. It has no header files, no shared object, no -l flags. Instead it ships executables — and Alien::Xrepo knows exactly where they live:

use v5.40;
use Alien::Xrepo;
use Config;

my $repo = Alien::Xrepo->new;
my $ninja = $repo->install('ninja');

# Where the executables live
my @bins = $ninja->bin_dir;
say 'ninja bin: ' . $bins[0];

# The full installation root (bin/, share/, etc.)
say 'root:      ' . $ninja->installdir;

# What kind of package is it?
say 'kind:      ' . $ninja->kind;    # "binary"

The key difference from a library package is that bin_dir points to a bin/ directory full of executables, not a lib/ directory full of .dll/.so files.

Running a binary tool

There are two common patterns for calling an installed tool from Perl.

Option A: run it directly

Resolve the exact executable name (.exe on Windows, bare name on Unix) inside bin_dir:

use Path::Tiny;

my ($exe) = grep { -e $_ }
            map  { path($_)->child($^O eq 'MSWin32' ? 'ninja.exe' : 'ninja') }
            $ninja->bin_dir;

say `$exe --version`;

Option B: put bin_dir on PATH

Prepend bin_dir to PATH so that every system call in this process (and every child it spawns) finds the tool:

local $ENV{PATH} = join $Config{path_sep}, $ninja->bin_dir, $ENV{PATH};

system 'ninja', '--version';   # resolves to the installed ninja

Option B is especially useful when a build tool (like meson) invokes another tool (like ninja) as a subprocess.

Libraries and binaries in the same store

You are free to install a library and a tool side by side — they share the same package store:

my $repo = Alien::Xrepo->new;
my $png  = $repo->install('libpng');    # kind: library
my $ninja = $repo->install('ninja');    # kind: binary

There is no conflict: libpng lives under lib/ and include/, while ninja lives under bin/. Both report their kind correctly so you can branch on it in your code:

for my $pkg ($png, $ninja) {
    if ($pkg->kind eq 'library') {
        say $pkg->libpath;            # shared object path
    } else {
        say $pkg->bin_dir;            # directory containing executables
    }
}

A practical example: build a C project against an installed library

If you are building a small C program and want to link it against libpng while driving the build with ninja, you can fetch the compiler flags from one and the build tool from the other:

use v5.40;
use Alien::Xrepo;
use Path::Tiny;
use Config;

my $repo = Alien::Xrepo->new;

# Libraries
$repo->install('libpng');
my $cflags  = $repo->fetch('libpng', undef, cflags  => 1);
my $ldflags = $repo->fetch('libpng', undef, ldflags => 1);

# Build tool
$repo->install('ninja');
local $ENV{PATH} = join $Config{path_sep}, $repo->install('ninja')->bin_dir, $ENV{PATH};

# Write a minimal build.ninja
my $src = path('prog.c')->absolute;
my $out = path('prog')->absolute;
path('build.ninja')->spew(<<NINJA);
build $out: link $src
  rule = gcc \$cflags \$in -o \$out \$ldflags
NINJA

system 'ninja';

No system-level apt install or brew install required — everything is resolved by Perl at runtime.

When is this useful?

Binary packages close the last mile: Perl now controls both what it links against and what drives the build.