#!/usr/bin/env python
#
# Copyright (C) 2011-2024 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 __future__ import print_function, division, absolute_import #, unicode_literals

try:
    from ConfigParser import ConfigParser
except ImportError:
    from configparser import ConfigParser
from time import gmtime, strftime

import os
import re
import stat
import sys

py2 = sys.version_info[0] <= 2

if sys.version[0:3] < "2.7":
    sys.stderr.write("makemake requires Python version 2.7 or above. Exiting.")
    sys.exit(1)

from subprocess import Popen, PIPE
import shlex

def getstatusoutput(command):
    if py2:
        process = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
    else:
        process = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True)
    out, _ = process.communicate()
    return (process.returncode, out)

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

#
# ABINIT blocks (split of the source tree)
#

abinit_blocks = {
  "common": "shared/common/src",
  "core": "src",
  "libpaw": "shared/libpaw",
}

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

#
# Internal classes and functions
#

# Personalized configuration parser
class MyConfigParser(ConfigParser):

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

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

# Display section header
def print_header(name, title):

  print("[%s] === %s ===" % (name, title))

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

# Display message
def print_message(name, title):

  print("[%s] %s" % (name, title))

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

# Run external commands and check for errors
def run_script(name, title, cmdline, stop_on_error=True,
      indent_output=True):

  print("[%s]   %s" % (name, title))
  (ret, msg) = getstatusoutput(cmdline + " 2>&1")
  if ( msg != "" ):
    if ( indent_output ):
      sys.stderr.write("    " + re.sub("\n", "\n    ", msg) + "\n")
    else:
      sys.stderr.write(msg + "\n")
  if ( (ret != 0) and stop_on_error ):
    sys.stderr.write("cmdline: %s\n" % cmdline)
    sys.stderr.write("[%s] Aborting now! (ret = %d)\n" % (name, ret))
    sys.exit(1)

  return ret

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

# Translate version strings into usable numbers
def translate_version(name, version_string):

  # Init
  ret = version_string
  ret = ret.split(".")

  # Force x.y.z version numbers
  while ( len(ret) < 3 ):
    ret.append("0")
  if ( len(ret) > 3 ):
    ret = ret[0:3]

  # Force 2 digits
  for i in range(len(ret)):
    try:
      if ( int(ret[i]) < 10 ):
        ret[i] = "0"+ret[i]
      elif ( int(ret[i]) > 99 ):
        sys.stderr.write(
          "[%s] Error: cannot handle 3-digit version numbers\n" % (name))
        sys.stderr.write("[%s] Aborting now!\n" % (name))
        sys.exit(1)
    except ValueError:
      if ( (i == 2) and (re.match("^[0-9][a-z]$", ret[i])) ):
        ret[i] = re.sub("[a-z]", "", ret[i])
        if ( len(ret[i]) < 2 ):
          ret[i] = "0" + ret[i]
      else:
        sys.stderr.write(
          "[%s] WARNING: invalid version number '%s' set to 0\n" % \
            (name, ret[i]))
        ret[i] = "00"

  # Finish
  ret = int("".join(ret))

  return ret

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

#
# Main program
#

# Initial setup
my_name     = "makemake"
my_configs  = {
  "make":"config/specs/makemake.conf",
  "bsys":"config/specs/buildsys.conf",
  "bins":"config/specs/binaries.conf",
  "libs":"config/specs/corelibs.conf",
  "fbks":"config/specs/fbversion.conf"}

# 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") ):
  sys.stderr.write("[%s] You must be in the top of an ABINIT source tree!\n" % \
    (my_name))
  sys.stderr.write("[%s] Aborting now!\n" % (my_name))
  sys.exit(1)

# Check if we have config files
if ( os.path.exists(my_configs["make"]) ):
  mcnf = MyConfigParser()
  mcnf.read(my_configs["make"])
else:
  sys.stderr.write("[%s] Could not find config file (%s)" % \
    (my_name, my_configs["make"]))
  sys.stderr.write("[%s] Aborting now!" % (my_name))
  sys.exit(10)
if ( os.path.exists(my_configs["bsys"]) ):
  bcnf = MyConfigParser()
  bcnf.read(my_configs["bsys"])
else:
  sys.stderr.write("[%s] Could not find config file (%s)" % \
    (my_name, my_configs["bsys"]))
  sys.stderr.write("[%s] Aborting now!" % (my_name))
  sys.exit(11)
if ( os.path.exists(my_configs["bins"]) ):
  xcnf = MyConfigParser()
  xcnf.read(my_configs["bins"])
else:
  sys.stderr.write("[%s] Could not find config file (%s)" % \
    (my_name, my_configs["bins"]))
  sys.stderr.write("[%s] Aborting now!" % (my_name))
  sys.exit(12)
if ( os.path.exists(my_configs["libs"]) ):
  lcnf = MyConfigParser()
  lcnf.read(my_configs["libs"])
else:
  sys.stderr.write("[%s] Could not find config file (%s)" % \
    (my_name, my_configs["bins"]))
  sys.stderr.write("[%s] Aborting now!" % (my_name))
  sys.exit(13)
if ( not os.path.exists(my_configs["fbks"]) ):
  sys.stderr.write("[%s] Could not find config file (%s)" % \
    (my_name, my_configs["fbks"]))
  sys.stderr.write("[%s] Aborting now!" % (my_name))
  sys.exit(14)

# Parse command-line arguments
from optparse import OptionParser
my_help = "Usage: %prog [options] vdW-DF_file"
parser = OptionParser(usage=my_help, version="%prog for Abinit 9")
parser.add_option("-a", "--without-autotools", action="store_false",
  dest="run_autotools", default=True,
  help="Skip Autotools-related operations")
parser.add_option("-b", "--without-buildsys", action="store_false",
  dest="run_buildsys", default=True,
  help="Skip build-system update")
parser.add_option("-c", "--clean", action="store_true",
  dest="run_clean", default=False,
  help="Clean source tree")
parser.add_option("-k", "--keep-source", action="store_false",
  dest="run_wipe", default=True,
  help="Keep script-generated files when cleaning source tree")
parser.add_option("-m", "--without-makefiles", action="store_false",
  dest="run_makefiles", default=True,
  help="Skip makefile generation")
parser.add_option("-n", "--no-split", action="store_false",
  dest="split_source", default=True,
  help="Behave as if the source tree were not split")
parser.add_option("-s", "--without-source", action="store_false",
  dest="run_source", default=True,
  help="Skip source tree update (will skip abisrc as well)")
parser.add_option("-t", "--toggle", action="store", metavar="LIST",
  dest="toggle",
  help="Comma-separated list of subsystems to toggle")
parser.add_option("-x", "--without-subsystems", action="store_false",
  dest="run_subsystems", default=True,
  help="Skip subsystem synchronization")
(opts, args) = parser.parse_args()

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

# Banner
print_message(my_name, "Starting at %s" % (now))
print_message(my_name, "-------------------------------------")

# Make sure the directory tree is writable
print_header(my_name, "Source tree consistency")
run_script(my_name, "Enabling write permission for all dirs & files",
  "chmod -R u+w .")

# Special case: clean-up
if ( opts.run_clean ):
  my_name = "abiclean"
  print_header(my_name, "Temporary directories and files")
  run_script(my_name, "Removing Bazaar backup files",
    r"find src -name '*.~[0-9]~' -exec rm -f {} \;")
  run_script(my_name, "Removing temporary build dirs",
    "rm -rf tmp*")
  run_script(my_name, "Removing temporary test dirs",
    r"find . -depth -name 'tmp-*' -exec rm -rf {} \;")
  run_script(my_name, "Removing old files",
    r"find src -name '*.old' -exec rm -f {} \;")
  run_script(my_name, "Removing compiled Python files",
    r"find . -name '*.pyc' -o -name '*.pyo' -exec rm -f {} \;")
  run_script(my_name, "Removing compiled Python cache directories",
    r"find . -depth -name '__pycache__' -exec rm -rf {} \;")
  run_script(my_name, "Removing symbolic links in common",
    r"find shared/common/src -type l -exec rm -f {} \;")
  run_script(my_name, "Removing symbolic links in core",
    r"find src -type l -exec rm -f {} \;")

  print_header(my_name, "Abinit scripts outputs")
  run_script(my_name, "Removing file lists",
    "rm -f config/dist/auto-*.lst")
  run_script(my_name, "Removing M4 macros",
    "rm -f config/m4/auto-*.m4")
  #run_script(my_name, "Removing abilint outputs ",
  #  "rm -f .abilint abilint.log abilint.out")
  run_script(my_name, "Removing configuration dumper input",
    "rm -f config.dump.in")
  run_script(my_name, "Removing source split scripts",
    "rm -rf config/split")
  for block in sorted(abinit_blocks.keys()):
    run_script(my_name, "Removing abinit.dep files in %s" % block,
      r"find %s -name 'abinit.dep' -exec rm {} \;" % abinit_blocks[block])
    run_script(my_name, "Removing abinit.dir files in %s" % block,
      r"find %s -name 'abinit.dir' -exec rm {} \;" % abinit_blocks[block])
    run_script(my_name, "Removing abirules logs in %s" % block,
      "rm -f %s/tmp-abirules.log" % abinit_blocks[block])
    run_script(my_name, "Removing abirules outputs in %s" % block,
      r"find %s -name '*.abirules' -exec rm -f {} \;" % abinit_blocks[block])
    run_script(my_name, "Removing abiauty outputs in %s" % block,
      r"find %s -name '*.abiauty' -exec rm -f {} \;" % abinit_blocks[block])

  print_header(my_name, "Core-managed documentation")
  run_script(my_name, "Removing certified build examples",
    "rm -f doc/build/config-examples/*.ac")
  run_script(my_name, "Removing uncertified build examples",
    "rm -f doc/build/config-examples/uncertified/*.ac")
  run_script(my_name, "Removing RoboDOC tarball",
    "rm -f robodoc-html*.tar.gz")

  # Identify subdirs to clean, delegate the rest to the subsystems
  print_header(my_name, "Core-managed build files")
  run_script(my_name, "Removing top Makefiles",
    "rm -f Makefile.am Makefile.in Makefile")
  cln_dirs = ["."]
  for bsub in bcnf.sections():
    if ( bcnf.get(bsub, "type") in ("master", "subsystem") ):
      cln_dirs += bcnf.get(bsub, "subdirs").split()
  for dcln in cln_dirs:
    run_script(my_name, "Removing local data in %s/" % dcln,
      "rm -f %s/config/local/*" % dcln)
    run_script(my_name, "Removing autoheader outputs in %s/" % dcln,
      "cd %s && rm -f config.h.in* config.h" % dcln)
    run_script(my_name, "Removing autoconf outputs in %s/" % dcln,
      "cd %s && rm -rf aclocal.m4 autom4te.cache configure confstat*" % dcln)
    if ( os.path.isdir("%s/config/gnu" % dcln) ):
      run_script(my_name, "Removing automake helpers in %s/" % dcln,
        "cd %s/config/gnu && rm -f compile config.guess config.sub depcomp install-sh ltmain.sh missing" % dcln)
    run_script(my_name, "Removing configure logs in %s/" % dcln,
      "cd %s && rm -f core config.log config.status stamp-h1" % dcln)
    if ( dcln != "." ):
      run_script(my_name, "Removing .deps dirs in %s/" % dcln,
        r"find %s -depth -name '.deps' -exec rm -rf {} \;" % dcln)
      run_script(my_name, "Removing Makefile.am files in %s/" % dcln,
        r"find %s -name Makefile.am -exec rm -f {} \;" % dcln)
      run_script(my_name, "Removing Makefile.in files in %s/" % dcln,
        r"find %s -name Makefile.in -exec rm -f {} \;" % dcln)
      run_script(my_name, "Removing Makefile files in %s/" % dcln,
        r"find %s -name Makefile -exec rm -f {} \;" % dcln)
      run_script(my_name, "Removing CmakeCache.txt files in %s/" % dcln,
        r"find %s -name CMakeCache.txt -exec rm -rf {} \;" % dcln)
      run_script(my_name, "Removing CmakeFiles files in %s/" % dcln,
        r"find %s -name CMakeFiles -type d -exec rm -rf {} \;" % dcln, stop_on_error=False)
      run_script(my_name, "Removing object files in %s/" % dcln,
        r"find %s -name '*.o' -exec rm {} \;" % dcln)
      run_script(my_name, "Removing libraries files in %s/" % dcln,
        r"find %s -name '*.a' -exec rm {} \;" % dcln)
  for block in sorted(abinit_blocks.keys()):
    run_script(my_name, "Removing binary Fortran modules in %s" % block,
      r"find %s -name '*.mod' -exec rm {} \;" % abinit_blocks[block])
  run_script(my_name, "Removing Python-generated files in src/",
    r"find src -name '*.pickle' -exec rm {} \;")
  abinit_bins = xcnf.sections()
  abinit_bins.sort()
  abinit_bins = "src/98_main/" + " src/98_main/".join(abinit_bins)
  run_script(my_name, "Removing main Abinit binaries",
    "rm -f %s" % (abinit_bins))

  print_header(my_name, "Script-generated source files")
  if ( opts.run_wipe ):
    run_script(my_name, "Removing Fortran interfaces",
      r"find src -name 'interfaces_*.F90' -exec rm {} \;")
    run_script(my_name, "Removing CPP options dumper routine",
      "rm -f shared/common/src/14_hidewrite/m_cppopts_dumper.F90 shared/common/src/14_hidewrite/m_cppopts_dumper.F90.cmake")
    run_script(my_name, "Removing optimization flags dumper routine",
      "rm -f shared/common/src/14_hidewrite/m_optim_dumper.F90 shared/common/src/14_hidewrite/m_optim_dumper.F90.cmake")
  else:
    print_message(my_name,
      "*** Skipping script-generated source file removal ***")

  print_header(my_name, "Files produced by the configure script")
  run_script(my_name, "Removing top config.* files",
    "rm -f abinit.pc config.dump config.mk config.optim config.pc config.py config.sh")
  run_script(my_name, "Removing Fortran modules containing package info",
    "rm -f shared/common/src/14_hidewrite/m_build_info.F90")

  print_header(my_name, "Subsystems")
  run_script(my_name, "Removing copied files in shared/common",
    "/bin/rm -f shared/common/AUTHORS shared/common/COPYING shared/common/ChangeLog shared/common/NEWS shared/common/README shared/common/README.md")
  for subsys in bcnf.sections():
    if ( bcnf.get(subsys, "type") == "subsystem" ):
      for subdir in bcnf.get(subsys, "subdirs").split():
        run_script(my_name, "Sluicing out the %s subsystem (subdir: %s)" % \
          (subsys, subdir),
          "cd %s && ./wipeout.sh" % subdir, indent_output=False)
    elif ( bcnf.get(subsys, "type") == "data" ):
      for subdir in bcnf.get(subsys, "subdirs").split():
        run_script(my_name, "Removing Makefile.am files in %s/" % subdir,
          "rm -f %s/Makefile.am" % subdir)
        run_script(my_name, "Removing Makefile.in files in %s/" % subdir,
          "rm -f %s/Makefile.in" % subdir)
        run_script(my_name, "Removing Makefile files in %s/" % subdir,
          "rm -f %s/Makefile" % subdir)

  run_script(my_name, "Removing byte-compiled ABINIT files",
    r"find . -name 'abinit.srcc' -exec rm {} \;")
  run_script(my_name, "Removing byte-compiled Python files",
    r"find . -name '*.py[co]' -exec rm {} \;")
  run_script(my_name, "Removing Python cache dirs",
    r"find . -depth -name '__pycache__' -exec rm -r {} \;")
  run_script(my_name, "Removing abisrc.py Pickle files",
    r"find . -depth -name '_project_py[23].pickle' -exec rm {} \;")
  run_script(my_name, "Removing abisrc output files",
    "rm -f abisrc.stderr abisrc.stdout")

  # Remove LibPAW symlink
  libpaw_dir = [item for item in lcnf.sections() \
    if ( lcnf.get(item, "parent") == "libpaw" )][0]
  libpaw_ln = os.path.join("shared", "common", "src", libpaw_dir)
  if ( os.path.exists(libpaw_ln) ):
    if ( os.path.islink(libpaw_ln) ):
      os.remove(libpaw_ln)
    else:
      raise OSError("a file is blocking the way: '%s' is not a symlink" % libpaw_ln)

  now = strftime("%Y/%m/%d %H:%M:%S +0000", gmtime())
  print_message(my_name, "--------------------------------------")
  print_message(my_name, "Finishing at %s" % (now))
  try:
    end_time = int(strftime("%s", gmtime()))
  except:
    end_time = 0
  print_message(my_name, "Time elapsed: %ds" % (end_time - start_time))
  sys.exit(0)

# Get Autotools versions
(m4_ret, m4_version) = getstatusoutput("m4 --version | sed 's/o/ /g' ")
(ac_ret, ac_version) = getstatusoutput("autoconf --version")
(am_ret, am_version) = getstatusoutput("automake --version")
(lt_ret, lt_version) = getstatusoutput("libtool  --version")

# Extract and process version numbers
if ( m4_ret == 0 ):
  m4_version = m4_version.split("\n")[0]
  m4_version = re.sub(r"^(GNU [Mm]4|m4 \(GNU M4\)) ", "", m4_version)
  m4_version = re.sub(" .*", "", m4_version)
  m4_version = translate_version(my_name, m4_version)
else:
  m4_version = 0

if ( ac_ret == 0 ):
  ac_version = ac_version.split("\n")[0]
  ac_version = re.sub(r".*\(GNU Autoconf\) ", "", ac_version)
  ac_version = re.sub(" .*", "", ac_version)
  ac_version = translate_version(my_name, ac_version)
else:
  ac_version = 0

if ( am_ret == 0 ):
  am_version = am_version.split("\n")[0]
  am_version = re.sub(r".*\(GNU automake\) ", "", am_version)
  am_version = re.sub(" .*", "", am_version)
  am_version = translate_version(my_name, am_version)
else:
  am_version = 0

if ( lt_ret == 0 ):
  lt_version = lt_version.split("\n")[0]
  lt_version = re.sub(r".*\(GNU libtool\) ", "", lt_version)
  lt_version = re.sub(" .*", "", lt_version)
  lt_version = re.sub("-.*", "", lt_version)
  lt_version = translate_version(my_name, lt_version)
else:
  lt_version = 0

if ( m4_version < 10408 ):
  sys.stderr.write("[%s] Error: M4 is too old (%d) - " % \
    (my_name, m4_version) + \
    "please install v1.4.8 or above\n%s: Aborting now\n" % (my_name))
  sys.exit(10)

if ( ac_version < 26100 ):
  sys.stderr.write("[%s] Error: Autoconf is too old (%d) - " % \
    (my_name, ac_version) + \
    "please install v2.61 or above\n%s: Aborting now\n" % (my_name))
  sys.exit(20)

if ( am_version < 11000 ):
  sys.stderr.write("[%s] Error: Automake is too old (%d) - " % \
    (my_name, am_version) + \
    "please install v1.10 or above\n%s: Aborting now\n" % (my_name))
  sys.exit(30)

# Make version information available to other scripts
at_info = open("config/local/autotools.sh", "wt")
at_info.write("""# Autotools version information
abi_m4_version="%6.6d"
abi_ac_version="%6.6d"
abi_am_version="%6.6d"
abi_lt_version="%6.6d"
""" % (m4_version, ac_version, am_version, lt_version))
at_info.close()

# Update build system
print_header(my_name, "Build system update")

if ( opts.run_buildsys ):
  run_script(my_name, "Resetting configuration dumper",
    "./config/scripts/make-config-dump-in")
  run_script(my_name, "Generating macros for the Autotools",
    "./config/scripts/make-macros-autotools")
  run_script(my_name, "Generating macros for dumpers",
    "./config/scripts/make-macros-dumpers")
  run_script(my_name, "Generating macros for environment variables",
    "./config/scripts/make-macros-environment")
  run_script(my_name, "Generating macros for command-line options",
    "./config/scripts/make-macros-options")
  run_script(my_name, "Generating macros for hints",
    "./config/scripts/make-macros-hints")
  run_script(my_name, "Generating macros for debugging",
    "./config/scripts/make-macros-debug")
  run_script(my_name, "Generating macros for default optimizations",
    "./config/scripts/make-macros-optim")
  run_script(my_name, "Generating macros for per-directory optimizations",
    "./config/scripts/make-macros-dirflags")
  run_script(my_name, "Generating macros for core libraries",
    "./config/scripts/make-macros-corelibs")
  run_script(my_name, "Generating macros for feature triggers",
    "./config/scripts/make-macros-triggers")
  run_script(my_name, "Generating macros for configure output",
    "./config/scripts/make-macros-output")
  run_script(my_name, "Generating source split symlink maker",
    "./config/scripts/make-symlink-maker")
else:
  print_message(my_name, "*** Skipping build system update ***")

# Update source tree
print_header(my_name, "Source tree update")

