#!/usr/bin/env python
#
# Copyright (C) 2005-2010 ABINIT Group (Yann Pouillon)
#
# This file is part of the ABINIT software package. For license information,
# please see the COPYING file in the top-level directory of the ABINIT source
# distribution.
#

from ConfigParser import ConfigParser,NoOptionError
from time import gmtime,strftime

import commands
import os
import re
import sys

class MyConfigParser(ConfigParser):

  def optionxform(self,option):
    return str(option)

# ---------------------------------------------------------------------------- #

#
# Subroutines
#

# Macro header
def macro_header(name,stamp):

  return """# Generated by %s on %s

#
# ABINIT plug-in support for the "configure" script
#

#
# IMPORTANT NOTE
#
# This file has been automatically generated by the %s
# script. If you try to edit it, your changes will systematically be
# overwritten.
#
""" % (name,stamp,name)



# Init macro header
def macro_plugin(nick,name,desc,libs,bins,md5s,urls,include=None,tricks="${abi_fc_vendor},${abi_fc_version}"):

  # Create template
  ret = """


# ABI_PLUGIN_@PKG_NICK@()
# -------------------
#
# Sets all variables needed to handle the @PKG_NICK@ plug-in.
#
AC_DEFUN([ABI_PLUGIN_@PKG_NICK@],[
  dnl Initial setup
  lib_@pkg_nick@_incs=""
  lib_@pkg_nick@_libs=""
  build_@pkg_nick@="no"
  abi_plug_download="no"
  abi_plug_ready="no"
  abi_plug_tarball="no"
  abi_plug_bins=""
  abi_plug_incs=""
  abi_plug_libs=""

  dnl Select downloader and corresponding options
  if test "${WGET}" != ""; then
    abi_plug_downloader="${WGET}"
    abi_plug_dlopts="--timeout=15 --tries=1 -q -O"
  elif test "${CURL}" != ""; then
    abi_plug_downloader="${CURL}"
    abi_plug_dlopts="--connect-time 15 --retry 1 -s -S -o"
  fi

  dnl Define variables needed to build the library
  @pkg_nick@_pkg_name="@PKG_NAME@"
  AC_SUBST(@pkg_nick@_pkg_name)
  @pkg_nick@_pkg_string="@PKG_DESC@"
  AC_SUBST(@pkg_nick@_pkg_string)

  if test -z "${CFGFLAGS_@PKG_NICK@}"; then
    CFGFLAGS_@PKG_NICK@=""
  fi
  AC_SUBST(CFGFLAGS_@PKG_NICK@)
  if test -z "${CPPFLAGS_@PKG_NICK@}"; then
    CPPFLAGS_@PKG_NICK@=""
  fi
  AC_SUBST(CPPFLAGS_@PKG_NICK@)
  if test -z "${CFLAGS_@PKG_NICK@}"; then
    CFLAGS_@PKG_NICK@="${CFLAGS}"
  fi
  AC_SUBST(CFLAGS_@PKG_NICK@)
  if test -z "${CXXFLAGS_@PKG_NICK@}"; then
    CXXFLAGS_@PKG_NICK@="${CXXFLAGS}"
  fi
  AC_SUBST(CXXFLAGS_@PKG_NICK@)
  if test -z "${FCFLAGS_@PKG_NICK@}"; then
    FCFLAGS_@PKG_NICK@="${FCFLAGS}"
  fi
  AC_SUBST(FCFLAGS_@PKG_NICK@)

  dnl Add optimizations for Fortran flags
  FCFLAGS_@PKG_NICK@="${FCFLAGS_@PKG_NICK@} ${fcflags_opt_@pkg_nick@}"

  dnl Check whether to activate plug-in
  if test "${enable_@pkg_nick@}" = "yes"; then

    dnl Check for already installed plug-ins
    if test "${with_plugins_prefix}" != ""; then
      AC_MSG_CHECKING([whether @PKG_NICK@ is ready for use])
      abi_plug_ready="yes"
      abi_plug_bins="${with_plugins_prefix}/bin"
      abi_plug_incs="-I${with_plugins_prefix}/include"
      abi_plug_libs="-L${with_plugins_prefix}/lib"
      @PLUG_INST_TEST@
      AC_MSG_RESULT([${abi_plug_ready}])
    fi

    if test "${abi_plug_ready}" = "yes"; then
      with_@pkg_nick@_bins="${abi_plug_bins}"
      with_@pkg_nick@_includes="${abi_plug_incs}"
      with_@pkg_nick@_libs="${abi_plug_libs}"
    fi

    dnl Check whether command-line plug-in options have been specified
    if test @INCS_TEST@"${with_@pkg_nick@_libs}" = ""; then

      dnl Check for a tarball repository
      dnl Check MD5 sum of source tarball
      AC_MSG_CHECKING([for a source tarball of @PKG_NICK@])
      if test -s "${abinit_tardir}/@PKG_NAME@.tar.gz"; then
        AC_MSG_RESULT([yes])
        ABI_CHECK_MD5SUM([${abinit_tardir}/@PKG_NAME@.tar.gz],[@MD5SUM@])
        case "${abi_md5_ok}" in
          yes)
            abi_plug_tarball="yes"
            AC_MSG_NOTICE([tarball MD5 check succeeded])
            ;;
          unknown)
            abi_plug_tarball="yes"
            AC_MSG_WARN([could not check integrity of tarball])
            ;;
          no)
            AC_MSG_WARN([tarball MD5 check failed])
            ;;
        esac
      else
        AC_MSG_RESULT([no])
      fi

      dnl Get the package
      if test "${abi_plug_ready}" = "no"; then
        if test "${abi_plug_tarball}" = "no"; then
          AC_MSG_NOTICE([downloading @PKG_NICK@ - this may take a while])

          @DOWNLOAD@

          dnl Check MD5 sum of downloaded tarball
          if test -s "${abinit_tardir}/@PKG_NAME@.tar.gz"; then
            ABI_CHECK_MD5SUM([${abinit_tardir}/@PKG_NAME@.tar.gz],[@MD5SUM@])
            case "${abi_md5_ok}" in
              yes)
                abi_plug_tarball="yes"
                AC_MSG_NOTICE([tarball MD5 check succeeded])
                ;;
              unknown)
                abi_plug_tarball="yes"
                AC_MSG_WARN([could not check integrity of tarball])
                ;;
              no)
                AC_MSG_WARN([tarball MD5 check failed])
                ;;
            esac
          fi
        fi

        dnl Enable plug-in support only if the download was successful
        if test "${abi_plug_tarball}" = "yes"; then
          lib_@pkg_nick@_incs="@INCS_PATH@"
          lib_@pkg_nick@_libs="@LIBS_SPECS@"
          build_@pkg_nick@="yes"
        else
          lib_@pkg_nick@_incs=""
          lib_@pkg_nick@_libs=""
          enable_@pkg_nick@="no"
          build_@pkg_nick@="no"
          AC_MSG_WARN([could not download @PKG_NICK@ plug-in tarball])
          AC_MSG_WARN([support for @PKG_NICK@ plug-in has been disabled])
        fi
      fi
    else
      lib_@pkg_nick@_incs="${with_@pkg_nick@_includes}"
      lib_@pkg_nick@_libs="${with_@pkg_nick@_libs}"
      build_@pkg_nick@="no"
    fi

    dnl FIXME: the following is a trick for the Sun Studio compiler
    if test "${lib_@pkg_nick@_incs}" != ""; then
      if test "${abi_fc_vendor}" = "sun"; then
        tmp_@pkg_nick@_incs=`echo "${lib_@pkg_nick@_incs}" | sed -e 's/-I/-M/g'`
        lib_@pkg_nick@_incs="${lib_@pkg_nick@_incs} ${tmp_@pkg_nick@_incs}"
      fi
    fi
  else
    enable_@pkg_nick@="no"
    build_@pkg_nick@="no"
  fi

  dnl Set variables required to build binaries
@BINS_SPECS@

  dnl Apply tricks
  if test "${build_@pkg_nick@}" = "yes"; then
    ABI_TRICKS_@PKG_NICK@(@TRICKS@)
    if test "${@pkg_nick@_tricks_bypass}" = "yes"; then
      build_@pkg_nick@="no"
    fi
  fi

  dnl Output results
  AC_MSG_CHECKING([whether to enable the @PKG_NICK@ plug-in])
  AC_MSG_RESULT([${enable_@pkg_nick@}])
  AC_MSG_CHECKING([whether to build the @PKG_NICK@ plug-in])
  AC_MSG_RESULT([${build_@pkg_nick@}])

  dnl Define preprocessing macro
  if test "${enable_@pkg_nick@}" = "yes"; then
    AC_DEFINE([HAVE_@PKG_NICK@],1,[Define to 1 if you want support for @PKG_NICK@])
  fi

  dnl Substitute variables needed for the use of the plug-in
  AC_SUBST(lib_@pkg_nick@_incs)
  AC_SUBST(lib_@pkg_nick@_libs)
  AC_SUBST(build_@pkg_nick@)

  dnl Inform Automake
  AM_CONDITIONAL(DO_TEST_@PKG_NICK@,test "${enable_@pkg_nick@}" = "yes")
  AM_CONDITIONAL(DO_BUILD_@PKG_NICK@,test "${build_@pkg_nick@}" = "yes")
]) # ABI_PLUGIN_@PKG_NICK@
"""

  # Set include specs
  if ( include is None ):
    incs_test = ""
    incs_path = ""
  else:
    incs_test = "\"${with_@pkg_nick@_includes}\" = \"\" -o "
    incs_path = "-I\$(abinit_builddir)/plugins/@pkg_nick@"

  # Set lib specs
  tmp_libs = ""
  for lib in libs:
    if ( re.match("lib.*\.a",lib) ):
      tmp_libs = " -l%s" % (re.sub(r"lib(.*)\.a",r"\1",lib)) + tmp_libs
  libs_specs = "-L\$(abinit_builddir)/plugins/@pkg_nick@" + tmp_libs

  # Set bin specs
  if ( bins is None ):
    bins_specs = "  dnl No binary for @PKG_NICK@"
  else:
    bins_specs = "  FCLIBS_@PKG_NICK@=''\n" \
               + "  AC_SUBST(FCLIBS_@PKG_NICK@)"

  # Generate install-test procedure
  inst_proc = ""
  if ( bins != None ):
    for bin in bins:
      inst_proc += "\n" \
                +  "      if test ! -x \"${with_plugins_prefix}/bin/%s\"; then\n"  % (bin) \
                +  "        abi_plug_ready=\"no\"\n" \
                +  "      fi\n"

  if ( libs != None ):
    for lib in libs:
      inst_proc += "\n" \
                +  "      if test -s \"${with_plugins_prefix}/lib/lib%s.a\"; then\n" % (lib) \
                +  "        abi_plug_libs=\"${abi_plug_libs} -l%s\"\n" % (lib) \
                +  "      else\n" \
                +  "        abi_plug_ready=\"no\"\n" \
                +  "      fi\n"

  # Generate download procedure
  urls_proc = ""
  urls_idx  = 1
  for url in urls:
    urls_proc += "          if test ! -s \"${abinit_tardir}/@PKG_NAME@.tar.gz\"; then\n"
    urls_proc += "            AC_MSG_CHECKING([availability of @PKG_NICK@ from URL %d])\n" % (urls_idx)
    urls_proc += "            ${abi_plug_downloader} ${abi_plug_dlopts} \\\n"
    urls_proc += "              \"${abinit_tardir}/@PKG_NAME@.tar.gz\" \\\n"
    urls_proc += "              '%s'\n" % (url)
    urls_proc += "            test -s \"${abinit_tardir}/@PKG_NAME@.tar.gz\" && abi_plug_download=\"yes\"\n"
    urls_proc += "            AC_MSG_RESULT([${abi_plug_download}])\n"
    urls_proc += "          fi\n"
    urls_idx  += 1
  urls_proc = urls_proc.lstrip()

  # Transform template (the order is important)
  ret = re.sub("@INCS_TEST@",incs_test,ret)
  ret = re.sub("@INCS_PATH@",incs_path,ret)
  ret = re.sub("@LIBS_SPECS@",libs_specs,ret)
  ret = re.sub("@BINS_SPECS@",bins_specs,ret)
  ret = re.sub("@PLUG_INST_TEST@",inst_proc,ret)
  ret = re.sub("@DOWNLOAD@",urls_proc,ret)
  ret = re.sub("@MD5SUM@",md5s,ret)
  ret = re.sub("@TRICKS@",tricks,ret)
  ret = re.sub("@PKG_NAME@",name,ret)
  ret = re.sub("@PKG_DESC@",desc,ret)
  ret = re.sub("@PKG_NICK@",nick.upper(),ret)
  ret = re.sub("@pkg_nick@",nick,ret)

  return ret



# ---------------------------------------------------------------------------- #

#
# Main program
#

# Initial setup
my_name    = "make-macros-plugins"
my_configs = ["config/specs/plugins.conf"]
my_output  = "config/m4/auto-plugins.m4"

# Check if we are in the top of the ABINIT source tree
if ( not os.path.exists("configure.ac") or
     not os.path.exists("src/98_main/abinit.F90") ):
  print "%s: You must be in the top of an ABINIT source tree." % my_name
  print "%s: Aborting now." % my_name
  sys.exit(1)

# Read config file(s)
for cnf_file in my_configs:
  if ( os.path.exists(cnf_file) ):
    if ( re.search("\.cf$",cnf_file) ):
      execfile(cnf_file)
  else:
    print "%s: Could not find config file (%s)." % (my_name,cnf_file)
    print "%s: Aborting now." % my_name
    sys.exit(2)

# What time is it?
now = strftime("%Y/%m/%d %H:%M:%S +0000",gmtime())

# Init
cnf = MyConfigParser()
cnf.read(my_configs[0])
abinit_plugins = cnf.sections()
abinit_plugins.sort()

# Start writing macro
m4 = file(my_output,"w")
m4.write(macro_header(my_name,now))

# Process plug-ins
for pkg in abinit_plugins:

  # Extract mandatory package information
  pkg_name = cnf.get(pkg,"name")
  pkg_nick = cnf.get(pkg,"nickname")
  pkg_desc = cnf.get(pkg,"description")
  pkg_md5s = cnf.get(pkg,"md5sum")
  pkg_urls = cnf.get(pkg,"urls").split()

  # Extract optional package information
  try:
    pkg_bins = cnf.get(pkg,"binaries").split()
  except NoOptionError:
    pkg_bins = None
  try:
    pkg_hdrs = cnf.get(pkg,"headers").split()
  except NoOptionError:
    pkg_hdrs = None
  try:
    pkg_libs = cnf.get(pkg,"libraries").split()
  except NoOptionError:
    pkg_libs = None
  try:
    pkg_mods = cnf.get(pkg,"modules").split()
  except NoOptionError:
    pkg_mods = None

  # Check whether the plug-in exports C headers or modules
  if ( (pkg_hdrs is None) and (pkg_mods is None) ):
    pkg_incs = None
  else:
    pkg_incs = "yes"

  # Write macro
  m4.write(macro_plugin(pkg,pkg_name,pkg_desc,pkg_libs,pkg_bins,pkg_md5s,pkg_urls,pkg_incs))

# Finish
m4.close()

tmp = commands.getoutput("./config/scripts/add-header-typed Autoconf %s" % (my_output))
if ( tmp != "" ):
  print tmp
