Initial Commit

This commit is contained in:
Riley Schneider
2025-12-03 16:38:10 +01:00
parent c5e26bf594
commit b732d8d4b5
17680 changed files with 5977495 additions and 2 deletions

View File

@@ -0,0 +1,133 @@
# PODNAME: Alien::Build::Manual::Alien
# ABSTRACT: General alien author documentation
# VERSION
__END__
=pod
=encoding UTF-8
=head1 NAME
Alien::Build::Manual::Alien - General alien author documentation
=head1 VERSION
version 2.38
=head1 SYNOPSIS
perldoc Alien::Build::Manual::Alien
=head1 DESCRIPTION
The goal of the L<Alien> namespace is to provide non-CPAN dependencies (so called "Alien" dependencies) for
CPAN modules. The history and intent of this idea is documented in the documentation-only L<Alien> module.
The C<Alien-Build> distribution provides a framework for building aliens. The intent is to fix bugs and
enhance the interface of a number of common tools so that all aliens may benefit. The distribution is broken
up into these parts:
=over 4
=item The Alien Installer (configure / build-time)
L<Alien::Build> and L<alienfile> are used to detect and install aliens. They are further documented in
L<Alien::Build::Manual::AlienAuthor>.
=item The Alien Runtime (runtime)
L<Alien::Base> is the base class for aliens in the C<Alien-Build> system. Its use by Alien consumers
is documented in L<Alien::Build::Manual::AlienUser>.
=item The Plugin system (configure / build-time)
Because many packages are implemented using different tools, the detection, build and install logic
for a particular L<Alien> can vary a lot. As such, much of L<Alien::Build> is implemented as a
series of plugins that inherit from L<Alien::Build::Plugin>. An overview of building your own
plugins is documented in L<Alien::Build::Manual::PluginAuthor>.
=back
Additional useful documentation may be found here:
=over 4
=item FAQ
L<Alien::Build::Manual::FAQ>
=item Contributing
L<Alien::Build::Manual::Contributing>
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
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 Pisar (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)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011-2020 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.
=cut

View File

