<reponame>PaulJonasJost/AMICI<gh_stars>0
"""Setuptools file for creating AMICI module

This file is based on setuptools alone and does not require CMake.
All sources are compiled anew.

This file expects to be run from within its directory.

Requires:
- swig3.0
- setuptools
- pkgconfig python+executables
- hdf5 libraries and headers
"""

import os
import subprocess
import sys

import setup_clibs  # Must run from within containing directory

from distutils import log


def try_install(package: str) -> None:
    """Try installing the given package using pip. Exit on error."""

    log.info(f"Missing required package {package}. Trying to install...")
    errno = subprocess.call([sys.executable, "-m", "pip", "install", package])
    if errno:
        log.error(f"Failed trying to install {package}. "
                  "Please install manually before installing AMICI.")
        raise SystemExit(errno)

    import importlib
    importlib.invalidate_caches()
    globals()[package] = importlib.import_module(package)


try:
    # Required for numpy include directory, and for importing anything from
    # the `amici` package. Therefore, try before any amici import.
    import numpy as np
except ImportError:
    # We need numpy, but setup_requires fires too late
    try_install('numpy')
    # retry
    import numpy as np

try:
    # Required for customizing installation, but setup_requires fires too late
    from setuptools import find_packages, setup, Extension
except ImportError:
    try_install('setuptools')
    from setuptools import find_packages, setup, Extension

# Add current directory to path, as we need some modules from the AMICI
# package already for installation
sys.path.insert(0, os.getcwd())

from amici import __version__
from amici.custom_commands import (
    AmiciInstall, AmiciBuildCLib, AmiciDevelop,
    AmiciInstallLib, AmiciBuildExt, AmiciSDist)
from amici.setuptools import (
    get_blas_config,
    get_hdf5_config,
    add_coverage_flags_if_required,
    add_debug_flags_if_required,
    add_openmp_flags,
)

# Python version check. We need >= 3.6 due to e.g. f-strings
if sys.version_info < (3, 6):
    sys.exit('amici requires at least Python version 3.6')


