# Copyright 2014 Jeffrey Kegler
# This file is part of Marpa::R2.  Marpa::R2 is free software: you can
# redistribute it and/or modify it under the terms of the GNU Lesser
# General Public License as published by the Free Software Foundation,
# either version 3 of the License, or (at your option) any later version.
#
# Marpa::R2 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser
# General Public License along with Marpa::R2.  If not, see
# http://www.gnu.org/licenses/.

package Marpa::R2::Build_Me;

use 5.010;
use strict;
use warnings;

@Marpa::R2::Build_Me::ISA = ('Module::Build');

use Config;
use ExtUtils::Manifest;
use File::Copy;
use Cwd;
use IPC::Cmd;
use Module::Build;
use Fatal qw(open close chdir chmod utime);
use English qw( -no_match_vars );
use Time::Piece;

use Marpa::R2::Config;

BEGIN {
    if ($Marpa::R2::USE_PERL_AUTOCONF) {
        say "Using Config::AutoConf";
        for my $package (qw( ExtUtils::MakeMaker Config::AutoConf ))
        {
            if ( not eval "require $package" ) {
                die "$package is not installed: $EVAL_ERROR\n",
                    "    Module $package is required for Windows and for USE_PERL_AUTOCONF mode\n";
            }
            my $version = $Marpa::R2::VERSION_FOR_CONFIG{$package};
            if ( not $package->VERSION($version) ) {
                die "Version $version of $package is not installed\n",
                    "    Version $version of $package is required for Windows and for USE_PERL_AUTOCONF mode\n";
            }
        } ## end for my $package (...)
    } ## end if ($Marpa::R2::USE_PERL_AUTOCONF)
} ## end BEGIN

my $preamble = <<'END_OF_STRING';
# This file is written by Build.PL
# It is not intended to be modified directly

END_OF_STRING

sub installed_contents {
    my ( $self, $package ) = @_;
    my $marpa_version = $self->dist_version();
    my $text             = $preamble;
    $text .= "package $package;\n";

##no critic(ValuesAndExpressions::RequireInterpolationOfMetachars)
    $text .= q{use vars qw($VERSION $STRING_VERSION)} . qq{;\n};
    $text .= q{$VERSION = '} . $marpa_version . qq{';\n};
    $text .= q{$STRING_VERSION = $VERSION} . qq{;\n};
    $text .= q{$VERSION = eval $VERSION} . qq{;\n};
##use critic

    $text .= "1;\n";
    return $text;
} ## end sub installed_contents

sub xs_version_contents {
    my ( $self, $package ) = @_;
    my @use_packages =
        qw( Scalar::Util List::Util Carp Data::Dumper );
    my $text = $preamble;
    $text .= "package $package;\n";

##no critic(ValuesAndExpressions::RequireInterpolationOfMetachars)
    $text .= q{use vars qw($TIMESTAMP)} . qq{;\n};
    $text .= q{$TIMESTAMP='} . localtime()->datetime . qq{';\n};
##use critic

    for my $package (@use_packages) {
        my $version = $Marpa::R2::VERSION_FOR_CONFIG{$package};
        die "No version defined for $package" if not defined $version;
        $text .= "use $package $version ();\n";
    }
    $text .= "1;\n";
    return $text;
} ## end sub xs_version_contents

sub perl_version_contents {
    my ( $self, $package, ) = @_;
    my @use_packages     = qw( Scalar::Util Carp Data::Dumper PPI Marpa::R2 );
    my $text             = $preamble;
    my $marpa_version = $self->dist_version();
    $text .= "package $package;\n";

##no critic(ValuesAndExpressions::RequireInterpolationOfMetachars)
    $text .= q{use vars qw($TIMESTAMP)} . qq{;\n};
    $text .= q{$TIMESTAMP='} . localtime()->datetime . qq{';\n};
##use critic

    for my $package (@use_packages) {
        my $version =
              $package eq 'Marpa::R2'
            ? $marpa_version
            : $Marpa::R2::VERSION_FOR_CONFIG{$package};
        die "No version defined for $package" if not defined $version;
        $text .= "use $package $version ();\n";
    } ## end for my $package (@use_packages)
    $text .= "1;\n";
    return $text;
} ## end sub perl_version_contents