if ( opts.run_source ):
  run_script(my_name, "Removing build examples",
    "./config/scripts/clean-build-examples")
  run_script(my_name, "Generating build examples",
    "./config/scripts/make-build-examples")
  run_script(my_name, "Generating CPP option dumper",
    "./config/scripts/make-cppopts-dumper")

  # Create a symlink to the sources of LibPAW, so that the same naming
  # conventions can be used in the whole shared section of ABINIT
  libpaw_dir = [item for item in lcnf.sections() \
    if ( lcnf.get(item, "parent") == "libpaw" )][0]
  src = os.path.join("..", "..", "libpaw", "src")
  dst = os.path.join("shared", "common", "src", libpaw_dir)
  if ( os.path.islink(dst) ):
    os.remove(dst)
  elif ( os.path.exists(dst) ):
    raise OSError("a file is blocking the way: '%s' is not a symlink" % dst)
  os.symlink(src, dst)

  # Call abisrc to build dependency graph and create files for buildsystem
  #abisrc_cmdline = "./abisrc.py validate"
  abisrc_cmdline = "./abisrc.py makemake > abisrc.stdout 2> abisrc.stderr"
  print_message(my_name, "---> running %s" % (abisrc_cmdline))
  abisrc_exitcode = os.system(abisrc_cmdline)
   
  if abisrc_exitcode != 0:
    sys.stderr.write("""
       ****************************************************************
       ***                     FATAL ERROR!                         ***
       ****************************************************************
       *** The abisrc.py script returned a non-zero exit status.    ***
       *** This usually means that the script found subroutines or  ***
       *** functions that are not declared inside modules or that   ***
       *** there is a syntax error in the Fortran code or a bug     ***
       *** See above error message.                                 ***
       ****                                                         ***
       *** Please note that procedures outside Fortran modules are  ***
       *** not allowed any longer. See abisrc.stderr for details    ***
       *** about the errors.                                        ***
       ****************************************************************\n""")
    sys.stderr.write("[%s] Aborting now\n" % (my_name))
    sys.exit(1)
else:
  print_message(my_name, "*** Skipping source tree update ***")

# Generate subsystem data
print_header(my_name, "Synchronization of subsystems")

if ( opts.run_subsystems ):
  run_script(my_name, "Wiping out former file lists for subsystems",
    "rm -f config/dist/auto-*.lst")

  sub_togls = []
  togl_mode = {"attached":"detached", "detached":"attached"}

  if ( opts.toggle ):
    sub_togls = opts.toggle.split(",")
    sub_mlist = open("config/dist/custom-modes.lst", "wt")

  for subsys in bcnf.sections():
    if ( bcnf.get(subsys, "type") == "subsystem" ):
      for subdir in bcnf.get(subsys, "subdirs").split():
        run_script(my_name, "Sluicing out the %s subsystem (subdir: %s)" % \
          (subsys, subdir),
          "cd %s && ./wipeout.sh" % subsys, indent_output=False)
        run_script(my_name, "Refreshing the %s subsystem (subdir: %s)" % \
          (subsys, subdir),
          "cd %s && ./autogen.sh" % subsys,
          indent_output=False)
      sub_mode = bcnf.get(subsys, "mode")
      if ( subsys in sub_togls ):
        sub_mode = togl_mode[sub_mode]
        sub_mlist.write("%s %s\n" % (subsys, sub_mode))

  if ( opts.toggle ):
    sub_mlist.close()

  run_script(my_name, "Generating file lists for subsystems",
    "./config/scripts/make-file-lists")
  run_script(my_name, "Generating macros for subsystems",
    "./config/scripts/make-macros-subsystems")
  run_script(my_name, "Populating required files in shared/common",
    "/bin/cp -f AUTHORS COPYING ChangeLog NEWS README README.md shared/common")
else:
  print_message(my_name, "*** Skipping synchronization of subsystems ***")

# Generate makefiles
print_header(my_name, "Makefile generation (for Automake)")

if ( opts.run_makefiles ):
  run_script(my_name, "Generating makefiles for core libraries",
    "./config/scripts/make-makefiles-corelibs")
  run_script(my_name, "Generating makefiles for binaries",
    "./config/scripts/make-makefiles-binaries")
  run_script(my_name, "Generating makefiles for exports",
    "./config/scripts/make-makefiles-exports")
  run_script(my_name, "Generating makefiles for abichecks",
    "./config/scripts/make-makefiles-abichecks")
  run_script(my_name, "Generating intermediate makefiles",
    "./config/scripts/make-makefiles-inter")
  run_script(my_name, "Generating top makefile",
    "./config/scripts/make-makefiles-top")
  run_script(my_name, "Adding individual binary targets to top makefile",
    "./config/scripts/add-targets-binaries")
  run_script(my_name, "Adding individual library targets to top makefile",
    "./config/scripts/add-targets-libraries")
else:
  print_message(my_name, "*** Skipping makefile generation ***")

# Build Autotools framework
# Note: do not use "automake --force-missing", as it overwrites the
#       INSTALL file.
print_header(my_name, "Autotools framework generation")

if ( opts.run_autotools ):
  run_script(my_name, "Generating aclocal.m4",
    "aclocal -I config/m4")
  run_script(my_name, "Generating config.h.in",
    "autoheader")
  run_script(my_name, "Generating configure script",
    "autoconf")
  #run_script(my_name, "Generating libtool scripts",
  #  "libtoolize --automake --copy --force")
  run_script(my_name, "Generating Makefile.in for each directory",
    "automake --add-missing --copy")
else:
  print_message(my_name, "*** Skipping Autotools framework generation ***")

# Display warnings
if ( m4_version < 10415 ):
  print("""
     ****************************************************************
     ***                        WARNING!                          ***
     ****************************************************************
     ***                                                          ***
     *** Versions of M4 prior to 1.4.15 are known to crash the    ***
     *** configure script in random situations and have a few     ***
     *** security issues. Use at your own risks.                  ***
     ***                                                          ***
     *** We highly recommend you to upgrade to M4 1.4.17 for a    ***
     *** smoother experience with Fortran 2003 compilers.         ***
     ***                                                          ***
     *** Thanks a lot in advance for your understanding.          ***
     ***                                                          ***
     ****************************************************************
""")

if ( ac_version < 26800 ):
  print("""
     ****************************************************************
     ***                        WARNING!                          ***
     ****************************************************************
     ***                                                          ***
     *** You are using a highly outdated version of Autoconf.     ***
     ***                                                          ***
     *** Autoconf 2.68, released in 2010, fixes regressions,      ***
     *** Fortran-related bugs, and performance issues,            ***
     *** introduced in Autoconf versions from 2.64 to 2.67.       ***
     *** Autoconf 2.69 has been the reference version since 2012  ***
     *** and has become the default one in most Unix systems      ***
     *** since 2014.                                              ***
     ***                                                          ***
     *** You are thus strongly advised to upgrade your version    ***
     *** of Autoconf if you want to contribute to Abinit with     ***
     *** a fully functional build system. Otherwise you might     ***
     *** encounter strange issues difficult to understand and     ***
     *** impossible to reproduce by other developers.             ***
     ***                                                          ***
     *** Thanks a lot in advance for your understanding.          ***
     ***                                                          ***
     ****************************************************************
""")

if ( am_version < 11202 ):
  print("""
     ****************************************************************
     ***                        WARNING!                          ***
     ****************************************************************
     ***                                                          ***
     *** Automake versions prior to 1.12.2 contain several        ***
     *** portability bugs, an incomplete support for Fortran      ***
     *** 2003 and a security issue (CVE-2012-3386). More recent   ***
     *** versions, published since 2012, are now available and    ***
     *** fix many long-standing bugs. The minimum suitable        ***
     *** version for Abinit is 1.12.6, and the recommended one    ***
     *** is 1.15.                                                 ***
     ***                                                          ***
     *** You are strongly advised to upgrade your version of      ***
     *** Automake at your earliest convenience. Otherwise you     ***
     *** might experience strange issues difficult to understand  ***
     *** and impossible to reproduce by other developers.         ***
     ***                                                          ***
     *** Thanks a lot in advance for your understanding.          ***
     ***                                                          ***
     ****************************************************************
""")

if ( lt_version < 20402 ):
  print("""
     ****************************************************************
     ***                        WARNING!                          ***
     ****************************************************************
     ***                                                          ***
     *** Libtool integration into ABINIT has now started. You     ***
     *** will have to install Libtool 2.4.2 or later if you want  ***
     *** to benefit from the advanced features it provides.       ***
     ***                                                          ***
     *** Some features of the build system will be disabled until ***
     *** you install a proper version of Libtool. The recommended ***
     *** one is 2.4.6.                                            ***
     ***                                                          ***
     ****************************************************************
""")

# Footer
now = strftime("%Y/%m/%d %H:%M:%S +0000", gmtime())
print_message(my_name, "--------------------------------------")
print_message(my_name, "Finishing at %s" % (now))

# The end
try:
  end_time = int(strftime("%s", gmtime()))
except:
  end_time = 0
print_message(my_name, "Time elapsed: %ds" % (end_time - start_time))