def main():
    # Extra compiler flags
    cxx_flags = []
    amici_module_linker_flags = []
    define_macros = []

    add_openmp_flags(cxx_flags=cxx_flags, ldflags=amici_module_linker_flags)

    blaspkgcfg = get_blas_config()
    amici_module_linker_flags.extend(blaspkgcfg['extra_link_args'])
    amici_module_linker_flags.extend(
        f'-l{lib}' for lib in blaspkgcfg['libraries'])
    define_macros.extend(blaspkgcfg['define_macros'])

    extension_sources = [
        'amici/amici_wrap.cxx',  # swig interface
    ]

    h5pkgcfg = get_hdf5_config()

    if h5pkgcfg['found']:
        # Manually add linker flags. The libraries passed to Extension will
        # end up in front of the clibs in the linker line and not after, where
        # they are required.
        print("HDF5 library found. Building AMICI with HDF5 support.")
        amici_module_linker_flags.extend(
            [f'-l{lib}' for lib in
             ['hdf5_hl_cpp', 'hdf5_hl', 'hdf5_cpp', 'hdf5']])
        define_macros.extend(h5pkgcfg['define_macros'])
    else:
        print("HDF5 library NOT found. Building AMICI WITHOUT HDF5 support.")
        define_macros.append(('AMICI_SWIG_WITHOUT_HDF5', None))

    add_coverage_flags_if_required(
        cxx_flags,
        amici_module_linker_flags,
    )

    add_debug_flags_if_required(
        cxx_flags,
        amici_module_linker_flags,
    )

    # compiler and linker flags for libamici
    if 'AMICI_CXXFLAGS' in os.environ:
        cxx_flags.extend(os.environ['AMICI_CXXFLAGS'].split(' '))
    if 'AMICI_LDFLAGS' in os.environ:
        amici_module_linker_flags.extend(
            os.environ['AMICI_LDFLAGS'].split(' '))

    libamici = setup_clibs.get_lib_amici(
        h5pkgcfg=h5pkgcfg, blaspkgcfg=blaspkgcfg,
        extra_compiler_flags=cxx_flags)
    libsundials = setup_clibs.get_lib_sundials(extra_compiler_flags=cxx_flags)
    libsuitesparse = setup_clibs.get_lib_suite_sparse(
        extra_compiler_flags=cxx_flags + ['-DDLONG']
    )

    # Readme as long package description to go on PyPi
    # (https://pypi.org/project/amici/)
    with open(os.path.join(os.path.dirname(__file__), "README.md"),
              "r", encoding="utf-8") as fh:
        long_description = fh.read()

    # Build shared object
    amici_module = Extension(
        name='amici._amici',
        sources=extension_sources,
        include_dirs=['amici/include',
                      'amici/ThirdParty/gsl/',
                      *libsundials[1]['include_dirs'],
                      *libsuitesparse[1]['include_dirs'],
                      *h5pkgcfg['include_dirs'],
                      *blaspkgcfg['include_dirs'],
                      np.get_include()
                      ],
        # Cannot use here, see above
        # libraries=[
        #    'hdf5_hl_cpp', 'hdf5_hl', 'hdf5_cpp', 'hdf5'
        # ],
        define_macros=define_macros,
        library_dirs=[
            *h5pkgcfg['library_dirs'],
            *blaspkgcfg['library_dirs'],
            'amici/libs',  # clib target directory
        ],
        extra_compile_args=cxx_flags,
        extra_link_args=amici_module_linker_flags
    )
    # Monkey-patch extension (see
    # `custom_commands.set_compiler_specific_extension_options`)
    amici_module.extra_compile_args_mingw32 = ['-std=c++14']
    amici_module.extra_compile_args_unix = ['-std=c++14']
    amici_module.extra_compile_args_msvc = ['/std:c++14']

    # Install
    setup(
        name='amici',
        cmdclass={
            'install': AmiciInstall,
            'sdist': AmiciSDist,
            'build_ext': AmiciBuildExt,
            'build_clib': AmiciBuildCLib,
            'install_lib': AmiciInstallLib,
            'develop': AmiciDevelop,
        },
        version=__version__,
        description='Advanced multi-language Interface to CVODES and IDAS',
        long_description=long_description,
        long_description_content_type="text/markdown",
        url='https://github.com/AMICI-dev/AMICI',
        author='<NAME>, <NAME>, <NAME> and '
               '<NAME>',
        author_email='<EMAIL>',
        license='BSD',
        libraries=[libamici, libsundials, libsuitesparse],
        ext_modules=[amici_module],
        packages=find_packages(),
        package_dir={'amici': 'amici'},
        entry_points={
            'console_scripts': [
                'amici_import_petab = amici.petab_import:main',
                # for backwards compatibility
                'amici_import_petab.py = amici.petab_import:main'
            ]
        },
        install_requires=['sympy>=1.6.0',
                          'python-libsbml',
                          'h5py',
                          'pandas',
                          'pkgconfig',
                          'wurlitzer',
                          'toposort'],
        setup_requires=['setuptools>=40.6.3'],
        python_requires='>=3.6',
        extras_require={
            'petab': ['petab>=0.1.11'],
            'pysb': ['pysb>=1.11.0']
        },
        package_data={
            'amici': ['amici/include/amici/*',
                      'src/*template*',
                      'swig/*',
                      'libs/*',
                      'setup.py.template',
                      ],
        },
        zip_safe=False,
        include_package_data=True,
        exclude_package_data={
            '': ['README.txt'],
        },
        test_suite="tests",
        classifiers=[
            'Development Status :: 5 - Production/Stable',
            'Intended Audience :: Science/Research',
            'License :: OSI Approved :: BSD License',
            'Operating System :: POSIX :: Linux',
            'Operating System :: MacOS :: MacOS X',
            'Programming Language :: Python',
            'Programming Language :: C++',
            'Topic :: Scientific/Engineering :: Bio-Informatics',
        ],
    )


if __name__ == '__main__':
    main()

"""Setuptools file for creating AMICI module

This file is based on setuptools alone and does not require CMake.
All sources are compiled anew.

This file expects to be run from within its directory.

Requires:
- swig3.0
- setuptools
- pkgconfig python+executables
- hdf5 libraries and headers
"""

import os
import subprocess
import sys

import setup_clibs  # Must run from within containing directory

from distutils import log


def try_install(package: str) -> None:
    """Try installing the given package using pip. Exit on error."""

    log.info(f"Missing required package {package}. Trying to install...")
    errno = subprocess.call([sys.executable, "-m", "pip", "install", package])
    if errno:
        log.error(f"Failed trying to install {package}. "
                  "Please install manually before installing AMICI.")
        raise SystemExit(errno)

    import importlib
    importlib.invalidate_caches()
    globals()[package] = importlib.import_module(package)


try:
    # Required for numpy include directory, and for importing anything from
    # the `amici` package. Therefore, try before any amici import.
    import numpy as np
except ImportError:
    # We need numpy, but setup_requires fires too late
    try_install('numpy')
    # retry
    import numpy as np

try:
    # Required for customizing installation, but setup_requires fires too late
    from setuptools import find_packages, setup, Extension
except ImportError:
    try_install('setuptools')
    from setuptools import find_packages, setup, Extension

# Add current directory to path, as we need some modules from the AMICI
# package already for installation
sys.path.insert(0, os.getcwd())

from amici import __version__
from amici.custom_commands import (
    AmiciInstall, AmiciBuildCLib, AmiciDevelop,
    AmiciInstallLib, AmiciBuildExt, AmiciSDist)
from amici.setuptools import (
    get_blas_config,
    get_hdf5_config,
    add_coverage_flags_if_required,
    add_debug_flags_if_required,
    add_openmp_flags,
)

# Python version check. We need >= 3.6 due to e.g. f-strings
if sys.version_info < (3, 6):
    sys.exit('amici requires at least Python version 3.6')


def main():
    # Extra compiler flags
    cxx_flags = []
    amici_module_linker_flags = []
    define_macros = []

    add_openmp_flags(cxx_flags=cxx_flags, ldflags=amici_module_linker_flags)

    blaspkgcfg = get_blas_config()
    amici_module_linker_flags.extend(blaspkgcfg['extra_link_args'])
    amici_module_linker_flags.extend(
        f'-l{lib}' for lib in blaspkgcfg['libraries'])
    define_macros.extend(blaspkgcfg['define_macros'])

    extension_sources = [
        'amici/amici_wrap.cxx',  # swig interface
    ]

    h5pkgcfg = get_hdf5_config()

    if h5pkgcfg['found']:
        # Manually add linker flags. The libraries passed to Extension will
        # end up in front of the clibs in the linker line and not after, where
        # they are required.
        print("HDF5 library found. Building AMICI with HDF5 support.")
        amici_module_linker_flags.extend(
            [f'-l{lib}' for lib in
             ['hdf5_hl_cpp', 'hdf5_hl', 'hdf5_cpp', 'hdf5']])
        define_macros.extend(h5pkgcfg['define_macros'])
    else:
        print("HDF5 library NOT found. Building AMICI WITHOUT HDF5 support.")
        define_macros.append(('AMICI_SWIG_WITHOUT_HDF5', None))

    add_coverage_flags_if_required(
        cxx_flags,
        amici_module_linker_flags,
    )

    add_debug_flags_if_required(
        cxx_flags,
        amici_module_linker_flags,
    )

    # compiler and linker flags for libamici
    if 'AMICI_CXXFLAGS' in os.environ:
        cxx_flags.extend(os.environ['AMICI_CXXFLAGS'].split(' '))
    if 'AMICI_LDFLAGS' in os.environ:
        amici_module_linker_flags.extend(
            os.environ['AMICI_LDFLAGS'].split(' '))

    libamici = setup_clibs.get_lib_amici(
        h5pkgcfg=h5pkgcfg, blaspkgcfg=blaspkgcfg,
        extra_compiler_flags=cxx_flags)
    libsundials = setup_clibs.get_lib_sundials(extra_compiler_flags=cxx_flags)
    libsuitesparse = setup_clibs.get_lib_suite_sparse(
        extra_compiler_flags=cxx_flags + ['-DDLONG']
    )

    # Readme as long package description to go on PyPi
    # (https://pypi.org/project/amici/)
    with open(os.path.join(os.path.dirname(__file__), "README.md"),
              "r", encoding="utf-8") as fh:
        long_description = fh.read()

    # Build shared object
    amici_module = Extension(
        name='amici._amici',
        sources=extension_sources,
        include_dirs=['amici/include',
                      'amici/ThirdParty/gsl/',
                      *libsundials[1]['include_dirs'],
                      *libsuitesparse[1]['include_dirs'],
                      *h5pkgcfg['include_dirs'],
                      *blaspkgcfg['include_dirs'],
                      np.get_include()
                      ],
        # Cannot use here, see above
        # libraries=[
        #    'hdf5_hl_cpp', 'hdf5_hl', 'hdf5_cpp', 'hdf5'
        # ],
        define_macros=define_macros,
        library_dirs=[
            *h5pkgcfg['library_dirs'],
            *blaspkgcfg['library_dirs'],
            'amici/libs',  # clib target directory
        ],
        extra_compile_args=cxx_flags,
        extra_link_args=amici_module_linker_flags
    )
    # Monkey-patch extension (see
    # `custom_commands.set_compiler_specific_extension_options`)
    amici_module.extra_compile_args_mingw32 = ['-std=c++14']
    amici_module.extra_compile_args_unix = ['-std=c++14']
    amici_module.extra_compile_args_msvc = ['/std:c++14']

    # Install
    setup(
        name='amici',
        cmdclass={
            'install': AmiciInstall,
            'sdist': AmiciSDist,
            'build_ext': AmiciBuildExt,
            'build_clib': AmiciBuildCLib,
            'install_lib': AmiciInstallLib,
            'develop': AmiciDevelop,
        },
        version=__version__,
        description='Advanced multi-language Interface to CVODES and IDAS',
        long_description=long_description,
        long_description_content_type="text/markdown",
        url='https://github.com/AMICI-dev/AMICI',
        author='<NAME>, <NAME>, <NAME> and '
               '<NAME>',
        author_email='<EMAIL>',
        license='BSD',
        libraries=[libamici, libsundials, libsuitesparse],
        ext_modules=[amici_module],
        packages=find_packages(),
        package_dir={'amici': 'amici'},
        entry_points={
            'console_scripts': [
                'amici_import_petab = amici.petab_import:main',
                # for backwards compatibility
                'amici_import_petab.py = amici.petab_import:main'
            ]
        },
        install_requires=['sympy>=1.6.0',
                          'python-libsbml',
                          'h5py',
                          'pandas',
                          'pkgconfig',
                          'wurlitzer',
                          'toposort'],
        setup_requires=['setuptools>=40.6.3'],
        python_requires='>=3.6',
        extras_require={
            'petab': ['petab>=0.1.11'],
            'pysb': ['pysb>=1.11.0']
        },
        package_data={
            'amici': ['amici/include/amici/*',
                      'src/*template*',
                      'swig/*',
                      'libs/*',
                      'setup.py.template',
                      ],
        },
        zip_safe=False,
        include_package_data=True,
        exclude_package_data={
            '': ['README.txt'],
        },
        test_suite="tests",
        classifiers=[
            'Development Status :: 5 - Production/Stable',
            'Intended Audience :: Science/Research',
            'License :: OSI Approved :: BSD License',
            'Operating System :: POSIX :: Linux',
            'Operating System :: MacOS :: MacOS X',
            'Programming Language :: Python',
            'Programming Language :: C++',
            'Topic :: Scientific/Engineering :: Bio-Informatics',
        ],
    )


if __name__ == '__main__':
    main()
Setuptools file for creating AMICI module  This file is based on setuptools alone and does not require CMake. All sources are compiled anew.  This file expects to be run from within its directory.  Requires: - swig3.0 - setuptools - pkgconfig python+executables - hdf5 libraries and headers # Must run from within containing directory Try installing the given package using pip. Exit on error. # Required for numpy include directory, and for importing anything from # the `amici` package. Therefore, try before any amici import. # We need numpy, but setup_requires fires too late # retry # Required for customizing installation, but setup_requires fires too late # Add current directory to path, as we need some modules from the AMICI # package already for installation # Python version check. We need >= 3.6 due to e.g. f-strings # Extra compiler flags # swig interface # Manually add linker flags. The libraries passed to Extension will # end up in front of the clibs in the linker line and not after, where # they are required. # compiler and linker flags for libamici # Readme as long package description to go on PyPi # (https://pypi.org/project/amici/) # Build shared object # Cannot use here, see above # libraries=[ #    'hdf5_hl_cpp', 'hdf5_hl', 'hdf5_cpp', 'hdf5' # ], # clib target directory # Monkey-patch extension (see # `custom_commands.set_compiler_specific_extension_options`) # Install # for backwards compatibility