Go to main content

man pages section 3: Library Interfaces and Headers

Exit Print View

Updated: Wednesday, July 27, 2022
 
 

Alien::Build::Manual::AlienAuthor (3)

Name

Alien::Build::Manual::AlienAuthor - Alien author documentation

Synopsis

perldoc Alien::Build::Manual::AlienAuthor

Description

User Contributed Perl Documentation
                                          Alien::Build::Manual::AlienAuthor(3)



NAME
       Alien::Build::Manual::AlienAuthor - Alien author documentation

VERSION
       version 1.89

SYNOPSIS
        perldoc Alien::Build::Manual::AlienAuthor

DESCRIPTION
       This document is intended to teach Alien authors how to build their own
       Alien distribution using Alien::Build and Alien::Base.  Such an Alien
       distribution consists of three essential parts:

       An 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.

       An installer "Makefile.PL" or "Build.PL" or a "dist.ini" if you are
       using Dist::Zilla
           This is a thin layer between your alienfile recipe, and the Perl
           installer (either ExtUtils::MakeMaker or Module::Build.

       A Perl class (.pm file) that inherits from Alien::Base
           For most Aliens this does not need to be customized at all, since
           Alien::Base usually does what you need.

       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
       alienfile, but we will also briefly cover making a simple "Makefile.PL"
       or "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.

   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 alienfile.  For example, lets
       suppose that libfoo is built using autoconf and provides a "pkg-config"
       ".pc" file.

       (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 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, "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 ".pc" file, usually in "$PREFIX/lib/pkgconfig"
       then, your tool uses "pkg-config").

       Here is the 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
       alienfile is just some Perl with some alien specific sugar.  The first
       line

        use alienfile;

       imports the sugar into the alienfile.  It also is a flag for the reader
       to see that this is an 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 "pkg-config" is in the path, and if libfoo is installed,
       this should exit with a success (0) and tell Alien::Build to use the
       system library.  If either "pkg-config" in the PATH, or if libfoo is
       not installed, then it will exist with non-success (!= 0) and tells
       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 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 "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
       "die" to indicate a failure.

       The next part of the alienfile is the "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 "%{.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
       has been downloaded and extracted.  Note the special variable
       "%{.install.prefix}" is the location where the library should be
       installed.  "%{make}" is a helper which will be 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 will be
       available to "Alien::Libfoo" once it is installed.  (Install
       properties, which are the ones that we have seen up till now are 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 is called "sys" and not "system" is so that it
       does not conflict with the built in "system" function)!

   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
       Aliens that use autoconf and "pkg-config" (which are quite common.
       alienfile allows you to use plugins.  See Alien::Build::Plugin for a
       list of some of the plugin categories.

       For now, I will 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 that we use is the "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
       Alien::Build::Plugin::PkgConfig::Negotiate, it may choose to use
       command line tools, a pure Perl implementation (PkgConfig), or
       libpkgconf, depending on what is available).  When using negotiation
       plugins you may omit the "::Negotiate" suffix.  So as you can see using
       the plugin here is an advantage because it is more reliable that 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, "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
       (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',
          ];
        };

   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".

        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',
        ));

        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 base 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 base qw( Alien::Base );

        sub bar_baz {
          my($self) = @_;
          $self->runtime_prop->{bar_baz},
        };

        1;

   Testing
       (optional, but highly recommended)

       You should write a test using 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 { 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(class)

       You can also use 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;

       More details on testing Alien modules can be found in the Test::Alien
       documentation.

       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)

       You can also use the Alien::Build Dist::Zilla plugin
       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 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.

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 Fandio (salva)

       Gianni Ceccarelli (dakkar)

       Pavel Shaydo (zwon, trinitum)

       Kang-min Liu (, gugod)

       Nicholas Shipp (nshp)

       Juan Julin Merelo Guervs (JJ)

       Joel Berger (JBERGER)

       Petr Pisar (ppisar)

       Lance Wicks (LANCEW)

       Ahmad Fatoum (a3f, ATHREEF)

       Jos Joaqun Atria (JJATRIA)

       Duke Leto (LETO)

       Shoichi Kaji (SKAJI)

       Shawn Laffan (SLAFFAN)

       Paul Evans (leonerd, PEVANS)

COPYRIGHT AND LICENSE
       This software is copyright (c) 2011-2019 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.



ATTRIBUTES
       See attributes(7) for descriptions of the following attributes:


       +---------------+--------------------------------+
       |ATTRIBUTE TYPE |        ATTRIBUTE VALUE         |
       +---------------+--------------------------------+
       |Availability   | library/perl-5/alien-build-532 |
       +---------------+--------------------------------+
       |Stability      | Volatile                       |
       +---------------+--------------------------------+

NOTES
       Source code for open source software components in Oracle Solaris can
       be found at https://www.oracle.com/downloads/opensource/solaris-source-
       code-downloads.html.

       This software was built from source available at
       https://github.com/oracle/solaris-userland.  The original community
       source was downloaded from
       http://www.cpan.org/authors/id/P/PL/PLICEASE/Alien-Build-1.89.tar.gz.

       Further information about this software can be found on the open source
       community website at http://search.cpan.org/dist/Alien-Build/.



perl v5.32.0                      2019-09-25
                                          Alien::Build::Manual::AlienAuthor(3)