The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use Data::Dumper;
use Config;
use Cwd qw(abs_path);
use ExtUtils::MakeMaker 6.64;
use Getopt::Long;

GetOptions(
	   'gdb:s' => \$gdb,
	   debug => \$debug,
	   help => \$help,
	  );

usage() if $help;

#============================================================================
# What python are we going to try?
#============================================================================
my $sel = $ENV{INLINE_PYTHON_EXECUTABLE};
unless ($sel) {
    my @pythons;
    my %pythons;
    my $sep = $^O eq 'MSWin32' ? ";" : ":";
    for $p (split /$sep/, $ENV{PATH}) {
        $p =~ s/^~/$ENV{HOME}/;
        $p .= "/python";
        next unless -f $p and -x $p;
        next if $pythons{abs_path($p)}++; # filter symlinked duplicates
        push @pythons, { path => $p };
    }

    # Keep them in PATH order.
    # @pythons = sort { $a->{path} cmp $b->{path} } @pythons;

    my $num = 1;
    print "Found these python executables on your PATH:\n";
    print $num++ . ". " . $_->{path} . "\n" for @pythons;

    if (@pythons == 1 and not $sel) {
        $sel = $pythons[0];
        print "Using the only python executable I could find\n";
        print 'Set the INLINE_PYTHON_EXECUTABLE environment variable to'
            . " the full path to your python executable to override this selection.\n";
    }
    unless ($sel) {
        $sel = prompt("Use which?", '1');
        if ($sel =~ /^\d+$/) {
            die 'Invalid number. Please enter only numbers from 1 to ' . ($num - 1)
                . " or the full path to your python executable.\n"
                . 'Set the INLINE_PYTHON_EXECUTABLE environment variable to'
                . " the full path to your python executable to avoid this question.\n"
                if $sel > ($num - 1);
            $sel = $pythons[$sel - 1];
        }
    }
}
$sel = { path => $sel } unless ref $sel eq 'HASH'; # in case the user entered a path

print "Using $sel->{path}\n";

#============================================================================
# Interrogate the python interpreter (or the user) for required flags
#============================================================================
interrogate($sel);

# Fix up the libpath and libpython
die "Could not find Python.h in include path. make will not work" unless -e "$sel->{incpath}/Python.h";
substr($sel->{incpath}, 0, 0) = "-I";
substr($sel->{libpath}, 0, 0) = "-L";
$sel->{libpython} =~ s/lib(.*)(?:\.\Q$Config{dlext}\E|\Q$Config{_a}\E)/-l$1/;

my @flags;
push @flags, debug_flag() if defined $gdb;
push @flags, '-DI_PY_DEBUG' if $debug;
push @flags, 'none (perl Makefile.PL --help for details)' unless @flags;
print <<END;
Using These Settings:
   Extra Libs:  $sel->{syslibs}
   Python Lib:  $sel->{libpath} $sel->{libpython}
   Includes:    $sel->{incpath}
   Extra Flags: @flags
END

#============================================================================
# Finalize, and write the makefile
#============================================================================
$defs = join ' ', qw(-DEXPOSE_PERL -DCREATE_PYTHON -UCREATE_PERL),
	$debug ? "-DI_PY_DEBUG" : ();

WriteMakefile(
	      $defs ? (DEFINE => $defs) : (),
	      defined $gdb  ? (OPTIMIZE => debug_flag()) : (),
	      INC => $sel->{incpath},
	      LIBS => (join " ", @$sel{qw(libpath libpython syslibs)}),
	      NAME => 'Inline::Python',
              ABSTRACT_FROM => 'Python.pod',
              AUTHOR => 'Neil Watkiss <NEILW@cpan.org>',
              LICENSE => 'perl',
	      VERSION_FROM => 'Python.pm',
	      PREREQ_PM => {
                            'Inline'       => 0.46,
                            'Digest::MD5'  => 2.50,
                            'Data::Dumper' => 0,
			  },
        TEST_REQUIRES => {
                            'Test'                => 0,
                            'Test::More'          => 0,
                            'Test::Deep'          => 0,
                            'Proc::ProcessTable'  => '0.53',
        },
	      OBJECT => 'Python.o py2pl.o perlmodule.o util.o',
	      META_MERGE => {
			    "meta-spec" => { version => 2 },
			    resources => {
			     repository => {
			      type => 'git',
			      url => 'http://github.com/niner/inline-python-pm.git',
			      web => 'http://github.com/niner/inline-python-pm',
			     },
			    },
			   },
	      clean => {FILES => 'blib_test/'},
	     );

#============================================================================
# Tries to ask the python interpreter what libraries we need, where its
# include directories are, etc.
#============================================================================
sub interrogate {
    my $ref = shift;
    return query_options($ref) unless test_interrogate($ref);
    $ref->{syslibs}   = get_config_var($ref, "LIBS");
    $ref->{incpath}   = get_config_var($ref, "INCLUDEPY");
    $ref->{libpath}   = get_config_var($ref, "LIBPL");
    $ref->{ldlib}     = get_config_var($ref, "LDLIBRARY");
    $ref->{libpython} = get_config_var($ref, "LIBRARY");
    my $tmp = rindex($ref->{libpython}, '/') + 1;
    $ref->{libpython} = substr($ref->{libpython}, $tmp);
    $ref->{libpath} = join '/', (get_config_var($ref, "LIBDEST"),
				 'config')
      if ($ref->{libpath} eq 'None');
    return query_options($ref) unless sanity_check($ref);
}

sub test_interrogate {
    my $ref = shift;
    `$ref->{path} -c "import distutils.sysconfig; distutils.sysconfig.get_config_var" 2>&1`;
    print <<END if $?;

This python is so old it doesn't know how to answer my questions.

Instead, you will be asked a series of questions about it. If possible, 
I will give you a set of reasonable options to choose from. You can 
always enter the complete answer yourself if none of mine are correct.
END
    #' stupid vim.
    return $? == 0 ? 1 : 0;
}

sub sanity_check {
    my $ref = shift;

    $ref->{libpython} = $ref->{ldlib}
        if not -f join '/', $ref->{libpath}, $ref->{libpython}
           and -f join '/', $ref->{libpath}, $ref->{ldlib};

    unless (-d $ref->{libpath} &&
	    -d $ref->{incpath} &&
	    (-f join '/', $ref->{libpath}, $ref->{libpython})
	   ) {
	print <<END and return 0;

This python's configuration files are messed up. You'll have have to
answer the questions yourself. Here is what Python said:

   Extra Libs:  $sel->{syslibs}
   Python Library:  $sel->{libpath}/$sel->{libpython}
   Include Path:    $sel->{incpath}
END
    # ' stupid vim.
    }
    return 1;
}

sub get_config_var {
    my $ref = shift;
    my $key = shift;
    my $exe = $ref->{path};
    my $val = `$exe -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_var('$key'))"`;
    chomp $val;
    return $val;
}

sub query_options {
    my $ref = shift;

    # Every python I've seen needs pthreads. Obviously not on windows.
    my $libs_guess = $ref->{syslibs} ? $ref->{syslibs} : 
		     $^O eq 'MSWin32' ? '' : '-lpthread';
    print <<END;

	1. LIBS option. I need to know what extra libraries, if any,  
	   are required by this build of python. I recommend this:
	   ${ $libs_guess ? \$libs_guess : \"No extra libraries" }

END
    $ref->{syslibs} = prompt("Enter extra libraries (e.g. -lfoo -lbar)", 
			     $libs_guess);

    print <<END;

	2. LIBRARY option. The location of the python library. 
	   Inline::Python needs to link against it to use Python.

	Here are the libraries I know about:
END
    my @libs = show_python_libs($ref);
    my $lib = prompt("Which? Or enter another.", '1');
    $lib = $libs[$lib-1] if $lib =~ /^\d+$/;
    $lib =~ s|\\|/|g;
    $ref->{libpath} = substr($lib, 0, rindex($lib, '/'));
    $ref->{libpython} = substr($lib, rindex($lib, '/')+1);

    print <<END;

	3. INCLUDE option. The location of the python include files.
	   Inline::Python needs these to compile.

	Here are the locations I know about:
END
    my @incs = show_python_incs($ref);
    my $inc  = prompt("Which? Or enter another.", '1');
    $inc = $incs[$inc-1] if $inc =~ /^\d+$/;
    $ref->{incpath} = $inc;
}