sub file_write {
    my ( $self, $contents, @name_components ) = @_;
    my $base_dir  = $self->base_dir();
    my $file_name = pop @name_components;
    my $dir_name  = File::Spec->catdir( $base_dir, @name_components );
    -d $dir_name or mkdir $dir_name;
    my $path_name = File::Spec->catfile( $dir_name, $file_name );
    open my $fh, q{>}, $path_name;
    print {$fh} $contents or die "print failed: $ERRNO";
    close $fh;
    return 1;
} ## end sub file_write

sub file_slurp {
    my ( $self, @name_components ) = @_;
    my $path_name = File::Spec->catfile( @name_components );
    open my $fh, q{<}, $path_name;
    my $contents = do { local $RS = undef; <$fh> };
    close $fh;
    return $contents;
}

# This is based on _infer_xs_spec() from Module::Build.  It was
# copied here in order to be customized
sub marpa_infer_xs_spec {
    my $self = shift;
    my $file = shift;

    my $cf = $self->{config};

    my %spec;

    my ( $v, $d, $f ) = File::Spec->splitpath($file);
    my @d = File::Spec->splitdir($d);
    ( my $file_base = $f ) =~ s/\. [^.]+ \z//xmsi;

    $spec{base_name} = $file_base;

    $spec{src_dir} = File::Spec->catpath( $v, $d, q{} );

    # the module name
    shift @d while @d && ( $d[0] eq 'lib' || $d[0] eq q{} );
    pop @d while @d && $d[-1] eq q{};
    $spec{module_name} = join q{::}, @d, $file_base;

    $spec{archdir} =
        File::Spec->catdir( $self->blib, 'arch', 'auto', @d, $file_base );

    $spec{bs_file} = File::Spec->catfile( $spec{archdir}, "${file_base}.bs" );

    $spec{lib_file} =
        File::Spec->catfile( $spec{archdir},
        "${file_base}." . $cf->get('dlext') );

    $spec{c_file} = File::Spec->catfile( $spec{src_dir}, "${file_base}.c" );

    $spec{obj_file} =
        File::Spec->catfile( $spec{src_dir},
        "${file_base}" . $cf->get('obj_ext') );

    return \%spec;
} ## end sub marpa_infer_xs_spec

sub gcc_is_at_least {
    my ($required) = @_;
    state $gcc_version = $Config{gccversion};
    return if not $gcc_version;
    my @actual = ($gcc_version =~ m/ \A (\d+) [.] (\d+) [.] (\d+) \z /xms);
    return if @actual != 3;
    my @required = ($required =~ m/ \A (\d+) [.] (\d+) [.] (\d+) \z /xms);
    die if scalar @required != 3;
    my $cmp = $actual[0] <=> $required[0] ||
     $actual[1] <=> $required[1]  ||
     $actual[2] <=> $required[2] ;
    return $cmp >= 0 ? 1 : 0;
}

# The following initially copied from Module::Build, to be customized for
# Marpa.
sub process_xs {
    my ( $self, $xs_file ) = @_;

    my $development_mode = $self->args('Dev');

    my $spec = marpa_infer_xs_spec( $self, $xs_file );

    my $xs_dir = File::Spec->catdir(qw(xs));
    my $gp_xsh = File::Spec->catfile( $xs_dir, 'general_pattern.xsh' );
    if ($development_mode) {
        my $gp_generate_pl = File::Spec->catfile( $xs_dir, 'gp_generate.pl' );
        if ( not $self->up_to_date( [$gp_generate_pl], $gp_xsh ) ) {
            if (not IPC::Cmd::run(
                    command => [ $EXECUTABLE_NAME, $gp_generate_pl, $gp_xsh ],
                    verbose => 1
                )
                )
            {
                die "Could not generate $gp_xsh";
            } ## end if ( not IPC::Cmd::run( command => [ $EXECUTABLE_NAME...]))
        } ## end if ( not $self->up_to_date( [$gp_generate_pl], $gp_xsh...))
    } ## end if ($development_mode)

    my $dest_gp_xsh =
        File::Spec->catfile( $spec->{src_dir}, 'general_pattern.xsh' );
    $self->copy_if_modified( from => $gp_xsh, to => $dest_gp_xsh, );

    # .xs -> .c
    $self->add_to_cleanup( $spec->{c_file} );

    my @libmarpa_build_dir = File::Spec->splitdir( $self->base_dir );
    push @libmarpa_build_dir,
        (
        defined $self->args('libmarpa-shared')
        ? 'libmarpa_shared'
        : 'libmarpa_build' );
    my $libmarpa_build_dir = File::Spec->catdir(@libmarpa_build_dir);

    my @xs_dependencies = ( 'typemap', 'Build', $xs_file, $dest_gp_xsh );
    push @xs_dependencies,
        map { File::Spec->catfile( @libmarpa_build_dir, $_ ) }
        qw(config.h marpa.h marpa_slif.h marpa_codes.c );

    if ( not $self->up_to_date( \@xs_dependencies, $spec->{c_file} ) ) {
        $self->verbose() and say "compiling $xs_file";
        $self->compile_xs( $xs_file, outfile => $spec->{c_file} );
    }

    # .c -> .o
    my $v = $self->dist_version;
    $self->verbose() and say "compiling $spec->{c_file}";
    my @new_ccflags = ( '-I', $libmarpa_build_dir, '-I', 'xs' );

    if ( $self->config('ccname') eq 'gcc' ) {
        ## -W instead of -Wextra is case the GCC is pre 3.0.0
        ## -Winline omitted because too noisy
        push @new_ccflags, qw( -Wall -W -ansi
            -Wpointer-arith -Wstrict-prototypes -Wwrite-strings
            -Wmissing-declarations );
        push @new_ccflags, '-Wdeclaration-after-statement' if gcc_is_at_least('3.4.6');
    } ## end if ( $self->config('cc') eq 'gcc' )
    if ( defined $self->args('XS-debug') ) {
        say 'XS-debug flag is on';
        if ( $self->config('ccname') eq 'gcc' ) {
            push @new_ccflags, qw( -Wall -ansi -pedantic -Wundef -Wendif-labels );
        } ## end if ( $self->config('cc') eq 'gcc' )
        elsif ( $self->config('ccname') eq 'cl' ){
            ## gcc's -Wundef is msvc's C4668 that is on with -Wall
            ## gcc's -ansi -pedantic is msvc's /Za, but windows.h can't be compiled with it
            ## msvc's equivalent for gcc's -Wendif-labels can't be found :)
            push @new_ccflags, qw( -W4 -Wp64 );
        } ## end elsif ( $self->config('ccname') eq 'cl' ){
    }
    my $ccflags = $self->config('ccflags');
    $self->config( ccflags => ( $ccflags . q{ } . join q{ }, @new_ccflags ) );
    $self->compile_c( $spec->{c_file},
        defines => { VERSION => qq{"$v"}, XS_VERSION => qq{"$v"} } );

    # archdir
    # Legacy mkpath(), for compatibility with Perl 5.10.0
    File::Path::mkpath( $spec->{archdir}, 0, ( oct 777 ) )
        if not -d $spec->{archdir};

    my @extra_linker_flags = ();
    my $libmarpa_build_directory = File::Spec->catdir(
        $self->base_dir(),
        (   defined $self->args('libmarpa-shared')
            ? 'libmarpa_shared'
            : 'libmarpa_build'
        )
    );

    if ( defined $self->args('libmarpa-shared') ) {
        my $libmarpa_libs_dir;
        if ($Marpa::R2::USE_PERL_AUTOCONF) {
            $libmarpa_libs_dir =
                File::Spec->catdir( $libmarpa_build_directory,
                qw(blib_shared arch auto libmarpa) );
        }
        else {
            $libmarpa_libs_dir =
                File::Spec->catdir( $libmarpa_build_directory, '.libs' );
        }
        push @extra_linker_flags, '-L' . $libmarpa_libs_dir;
        my $version_file_name =
            File::Spec->catfile( $libmarpa_build_directory, 'LIB_VERSION' );
        my $libmarpa_version = $self->file_slurp($version_file_name);
        chomp $libmarpa_version;
        my @libmarpa_version = split /[.]/xms, $libmarpa_version;
        push @extra_linker_flags,
            sprintf q{-lmarpa-} . ( join q{.}, @libmarpa_version );
    } ## end if ( defined $self->args('libmarpa-shared') )
    else {
        my $libmarpa_archive;
        FIND_LIBRARY: {
            if ($Marpa::R2::USE_PERL_AUTOCONF) {
                my $libmarpa_libs_dir =
                    File::Spec->catdir( $libmarpa_build_directory, 'blib',
                    'arch', 'auto', 'libmarpa' );
                $libmarpa_archive = File::Spec->catfile( $libmarpa_libs_dir,
                    "libmarpa$Config{lib_ext}" );
                last FIND_LIBRARY;
            } ## end if ($Marpa::R2::USE_PERL_AUTOCONF)
            my $libmarpa_libs_dir =
                File::Spec->catdir( $libmarpa_build_directory, '.libs' );
            $libmarpa_archive =
                File::Spec->catfile( $libmarpa_libs_dir, 'libmarpa.a' );
        } ## end FIND_LIBRARY:
        push @{ $self->{properties}->{objects} }, $libmarpa_archive;
    } ## end else [ if ( defined $self->args('libmarpa-shared') ) ]

    # .xs -> .bs
    $self->add_to_cleanup( $spec->{bs_file} );
    unless ( $self->up_to_date( $xs_file, $spec->{bs_file} ) ) {
        require ExtUtils::Mkbootstrap;
        $self->log_info(
            "ExtUtils::Mkbootstrap::Mkbootstrap('$spec->{bs_file}')\n");
        ExtUtils::Mkbootstrap::Mkbootstrap( $spec->{bs_file} )
            ;    # Original had $BSLOADLIBS - what's that?
        { my $fh = IO::File->new(">> $spec->{bs_file}") }    # create
        my $time = time;
        utime $time, $time, $spec->{bs_file};                # touch
    } ## end unless ( $self->up_to_date( $xs_file, $spec->{bs_file} ) )

    # .o -> .(a|bundle)
    return marpa_link_c( $self, $spec, \@extra_linker_flags );
} ## end sub process_xs

# The following was initially copied from Module::Build, and has
# been customized for Marpa.
sub marpa_link_c {
    my ( $self, $spec, $extra_linker_flags ) = @_;
    my $p = $self->{properties};                             # For convenience

    $self->add_to_cleanup( $spec->{lib_file} );

    my $objects = $p->{objects} || [];

    return $spec->{lib_file}
        if $self->up_to_date( [ $spec->{obj_file}, @{$objects} ],
        $spec->{lib_file} );

    my $module_name = $spec->{module_name} || $self->module_name;

    my @extra_linker_flags = @{$p->{extra_linker_flags}};
    push @extra_linker_flags, @{$extra_linker_flags};
    $self->cbuilder->link(
        module_name        => $module_name,
        objects            => [ $spec->{obj_file}, @{$objects} ],
        lib_file           => $spec->{lib_file},
        extra_linker_flags => \@extra_linker_flags
    );

    return $spec->{lib_file};
} ## end sub marpa_link_c

sub do_libmarpa {
    my $self     = shift;
    my $cwd      = $self->cwd();
    my $base_dir = $self->base_dir();

    my $dist_dir = File::Spec->catdir( $base_dir, 'libmarpa_dist' );
    my $build_dir = File::Spec->catdir(
        $base_dir,
        (   defined $self->args('libmarpa-shared')
            ? 'libmarpa_shared'
            : 'libmarpa_build'
        )
    );

    my $build_stamp_file = File::Spec->catfile( $build_dir, 'stamp-h1' );
    my $dist_stamp_file = File::Spec->catfile( $dist_dir, 'stamp-h1' );

    # If build directory exists and contains a stamp file more recent than the
    # tar file, we are done.
    return if  $self->up_to_date( [$dist_stamp_file], $build_stamp_file ) ;

    # Otherwise, rebuild from scratch
    File::Path::rmtree($build_dir);

    if ( $self->verbose() ) {
                say join q{ }, "Copying files from $dist_dir to $build_dir"
                or die "print failed: $ERRNO";
    }

     ## Make sure build dir structure exists, even if empty
     my $m4_dir = File::Spec->catdir( $build_dir, 'm4' );

     # Legacy mkpath(), for compatibility with Perl 5.10.0
     File::Path::mkpath($m4_dir);

    my @copy_work_list = ();
    {
        my $from_m4_dir = File::Spec->catdir( $dist_dir, 'm4' );
        my $to_m4_dir = File::Spec->catdir( $build_dir, 'm4' );
        chdir $from_m4_dir;
        for my $file (<*>) {
          my $from_file = File::Spec->catfile($from_m4_dir, $file);
          my $to_file = File::Spec->catfile($to_m4_dir, $file);
          push @copy_work_list, [$from_file, $to_file];
        }
        chdir $cwd;
    }
    {
        chdir $dist_dir;
        FILE: for my $file (<*>) {
          next FILE if -d $file;
          next FILE if $file eq 'stamp-h1';
          my $from_file = File::Spec->catfile($dist_dir, $file);
          my $to_file = File::Spec->catfile($build_dir, $file);
          push @copy_work_list, [$from_file, $to_file];
        }
        chdir $cwd;
    }
    for my $file (@copy_work_list) {
        File::Copy::copy(@{$file});
    }

    chdir $build_dir;

    if (! $Marpa::R2::USE_PERL_AUTOCONF) {

            # This is only necessary for GNU autoconf, which is aggressive
            # about looking for things to update

            # Some files should NEVER be updated in this directory, by
            # make or anything else.  If for some reason they are
            # out of date, stamp them up to date

            my @m4_files         = glob('m4/*.m4');
            my $configure_script = 'configure';
        
            if (not $self->up_to_date( [ 'configure.ac', @m4_files ], 'aclocal.m4' ) )
            {
                utime time(), time(), 'aclocal.m4';
            }
            if (not $self->up_to_date(
                    [ 'configure.ac', 'Makefile.am', 'aclocal.m4' ],
                    'Makefile.in'
                )
                )
            {
                utime time(), time(), 'Makefile.in';
            } ## end if ( not $self->up_to_date( [ 'configure.ac', 'Makefile.am'...]))
            if (not $self->up_to_date(
                    [ 'configure.ac',    'aclocal.m4' ],
                    [ $configure_script, 'config.h.in' ]
                )
                )
            {
                utime time(), time(), $configure_script;
                utime time(), time(), 'config.h.in';
            } ## end if ( not $self->up_to_date( [ 'configure.ac', 'aclocal.m4'...]))
        
            if ( $self->verbose() ) {
                print "Configuring libmarpa\n"
                    or die "print failed: $ERRNO";
            }
            my $shell = $Config{sh};
        
        ##no critic(ValuesAndExpressions::RequireInterpolationOfMetachars)
            $shell or die q{No Bourne shell available says $Config{sh}};
        ##use critic
    }
    
    my $original_cflags = $ENV{CFLAGS};
    local $ENV{CFLAGS};
    $ENV{CFLAGS} = $original_cflags if defined $original_cflags;

    # We need PIC, but do not want the overhead of building the shared library
    my @configure_command_args = ();
    if ( defined $self->args('libmarpa-shared') ) {
        push @configure_command_args, qw(--disable-static);
    }
    else {
        push @configure_command_args, qw(--with-pic --disable-shared);
    }

    my @debug_flags = ();
    if ( defined $self->args('Marpa-debug') ) {
        if ( defined $ENV{LIBMARPA_CFLAGS} ) {
            $ENV{CFLAGS} = $ENV{LIBMARPA_CFLAGS};
        }
        push @debug_flags, '-DMARPA_DEBUG=1';
        push @debug_flags, '-fno-inline', '-Wno-inline' if ($self->config('cc') eq 'gcc');
        push @configure_command_args,
            'MARPA_DEBUG_FLAG=' . ( join q{ }, @debug_flags );
    } ## end if ( defined $self->args('Marpa-debug') )
        
    # As of this writing, only used by Config::AutoConf logic,
    # but that may change.
    my $libmarpa_version = $self->file_slurp('LIB_VERSION');
    chomp $libmarpa_version;
    my @libmarpa_version = split /[.]/xms, $libmarpa_version;

    if ($Marpa::R2::USE_PERL_AUTOCONF) {

        #
        ## C.f. http://fr.slideshare.net/hashashin/building-c-and-c-libraries-with-perl
        #
        my @c = qw/marpa_ami.c marpa_avl.c marpa.c
            marpa_codes.c marpa_obs.c marpa_slif.c marpa_tavl.c/;
        if (! -r 'config.h') {
            #
            ## Because Config::AutoConf can only generate #define/#undef
            ## stubs, we write our config.h with these stubs, our config.h
            ## will then include a generated config_from_autoconf.h
            #
            if ( $self->verbose() ) {
                say join q{ }, "Doing config.h"
                    or die "print failed: $ERRNO";
            }
            open my $config_fh, '>>', 'config.h' || die "Cannot open config.h, $!\n";
            my $ac = Config::AutoConf->new();
            my $inline_ok = 0;
            {
                $ac->msg_checking('inline');
                my $program = $ac->lang_build_program("static inline int testinline() {return 1;}\n", 'testinline');
                $inline_ok = $ac->compile_if_else($program);
                $ac->msg_result($inline_ok ? 'yes' : 'no');
            }
            my $inline = '';
            if (! $inline_ok) {
                foreach (qw/__inline__ __inline/) {
                    my $candidate = $_;
                    $ac->msg_checking($candidate);
                    my $program = $ac->lang_build_program("static $candidate int testinline() {return 1;}\n", 'testinline');
                    my $rc = $ac->compile_if_else($program);
                    $ac->msg_result($rc ? 'yes' : 'no');
                    if ($rc) {
                        $inline = $candidate;
                        last;
                    }
                }
            }
            if ($inline) {
                print {$config_fh} <<INLINEHOOK;
#ifndef __CONFIG_WITH_STUBS_H
#ifndef __cplusplus
#define inline $inline
#endif
#include "config_from_autoconf.h"
#endif /* __CONFIG_WITH_STUBS_H */
INLINEHOOK
            } else {
                print {$config_fh} <<INLINEHOOK;
#ifndef __CONFIG_WITH_STUBS_H
#ifndef __cplusplus
/* #undef inline */
#endif
#include "config_from_autoconf.h"
#endif /* __CONFIG_WITH_STUBS_H */
INLINEHOOK
            }

            # Config::Autoconf mistakes 0 for undef, so these must be done explicitly
            say {$config_fh} join q{ }, '#define MARPA_LIB_MAJOR_VERSION', $libmarpa_version[0];
            say {$config_fh} join q{ }, '#define MARPA_LIB_MINOR_VERSION', $libmarpa_version[1];
            say {$config_fh} join q{ }, '#define MARPA_LIB_MICRO_VERSION', $libmarpa_version[2];

            close($config_fh);
            $ac = Config::AutoConf->new();
            my $sizeof_int = $ac->check_sizeof_type('int');
            if ($sizeof_int < 4) {
                die "Marpa requires that int be at least 32 bits -- on this system that is not the case";
            }

            $ac->check_stdc_headers;
            $ac->check_default_headers();

            # This check was to prepare for Perl's 64-bit pseudo-UTF8 characters inside the SLIF.
            # It needs to be rethought.
            #
            # if (!$ac->check_type('unsigned long long int')) {
                # die "Marpa requires that unsigned long long int is supported by your compiler";
            # }

            $ac->define_var('PACKAGE', "\"libmarpa\"");
            $ac->define_var('PACKAGE_BUGREPORT', "\"http://rt.cpan.org/NoAuth/Bugs.html?Dist=Marpa\"");
            $ac->define_var('PACKAGE_NAME', "\"libmarpa\"");
            $ac->define_var('PACKAGE_STRING', "\"libmarpa $libmarpa_version[0].$libmarpa_version[1].$libmarpa_version[2]\"");
            $ac->define_var('PACKAGE_TARNAME', "\"libmarpa\"");
            $ac->define_var('PACKAGE_URL', "\"\"");
            $ac->define_var('PACKAGE_VERSION', "\"$libmarpa_version\"");
            $ac->define_var('PACKAGE_STRING', "\"$libmarpa_version\"");
            $ac->write_config_h('config_from_autoconf.h');
        }

        my @o = map {s/\.c$/$Config{obj_ext}/; $_} @c;
        if (! -r 'Makefile.PL') {
            open my $makefile_pl_fh, '>', 'Makefile.PL';
            my $CCFLAGS = @debug_flags ? "$Config{ccflags} @debug_flags" : '';
            my $linktype = 'static';
            my $blib = 'blib';
            my $name = 'libmarpa';
            if (defined $self->args('libmarpa-shared')) {
                $linktype = 'dynamic';
                $blib = 'blib_shared';
            }
            print {$makefile_pl_fh} "
use ExtUtils::MakeMaker;
WriteMakefile(VERSION        => \"$libmarpa_version\",
              XS_VERSION     => \"$libmarpa_version\",
              NAME           => \"$name\",
              OBJECT         => '@o',
              CCFLAGS        => '$CCFLAGS',
              INST_LIB       => \"./$blib/lib\",
              INST_ARCHLIB   => \"./$blib/arch\",
              INST_SCRIPT    => \"./$blib/script\",
              INST_BIN       => \"./$blib/bin\",
              LINKTYPE       => $linktype);
";
            close $makefile_pl_fh;
            die 'Making Makefile: perl Failure'
                if not IPC::Cmd::run( command => [$^X, 'Makefile.PL'], verbose => 1 );
        }
    } else {
            my $shell = $Config{sh};
            my $configure_script = 'configure';
            if ( $self->verbose() ) {
                say join q{ }, "Running command:", $shell, $configure_script,
                    @configure_command_args
                    or die "print failed: $ERRNO";
            }
            if (not IPC::Cmd::run(
                    command => [ $shell, $configure_script, @configure_command_args ],
                    verbose => 1
                )
                )
            {
                say {*STDERR} "Failed: $configure_script"
                    or die "say failed: $ERRNO";
                say {*STDERR} "Current directory: $build_dir"
                    or die "say failed: $ERRNO";
                die 'Cannot run libmarpa configure';
            } ## end if ( not IPC::Cmd::run( command => [ $shell, $configure_script...]))
        
    }
    if ( $self->verbose() ) {
        print "Making libmarpa: Start\n" or die "Cannot print: $ERRNO";
    }
    die 'Making libmarpa: make Failure'
        if not IPC::Cmd::run( command => [$Config{make}], verbose => 1 );

    ## stamp-h1 is a by-product of the GNU autotools, but for Config::AutoConf,
    ## I need to dummy one up, so do it for both
    {
        open my $time_stamp_fh, q{>}, 'stamp-h1';
        say {$time_stamp_fh} scalar localtime();
        close $time_stamp_fh;
    }

    chdir $cwd;

    if ( $Marpa::R2::USE_PERL_AUTOCONF
        && defined $self->args('libmarpa-shared') )
    {
        # Config::AutoConf creates a shared library with the wrong name, and
        # easiest thing to do is to make a copy with the right one.
        my $libmarpa_libs_dir =
            File::Spec->catdir( $build_dir,
            qw(blib_shared arch auto libmarpa) );
        my $from = File::Spec->catfile( $libmarpa_libs_dir, 'libmarpa.so' );
        my $to = File::Spec->catfile( $libmarpa_libs_dir, 'libmarpa-' .$libmarpa_version . '.so' );
        say "Copying $from -> $to ";
        File::Copy::copy($from, $to) or die "Copy of $from to $to failed: $EVAL_ERROR";
    } ## end if ( $Marpa::R2::USE_PERL_AUTOCONF && defined $self->...)

    return 1;

} ## end sub do_libmarpa

sub ACTION_manifest {
    die qq{Automatic generation of the MANIFEST file is disabled\n}
        . qq{The Marpa MANIFEST file is handwritten\n};
}

sub make_writeable {
    my $file = shift;
    die qq{"$file" in "}, getcwd(), qq{" does not exist} if ! -e $file;
    my $current_mode =  (stat $file)[2];
    die qq{mode not defined for $file} if not defined $current_mode;
    chmod $current_mode | (oct 200), $file;
}

sub ACTION_licensecheck {
    my $self = shift;

    require inc::Marpa::R2::License;

    my $manifest = [keys %{ExtUtils::Manifest::maniread()}];
    my @license_problems =
        Marpa::R2::License::license_problems( $manifest, $self->verbose() );
    if (@license_problems) {
        print {*STDERR} join q{}, @license_problems
            or die "Cannot print: $ERRNO";
        die 'Fatal error due to license language issues';
    }
} ## end sub ACTION_licensecheck

sub ACTION_metacheck {
    my $self = shift;

    # does not check CPAN::Meta
    # version -- assumes updated with Module::Build
    # this should only be run when making distributions 
    # not on install, so we don't have to be too paranoid
    require CPAN::Meta;

    my $marpa_version = $self->dist_version();
    my $meta = CPAN::Meta->load_file($self->metafile());
    my $provides = $meta->{provides};
    my @metacheck_problems = ();
    PROVIDED: for my $provided_name (keys %{$provides}) {
      my $provided_version = $provides->{$provided_name}->{version};
      if (not defined $provided_version) {
          push @metacheck_problems, "No version for $provided_name\n";
          next PROVIDED;
      }
      if ($provided_version ne $marpa_version) {
          push @metacheck_problems,
          "Version of $provided_name is $provided_version, but Marpa version is $marpa_version\n";
      }
    }
    if (@metacheck_problems) {
        print {*STDERR} join q{}, @metacheck_problems
            or die "Cannot print: $ERRNO";
        die 'Fatal error due to META file issues';
    }
}

sub ACTION_distcheck {
    my $self = shift;
    $self->ACTION_licensecheck();
    $self->ACTION_metacheck();
    return $self->SUPER::ACTION_distcheck;
} ## end sub ACTION_distcheck

sub ACTION_distmeta {
    my $self         = shift;
    my $return_value = $self->SUPER::ACTION_distmeta;

    # does not check CPAN::Meta
    # version -- assumes updated with Module::Build
    # this should only be run when making distributions
    # not on install, so we don't have to be too paranoid
    require CPAN::Meta;

    my $meta   = CPAN::Meta->load_file( $self->metafile() );
    my @delete = ('DynaLoader');
    for my $provided ( keys %{ $meta->{provides} } ) {
        push @delete, $provided if $provided =~ m/\AMarpa::R2::Inner::/xms;
        push @delete, $provided if $provided =~ m/\AMarpa::R2::Internal::/xms;
        push @delete, $provided if $provided =~ m/\AMarpa::R2::HTML::Internal::/xms;
        push @delete, $provided if $provided =~ m/::Internal\z/xms;
    }
    if (@delete) {
        for my $deletion (@delete) {
            delete $meta->{provides}->{$deletion};
        }
        if (defined $meta->{dynamic_config}) {
          # Make sure this stays numeric
          $meta->{dynamic_config} = $meta->{dynamic_config}+0;
        }
        $meta->save( 'META.yml', { version => '1.4' } );
        $meta->save('META.json');
        $self->log_info("Revised META.yml and META.json\n");
    } ## end if (@delete)

    return $return_value;
} ## end sub ACTION_distmeta

sub ACTION_dist {
    my $self = shift;
    open my $fh, q{<}, 'Changes';
    my $changes = do {
        local $RS = undef;
        <$fh>;
    };
    close $fh;
    my $marpa_version = $self->dist_version();
    die qq{"$marpa_version" not in Changes file}
        if 0 > index $changes, $marpa_version;
    return $self->SUPER::ACTION_dist;
} ## end sub ACTION_dist

sub write_installed_pm {
    my ( $self, @components ) = @_;
    my $filename           = 'Installed';
    my @package_components = @components[ 1 .. $#components ];
    my $contents = installed_contents( $self, join q{::}, @package_components,
        $filename );
    $filename .= q{.pm};
    return $self->file_write( $contents, @components, $filename );
} ## end sub write_installed_pm

sub ACTION_code {
    my $self               = shift;
    my @r2_perl_components = qw(pperl Marpa R2 Perl);
    my @r2_components      = qw(lib Marpa R2);
    my $config_pm_filename = File::Spec->catfile(qw(inc Marpa R2 Config.pm ));
    my $build_filename     = 'Build';
    my @derived_files      = (
        File::Spec->catfile( @r2_components,      'Version.pm' ),
        File::Spec->catfile( @r2_components,      'Installed.pm' ),
        File::Spec->catfile( @r2_perl_components, 'Version.pm' ),
        File::Spec->catfile( @r2_perl_components, 'Installed.pm' ),
    );
    if (not $self->up_to_date(
            [ $config_pm_filename, $build_filename ],
            \@derived_files
        )
        )
    {
        say {*STDERR} 'Writing version files' or die "say failed: $ERRNO";
        write_installed_pm( $self, qw(lib Marpa R2 ) );
        write_installed_pm( $self, qw(pperl Marpa R2 Perl ) );
        my $perl_version_pm =
            perl_version_contents( $self, 'Marpa::R2::Perl' );
        my $version_pm = xs_version_contents( $self, 'Marpa::R2' );
        $self->file_write( $version_pm, qw(lib Marpa R2 Version.pm) );
        $self->file_write( $perl_version_pm,
            qw(pperl Marpa R2 Perl Version.pm) );
    } ## end if ( not $self->up_to_date( [ $config_pm_filename, ...]))

    $self->do_libmarpa();

    if ( defined $self->args('libmarpa-shared') ) {
        $self->blib('blib_shared');
    }
    return $self->SUPER::ACTION_code;
} ## end sub ACTION_code

sub ACTION_clean {
    my $self = shift;

    my $curdir = File::Spec->rel2abs( File::Spec->curdir() );
    if ( $self->verbose ) {
        print "Cleaning libmarpa\n" or die "print failed: $ERRNO";
    }

    return $self->SUPER::ACTION_clean;
} ## end sub ACTION_clean

sub ACTION_test {
    my $self = shift;
    local $ENV{PERL_DL_NONLAZY} = 1;
    if ( defined $self->args('libmarpa-shared') ) {
        $ENV{MARPA_TEST_BLIB} = 'blib_shared';
        $self->blib('blib_shared');
    }
    return $self->SUPER::ACTION_test;
}

sub ACTION_install {
    my $self = shift;
    die qq{"libmarpa-shared" not supported for "install" target\n},
        qq{  Marpa::R2 supports testing of shared libraries, but is not an installer for them\n}
        if defined $self->args('libmarpa-shared');
    return $self->SUPER::ACTION_install;
}

1;

# vim: expandtab shiftwidth=4:
