# -*- coding: utf-8 -*-

import os, os.path, sys
from os.path import join as pjoin

# Make sure that we have a good scons-version
EnsureSConsVersion(0, 96)

# Import the local 'scons'
# Add simula-scons to sys.path and PYTHONPATH
os.environ["PYTHONPATH"] = \
    os.pathsep.join([os.environ.get("PYTHONPATH",""),
                     pjoin(os.getcwd(), "scons", "simula-scons")])
sys.path.insert(0, pjoin(os.getcwd(), "scons", "simula-scons"))
import simula_scons as scons
 
# Import local exceptions
from simula_scons.Errors import PkgconfigError, PkgconfigMissing

# Create a SCons Environment based on the main os environment
env = scons.ExtendedEnvironment(ENV=os.environ)

# Set a projectname. Used in some places, like pkg-config generator
env["projectname"] = "syfi"

# Set version
env["SYFILIB_VERSION"] = "0.6.1"

def replace_lines(dotin_fname, replacements):
    f = file(dotin_fname)
    lines = f.readlines()
    f.close()

    for item in replacements:
        # find line(s) in body that match the substitution key:
        repl_lines = [l for l in lines if "@"+item+"@" in l]
        # replace the key with the replacements item, and feed into the body
        # We support multiple occurences of a string, although that should be 
        # rare:
        for l in repl_lines:
            lines[lines.index(l)] = l.replace("@"+item+"@", replacements[item])

    f = file(dotin_fname[:-3], 'w')
    f.writelines(lines)
    f.close()

replacements = {'SYFILIB_VERSION': env['SYFILIB_VERSION']}
replace_lines(pjoin('syfi', 'scons.cfg.in'), replacements)

major, minor, micro = env["SYFILIB_VERSION"].split('.')
replacements = {'SYFILIB_MAJOR_VERSION': major,
                'SYFILIB_MINOR_VERSION': minor,
                'SYFILIB_MICRO_VERSION': micro}
replace_lines(pjoin('syfi', 'syfi_version.h.in'), replacements)

try:
    out, err = scons.runCommand("pkg-config", ["ginac", "--modversion"])
except:
    print "Unable to get ginac version"
    # this will fail later
    out = "1.4.1"
major, minor, micro = out.split('.')
replacements = {'GINACLIB_MAJOR_VERSION': major,
                'GINACLIB_MINOR_VERSION': minor,
                'GINACLIB_MICRO_VERSION': micro}
replace_lines(pjoin('syfi', 'swig', 'SyFi.i.in'), replacements)


scons.setDefaultEnv(env)

# Specify a file where SCons store file signatures, used to figure out what is
# changed. We store it in the 'scons' subdirectory to avoid mixing signatures
# with the files in SyFi. 
syfi_sconsignfile = pjoin(os.getcwd(), "scons", ".sconsign")
env.SConsignFile(syfi_sconsignfile)

# -----------------------------------------------------------------------------
# Command line option handling
# -----------------------------------------------------------------------------

DefaultPackages = ""

default_prefix = pjoin(os.path.sep, "usr", "local")

# Build the commandline options for SCons:
options = [
    # configurable options for installation:
    scons.PathOption("prefix", "Installation prefix", default_prefix),
    scons.PathOption("DESTDIR",
                     "Prepend DESTDIR to each installed target file", None),
    scons.PathOption("binDir", "Binary installation directory",
                     pjoin("$prefix", "bin")),
    scons.PathOption("manDir", "Manual page installation directory",
                     os.path.join("$prefix", "share", "man")),
    scons.PathOption("libDir", "Library installation directory",
                     pjoin("$prefix", "lib")),
    scons.PathOption("pkgConfDir", "Directory for installation of pkg-config files",
                     pjoin("$prefix", "lib", "pkgconfig")),
    scons.PathOption("includeDir", "C/C++ header installation directory",
                     pjoin("$prefix", "include")),
    scons.PathOption("pythonModuleDir", "Python module installation directory", 
                     scons.defaultPythonLib(prefix="$prefix")),
    scons.PathOption("pythonExtDir", "Python extension module installation directory", 
                     scons.defaultPythonLib(prefix="$prefix", plat_specific=True)),
    # configurable options for how we want to build:
    BoolOption("enableDebug", "Build with debug information", 1),
    BoolOption("enableDebugUblas", "Add some extra Ublas debug information", 0),
    BoolOption("enableOptimize", "Compile with optimization", 0),
    BoolOption("enableDocs", "Build documentation", 0),
    BoolOption("enableTests", "Build tests", 0),
    # Enable or disable external packages.
    # These will also be listed in scons.cfg files, but if they are 
    # disabled here, that will override scons.cfg. Remark that unless the
    # module is listed as OptDependencies in scons.cfg, the whole module
    # will be turned off.
    BoolOption("cacheOptions", "Cache command-line options for later invocations", 1),
    BoolOption("veryClean", "Remove the sconsign file during clean, must be set during regular build", 0),
    ("customCxxFlags", "Customize compilation of C++ code", ""),
    ("customLinkFlags", "Customize linking of C++ code", ""),
    ("SSLOG", "Set Simula scons log file",
     pjoin(os.getcwd(), "scons", "simula_scons.log")),
    BoolOption("enableResolveCompiler", "Run tests to verify compiler", 1),
    ]


# This Configure class handles both command-line options (which are merged into 
# the environment) and autoconf-style tests. A special feature is that it
# remembers configuration parameters (options and results from tests) between
# invocations so that they are re-used when cleaning after a previous build.
configure = scons.Configure(env, ARGUMENTS, options)

# Open log file for writing
scons.logOpen(env)
scons.log("=================== %s log ===================" % env["projectname"])
scons.logDate()

# Writing the simula_scons used to the log:
scons.log("Using simula_scons from: %s" % scons.__file__)

# Notify the user about that options from scons/options.cache are being used:
if not env.GetOption("clean"):
  try:
    lines = file(pjoin('scons', 'options.cache')).readlines()
    if lines:
      print "Using options from scons/options.cache"
  except:
    pass

# If we are in very-clean mode, remove the sconsign file, the cached
# options file, and the generated pkgconfig files
if env.GetOption("clean"):
  try:
    if env["veryClean"]:
      os.unlink("%s.dblite" % syfi_sconsignfile)
      os.unlink(os.path.join('scons', 'options.cache'))
      import glob
      for f in glob.glob(os.path.join('scons', 'pkgconfig', '*.pc')):
        os.unlink(f)
  except OSError, msg:
    scons.log("Error using 'veryClean' option:\n%s\n" % msg)

# Default CXX and FORTRAN flags
env["CXXFLAGS"] = "-Wall -pipe -ansi" # -Werror"
#env["SHFORTRANFLAGS"] = "-Wall -pipe -fPIC"

# Default link flags
env["LINKFLAGS"] = ""

# If Debug is enabled, add -g:
if env["enableDebug"]:
  env.Append(CXXFLAGS=" -DDEBUG -g")

if not env["enableDebugUblas"]:
  env.Append(CXXFLAGS=" -DNDEBUG")

# if Optimization is requested, use -O3
if env["enableOptimize"]:
  env.Append(CXXFLAGS=" -O3")
else:
  # FIXME: why are we optimizing when enableOptimize is False?
  env.Append(CXXFLAGS=" -O2")

# Append whatever custom flags given
if env["customCxxFlags"]:
  env.Append(CXXFLAGS=" " + env["customCxxFlags"])

# Append custom linker flags
if env["customLinkFlags"]:
  env.Append(LINKFLAGS=" " + env["customLinkFlags"])

# Look for custom compiler and linker flags in os.environ
if os.environ.has_key("CXXFLAGS"):
  env.Append(CXXFLAGS=" %s" % os.environ["CXXFLAGS"])
if os.environ.has_key("LINKFLAGS"):
  env.Append(LINKFLAGS=" %s" % os.environ["LINKFLAGS"])

# Determine which compiler to be used:
cxx_compilers = ["c++", "g++", "CC"]
# Use CXX from os.environ if available:
env["CXX"] = os.environ.get("CXX", env.Detect(cxx_compilers))
if not env["CXX"]:
  print "Unable to find any valid C++ compiler."
  # try to use g++ as default:
  env["CXX"] = "g++"

# process list of packages to be included in allowed Dependencies.
# Do we need this any more? I think we rather pick up (external) packages from
# the scons.cfg files. Actually, I doubt usePackages is ever used?
#env["usePackages"] = scons.resolvePackages(env["usePackages"].split(','),\
#        env.get("customDefaultPackages", DefaultPackages).split(","))

# Figure out if we should fetch datafiles, and set an option in env. 
#doFetch = "fetch" in COMMAND_LINE_TARGETS or (env["autoFetch"]) and not env.GetOption("clean")
#env["doFetch"] = doFetch

# -----------------------------------------------------------------------------
# Call the main SConscript in the project directory
# -----------------------------------------------------------------------------
try:
    # Invoke the SConscript as if it was situated in the build directory, this
    # tells SCons to build beneath this
    buildDataHash = env.SConscript(pjoin(env["projectname"], "SConscript"),
                                   exports=["env", "configure"])
except PkgconfigError, err:
    sys.stderr.write("%s\n" % err)
    Exit(1)

# -----------------------------------------------------------------------------
# Set up build targets
# -----------------------------------------------------------------------------

# default build-targets: shared libs, extension modules, programs, demos, and
# documentation.
for n in buildDataHash["shlibs"] + buildDataHash["extModules"] + \
        buildDataHash["progs"], buildDataHash["docs"], buildDataHash["tests"]:
  env.Default(n)

# -----------------------------------------------------------------------------
# Set up installation targets
# -----------------------------------------------------------------------------

install_prefix = "$prefix"
if env.get("DESTDIR"):
  install_prefix = os.path.join("$DESTDIR", "$prefix")
binDir = env["binDir"].replace("$prefix", install_prefix)
includeDir = env["includeDir"].replace("$prefix", install_prefix)
libDir = env["libDir"].replace("$prefix", install_prefix)
pkgConfDir = env["pkgConfDir"].replace("$prefix", install_prefix)
pythonModuleDir = env["pythonModuleDir"].replace("$prefix", install_prefix)
pythonExtDir = env["pythonExtDir"].replace("$prefix", install_prefix)
manDir = env["manDir"].replace("$prefix", install_prefix)

# install sfc into binDir:
env.Install(binDir, pjoin("scripts", "sfc"))

# install sfc manual page into manDir/man1:
env.Install(os.path.join(manDir, "man1"),
            os.path.join("doc", "man", "man1", "sfc.1.gz"))

# shared libraries goes into our libDir:
for l in buildDataHash["shlibs"]:
    env.InstallVersionedSharedLibrary(libDir, l)

# install header files in includeDir/dolfin
for h in buildDataHash["headers"]:
    env.Install(pjoin(includeDir, "SyFi"), h)

# install python scripts in the bin directory
for s in buildDataHash["pythonScripts"]:
    env.Install(binDir, s)

# install python modules, usually in site-packages
for m in buildDataHash["pythonModules"]:
    env.Install(pythonModuleDir, m)

# install extension modules, usually in site-packages
for e in buildDataHash["extModules"]:
    env.Install(pythonExtDir, e)

# install SWIG interface files in includeDir/SyFi/swig
for s in buildDataHash["swigfiles"]:
    env.Install(pjoin(includeDir, "SyFi", "swig"), s)

# install generated pkg-config files in pkgConfDir/lib/pkgconfig
for p in buildDataHash["pkgconfig"]:
    env.Install(pkgConfDir, p)

# grab installation prefix, substitute all scons construction variables
# (those prefixed with $...), and create a normalized path:
prefix = os.path.normpath(env.subst(install_prefix))
# add '/' (or similar) at the end of prefix if missing:
if not prefix[-1] == os.path.sep:
  prefix += os.path.sep

installfiles = scons.buildFileList(buildDataHash["pythonPackageDirs"])
for f in installfiles:
    installpath=os.path.sep.join(os.path.dirname(f).split(os.path.sep)[1:])
    env.Install(pjoin(pythonModuleDir, installpath), f)

# install docs
_targetdir = pjoin(prefix, "share", "doc", env["projectname"])
if 'install' in COMMAND_LINE_TARGETS and not os.path.isdir(_targetdir):
    os.makedirs(_targetdir)
env = scons.addInstallTargets(env, sourcefiles=buildDataHash["docs"],
                              targetdir=_targetdir)

# Instruct scons what to do when user requests 'install'
targets = [binDir, manDir, libDir, includeDir,
           pkgConfDir, pythonModuleDir, pythonExtDir]
env.Alias("install", targets)

# Create helper file for setting environment variables
scons.createHelperFile(env)

# Close log file
scons.logClose()

# vim:ft=python ts=2 sw=2
