#!/usr/bin/env python # -*- coding: utf-8 -*- """setup.py file for @PYPACKAGE_NAME@ """ from setuptools import find_packages from numpy.distutils.core import setup, Extension from numpy.distutils.misc_util import Configuration from distutils.spawn import find_executable import os import re import glob import fnmatch # tools to deal with fortran dependencies. import sys sys.path.append('@CMAKE_BINARY_DIR@/') import sort_f90 # --- Check if c++ interface is enabled --- enable_cpp = "@USE_CXX@" if enable_cpp: swig_executable = find_executable("@SWIG_EXECUTABLE@") def parseCMakeVar(var): """post-process cmake list-like variable Example:: a = parseCMakeVar("var1;var2;var3;") # --> a = ['var', 'var2', 'var3'] """ if var != "": res = list(set(var.split(';'))) # list/set stuff to avoid duplicates # remove empty strings to avoid '-I -I' things leading to bugs if res.count(''): res.remove('') return res else: return [] def parseCMakeDefines(var): """post-process cmake variables, corresponding to pre-processor definitions. Example:: res = parseCMakeDefines("-DVAR=VAL1:-DVAR2=VAL2:-DVAR3=VAL3") # --> res = [('VAR', 'VAL1'), ('VAR2', 'VAL2'), ('VAR3', 'VAL3')] """ defines = parseCMakeVar(var) if len(defines)==0: return None # regex to match compiler defines like -DMACRO_NAME or # -DMACRO_NAME = MACRO_VALUE p = re.compile('\s*(?:-D)?\s*(\w+)(?:\s*=\s*(\w+))?\s*') res = list() for d in defines: m = p.match(d) if m: res.append(m.group(1,2)) else: print "\tWarning: Could not extract cmake define from '", d, "'." return res # --- external libraries to be linked with --- hysop_link_libraries = parseCMakeVar("@HYSOP_LINK_LIBRARIES@") # deal with macosx framework ... extra_flags = [] for lib in hysop_link_libraries: res = lib.find('framework') if res >= 0: index = hysop_link_libraries.index(lib) filename = os.path.basename(lib) hysop_link_libraries.pop(index) filename = filename.split('.')[0] extra_flags.append('-framework ' + filename) hysop_link_libraries += extra_flags # hysop_link_libraries_names = set() # hysop_link_libraries_dirs = set() # for lib in hysop_link_libraries: # hysop_link_libraries_names.add(os.path.basename(lib)) # hysop_link_libraries_dirs.add(os.path.dirname(lib)) # hysop_link_libraries_names = list(hysop_link_libraries_names) # hysop_link_libraries_dirs = list(hysop_link_libraries_dirs) def create_fortran_extension(name, pyf_file=None, src_dirs=None, sources=None, libdir=None, libs=None, debug_mode=0): """Create a new f2py module from fortran files Parameters ---------- name: string python module name pyf_file: string, optional f2py interface file (absolute path) src_dirs: list, optional all directories containing fortran sources (absolute path) that must be taken into account. Warning: dir. scan is not recursive. sources: list, optional all fortran files that must be taken into account libs: list of strings, optional names of all libraries to be linked with to build f2py module --> for -l option libdir: list of strings, optional list of directories, absolute path to libraries listed in libs --> for -L... option debug_mode: boolean, optional if true enable f2py debug mode, default=False Notes ----- * either sources or src_dirs is required but both can be used simultaneously. * only .f90 and .f95 files are taken into account. """ # --- Collect list of fortran files --- if sources is None: sources = [] assert src_dirs is not None for sdir in src_dirs: sources += glob.glob(os.path.join(sdir, '*.f95')) sources += glob.glob(os.path.join(sdir, '*.f90')) # Reorder source list with fortran modules # dependencies. It seems that this is not taken into # account in f2py or distutils. if pyf_file is not None: sources.append(pyf_file) sources = sort_f90.sort(sources) # --- set f2py options --- f2py_options = ['--no-lower', '--no-wrap-functions'] options = [] if debug_mode == 1: options.append(('F2PY_REPORT_ON_ARRAY_COPY', '1')) if os.uname()[0] == 'Linux': options.append(('F2PY_REPORT_ATEXIT', '1')) # --- set include dir --- inc_dir = parseCMakeVar("@FORTRAN_INCLUDE_DIRS@") inc_dir += parseCMakeVar("@MPI_Fortran_INCLUDE_PATH@") inc_dir += parseCMakeVar('@CMAKE_Fortran_MODULE_DIRECTORY@') # --- set compilation flags --- fortran_flags = ['@Fortran_FLAGS@'] # --- set include dir --- # inc_dir += hysop_link_libraries_dirs # --- set external libraries --- # libs += hysop_link_libraries_names # we trust cmake for external libraries and # add them to linker, without using libraries option extra_link_args = hysop_link_libraries ext_fort = Extension(name=name, sources=sources, f2py_options=f2py_options, include_dirs=inc_dir, define_macros=options, library_dirs=libdir, libraries=libs, extra_f90_compile_args=fortran_flags, extra_link_args=extra_link_args ) return ext_fort def create_swig_extension(name, inc_dirs, src_dirs=None, sources=None): """Create a python module from C++ files, using swig Parameters ---------- name: string module name inc_dirs: list list of directories for includes (-I ...), absolute path containing headers (.hpp) and swig files (.i) sources: list, optional all c++ files that must be taken into account src_dirs: list, optional all directories containing fortran sources (absolute path) that must be taken into account. Warning: no recursive scan in src_dirs. Notes ----- * either sources, src_dirs or inc_dirs is required but all can be used simultaneously. * only .cpp files are taken into account. * main config file for swig is assumed to be name.i in CMAKE_SOURCE_DIR/swig/ """ swig_dir = os.path.join('@CMAKE_SOURCE_DIR@', 'swig') swig_config_file = os.path.join(swig_dir, name + '.i') include_dirs = set(inc_dirs) if sources is None: sources = [] assert src_dirs is not None or inc_dirs is not None if(src_dirs is None): assert(inc_dirs is not None) for idir in inc_dirs: #sources += glob.glob(os.path.join(idir, '**/*.cpp'), recursive=True) for root, dirnames, filenames in os.walk(idir): for filename in fnmatch.filter(filenames, '*.cpp'): sources.append(os.path.join(root, filename)) else: for sdir in src_dirs: sources += glob.glob(os.path.join(sdir, '*.cpp')) sources.insert(0, swig_config_file) include_dirs = list(include_dirs) name = 'hysop._' + name swig_opts = ['-I' + swig_dir, '-O', '-Wextra', '-Werror', '-c++', '-extranative', '-safecstrings'] extern_includes = parseCMakeVar("@CXX_EXT_INCLUDES@") for exti in extern_includes: include_dirs.append(exti) libraries = parseCMakeVar("@CXX_EXT_LIBS@") library_dirs = parseCMakeVar("@CXX_EXT_LIB_DIRS@") extra_compile_args = parseCMakeVar("@CXX_FLAGS@") extra_link_args = parseCMakeVar("@CXX_LINKER_FLAGS@") define_macros = parseCMakeDefines("@CXX_EXTRA_DEFINES@") swig_ext = Extension(name, sources=sources, language='c++', swig_opts=swig_opts, include_dirs=include_dirs, library_dirs=library_dirs, libraries=libraries, define_macros=define_macros, runtime_library_dirs=library_dirs, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args) return swig_ext # ------------ Set list of packages required to build the module ------------- # List of modules (directories) to be included with_test = "@WITH_TESTS@" is "ON" if with_test: packages = find_packages(exclude=["*gpu*"], where="@CMAKE_SOURCE_DIR@") else: packages = find_packages(exclude=["*tests*", "*gpu*"], where="@CMAKE_SOURCE_DIR@") if "@WITH_GPU@" is "ON": packages.append('hysop.gpu') if with_test: packages.append('hysop.gpu.tests') # Enable this to get debug info DISTUTILS_DEBUG = 1 # ------------ Extensions (f2py, cython, ...) setup ------------ ext_modules = [] # Check if fortran interface is enabled enable_fortran = "@USE_FORTRAN@" ext = {} if enable_fortran is "ON": fortran_root = \ '@CMAKE_SOURCE_DIR@/hysop' hysop_libdir = ['@CMAKE_BINARY_DIR@/src'] hysoplib = ['@HYSOP_LIBRARY_NAME@'] f2py_options = ['--no-lower'] fortran_src = set([]) # -- list of directories which contains fortran sources -- # path must be relative to hysop package directory subdirs = ['common_f', ] ####fortran_src.add('f2py/parameters.f90') # -- fftw fortran sources -- withfftw = "@WITH_FFTW@" if withfftw is "ON": subdirs.append(os.path.join('numerics', 'fftw_f')) fftwdir = '@FFTWLIB@' hysop_libdir.append(fftwdir) # -- scales sources -- withscales = '@WITH_SCALES@' if withscales is "ON": subdirs.append('scales_f') #fortran_src.add('f2py/scales2py.f90') # -- other fortran sources -- withextras = '@WITH_EXTRAS@' if withextras is "ON": subdirs.append(os.path.join('numerics', 'extras_f')) # -- set full path to fortran sources -- fortran_src = list(fortran_src) for i in xrange(len(fortran_src)): fortran_src[i] = os.path.join(fortran_root, fortran_src[i]) precision_file = os.path.join('@GENERATED_FORTRAN_FILES_DIR@', 'precision.f95') fortran_src.insert(0, precision_file) # === Draft for future implementation of fortran interface === # -- f2py signature file -- pyf_file = os.path.join('@GENERATED_FORTRAN_FILES_DIR@', 'f2hysop.pyf') # change from relative path in subdirs to absolute path in num_dirs num_dirs = [] for sd in subdirs: num_dirs.append(os.path.join(fortran_root, sd)) num_dirs.append('@GENERATED_FORTRAN_FILES_DIR@') # create python interface to fortran sources # For the moment, it includes the 'old' interface # to scales and fftw (in sources) and the new # interface, in src_dirs ext['f2hysop'] = create_fortran_extension( name='hysop.f2hysop', sources=fortran_src, libdir=hysop_libdir, libs=hysoplib, pyf_file=pyf_file, src_dirs=num_dirs) for ex in ext: ext_modules.append(ext[ex]) # --- C++ files and swig interface -- if enable_cpp is "ON": # path to .i files swig_include_dirs = [os.path.join('@CMAKE_SOURCE_DIR@','swig')] cpp_include_dirs = ['src/fftw','src/hysop++/src'] for d in cpp_include_dirs: swig_include_dirs.append(os.path.join('@CMAKE_SOURCE_DIR@', d)) ext = {} cpp2hysop = "@CPP_2_HYSOP@" ext[cpp2hysop] = create_swig_extension(name=cpp2hysop, inc_dirs=swig_include_dirs) for ex in ext: ext_modules.append(ext[ex]) data_files = [] if "@WITH_GPU@" is "ON": cl_src_dirs = ["cl_src", "cl_src/kernels", "cl_src/advection", "cl_src/remeshing"] for cl_dir in cl_src_dirs: data_files.append( ('./hysop/gpu/' + cl_dir, ['@CMAKE_SOURCE_DIR@/hysop/gpu/' + cl_dir + '/' + cl_file for cl_file in os.listdir( '@CMAKE_SOURCE_DIR@/hysop/gpu/' + cl_dir + '/') if cl_file[0] != '.' and cl_file[0] != '#' and cl_file[-3:] == '.cl'])) descr = 'Hybrid Computation with Particles.' authors = 'G.H Cottet, J.M Etancelin, J.B Keck, C.Mimeau, F.PĂ©rignon, C. Picard' # authors = 'HySoP development team' config = Configuration( name='@PACKAGE_NAME@', version='@HYSOP_VERSION@', description=descr, author=authors, author_email='hysop-members@lists.forge.imag.fr', url='https://forge.imag.fr/projects/hysop/', license='GNU General Public License (GPLv3)', package_dir={'': '@CMAKE_SOURCE_DIR@'}, ext_modules=ext_modules, packages=packages, data_files=data_files, ) setup(**config.todict())