The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Dist::Zilla::Plugin::MakeMaker::Awesome;
BEGIN {
  $Dist::Zilla::Plugin::MakeMaker::Awesome::AUTHORITY = 'cpan:AVAR';
}
# git description: v0.21-4-g1c3dd98
$Dist::Zilla::Plugin::MakeMaker::Awesome::VERSION = '0.22';
# ABSTRACT: A more awesome MakeMaker plugin for L<Dist::Zilla>
# KEYWORDS: plugin installer MakeMaker Makefile.PL toolchain customize override

use Moose;
use MooseX::Types::Moose qw< Str ArrayRef HashRef >;
use MooseX::Types::Stringlike 'Stringlike';
use Moose::Autobox;
use namespace::autoclean;
use CPAN::Meta::Requirements 2.121; # requirements_for_module

extends 'Dist::Zilla::Plugin::MakeMaker' => { -version => 5.001 };

sub mvp_multivalue_args { qw(WriteMakefile_arg_strs test_files exe_files) }

sub mvp_aliases {
    +{
        WriteMakefile_arg => 'WriteMakefile_arg_strs',
        test_file => 'test_files',
        exe_file => 'exe_files',
    }
}

has MakeFile_PL_template => (
    is            => 'ro',
    isa           => Stringlike,
    coerce        => 1,
    lazy          => 1,
    builder       => '_build_MakeFile_PL_template',
    documentation => "The Text::Template used to construct the ExtUtils::MakeMaker Makefile.PL",
);

sub _build_MakeFile_PL_template {
    my ($self) = @_;

    my $template = <<'TEMPLATE';
# This Makefile.PL for {{ $dist->name }} was generated by
# {{ ref $plugin }} {{ $plugin->VERSION || '<self>' }}
# and Dist::Zilla::Plugin::MakeMaker::Awesome {{ Dist::Zilla::Plugin::MakeMaker::Awesome->VERSION }}.
# Don't edit it but the dist.ini and plugins used to construct it.

use strict;
use warnings;

{{ $perl_prereq ? qq[use $perl_prereq;] : ''; }}

use ExtUtils::MakeMaker{{ defined $eumm_version ? ' ' . $eumm_version : '' }};

{{ $share_dir_block[0] }}

my {{ $WriteMakefileArgs }}
{{
    @$extra_args ? "%WriteMakefileArgs = (\n"
        . join('', map { "    " . $_ . ",\n" } '%WriteMakefileArgs', @$extra_args)
        . ");\n"
    : '';
}}
my {{ $fallback_prereqs }}

unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
  delete $WriteMakefileArgs{TEST_REQUIRES};
  delete $WriteMakefileArgs{BUILD_REQUIRES};
  $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs;
}

delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
  unless eval { ExtUtils::MakeMaker->VERSION(6.52) };

WriteMakefile(%WriteMakefileArgs);

{{ $share_dir_block[1] }}

TEMPLATE

  return $template;
}

has WriteMakefile_arg_strs => (
    is => 'ro', isa => ArrayRef[Str],
    traits => ['Array'],
    lazy => 1,
    default => sub { [] },
    documentation => "Additional arguments passed to ExtUtils::MakeMaker's WriteMakefile()",
);

has WriteMakefile_args => (
    isa           => HashRef,
    traits        => ['Hash'],
    handles       => {
        WriteMakefile_args => 'elements',
        delete_WriteMakefile_arg => 'delete',
    },
    lazy          => 1,
    builder       => '_build_WriteMakefile_args',
    documentation => "The arguments passed to ExtUtils::MakeMaker's WriteMakefile()",
);

sub _build_WriteMakefile_args {
    my ($self) = @_;

    (my $name = $self->zilla->name) =~ s/-/::/g;
    my $test_files = $self->test_files;

    my $prereqs = $self->zilla->prereqs;
    my $perl_prereq = $prereqs->requirements_for(qw(runtime requires))
    ->as_string_hash->{perl};

    $perl_prereq = version->parse($perl_prereq)->numify if $perl_prereq;

    my $prereqs_dump = sub {
        $prereqs->requirements_for(@_)
        ->clone
        ->clear_requirement('perl')
        ->as_string_hash;
    };

    my $build_prereq = $prereqs_dump->(qw(build requires));
    my $test_prereq = $prereqs_dump->(qw(test requires));

    my %WriteMakefile = (
        DISTNAME  => $self->zilla->name,
        NAME      => $name,
        AUTHOR    => $self->zilla->authors->join(q{, }),
        ABSTRACT  => $self->zilla->abstract,
        VERSION   => $self->zilla->version,
        LICENSE   => $self->zilla->license->meta_yml_name,
        EXE_FILES => $self->exe_files,

        CONFIGURE_REQUIRES => $prereqs_dump->(qw(configure requires)),
        keys %$build_prereq ? ( BUILD_REQUIRES => $build_prereq ) : (),
        keys %$test_prereq ? ( TEST_REQUIRES => $test_prereq ) : (),
        PREREQ_PM          => $prereqs_dump->(qw(runtime   requires)),

        test => { TESTS => join q{ }, sort @$test_files },

        $perl_prereq ? ( MIN_PERL_VERSION => $perl_prereq ) : (),
    );

    return \%WriteMakefile;
}


has WriteMakefile_dump => (
    is            => 'ro',
    isa           => Stringlike,
    coerce        => 1,
    lazy          => 1,
    builder       => '_build_WriteMakefile_dump',
    documentation => "A Data::Dumper Str for using WriteMakefile_args used by MakeFile_PL_template"
);

sub _build_WriteMakefile_dump {
    my ($self) = @_;
    # Get arguments for WriteMakefile
    my %write_makefile_args = $self->WriteMakefile_args;

    return $self->_dump_as(\%write_makefile_args, '*WriteMakefileArgs');
}

has test_files => (
    is            => 'ro',
    isa           => ArrayRef[Str],
    lazy          => 1,
    builder       => '_build_test_files',
    documentation => "The glob paths given to the C<< test => { TESTS => ... } >> parameter for ExtUtils::MakeMaker's WriteMakefile() (in munged form)",
);

sub _build_test_files {
    my ($self) = @_;

    my %test_files;
    for my $file ($self->zilla->files->flatten) {
        next unless $file->name =~ m{\At/.+\.t\z};
        (my $pattern = $file->name) =~ s{/[^/]+\.t\z}{/*.t}g;

        $test_files{$pattern} = 1;
    }

    return [ keys %test_files ];
}

has exe_files => (
    is            => 'ro',
    isa           => ArrayRef[Str],
    lazy          => 1,
    builder       => '_build_exe_files',
    documentation => "The test directories given to ExtUtils::MakeMaker's EXE_FILES (in munged form)",
);

sub _build_exe_files {
    my ($self) = @_;

    my @exe_files =
        $self->zilla->find_files(':ExecFiles')->map(sub { $_->name })->flatten;

    return \@exe_files;
}

has share_dir_block => (
    is            => 'ro',
    isa           => ArrayRef[Str],
    auto_deref    => 1,
    lazy          => 1,
    builder       => '_build_share_dir_block',
    documentation => "The share dir block used in `MakeFile_PL_template'",
);

sub _build_share_dir_block {
    my ($self) = @_;

    my @share_dir_block = (q{}, q{});

    my $share_dir_map = $self->zilla->_share_dir_map;
    if ( keys %$share_dir_map ) {
        # split in two to foil CPANTS prereq_matches_use
        my $preamble = qq{use File::Shar}.qq{eDir::Install;\n};
        if ( my $dist_share_dir = $share_dir_map->{dist} ) {
            $dist_share_dir = quotemeta $dist_share_dir;
            $preamble .= qq{install_share dist => "$dist_share_dir";\n};
        }

        if ( my $mod_map = $share_dir_map->{module} ) {
            for my $mod ( keys %$mod_map ) {
                my $mod_share_dir = quotemeta $mod_map->{$mod};
                $preamble .= qq{install_share module => "$mod", "$mod_share_dir";\n};
            }
        }
        @share_dir_block = (
            $preamble,
            qq{\{\npackage\nMY;\nuse File::ShareDir::Install qw(postamble);\n\}\n},

        );
    }

    return \@share_dir_block;
}

sub register_prereqs {
    my ($self) = @_;

    $self->zilla->register_prereqs(
        { phase => 'configure' },
        'ExtUtils::MakeMaker' => $self->eumm_version || 0,
    );

    return unless keys %{ $self->zilla->_share_dir_map };

    $self->zilla->register_prereqs(
        { phase => 'configure' },
        'File::ShareDir::Install' => 0.03,
    );

    return {};
}

sub setup_installer {
    my $self = shift;

    ## Sanity checks
    $self->log_fatal("can't install files with whitespace in their names")
        if grep { /\s/ } @{$self->exe_files};

    my $perl_prereq = $self->delete_WriteMakefile_arg('MIN_PERL_VERSION');

    my $content = $self->fill_in_string(
        $self->MakeFile_PL_template,
        {
            dist              => \($self->zilla),
            plugin            => \$self,
            eumm_version      => \($self->eumm_version),
            perl_prereq       => \$perl_prereq,
            share_dir_block   => [ $self->share_dir_block ],
            fallback_prereqs  => \($self->fallback_prereq_pm),
            WriteMakefileArgs => \($self->WriteMakefile_dump),
            extra_args        => \($self->WriteMakefile_arg_strs),
        },
    );

    my $file = Dist::Zilla::File::InMemory->new({
        name    => 'Makefile.PL',
        content => $content,
    });

    $self->add_file($file);
    return;
}

__PACKAGE__->meta->make_immutable;

__END__

=pod

=encoding UTF-8

=head1 NAME

Dist::Zilla::Plugin::MakeMaker::Awesome - A more awesome MakeMaker plugin for L<Dist::Zilla>

=head1 VERSION

version 0.22

=head1 SYNOPSIS

In your F<dist.ini>:

    [MakeMaker::Awesome]
    WriteMakefile_arg = CCFLAGS => `pkg-config --cflags libpng`
    WriteMakefile_arg = LIBS => [ `pkg-config --libs libpng` ]

or:

    ;; Replace [MakeMaker]
    ;[MakeMaker]
    [=inc::MyMakeMaker]

=head1 DESCRIPTION

L<Dist::Zilla>'s L<MakeMaker|Dist::Zilla::Plugin::MakeMaker> plugin is
limited, if you want to stray from the marked path and do something
that would normally be done in a C<package MY> section or otherwise
run custom code in your F<Makefile.PL> you're out of luck.

This plugin is 100% compatible with L<Dist::Zilla::Plugin::MakeMaker> -- we
add additional customization hooks by subclassing it.

=head1 CONFIGURATION OPTIONS

Many features can be accessed directly via F<dist.ini>, by setting options.
For options where you expect a multi-line string to be inserted into
F<Makefile.PL>, use the config option more than once, setting each line
separately.

=head2 WriteMakefile_arg

A string, which evaluates to an even-numbered list, which will be included in the call to
C<WriteMakefile>.  Any code is legal that can be inserted into a list of other
key-value pairs, for example:

    [MakeMaker::Awesome]
    WriteMakefile_arg = ( $^O eq 'solaris' ? ( CCFLAGS => '-Wall' ) : ())

Can be used more than once.

=for stopwords DynamicPrereqs

Note: you (intentionally) cannot use this mechanism for specifying dynamic
prerequisites, as previous occurrences of a top-level key will be overwritten
(additionally, you cannot set the fallback prereqs from here). You should take
a look at L<[DynamicPrereqs]|Dist::Zilla::Plugin::DynamicPrereqs> for this.

=head2 test_file

A glob path given to the C<< test => { TESTS => ... } >> parameter for
L<ExtUtils::MakeMaker/WriteMakefile>. Can be used more than once.
Defaults to F<.t> files under F<t/>.  B<NOT> a directory name, despite the name.

=head2 exe_file

The file given to the C<EXE_FILES> parameter for
L<ExtUtils::MakeMaker/WriteMakefile>. Can be used more than once.
Defaults to using data from C<:ExecDir> plugins.

=head1 SUBCLASSING

You can further customize the content of F<Makefile.PL> by subclassing this plugin,
L<Dist::Zilla::Plugin::MakeMaker::Awesome>.

As an example, adding a C<package MY> section to your
F<Makefile.PL>:

In your F<dist.ini>:

    [=inc::MyDistMakeMaker / MyDistMakeMaker]

Then in your F<inc/MyDistMakeMaker.pm>, real example from L<Hailo>
(which has C<[=inc::HailoMakeMaker / HailoMakeMaker]> in its
F<dist.ini>):

    package inc::HailoMakeMaker;
    use Moose;

    extends 'Dist::Zilla::Plugin::MakeMaker::Awesome';

    override _build_MakeFile_PL_template => sub {
        my ($self) = @_;
        my $template = super();

        $template .= <<'TEMPLATE';
    package MY;

    sub test {
        my $inherited = shift->SUPER::test(@_);

        # Run tests with Moose and Mouse
        $inherited =~ s/^test_dynamic :: pure_all\n\t(.*?)\n/test_dynamic :: pure_all\n\tANY_MOOSE=Mouse $1\n\tANY_MOOSE=Moose $1\n/m;

        return $inherited;
    }
    TEMPLATE

        return $template;
    };

    __PACKAGE__->meta->make_immutable;

=for stopwords distro

Or maybe you're writing an XS distro and want to pass custom arguments
to C<WriteMakefile()>, here's an example of adding a C<LIBS> argument
in L<re::engine::PCRE> (note that you can also achieve this without
subclassing, by passing the L</WriteMakefile_arg> option):

    package inc::PCREMakeMaker;
    use Moose;

    extends 'Dist::Zilla::Plugin::MakeMaker::Awesome';

    override _build_WriteMakefile_args => sub { +{
        # Add LIBS => to WriteMakefile() args
        %{ super() },
        LIBS => [ '-lpcre' ],
    } };

    __PACKAGE__->meta->make_immutable;

And another example from L<re::engine::Plan9>, which determines the arguments
dynamically at build time:

    package inc::Plan9MakeMaker;
    use Moose;

    extends 'Dist::Zilla::Plugin::MakeMaker::Awesome';

    override _build_WriteMakefile_args => sub {
        my ($self) = @_;

        our @DIR = qw(libutf libfmt libregexp);
        our @OBJ = map { s/\.c$/.o/; $_ }
                   grep { ! /test/ }
                   glob "lib*/*.c";

        return +{
            %{ super() },
            DIR           => [ @DIR ],
            INC           => join(' ', map { "-I$_" } @DIR),

            # This used to be '-shared lib*/*.o' but that doesn't work on Win32
            LDDLFLAGS     => "-shared @OBJ",
        };
    };

    __PACKAGE__->meta->make_immutable;

If you have custom code in your L<ExtUtils::MakeMaker>-based
F<Makefile.PL> that L<Dist::Zilla> can't replace via its default
facilities you'll be able to replace it by using this module.

Even if your F<Makefile.PL> isn't L<ExtUtils::MakeMaker>-based you
should be able to override it. You'll just have to provide a new
L</"_build_MakeFile_PL_template">.

=for stopwords overridable

=head2 OVERRIDABLE METHODS

These are the methods you can currently C<override> or method-modify in your
custom F<inc/> module. The work that this module does is entirely done in
small modular methods that can be overridden in your subclass. Here are
some of the highlights:

=for Pod::Coverage mvp_multivalue_args mvp_aliases

=head3 _build_MakeFile_PL_template

Returns a L<Text::Template> string used to construct the F<Makefile.PL>.

If you need to insert some additional code to the beginning or end of
F<Makefile.PL> (without modifying the existing content, you should use an
C<around> method modifier, something like this:

    around _build_MakeFile_PL_template => sub {
        my $orig = shift;
        my $self = shift;

        my $NEW_CONTENT = ...;

        # insert new content near the beginning of the file, preserving the
        # preamble header
        my $string = $self->$orig(@_);
        $string =~ m/use warnings;\n\n/g;
        return substr($string, 0, pos($string)) . $NEW_CONTENT . substr($string, pos($string));
    };

=head3 _build_WriteMakefile_args

A C<HashRef> of arguments that will be passed to
L<ExtUtils::MakeMaker>'s C<WriteMakefile> function.

=head3 _build_WriteMakefile_dump

Takes the return value of L</"_build_WriteMakefile_args"> and
constructs a L<Str> that will be included in the F<Makefile.PL> by
L</"_build_MakeFile_PL_template">.

=head3 _build_test_files

The glob paths given to the C<< test => { TESTS => ... } >> parameter for
L<ExtUtils::MakeMaker/WriteMakefile>.  Defaults to F<.t> files under F<t/>.
B<NOT> directories, despite the name.

=head3 _build_exe_files

The files given to the C<EXE_FILES> parameter for
L<ExtUtils::MakeMaker/WriteMakefile>.
Defaults to using data from C<:ExecDir> plugins.

=head3 register_prereqs

=head3 setup_installer

=for stopwords dirs

The test/bin/share dirs and exe_files. These will all be passed to
F</"_build_WriteMakefile_args"> later.

=head3 _build_share_dir_block

=for stopwords sharedir

An C<ArrayRef[Str]> with two elements to be used by
L</"_build_MakeFile_PL_template">. The first will declare your
L<sharedir|File::ShareDir::Install> and the second will add a magic
C<package MY> section to install it. Deep magic.

=head2 OTHER

The main entry point is C<setup_installer> via the
L<Dist::Zilla::Role::InstallTool> role. There are also other magic
Dist::Zilla roles, check the source for more info.

=head1 DIAGNOSTICS

=over

=item attempt to add F<Makefile.PL> multiple times

This error from L<Dist::Zilla> means that you've used both
C<[MakeMaker]> and C<[MakeMaker::Awesome]>. You've either included
C<MakeMaker> directly in F<dist.ini>, or you have plugin bundle that
includes it. See L<@Filter|Dist::Zilla::PluginBundle::Filter> for how
to filter it out.

=back

=head1 BUGS

=for stopwords INI

This plugin would suck less if L<Dist::Zilla> didn't use a INI-based
config system so you could add a stuff like this in your main
configuration file like you can with L<Module::Install>.

The F<.ini> file format can only support key-value pairs whereas any
complex use of L<ExtUtils::MakeMaker> requires running custom Perl
code and passing complex data structures to C<WriteMakefile>.

=head1 AUTHOR

Ævar Arnfjörð Bjarmason <avar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by Ævar Arnfjörð Bjarmason.

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

=head1 CONTRIBUTORS

=over 4

=item *

Jesse Luehrs <doy@tozt.net>

=item *

Karen Etheridge <ether@cpan.org>

=item *

Robin Smidsrød <robin@smidsrod.no>

=item *

Vladimir Timofeev <vovkasm@gmail.com>

=back

=cut