1Alien::Build::Manual::PUlsuegrinCAountthroirb(u3t)ed PerAlliDeonc:u:mBeunitladt:i:oMnanual::PluginAuthor(3)
2
3
4
6 Alien::Build::Manual::PluginAuthor - Alien::Build plugin author
7 documentation
8
10 version 2.80
11
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
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
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
632 Alien::Build::Manual
633 Other Alien::Build manuals.
634
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.1 2023-05-A1l5ien::Build::Manual::PluginAuthor(3)