The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use ExtUtils::MakeMaker;
use FindBin;
use lib "$FindBin::Bin/inc";
use ILCPPConfig::CompilerGuess 'guess_compiler';
use Fcntl qw( :DEFAULT :flock );
use strict;
# use 5.008001
# Inline v0.62 now requires 5.008001.


# We're using bareword file handles and two arg open for backward
# compatibility in Makefile.PL.  Here we disable those tests in Perl::Critic.
## no critic (bareword file handle)
## no critic (two-argument open)

# The next line is verified via t/13version_numbers.t. Retain single quoting.
my $DIST_VERSION    = '0.59';                        # DRY in Makefile.PL.
my $CPP_Config_path = 'lib/Inline/CPP/Config.pm';

my $test_cpp_filename = 'ilcpptest';        # '.cpp' appended via open.
my $test_cpp          = <<'END_TEST_CPP';
#include <iostream>
int main(){ return 0; }
END_TEST_CPP

my %PREREQ_PM = (
  'Inline'            => '0.67', # Case insensitivity.
  'Inline::C'         => '0.62',
  'Parse::RecDescent' => '0',
  'Carp'              => '0',
);


check_prereqs( \%PREREQ_PM ) or warn "!!! PREREQUISITES NOT MET !!!";

my( $cc_guess, $libs_guess ) = guess_compiler();

my $cpp_compiler
  = prompt( "What C++ compiler would you like to use?", $cc_guess );
  
my $libs
  = prompt( "What default libraries would you like to include?", $libs_guess );

configure_distribution( $test_cpp_filename, $cpp_compiler, $libs );

WriteMakefile(
  NAME           => 'Inline::CPP',
  AUTHOR         => 'David Oswald <davido@cpan.org>',
  VERSION_FROM   => 'lib/Inline/CPP.pm',
  ABSTRACT_FROM  => 'lib/Inline/CPP.pod',
  LICENSE        => 'artistic',
  PREREQ_PM      => \%PREREQ_PM,
  BUILD_REQUIRES => {
    'Test::More' => '0.98',    # Core. Minimum version.
    'Config'     => '0',       # Core.
    'English'    => '0',       # Core.
    'File::Temp' => '0',       # Core.
  },
  CONFIGURE_REQUIRES => {
    'ExtUtils::CppGuess'  => '0.07',    # Beginning IL::CPP 0.54_001.
    'ExtUtils::MakeMaker' => '6.56',    # Minimum version.
    'FindBin'             => '0',       # Core.
    'Config'              => '0',       # Core.
  },
  META_MERGE => {
    'meta-spec' => {
      version => 2,
      url     => 'http://search.cpan.org/perldoc?CPAN::Meta::Spec',
    },
    no_index   => { directory => [ 'inc' ], },
    resources   => {
      license    => 'http://dev.perl.org/licenses/artistic.html',
      bugtracker => 'http://github.com/daoswald/Inline-CPP/issues',
      homepage   => 'http://github.com/daoswald/Inline-CPP',
      repository => {
        type => 'git',
        url  => 'http://github.com/daoswald/Inline-CPP.git',
        web  => 'http://github.com/daoswald/Inline-CPP',
      },
    },
    provides   => {
      'Inline::CPP' => {
        file    => 'lib/Inline/CPP.pm',
        version => $DIST_VERSION,
      },
      'Inline::CPP::Grammar' => {
        file    => 'lib/Inline/CPP/Grammar.pm',
        version => $DIST_VERSION,
      },
      'Inline::CPP::Config' => {
        file    => 'lib/Inline/CPP/Config.pm',
        version => $DIST_VERSION,
      },
    },
  },
  MIN_PERL_VERSION => '5.008001',  # Modern Inline versions require 5.8.1.
  test  => { RECURSIVE_TEST_FILES => 1 },
  clean => { FILES => '_Inline/ Inline-CPP-*/' }, # Paths can be space delimited.
);

#============================================================================
# We'll do our own prerequisite checking, since MakeMaker does it
# in a way that always fails: 'use Inline::C 0.33' will never work.
#============================================================================

sub check_prereqs {
  my $prereq_pm_href = shift;
  my $prereqs_ok = 1;

  for( sort keys %{$prereq_pm_href} ) {
    ## no critic (eval);
    eval "require $_";

    # We eval version numbers to normalize _xxx dev numbering.
    my $have = eval 'no strict q/refs/; ${"${_}::VERSION"}';

    use strict q/refs/;

    my $want = eval $prereq_pm_href->{$_};

    if( $@  or  $have < $want ) {
      warn "Warning: prerequisite $_ version $prereq_pm_href->{$_} not found.";
      $prereqs_ok = 0;
    }
  }
  return $prereqs_ok;
}


###############################################################################
# Test for appropriate header style.
# Configure distribution defaults in Inline::CPP::Config.pm.
###############################################################################

sub configure_distribution {
  my( $test_cpp_filename, $cpp_compiler, $libs ) = @_;
  
  # Obtain a sentinel lock. Hold it until file work is done.

  sysopen TESTCPP_LOCK, "$test_cpp_filename.cpp.lock", O_WRONLY | O_CREAT
    or die "Makefile.PL: Couldn't open $test_cpp_filename.cpp.lock " .
           "as a lock sentinel:\n$!";
  flock TESTCPP_LOCK, LOCK_EX
    or die "Makefile.PL: Couldn't flock $test_cpp_filename.cpp.lock:\n$!\n";
    
  # Test whether the compiler prefers <iostream> or <iostream.h>.

  sysopen TESTCPP, "$test_cpp_filename.cpp", O_WRONLY | O_CREAT
    or die "Makefile.PL: Couldn't open $test_cpp_filename.cpp"
         . " for output:\n$!\n";

  truncate TESTCPP, 0
    or die "Makefile.PL: Couldn't truncate $test_cpp_filename.cpp:\n$!\n";

  print TESTCPP $test_cpp;

  close TESTCPP
    or die "Makefile.PL: Couldn't close $test_cpp_filename:\n$!\n";

  # Compile our test C++ program that includes the <iostream> header.
  my $result;
  if ( $cpp_compiler =~ m/^cl/ ) {
    # MS compilers don't support -o (or -o is deprecated for them).
    $result = system( qq{$cpp_compiler -Fe:$test_cpp_filename.exe }
      . qq{$test_cpp_filename.cpp} );
  }
  else {
    $result = system( qq{$cpp_compiler -o $test_cpp_filename.exe }
      . qq{$test_cpp_filename.cpp} );
  }

  my $iostream_fname = 'iostream';
  my $comment        = '';
  if ( $result != 0 ) {
    # Compiling with <iostream> failed, so we'll assume .h headers.
    print "Detected <iostream.h> style headers. ('.h' needed.)\n";
    $iostream_fname = 'iostream.h';
    $comment        = '//';                 # Prepend a comment to a #define.
  }
  else {
    # Compiling with <iostream> passed, so we'll assume Standard headers.
    print "Detected <iostream> style headers. ('.h' not needed.)\n";
    unlink "$test_cpp_filename.exe" or warn $!;     # Unlink the executable.
  }

  unlink "$test_cpp_filename.cpp" or warn $!;         # Unlink the test source.

  # Apply the distribution defaults:

  open CPP_Config, $CPP_Config_path
    or die "Makefile.PL: Can't read from $CPP_Config_path"
         . " for configuration!\n$!";

  my @lines = <CPP_Config>;

  close CPP_Config;

  for (@lines) {
    s{ ( our \s* \$compiler    \s* = \s* ['"] ) [^'"]+ } {$1$cpp_compiler}x;
    s{ ( our \s* \$libs        \s* = \s* ['"] ) [^'"]+ } {$1$libs}x;
    s{ ( our \s* \$iostream_fn \s* = \s* ['"] ) [^'"]+ } {$1$iostream_fname}x;
    s{ ^ [^#]* ( \#define \s+ __INLINE_CPP_NAMESPACE_STD    ) } {$comment$1}x;
    s{ ^ [^#]* ( \#define \s+ __INLINE_CPP_STANDARD_HEADERS ) } {$comment$1}x;
  }


  # Lock friendly open for output.
  sysopen CPP_Config, $CPP_Config_path, O_WRONLY | O_CREAT
    or die "Makefile.PL: Can't open $CPP_Config_path "
         . "to write configuration:\n$!";

  truncate CPP_Config, 0
    or die "Makefile.PL: Can't truncate $CPP_Config_path to write config:\n$!";

  print CPP_Config @lines
    or die "Can't write to $CPP_Config_path for configuration!\n$!";

  close CPP_Config
    or die "Can't close $CPP_Config_path after config output!\n$!";

  close TESTCPP_LOCK; # Release lock only after test compilation, and output to
                      # Config.pm.

  unlink "$test_cpp_filename.cpp.lock" or warn $!;  # Unlink the test sentinel.

  return;
}