package ExtUtils::MakeMaker::Dump;

our $DATE = '2015-01-14'; # DATE
our $VERSION = '0.03'; # VERSION

use 5.010;
use strict;
use warnings;

use Exporter qw(import);
our @EXPORT_OK = qw(dump_makefile_pl_script);

our %SPEC;

$SPEC{dump_makefile_pl_script} = {
    v => 1.1,
    summary => 'Run a Makefile.PL script but only to '.
        'dump the %WriteMakefileArgs',
    description => <<'_',

This function runs `Makefile.PL` script that uses `ExtUtils::MakeMaker` but
monkey-patches beforehand so that `WriteMakefile()` will dump the argument and
then exit. The goal is to get the argument without actually running the script
to produce a Makefile.

This is used for example in `App::lcpan` project. When a release file does not
contain any `META.json` or `META.yml` file, the next best thing to try is to
extract this information from `Makefile.PL`. Since this is an executable script,
we'll need to run it, but we don't want to actually produce a Makefile, hence
the patch. (Another alternative would be to do a static analysis of the script.)

Note: `Makefile.PL` using `Module::Install` works too, because under the hood
it's still `ExtUtils::MakeMaker`.

_
    args => {
        filename => {
            summary => 'Path to the script',
            req => 1,
            schema => 'str*',
        },
        libs => {
            summary => 'Libraries to unshift to @INC when running script',
            schema  => ['array*' => of => 'str*'],
        },
    },
};
sub dump_makefile_pl_script {
    require Capture::Tiny;
    require UUID::Random;

    my %args = @_;

    my $filename = $args{filename} or return [400, "Please specify filename"];
    (-f $filename) or return [404, "No such file: $filename"];

    my $libs = $args{libs} // [];

    my $tag = UUID::Random::generate();
    my @cmd = (
        $^X, (map {"-I$_"} @$libs),
        "-MTimeout::Self=3", # to defeat scripts that prompts for stuffs
        "-MExtUtils::MakeMaker::Patch::DumpAndExit=-tag,$tag",
        $filename,
        "--version",
    );
    my ($stdout, $stderr, $exit) = Capture::Tiny::capture(
        sub { system @cmd },
    );

    my $wmf_args;
    if ($stdout =~ /^# BEGIN DUMP $tag\s+(.*)^# END DUMP $tag/ms) {
        $wmf_args = eval $1;
        if ($@) {
            return [500, "Error in eval-ing captured ".
                        "\\\%WriteMakefileArgs: $@, raw capture: <<<$1>>>"];
        }
        if (ref($wmf_args) ne 'HASH') {
            return [500, "Didn't get a hash \%WriteMakefileArgs, ".
                        "raw capture: stdout=<<$stdout>>"];
        }
    } else {
        return [500, "Can't capture \%WriteMakefileArgs, raw capture: ".
                    "stdout=<<$stdout>>, stderr=<<$stderr>>"];
    }

    [200, "OK", $wmf_args];
}

1;
# ABSTRACT: Run a Makefile.PL script but only to dump the %WriteMakefileArgs

__END__

=pod

=encoding UTF-8

=head1 NAME

ExtUtils::MakeMaker::Dump - Run a Makefile.PL script but only to dump the %WriteMakefileArgs

=head1 VERSION

This document describes version 0.03 of ExtUtils::MakeMaker::Dump (from Perl distribution ExtUtils-MakeMaker-Dump), released on 2015-01-14.

=head1 FUNCTIONS


=head2 dump_makefile_pl_script(%args) -> [status, msg, result, meta]

{en_US Run a Makefile.PL script but only to dump the %WriteMakefileArgs}.

{en_US 
This function runs C<Makefile.PL> script that uses C<ExtUtils::MakeMaker> but
monkey-patches beforehand so that C<WriteMakefile()> will dump the argument and
then exit. The goal is to get the argument without actually running the script
to produce a Makefile.

This is used for example in C<App::lcpan> project. When a release file does not
contain any C<META.json> or C<META.yml> file, the next best thing to try is to
extract this information from C<Makefile.PL>. Since this is an executable script,
we'll need to run it, but we don't want to actually produce a Makefile, hence
the patch. (Another alternative would be to do a static analysis of the script.)

Note: C<Makefile.PL> using C<Module::Install> works too, because under the hood
it's still C<ExtUtils::MakeMaker>.
}

Arguments ('*' denotes required arguments):

=over 4

=item * B<filename>* => I<str>

{en_US Path to the script}.

=item * B<libs> => I<array[str]>

{en_US Libraries to unshift to @INC when running script}.

=back

Returns an enveloped result (an array).

First element (status) is an integer containing HTTP status code
(200 means OK, 4xx caller error, 5xx function error). Second element
(msg) is a string containing error message, or 'OK' if status is
200. Third element (result) is optional, the actual result. Fourth
element (meta) is called result metadata and is optional, a hash
that contains extra information.

Return value:  (any)

=head1 SEE ALSO

L<Module::Build::Dump>

=head1 HOMEPAGE

Please visit the project's homepage at L<https://metacpan.org/release/ExtUtils-MakeMaker-Dump>.

=head1 SOURCE

Source repository is at L<https://github.com/perlancar/perl-ExtUtils-MakeMaker-Dump>.

=head1 BUGS

Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=ExtUtils-MakeMaker-Dump>

When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.

=head1 AUTHOR

perlancar <perlancar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by perlancar@cpan.org.

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