@@ -0,0 +1,736 @@
# PODNAME: Alien::Build::Manual::AlienAuthor
# ABSTRACT: Alien author documentation
# VERSION
__END__
=pod
=encoding UTF-8
=head1 NAME
Alien::Build::Manual::AlienAuthor - Alien author documentation
=head1 VERSION
version 2.38
=head1 SYNOPSIS
perldoc Alien::Build::Manual::AlienAuthor
=head1 DESCRIPTION
B<Note>: Please read the entire document before you get started in
writing your own L<alienfile>. The section on dynamic vs. static
libraries will likely save you a lot of grief if you read it now!
This document is intended to teach L<Alien> authors how to build their
own L<Alien> distribution using L<Alien::Build> and L<Alien::Base>.
Such an L<Alien> distribution consists of three essential parts:
=over 4
=item An L<alienfile>
This is a recipe for how to 1) detect an already installed version of
the library or tool you are alienizing 2) download and build the library
or tool that you are alienizing and 3) gather the configuration settings
necessary for the use of that library or tool.
=item An installer C<Makefile.PL> or C<Build.PL> or a C<dist.ini> if you are using L<Dist::Zilla>
This is a thin layer between your L<alienfile> recipe, and the Perl
installer (either L<ExtUtils::MakeMaker> or L<Module::Build>.
=item A Perl class (.pm file) that inherits from L<Alien::Base>
For most L<Alien>s this does not need to be customized at all, since
L<Alien::Base> usually does what you need.
=back
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 will focus mainly on instructing you how to construct an
L<alienfile>, but we will also briefly cover making a simple
C<Makefile.PL> or C<dist.ini> to go along with it. We will also touch
on when you might want to extend your subclass to add non-standard
functionality.
=head2 Using commands
Most software libraries and tools will come with instructions for how to
install them in the form of commands that you are intended to type into
a shell manually. The easiest way to automate those instructions is to
just put the commands in your L<alienfile>. For example, lets suppose
that libfoo is built using autoconf and provides a C<pkg-config> C<.pc>
file.
We will also later discuss plugins. For common build systems like
autoconf or CMake, it is usually better to use the appropriate plugin
because they will handle corner cases better than a simple set of
commands. We're going to take a look at commands first because it's
easier to understand the different phases with commands.
(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 is the C equivalent to L<ExtUtils::MakeMaker>, if you
will. Basically, if your library or tool instructions start with
'./configure' it is most likely an autoconf based library or tool).
(Aside2, C<pkg-config> is a standard-ish way to provide the compiler and
linker flags needed for compiling and linking against the library. If
your tool installs a C<.pc> file, usually in C<$PREFIX/lib/pkgconfig>
then, your tool uses C<pkg-config>).
Here is the L<alienfile> that 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 is a lot going on here, so lets decode it a little bit. An
L<alienfile> is just some Perl with some alien specific sugar. The
first line
use alienfile;
imports the sugar into the L<alienfile>. It also is a flag for the
reader to see that this is an L<alienfile> and not some other kind of
Perl script.
The second line is the probe directive:
probe [ 'pkg-config --exists libfoo' ];
is used to see if the library is already installed on the target system.
If C<pkg-config> is in the path, and if libfoo is installed, this should
exit with a success (0) and tell L<Alien::Build> to use the system
library. If either C<pkg-config> in the PATH, or if libfoo is not
installed, then it will exist with non-success (!= 0) and tells
L<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. L<Alien::Build> will
stop on the first successfully found system library found. Say our
library libfoo comes with a C<.pc> file for use with C<pkg-config> and
also provides a C<foo-config> program to find the same values. You
could then specify this in your L<alienfile>
probe [ 'pkg-config --exists libfoo' ];
probe [ 'foo-config --version' ];
Other directives can be specified multiple times if there are different
methods that can be tried for the various steps.
Sometimes it is 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 C<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 not. (Other directives should return a true value on
success, and a false value). You can also throw an exception with
C<die> to indicate a failure.
The next part of the L<alienfile> is the C<share> block, which is used
to group the directives which are used to download and install the
library or tool in the event that it is not 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 that you are 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 is downloaded. In the extract step, you
can use the variable C<%{.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 L<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
has been downloaded and extracted. Note the special variable
C<%{.install.prefix}> is the location where the library should be
installed. C<%{make}> is a helper which will be replaced by the
appropriate C<make>, which may be called something different on some
platforms (on Windows for example, it frequently may be called C<nmake>
or C<dmake>).
The final part of the L<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 C<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
L<Alien::Build> that the output from the command should be stored in the
given variable. The runtime variables are the ones that will be
available to C<Alien::Libfoo> once it is installed. (Install
properties, which are the ones that we have seen up till now are thrown
away once the L<Alien> distribution is installed.
You can also provide a C<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 is called C<sys> and not C<system> is so that it
does not conflict with the built in C<system> function)!
=head2 Using plugins
The first example is a good way of showing the full manual path that you
can choose, but there is a lot of repetition, if you are doing many
L<Alien>s that use autoconf and C<pkg-config> (which are quite common.
L<alienfile> allows you to use plugins. See L<Alien::Build::Plugin> for
a list of some of the plugin categories.
For now, I will just show you how to write the L<alienfile> for libfoo
above using L<Alien::Build::Plugin::Build::Autoconf>,
L<Alien::Build::Plugin::PkgConfig::Negotiate>,
L<Alien::Build::Plugin::Download::Negotiate>, and
L<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 that we use is the C<pkg-config> negotiation plugin. A
negotiation plugin is one which doesn't do the actual work but selects
the best one from a set of plugins depending on your platform and
environment. (In the case of
L<Alien::Build::Plugin::PkgConfig::Negotiate>, it may choose to use
command line tools, a pure Perl implementation (L<PkgConfig>), or
libpkgconf, depending on what is available). When using negotiation
plugins you may omit the C<::Negotiate> suffix. So as you can see using
the plugin here is an advantage because it is more reliable than just
specifying a command which may not be installed!
Next we use the download negotiation plugin. This is also better than
the version above, because again, C<wget> my not be installed on the
target system. Also you can specify a URL which will be scanned for
links, and use the most recent version.
We use the Extract negotiation plugin to use either command line tools,
or Perl libraries to extract from the archive once it is downloaded.
Finally we use the Autoconf plugin
(L<Alien::Build::Plugin::Build::Autoconf>). This is a lot more
sophisticated and reliable than in the previous example, for a number of
reasons. This version will even work on Windows assuming the library or
tool you are alienizing supports that platform!
Strictly speaking the build directive is not necessary, because the
autoconf plugin provides a default which is reasonable. The only reason
that 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',
];
};
=head2 A note about dynamic vs. static libraries
If you are using your L<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 L<Alien::Role::Dino>.
Actually let me back up a minute. For a C<share> install it is best
to use static libraries to build your XS extension. This is because
if your L<Alien> is ever upgraded to a new version it can break your
existing XS modules. For a C<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
C<--disable-shared> command. The Autoconf plugin also does this by
default.
Sometimes though you will have a package that builds both, or maybe
you I<want> both static and dynamic libraries to work with XS and FFI.
For that case, there is the L<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 are 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 C<clean_install> property on L<Alien::Build::MM>, which will
clean out the share directory on upgrade, and possibly save you a lot
of grief.
=head2 Verifying and debugging your alienfile
You could feed your alienfile directly into L<Alien::Build>, or
L<Alien::Build::MM>, but it is sometimes useful to test your alienfile
using the C<af> command (it does not come with L<Alien::Build>, you need
to install L<App::af>). By default C<af> will use the C<alienfile> in
the current directory (just as C<make> uses the C<Makefile> in the
current directory; just like C<make> you can use the C<-f> option to
specify a different L<alienfile>).
You can test your L<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 C<--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 C<--before> and C<--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 C<af>
command. See L<af> for details.
=head2 Integrating with MakeMaker
Once you have a working L<alienfile> you can write your C<Makefile.PL>.
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 C<lib/Alien/Libfoo.pm> that goes along with it is very simple:
package Alien::Libfoo;
use strict;
use warnings;
use base qw( Alien::Base );
1;
You are done and can install it normally:
% perl Makefile.PL
% make
% make test
% make install
=head2 Integrating with Module::Build
Please don't! Okay if you have to there is L<Alien::Build::MB>.
=head2 Non standard configuration
L<Alien::Base> support most of the things that your L<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
L<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 L<Alien::Base> subclass:
package Alien::Libfoo;
use strict;
use warnings;
use base qw( Alien::Base );
sub bar_baz {
my($self) = @_;
$self->runtime_prop->{bar_baz},
};
1;
=head2 Testing
(optional, but highly recommended)
You should write a test using L<Test::Alien> to make sure that your
alien will work with any XS modules that are going to use it:
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 L<Test::Alien> to test tools instead of libraries:
use Test2::V0;
use Test::Alien;
use Alien::Libfoo;
alien_ok 'Alien::Libfoo';
run_ok(['foo', '--version'])
->exit_is(0);
done_testing;
You can also write tests specifically for L<FFI::Platypus>, if your
alien is going to be used to write FFI bindings. (the test below
is the FFI equivalent to the XS example above).
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 C<ffi_ok> you want to make sure that 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 C<dynamic_libs> doesn't return an empty list.
...
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 L<Alien> modules can be found in the
L<Test::Alien> documentation.
You can also run the tests that come with the package that you are alienizing,
by using a C<test> block in your L<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 C<make check> command:
use alienfile;
plugin 'PkgConfig' => 'libfoo';
share {
... # standard build steps.
test [ '%{make} check' ];
};
=head2 Dist::Zilla
(optional, mildly recommended)
You can also use the L<Alien::Build> L<Dist::Zilla> plugin
L<Dist::Zilla::Plugin::AlienBuild>:
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 L<Alien::Build> and L<Alien::Base> are used.
See the plugin documentation for additional details.
=head2 Using your Alien
Once you have installed you can use your Alien. See
L<Alien::Build::Manual::AlienUser> for guidance on that.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
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 Pisar (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)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011-2020 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.
=cut

View File

@@ -0,0 +1,263 @@
# PODNAME: Alien::Build::Manual::AlienUser
# ABSTRACT: Alien user documentation
# VERSION
__END__
=pod
=encoding UTF-8
=head1 NAME
Alien::Build::Manual::AlienUser - Alien user documentation
=head1 VERSION
version 2.38
=head1 SYNOPSIS
perldoc Alien::Build::Manual::AlienUser
=head1 DESCRIPTION
This document is intended for a user of an L<Alien::Base> based L<Alien>
module's user. Although specifically geared for L<Alien::Base>
subclasses, it may have some useful hints for L<Alien> in general.
Full working examples of how to use an L<Alien> module are also bundled
with L<Alien::Build> in the distribution's C<example/user> directory.
Those examples use L<Alien::xz>, which uses L<alienfile> + L<Alien::Build>
+ L<Alien::Base>.
The following documentation will assume you are trying to use an L<Alien>
called C<Alien::Foo> which provides the library C<libfoo> and the command
line tool C<foo>. Many L<Alien>s will only provide one or the other.
The best interface to use for using L<Alien::Base> based aliens is
L<Alien::Base::Wrapper>. This allows you to combine multiple aliens together
and handles a number of corner obscure corner cases that using L<Alien>s
directly does not. Also as of 0.64, L<Alien::Base::Wrapper> comes bundled
with L<Alien::Build> and L<Alien::Base> anyway, so it is not an extra
dependency.
What follows are the main use cases.
=head2 ExtUtils::MakeMaker
use ExtUtils::MakeMaker;
use Alien::Base::Wrapper ();
WriteMakefile(
Alien::Base::Wrapper->new('Alien::Foo')->mm_args2(
NAME => 'FOO::XS',
...
),
);
L<Alien::Base::Wrapper> will take a hash of C<WriteMakefile> arguments
and insert the appropriate compiler and linker flags for you. This
is recommended over doing this yourself as the exact incantation to
get EUMM to work is tricky to get right.
The C<mm_args2> method will also set your C<CONFIGURE_REQUIRES> for
L<Alien::Base::Wrapper>, L<ExtUtils::MakeMaker> and any aliens that
you specify.
=head2 Module::Build
use Module::Build;
use Alien::Base::Wrapper qw( Alien::Foo !export );
use Alien::Foo;
my $build = Module::Build->new(
...
configure_requires => {
'Alien::Base::Wrapper' => '0',
'Alien::Foo' => '0',
...
},
Alien::Base::Wrapper->mb_args,
...
);
$build->create_build_script;
For L<Module::Build> you can also use L<Alien::Base::Wrapper>, but
you will have to specify the C<configure_requires> yourself.
=head2 Inline::C / Inline::CPP
use Inline 0.56 with => 'Alien::Foo';
L<Inline::C> and L<Inline::CPP> can be configured
to use an L<Alien::Base> based L<Alien> with the C<with> keyword.
=head2 ExtUtils::Depends
use ExtUtils::MakeMaker;
use ExtUtils::Depends;
my $pkg = ExtUtils::Depends->new("Alien::Foo");
WriteMakefile(
...
$pkg->get_makefile_vars,
...
);
L<ExtUtils::Depends> works similar to L<Alien::Base::Wrapper>, but uses
the L<Inline> interface under the covers.
=head2 Dist::Zilla
[@Filter]
-bundle = @Basic
-remove = MakeMaker
[Prereqs / ConfigureRequires]
Alien::Foo = 0
[MakeMaker::Awesome]
header = use Alien::Base::Wrapper qw( Alien::Foo !export );
WriteMakefile_arg = Alien::Base::Wrapper->mm_args
=head2 FFI::Platypus
use FFI::Platypus;
use Alien::Foo;
my $ffi = FFI::Platypus->new(
lib => [ Alien::Foo->dynamic_libs ],
);
Not all L<Alien>s provide dynamic libraries, but those that do can be
used by L<FFI::Platypus>. Unlike an XS module, these
need to be a regular run time prerequisite.
=head2 Inline::C
use Inline with => 'Alien::Foo';
use Inline C => <<~'END';
#include <foo.h>
const char *my_foo_wrapper()
{
foo();
}
END
sub exported_foo()
{
my_foo_wrapper();
}
=head2 tool
use Alien::Foo;
use Env qw( @PATH );
unshift @PATH, Alien::Foo->bin_dir;
system 'foo', '--bar', '--baz';
Some L<Alien>s provide tools instead of or in addition to a library.
You need to add them to the C<PATH> environment variable though.
(Unless the tool is already provided by the system, in which case
it is already in the path and the C<bin_dir> method will return an
empty list).
=head1 ENVIRONMENT
=over 4
=item ALIEN_INSTALL_TYPE
Although the recommended way for a consumer to use an L<Alien::Base> based L<Alien>
is to declare it as a static configure and build-time dependency, some consumers
may prefer to fallback on using an L<Alien> only when the consumer itself cannot
detect the necessary package. In some cases the consumer may want the user to opt-in
to using an L<Alien> before requiring it.
To keep the interface consistent among Aliens, the consumer of the fallback opt-in
L<Alien> may fallback on the L<Alien> if the environment variable C<ALIEN_INSTALL_TYPE>
is set to any value. The rationale is that by setting this environment variable the
user is aware that L<Alien> modules may be installed and have indicated consent.
The actual implementation of this, by its nature would have to be in the consuming
CPAN module.
This behavior should be documented in the consumer's POD.
See L<Alien::Build/ENVIRONMENT> for more details on the usage of this environment
variable.
=back
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
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 Pisar (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)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011-2020 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.
=cut

View File

@@ -0,0 +1,263 @@
# PODNAME: Alien::Build::Manual::Contributing
# ABSTRACT: Over-detailed contributing guide
# VERSION
__END__
=pod
=encoding UTF-8
=head1 NAME
Alien::Build::Manual::Contributing - Over-detailed contributing guide
=head1 VERSION
version 2.38
=head1 SYNOPSIS
perldoc Alien::Build::Manual::Contributing
=head1 DESCRIPTION
Thank you for considering to contribute to my open source project! If
you have a small patch please consider just submitting it. Doing so
through the project GitHub is probably the best way:
L<https://github.com/plicease/Alien-Build/issues>
If you have a more invasive enhancement or bugfix to contribute, please
take the time to review these guidelines. In general it is good idea to
work closely with the L<Alien::Build> developers, and the best way to
contact them is on the C<#native> IRC channel on irc.perl.org.
=head2 History
Joel Berger wrote the original L<Alien::Base>. This distribution
included the runtime code L<Alien::Base> and an installer class
L<Alien::Base::ModuleBuild>. The significant thing about L<Alien::Base>
was that it provided tools to make it relatively easy for people to roll
their own L<Alien> distributions. Over time, the PerlAlien (github
organization) or "Alien::Base team" has taken over development of
L<Alien::Base> with myself (Graham Ollis) being responsible for
integration and releases. Joel Berger is still involved in the project.
Since the original development of L<Alien::Base>, L<Module::Build>, on
which L<Alien::Base::ModuleBuild> is based, has been removed from the
core of Perl. It seemed worthwhile to write a replacement installer
that works with L<ExtUtils::MakeMaker> which IS still bundled with the
Perl core. Because this is a significant undertaking it is my intention
to integrate the many lessons learned by Joel Berger, myself and the
"Alien::Base team" as possible. If the interface seems good then it is
because I've stolen the ideas from some pretty good places.
=head2 Philosophy
=head3 Alien runtime should be as config-only as possible.
Ideally the code for an L<Alien::Base> based L<Alien> should simply
inherit from L<Alien::Base>, like so:
package Alien::libfoo;
use base qw( Alien::Base );
1;
The detection logic should be done by the installer code (L<alienfile>
and L<Alien::Build>) and saved into runtime properties (see
L<Alien::Build/runtime_prop>). And as much as
possible the runtime should be implemented in the base class (L<Alien::Base>).
Where reasonable, the base class should be expanded to meet the needs
of this arrangement.
=head3 when downloading a package grab the latest version
If the maintainer of an L<Alien> disappears for a while, and if the
version downloaded during a "share" install is hardcoded in the
L<alienfile>, it can be problematic for end-users.
There are exceptions, of course, in particular when a package provides
a very unstable interface from version to version it makes sense
to hard code the version and for the Alien developer and Alien consumer
developer to coordinate closely.
=head3 when installing a package the operating system as a whole should not be affected
The convenience of using an L<Alien> is that a user of a CPAN module
that consumes an L<Alien> doesn't need to know the exact incantation
to install the libraries on which it depends (or indeed it may not be
easily installed through the package manager anyway).
As a corollary, a user of a CPAN module that consumes an L<Alien>
module shouldn't expect operating system level packages to be
installed, or for these packages to be installed in common system
level directories, like C</usr/local> or C</opt>. Instead a "share"
directory associated with the Perl install and L<Alien> module
should be used.
Plugins that require user opt-in could be written to prompt a user
to automatically install operating system packages, but this should
never be done by default or without consent by the user.
=head3 avoid dependencies
One of the challenges with L<Alien> development is that you are by the
nature of the problem, trying to make everyone happy. Developers
working out of CPAN just want stuff to work, and some build environments
can be hostile in terms of tool availability, so for reliability you end
up pulling a lot of dependencies. On the other hand, operating system
vendors who are building Perl modules usually want to use the system
version of a library so that they do not have to patch libraries in
multiple places. Such vendors have to package any extra dependencies
and having to do so for packages that the don't even use makes them
understandably unhappy.
As general policy the L<Alien::Build> core should have as few
dependencies as possible, and should only pull extra dependencies if
they are needed. Where dependencies cannot be avoidable, popular and
reliable CPAN modules, which are already available as packages in the
major Linux vendors (Debian, Red Hat) should be preferred.
As such L<Alien::Build> is hyper aggressive at using dynamic
prerequisites.
=head3 interface agnostic
One of the challenges with L<Alien::Buil::ModuleBuild> was that
L<Module::Build> was pulled from the core. In addition, there is a
degree of hostility toward L<Module::Build> in some corners of the Perl
community. I agree with Joel Berger's rationale for choosing
L<Module::Build> at the time, as I believe its interface more easily
lends itself to building L<Alien> distributions.
That said, an important feature of L<Alien::Build> is that it is
installer agnostic. Although it is initially designed to work with
L<ExtUtils::MakeMaker>, it has been designed from the ground up to work
with any installer (Perl, or otherwise).
As an extension of this, although L<Alien::Build> may have external CPAN
dependencies, they should not be exposed to developers USING
L<Alien::Build>. As an example, L<Path::Tiny> is used heavily
internally because it does what L<File::Spec> does, plus the things that
it doesn't, and uses forward slashes on Windows (backslashes are the
"correct separator on windows, but actually using them tends to break
everything). However, there aren't any interfaces in L<Alien::Build>
that will return a L<Path::Tiny> object (or if there are, then this is a
bug).
This means that if we ever need to port L<Alien::Build> to a platform
that doesn't support L<Path::Tiny> (such as VMS), then it may require
some work to L<Alien::Build> itself, modules that USE L<Alien::Build>
shouldn't need to be modified.
=head3 plugable
The actual logic that probes the system, downloads source and builds it
should be as pluggable as possible. One of the challenges with
L<Alien::Build::ModuleBuild> was that it was designed to work well with
software that works with C<autoconf> and C<pkg-config>. While you can
build with other tools, you have to know a bit of how the installer
logic works, and which hooks need to be tweaked.
L<Alien::Build> has plugins for C<autoconf>, C<pkgconf> (successor of
C<pkg-config>), vanilla Makefiles, and CMake. If your build system
doesn't have a plugin, then all you have to do is write one! Plugins
that prove their worth may be merged into the L<Alien::Build> core.
Plugins that after a while feel like maybe not such a good idea may be
removed from the core, or even from CPAN itself.
In addition, L<Alien::Build> has a special type of plugin, called a
negotiator which picks the best plugin for the particular environment
that it is running in. This way, as development of the negotiator and
plugins develop over time modules that use L<Alien::Build> will benefit,
without having to change the way they interface with L<Alien::Build>
=head1 ACKNOWLEDGEMENT
I would like to that Joel Berger for getting things running in the first
place. Also important to thank other members of the "Alien::Base team":
Zaki Mughal (SIVOAIS)
Ed J (ETJ, mohawk)
Also kind thanks to all of the developers who have contributed to
L<Alien::Base> over the years:
L<https://metacpan.org/pod/Alien::Base#CONTRIBUTORS>
=head1 SEE ALSO
L<alienfile>, L<Alien::Build::MM>, L<Alien::Build::Plugin>, L<Alien::Base>, L<Alien>
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
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 Pisar (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)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011-2020 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.
=cut

View File

@@ -0,0 +1,544 @@
# PODNAME: Alien::Build::Manual::FAQ
# ABSTRACT: Frequently Asked Questions about Alien::Build
# VERSION
__END__
=pod
=encoding UTF-8
=head1 NAME
Alien::Build::Manual::FAQ - Frequently Asked Questions about Alien::Build
=head1 VERSION
version 2.38
=head1 SYNOPSIS
perldoc Alien::Build::Manual::FAQ
=head1 DESCRIPTION
This document serves to answer the most frequently asked questions made by developers
creating L<Alien> modules using L<Alien::Build>.
=head1 QUESTIONS
=head2 What is Alien, Alien::Base and Alien::Build?
Alien in a Perl namespace for defining dependencies in CPAN for libraries and tools which
are not "native" to CPAN. For a manifesto style description of the Why, and How see
L<Alien>. L<Alien::Base> is a base class for the L<Alien> runtime. L<Alien::Build> is
a tool for probing the operating system for existing libraries and tools, and downloading, building
and installing packages. L<alienfile> is a recipe format for describing how to probe,
download, build and install a package.
=head2 How do I build a package that uses I<build system>
=head3 autoconf
Use the autoconf plugin (L<Alien::Build::Plugin::Build::Autoconf>). If your package
provides a pkg-config C<.pc> file, then you can also use the PkgConfig plugin
(L<Alien::Build::Plugin::PkgConfig::Negotiate>).
use alienfile
plugin PkgConfig => 'libfoo';
share {
start_url => 'http://example.org/dist';
plugin Download => (
version => qr/libfoo-([0-9\.])\.tar\.gz$/,
);
plugin Extract => 'tar.gz';
plugin 'Build::Autoconf';
};
If you need to provide custom flags to configure, you can do that too:
share {
plugin 'Build::Autoconf';
build [
'%{configure} --disable-shared --enable-foo',
'%{make}',
'%{make} install',
];
};
If your package requires GNU Make, use C<%{gmake}> instead of C<%{make}>.
=head3 autoconf-like
If you see an error like this:
Unknown option "--with-pic".
It is because the autoconf plugin uses the C<--with-pic> option by default, since
it makes sense most of the time, and autoconf usually ignores options that it does
not recognize. Some autoconf style build systems fail when they see an option that
they do not recognize. You can turn this behavior off for these packages:
plugin 'Build::Autoconf' => (
with_pic => 0,
);
Another thing about the autoconf plugin is that it uses C<DESTDIR> to do a double
staged install. If you see an error like "nothing was installed into destdir", that
means that your package does not support C<DESTDIR>. You should instead use the
MSYS plugin and use a command sequence to do the build like this:
share {
plugin 'Build::MSYS';
build [
# explicitly running configure with "sh" will make sure that
# it works on windows as well as UNIX.
'sh configure --prefix=%{.install.prefix} --disable-shared',
'%{make}',
'%{make} install',
];
};
=head3 CMake
There is an alien L<Alien::cmake3> that provides C<cmake> 3.x or better (It is preferred to the
older L<Alien::CMake>). Though it is recommended that you use the C<cmake>
(L<Alien::Build::Plugin::Build::CMake>) plugin instead of using L<Alien::cmake3>.
use alienfile;
share {
plugin 'Build::CMake';
build [
# this is the default build step, if you do not specify one.
[ '%{cmake}',
@{ meta->prop->{plugin_build_cmake}->{args} },
# ... put extra cmake args here ...
'.'
],
'%{make}',
'%{make} install',
];
};
=head3 vanilla Makefiles
L<Alien::Build> provides a helper (C<%{make}>) for the C<make> that is used by Perl and
L<ExtUtils::MakeMaker> (EUMM). Unfortunately the C<make> supported by Perl and EUMM on
Windows (C<nmake> and C<dmake>) are not widely supported by most open source projects.
(Thankfully recent perls and EUMM support GNU Make on windows now).
You can use the C<make> plugin (L<Alien::Build::Plugin::Build::Make>) to tell the
L<Alien::Build> system know which make the project that you are alienizing requires.
plugin 'Build::Make' => 'umake';
# umake makes %{make} either GNU Make or BSD Make on Unix and GNU Make on Windows.
build {
build [
# You can use the Perl config compiler and cflags using the %{perl.config...} helper
[ '%{make}', 'CC=%{perl.config.cc}', 'CFLAGS=%{perl.config.cccdlflags} %{perl.config.optimize}' ],
[ '%{make}', 'install', 'PREFIX=%{.install.prefix}' ],
],
};
Some open source projects require GNU Make, and you can specify that, and L<Alien::gmake>
will be pulled in on platforms that do not already have it.
plugin 'Build::Make' => 'gmake';
...
=head2 How do I probe for a package that uses pkg-config
Use the C<pkg-config> plugin (L<Alien::Build::Plugin::PkgConfig::Negotiate>):
use alienfile;
plugin 'PkgConfig' => (
pkg_name => 'libfoo',
);
It will probe for a system version of the library. It will also add the appropriate C<version>
C<cflags> and C<libs> properties on either a C<system> or C<share> install.
=head2 How do I specify a minimum or exact version requirement for packages that use pkg-config?
The various pkg-config plugins all support atleast_version, exact_version and maximum_version
fields, which have the same meaning as the C<pkg-config> command line interface:
use alienfile;
plugin 'PkgConfig', pkg_name => foo, atleast_version => '1.2.3';
or
use alienfile;
plugin 'PkgConfig', pkg_name => foo, exact_version => '1.2.3';
=head2 How to create an Alien module for a packages that do not support pkg-config?
=head3 Packages that provide a configuration script
Many packages provide a command that you can use to get the appropriate version, compiler
and linker flags. For those packages you can just use the commands in your L<alienfile>.
Something like this:
use alienfile;
probe [ 'foo-config --version' ];
share {
...
build [
'%{make} PREFIX=%{.runtime.prefix}',
'%{amek} install PREFIX=%{.runtime.prefix}',
];
};
gather [
[ 'foo-config', '--version', \'%{.runtime.version}' ],
[ 'foo-config', '--cflags', \'%{.runtime.cflags}' ],
[ 'foo-config', '--libs', \'%{.runtime.libs}' ],
];
=head3 Packages that require a compile test
Some packages just expect you do know that C<-lfoo> will work. For those you can use
the C<cbuilder> plugin (L<Alien::Build::Plugin::Probe::CBuilder>.
use alienfile;
plugin 'Probe::CBuilder' => (
cflags => '-I/opt/libfoo/include',
libs => '-L/opt/libfoo/lib -lfoo',
);
share {
...
gather sub {
my($build) = @_;
my $prefix = $build->runtime_prop->{prefix};
$build->runtime_prop->{cflags} = "-I$prefix/include ";
$build->runtime_prop->{libs} = "-L$prefix/lib -lfoo ";
};
}
This plugin will build a small program with these flags and test that it works. (There
are also options to provide a program that can make simple tests to ensure the library
works). If the probe works, it will set the compiler and linker flags. (There are also
options for extracting the version from the test program). If you do a share install
you will need to set the compiler and linker flags yourself in the gather step, if you
aren't using a build plugin that will do that for you.
=head2 Can/Should I write a tool oriented Alien module?
Certainly. The original intent was to provide libraries, but tools are also quite doable using
the L<Alien::Build> toolset. A good example of how to do this is L<Alien::nasm>. You will want
to use the 'Probe::CommandLine':
use alienfile;
plugin 'Probe::CommandLine' => (
command => 'gzip',
);
=head2 How do I test my package once it is built (before it is installed)?
Use L<Test::Alien>. It has extensive documentation, and integrates nicely with L<Alien::Base>.
=head2 How do I patch packages that need alterations?
If you have a diff file you can use patch:
use alienfile;
probe sub { 'share' }; # replace with appropriate probe
share {
...
patch [ '%{patch} -p1 < %{.install.patch}/mypatch.diff' ];
build [ ... ] ;
}
...
You can also patch using Perl if that is easier:
use alienfile;
probe sub { 'share' };
share {
...
patch sub {
my($build) = @_;
# make changes to source prior to build
};
build [ ... ];
};
=head2 The flags that a plugin produces are wrong!
Sometimes, the compiler or linker flags that the PkgConfig plugin comes up with are not quite
right. (Frequently this is actually because a package maintainer is providing a broken
C<.pc> file). (Other plugins may also have problems). You could replace the plugin's C<gather> step
but a better way is to provide a subroutine callback to be called after the gather stage
is complete. You can do this with the L<alienfile> C<after> directive:
use alienfile;
plugin 'PlgConfig' => 'libfoo';
share {
...
after 'gather' => sub {
my($build) = @_;
$build->runtime_prop->{libs} .= " -lbar"; # libfoo also requires libbar
$build->runtime_prop->{libs_static} .= " -lbar -lbaz"; # libfoo also requires libbaz under static linkage
};
};
Sometimes you only need to do this on certain platforms. You can adjust the logic based on C<$^O>
appropriately.
use alienfile;
plugin 'PlgConfig' => 'libfoo';
share {
...
after 'gather' => sub {
my($build) = @_;
if($^O eq 'MSWin32') {
$build->runtime_prop->{libs} .= " -lpsapi";
}
};
};
=head2 "cannot open shared object file" trying to load XS
The error looks something like this:
t/acme_alien_dontpanic2.t ....... 1/?
# Failed test 'xs'
# at t/acme_alien_dontpanic2.t line 13.
# XSLoader failed
# Can't load '/home/cip/.cpanm/work/1581635869.456/Acme-Alien-DontPanic2-2.0401/_alien/tmp/test-alien-lyiQNX/auto/Test/Alien/XS/ Mod0/Mod0.so' for module Test::Alien::XS::Mod0: libdontpanic.so.0: cannot open shared object file: No such file or directory at /opt /perl/5.30.1/lib/5.30.1/x86_64-linux/DynaLoader.pm line 193.
# at /home/cip/perl5/lib/perl5/Test/Alien.pm line 414.
# Compilation failed in require at /home/cip/perl5/lib/perl5/Test/Alien.pm line 414.
# BEGIN failed--compilation aborted at /home/cip/perl5/lib/perl5/Test/Alien.pm line 414.
t/acme_alien_dontpanic2.t ....... Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/6 subtests
t/acme_alien_dontpanic2__ffi.t .. ok
This error happened at test time for the Alien, but depending on your environment and Alien it might
happen later and the actual diagnostic wording might vary.
This is usually because your XS or Alien tries to use dynamic libraries instead of dynamic libraries.
Please consult the section about dynamic vs. static libraries in L<Alien::Build::Manual::AlienAuthor>.
The TL;DR is that L<Alien::Build::Plugin::Gather::IsolateDynamic> might help.
If you are the Alien author and the package you are alienizing doesn't have a static option you can
use L<Alien::Role::Dino>, but please note the extended set of caveats!
=head2 599 Internal Exception errors downloading packages from the internet
Alien::Build::Plugin::Fetch::HTTPTiny> 599 Internal Exception fetching http://dist.libuv.org/dist/v1.15.0
Alien::Build::Plugin::Fetch::HTTPTiny> exception: IO::Socket::SSL 1.42 must be installed for https support
Alien::Build::Plugin::Fetch::HTTPTiny> exception: Net::SSLeay 1.49 must be installed for https support
Alien::Build::Plugin::Fetch::HTTPTiny> An attempt at a SSL URL https was made, but your HTTP::Tiny does not appear to be able to use https.
Alien::Build::Plugin::Fetch::HTTPTiny> Please see: https://metacpan.org/pod/Alien::Build::Manual::FAQ#599-Internal-Exception-errors-downloading-packages-from-the-internet
error fetching http://dist.libuv.org/dist/v1.15.0: 599 Internal Exception at /Users/ollisg/.perlbrew/libs/perl-5.26.0@test1/lib/perl5/Alien/Build/Plugin/Fetch/HTTPTiny.pm line 68.
(Older versions of L<Alien::Build> produced a less verbose more confusing version of this diagnostic).
TL;DR, instead of this:
share {
start_url => 'http://example.org/dist';
...
};
do this:
share {
start_url => 'https://example.org/dist';
};
If the website is going to redirect to a secure URL anyway.
The "599 Internal Exception" indicates an "internal" exception from L<HTTP::Tiny> and is not a real
HTTP status code or error. This could mean a number of different problems, but most frequently
indicates that a SSL request was made without the required modules (L<Net::SSLeay> and
L<IO::Socket::SSL>). Normally the L<Alien::Build::Plugin::Download::Negotiate>
and L<Alien::Build::Plugin::Fetch::HTTPTiny> will make sure that the appropriate modules are added
to your prerequisites for you if you specify a C<https> URL. Some websites allow an initial request
from C<http> but then redirect to C<https>. If you can it is better to specify C<https>, if you
cannot, then you can instead use the C<ssl> property on either of those two plugins.
=head2 Network fetch is turned off
If you get an error like this:
Alien::Build> install type share requested or detected, but network fetch is turned off
Alien::Build> see see https://metacpan.org/pod/Alien::Build::Manual::FAQ#Network-fetch-is-turned-off
This is because your environment is setup not to install aliens that require the network. You
can turn network fetch back on by setting C<ALIEN_INSTALL_NETWORK> to true, or by unsetting it.
This environment variable is designed for environments that don't ever want to install aliens that
require downloading source packages over the internet.
=head2 I would really prefer you not download stuff off the internet
The idea of L<Alien> is to download missing packages and build them automatically to make installing
easier. Some people may not like this, or may even have security requirements that they not download
random package over the internet (caveat, downloading random stuff off of CPAN may not be any safer,
so make sure you audit all of the open source software that you use appropriately). Another reason
you may not want to download from the internet is if you are packaging up an alien for an operating
system vendor, which will always want to use the system version of a library. In that situation you
don't want L<Alien::Build> to go off and download something from the internet because the probe failed
for some reason.
This is easy to take care of, simply set C<ALIEN_INSTALL_TYPE> to C<system> and a build from source
code will never be attempted. On systems that do not provide system versions of the library or tool
you will get an error, allowing you to install the library, and retry the alien install. You can
also set the environment variable on just some aliens.
% export ALIEN_INSTALL_TYPE=system # for everyone
% env ALIEN_INSTALL_TYPE=system cpanm -v Alien::libfoo
=head2 For testing I would like to test both system and share installs!
You can use the C<ALIEN_INSTALL_TYPE> environment variable. It will force either a C<share> or
C<system> install depending on how it is set. For travis you can do something like this:
env:
matrix:
- ALIEN_INSTALL_TYPE=share
- ALIEN_INSTALL_TYPE=system
=head2 How do I use Alien::Build from Dist::Zilla?
For creating L<Alien::Base> and L<Alien::Build> based dist from L<Dist::Zilla> you can use the
dzil plugin L<Dist::Zilla::Plugin::AlienBuild>.
=head2 Cannot find either a share directory or a ConfigData module
If you see an error like this:
Cannot find either a share directory or a ConfigData module for Alien::libfoo.
(Alien::libfoo loaded from lib/Alien/libfoo.pm)
Please see https://metacpan.org/pod/distribution/Alien-Build/lib/Alien/Build/Manual/FAQ.pod#Cannot-find-either-a-share-directory-or-a-ConfigData-module
Can't locate Alien/libfoo/ConfigData.pm in @INC (you may need to install the Alien::libfoo::ConfigData module) (@INC contains: ...)
it means you are trying to use an Alien that hasn't been properly installed. An L<Alien::Base>
based Alien needs to have either the share directory build during the install process or for
older legacy L<Alien::Base::ModuleBuild> based Aliens, a ConfigData module generated by
L<Module::Build>.
This usually happens if you try to use an Alien module from the lib directory as part of the
Alien's distribution. You need to build the alien and use C<blib/lib> instead of C<lib> or
install the alien and use the installed path.
It is also possible that your Alien installer is not set up correctly. Make sure your
C<Makefile.PL> is using L<Alien::Build::MM> correctly.
=head2 I have a question not listed here!
There are a number of forums available to people working on L<Alien>, L<Alien::Base> and
L<Alien::Build> modules:
=over 4
=item C<#native> on irc.perl.org
This is intended for native interfaces in general so is a good place for questions about L<Alien>
generally or L<Alien::Base> and L<Alien::Build> specifically.
=item mailing list
The C<perl5-alien> google group is intended for L<Alien> issues generally, including L<Alien::Base>
and L<Alien::Build>.
L<https://groups.google.com/forum/#!forum/perl5-alien>
=item Open a support ticket
If you have an issue with L<Alie::Build> itself, then please open a support ticket on the project's GitHub issue
tracker.
L<https://github.com/plicease/Alien-Build/issues>
=back
=head1 SEE ALSO
L<Alien::Build>, L<Alien::Build::MM>, L<Alien::Build::Plugin>, L<alienfile>
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
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 Pisar (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)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011-2020 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.
=cut

View File

@@ -0,0 +1,497 @@
# PODNAME: Alien::Build::Manual::PluginAuthor
# ABSTRACT: Alien::Build plugin author documentation
# VERSION
__END__
=pod
=encoding UTF-8
=head1 NAME
Alien::Build::Manual::PluginAuthor - Alien::Build plugin author documentation
=head1 VERSION
version 2.38
=head1 SYNOPSIS
your plugin:
package Alien::Build::Plugin::Build::MyPlugin;
use strict;
use warnings;
use Alien::Build::Plugin;
has arg1 => 'default_for arg1';
has arg2 => sub { [ 'default', 'for', 'arg2' ] };
sub init
{
my($self, $meta) = @_;
...
}
1;
and then from L<alienfile>:
use alienfile;
plugin 'Build::MyPlugin' => (
arg1 => 'override for arg1',
arg2 => [ 'something', 'else' ],
);
=head1 DESCRIPTION
This document explains how to write L<Alien::Build> plugins using the
L<Alien::Build::Plugin> base class. Plugins use L<Alien::Build::Plugin>,
which sets the appropriate base class, and provides you with the C<has>
property builder. C<has> takes two arguments, the name of the property
and the default value. (As with L<Moose> and L<Moo>, you should use a
code reference to specify default values for non-string defaults).
The only method that you need to implement is C<init>. From this method
you can add hooks to change the behavior of the L<alienfile> recipe.
sub init
{
my($self, $meta) = @_;
$meta->register_hook(
probe => sub {
my($build) = @_;
if( ... )
{
return 'system';
}
else
{
return 'share';
}
},
);
}
Hooks get the L<Alien::Build> instance as their first argument, and depending
on the hook may get additional arguments.
You can also modify hooks using C<before_hook>, C<around_hook> and C<after_hook>:
sub init
{
my($self, $meta) = @_;
$meta->before_hook(
build => sub {
my($build) = @_;
$build->log('this runs before the build');
},
);
$meta->after_hook(
build => sub {
my($build) = @_;
$build->log('this runs after the build');
},
);
$meta->around_hook(
build => sub {
my $orig = shift;
# around hooks are useful for setting environment variables
local $ENV{CPPFLAGS} = '-I/foo/include';
$orig->(@_);
},
);
}
You can and should write tests for your plugin. The best way to do
this is using L<Test::Alien::Build>, which allows you to write an
inline L<alienfile> in your test.
use Test::V0;
use Test::Alien::Build;
my $build = alienfile_ok q{
use alienfile;
plugin 'Build::MyPlugin' => (
arg1 => 'override for arg1',
arg2 => [ 'something', 'else' ],
);
...
};
# you can interrogate $build, it is an instance of L<Alien::Build>.
my $alien = alien_build_ok;
# you can interrogate $alien, it is an instance of L<Alien::Base>.
=head1 HOOKS
=head2 probe hook
$meta->register_hook( probe => sub {
my($build) = @_;
return 'system' if ...; # system install
return 'share'; # otherwise
});
$meta->register_hook( probe => [ $command ] );
This hook should return the string C<system> if the operating
system provides the library or tool. It should return C<share>
otherwise.
You can also use a command that returns true when the tool
or library is available. For example for use with C<pkg-config>:
$meta->register_hook( probe =>
[ '%{pkgconf} --exists libfoo' ] );
Or if you needed a minimum version:
$meta->register_hook( probe =>
[ '%{pkgconf} --atleast-version=1.00 libfoo' ] );
Note that this hook SHOULD NOT gather system properties, such as
cflags, libs, versions, etc, because the probe hook will be skipped
in the event the environment variable C<ALIEN_INSTALL_TYPE> is set.
The detection of these properties should instead be done by the
C<gather_system> hook, below.
=head2 gather_system hook
$meta->register_hook( gather_system => sub {
my($build) = @_;
$build->runtime_prop->{cflags} = ...;
$build->runtime_prop->{libs} = ...;
$build->runtime_prop->{version} = ...;
});
This hook is called for a system install to determine the properties
necessary for using the library or tool. These properties should be
stored in the C<runtime_prop> hash as shown above. Typical properties
that are needed for libraries are cflags and libs. If at all possible
you should also try to determine the version of the library or tool.
=head2 download hook
$meta->register_hook( download => sub {
my($build) = @_;
...
});
This hook is used to download from the internet the source. Either as
an archive (like tar, zip, etc), or as a directory of files (git clone,
etc). When the hook is called, the current working directory will be a
new empty directory, so you can save the download to the current
directory. If you store a single file in the directory, L<Alien::Build>
will assume that it is an archive, which will be processed by the
extract hook below. If you store multiple files, L<Alien::Build> will
assume the current directory is the source root. If no files are stored
at all, an exception with an appropriate diagnostic will be thrown.
B<Note>: If you register this hook, then the fetch, decode and prefer
hooks will NOT be called.
=head2 fetch hook
package Alien::Build::Plugin::MyPlugin;
use strict;
use warnings;
use Alien::Build::Plugin;
use Carp ();
has '+url' => sub { Carp::croak "url is required property" };
sub init
{
my($self, $meta) = @_;
$meta->register_hook( fetch => sub {
my($build, $url) = @_;
...
}
}
1;
Used to fetch a resource. The first time it will be called without an
argument, so the configuration used to find the resource should be
specified by the plugin's properties. On subsequent calls the first
argument will be a URL.
Normally the first fetch will be to either a file or a directory listing.
If it is a file then the content should be returned as a hash reference
with the following keys:
# content of file stored in Perl
return {
type => 'file',
filename => $filename,
content => $content,
version => $version, # optional, if known
};
# content of file stored in the filesystem
return {
type => 'file',
filename => $filename,
path => $path, # full file system path to file
version => $version, # optional, if known
tmp => $tmp, # optional
};
C<$tmp> if set will indicate if the file is temporary or not, and can
be used by L<Alien::Build> to save a copy in some cases. The default
is true, so L<Alien::Build> assumes the file or directory is temporary
if you don't tell it otherwise.
If the URL points to a directory listing you should return it as either
a hash reference containing a list of files:
return {
type => 'list',
list => [
# filename: each filename should be just the
# filename portion, no path or url.
# url: each url should be the complete url
# needed to fetch the file.
# version: OPTIONAL, may be provided by some fetch or prefer
{ filename => $filename1, url => $url1, version => $version1 },
{ filename => $filename2, url => $url2, version => $version2 },
]
};
or if the listing is in HTML format as a hash reference containing the
HTML information:
return {
type => 'html',
charset => $charset, # optional
base => $base, # the base URL: used for computing relative URLs
content => $content, # the HTML content
};
or a directory listing (usually produced by ftp servers) as a hash
reference:
return {
type => 'dir_listing',
base => $base,
content => $content,
};
=head2 decode hook
sub init
{
my($self, $meta) = @_;
$meta->register_hook( decode => sub {
my($build, $res) = @_;
...
}
}
This hook takes a response hash reference from the C<fetch> hook above
with a type of C<html> or C<dir_listing> and converts it into a response
hash reference of type C<list>. In short it takes an HTML or FTP file
listing response from a fetch hook and converts it into a list of filenames
and links that can be used by the prefer hook to choose the correct file to
download. See C<fetch> for the specification of the input and response
hash references.
=head2 prefer hook
sub init
{
my($self, $meta) = @_;
$meta->register_hook( prefer => sub {
my($build, $res) = @_;
return {
type => 'list',
list => [sort @{ $res->{list} }],
};
}
}
This hook sorts candidates from a listing generated from either the C<fetch>
or C<decode> hooks. It should return a new list hash reference with the
candidates sorted from best to worst. It may also remove candidates
that are totally unacceptable.
=head2 extract hook
$meta->register_hook( extract => sub {
my($build, $archive) = @_;
...
});
=head2 patch hook
$meta->register_hook( patch => sub {
my($build) = @_;
...
});
This hook is completely optional. If registered, it will be triggered after
extraction and before build. It allows you to apply any patches or make any
modifications to the source if they are necessary.
=head2 patch_ffi hook
$meta->register_hook( patch_ffi => sub {
my($build) = @_;
...
});
This hook is exactly like the C<patch> hook, except it fires only on an
FFI build.
=head2 build hook
$meta->register_hook( build => sub {
my($build) = @_;
...
});
This does the main build of the alienized project and installs it into
the staging area. The current directory is the build root. You need
to run whatever tools are necessary for the project, and install them
into C<%{.install.prefix}>.
=head2 build_ffi hook
$meta->register_hook( build_ffi => sub {
my($build) = @_;
...
});
This is the same as C<build>, except it fires only on a FFI build.
=head2 gather_share hook
$meta->register_hook( gather_share => sub {
my($build) = @_;
...
});
This is the same as C<gather_system>, except it fires after a C<share>
install.
=head2 gather_ffi hook
$meta->register_hook( gather_ffi => sub {
my($build) = @_;
...
});
This is the same as C<gather_share>, except it fires after a C<share> FFI
install.
=head2 override hook
$meta->register_hook( override => sub {
my($build) = @_;
});
This allows you to alter the override logic. It should return one of
C<share>, C<system>, C<default> or C<''>. The default implementation is
just this:
return $ENV{ALIEN_INSTALL_TYPE} || '';
=head2 clean_install
$meta->register_hook( clean_install => sub {
my($build) = @_;
});
This hook allows you to remove files from the final install location before
the files are installed by the installer layer (examples: L<Alien::Build::MM>,
L<Alien::Build::MB> or L<App::af>). This hook is never called by default,
and must be enabled via the interface to the installer layer.
This hook SHOULD NOT remove the C<_alien> directory or its content from the
install location.
The default implementation removes all the files EXCEPT the C<_alien> directory
and its content.
=head1 AUTHOR
Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
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 Pisar (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)
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2011-2020 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.
=cut