1Alien::Build::Manual::PUlsuegrinCAountthroirb(u3t)ed PerAlliDeonc:u:mBeunitladt:i:oMnanual::PluginAuthor(3)
2
3
4

NAME

6       Alien::Build::Manual::PluginAuthor - Alien::Build plugin author
7       documentation
8

VERSION

10       version 2.77
11

SYNOPSIS

13       your plugin:
14
15        package Alien::Build::Plugin::Build::MyPlugin;
16
17        use strict;
18        use warnings;
19        use Alien::Build::Plugin;
20
21        has arg1 => 'default_for arg1';
22        has arg2 => sub { [ 'default', 'for', 'arg2' ] };
23
24        sub init
25        {
26          my($self, $meta) = @_;
27          ...
28        }
29
30        1;
31
32       and then from alienfile:
33
34        use alienfile;
35        plugin 'Build::MyPlugin' => (
36          arg1 => 'override for arg1',
37          arg2 => [ 'something', 'else' ],
38        );
39
40       Perlish pseudo code for how plugins are called:
41
42        my $probe;
43        my $override = override();
44
45        if($override eq 'system') {
46
47          $probe = probe();
48
49          if($probe ne 'system') {
50            die 'system tool or library not found';
51          }
52
53        }
54
55        elsif($override eq 'default') {
56          $probe = probe();
57
58        } else { # $override eq 'share'
59          # note that in this instance the
60          # probe hook is never called
61          $probe = 'share';
62        }
63
64        if($probe eq 'system') {
65          gather_system();
66
67        } else { # $probe eq 'share'
68
69          download();
70          extract();
71          patch();
72          build();
73          gather_share();
74
75          # Check to see if there isa build_ffi hook
76          if(defined &build_ffi) {
77            patch_ffi();
78            build_ffi();
79            gather_ffi();
80          }
81        }
82
83        # By default this just returns the value of $ENV{ALIEN_INSTALL_TYPE}
84        sub override {
85          return $ENV{ALIEN_INSTALL_TYPE};
86        }
87
88        # Default download implementation; can be
89        # replaced by specifying a different download
90        # hook.  See Alien::Build::Plugin::Core::Download
91        # for detailed implementation.
92        sub download {
93
94          my $response = fetch();
95
96          if($response->{type} eq 'html' || $response->{type} eq 'dir_listing') {
97            # decode will transform an HTML listing (html) or a FTP directory
98            # listing (dir_listing) into a regular list
99            $response = decode($response);
100          }
101
102          if($response->{type} eq 'list') {
103
104            # prefer will filter bad entries in the list
105            # and sort them so that the first one is
106            # the one that we want
107            $response = prefer($response);
108
109            my $first_preferred = $res->{list}->[0];
110
111            # prefer can sometimes infer the version from the
112            # filename.
113            if(defined $first_preferred->{version}) {
114              # not a hook
115              runtime_prop->{version} = $first_preferred->{version};
116            }
117
118            $response = fetch($first_preferred);
119
120          }
121
122          if($response->{type} eq 'file') {
123            # not a hook
124            write_file_to_disk $response;
125          }
126
127        }
128

DESCRIPTION

130       This document explains how to write Alien::Build plugins using the
131       Alien::Build::Plugin base class.
132
133   Writing plugins
134       Plugins use Alien::Build::Plugin, which sets the appropriate base
135       class, and provides you with the "has" property builder.  "has" takes
136       two arguments, the name of the property and the default value.  (As
137       with Moose and Moo, you should use a code reference to specify default
138       values for non-string defaults).  No not set this as your plugin's base
139       class directly:
140
141        use parent qw( Alien::Build::Plugin );  # wrong
142        use Alien::Build::Plugin;               # right
143
144       The only method that you need to implement is "init".  From this method
145       you can add hooks to change the behavior of the alienfile recipe.  This
146       is a very simple example of a probe hook, with the actual probe logic
147       removed:
148
149        sub init
150        {
151          my($self, $meta) = @_;
152          $meta->register_hook(
153            probe => sub {
154              my($build) = @_;
155              if( ... )
156              {
157                return 'system';
158              }
159              else
160              {
161                return 'share';
162              }
163            },
164          );
165        }
166
167       Hooks get the Alien::Build instance as their first argument, and
168       depending on the hook may get additional arguments.
169
170   Modifying hooks
171       You can also modify hooks using "before_hook", "around_hook" and
172       "after_hook", similar to Moose modifiers:
173
174        sub init
175        {
176          my($self, $meta) = @_;
177
178          $meta->before_hook(
179            build => sub {
180              my($build) = @_;
181              $build->log('this runs before the build');
182            },
183          );
184
185          $meta->after_hook(
186            build => sub {
187              my($build) = @_;
188              $build->log('this runs after the build');
189            },
190          );
191
192          $meta->around_hook(
193            build => sub {
194              my $orig = shift;
195
196              # around hooks are useful for setting environment variables
197              local $ENV{CPPFLAGS} = '-I/foo/include';
198
199              $orig->(@_);
200            },
201          );
202        }
203
204   Testing plugins
205       You can and should write tests for your plugin.  The best way to do
206       this is using Test::Alien::Build, which allows you to write an inline
207       alienfile in your test.  Here is an example:
208
209        use Test::V0;
210        use Test::Alien::Build;
211
212        my $build = alienfile_ok q{
213          use alienfile;
214          plugin 'Build::MyPlugin' => (
215            arg1 => 'override for arg1',
216            arg2 => [ 'something', 'else' ],
217          );
218          ...
219        };
220
221        # you can interrogate $build, it is an instance of L<Alien::Build>.
222
223        my $alien = alien_build_ok;
224
225        # you can interrogate $alien, it is an instance of L<Alien::Base>.
226
227   Negotiator plugins
228       A Negotiator plugin doesn't itself typically implement anything on its
229       own, but picks the best plugin to achieve a particular goal.
230
231       The "best" plugin can in some cases vary depending on the platform or
232       tools that are available.  For example The download negotiator might
233       choose to use the fetch plugin that relies on the command line "curl",
234       or it might choose the fetch plugin that relies on the Perl module
235       HTTP::Tiny depending on the platform and what is already installed.
236       (For either to be useful they have to support SSL).
237
238       The Negotiator plugin is by convention named something like
239       "Alien::Build::Plugin::*::Negotiate", but is typically invoked without
240       the "::Negotiate" suffix.  For example:
241
242        plugin 'Download'; # is short for Alien::Build::Plugin::Download::Negotiator
243
244       Here is a simple example of a negotiator which picks "curl" if already
245       installed and HTTP::Tiny otherwise.  (The actual download plugin is a
246       lot smarter and complicated than this, but this is a good simplified
247       example).
248
249        package Alien::Build::Plugin::Download::Negotiate;
250
251        use strict;
252        use warnings;
253        use Alien::Build::Plugin;
254        use File::Which qw( which );
255
256        sub init
257        {
258          my($self, $meta) = @_;
259
260          if(which('curl')) {
261            $meta->apply_plugin('Fetch::Curl');
262          } else {
263            $meta->apply_plugin('Fetch::HTTPTiny');
264          }
265        }
266
267   Hooks
268       The remainder of this document is a reference for the hooks that you
269       can register.  Generally speaking you can register any hook that you
270       like, but some care must be taken as some hooks have default behavior
271       that will be overridden when you register a hook.  The hooks are
272       presented in alphabetical order.  The execution order is shown in the
273       flowchart above (if you are browsing the HTML version of this
274       document), or the Perlish pseudo code in the synopsis section.
275

HOOKS

277   build hook
278        $meta->register_hook( build => sub {
279          my($build) = @_;
280          ...
281        });
282
283       This does the main build of the alienized project and installs it into
284       the staging area.  The current directory is the build root.  You need
285       to run whatever tools are necessary for the project, and install them
286       into "$build-"install_prop->{prefix}> ("%{.install.prefix}").
287
288   build_ffi hook
289        $meta->register_hook( build_ffi => sub {
290          my($build) = @_;
291          ...
292        });
293
294       This is the same as build, except it fires only on a FFI build.
295
296   decode hook
297        $meta->register_hook( decode => sub {
298          my($build, $res) = @_;
299          ...
300        }
301
302       This hook takes a response hash reference from the "fetch" hook above
303       with a type of "html" or "dir_listing" and converts it into a response
304       hash reference of type "list".  In short it takes an HTML or FTP file
305       listing response from a fetch hook and converts it into a list of
306       filenames and links that can be used by the prefer hook to choose the
307       correct file to download.  See the fetch hook for the specification of
308       the input and response hash references.
309
310   check_digest hook
311        # implement the well known FOO-92 digest
312        $meta->register_hook( check_digest => sub {
313          my($build, $file, $algorithm, $digest) = @_;
314          if($algorithm ne 'FOO92') {
315            return 0;
316          }
317          my $actual = foo92_hex_digest($file);
318          if($actual eq $digest) {
319            return 1;
320          } else {
321            die "Digest FOO92 does not match: got $actual, expected $digest";
322          }
323        });
324
325       This hook should check the given $file (the format is the same as used
326       by the fetch hook) matches the given $digest using the given
327       $algorithm.  If the plugin does not support the given algorithm, then
328       it should return a false value.  If the digest does not match, it
329       should throw an exception.  If the digest matches, it should return a
330       true value.
331
332   clean_install
333        $meta->register_hook( clean_install => sub {
334          my($build) = @_;
335        });
336
337       This hook allows you to remove files from the final install location
338       before the files are installed by the installer layer (examples:
339       Alien::Build::MM, Alien::Build::MB or App::af).  This hook is not
340       called by default, and must be enabled via the interface to the
341       installer layer (example: "clean_install" in Alien::Build::MM).
342
343       This hook SHOULD NOT remove the "_alien" directory or its content from
344       the install location.
345
346       The default implementation removes all the files EXCEPT the "_alien"
347       directory and its content.
348
349   download hook
350        $meta->register_hook( download => sub {
351          my($build) = @_;
352          ...
353        });
354
355       This hook is used to download from the internet the source.  Either as
356       an archive (like tar, zip, etc), or as a directory of files ("git
357       clone", etc).  When the hook is called, the current working directory
358       will be a new empty directory, so you can save the download to the
359       current directory.  If you store a single file in the directory,
360       Alien::Build will assume that it is an archive, which will be processed
361       by the extract hook.  If you store multiple files, Alien::Build will
362       assume the current directory is the source root.  If no files are
363       stored at all, an exception with an appropriate diagnostic will be
364       thrown.
365
366       Note: If you register this hook, then the fetch, decode and prefer
367       hooks will NOT be called, unless you call them yourself from this hook.
368
369   extract hook
370        $meta->register_hook( extract => sub {
371          my($build, $archive) = @_;
372          ...
373        });
374
375       This hook is used to extract an archive that has already been
376       downloaded.  Alien::Build already has plugins for the most common
377       archive formats, so you will likely only need this to add support for
378       new or novel archive formats.  When this hook is called, the current
379       working directory will be a new empty directory, so you can save the
380       content of the archive to the current directory.  If a single directory
381       is written to the current directory, Alien::Build will assume that is
382       the root directory of the package.  If multiple files and/or
383       directories are present, that will indicate that the current working
384       directory is the root of the package.  The logic typically handles
385       correctly the default behavior for tar (where packages are typically
386       extracted to a subdirectory) and for zip (where packages are typically
387       extracted to the current directory).
388
389   fetch hook
390        package Alien::Build::Plugin::MyPlugin;
391
392        use strict;
393        use warnings;
394        use Alien::Build::Plugin;
395        use Carp ();
396
397        has '+url' => sub { Carp::croak "url is required property" };
398
399        sub init
400        {
401          my($self, $meta) = @_;
402
403          $meta->register_hook( fetch => sub {
404            my($build, $url, %options) = @_;
405            ...
406          }
407        }
408
409        1;
410
411       Used to fetch a resource.  The first time it will be called without an
412       argument (or with $url set to "undef", so the configuration used to
413       find the resource should be specified by the plugin's properties.  On
414       subsequent calls the first argument will be a URL.
415
416       The %options hash may contain these options:
417
418       http_headers
419           HTTP request headers, if an appropriate protocol is being used.
420           The headers are provided as an array reference of key/value pairs,
421           which allows for duplicate header keys with multiple values.
422
423           If a non-HTTP protocol is used, or if the plugin cannot otherwise
424           send HTTP request headers, the plugin SHOULD issue a warning using
425           the "$build->log" method, but because this option wasn't part of
426           the original spec, the plugin MAY no issue that warning while
427           ignoring it.
428
429       Note that versions of Alien::Build prior to 2.39 did not pass the
430       options hash into the fetch plugin.
431
432       Normally the first fetch will be to either a file or a directory
433       listing.  If it is a file then the content should be returned as a hash
434       reference with the following keys:
435
436        # content of file stored in Perl
437        return {
438          type     => 'file',
439          filename => $filename,
440          content  => $content,
441          version  => $version,  # optional, if known
442          protocol => $protocol, # AB 2.60 optional, but recommended
443        };
444
445        # content of file stored in the filesystem
446        return {
447          type     => 'file',
448          filename => $filename,
449          path     => $path,     # full file system path to file
450          version  => $version,  # optional, if known
451          tmp      => $tmp,      # optional
452          protocol => $protocol, # AB 2.60 optional, but recommended
453        };
454
455       $tmp if set will indicate if the file is temporary or not, and can be
456       used by Alien::Build to save a copy in some cases.  The default is
457       true, so Alien::Build assumes the file or directory is temporary if you
458       don't tell it otherwise.  Probably the most common situation when you
459       would set "tmp" to false, is when the file is bundled inside the Alien
460       distribution.  See Alien::Build::Plugin::Fetch::Local for example.
461
462       If the URL points to a directory listing you should return it as either
463       a hash reference containing a list of files:
464
465        return {
466          type => 'list',
467          list => [
468            # filename: each filename should be just the
469            #   filename portion, no path or url.
470            # url: each url should be the complete url
471            #   needed to fetch the file.
472            # version: OPTIONAL, may be provided by some fetch or prefer
473            { filename => $filename1, url => $url1, version => $version1 },
474            { filename => $filename2, url => $url2, version => $version2 },
475          ],
476          protocol => $protocol, # AB 2.60 optional, but recommended
477        };
478
479       or if the listing is in HTML format as a hash reference containing the
480       HTML information:
481
482        return {
483          type => 'html',
484          charset  => $charset, # optional
485          base     => $base,    # the base URL: used for computing relative URLs
486          content  => $content, # the HTML content
487          protocol => $protocol, # optional, but recommended
488        };
489
490       or a directory listing (usually produced by an FTP servers) as a hash
491       reference:
492
493        return {
494          type     => 'dir_listing',
495          base     => $base,
496          content  => $content,
497          protocol => $protocol, # AB 2.60 optional, but recommended
498        };
499
500       [version 2.60]
501
502       For all of these responses $protocol is optional, since it was not part
503       of the original spec, however it is strongly recommended that you
504       include this field, because future versions of Alien::Build will use
505       this to determine if a file was downloaded securely (that is via a
506       secure protocol such as SSL).
507
508       Some plugins (like decode plugins ) trans late a file hash from one
509       type to another, they should maintain the $protocol from the old to the
510       new representation of the file.
511
512   gather_ffi hook
513        $meta->register_hook( gather_ffi => sub {
514          my($build) = @_;
515          $build->runtime_prop->{cflags}  = ...;
516          $build->runtime_prop->{libs}    = ...;
517          $build->runtime_prop->{version} = ...;
518        });
519
520       This hook is called for a FFI build to determine the properties
521       necessary for using the library or tool.  These properties should be
522       stored in the runtime_prop hash as shown above.  Typical properties
523       that are needed for libraries are cflags and libs.  If at all possible
524       you should also try to determine the version of the library or tool.
525
526   gather_share hook
527        $meta->register_hook( gather_share => sub {
528          my($build) = @_;
529          $build->runtime_prop->{cflags}  = ...;
530          $build->runtime_prop->{libs}    = ...;
531          $build->runtime_prop->{version} = ...;
532        });
533
534       This hook is called for a share install to determine the properties
535       necessary for using the library or tool.  These properties should be
536       stored in the runtime_prop hash as shown above.  Typical properties
537       that are needed for libraries are cflags and libs.  If at all possible
538       you should also try to determine the version of the library or tool.
539
540   gather_system hook
541        $meta->register_hook( gather_system => sub {
542          my($build) = @_;
543          $build->runtime_prop->{cflags}  = ...;
544          $build->runtime_prop->{libs}    = ...;
545          $build->runtime_prop->{version} = ...;
546        });
547
548       This hook is called for a system install to determine the properties
549       necessary for using the library or tool.  These properties should be
550       stored in the runtime_prop hash as shown above.  Typical properties
551       that are needed for libraries are cflags and libs.  If at all possible
552       you should also try to determine the version of the library or tool.
553
554   override hook
555        $meta->register_hook( override => sub {
556          my($build) = @_;
557          return $ENV{ALIEN_INSTALL_TYPE} || '';
558        });
559
560       This allows you to alter the override logic.  It should return one of
561       "share", "system", "default" or ''.  The default implementation is
562       shown above.  Alien::Build::Plugin::Probe::Override and
563       Alien::Build::Plugin::Probe::OverrideCI are examples of how you can use
564       this hook.
565
566   patch hook
567        $meta->register_hook( patch => sub {
568          my($build) = @_;
569          ...
570        });
571
572       This hook is completely optional.  If registered, it will be triggered
573       after extraction and before build.  It allows you to apply any patches
574       or make any modifications to the source if they are necessary.
575
576   patch_ffi hook
577        $meta->register_hook( patch_ffi => sub {
578          my($build) = @_;
579          ...
580        });
581
582       This hook is exactly like the patch hook, except it fires only on an
583       FFI build.
584
585   prefer hook
586        $meta->register_hook( prefer => sub {
587          my($build, $res) = @_;
588          return {
589            type => 'list',
590            list => [sort @{ $res->{list} }],
591          };
592        }
593
594       This hook sorts candidates from a listing generated from either the
595       "fetch" or "decode" hooks.  It should return a new list hash reference
596       with the candidates sorted from best to worst.  It may also remove
597       candidates that are totally unacceptable.
598
599   probe hook
600        $meta->register_hook( probe => sub {
601          my($build) = @_;
602          return 'system' if ...; # system install
603          return 'share';         # otherwise
604        });
605
606        $meta->register_hook( probe => [ $command ] );
607
608       This hook should return the string "system" if the operating system
609       provides the library or tool.  It should return "share" otherwise.
610
611       You can also use a command that returns true when the tool or library
612       is available.  For example for use with "pkg-config":
613
614        $meta->register_hook( probe =>
615          [ '%{pkgconf} --exists libfoo' ] );
616
617       Or if you needed a minimum version:
618
619        $meta->register_hook( probe =>
620          [ '%{pkgconf} --atleast-version=1.00 libfoo' ] );
621
622       Note that this hook SHOULD NOT gather system properties, such as
623       cflags, libs, versions, etc, because the probe hook will be skipped in
624       the event the environment variable "ALIEN_INSTALL_TYPE" is set.  The
625       detection of these properties should instead be done by the
626       gather_system hook.
627
628       Multiple probe hooks can be given.  These will be used in sequence,
629       stopping at the first that detects a system installation.
630

SEE ALSO

632       Alien::Build::Manual
633           Other Alien::Build manuals.
634

AUTHOR

636       Author: Graham Ollis <plicease@cpan.org>
637
638       Contributors:
639
640       Diab Jerius (DJERIUS)
641
642       Roy Storey (KIWIROY)
643
644       Ilya Pavlov
645
646       David Mertens (run4flat)
647
648       Mark Nunberg (mordy, mnunberg)
649
650       Christian Walde (Mithaldu)
651
652       Brian Wightman (MidLifeXis)
653
654       Zaki Mughal (zmughal)
655
656       mohawk (mohawk2, ETJ)
657
658       Vikas N Kumar (vikasnkumar)
659
660       Flavio Poletti (polettix)
661
662       Salvador Fandiño (salva)
663
664       Gianni Ceccarelli (dakkar)
665
666       Pavel Shaydo (zwon, trinitum)
667
668       Kang-min Liu (劉康民, gugod)
669
670       Nicholas Shipp (nshp)
671
672       Juan Julián Merelo Guervós (JJ)
673
674       Joel Berger (JBERGER)
675
676       Petr Písař (ppisar)
677
678       Lance Wicks (LANCEW)
679
680       Ahmad Fatoum (a3f, ATHREEF)
681
682       José Joaquín Atria (JJATRIA)
683
684       Duke Leto (LETO)
685
686       Shoichi Kaji (SKAJI)
687
688       Shawn Laffan (SLAFFAN)
689
690       Paul Evans (leonerd, PEVANS)
691
692       Håkon Hægland (hakonhagland, HAKONH)
693
694       nick nauwelaerts (INPHOBIA)
695
696       Florian Weimer
697
699       This software is copyright (c) 2011-2022 by Graham Ollis.
700
701       This is free software; you can redistribute it and/or modify it under
702       the same terms as the Perl 5 programming language system itself.
703
704
705
706perl v5.36.0                      2023-01-A2l0ien::Build::Manual::PluginAuthor(3)
Impressum