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