#!/usr/bin/env python3 # -*- 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 def parseCMakeVar(var, sep=';'): """ Post-process cmake list-like variables. Example:: a = parseCMakeVar("var1;var2 ; var3;") # --> a = ['var', 'var2', 'var3'] """ if var != "": # split and remove empty strings res = list(var.split(sep)) return list(filter(len, map(str.strip, 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(r'\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 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 = parseCMakeVar('@Fortran_FLAGS@', sep=' ') # 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 # ------------ Set list of packages required to build the module ------------- # List of modules (directories) to be included with_test = "@WITH_TESTS@" == "ON" with_opencl = "@WITH_OPENCL@" == "ON" exclude=[] if not with_test: exclude.append('*tests*') if not with_opencl: exclude.append('*opencl*') packages = find_packages(exclude=exclude, where="@CMAKE_SOURCE_DIR@") # 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 == "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 == "ON": subdirs.append(os.path.join('numerics', 'fftw_f')) fftwdirs = parseCMakeVar('@FFTWLIB@') hysop_libdir.extend(fftwdirs) # -- scales sources -- withscales = '@WITH_SCALES@' if withscales == "ON": subdirs.append('scales_f') #fortran_src.add('f2py/scales2py.f90') # -- other fortran sources -- withextras = '@WITH_EXTRAS@' if withextras == "ON": subdirs.append(os.path.join('numerics', 'extras_f')) # -- set full path to fortran sources -- fortran_src = list(fortran_src) for i in range(len(fortran_src)): fortran_src[i] = os.path.join(fortran_root, fortran_src[i]) precision_file = os.path.join('@GENERATED_FORTRAN_FILES_DIR@', 'precision.f90') fortran_src.insert(0, precision_file) # === Draft for future implementation of fortran interface === # -- f2py signature file -- f2hysop = "@F_2_HYSOP@" 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)) hysop_libdir = [ld.strip() for ld in hysop_libdir] # hysop_libdir = ' '.join([ '-L{}'.format(ld) if ld[1]!='L' else hysop_libdir]) 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]) data_files = [] descr = 'Hybrid Computation with Particles.' authors = 'G.H Cottet, J.M Etancelin, J.B Keck, C.Mimeau, F.PĂ©rignon, C. Picard' config = Configuration( name='@PACKAGE_NAME@', version='@HYSOP_VERSION@', description=descr, author=authors, author_email='hysop-members@lists.forge.imag.fr', url='https://gricad-gitlab.univ-grenoble-alpes.fr/particle_methods/hysop', license='Apache Licence, Version 2.0 (APLv2)', package_dir={'': '@CMAKE_SOURCE_DIR@'}, ext_modules=ext_modules, packages=packages, data_files=data_files, ) setup(**config.todict())