1Alien::Build::Manual::AUlsieernACuotnhtorri(b3u)ted PerlAlDioecnu:m:eBnutialtdi:o:nManual::AlienAuthor(3)
2
3
4
6 Alien::Build::Manual::AlienAuthor - Alien author documentation
7
9 version 1.93
10
12 perldoc Alien::Build::Manual::AlienAuthor
13
15 This document is intended to teach Alien authors how to build their own
16 Alien distribution using Alien::Build and Alien::Base. Such an Alien
17 distribution consists of three essential parts:
18
19 An alienfile
20 This is a recipe for how to 1) detect an already installed version
21 of the library or tool you are alienizing 2) download and build the
22 library or tool that you are alienizing and 3) gather the
23 configuration settings necessary for the use of that library or
24 tool.
25
26 An installer "Makefile.PL" or "Build.PL" or a "dist.ini" if you are
27 using Dist::Zilla
28 This is a thin layer between your alienfile recipe, and the Perl
29 installer (either ExtUtils::MakeMaker or Module::Build.
30
31 A Perl class (.pm file) that inherits from Alien::Base
32 For most Aliens this does not need to be customized at all, since
33 Alien::Base usually does what you need.
34
35 For example if you were alienizing a library called libfoo, you might
36 have these files:
37
38 Alien-Libfoo-1.00/Makefile.PL
39 Alien-Libfoo-1.00/alienfile
40 Alien-Libfoo-1.00/lib/Alien/Libfoo.pm
41
42 This document will focus mainly on instructing you how to construct an
43 alienfile, but we will also briefly cover making a simple "Makefile.PL"
44 or "dist.ini" to go along with it. We will also touch on when you
45 might want to extend your subclass to add non-standard functionality.
46
47 Using commands
48 Most software libraries and tools will come with instructions for how
49 to install them in the form of commands that you are intended to type
50 into a shell manually. The easiest way to automate those instructions
51 is to just put the commands in your alienfile. For example, lets
52 suppose that libfoo is built using autoconf and provides a "pkg-config"
53 ".pc" file.
54
55 We will also later discuss plugins. For common build systems like
56 autoconf or CMake, it is usually better to use the appropriate plugin
57 because they will handle corner cases better than a simple set of
58 commands. We're going to take a look at commands first because it's
59 easier to understand the different phases with commands.
60
61 (Aside, autoconf is a series of tools and macros used to configure
62 (usually) a C or C++ library or tool by generating any number of
63 Makefiles. It is the C equivalent to ExtUtils::MakeMaker, if you will.
64 Basically, if your library or tool instructions start with
65 './configure' it is most likely an autoconf based library or tool).
66
67 (Aside2, "pkg-config" is a standard-ish way to provide the compiler and
68 linker flags needed for compiling and linking against the library. If
69 your tool installs a ".pc" file, usually in "$PREFIX/lib/pkgconfig"
70 then, your tool uses "pkg-config").
71
72 Here is the alienfile that you might have:
73
74 use alienfile;
75
76 probe [ 'pkg-config --exists libfoo' ];
77
78 share {
79
80 start_url 'http://www.libfoo.org/src/libfoo-1.00.tar.gz';
81
82 download [ 'wget %{.meta.start_url}' ];
83
84 extract [ 'tar zxf %{.install.download}' ];
85
86 build [
87 [ './configure --prefix=%{.install.prefix} --disable-shared' ],
88 [ '%{make}' ],
89 [ '%{make} install' ],
90 ];
91
92 };
93
94 gather [
95 [ 'pkg-config --modversion libfoo', \'%{.runtime.version}' ],
96 [ 'pkg-config --cflags libfoo', \'%{.runtime.cflags}' ],
97 [ 'pkg-config --libs libfoo', \'%{.runtime.libs}' ],
98 ];
99
100 There is a lot going on here, so lets decode it a little bit. An
101 alienfile is just some Perl with some alien specific sugar. The first
102 line
103
104 use alienfile;
105
106 imports the sugar into the alienfile. It also is a flag for the reader
107 to see that this is an alienfile and not some other kind of Perl
108 script.
109
110 The second line is the probe directive:
111
112 probe [ 'pkg-config --exists libfoo' ];
113
114 is used to see if the library is already installed on the target
115 system. If "pkg-config" is in the path, and if libfoo is installed,
116 this should exit with a success (0) and tell Alien::Build to use the
117 system library. If either "pkg-config" in the PATH, or if libfoo is
118 not installed, then it will exist with non-success (!= 0) and tells
119 Alien::Build to download and build from source.
120
121 You can provide as many probe directives as you want. This is useful
122 if there are different ways to probe for the system. Alien::Build will
123 stop on the first successfully found system library found. Say our
124 library libfoo comes with a ".pc" file for use with "pkg-config" and
125 also provides a "foo-config" program to find the same values. You
126 could then specify this in your alienfile
127
128 probe [ 'pkg-config --exists libfoo' ];
129 probe [ 'foo-config --version' ];
130
131 Other directives can be specified multiple times if there are different
132 methods that can be tried for the various steps.
133
134 Sometimes it is easier to probe for a library from Perl rather than
135 with a command. For that you can use a code reference. For example,
136 another way to call "pkg-config" would be from Perl:
137
138 probe sub {
139 my($build) = @_; # $build is the Alien::Build instance.
140 system 'pkg-config --exists libfoo';
141 $? == 0 ? 'system' : 'share';
142 };
143
144 The Perl code should return 'system' if the library is installed, and
145 'share' if not. (Other directives should return a true value on
146 success, and a false value). You can also throw an exception with
147 "die" to indicate a failure.
148
149 The next part of the alienfile is the "share" block, which is used to
150 group the directives which are used to download and install the library
151 or tool in the event that it is not already installed.
152
153 share {
154 start_url 'http://www.libfoo.org/src/libfoo-1.00.tar.gz';
155 download [ 'wget %{.meta.start_url}' ];
156 extract [ 'tar zxf %{.install.download}' ];
157 build [
158 [ './configure --prefix=%{.install.prefix} --disable-shared' ],
159 [ '%{make}' ],
160 [ '%{make} install' ],
161 ];
162 };
163
164 The start_url specifies where to find the package that you are
165 alienizing. It should be either a tarball (or zip file, or what have
166 you) or an HTML index. The download directive as you might imagine
167 specifies how to download the library or tool. The extract directive
168 specifies how to extract the archive once it is downloaded. In the
169 extract step, you can use the variable "%{.install.download}" as a
170 placeholder for the archive that was downloaded in the download step.
171 This is also accessible if you use a code reference from the
172 Alien::Build instance:
173
174 share {
175 ...
176 requires 'Archive::Extract';
177 extract sub {
178 my($build) = @_;
179 my $tarball = $build->install_prop->{download};
180 my $ae = Archive::Extract->new( archive => $tarball );
181 $ae->extract;
182 1;
183 }
184 ...
185 };
186
187 The build directive specifies how to build the library or tool once it
188 has been downloaded and extracted. Note the special variable
189 "%{.install.prefix}" is the location where the library should be
190 installed. "%{make}" is a helper which will be replaced by the
191 appropriate "make", which may be called something different on some
192 platforms (on Windows for example, it frequently may be called "nmake"
193 or "dmake").
194
195 The final part of the alienfile has a gather directive which specifies
196 how to get the details on how to compile and link against the library.
197 For this, once again we use the "pkg-config" command:
198
199 gather [
200 [ 'pkg-config --modversion libfoo', \'%{.runtime.version}' ],
201 [ 'pkg-config --cflags libfoo', \'%{.runtime.cflags}' ],
202 [ 'pkg-config --libs libfoo', \'%{.runtime.libs}' ],
203 ];
204
205 The scalar reference as the final item in the command list tells
206 Alien::Build that the output from the command should be stored in the
207 given variable. The runtime variables are the ones that will be
208 available to "Alien::Libfoo" once it is installed. (Install
209 properties, which are the ones that we have seen up till now are thrown
210 away once the Alien distribution is installed.
211
212 You can also provide a "sys" block for directives that should be used
213 when a system install is detected. Normally you only need to do this
214 if the gather step is different between share and system installs. For
215 example, the above is equivalent to:
216
217 build {
218 ...
219 gather [
220 [ 'pkg-config --modversion libfoo', \'%{.runtime.version}' ],
221 [ 'pkg-config --cflags libfoo', \'%{.runtime.cflags}' ],
222 [ 'pkg-config --libs libfoo', \'%{.runtime.libs}' ],
223 ];
224 };
225
226 sys {
227 gather [
228 [ 'pkg-config --modversion libfoo', \'%{.runtime.version}' ],
229 [ 'pkg-config --cflags libfoo', \'%{.runtime.cflags}' ],
230 [ 'pkg-config --libs libfoo', \'%{.runtime.libs}' ],
231 ];
232 };
233
234 (Aside3, the reason it is called "sys" and not "system" is so that it
235 does not conflict with the built in "system" function)!
236
237 Using plugins
238 The first example is a good way of showing the full manual path that
239 you can choose, but there is a lot of repetition, if you are doing many
240 Aliens that use autoconf and "pkg-config" (which are quite common.
241 alienfile allows you to use plugins. See Alien::Build::Plugin for a
242 list of some of the plugin categories.
243
244 For now, I will just show you how to write the alienfile for libfoo
245 above using Alien::Build::Plugin::Build::Autoconf,
246 Alien::Build::Plugin::PkgConfig::Negotiate,
247 Alien::Build::Plugin::Download::Negotiate, and
248 Alien::Build::Plugin::Extract::Negotiate
249
250 use alienfile;
251
252 plugin 'PkgConfig' => (
253 pkg_name => 'libfoo',
254 );
255
256 share {
257 start_url 'http://www.libfoo.org/src';
258 plugin 'Download' => (
259 filter => qr/^libfoo-[0-9\.]+\.tar\.gz$/,
260 version => qr/^libfoo-([0-9\.]+)\.tar\.gz$/,
261 );
262 plugin 'Extract' => 'tar.gz';
263 plugin 'Build::Autoconf';
264 build [
265 '%{configure} --disable-shared',
266 '%{make}',
267 '%{make} install',
268 ];
269 };
270
271 The first plugin that we use is the "pkg-config" negotiation plugin. A
272 negotiation plugin is one which doesn't do the actual work but selects
273 the best one from a set of plugins depending on your platform and
274 environment. (In the case of
275 Alien::Build::Plugin::PkgConfig::Negotiate, it may choose to use
276 command line tools, a pure Perl implementation (PkgConfig), or
277 libpkgconf, depending on what is available). When using negotiation
278 plugins you may omit the "::Negotiate" suffix. So as you can see using
279 the plugin here is an advantage because it is more reliable that just
280 specifying a command which may not be installed!
281
282 Next we use the download negotiation plugin. This is also better than
283 the version above, because again, "wget" my not be installed on the
284 target system. Also you can specify a URL which will be scanned for
285 links, and use the most recent version.
286
287 We use the Extract negotiation plugin to use either command line tools,
288 or Perl libraries to extract from the archive once it is downloaded.
289
290 Finally we use the Autoconf plugin
291 (Alien::Build::Plugin::Build::Autoconf). This is a lot more
292 sophisticated and reliable than in the previous example, for a number
293 of reasons. This version will even work on Windows assuming the
294 library or tool you are alienizing supports that platform!
295
296 Strictly speaking the build directive is not necessary, because the
297 autoconf plugin provides a default which is reasonable. The only
298 reason that you would want to include it is if you need to provide
299 additional flags to the configure step.
300
301 share {
302 ...
303 build [
304 '%{configure} --enable-bar --enable-baz --disable-shared',
305 '%{make}',
306 '%{make} install',
307 ];
308 };
309
310 Verifying and debugging your alienfile
311 You could feed your alienfile directly into Alien::Build, or
312 Alien::Build::MM, but it is sometimes useful to test your alienfile
313 using the "af" command (it does not come with Alien::Build, you need to
314 install App::af). By default "af" will use the "alienfile" in the
315 current directory (just as "make" uses the "Makefile" in the current
316 directory; just like "make" you can use the "-f" option to specify a
317 different alienfile).
318
319 You can test your alienfile in dry run mode:
320
321 % af install --dry-run
322 Alien::Build::Plugin::Core::Legacy> adding legacy hash to config
323 Alien::Build::Plugin::Core::Gather> mkdir -p /tmp/I2YXRyxb0r/_alien
324 ---
325 cflags: ''
326 cflags_static: ''
327 install_type: system
328 legacy:
329 finished_installing: 1
330 install_type: system
331 name: libfoo
332 original_prefix: /tmp/7RtAusykNN
333 version: 1.2.3
334 libs: '-lfoo '
335 libs_static: '-lfoo '
336 prefix: /tmp/7RtAusykNN
337 version: 1.2.3
338
339 You can use the "--type" option to force a share install (download and
340 build from source):
341
342 % af install --type=share --dry-run
343 Alien::Build::Plugin::Core::Download> decoding html
344 Alien::Build::Plugin::Core::Download> candidate *https://www.libfoo.org/download/libfoo-1.2.4.tar.gz
345 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.3.tar.gz
346 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.2.tar.gz
347 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.1.tar.gz
348 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.0.tar.gz
349 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.9.tar.gz
350 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.8.tar.gz
351 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.7.tar.gz
352 Alien::Build::Plugin::Core::Download> candidate ...
353 Alien::Build::Plugin::Core::Download> setting version based on archive to 1.2.4
354 Alien::Build::Plugin::Core::Download> downloaded libfoo-1.2.4.tar.gz
355 Alien::Build::CommandSequence> + ./configure --prefix=/tmp/P22WEXj80r --with-pic --disable-shared
356 ... snip ...
357 Alien::Build::Plugin::Core::Gather> mkdir -p /tmp/WsoLAQ889w/_alien
358 ---
359 cflags: ''
360 cflags_static: ''
361 install_type: share
362 legacy:
363 finished_installing: 1
364 install_type: share
365 original_prefix: /tmp/P22WEXj80r
366 version: 1.2.4
367 libs: '-L/tmp/P22WEXj80r/lib -lfoo '
368 libs_static: '-L/tmp/P22WEXj80r/lib -lfoo '
369 prefix: /tmp/P22WEXj80r
370 version: 1.2.4
371
372 You can also use the "--before" and "--after" options to take a peek at
373 what the build environment looks like at different stages as well,
374 which can sometimes be useful:
375
376 % af install --dry-run --type=share --before build bash
377 Alien::Build::Plugin::Core::Download> decoding html
378 Alien::Build::Plugin::Core::Download> candidate *https://www.libfoo.org/download/libfoo-1.2.4.tar.gz
379 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.3.tar.gz
380 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.2.tar.gz
381 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.1.tar.gz
382 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.2.0.tar.gz
383 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.9.tar.gz
384 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.8.tar.gz
385 Alien::Build::Plugin::Core::Download> candidate https://www.libfoo.org/download/libfoo-1.1.7.tar.gz
386 Alien::Build::Plugin::Core::Download> candidate ...
387 Alien::Build::Plugin::Core::Download> setting version based on archive to 1.2.4
388 Alien::Build::Plugin::Core::Download> downloaded libfoo-1.2.4.tar.gz
389 App::af::install> [ before build ] + bash
390 /tmp/fbVPu4LRTs/build_5AVn/libfoo-1.2.4$ ls
391 CHANGES Makefile autoconf.ac lib
392 /tmp/fbVPu4LRTs/build_5AVn/libfoo-1.2.4$
393
394 There are a lot of other useful things that you can do with the "af"
395 command. See af for details.
396
397 Integrating with MakeMaker
398 Once you have a working alienfile you can write your "Makefile.PL".
399
400 use ExtUtils::MakeMaker;
401 use Alien::Build::MM;
402
403 my $abmm = Alien::Build::MM->new;
404
405 WriteMakefile($abmm->mm_args(
406 ABSTRACT => 'Discover or download and install libfoo',
407 DISTNAME => 'Alien-Libfoo',
408 NAME => 'Alien::Libfoo',
409 VERSION_FROM => 'lib/Alien/Libfoo.pm',
410 CONFIGURE_REQUIRES => {
411 'Alien::Build::MM' => 0,
412 },
413 BUILD_REQUIRES => {
414 'Alien::Build::MM' => 0,
415 },
416 PREREQ_PM => {
417 'Alien::Base' => 0,
418 },
419 # If you are going to write the recommended
420 # tests you will will want these:
421 TEST_REQUIRES => {
422 'Test::Alien' => 0,
423 'Test2::V0' => 0,
424 },
425 ));
426
427 sub MY::postamble {
428 $abmm->mm_postamble;
429 }
430
431 The "lib/Alien/Libfoo.pm" that goes along with it is very simple:
432
433 package Alien::Libfoo;
434
435 use strict;
436 use warnings;
437 use base qw( Alien::Base );
438
439 1;
440
441 You are done and can install it normally:
442
443 % perl Makefile.PL
444 % make
445 % make test
446 % make install
447
448 Integrating with Module::Build
449 Please don't! Okay if you have to there is Alien::Build::MB.
450
451 Non standard configuration
452 Alien::Base support most of the things that your Alien will need, like
453 compiler flags (cflags), linker flags (libs) and binary directory
454 (bin_dir). Your library or tool may have other configuration items
455 which are not supported by default. You can store the values in the
456 alienfile into the runtime properties:
457
458 gather [
459 # standard:
460 [ 'foo-config --version libfoo', \'%{.runtime.version}' ],
461 [ 'foo-config --cflags libfoo', \'%{.runtime.cflags}' ],
462 [ 'foo-config --libs libfoo', \'%{.runtime.libs}' ],
463 # non-standard
464 [ 'foo-config --bar-baz libfoo', \'%{.runtime.bar_baz}' ],
465 ];
466
467 then you can expose them in your Alien::Base subclass:
468
469 package Alien::Libfoo;
470
471 use strict;
472 use warnings;
473 use base qw( Alien::Base );
474
475 sub bar_baz {
476 my($self) = @_;
477 $self->runtime_prop->{bar_baz},
478 };
479
480 1;
481
482 Testing
483 (optional, but highly recommended)
484
485 You should write a test using Test::Alien to make sure that your alien
486 will work with any XS modules that are going to use it:
487
488 use Test2::V0;
489 use Test::Alien;
490 use Alien::Libfoo;
491
492 alien_ok 'Alien::Libfoo';
493
494 xs_ok { local $/; <DATA> }, with_subtest {
495 is Foo::something(), 1, 'Foo::something() returns 1';
496 };
497
498 done_testing;
499
500 __DATA__
501 #include "EXTERN.h"
502 #include "perl.h"
503 #include "XSUB.h"
504 #include <foo.h>
505
506 MODULE = Foo PACKAGE = Foo
507
508 int something()
509
510 You can also use Test::Alien to test tools instead of libraries:
511
512 use Test2::V0;
513 use Test::Alien;
514 use Alien::Libfoo;
515
516 alien_ok 'Alien::Libfoo';
517 run_ok(['foo', '--version'])
518 ->exit_is(0);
519
520 done_testing;
521
522 You can also write tests specifically for FFI::Platypus, if your alien
523 is going to be used to write FFI bindings. (the test below is the FFI
524 equivalent to the XS example above).
525
526 use Test2::V0;
527 use Test::Alien;
528 use Alien::Libfoo;
529
530 alien_ok 'Alien::Libfoo';
531 ffi_ok { symbols => [ 'something' ] }, with_subtest {
532 # $ffi is an instance of FFI::Platypus with the lib
533 # set appropriately.
534 my($ffi) = @_;
535 my $something = $ffi->function( something => [] => 'int' );
536 is $something->call(), 1, 'Foo::something() returns 1';
537 };
538
539 If you do use "ffi_ok" you want to make sure that your alien reliably
540 produces dynamic libraries. If it isn't consistent (if for example
541 some platforms tend not to provide or build dynamic libraries), you can
542 check that "dynamic_libs" doesn't return an empty list.
543
544 ...
545 alien_ok 'Alien::Libfoo';
546 SKIP: {
547 skip "This test requires a dynamic library"
548 unless Alien::Libfoo->dynamic_libs;
549 ffi_ok { symbols [ 'something' ] }, with_subtest {
550 ...
551 };
552 }
553
554 More details on testing Alien modules can be found in the Test::Alien
555 documentation.
556
557 You can also run the tests that come with the package that you are
558 alienizing, by using a "test" block in your alienfile. Keep in mind
559 that some packages use testing tools or have other prerequisites that
560 will not be available on your users machines when they attempt to
561 install your alien. So you do not want to blindly add a test block
562 without checking what the prereqs are. For Autoconf style packages you
563 typically test a package using the "make check" command:
564
565 use alienfile;
566
567 plugin 'PkgConfig' => 'libfoo';
568
569 share {
570 ... # standard build steps.
571 test [ '%{make} check' ];
572 };
573
574 Dist::Zilla
575 (optional, mildly recommended)
576
577 You can also use the Alien::Build Dist::Zilla plugin
578 Dist::Zilla::Plugin::AlienBuild:
579
580 name = Alien-Libfoo
581 author = E. Xavier Ample <example@cpan.org>
582 license = Perl_5
583 copyright_holder = E. Xavier Ample <example@cpan.org>
584 copyright_year = 2017
585 version = 0.01
586
587 [@Basic]
588 [AlienBuild]
589
590 The plugin takes care of a lot of details like making sure that the
591 correct minimum versions of Alien::Build and Alien::Base are used. See
592 the plugin documentation for additional details.
593
594 Using your Alien
595 Once you have installed you can use your Alien. See
596 Alien::Build::Manual::AlienUser for guidance on that.
597
599 Author: Graham Ollis <plicease@cpan.org>
600
601 Contributors:
602
603 Diab Jerius (DJERIUS)
604
605 Roy Storey (KIWIROY)
606
607 Ilya Pavlov
608
609 David Mertens (run4flat)
610
611 Mark Nunberg (mordy, mnunberg)
612
613 Christian Walde (Mithaldu)
614
615 Brian Wightman (MidLifeXis)
616
617 Zaki Mughal (zmughal)
618
619 mohawk (mohawk2, ETJ)
620
621 Vikas N Kumar (vikasnkumar)
622
623 Flavio Poletti (polettix)
624
625 Salvador Fandiño (salva)
626
627 Gianni Ceccarelli (dakkar)
628
629 Pavel Shaydo (zwon, trinitum)
630
631 Kang-min Liu (劉康民, gugod)
632
633 Nicholas Shipp (nshp)
634
635 Juan Julián Merelo Guervós (JJ)
636
637 Joel Berger (JBERGER)
638
639 Petr Pisar (ppisar)
640
641 Lance Wicks (LANCEW)
642
643 Ahmad Fatoum (a3f, ATHREEF)
644
645 José Joaquín Atria (JJATRIA)
646
647 Duke Leto (LETO)
648
649 Shoichi Kaji (SKAJI)
650
651 Shawn Laffan (SLAFFAN)
652
653 Paul Evans (leonerd, PEVANS)
654
656 This software is copyright (c) 2011-2019 by Graham Ollis.
657
658 This is free software; you can redistribute it and/or modify it under
659 the same terms as the Perl 5 programming language system itself.
660
661
662
663perl v5.30.1 2019-12-1A0lien::Build::Manual::AlienAuthor(3)