Alien::...enAuthor(3)
π Alien::Build::Manual::AlienAuthor π
okay so bestie, this page lives in the User Contributed Perl Documentation family (that's the (3) vibe) and it's literally the whole guide to becoming an Alien author. no cap. π
β¨ NAME β¨
Alien::Build::Manual::AlienAuthor β Alien author documentation
β¨ VERSION β¨
version 2.88
β¨ SYNOPSIS β¨
perldoc Alien::Build::Manual::AlienAuthor
β¨ DESCRIPTION β¨
Note: bestie, please read this ENTIRE document before you even think about starting to write your own alienfile. the section on dynamic vs. static libraries will lowkey save you SO much grief if you read it now. i'm not being dramatic, i'm being correct. π
this doc exists to teach Alien authors how to build their own Alien distribution using Alien::Build and Alien::Base. and like, such an Alien distribution is made of three essential parts:
- An
alienfileβ this is the recipe for how to 1) detect an already-installed version of the library or tool you're alienizing 2) download and build the library or tool that you're alienizing, and 3) gather all the config settings you need for using that library or tool. - An installer
Makefile.PLorBuild.PLor adist.iniif you're usingDist::Zillaβ this is a thin little layer sitting between youralienfilerecipe and the Perl installer (which is eitherExtUtils::MakeMakerorModule::Build). - A Perl class (a
.pmfile) that inherits fromAlien::Baseβ for most Aliens you don't need to customize this at all, becauseAlien::Baseusually just does what you need. iconic behavior.
for example, if you were alienizing a library called libfoo, you might have these files:
Alien-Libfoo-1.00/Makefile.PL
Alien-Libfoo-1.00/alienfile
Alien-Libfoo-1.00/lib/Alien/Libfoo.pm
this document is mainly gonna focus on teaching you how to construct an alienfile, but we'll also briefly cover making a simple Makefile.PL or dist.ini to go along with it. and we'll touch on when you might want to extend your subclass to add non-standard functionality. π¦
πΈ Using commands πΈ
most software libraries and tools come with instructions for how to install them, in the form of commands you're supposed to type into a shell manually. the easiest way to automate those instructions is to just, like, put the commands in your alienfile. for example, let's suppose libfoo is built using autoconf and provides a pkg-config .pc file.
we're also gonna discuss plugins later. for common build systems like autoconf or CMake, it's usually better to use the appropriate plugin, because they handle corner cases way better than a simple set of commands. but we're looking at commands first because it's easier to understand the different phases with commands. iykyk.
(Aside, autoconf is a series of tools and macros used to configure (usually) a C or C++ library or tool by generating any number of Makefiles. it's the C equivalent toExtUtils::MakeMaker, if you will. basically, if your library or tool instructions start with'./configure'it's most likely an autoconf based library or tool).
(Aside2,pkg-configis a standard-ish way to provide the compiler and linker flags needed for compiling and linking against the library. if your tool installs a.pcfile, usually in$PREFIX/lib/pkgconfig, then your tool usespkg-config).
here's the alienfile you might have:
use alienfile;
probe [ 'pkg-config --exists libfoo' ];
share {
start_url 'http://www.libfoo.org/src/libfoo-1.00.tar.gz';
download [ 'wget %{.meta.start_url}' ];
extract [ 'tar zxf %{.install.download}' ];
build [
[ './configure --prefix=%{.install.prefix} --disable-shared' ],
[ '%{make}' ],
[ '%{make} install' ],
];
};
gather [
[ 'pkg-config --modversion libfoo', \'%{.runtime.version}' ],
[ 'pkg-config --cflags libfoo', \'%{.runtime.cflags}' ],
[ 'pkg-config --libs libfoo', \'%{.runtime.libs}' ],
];
there's a LOT going on here, so let's decode it a little bit. an alienfile is just some Perl with some alien-specific sugar sprinkled on top. the first line
use alienfile;
imports that sugar into the alienfile. it's also a little flag for whoever's reading, so they can see this is an alienfile and not some other kind of Perl script. π
the second line is the probe directive:
probe [ 'pkg-config --exists libfoo' ];
and it's used to check if the library is already installed on the target system. if pkg-config is in the path, and if libfoo is installed, this should exit with success (0) and tell Alien::Build to use the system library. if either pkg-config is in the PATH, or if libfoo isn't installed, then it'll exit with non-success (!= 0) and tell Alien::Build to download and build from source. π
you can provide as many probe directives as you want. this is useful if there are different ways to probe for the system. Alien::Build will stop on the first successfully found system library found. say our library libfoo comes with a .pc file for use with pkg-config and also provides a foo-config program to find the same values. you could then specify this in your alienfile:
probe [ 'pkg-config --exists libfoo' ];
probe [ 'foo-config --version' ];
other directives can be specified multiple times too, if there are different methods that can be tried for the various steps.
sometimes it's easier to probe for a library from Perl rather than with a command. for that you can use a code reference. for example, another way to call pkg-config would be from Perl:
probe sub {
my($build) = @_; # $build is the Alien::Build instance.
system 'pkg-config --exists libfoo';
$? == 0 ? 'system' : 'share';
};
the Perl code should return 'system' if the library is installed, and 'share' if it's not. (other directives should return a true value on success, and a false value on failure). you can also throw an exception with die to indicate a failure.
the next part of the alienfile is the share block, which is used to group the directives that get used to download and install the library or tool in the event that it isn't already installed.
share {
start_url 'http://www.libfoo.org/src/libfoo-1.00.tar.gz';
download [ 'wget %{.meta.start_url}' ];
extract [ 'tar zxf %{.install.download}' ];
build [
[ './configure --prefix=%{.install.prefix} --disable-shared' ],
[ '%{make}' ],
[ '%{make} install' ],
];
};
the start_url specifies where to find the package you're alienizing. it should be either a tarball (or zip file, or what have you) or an HTML index. the download directive, as you might imagine, specifies how to download the library or tool. the extract directive specifies how to extract the archive once it's downloaded. in the extract step, you can use the variable %{.install.download} as a placeholder for the archive that was downloaded in the download step. this is also accessible if you use a code reference from the Alien::Build instance:
share {
...
requires 'Archive::Extract';
extract sub {
my($build) = @_;
my $tarball = $build->install_prop->{download};
my $ae = Archive::Extract->new( archive => $tarball );
$ae->extract;
1;
}
...
};
the build directive specifies how to build the library or tool once it's been downloaded and extracted. note that the special variable %{.install.prefix} is the location where the library should be installed. %{make} is a helper that'll get replaced by the appropriate make, which may be called something different on some platforms (on Windows, for example, it frequently may be called nmake or dmake). π
the final part of the alienfile has a gather directive, which specifies how to get the details on how to compile and link against the library. for this, once again, we use the pkg-config command:
gather [
[ 'pkg-config --modversion libfoo', \'%{.runtime.version}' ],
[ 'pkg-config --cflags libfoo', \'%{.runtime.cflags}' ],
[ 'pkg-config --libs libfoo', \'%{.runtime.libs}' ],
];
the scalar reference as the final item in the command list tells Alien::Build that the output from the command should be stored in the given variable. the runtime variables are the ones that'll be available to Alien::Libfoo once it's installed. (install properties β which are the ones we've seen up till now β get thrown away once the Alien distribution is installed).
you can also provide a sys block for directives that should be used when a system install is detected. normally you only need to do this if the gather step is different between share and system installs. for example, the above is equivalent to:
build {
...
gather [
[ 'pkg-config --modversion libfoo', \'%{.runtime.version}' ],
[ 'pkg-config --cflags libfoo', \'%{.runtime.cflags}' ],
[ 'pkg-config --libs libfoo', \'%{.runtime.libs}' ],
];
};
sys {
gather [
[ 'pkg-config --modversion libfoo', \'%{.runtime.version}' ],
[ 'pkg-config --cflags libfoo', \'%{.runtime.cflags}' ],
[ 'pkg-config --libs libfoo', \'%{.runtime.libs}' ],
];
};
(Aside3, the reason it's calledsysand notsystemis so that it doesn't conflict with the built-insystemfunction)! π«Ά
π Using plugins π
the first example is a great way of showing the full manual path you can choose, but there's a LOT of repetition if you're doing many Aliens that use autoconf and pkg-config (which are, like, quite common). alienfile lets you use plugins. see Alien::Build::Plugin for a list of some of the plugin categories. β¨
for now, i'll just show you how to write the alienfile for libfoo above using Alien::Build::Plugin::Build::Autoconf, Alien::Build::Plugin::PkgConfig::Negotiate, Alien::Build::Plugin::Download::Negotiate, and Alien::Build::Plugin::Extract::Negotiate
use alienfile;
plugin 'PkgConfig' => (
pkg_name => 'libfoo',
);
share {
start_url 'http://www.libfoo.org/src';
plugin 'Download' => (
filter => qr/^libfoo-[0-9\.]+\.tar\.gz$/,
version => qr/^libfoo-([0-9\.]+)\.tar\.gz$/,
);
plugin 'Extract' => 'tar.gz';
plugin 'Build::Autoconf';
build [
'%{configure} --disable-shared',
'%{make}',
'%{make} install',
];
};
The first plugin we're reaching for is the pkg-config negotiation plugin. A negotiation plugin is the one that doesn't actually do the work β it just picks the bestie out of a whole set of plugins depending on your platform and your environment. (For Alien::Build::Plugin::PkgConfig::Negotiate specifically, it might choose command line tools, or the pure Perl implementation (PkgConfig), or libpkgconf, depending on what's available.) And when you're rolling with negotiation plugins you can literally just drop the ::Negotiate suffix, no cap. So yeah, using the plugin here is a whole advantage because it's way more reliable than just naming a command that might not even be installed. π
Next we use the download negotiation plugin. Also better than the version above, because again, wget may not be installed on the target system. Also you can specify a URL which will be scanned for links, and it'll use the most recent version. π₯
Then we use the Extract negotiation plugin, so we can use either command line tools or Perl libraries to extract from the archive once it's downloaded. π
Finally we use the Autoconf plugin (Alien::Build::Plugin::Build::Autoconf). This one is so much more sophisticated and reliable than the previous example, for a number of reasons. This version will even work on Windows β assuming the library or tool you're alienizing supports that platform! π
Strictly speaking the build directive is not necessary, because the autoconf plugin provides a default that's reasonable. The only reason you would want to include it is if you need to provide additional flags to the configure step.
share {
...
build [
'%{configure} --enable-bar --enable-baz --disable-shared',
'%{make}',
'%{make} install',
];
};
πΈ Multiple .pc files πΈ
Some packages come with multiple libraries paired with multiple .pc files. In that case you want to hand Alien::Build::Plugin::PkgConfig::Negotiate an array reference of package names, bestie. Like so:
plugin 'PkgConfig' => (
pkg_name => [ 'foo', 'bar', 'baz' ],
);
All packages must be found in order for the system install to succeed. Once installed, the first pkg_name will be used by default (in this example foo), and you can retrieve any other pkg_name using the Alien::Base alt method. β¨
π¦ A note about dynamic vs. static libraries π¦
If you're using your Alien to build an XS module, it is important that you use static libraries if possible. If you have a package that refuses to build a static library, then you can use Alien::Role::Dino.
Actually let me back up a minute. For a share install it is best to use static libraries to build your XS extension. Why? Because if your Alien is ever upgraded to a new version it can break your existing XS modules. π For a system install, shared libraries are usually best because you can often get security patches without having to re-build anything in perl land.
If you looked closely at the Using commands and Using plugins sections above, you may notice that we went out of our way where possible to tell Autotools to build only static libraries using the --disable-shared command. The Autoconf plugin also does this by default. π
Sometimes though you'll have a package that builds both, or maybe you want both static and dynamic libraries to work with XS and FFI. For that case, there is the Alien::Build::Plugin::Gather::IsolateDynamic plugin.
use alienfile;
...
plugin 'Gather::IsolateDynamic';
What it does is that it moves the dynamic libraries (usually .so on Unix and .DLL on Windows) to a place where they can be found by FFI, and where they won't be used by the compiler for building XS. It usually doesn't do any harm to include this plugin, so if you're just starting out you might want to add it anyway. Arguably it should have been the default behavior from the beginning. π
If you have already published an Alien that does not isolate its dynamic libraries, then you might get some fails from old upgraded aliens because the share directory isn't cleaned up by default (this is perhaps a design bug in the way that share directories work, but it is a long standing characteristic). One work around for this is to use the clean_install property on Alien::Build::MM, which will clean out the share directory on upgrade, and possibly save you a lot of grief. π«Ά
π Verifying and debugging your alienfile π
You could feed your alienfile directly into Alien::Build, or Alien::Build::MM, but it is sometimes useful to test your alienfile using the af command (it does not come with Alien::Build, you need to install App::af). By default af will use the alienfile in the current directory (just as make uses the Makefile in the current directory; just like make you can use the -f option to specify a different alienfile).
You can test your alienfile in dry run mode:
% af install --dry-run
Alien::Build::Plugin::Core::Legacy> adding legacy hash to config
Alien::Build::Plugin::Core::Gather> mkdir -p /tmp/I2YXRyxb0r/_alien
---
cflags: ''
cflags_static: ''
install_type: system
legacy:
finished_installing: 1
install_type: system
name: libfoo
original_prefix: /tmp/7RtAusykNN
version: 1.2.3
libs: '-lfoo '
libs_static: '-lfoo '
prefix: /tmp/7RtAusykNN
version: 1.2.3
You can use the --type option to force a share install (download and build from source):
% af install --type=share --dry-run
Alien::Build::Plugin::Core::Download> decoding html
Alien::Build::Plugin::Core::Download> candidate *https://www.libfoo.org/download/libfoo-1.2.4.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.3.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.2.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.1.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.0.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.9.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.8.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.7.tar.gz
Alien::Build::Plugin::Core::Download> candidate ...
Alien::Build::Plugin::Core::Download> setting version based on archive to 1.2.4
Alien::Build::Plugin::Core::Download> downloaded libfoo-1.2.4.tar.gz
Alien::Build::CommandSequence> + ./configure --prefix=/tmp/P22WEXj80r --with-pic --disable-shared
... snip ...
Alien::Build::Plugin::Core::Gather> mkdir -p /tmp/WsoLAQ889w/_alien
---
cflags: ''
cflags_static: ''
install_type: share
legacy:
finished_installing: 1
install_type: share
original_prefix: /tmp/P22WEXj80r
version: 1.2.4
libs: '-L/tmp/P22WEXj80r/lib -lfoo '
libs_static: '-L/tmp/P22WEXj80r/lib -lfoo '
prefix: /tmp/P22WEXj80r
version: 1.2.4
You can also use the --before and --after options to take a peek at what the build environment looks like at different stages as well, which can sometimes be useful:
% af install --dry-run --type=share --before build bash
Alien::Build::Plugin::Core::Download> decoding html
Alien::Build::Plugin::Core::Download> candidate *https://www.libfoo.org/download/libfoo-1.2.4.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.3.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.2.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.1.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.0.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.9.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.8.tar.gz
Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.7.tar.gz
Alien::Build::Plugin::Core::Download> candidate ...
Alien::Build::Plugin::Core::Download> setting version based on archive to 1.2.4
Alien::Build::Plugin::Core::Download> downloaded libfoo-1.2.4.tar.gz
App::af::install> [ before build ] + bash
/tmp/fbVPu4LRTs/build_5AVn/libfoo-1.2.4$ ls
CHANGES Makefile autoconf.ac lib
/tmp/fbVPu4LRTs/build_5AVn/libfoo-1.2.4$
There are a lot of other useful things that you can do with the af command. See af for details. β
π«Ά Integrating with MakeMaker π«Ά
Once you have a working alienfile you can write your Makefile.PL. Slay.
use ExtUtils::MakeMaker;
use Alien::Build::MM;
my $abmm = Alien::Build::MM->new;
WriteMakefile($abmm->mm_args(
ABSTRACT => 'Discover or download and install libfoo',
DISTNAME => 'Alien-Libfoo',
NAME => 'Alien::Libfoo',
VERSION_FROM => 'lib/Alien/Libfoo.pm',
CONFIGURE_REQUIRES => {
'Alien::Build::MM' => 0,
},
BUILD_REQUIRES => {
'Alien::Build::MM' => 0,
},
PREREQ_PM => {
'Alien::Base' => 0,
},
# If you are going to write the recommended
# tests you will will want these:
TEST_REQUIRES => {
'Test::Alien' => 0,
'Test2::V0' => 0,
},
));
sub MY::postamble {
$abmm->mm_postamble;
}
The lib/Alien/Libfoo.pm that goes along with it is very simple:
package Alien::Libfoo;
use strict;
use warnings;
use parent qw( Alien::Base );
1;
You are done and can install it normally:
% perl Makefile.PL
% make
% make test
% make install
π Integrating with Module::Build π
Please don't! Okay if you have to there is Alien::Build::MB. π
β¨ Non standard configuration β¨
Alien::Base support most of the things that your Alien will need, like compiler flags (cflags), linker flags (libs) and binary directory (bin_dir). Your library or tool may have other configuration items which are not supported by default. You can store the values in the alienfile into the runtime properties:
gather [
# standard:
[ 'foo-config --version libfoo', \'%{.runtime.version}' ],
[ 'foo-config --cflags libfoo', \'%{.runtime.cflags}' ],
[ 'foo-config --libs libfoo', \'%{.runtime.libs}' ],
# non-standard
[ 'foo-config --bar-baz libfoo', \'%{.runtime.bar_baz}' ],
];
then you can expose them in your Alien::Base subclass:
package Alien::Libfoo;
use strict;
use warnings;
use parent qw( Alien::Base );
sub bar_baz {
my($self) = @_;
$self->runtime_prop->{bar_baz},
};
1;
β¨ Testing β¨
(optional, but highly recommended β like, do it, bestie π«Ά)
Okay so you should totally write a test using Test::Alien to make sure your alien will work with any XS modules that are going to use it. Here's the tea:
use Test2::V0;
use Test::Alien;
use Alien::Libfoo;
alien_ok 'Alien::Libfoo';
xs_ok do { local $/; <DATA> }, with_subtest {
is Foo::something(), 1, 'Foo::something() returns 1';
};
done_testing;
__DATA__
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include <foo.h>
MODULE = Foo PACKAGE = Foo
int something()
You can also use Test::Alien to test tools instead of libraries, which is a whole mood:
use Test2::V0;
use Test::Alien;
use Alien::Libfoo;
alien_ok 'Alien::Libfoo';
run_ok(['foo', '--version'])
->exit_is(0);
done_testing;
And if your alien is gonna be used to write FFI bindings, you can write tests specifically for FFI::Platypus too. The test below is basically the FFI twin of the XS example above, no cap:
use Test2::V0;
use Test::Alien;
use Alien::Libfoo;
alien_ok 'Alien::Libfoo';
ffi_ok { symbols => [ 'something' ] }, with_subtest {
# $ffi is an instance of FFI::Platypus with the lib
# set appropriately.
my($ffi) = @_;
my $something = $ffi->function( something => [] => 'int' );
is $something->call(), 1, 'Foo::something() returns 1';
};
If you do use ffi_ok, you wanna make sure your alien reliably produces dynamic libraries. If it isn't consistent (if for example some platforms tend not to provide or build dynamic libraries), you can check that dynamic_libs doesn't return an empty list. Periodt.
...
alien_ok 'Alien::Libfoo';
SKIP: {
skip "This test requires a dynamic library"
unless Alien::Libfoo->dynamic_libs;
ffi_ok { symbols [ 'something' ] }, with_subtest {
...
};
}
More details on testing Alien modules can be found in the Test::Alien documentation, iykyk.
You can also run the tests that come with the package that you are alienizing, by using a "test" block in your alienfile. Keep in mind that some packages use testing tools or have other prerequisites that will not be available on your users machines when they attempt to install your alien. So you do not want to blindly add a test block without checking what the prereqs are. For Autoconf style packages you typically test a package using the "make check" command:
use alienfile;
plugin 'PkgConfig' => 'libfoo';
share {
... # standard build steps.
test [ '%{make} check' ];
};
π Dist::Zilla π
(optional, mildly recommended β like, it's cute if you do it π )
You can also use the Alien::Build Dist::Zilla plugin Dist::Zilla::Plugin::AlienBuild, which is a whole vibe:
name = Alien-Libfoo
author = E. Xavier Ample <example@cpan.org>
license = Perl_5
copyright_holder = E. Xavier Ample <example@cpan.org>
copyright_year = 2017
version = 0.01
[@Basic]
[AlienBuild]
The plugin takes care of a lot of details like making sure that the correct minimum versions of Alien::Build and Alien::Base are used. See the plugin documentation for additional details.
π Using your Alien π
Once you have installed you can use your Alien. See Alien::Build::Manual::AlienUser for guidance on that, bestie.
π SEE ALSO π
Alien::Build::Manualβ OtherAlien::Buildmanuals. π
π AUTHOR π
Author: Graham Ollis <plicease@cpan.org>
Contributors:
- Diab Jerius (DJERIUS)
- Roy Storey (KIWIROY)
- Ilya Pavlov
- David Mertens (run4flat)
- Mark Nunberg (mordy, mnunberg)
- Christian Walde (Mithaldu)
- Brian Wightman (MidLifeXis)
- Zaki Mughal (zmughal)
- mohawk (mohawk2, ETJ)
- Vikas N Kumar (vikasnkumar)
- Flavio Poletti (polettix)
- Salvador FandiΓ±o (salva)
- Gianni Ceccarelli (dakkar)
- Pavel Shaydo (zwon, trinitum)
- Kang-min Liu (εεΊ·ζ°, gugod)
- Nicholas Shipp (nshp)
- Juan JuliΓ‘n Merelo GuervΓ³s (JJ)
- Joel Berger (JBERGER)
- Petr PΓsaΕ (ppisar)
- Lance Wicks (LANCEW)
- Ahmad Fatoum (a3f, ATHREEF)
- JosΓ© JoaquΓn Atria (JJATRIA)
- Duke Leto (LETO)
- Shoichi Kaji (SKAJI)
- Shawn Laffan (SLAFFAN)
- Paul Evans (leonerd, PEVANS)
- HΓ₯kon HΓ¦gland (hakonhagland, HAKONH)
- nick nauwelaerts (INPHOBIA)
- Florian Weimer
- Marcel Telka (mtelka)
π COPYRIGHT AND LICENSE π
This software is copyright (c) 2011-2022 by Graham Ollis.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
That's a wrap! This page came from perl v5.42.3, dated 2026-09-21, and it's Alien::...enAuthor(3). Stay iconic, bestie! π β¨
π¦ where this page comes from
- fedora 43: perl-Alien-Build 2.88-1.fc43 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- fedora 44: perl-Alien-Build 2.88-1.fc44 Β· GPL-1.0-or-later OR Artistic-1.0-Perl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms