1package Alien::Build;
2
3use strict;
4use warnings;
5use 5.008004;
6use Path::Tiny ();
7use Carp ();
8use File::chdir;
9use JSON::PP ();
10use Env qw( @PATH @PKG_CONFIG_PATH );
11use Config ();
12use Alien::Build::Log;
13
14# ABSTRACT: Build external dependencies for use in CPAN
15our $VERSION = '2.45'; # VERSION
16
17
18sub _path { goto \&Path::Tiny::path }
19
20
21sub new
22{
23  my($class, %args) = @_;
24  my $self = bless {
25    install_prop => {
26      root  => _path($args{root} || "_alien")->absolute->stringify,
27      patch => (defined $args{patch}) ? _path($args{patch})->absolute->stringify : undef,
28    },
29    runtime_prop => {
30      alien_build_version => $Alien::Build::VERSION || 'dev',
31    },
32    plugin_instance_prop => {},
33    bin_dir => [],
34    pkg_config_path => [],
35    aclocal_path => [],
36  }, $class;
37
38  $self->meta->filename(
39    $args{filename} || do {
40      my(undef, $filename) = caller;
41      _path($filename)->absolute->stringify;
42    }
43  );
44
45  if($args{meta_prop})
46  {
47    $self->meta->prop->{$_} = $args{meta_prop}->{$_} for keys %{ $args{meta_prop} };
48  }
49
50  $self;
51}
52
53
54my $count = 0;
55
56sub load
57{
58  my(undef, $alienfile, @args) = @_;
59
60  my $rcfile = Path::Tiny->new($ENV{ALIEN_BUILD_RC} || '~/.alienbuild/rc.pl')->absolute;
61  if(-r $rcfile)
62  {
63    require Alien::Build::rc;
64    package Alien::Build::rc;
65    require $rcfile;
66  }
67
68  unless(-r $alienfile)
69  {
70    Carp::croak "Unable to read alienfile: $alienfile";
71  }
72
73  my $file = _path $alienfile;
74  my $name = $file->parent->basename;
75  $name =~ s/^alien-//i;
76  $name =~ s/[^a-z]//g;
77  $name = 'x' if $name eq '';
78  $name = ucfirst $name;
79
80  my $class = "Alien::Build::Auto::$name@{[ $count++ ]}";
81
82  { no strict 'refs';
83  @{ "${class}::ISA" } = ('Alien::Build');
84  *{ "${class}::Alienfile::meta" } = sub {
85    $class =~ s{::Alienfile$}{};
86    $class->meta;
87  }};
88
89  my @preload = qw( Core::Setup Core::Download Core::FFI Core::Override Core::CleanInstall );
90  push @preload, @Alien::Build::rc::PRELOAD;
91  push @preload, split /;/, $ENV{ALIEN_BUILD_PRELOAD}
92    if defined $ENV{ALIEN_BUILD_PRELOAD};
93
94  my @postload = qw( Core::Legacy Core::Gather Core::Tail );
95  push @postload, @Alien::Build::rc::POSTLOAD;
96  push @postload, split /;/, $ENV{ALIEN_BUILD_POSTLOAD}
97    if defined $ENV{ALIEN_BUILD_POSTLOAD};
98
99  my $self = $class->new(
100    filename => $file->absolute->stringify,
101    @args,
102  );
103
104  require alienfile;
105
106  foreach my $preload (@preload)
107  {
108    ref $preload eq 'CODE' ? $preload->($self->meta) : $self->meta->apply_plugin($preload);
109  }
110
111  # TODO: do this without a string eval ?
112  ## no critic
113  eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) . qq{
114    package ${class}::Alienfile;
115    do '@{[ $file->absolute->stringify ]}';
116    die \$\@ if \$\@;
117  };
118  die $@ if $@;
119  ## use critic
120
121  foreach my $postload (@postload)
122  {
123    ref $postload eq 'CODE' ? $postload->($self->meta) : $self->meta->apply_plugin($postload);
124  }
125
126  $self->{args} = \@args;
127  unless(defined $self->meta->prop->{arch})
128  {
129    $self->meta->prop->{arch} = 1;
130  }
131
132  unless(defined $self->meta->prop->{network})
133  {
134    $self->meta->prop->{network} = 1;
135    ## https://github.com/PerlAlien/Alien-Build/issues/23#issuecomment-341114414
136    #$self->meta->prop->{network} = 0 if $ENV{NO_NETWORK_TESTING};
137    $self->meta->prop->{network} = 0 if (defined $ENV{ALIEN_INSTALL_NETWORK}) && ! $ENV{ALIEN_INSTALL_NETWORK};
138  }
139
140  unless(defined $self->meta->prop->{local_source})
141  {
142    if(! defined $self->meta->prop->{start_url})
143    {
144      $self->meta->prop->{local_source} = 0;
145    }
146    # we assume URL schemes are at least two characters, that
147    # way Windows absolute paths can be used as local start_url
148    elsif($self->meta->prop->{start_url} =~ /^([a-z]{2,}):/i)
149    {
150      my $scheme = $1;
151      $self->meta->prop->{local_source} = $scheme eq 'file';
152    }
153    else
154    {
155      $self->meta->prop->{local_source} = 1;
156    }
157  }
158
159  return $self;
160}
161
162
163sub resume
164{
165  my(undef, $alienfile, $root) = @_;
166  my $h = JSON::PP::decode_json(_path("$root/state.json")->slurp);
167  my $self = Alien::Build->load("$alienfile", @{ $h->{args} });
168  $self->{install_prop}         = $h->{install};
169  $self->{plugin_instance_prop} = $h->{plugin_instance};
170  $self->{runtime_prop}         = $h->{runtime};
171  $self;
172}
173
174
175sub meta_prop
176{
177  my($class) = @_;
178  $class->meta->prop;
179}
180
181
182sub install_prop
183{
184  shift->{install_prop};
185}
186
187
188sub plugin_instance_prop
189{
190  my($self, $plugin) = @_;
191  my $instance_id = $plugin->instance_id;
192  $self->{plugin_instance_prop}->{$instance_id} ||= {};
193}
194
195
196sub runtime_prop
197{
198  shift->{runtime_prop};
199}
200
201
202sub hook_prop
203{
204  shift->{hook_prop};
205}
206
207sub _command_prop
208{
209  my($self) = @_;
210
211  return {
212    alien => {
213      install => $self->install_prop,
214      runtime => $self->runtime_prop,
215      hook    => $self->hook_prop,
216      meta    => $self->meta_prop,
217    },
218    perl => {
219      config => \%Config::Config,
220    },
221    env => \%ENV,
222  };
223}
224
225
226sub checkpoint
227{
228  my($self) = @_;
229  my $root = $self->root;
230  _path("$root/state.json")->spew(
231    JSON::PP->new->pretty->canonical(1)->ascii->encode({
232      install         => $self->install_prop,
233      runtime         => $self->runtime_prop,
234      plugin_instance => $self->{plugin_instance_prop},
235      args            => $self->{args},
236    })
237  );
238  $self;
239}
240
241
242sub root
243{
244  my($self) = @_;
245  my $root = $self->install_prop->{root};
246  _path($root)->mkpath unless -d $root;
247  $root;
248}
249
250
251sub install_type
252{
253  my($self) = @_;
254  $self->{runtime_prop}->{install_type} ||= $self->probe;
255}
256
257
258sub set_prefix
259{
260  my($self, $prefix) = @_;
261
262  if($self->meta_prop->{destdir})
263  {
264    $self->runtime_prop->{prefix} =
265    $self->install_prop->{prefix} = $prefix;
266  }
267  else
268  {
269    $self->runtime_prop->{prefix} = $prefix;
270    $self->install_prop->{prefix} = $self->install_prop->{stage};
271  }
272}
273
274
275sub set_stage
276{
277  my($self, $dir) = @_;
278  $self->install_prop->{stage} = $dir;
279}
280
281sub _merge
282{
283  my %h;
284  while(@_)
285  {
286    my $mod = shift;
287    my $ver = shift;
288    if((!defined $h{$mod}) || $ver > $h{$mod})
289    { $h{$mod} = $ver }
290  }
291  \%h;
292}
293
294
295sub requires
296{
297  my($self, $phase) = @_;
298  $phase ||= 'any';
299  my $meta = $self->meta;
300  $phase =~ /^(?:any|configure)$/
301  ? $meta->{require}->{$phase} || {}
302  : _merge %{ $meta->{require}->{any} }, %{ $meta->{require}->{$phase} };
303}
304
305
306sub load_requires
307{
308  my($self, $phase, $eval) = @_;
309  my $reqs = $self->requires($phase);
310  foreach my $mod (keys %$reqs)
311  {
312    my $ver = $reqs->{$mod};
313    my $check = sub {
314      my $pm = "$mod.pm";
315      $pm =~ s{::}{/}g;
316      require $pm;
317    };
318    if($eval)
319    {
320      eval { $check->() };
321      die "Required $mod @{[ $ver || 'undef' ]}, missing" if $@;
322    }
323    else
324    {
325      $check->();
326    }
327    # note Test::Alien::Build#alienfile_skip_if_missing_prereqs does a regex
328    # on this diagnostic, so if you change it here, change it there too.
329    die "Required $mod $ver, have @{[ $mod->VERSION || 0 ]}" if $ver && ! $mod->VERSION($ver);
330
331    # allow for requires on Alien::Build or Alien::Base
332    next if $mod eq 'Alien::Build';
333    next if $mod eq 'Alien::Base';
334
335    if($mod->can('bin_dir'))
336    {
337      push @{ $self->{bin_dir} }, $mod->bin_dir;
338    }
339
340    if(($mod->can('runtime_prop') && $mod->runtime_prop)
341    || ($mod->isa('Alien::Base')  && $mod->install_type('share')))
342    {
343      for my $dir (qw(lib share)) {
344          my $path = _path($mod->dist_dir)->child("$dir/pkgconfig");
345          if(-d $path)
346          {
347            push @{ $self->{pkg_config_path} }, $path->stringify;
348          }
349      }
350      my $path = _path($mod->dist_dir)->child('share/aclocal');
351      if(-d $path)
352      {
353        $path = "$path";
354        if($^O eq 'MSWin32')
355        {
356          # convert to MSYS path
357          $path =~ s{^([a-z]):}{/$1/}i;
358        }
359        push @{ $self->{aclocal_path} }, $path;
360      }
361    }
362
363    # sufficiently new Autotools have a aclocal_dir which will
364    # give us the directories we need.
365    if($mod eq 'Alien::Autotools' && $mod->can('aclocal_dir'))
366    {
367      push @{ $self->{aclocal_path} }, $mod->aclocal_dir;
368    }
369
370    if($mod->can('alien_helper'))
371    {
372      my $helpers = $mod->alien_helper;
373      foreach my $name (sort keys %$helpers)
374      {
375        my $code = $helpers->{$name};
376        $self->meta->interpolator->replace_helper($name => $code);
377      }
378    }
379
380  }
381  1;
382}
383
384sub _call_hook
385{
386  my $self = shift;
387
388  local $ENV{PATH} = $ENV{PATH};
389  unshift @PATH, @{ $self->{bin_dir} };
390
391  local $ENV{PKG_CONFIG_PATH} = $ENV{PKG_CONFIG_PATH};
392  unshift @PKG_CONFIG_PATH, @{ $self->{pkg_config_path} };
393
394  local $ENV{ACLOCAL_PATH} = $ENV{ACLOCAL_PATH};
395  # autoconf uses MSYS paths, even for the ACLOCAL_PATH environment variable, so we can't use Env for this.
396  {
397    my @path;
398    @path = split /:/, $ENV{ACLOCAL_PATH} if defined $ENV{ACLOCAL_PATH};
399    unshift @path, @{ $self->{aclocal_path} };
400    $ENV{ACLOCAL_PATH} = join ':', @path;
401  }
402
403  my $config = ref($_[0]) eq 'HASH' ? shift : {};
404  my($name, @args) = @_;
405
406  local $self->{hook_prop} = {};
407
408  $self->meta->call_hook( $config, $name => $self, @args );
409}
410
411
412sub probe
413{
414  my($self) = @_;
415  local $CWD = $self->root;
416  my $dir;
417
418  my $env = $self->_call_hook('override');
419  my $type;
420  my $error;
421
422  $env = '' if $env eq 'default';
423
424  if($env eq 'share')
425  {
426    $type = 'share';
427  }
428  else
429  {
430    $type = eval {
431      $self->_call_hook(
432        {
433          before   => sub {
434            $dir = Alien::Build::TempDir->new($self, "probe");
435            $CWD = "$dir";
436          },
437          after    => sub {
438            $CWD = $self->root;
439          },
440          ok       => 'system',
441          continue => sub {
442            if($_[0] eq 'system')
443            {
444              foreach my $name (qw( probe_class probe_instance_id ))
445              {
446                if(exists $self->hook_prop->{$name} && defined $self->hook_prop->{$name})
447                {
448                  $self->install_prop->{"system_$name"} = $self->hook_prop->{$name};
449                }
450              }
451              return undef;
452            }
453            else
454            {
455              return 1;
456            }
457          },
458        },
459        'probe',
460      );
461    };
462    $error = $@;
463    $type = 'share' unless defined $type;
464  }
465
466  if($error)
467  {
468    if($env eq 'system')
469    {
470      die $error;
471    }
472    $self->log("error in probe (will do a share install): $@");
473    $self->log("Don't panic, we will attempt a share build from source if possible.");
474    $self->log("Do not file a bug unless you expected a system install to succeed.");
475    $type = 'share';
476  }
477
478  if($env && $env ne $type)
479  {
480    die "requested $env install not available";
481  }
482
483  if($type !~ /^(system|share)$/)
484  {
485    Carp::croak "probe hook returned something other than system or share: $type";
486  }
487
488  if($type eq 'share' && (!$self->meta_prop->{network}) && (!$self->meta_prop->{local_source}))
489  {
490    $self->log("install type share requested or detected, but network fetch is turned off");
491    $self->log("see https://metacpan.org/pod/Alien::Build::Manual::FAQ#Network-fetch-is-turned-off");
492    Carp::croak "network fetch is turned off";
493  }
494
495  $self->runtime_prop->{install_type} = $type;
496
497  $type;
498}
499
500
501sub download
502{
503  my($self) = @_;
504
505  return $self unless $self->install_type eq 'share';
506  return $self if $self->install_prop->{complete}->{download};
507
508  if($self->meta->has_hook('download'))
509  {
510    my $tmp;
511    local $CWD;
512    my $valid = 0;
513
514    $self->_call_hook(
515      {
516        before => sub {
517          $tmp = Alien::Build::TempDir->new($self, "download");
518          $CWD = "$tmp";
519        },
520        verify => sub {
521          my @list = grep { $_->basename !~ /^\./, } _path('.')->children;
522
523          my $count = scalar @list;
524
525          if($count == 0)
526          {
527            die "no files downloaded";
528          }
529          elsif($count == 1)
530          {
531            my($archive) = $list[0];
532            if(-d $archive)
533            {
534              $self->log("single dir, assuming directory");
535            }
536            else
537            {
538              $self->log("single file, assuming archive");
539            }
540            $self->install_prop->{download} = $archive->absolute->stringify;
541            $self->install_prop->{complete}->{download} = 1;
542            $valid = 1;
543          }
544          else
545          {
546            $self->log("multiple files, assuming directory");
547            $self->install_prop->{complete}->{download} = 1;
548            $self->install_prop->{download} = _path('.')->absolute->stringify;
549            $valid = 1;
550          }
551        },
552        after  => sub {
553          $CWD = $self->root;
554        },
555      },
556      'download',
557    );
558
559    return $self if $valid;
560  }
561  else
562  {
563    # This will call the default download hook
564    # defined in Core::Download since the recipe
565    # does not provide a download hook
566    return $self->_call_hook('download');
567  }
568
569  die "download failed";
570}
571
572
573sub fetch
574{
575  my $self = shift;
576  $self->_call_hook( 'fetch' => @_ );
577}
578
579
580sub decode
581{
582  my($self, $res) = @_;
583  $self->_call_hook( decode => $res );
584}
585
586
587sub prefer
588{
589  my($self, $res) = @_;
590  $self->_call_hook( prefer => $res );
591}
592
593
594sub extract
595{
596  my($self, $archive) = @_;
597
598  $archive ||= $self->install_prop->{download};
599
600  unless(defined $archive)
601  {
602    die "tried to call extract before download";
603  }
604
605  my $nick_name = 'build';
606
607  if($self->meta_prop->{out_of_source})
608  {
609    $nick_name = 'extract';
610    my $extract = $self->install_prop->{extract};
611    return $extract if defined $extract && -d $extract;
612  }
613
614  my $tmp;
615  local $CWD;
616  my $ret;
617
618  $self->_call_hook({
619
620    before => sub {
621      # called build instead of extract, because this
622      # will be used for the build step, and technically
623      # extract is a substage of build anyway.
624      $tmp = Alien::Build::TempDir->new($self, $nick_name);
625      $CWD = "$tmp";
626    },
627    verify => sub {
628
629      my $path = '.';
630      if($self->meta_prop->{out_of_source} && $self->install_prop->{extract})
631      {
632        $path = $self->install_prop->{extract};
633      }
634
635      my @list = grep { $_->basename !~ /^\./ && $_->basename ne 'pax_global_header' } _path($path)->children;
636
637      my $count = scalar @list;
638
639      if($count == 0)
640      {
641        die "no files extracted";
642      }
643      elsif($count == 1 && -d $list[0])
644      {
645        $ret = $list[0]->absolute->stringify;
646      }
647      else
648      {
649        $ret = "$tmp";
650      }
651
652    },
653    after => sub {
654      $CWD = $self->root;
655    },
656
657  }, 'extract', $archive);
658
659  $self->install_prop->{extract} ||= $ret;
660  $ret ? $ret : ();
661}
662
663
664sub build
665{
666  my($self) = @_;
667
668  # save the evironment, in case some plugins decide
669  # to alter it.  Or us!  See just a few lines below.
670  local %ENV = %ENV;
671
672  my $stage = _path($self->install_prop->{stage});
673  $stage->mkpath;
674
675  my $tmp;
676
677  if($self->install_type eq 'share')
678  {
679    foreach my $suffix ('', '_ffi')
680    {
681      local $CWD;
682      delete $ENV{DESTDIR} unless $self->meta_prop->{destdir};
683
684      my %env_meta = %{ $self->meta_prop   ->{env} || {} };
685      my %env_inst = %{ $self->install_prop->{env} || {} };
686
687      if($self->meta_prop->{env_interpolate})
688      {
689        foreach my $key (keys %env_meta)
690        {
691          $env_meta{$key} = $self->meta->interpolator->interpolate($env_meta{$key});
692        }
693      }
694
695      %ENV = (%ENV, %env_meta);
696      %ENV = (%ENV, %env_inst);
697
698      my $destdir;
699
700      $self->_call_hook(
701      {
702        before => sub {
703          if($self->meta_prop->{out_of_source})
704          {
705            $self->extract;
706            $CWD = $tmp = Alien::Build::TempDir->new($self, 'build');
707          }
708          else
709          {
710            $CWD = $tmp = $self->extract;
711          }
712          if($self->meta_prop->{destdir})
713          {
714            $destdir = Alien::Build::TempDir->new($self, 'destdir');
715            $ENV{DESTDIR} = "$destdir";
716          }
717          $self->_call_hook({ all => 1 }, "patch${suffix}");
718        },
719        after => sub {
720          $destdir = "$destdir" if $destdir;
721        },
722      }, "build${suffix}");
723
724      $self->install_prop->{"_ab_build@{[ $suffix || '_share' ]}"} = "$CWD";
725
726      $self->_call_hook("gather@{[ $suffix || '_share' ]}");
727    }
728  }
729
730  elsif($self->install_type eq 'system')
731  {
732    local $CWD = $self->root;
733    my $dir;
734
735    $self->_call_hook(
736      {
737        before => sub {
738          $dir = Alien::Build::TempDir->new($self, "gather");
739          $CWD = "$dir";
740        },
741        after  => sub {
742          $CWD = $self->root;
743        },
744      },
745      'gather_system',
746    );
747
748    $self->install_prop->{finished} = 1;
749    $self->install_prop->{complete}->{gather_system} = 1;
750  }
751
752  $self;
753}
754
755
756sub test
757{
758  my($self) = @_;
759
760  if($self->install_type eq 'share')
761  {
762    foreach my $suffix ('_share', '_ffi')
763    {
764      if($self->meta->has_hook("test$suffix"))
765      {
766        my $dir = $self->install_prop->{"_ab_build$suffix"};
767        Carp::croak("no build directory to run tests") unless $dir && -d $dir;
768        local $CWD = $dir;
769        $self->_call_hook("test$suffix");
770      }
771    }
772  }
773  else
774  {
775    if($self->meta->has_hook("test_system"))
776    {
777      my $dir = Alien::Build::TempDir->new($self, "test");
778      local $CWD = "$dir";
779      $self->_call_hook("test_system");
780    }
781  }
782
783}
784
785
786sub clean_install
787{
788  my($self) = @_;
789  if($self->install_type eq 'share')
790  {
791    $self->_call_hook("clean_install");
792  }
793}
794
795
796sub system
797{
798  my($self, $command, @args) = @_;
799
800  my $prop = $self->_command_prop;
801
802  ($command, @args) = map {
803    $self->meta->interpolator->interpolate($_, $prop)
804  } ($command, @args);
805
806  $self->log("+ $command @args");
807
808  scalar @args
809    ? system $command, @args
810    : system $command;
811}
812
813
814sub log
815{
816  my(undef, $message) = @_;
817  my $caller = [caller];
818  chomp $message;
819  foreach my $line (split /\n/, $message)
820  {
821    Alien::Build::Log->default->log(
822      caller  => $caller,
823      message => $line,
824    );
825  }
826}
827
828
829{
830  my %meta;
831
832  sub meta
833  {
834    my($class) = @_;
835    $class = ref $class if ref $class;
836    $meta{$class} ||= Alien::Build::Meta->new( class => $class );
837  }
838}
839
840package Alien::Build::Meta;
841
842our @CARP_NOT = qw( alienfile );
843
844sub new
845{
846  my($class, %args) = @_;
847  my $self = bless {
848    phase => 'any',
849    build_suffix => '',
850    require => {
851      any    => {},
852      share  => {},
853      system => {},
854    },
855    around => {},
856    prop => {},
857    %args,
858  }, $class;
859  $self;
860}
861
862
863sub prop
864{
865  shift->{prop};
866}
867
868sub filename
869{
870  my($self, $new) = @_;
871  $self->{filename} = $new if defined $new;
872  $self->{filename};
873}
874
875
876sub add_requires
877{
878  my $self = shift;
879  my $phase = shift;
880  while(@_)
881  {
882    my $module = shift;
883    my $version = shift;
884    my $old = $self->{require}->{$phase}->{$module};
885    if((!defined $old) || $version > $old)
886    { $self->{require}->{$phase}->{$module} = $version }
887  }
888  $self;
889}
890
891
892sub interpolator
893{
894  my($self, $new) = @_;
895  if(defined $new)
896  {
897    if(defined $self->{intr})
898    {
899      Carp::croak "tried to set interpolator twice";
900    }
901    if(ref $new)
902    {
903      $self->{intr} = $new;
904    }
905    else
906    {
907      $self->{intr} = $new->new;
908    }
909  }
910  elsif(!defined $self->{intr})
911  {
912    require Alien::Build::Interpolate::Default;
913    $self->{intr} = Alien::Build::Interpolate::Default->new;
914  }
915  $self->{intr};
916}
917
918
919sub has_hook
920{
921  my($self, $name) = @_;
922  defined $self->{hook}->{$name};
923}
924
925
926sub _instr
927{
928  my($self, $name, $instr) = @_;
929  if(ref($instr) eq 'CODE')
930  {
931    return $instr;
932  }
933  elsif(ref($instr) eq 'ARRAY')
934  {
935    my %phase = (
936      download      => 'share',
937      fetch         => 'share',
938      decode        => 'share',
939      prefer        => 'share',
940      extract       => 'share',
941      patch         => 'share',
942      patch_ffi     => 'share',
943      build         => 'share',
944      build_ffi     => 'share',
945      stage         => 'share',
946      gather_ffi    => 'share',
947      gather_share  => 'share',
948      gather_system => 'system',
949      test_ffi      => 'share',
950      test_share    => 'share',
951      test_system   => 'system',
952    );
953    require Alien::Build::CommandSequence;
954    my $seq = Alien::Build::CommandSequence->new(@$instr);
955    $seq->apply_requirements($self, $phase{$name} || 'any');
956    return $seq;
957  }
958  else
959  {
960    Carp::croak "type not supported as a hook";
961  }
962}
963
964sub register_hook
965{
966  my($self, $name, $instr) = @_;
967  push @{ $self->{hook}->{$name} }, _instr $self, $name, $instr;
968  $self;
969}
970
971
972sub default_hook
973{
974  my($self, $name, $instr) = @_;
975  $self->{default_hook}->{$name} = _instr $self, $name, $instr;
976  $self;
977}
978
979
980sub around_hook
981{
982  my($self, $name, $code) = @_;
983  if(my $old = $self->{around}->{$name})
984  {
985    # this is the craziest shit I have ever
986    # come up with.
987    $self->{around}->{$name} = sub {
988      my $orig = shift;
989      $code->(sub { $old->($orig, @_) }, @_);
990    };
991  }
992  else
993  {
994    $self->{around}->{$name} = $code;
995  }
996}
997
998sub after_hook
999{
1000  my($self, $name, $code) = @_;
1001  $self->around_hook(
1002    $name => sub {
1003      my $orig = shift;
1004      my $ret = $orig->(@_);
1005      $code->(@_);
1006      $ret;
1007    }
1008  );
1009}
1010
1011sub before_hook
1012{
1013  my($self, $name, $code) = @_;
1014  $self->around_hook(
1015    $name => sub {
1016      my $orig = shift;
1017      $code->(@_);
1018      my $ret = $orig->(@_);
1019      $ret;
1020    }
1021  );
1022}
1023
1024
1025sub call_hook
1026{
1027  my $self = shift;
1028  my %args = ref $_[0] ? %{ shift() } : ();
1029  my($name, @args) = @_;
1030  my $error;
1031
1032  my @hooks = @{ $self->{hook}->{$name} || []};
1033
1034  if(@hooks == 0)
1035  {
1036    if(defined $self->{default_hook}->{$name})
1037    {
1038      @hooks = ($self->{default_hook}->{$name})
1039    }
1040    elsif(!$args{all})
1041    {
1042      Carp::croak "No hooks registered for $name";
1043    }
1044  }
1045
1046  my $value;
1047
1048  foreach my $hook (@hooks)
1049  {
1050    if(eval { $args[0]->isa('Alien::Build') })
1051    {
1052      %{ $args[0]->{hook_prop} } = (
1053        name => $name,
1054      );
1055    }
1056
1057    my $wrapper = $self->{around}->{$name} || sub { my $code = shift; $code->(@_) };
1058    my $value;
1059    $args{before}->() if $args{before};
1060    if(ref($hook) eq 'CODE')
1061    {
1062      $value = eval {
1063        my $value = $wrapper->(sub { $hook->(@_) }, @args);
1064        $args{verify}->('code') if $args{verify};
1065        $value;
1066      };
1067    }
1068    else
1069    {
1070      $value = $wrapper->(sub {
1071        eval {
1072          $hook->execute(@_);
1073          $args{verify}->('command') if $args{verify};
1074        };
1075        defined $args{ok} ? $args{ok} : 1;
1076      }, @args);
1077    }
1078    $error = $@;
1079    $args{after}->() if $args{after};
1080    if($args{all})
1081    {
1082      die if $error;
1083    }
1084    else
1085    {
1086      next if $error;
1087      next if $args{continue} && $args{continue}->($value);
1088      return $value;
1089    }
1090  }
1091
1092  die $error if $error && ! $args{all};
1093
1094  $value;
1095}
1096
1097
1098sub apply_plugin
1099{
1100  my($self, $name, @args) = @_;
1101
1102  my $class;
1103  my $pm;
1104  my $found;
1105
1106  if($name =~ /^=(.*)$/)
1107  {
1108    $class = $1;
1109    $pm    = "$class.pm";
1110    $pm    =~ s!::!/!g;
1111    $found = 1;
1112  }
1113
1114  if($name !~ /::/ && !$found)
1115  {
1116    foreach my $inc (@INC)
1117    {
1118      # TODO: allow negotiators to work with @INC hooks
1119      next if ref $inc;
1120      my $file = Path::Tiny->new("$inc/Alien/Build/Plugin/$name/Negotiate.pm");
1121      if(-r $file)
1122      {
1123        $class = "Alien::Build::Plugin::${name}::Negotiate";
1124        $pm    = "Alien/Build/Plugin/$name/Negotiate.pm";
1125        $found = 1;
1126        last;
1127      }
1128    }
1129  }
1130
1131  unless($found)
1132  {
1133    $class = "Alien::Build::Plugin::$name";
1134    $pm    = "Alien/Build/Plugin/$name.pm";
1135    $pm    =~ s{::}{/}g;
1136  }
1137
1138  require $pm unless $class->can('new');
1139  my $plugin = $class->new(@args);
1140  $plugin->init($self);
1141  $self;
1142}
1143
1144package Alien::Build::TempDir;
1145
1146# TODO: it's confusing that there is both a AB::TempDir and AB::Temp
1147# although they do different things.  there could maybe be a better
1148# name for AB::TempDir (maybe AB::TempBuildDir, though that is a little
1149# redundant).  Happily both are private classes, and either are able to
1150# rename, if a good name can be thought of.
1151
1152use overload '""' => sub { shift->as_string }, bool => sub { 1 }, fallback => 1;
1153use File::Temp qw( tempdir );
1154
1155sub new
1156{
1157  my($class, $build, $name) = @_;
1158  my $root = $build->install_prop->{root};
1159  Path::Tiny->new($root)->mkpath unless -d $root;
1160  bless {
1161    dir => Path::Tiny->new(tempdir( "${name}_XXXX", DIR => $root)),
1162  }, $class;
1163}
1164
1165sub as_string
1166{
1167  shift->{dir}->stringify;
1168}
1169
1170sub DESTROY
1171{
1172  my($self) = @_;
1173  if(-d $self->{dir} && $self->{dir}->children == 0)
1174  {
1175    rmdir($self->{dir}) || warn "unable to remove @{[ $self->{dir} ]} $!";
1176  }
1177}
1178
11791;
1180
1181__END__
1182
1183=pod
1184
1185=encoding UTF-8
1186
1187=head1 NAME
1188
1189Alien::Build - Build external dependencies for use in CPAN
1190
1191=head1 VERSION
1192
1193version 2.45
1194
1195=head1 SYNOPSIS
1196
1197 my $build = Alien::Build->load('./alienfile');
1198 $build->load_requires('configure');
1199 $build->set_prefix('/usr/local');
1200 $build->set_stage('/foo/mystage');  # needs to be absolute
1201 $build->load_requires($build->install_type);
1202 $build->download;
1203 $build->build;
1204 # files are now in /foo/mystage, it is your job (or
1205 # ExtUtils::MakeMaker, Module::Build, etc) to copy
1206 # those files into /usr/local
1207
1208=head1 DESCRIPTION
1209
1210This module provides tools for building external (non-CPAN) dependencies
1211for CPAN.  It is mainly designed to be used at install time of a CPAN
1212client, and work closely with L<Alien::Base> which is used at runtime.
1213
1214This is the detailed documentation for the L<Alien::Build> class.
1215If you are starting out you probably want to do so from one of these documents:
1216
1217=over 4
1218
1219=item L<Alien::Build::Manual::Alien>
1220
1221A broad overview of C<Alien-Build> and its ecosystem.
1222
1223=item L<Alien::Build::Manual::AlienUser>
1224
1225For users of an C<Alien::libfoo> that is implemented using L<Alien::Base>.
1226(The developer of C<Alien::libfoo> I<should> provide the documentation
1227necessary, but if not, this is the place to start).
1228
1229=item L<Alien::Build::Manual::AlienAuthor>
1230
1231If you are writing your own L<Alien> based on L<Alien::Build> and L<Alien::Base>.
1232
1233=item L<Alien::Build::Manual::FAQ>
1234
1235If you have a common question that has already been answered, like
1236"How do I use L<alienfile> with some build system".
1237
1238=item L<Alien::Build::Manual::PluginAuthor>
1239
1240This is for the brave souls who want to write plugins that will work with
1241L<Alien::Build> + L<alienfile>.
1242
1243=back
1244
1245Note that you will not usually create a L<Alien::Build> instance
1246directly, but rather be using a thin installer layer, such as
1247L<Alien::Build::MM> (for use with L<ExtUtils::MakeMaker>) or
1248L<Alien::Build::MB> (for use with L<Module::Build>).  One of the
1249goals of this project is to remain installer agnostic.
1250
1251=head1 CONSTRUCTORS
1252
1253=head2 new
1254
1255 my $build = Alien::Build->new;
1256
1257This creates a new empty instance of L<Alien::Build>.  Normally you will
1258want to use C<load> below to create an instance of L<Alien::Build> from
1259an L<alienfile> recipe.
1260
1261=head2 load
1262
1263 my $build = Alien::Build->load($alienfile);
1264
1265This creates an L<Alien::Build> instance with the given L<alienfile>
1266recipe.
1267
1268=head2 resume
1269
1270 my $build = Alien::Build->resume($alienfile, $root);
1271
1272Load a checkpointed L<Alien::Build> instance.  You will need the original
1273L<alienfile> and the build root (usually C<_alien>), and a build that
1274had been properly checkpointed using the C<checkpoint> method below.
1275
1276=head1 PROPERTIES
1277
1278There are three main properties for L<Alien::Build>.  There are a number
1279of properties documented here with a specific usage.  Note that these
1280properties may need to be serialized into something primitive like JSON
1281that does not support: regular expressions, code references of blessed
1282objects.
1283
1284If you are writing a plugin (L<Alien::Build::Plugin>) you should use a
1285prefix like "plugin_I<name>" (where I<name> is the name of your plugin)
1286so that it does not interfere with other plugin or future versions of
1287L<Alien::Build>.  For example, if you were writing
1288C<Alien::Build::Plugin::Fetch::NewProtocol>, please use the prefix
1289C<plugin_fetch_newprotocol>:
1290
1291 sub init
1292 {
1293   my($self, $meta) = @_;
1294
1295   $meta->prop( plugin_fetch_newprotocol_foo => 'some value' );
1296
1297   $meta->register_hook(
1298     some_hook => sub {
1299       my($build) = @_;
1300       $build->install_prop->{plugin_fetch_newprotocol_bar} = 'some other value';
1301       $build->runtime_prop->{plugin_fetch_newprotocol_baz} = 'and another value';
1302     }
1303   );
1304 }
1305
1306If you are writing a L<alienfile> recipe please use the prefix C<my_>:
1307
1308 use alienfile;
1309
1310 meta_prop->{my_foo} = 'some value';
1311
1312 probe sub {
1313   my($build) = @_;
1314   $build->install_prop->{my_bar} = 'some other value';
1315   $build->install_prop->{my_baz} = 'and another value';
1316 };
1317
1318Any property may be used from a command:
1319
1320 probe [ 'some command %{.meta.plugin_fetch_newprotocol_foo}' ];
1321 probe [ 'some command %{.install.plugin_fetch_newprotocol_bar}' ];
1322 probe [ 'some command %{.runtime.plugin_fetch_newprotocol_baz}' ];
1323 probe [ 'some command %{.meta.my_foo}' ];
1324 probe [ 'some command %{.install.my_bar}' ];
1325 probe [ 'some command %{.runtime.my_baz}' ];
1326
1327=head2 meta_prop
1328
1329 my $href = $build->meta_prop;
1330 my $href = Alien::Build->meta_prop;
1331
1332Meta properties have to do with the recipe itself, and not any particular
1333instance that probes or builds that recipe.  Meta properties can be changed
1334from within an L<alienfile> using the C<meta_prop> directive, or from
1335a plugin from its C<init> method (though should NOT be modified from any
1336hooks registered within that C<init> method).  This is not strictly enforced,
1337but if you do not follow this rule your recipe will likely be broken.
1338
1339=over
1340
1341=item arch
1342
1343This is a hint to an installer like L<Alien::Build::MM> or L<Alien::Build::MB>,
1344that the library or tool contains architecture dependent files and so should
1345be stored in an architecture dependent location.  If not specified by your
1346L<alienfile> then it will be set to true.
1347
1348=item destdir
1349
1350Use the C<DESTDIR> environment variable to stage your install before
1351copying the files into C<blib>.  This is the preferred method of
1352installing libraries because it improves reliability.  This technique
1353is supported by C<autoconf> and others.
1354
1355=item destdir_filter
1356
1357Regular expression for the files that should be copied from the C<DESTDIR>
1358into the stage directory.  If not defined, then all files will be copied.
1359
1360=item destdir_ffi_filter
1361
1362Same as C<destdir_filter> except applies to C<build_ffi> instead of C<build>.
1363
1364=item env
1365
1366Environment variables to override during the build stage.
1367
1368=item env_interpolate
1369
1370Environment variable values will be interpolated with helpers.  Example:
1371
1372 meta->prop->{env_interpolate} = 1;
1373 meta->prop->{env}->{PERL} = '%{perl}';
1374
1375=item local_source
1376
1377Set to true if source code package is available locally.  (that is not fetched
1378over the internet).  This is computed by default based on the C<start_url>
1379property.  Can be set by an L<alienfile> or plugin.
1380
1381=item platform
1382
1383Hash reference.  Contains information about the platform beyond just C<$^O>.
1384
1385=over 4
1386
1387=item compiler_type
1388
1389Refers to the type of flags that the compiler accepts.  May be expanded in the
1390future, but for now, will be one of:
1391
1392=over 4
1393
1394=item microsoft
1395
1396On Windows when using Microsoft Visual C++
1397
1398=item unix
1399
1400Virtually everything else, including gcc on windows.
1401
1402=back
1403
1404The main difference is that with Visual C++ C<-LIBPATH> should be used instead
1405of C<-L>, and static libraries should have the C<.LIB> suffix instead of C<.a>.
1406
1407=item system_type
1408
1409C<$^O> is frequently good enough to make platform specific logic in your
1410L<alienfile>, this handles the case when $^O can cover platforms that provide
1411multiple environments that Perl might run under.  The main example is windows,
1412but others may be added in the future.
1413
1414=over 4
1415
1416=item unix
1417
1418=item vms
1419
1420=item windows-activestate
1421
1422=item windows-microsoft
1423
1424=item windows-mingw
1425
1426=item windows-strawberry
1427
1428=item windows-unknown
1429
1430=back
1431
1432Note that C<cygwin> and C<msys> are considered C<unix> even though they run
1433on windows!
1434
1435=back
1436
1437=item out_of_source
1438
1439Build in a different directory from the where the source code is stored.
1440In autoconf this is referred to as a "VPATH" build.  Everyone else calls this
1441an "out-of-source" build.  When this property is true, instead of extracting
1442to the source build root, the downloaded source will be extracted to an source
1443extraction directory and the source build root will be empty.  You can use the
1444C<extract> install property to get the location of the extracted source.
1445
1446=item network
1447
1448True if a network fetch is available.  This should NOT be set by an L<alienfile>
1449or plugin.  This is computed based on the C<ALIEN_INSTALL_NETWORK> environment
1450variables.
1451
1452=item start_url
1453
1454The default or start URL used by fetch plugins.
1455
1456=back
1457
1458=head2 install_prop
1459
1460 my $href = $build->install_prop;
1461
1462Install properties are used during the install phase (either
1463under C<share> or C<system> install).  They are remembered for
1464the entire install phase, but not kept around during the runtime
1465phase.  Thus they cannot be accessed from your L<Alien::Base>
1466based module.
1467
1468=over
1469
1470=item autoconf_prefix
1471
1472The prefix as understood by autoconf.  This is only different on Windows
1473Where MSYS is used and paths like C<C:/foo> are  represented as C</C/foo>
1474which are understood by the MSYS tools, but not by Perl.  You should
1475only use this if you are using L<Alien::Build::Plugin::Autoconf> in
1476your L<alienfile>.
1477
1478=item download
1479
1480The location of the downloaded archive (tar.gz, or similar) or directory.
1481
1482=item env
1483
1484Environment variables to override during the build stage.
1485
1486=item extract
1487
1488The location of the last source extraction.  For a "out-of-source" build
1489(see the C<out_of_source> meta property above), this will only be set once.
1490For other types of builds, the source code may be extracted multiple times,
1491and thus this property may change.
1492
1493=item old
1494
1495Hash containing information on a previously installed Alien of the same
1496name, if available.  This may be useful in cases where you want to
1497reuse the previous install if it is still sufficient.
1498
1499=over 4
1500
1501=item prefix
1502
1503The prefix for the previous install.  Versions prior to 1.42 unfortunately
1504had this in typo form of C<preifx>.
1505
1506=item runtime
1507
1508The runtime properties from the previous install.
1509
1510=back
1511
1512=item patch
1513
1514Directory with patches.
1515
1516=item prefix
1517
1518The install time prefix.  Under a C<destdir> install this is the
1519same as the runtime or final install location.  Under a non-C<destdir>
1520install this is the C<stage> directory (usually the appropriate
1521share directory under C<blib>).
1522
1523=item root
1524
1525The build root directory.  This will be an absolute path.  It is the
1526absolute form of C<./_alien> by default.
1527
1528=item stage
1529
1530The stage directory where files will be copied.  This is usually the
1531root of the blib share directory.
1532
1533=item system_probe_class
1534
1535After the probe step this property may contain the plugin class that
1536performed the system probe.  It shouldn't be filled in directly by
1537the plugin (instead if should use the hook property C<probe_class>,
1538see below).  This is optional, and not all probe plugins will provide
1539this information.
1540
1541=item system_probe_instance_id
1542
1543After the probe step this property may contain the plugin instance id that
1544performed the system probe.  It shouldn't be filled in directly by
1545the plugin (instead if should use the hook property C<probe_instance_id>,
1546see below).  This is optional, and not all probe plugins will provide
1547this information.
1548
1549=back
1550
1551=head2 plugin_instance_prop
1552
1553 my $href = $build->plugin_instance_prop($plugin);
1554
1555This returns the private plugin instance properties for a given plugin.
1556This method should usually only be called internally by plugins themselves
1557to keep track of internal state.  Because the content can be used arbitrarily
1558by the owning plugin because it is private to the plugin, and thus is not
1559part of the L<Alien::Build> spec.
1560
1561=head2 runtime_prop
1562
1563 my $href = $build->runtime_prop;
1564
1565Runtime properties are used during the install and runtime phases
1566(either under C<share> or C<system> install).  This should include
1567anything that you will need to know to use the library or tool
1568during runtime, and shouldn't include anything that is no longer
1569relevant once the install process is complete.
1570
1571=over 4
1572
1573=item alien_build_version
1574
1575The version of L<Alien::Build> used to install the library or tool.
1576
1577=item alt
1578
1579Alternate configurations.  If the alienized package has multiple
1580libraries this could be used to store the different compiler or
1581linker flags for each library.
1582
1583=item cflags
1584
1585The compiler flags
1586
1587=item cflags_static
1588
1589The static compiler flags
1590
1591=item command
1592
1593The command name for tools where the name my differ from platform to
1594platform.  For example, the GNU version of make is usually C<make> in
1595Linux and C<gmake> on FreeBSD.
1596
1597=item ffi_name
1598
1599The name DLL or shared object "name" to use when searching for dynamic
1600libraries at runtime.  This is passed into L<FFI::CheckLib>, so if
1601your library is something like C<libarchive.so> or C<archive.dll> you
1602would set this to C<archive>.  This may be a string or an array of
1603strings.
1604
1605=item ffi_checklib
1606
1607This property contains two sub properties:
1608
1609=over 4
1610
1611=item share
1612
1613 $build->runtime_prop->{ffi_checklib}->{share} = [ ... ];
1614
1615Array of additional L<FFI::CheckLib> flags to pass in to C<find_lib>
1616for a C<share> install.
1617
1618=item system
1619
1620Array of additional L<FFI::CheckLib> flags to pass in to C<find_lib>
1621for a C<system> install.
1622
1623Among other things, useful for specifying the C<try_linker_script>
1624flag:
1625
1626 $build->runtime_prop->{ffi_checklib}->{system} = [ try_linker_script => 1 ];
1627
1628=back
1629
1630=item install_type
1631
1632The install type.  Is one of:
1633
1634=over 4
1635
1636=item system
1637
1638For when the library or tool is provided by the operating system, can be
1639detected by L<Alien::Build>, and is considered satisfactory by the
1640C<alienfile> recipe.
1641
1642=item share
1643
1644For when a system install is not possible, the library source will be
1645downloaded from the internet or retrieved in another appropriate fashion
1646and built.
1647
1648=back
1649
1650=item libs
1651
1652The library flags
1653
1654=item libs_static
1655
1656The static library flags
1657
1658=item perl_module_version
1659
1660The version of the Perl module used to install the alien (if available).
1661For example if L<Alien::curl> is installing C<libcurl> this would be the
1662version of L<Alien::curl> used during the install step.
1663
1664=item prefix
1665
1666The final install root.  This is usually they share directory.
1667
1668=item version
1669
1670The version of the library or tool
1671
1672=back
1673
1674=head2 hook_prop
1675
1676 my $href = $build->hook_prop;
1677
1678Hook properties are for the currently running (if any) hook.  They are
1679used only during the execution of each hook and are discarded after.
1680If no hook is currently running then C<hook_prop> will return C<undef>.
1681
1682=over 4
1683
1684=item name
1685
1686The name of the currently running hook.
1687
1688=item version (probe)
1689
1690Probe and PkgConfig plugins I<may> set this property indicating the
1691version of the alienized package.  Not all plugins and configurations
1692may be able to provide this.
1693
1694=item probe_class (probe)
1695
1696Probe and PkgConfig plugins I<may> set this property indicating the
1697plugin class that made the probe.  If the probe results in a system
1698install this will be propagated to C<system_probe_class> for later
1699use.
1700
1701=item probe_instance_id (probe)
1702
1703Probe and PkgConfig plugins I<may> set this property indicating the
1704plugin instance id that made the probe.  If the probe results in a
1705system install this will be propagated to C<system_probe_instance_id>
1706for later use.
1707
1708=back
1709
1710=head1 METHODS
1711
1712=head2 checkpoint
1713
1714 $build->checkpoint;
1715
1716Save any install or runtime properties so that they can be reloaded on
1717a subsequent run in a separate process.  This is useful if your build
1718needs to be done in multiple stages from a C<Makefile>, such as with
1719L<ExtUtils::MakeMaker>.  Once checkpointed you can use the C<resume>
1720constructor (documented above) to resume the probe/build/install]
1721process.
1722
1723=head2 root
1724
1725 my $dir = $build->root;
1726
1727This is just a shortcut for:
1728
1729 my $root = $build->install_prop->{root};
1730
1731Except that it will be created if it does not already exist.
1732
1733=head2 install_type
1734
1735 my $type = $build->install_type;
1736
1737This will return the install type.  (See the like named install property
1738above for details).  This method will call C<probe> if it has not already
1739been called.
1740
1741=head2 set_prefix
1742
1743 $build->set_prefix($prefix);
1744
1745Set the final (unstaged) prefix.  This is normally only called by L<Alien::Build::MM>
1746and similar modules.  It is not intended for use from plugins or from an L<alienfile>.
1747
1748=head2 set_stage
1749
1750 $build->set_stage($dir);
1751
1752Sets the stage directory.  This is normally only called by L<Alien::Build::MM>
1753and similar modules.  It is not intended for use from plugins or from an L<alienfile>.
1754
1755=head2 requires
1756
1757 my $hash = $build->requires($phase);
1758
1759Returns a hash reference of the modules required for the given phase.  Phases
1760include:
1761
1762=over 4
1763
1764=item configure
1765
1766These modules must already be available when the L<alienfile> is read.
1767
1768=item any
1769
1770These modules are used during either a C<system> or C<share> install.
1771
1772=item share
1773
1774These modules are used during the build phase of a C<share> install.
1775
1776=item system
1777
1778These modules are used during the build phase of a C<system> install.
1779
1780=back
1781
1782=head2 load_requires
1783
1784 $build->load_requires($phase);
1785
1786This loads the appropriate modules for the given phase (see C<requires> above
1787for a description of the phases).
1788
1789=head2 probe
1790
1791 my $install_type = $build->probe;
1792
1793Attempts to determine if the operating system has the library or
1794tool already installed.  If so, then the string C<system> will
1795be returned and a system install will be performed.  If not,
1796then the string C<share> will be installed and the tool or
1797library will be downloaded and built from source.
1798
1799If the environment variable C<ALIEN_INSTALL_TYPE> is set, then that
1800will force a specific type of install.  If the detection logic
1801cannot accommodate the install type requested then it will fail with
1802an exception.
1803
1804=head2 download
1805
1806 $build->download;
1807
1808Download the source, usually as a tarball, usually from the internet.
1809
1810Under a C<system> install this does not do anything.
1811
1812=head2 fetch
1813
1814 my $res = $build->fetch;
1815 my $res = $build->fetch($url, %options);
1816
1817Fetch a resource using the fetch hook.  Returns the same hash structure
1818described below in the hook documentation.
1819
1820[version 2.39]
1821
1822As of L<Alien::Build> 2.39, these options are supported:
1823
1824=over 4
1825
1826=item http_headers
1827
1828 my $res = $build->fetch($url, http_headers => [ $key1 => $value1, $key2 => $value 2, ... ]);
1829
1830Set the HTTP request headers on all outgoing HTTP requests.  Note that not all
1831protocols or fetch plugins support setting request headers, but the ones that
1832do not I<should> issue a warning if you try to set request headers and they
1833are not supported.
1834
1835=back
1836
1837=head2 decode
1838
1839 my $decoded_res = $build->decode($res);
1840
1841Decode the HTML or file listing returned by C<fetch>.  Returns the same
1842hash structure described below in the hook documentation.
1843
1844=head2 prefer
1845
1846 my $sorted_res = $build->prefer($res);
1847
1848Filter and sort candidates.  The preferred candidate will be returned first in the list.
1849The worst candidate will be returned last.  Returns the same hash structure described
1850below in the hook documentation.
1851
1852=head2 extract
1853
1854 my $dir = $build->extract;
1855 my $dir = $build->extract($archive);
1856
1857Extracts the given archive into a fresh directory.  This is normally called internally
1858to L<Alien::Build>, and for normal usage is not needed from a plugin or L<alienfile>.
1859
1860=head2 build
1861
1862 $build->build;
1863
1864Run the build step.  It is expected that C<probe> and C<download>
1865have already been performed.  What it actually does depends on the
1866type of install:
1867
1868=over 4
1869
1870=item share
1871
1872The source is extracted, and built as determined by the L<alienfile>
1873recipe.  If there is a C<gather_share> that will be executed last.
1874
1875=item system
1876
1877The C<gather_system> hook will be executed.
1878
1879=back
1880
1881=head2 test
1882
1883 $build->test;
1884
1885Run the test phase
1886
1887=head2 clean_install
1888
1889 $build->clean_install
1890
1891Clean files from the final install location.  The default implementation removes all
1892files recursively except for the C<_alien> directory.  This is helpful when you have
1893an old install with files that may break the new build.
1894
1895For a non-share install this doesn't do anything.
1896
1897=head2 system
1898
1899 $build->system($command);
1900 $build->system($command, @args);
1901
1902Interpolates the command and arguments and run the results using
1903the Perl C<system> command.
1904
1905=head2 log
1906
1907 $build->log($message);
1908
1909Send a message to the log.  By default this prints to C<STDOUT>.
1910
1911=head2 meta
1912
1913 my $meta = Alien::Build->meta;
1914 my $meta = $build->meta;
1915
1916Returns the meta object for your L<Alien::Build> class or instance.  The
1917meta object is a way to manipulate the recipe, and so any changes to the
1918meta object should be made before the C<probe>, C<download> or C<build> steps.
1919
1920=head1 META METHODS
1921
1922=head2 prop
1923
1924 my $href = $build->meta->prop;
1925
1926Meta properties.  This is the same as calling C<meta_prop> on
1927the class or L<Alien::Build> instance.
1928
1929=head2 add_requires
1930
1931 Alien::Build->meta->add_requires($phase, $module => $version, ...);
1932
1933Add the requirement to the given phase.  Phase should be one of:
1934
1935=over 4
1936
1937=item configure
1938
1939=item any
1940
1941=item share
1942
1943=item system
1944
1945=back
1946
1947=head2 interpolator
1948
1949 my $interpolator = $build->meta->interpolator;
1950 my $interpolator = Alien::Build->interpolator;
1951
1952Returns the L<Alien::Build::Interpolate> instance for the L<Alien::Build> class.
1953
1954=head2 has_hook
1955
1956 my $bool = $build->meta->has_hook($name);
1957 my $bool = Alien::Build->has_hook($name);
1958
1959Returns if there is a usable hook registered with the given name.
1960
1961=head2 register_hook
1962
1963 $build->meta->register_hook($name, $instructions);
1964 Alien::Build->meta->register_hook($name, $instructions);
1965
1966Register a hook with the given name.  C<$instruction> should be either
1967a code reference, or a command sequence, which is an array reference.
1968
1969=head2 default_hook
1970
1971 $build->meta->default_hook($name, $instructions);
1972 Alien::Build->meta->default_hook($name, $instructions);
1973
1974Register a default hook, which will be used if the L<alienfile> does not
1975register its own hook with that name.
1976
1977=head2 around_hook
1978
1979 $build->meta->around_hook($hook, $code);
1980 Alien::Build->meta->around_hook($name, $code);
1981
1982Wrap the given hook with a code reference.  This is similar to a L<Moose>
1983method modifier, except that it wraps around the given hook instead of
1984a method.  For example, this will add a probe system requirement:
1985
1986 $build->meta->around_hook(
1987   probe => sub {
1988     my $orig = shift;
1989     my $build = shift;
1990     my $type = $orig->($build, @_);
1991     return $type unless $type eq 'system';
1992     # also require a configuration file
1993     if(-f '/etc/foo.conf')
1994     {
1995       return 'system';
1996     }
1997     else
1998     {
1999       return 'share';
2000     }
2001   },
2002 );
2003
2004=head2 apply_plugin
2005
2006 Alien::Build->meta->apply_plugin($name);
2007 Alien::Build->meta->apply_plugin($name, @args);
2008
2009Apply the given plugin with the given arguments.
2010
2011=head1 ENVIRONMENT
2012
2013L<Alien::Build> responds to these environment variables:
2014
2015=over 4
2016
2017=item ALIEN_INSTALL_NETWORK
2018
2019If set to true (the default), then network fetch will be allowed.  If set to
2020false, then network fetch will not be allowed.
2021
2022What constitutes a local vs. network fetch is determined based on the C<start_url>
2023and C<local_source> meta properties.  An L<alienfile> or plugin C<could> override
2024this detection (possibly inappropriately), so this variable is not a substitute
2025for properly auditing of Perl modules for environments that require that.
2026
2027=item ALIEN_INSTALL_TYPE
2028
2029If set to C<share> or C<system>, it will override the system detection logic.
2030If set to C<default>, it will use the default setting for the L<alienfile>.
2031The behavior of other values is undefined.
2032
2033Although the recommended way for a consumer to use an L<Alien::Base> based L<Alien>
2034is to declare it as a static configure and build-time dependency, some consumers
2035may prefer to fallback on using an L<Alien> only when the consumer itself cannot
2036detect the necessary package. In some cases the consumer may want the user to opt-in
2037to using an L<Alien> before requiring it.
2038
2039To keep the interface consistent among Aliens, the consumer of the fallback opt-in
2040L<Alien> may fallback on the L<Alien> if the environment variable C<ALIEN_INSTALL_TYPE>
2041is set to any value. The rationale is that by setting this environment variable the
2042user is aware that L<Alien> modules may be installed and have indicated consent.
2043The actual implementation of this, by its nature would have to be in the consuming
2044CPAN module.
2045
2046=item ALIEN_BUILD_LOG
2047
2048The default log class used.  See L<Alien::Build::Log> and L<Alien::Build::Log::Default>.
2049
2050=item ALIEN_BUILD_RC
2051
2052Perl source file which can override some global defaults for L<Alien::Build>,
2053by, for example, setting preload and postload plugins.
2054
2055=item ALIEN_BUILD_PKG_CONFIG
2056
2057Override the logic in L<Alien::Build::Plugin::PkgConfig::Negotiate> which
2058chooses the best C<pkg-config> plugin.
2059
2060=item ALIEN_BUILD_PRELOAD
2061
2062semicolon separated list of plugins to automatically load before parsing
2063your L<alienfile>.
2064
2065=item ALIEN_BUILD_POSTLOAD
2066
2067semicolon separated list of plugins to automatically load after parsing
2068your L<alienfile>.
2069
2070=item DESTDIR
2071
2072This environment variable will be manipulated during a destdir install.
2073
2074=item PKG_CONFIG
2075
2076This environment variable can be used to override the program name for C<pkg-config>
2077when using the command line plugin: L<Alien::Build::Plugin::PkgConfig::CommandLine>.
2078
2079=item ftp_proxy, all_proxy
2080
2081If these environment variables are set, it may influence the Download negotiation
2082plugin L<Alien::Build::Plugin::Downaload::Negotiate>.  Other proxy variables may
2083be used by some Fetch plugins, if they support it.
2084
2085=back
2086
2087=head1 SUPPORT
2088
2089The intent of the C<Alien-Build> team is to support as best as possible
2090all Perls from 5.8.4 to the latest production version.  So long as they
2091are also supported by the Perl toolchain.
2092
2093Please feel encouraged to report issues that you encounter to the
2094project GitHub Issue tracker:
2095
2096=over 4
2097
2098=item L<https://github.com/PerlAlien/Alien-Build/issues>
2099
2100=back
2101
2102Better if you can fix the issue yourself, please feel encouraged to open
2103pull-request on the project GitHub:
2104
2105=over 4
2106
2107=item L<https://github.com/PerlAlien/Alien-Build/pulls>
2108
2109=back
2110
2111If you are confounded and have questions, join us on the C<#native>
2112channel on irc.perl.org.  The C<Alien-Build> developers frequent this
2113channel and can probably help point you in the right direction.  If you
2114don't have an IRC client handy, you can use this web interface:
2115
2116=over 4
2117
2118=item L<https://chat.mibbit.com/?channel=%23native&server=irc.perl.org>
2119
2120=back
2121
2122=head1 SEE ALSO
2123
2124L<Alien::Build::Manual::AlienAuthor>,
2125L<Alien::Build::Manual::AlienUser>,
2126L<Alien::Build::Manual::Contributing>,
2127L<Alien::Build::Manual::FAQ>,
2128L<Alien::Build::Manual::PluginAuthor>
2129
2130L<alienfile>, L<Alien::Build::MM>, L<Alien::Build::Plugin>, L<Alien::Base>, L<Alien>
2131
2132=head1 THANKS
2133
2134L<Alien::Base> was originally written by Joel Berger, the rest of this project would
2135not have been possible without him getting the project started.  Thanks to his support
2136I have been able to augment the original L<Alien::Base> system with a reliable set
2137of tools (L<Alien::Build>, L<alienfile>, L<Test::Alien>), which make up this toolset.
2138
2139The original L<Alien::Base> is still copyright (c) 2012-2020 Joel Berger.  It has
2140the same license as the rest of the Alien::Build and related tools distributed as
2141C<Alien-Build>.  Joel Berger thanked a number of people who helped in in the development
2142of L<Alien::Base>, in the documentation for that module.
2143
2144I would also like to acknowledge the other members of the PerlAlien github
2145organization, Zakariyya Mughal (sivoais, ZMUGHAL) and mohawk (ETJ).  Also important
2146in the early development of L<Alien::Build> were the early adopters Chase Whitener
2147(genio, CAPOEIRAB, author of L<Alien::libuv>), William N. Braswell, Jr (willthechill,
2148WBRASWELL, author of L<Alien::JPCRE2> and L<Alien::PCRE2>) and Ahmad Fatoum (a3f,
2149ATHREEF, author of L<Alien::libudev> and L<Alien::LibUSB>).
2150
2151The Alien ecosystem owes a debt to Dan Book, who goes by Grinnz on IRC, for answering
2152question about how to use L<Alien::Build> and friends.
2153
2154=head1 AUTHOR
2155
2156Author: Graham Ollis E<lt>plicease@cpan.orgE<gt>
2157
2158Contributors:
2159
2160Diab Jerius (DJERIUS)
2161
2162Roy Storey (KIWIROY)
2163
2164Ilya Pavlov
2165
2166David Mertens (run4flat)
2167
2168Mark Nunberg (mordy, mnunberg)
2169
2170Christian Walde (Mithaldu)
2171
2172Brian Wightman (MidLifeXis)
2173
2174Zaki Mughal (zmughal)
2175
2176mohawk (mohawk2, ETJ)
2177
2178Vikas N Kumar (vikasnkumar)
2179
2180Flavio Poletti (polettix)
2181
2182Salvador Fandiño (salva)
2183
2184Gianni Ceccarelli (dakkar)
2185
2186Pavel Shaydo (zwon, trinitum)
2187
2188Kang-min Liu (劉康民, gugod)
2189
2190Nicholas Shipp (nshp)
2191
2192Juan Julián Merelo Guervós (JJ)
2193
2194Joel Berger (JBERGER)
2195
2196Petr Písař (ppisar)
2197
2198Lance Wicks (LANCEW)
2199
2200Ahmad Fatoum (a3f, ATHREEF)
2201
2202José Joaquín Atria (JJATRIA)
2203
2204Duke Leto (LETO)
2205
2206Shoichi Kaji (SKAJI)
2207
2208Shawn Laffan (SLAFFAN)
2209
2210Paul Evans (leonerd, PEVANS)
2211
2212Håkon Hægland (hakonhagland, HAKONH)
2213
2214nick nauwelaerts (INPHOBIA)
2215
2216=head1 COPYRIGHT AND LICENSE
2217
2218This software is copyright (c) 2011-2020 by Graham Ollis.
2219
2220This is free software; you can redistribute it and/or modify it under
2221the same terms as the Perl 5 programming language system itself.
2222
2223=cut
2224