package Alien::Build::MB;

use strict;
use warnings;
use Path::Tiny ();
use Alien::Build;
use base qw( Module::Build );

# ABSTRACT: Alien::Build installer class for Module::Build
our $VERSION = '0.09'; # VERSION


sub new
{
  my($class, %args) = @_;

  if(! -f 'alienfile')
  {
    die "unable to find alienfile";
  }
  else
  {
    my $build = Alien::Build->load('alienfile', root => '_alien');
    $build->load_requires('configure');
    $build->root;
    $build->checkpoint;
  }

  my $self = $class->SUPER::new(%args);

  my $build = $self->alien_build(1);

  $self->_add_prereq( "${_}_requires", 'Module::Build'    => '0.36' ) for qw( configure build );
  $self->_add_prereq( "${_}_requires", 'Alien::Build::MB' => '0.01' ) for qw( configure build );
  $self->add_to_cleanup("_alien");

  foreach my $hook_name (qw( test_ffi test_share test_system ))
  {
    if($build->meta->has_hook($hook_name))
    {
      $self->_add_prereq( "configure_requires", 'Alien::Build::MB' => '0.05');
      last;
    }
  }

  my %config_requires = %{ $build->requires('configure') };
  foreach my $module (keys %config_requires)
  {
    my $version = $config_requires{$module};
    $self->_add_prereq( 'configure_requires', $module => $version );
  }

  unless($build->install_type =~ /^(share|system)$/)
  {
    die "unknown install type: @{[ $build->install_type ]}";
  }

  my %build_requires = %{ $build->requires($build->install_type) };
  foreach my $module (keys %build_requires)
  {
    my $version = $build_requires{$module};
    $self->_add_prereq( 'build_requires', $module => $version );
  }

  my $final_dest = $self->install_destination($build->meta_prop->{arch} ? 'arch' : 'lib');

  my $prefix = Path::Tiny->new($final_dest)
                         ->child("auto/share/dist")
                         ->child($self->dist_name)
                         ->absolute;
  my $stage  = Path::Tiny->new("blib/lib/auto/share/dist")
                         ->child($self->dist_name)
                         ->absolute;

  $build->set_stage ($stage->stringify );
  $build->set_prefix($prefix->stringify);
  $build->runtime_prop->{perl_module_version} = $self->dist_version;

  $build->checkpoint;

  if($self->alien_alienfile_meta)
  {
    $self->meta_merge->{x_alienfile} = {
      generated_by => "@{[ __PACKAGE__ ]} version @{[ __PACKAGE__->VERSION || 'dev' ]}",
      requires => {
        map {
          my %reqs = %{ $build->requires($_) };
          $reqs{$_} = "$reqs{$_}" for keys %reqs;
          $_ => \%reqs;
        } qw( share system )
      },
    };
  }

  $self;
}


__PACKAGE__->add_property( alien_alienfile_meta => 1 );


sub alien_build
{
  my($self, $noload) = @_;
  my $build = Alien::Build->resume('alienfile', '_alien');
  $build->load_requires('configure');
  $build->load_requires($build->install_type) unless $noload;
  $build;
}

sub _alien_already_done ($)
{
  my($name) = @_;
  my $path = Path::Tiny->new("_alien/mb/$name");
  return -f $path;
}

sub _alien_touch ($)
{
  my($name) = @_;
  my $path = Path::Tiny->new("_alien/mb/$name");
  $path->parent->mkpath;
  $path->touch;
}


sub ACTION_alien_download
{
  my($self) = @_;
  return $self if _alien_already_done 'download';
  my $build = $self->alien_build;
  $build->download;
  $build->checkpoint;
  _alien_touch 'download';
  $self;
}


sub ACTION_alien_build
{
  my($self) = @_;
  return $self if _alien_already_done 'build';
  $self->depends_on('alien_download');

  my $build = $self->alien_build;
  $build->build;

  if($build->meta_prop->{arch})
  {
    my $archdir = Path::Tiny->new("./blib/arch/auto/@{[ join '/', split /-/, $self->dist_name ]}");
    $archdir->mkpath;
    my $archfile = $archdir->child($archdir->basename . ".txt");
    $archfile->spew('Alien based distribution with architecture specific file in share');
  }

  {
    my @parts = split /-/, $self->dist_name;
    my $package = join '::', @parts;
    my $install_files = Path::Tiny->new("./blib/lib")->child( @parts, 'Install', 'Files.pm' );
    $install_files->parent->mkpath;
    $install_files->spew_utf8(
      "package ${package}::Install::Files;\n",
      "use strict;\n",
      "use warnings;\n",
      "require ${package};\n",
      "sub Inline { shift; ${package}->Inline(\@_) }\n",
      "1;\n",
      "\n",
      "=begin Pod::Coverage\n",
      "\n",
      "  Inline\n",
      "\n",
      "=cut\n",
    );
  };

  $build->checkpoint;

  _alien_touch 'build';
  $self;
}


sub ACTION_alien_test
{
  my($self) = @_;
  $self->depends_on('alien_build');

  my $build = $self->alien_build;
  if($build->can('test'))
  {
    $build->test;
    $build->checkpoint;
  }
  $self;
}

sub ACTION_test
{
  my($self) = @_;
  $self->depends_on('alien_test');
  $self->SUPER::ACTION_test;
}

sub ACTION_code
{
  my($self) = @_;
  $self->depends_on('alien_build');
  $self->SUPER::ACTION_code;
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Alien::Build::MB - Alien::Build installer class for Module::Build

=head1 VERSION

version 0.09

=head1 SYNOPSIS

In your Build.PL:

 use Alien::Build::MB;
 
 Alien::Build::MB->new(
   module_name => 'Alien::MyLibrary',
   ...
 );

=head1 DESCRIPTION

This is a L<Module::Build> subclass that uses L<Alien::Build> to
help create L<Alien> distributions.  The author recommends
L<Alien::Build::MM>, which uses L<ExtUtils::MakeMaker> instead.
The primary rationale for this class, is to prove independence
from any particular installer, so that other installers may be
added in the future if they become available.  If you really do
prefer to work with L<Module::Build> though, this may be the
installer for you!

=head1 CONSTRUCTOR

=head2 new

 my $abmb = Alien::Build::MB->new(%args);

Takes the usual L<Module::Build> arguments.

=head1 PROPERTIES

All L<Alien::Build::MB> specific properties have a C<alien_> prefix.

=head2 alien_alienfile_meta

If true (the default), then extra meta will be stored in C<x_alienfile> which includes
the C<share> and C<system> prereqs.

=head1 METHODS

=head2 alien_build

 my $build = $abmb->alien_build;

Returns a freshly deserialized instance of L<Alien::Build>.  If you make
any changes to this object's C<install_prop> or C<runtime_prop> properties
be sure that you also call C<< $build->checkpoint >>!

=head1 ACTIONS

These actions should automatically be called during the normal install
process.  For debugging you may want to call them separately.

=head2 ACTION_alien_download

 ./Build alien_download

Downloads the package from the internet.  For a system install this does
not do anything.

=head2 ACTION_alien_build

 ./Build alien_build

Build the package from source.

=head2 ACTION_alien_test

 ./Build alien_test

Run the package tests, if there are any.

=head1 SEE ALSO

L<Alien::Build>, L<Alien::Build::MM>, L<Alien::Base::ModuleBuild>

=head1 AUTHOR

Graham Ollis <plicease@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2017-2022 by Graham Ollis.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut