#!/usr/bin/env python
#
# Setup script for the Python bindings to NEST (PyNEST)
# 
# This file is part of NEST.
#
# Copyright (C) 2004 The NEST Initiative
#
# NEST is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# NEST is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with NEST.  If not, see <http://www.gnu.org/licenses/>.
#
# Author: Jochen Martin Eppler
# Date: 05/04/2007
#
# Rewritten to make use of Python's distutils package, 01/31/08
# Sven Schrader, Jochen Martin Eppler
#

import sys, os
from string import split
from sys import exit
import distutils.sysconfig as sysc


cxx                 = '@CXX@'
sources             = ['@PKGSRCDIR@/pynest/pynestkernel.cpp',
                       '@PKGSRCDIR@/pynest/pydatum.cpp',
                       '@PKGSRCDIR@/pynest/datumtopythonconverter.cpp',
                       '@PKGSRCDIR@/pynest/pynestpycsa.cpp']

# -fpermissive needed for ndarrayobject.h
# to compile due to comma terminated enumerations
pre_cxxflags = '@AM_CXXFLAGS@ @PYNEST_CXXFLAGS@'

pre_includes = [
    '@PKGSRCDIR@/libnestutil',
    '@PKGSRCDIR@/librandom',
    '@PKGSRCDIR@/sli',
    '@PKGSRCDIR@/nestkernel',
    '@PKGBUILDDIR@/libnestutil',
    '@PKGBUILDDIR@/nest',
    '@MUSIC_INCLUDE@',
    '@MPI_INCLUDE@',
    '@GSL_CFLAGS@',
    '@INCLTDL@']

pre_ldflags = [
    '-L@PKGBUILDDIR@/nestkernel/.libs',
    '-L@PKGBUILDDIR@/librandom/.libs',
    '-L@PKGBUILDDIR@/sli/.libs',
    '@LINKED_USER_MODULES@',
    '@LINKED_MODULES@',
    '-lnest',
    '-lrandom',
    '-lsli',
    '@KOMP_LIBS@',
    '@SLI_LIBS@',
    '@GSL_LIBS@',
    '@MUSIC_LIBS@',
    '@MPI_LIBS@',
    '-lpthread',
    '@LIBLTDL@',
    '@LIBADD_DL@']

include_dirs         = []
library_dirs         = []
libraries            = []
define_macros        = []
setup_flags          = []
extra_objects        = []
cxxflags             = []
extra_link_args      = ['@OPENMP_CXXFLAGS@']
runtime_library_dirs = []

for incs in pre_includes :
    for inc in incs.split(' '):
        if len(inc) > 1:
            if '-i' in inc.lower():
                include_dirs.append(inc.replace('-I', '').replace('-i',''))
            else:
                include_dirs.append(inc)

for libs in pre_ldflags :
    for lib in libs.split(' '):
        if len(lib) > 1:
            if lib[1] == 'L' :
                library_dirs.append(lib.replace('-L', ''))
            if lib[1] == 'l' :
                libraries.append(lib.replace('-l', ''))
            # read the libtool .la file and extract name and path
            if lib.endswith('.la'):
                filename=lib.replace('$(top_builddir)','@PKGBUILDDIR@')
                try:
                    fp = open(filename, 'r')
                    dlname=''
                    libdir=''
                    line = fp.readline()
                    while line:
                        # dlname: library name
                        if len(line) > 7 and line.startswith('dlname=') and dlname=='':
                            dlname = line[8:-2]
                        # old_library: library name (static module)
                        if len(line) > 12 and line.startswith('old_library=') and dlname=='':
                            dlname = line[13:-2]
                        # ...and its directory
                        if len(line) > 7 and line.startswith('libdir='):
                            libdir = line[8:-2]
                        
                        line = fp.readline()
                    fp.close()

                    # hard-code library path to avoid setting environment
                    if libdir:
                        runtime_library_dirs += [libdir]
                        
                    # full location of the object file
                    if dlname:
                        extra_objects += [ os.path.dirname(filename) + '/.libs/' + dlname ]
                except:
                    print 'Could not parse file', filename
                    
                
for flag in pre_cxxflags.strip().split(' '):
    if len(flag) > 1:
        cxxflags += [flag]

        
if 'build' in sys.argv :
    setup_flags += ['--build-base=@PKGBUILDDIR@/pynest/build']

# look for numpy libs and change flags/macros accordingly
# this dependency cannot be detected by distutils automatically
# HAVE_NUMPY must be set both in build and install mode
try:
    import numpy
    if 'arrayobject.h' in os.listdir(numpy.__path__[0]+ '/core/include/numpy'):
        include_dirs  += [ numpy.__path__[0] + '/core/include']
        define_macros += [('HAVE_NUMPY', None)]
    else:
        print '*** No numpy headers installed ***'
except ImportError:
        print '*** No numpy package installed ***'

if 'clean' in sys.argv :
    # 'make clean' does not work properly; python bug 510186
    print '*** running clean, ignore warnings about nonexistent directories ***'

sys.argv = sys.argv + setup_flags

# Change the compiler and linker:
# replace first argumemnt with compiler-variable 'cxx'
vars = sysc.get_config_vars()
for key in ['CC','LDSHARED', 'CXX', 'LINKCC']:
    value = vars[key].split()
    value[0] = cxx
    vars[key] = ' '.join(value)

# the following compiler options are not accepted by newer g++ versions
for opt in ["-Wstrict-prototypes", "-Wno-long-double", "-no-cpp-precomp"]:
    vars["CFLAGS"]=sysc.get_config_var("CFLAGS").replace(opt, "")


# from here, it's distutils' way
from distutils.core import setup, Extension

# an empty string is also interpreted as a library to link against.
if extra_link_args == ['']:
    extra_link_args = []

extdict = {'include_dirs'       : include_dirs,
           'libraries'          : libraries,
           'library_dirs'       : library_dirs,
           'sources'            : sources,
           'extra_compile_args' : cxxflags,
           'extra_link_args'    : extra_link_args,
           'define_macros'      : define_macros,
           'extra_objects'      : extra_objects}


# set linker-path correctly when using mpicc.
# distutils sets wrong parameters there. See Python bugs #445902, #1254718
if runtime_library_dirs != [] :
    if 'mpi' in cxx.lower() or cxx.lower()[-3:] in ('gcc', 'g++') :
        extdict['extra_link_args'] += ['-Wl,-rpath,' + dir for dir in runtime_library_dirs]
    else :
        extdict['runtime_library_dirs'] = runtime_library_dirs


setup(name         = 'PyNEST',
      version      = '@SLI_VERSION@',
      description  = 'PyNEST provides Python bindings for NEST',
      author       = 'The NEST Initiative',
      url          = 'http://www.nest-initiative.org',     
      packages     = ['nest', 'nest.tests'],
      package_dir  = {'nest': '@PKGSRCDIR@/pynest/nest'},
      package_data = {'nest': ['pynest-init.sli']},
      ext_modules  = [Extension('nest.pynestkernel', **extdict)])