Alien-Build

 view release on metacpan or  search on metacpan

lib/Alien/Build/Manual/AlienAuthor.pod  view on Meta::CPAN

   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 on failure).  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',
   ];



( run in 0.913 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )