#!/usr/bin/perl

# Author: Yoann Padioleau

######################################################################
# Prelude
######################################################################

# Yes I know about autoconf ... and autoconf sux.

# assume standard: diff
# assume standard: perl

#TODO check python 2.5 ?

# need latex and hevea if want to compile the documentation

#old: --with-menhir=/path/to/menhirLib or `ocamlfind query menhirLib`

my $project =
    "coccinelle";
my $projectcmdline =
    "spatch -sp_file demos/simple.cocci demos/simple.c -o /tmp/new_simple.c";

######################################################################
# Options
######################################################################

my $prefix="/usr/local";
my $python=1;
my $noocamlscripting=0;
my $opt=".opt";
my $tarzan=1;
my $pydir="pycaml";
my $menhirdir="menhirlib";
my $sexpdir="ocamlsexp";

local $_ = join ' ', @ARGV;

# Parse options
/-h/ || /--help/ and die "usage: $0 [--prefix=path] [--without-python] [--without-ocaml] [--no-opt]\n\n\t--no-opt\tDo not use the optimimized version of OCaml\n\t--opt\tUse the optimimized version of OCaml\n\n";
/--prefix=([^ ]*)/ and $prefix = $1;
/--without-python/ and $python = 0;
/--without-ocaml/ and $noocamlscripting = 1;
/--no-opt/ and $opt = "";
/--opt/ and $opt = ".opt";

#tarzan by default (used by score file parsing and now also for sexp_ast_c.ml)


#if($ARGV[0] =~ "--prefix=(.*)") {
#    $prefix = $1;
#}
#if($ARGV[1] =~ "--without-python") {
#    $python = 0;
#}

my $src="$prefix/share/$project";

######################################################################
# Side effects
######################################################################


######################################################################
# Helpers
######################################################################
#BEGIN { die "need Perl 5 or greater" if $] < 5 ; }

#use Common;
sub pr2 { print STDERR "@_\n" }
sub cat {
    my ($what) = @_;
    my @list;
    open(TMP, $what);
    while(<TMP>) { push @list, "$_"; }
    \@list;
}
sub notb   { !$_[0] }
sub mapf   { my ($f, $xs) = @_; [ map { &$f($_) } @{$xs} ] }
sub plural { my ($e) = @_; if ($e > 1) { "s" } else { "" } }

sub check_config { my ($command, $expect, $msggood, $msgbad) = @_;
    my $error = 0;

    my $full = cat($command);
    my $res = join(" ", @{$full});
#	       pr2 $res;
    if(notb($res =~ $expect)) { $error++; pr2 "!!!! $msgbad !!!!"; }
    else { pr2 $msggood }
    return $error;
}

######################################################################
# Let's go
######################################################################
pr2 "Checking your configuration.\n";

my $error = 0;

#---------------------------------------------------------------------
# Compilers and runtimes
#---------------------------------------------------------------------

$error +=
    check_config("echo \"1;;\\n\" | ocaml |",
                 "Objective(.*) 3.\(09|1[0-9]\)",
                 "OCaml (the wonderful language) is present.",
                 "The program ocaml is missing or is not a good version. We need at least 3.09",
                 );

unless ($noocamlscripting) {
$noocamlscripting = check_config("echo \"1;;\\n\" | ocaml |",
                 "Objective(.*) 3.1[1-9]",
                 "OCaml >= 3.11 is present. OCaml scripting activated.",
                 "OCaml scripting disabled. We need at least OCaml 3.11",
                 );
}
my $ocamlprefix = $noocamlscripting ? "no" : "yes";

if ($opt eq ".opt") {
   my $opt_check = `which ocamlc.opt 2> /dev/null`;
   if($opt_check =~ "/ocamlc.opt\$") {
	pr2 "ocamlc.opt is present.";
   }
   else {
   	$opt="";
	pr2 "ocamlc.opt not found";
   }

   my $opt_check = `which ocamlopt.opt 2> /dev/null`;
   if($opt_check =~ "/ocamlopt.opt\$") {
	pr2 "ocamlopt.opt is present.";
   }
   else {
   	$opt="";
	pr2 "ocamlopt.opt not found";
   }

   my $opt_check = `which ocamldep.opt 2> /dev/null`;
   if($opt_check =~ "/ocamldep.opt\$") {
	pr2 "ocamldep.opt is present.";
   }
   else {
   	$opt="";
	pr2 "ocamldep.opt not found";
   }

   my $opt_check = `which ocamllex.opt 2> /dev/null`;
   if($opt_check =~ "/ocamllex.opt\$") {
	pr2 "ocamllex.opt is present.";
   }
   else {
   	$opt="";
	pr2 "ocamllex.opt not found";
   }

   if($opt eq "") {
       pr2 "At least one native OCaml tool have not been found.";
       pr2 "Desactivation of all native OCaml tools for compilation.";
   }
}

#we have cached the result of menhir in the tgz we build.

#$error +=
#    check_config("menhir --version |",
#                 "menhir, version 20071212",
##                 "menhir, version 2006.*",
#                 "Menhir (the parser generator) is present.",
#                 "The program menhir is missing or is not a good version.",
#                 );


#---------------------------------------------------------------
# Developers tools
#---------------------------------------------------------------

pr2 "";

$error += check_config(
  "make -v 2>&1 |grep Make|",
  "GNU Make [^0-9]*3\.[0-9]+.*", #version 3.79.1, 3.81
  "make (gnu version) is present.",
  "The program gnu make is missing or is not a good version.
We need  3.XX",
);

#---------------------------------------------------------------------
# More developers tools
#---------------------------------------------------------------------

#---------------------------------------------------------------------
# Librairies
#---------------------------------------------------------------------