#============================================================================
# Python libraries to look for
#============================================================================
sub show_python_libs {
  my $ref = shift;
  my $exe = $ref->{path};

  # Convert the exe into a glob where we might find a library:
  $exe =~ s|[^/]+$||;
  $exe .= "../lib/python*/config/libpython*";

  my @py_libs = 
   (
   (map { $exe . $_ } '.a', '.so', '.lib'),
   '/usr/lib64/libpython*.a',
   '/usr/lib64/libpython*.so',
   '/usr/lib/libpython*.a',
   '/usr/lib/libpython*.so',
   '/usr/lib64/python*/config/libpython*.a',
   '/usr/lib64/python*/config/libpython*.so',
   '/usr/lib/python*/config/libpython*.a',
   '/usr/lib/python*/config/libpython*.so',
   '/usr/local/lib/libpython*.a',
   '/usr/local/lib/libpython*.so',
   '/usr/local/ActivePython-*/lib/python*/config/libpython*.a',
   '/usr/local/ActivePython-*/lib/python*/config/libpython*.so',

   # Win32 support
   'C:/Python*/libs/python*.lib',
   'C:/Program Files/Python*/libs/python*.lib',
  );

  my (@found, %found);
  push @found, grep { -f && $found{abspath($_)}++ == 0 } glob for @py_libs;
  @found = sort map { abspath($_) } @found;
  my $num = '1';
  print "\t   " . $num++ . ") " . $_ . "\n" for @found;
  print "\n";
  return @found;
}

#============================================================================
# Python include files to look for
#============================================================================
sub show_python_incs {
  my $ref = shift;
  my $exe = $ref->{path};

  # Convert the exe into a glob where we might find the includes:
  $exe =~ s|[^/]+$||;
  $exe .= "../include/python*";

  my @py_incs = 
   (
   $exe,
   '/usr/local/ActivePython-*/include/python*',
   '/usr/include/python*',
   '/usr/local/include/python*',

   # Win32 support
   'C:/Python*/include',
   'C:/Program Files/Python*/include',
   );

  my (@found, %found);
  push @found, grep { -d && $found{abspath($_)}++ == 0 } glob for @py_incs;
  @found = sort map { abspath($_) } @found;
  my $num = 1;
  print "\t   " . $num++ . ") " . $_ . "\n" for @found;
  print "\n";
  return @found;
}

# This can deal with files as well as directories
sub abspath {
    use Cwd qw(abs_path);
    my ($path, $file) = shift;
    if (-f $path) {
	my @p = split '/', $path;
        $path = join '/', @p[0..$#p-1]; # can't use -2 in a range
	$file = $p[-1];
    }
    $path = abs_path($path);
    return defined $file ? join '/', $path, $file : $path;
}

sub debug_flag {
    return $gdb if $gdb;
    $Config{osname} eq 'MSWin32' 	? return '-Zi' : return '-g';
}

sub usage {
    print <<'END';
Options:
    -gdb:   Turn on compiler's debugging flag (use my guess).
    -gdb=x  Pass your own debugging flag, not mine.
    -debug: Turn on many diagnostic print statements inside Inline::Python.
            This option is useful for tracing the execution path when
            debugging.
    -help:  This output.
END
# ' stupid vim
    exit 0;
}