# Python dev
my $PY_VERSION;
if($python) {
    if(check_config(
	"python --version 2>&1 |",
	'^Python 2\.([4567]|[4567]\.\S*)$',
	"python is present",
	"python is missing or is not a good version."
       ))
    {
	$python=0;
    }
    if($python) {
	my $PY_PREFIX = `python pycaml/getprefix.py | tr -d '\n'`;
	$PY_VERSION = `python pycaml/getversion.py | tr -d '\n'`;
	my $python_hdr = "$PY_PREFIX/include/python$PY_VERSION/Python.h";
	if(check_config(
	    "ls $python_hdr 2> /dev/null | ",
	    $python_hdr,
	    "Python.h found",
	    "Python.h missing - You need to install python-dev"
	    ))
	{
	    $python=0
	}
    }
}

my $ocamlfind=!check_config(
	"which ocamlfind 2> /dev/null |",
	'/ocamlfind$',
	"ocamlfind is present",
	"ocamlfind is missing -- Switch to local library copies."
    );

# pycaml. Binding between Python and OCaml
my $syspydir=`ocamlfind query pycaml 2> /dev/null | xargs echo -n`;

if($syspydir) {
    $pydir=$syspydir;
    pr2 "'pycaml' library is present in $pydir";
} else {
    pr2 "'pycaml' library is not present. A local copy will be used.";
}

# menhirLib. Runtime system for the parsers generated with menhir
my $sysmenhirdir=`ocamlfind query menhirLib 2> /dev/null | xargs echo -n`;

if($sysmenhirdir) {
    $menhirdir=$sysmenhirdir;
    pr2 "'menhirLib' library is present in $menhirdir";
} else {
    pr2 "'menhirLib' library is not present. A local copy will be used..";
}

# sexplib. Runtime system for the parsers generated with menhir
my $syssexpdir=`ocamlfind query sexplib 2> /dev/null | xargs echo -n`;

if($syssexpdir) {
    $sexpdir=$syssexpdir;
    pr2 "'sexplib' library is present in $sexpdir";
} else {
    pr2 "'sexplib' library is not present. A local copy will be used..";
}

######################################################################
# Generate config files (platform/portability issues)
######################################################################


######################################################################
# Generate globals files (features issues)
######################################################################

######################################################################
# Diagnostic
######################################################################


if($error) {
    pr2 "
----------------------------------------------------------------------
!!!! There seems to have problem, we have found $error missing package" .
plural($error) . ".
" . (($error > 1) ? "Some of those packages" : "This package") .
    " may be installed by picking " . ($error > 1 ? "them" : "it") .
    " in $project-dependencies.tgz available
on the $project website. !!!!
----------------------------------------------------------------------
";
} else {

    pr2
"----------------------------------------------------------------------

All seems fine for $project.

To compile $project type:
  \$ make depend
  \$ make all

Or alternatively, for the optimized version:
  \$ make all.opt
If you want both, you could use:
  \$ make world


To install type:
  \$ make install

Then, to test $project simply type:
  \$ $projectcmdline

";

    if($python) {
	pr2
"To use the python SmPL feature you may have to set some environment variables.
However, they are automatically set by the front-end installed in $prefix/bin.
For bash do:
export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:$prefix/lib
export PYTHONPATH=\$PYTHONPATH:$src/python"
    }

    pr2 "----------------------------------------------------------------------";
}

######################################################################
# Generating the configuration
######################################################################

pr2 "  Compilation configuration\n";

pr2 "$project target prefix: $prefix (you can use --prefix to override it)";
pr2 "Generating Makefile.config";
open(CONFIG, ">Makefile.config");
print CONFIG "# autogenerated by configure

#
INSTALL_PROGRAM?=install -c -m 755
INSTALL_LIB?=    install -c -m 755
INSTALL_DATA?=   install -c -m 644

# Where to install the binary
BINDIR=$prefix/bin

# Where to install the man pages
MANDIR=$prefix/man

# Where to install the lib
LIBDIR=$prefix/lib

# Where to install the configuration files
SHAREDIR=$src

BASH_COMPLETION_DIR=/etc/bash_completion.d

# Features
FEATURE_PYTHON=$python
FEATURE_TARZAN=$tarzan
";

if ($python) {
print CONFIG "PYVER=$PY_VERSION";
}

print CONFIG "
PYCAMLDIR=$pydir
MENHIRDIR=$menhirdir
SEXPDIR=$sexpdir
DYNLINK=$ocamlprefix

# The OPTBIN variable is here to allow to use ocamlc.opt instead of
# ocaml, when it is available, which speeds up compilation. So
# if you want the fast version of the ocaml chain tools, set this var
# or setenv it to \".opt\" in your startup script.
OPTBIN=$opt
";


my $pythonprefix = $python ? "yes" : "no";
pr2 "Support for python scripting: $pythonprefix";
`cd python; ln -sf ${pythonprefix}_pycocci.ml pycocci.ml; `;
`cd python; make depend`;

pr2 "Support for ocaml scripting: $ocamlprefix";
`cd ocaml; ln -sf ${ocamlprefix}_prepare_ocamlcocci.ml prepare_ocamlcocci.ml;`;
`cd ocaml; make depend`;

pr2 "Modifying globals/config.ml";
my $command = "perl -p -e 's#Not_found.\*#Not_found->\\\"$src\\\"#' globals/config.ml.in > globals/config.ml";
`$command`;

pr2 "----------------------------------------------------------------------";
pr2 "  Runtime dependency\n";

if ($ocamlfind) {
pr2 "Support for external ocaml library in ocaml scripting: yes (ocamlfind found)";
}
else {
pr2 "Support for external ocaml library in ocaml scripting: no (ocamlfind may be required at runtime)";
}

pr2 "";
