Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
setup.py 8.48 KiB
"""
Setup script for binarycpython
"""
import os
import subprocess
import re
import sys

import setuptools

from distutils.core import setup, Extension
import distutils.command.build

#
this_file = os.path.abspath(__file__)
this_file_dir = os.path.dirname(this_file)

###
REQUIRED_BINARY_C_VERSIONS = ["2.1.7", "2.2pre1", "2.2.0", "2.2.1"]

############################################################
# Defining functionality
############################################################

# Functions
def version():
    """
    opens VERSION and returns version number
    """

    with open("VERSION") as file:
        return file.read().strip()

def readme():
    """
    Opens readme file and returns content
    """

    with open("README.md") as file:
        return file.read()

def license():
    """
    Opens license file and returns the content
    """

    with open("LICENSE.md") as file:
        return file.read()

def requirements(directory):
    """
    Opens requirements.txt and returns content as a list
    """

    requirements_file = os.path.join(directory, 'requirements.txt')

    # Read out file and construct list
    requirements_list = []
    with open(requirements_file) as f:
        for el in f.readlines():
            requirements_list.append(el.strip())

    return requirements_list

def check_version(installed_binary_c_version, required_binary_c_versions):
    """Function to check the installed version and compare it to the required version"""
    message = """
    Something went wrong. Make sure that binary_c config exists.
    Possibly the binary_c version that is installed ({}) does not match the binary_c versions ({})
    that this release of the binary_c python module requires.
    """.format(
        installed_binary_c_version, required_binary_c_versions
    )
    assert installed_binary_c_version in required_binary_c_versions, message

def execute_make():
    """
    Function to execute the makefile.

    This makefile builds the binary_c_python_api library that python will use to interface wth
    """

    # Custom extra command:
    make_command = ["make"]

    p = subprocess.run(make_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout = p.stdout  # stdout = normal output
    stderr = p.stderr  # stderr = error output

    if p.returncode != 0:
        print("Something went wrong when executing the makefile:")
        print(stderr.decode("utf-8"))
        print("Aborting")
        sys.exit(-1)

    else:
        print(stdout.decode("utf-8"))
        print("Successfully built the libbinary_c_api.so")

def call_binary_c_config(binary_c_dir, command):
    """
    Function to call the binary_c config
    """


    binary_c_config = os.path.join(BINARY_C_DIR, "binary_c-config")

    command_result = (
        subprocess.run([binary_c_config, command], stdout=subprocess.PIPE, check=True)
        .stdout.decode("utf-8")
        .split()
    )

    return command_result

############################################################
# First level checks
############################################################

####
GSL_DIR = os.getenv("GSL_DIR", None)
if not GSL_DIR:
    print(
        "Warning: GSL_DIR is not set, this might lead to errors along the installation if\
        there is no other version of GSL in the include dirs"
    )
BINARY_C_DIR = os.getenv("BINARY_C", None)
if not BINARY_C_DIR:
    print(
        "\n\n****\n**** Error: the BINARY_C environment variable is not set.\n**** This environment variable should point to the root of your binary_c\n**** installation (i.e. the directory you acquired from the repository).\n**** Aborting setup.\n****\n\n"
    )
    quit(1)

############################################################
# Getting information from binary_c
############################################################

# binary_c must be installed.
BINARY_C_VERSION = call_binary_c_config(BINARY_C_DIR, "version")
check_version(BINARY_C_VERSION[0], REQUIRED_BINARY_C_VERSIONS)

BINARY_C_INCDIRS = call_binary_c_config(BINARY_C_DIR, "incdirs_list")
BINARY_C_LIBDIRS = call_binary_c_config(BINARY_C_DIR, "libdirs_list")
BINARY_C_CFLAGS = call_binary_c_config(BINARY_C_DIR, "cflags")
# BINARY_C_CFLAGS.remove('-fvisibility=hidden')
BINARY_C_LIBS = call_binary_c_config(BINARY_C_DIR, "libs_list")
BINARY_C_DEFINES = call_binary_c_config(BINARY_C_DIR, "define_macros")

# create list of tuples of defined macros
BINARY_C_DEFINE_MACROS = []
LONE = re.compile("^-D(.+)$")
PARTNER = re.compile("^-D(.+)=(.+)$")
for x in BINARY_C_DEFINES:
    y = PARTNER.match(x)
    if y:
        BINARY_C_DEFINE_MACROS.extend([(y.group(1), y.group(2))])
    else:
        y = LONE.match(x)
        if y:
            BINARY_C_DEFINE_MACROS.extend([(y.group(1), None)])

# add API header file
API_h = os.path.join(BINARY_C_DIR, "src", "API", "binary_c_API.h")

############################################################
# Determine all directories and libraries
############################################################

INCLUDE_DIRS = [
    os.path.join(BINARY_C_DIR, "src"),
    os.path.join(BINARY_C_DIR, "src", "API"),
    "include",
] + BINARY_C_INCDIRS
if GSL_DIR:
    INCLUDE_DIRS += [os.path.join(GSL_DIR, "include")]

# LIBRARIES = ["binary_c"] + BINARY_C_LIBS + ["binary_c_python_api"]
LIBRARIES = ["binary_c"] + BINARY_C_LIBS

LIBRARY_DIRS = [
    os.path.join(BINARY_C_DIR, "src"),
    "./",
    os.path.join(this_file_dir, "lib/"),
    os.path.join(this_file_dir, "binarycpython/"),
] + BINARY_C_LIBDIRS

RUNTIME_LIBRARY_DIRS = [
    os.path.join(BINARY_C_DIR, "src"),
    "./",
    os.path.join(this_file_dir, "lib/"),
] + BINARY_C_LIBDIRS

# filter out duplicates
INCLUDE_DIRS = list(dict.fromkeys(INCLUDE_DIRS))
BINARY_C_LIBS = list(dict.fromkeys(BINARY_C_LIBS))
LIBRARIES = list(dict.fromkeys(LIBRARIES))
LIBRARY_DIRS = list(dict.fromkeys(LIBRARY_DIRS))
RUNTIME_LIBRARY_DIRS = list(dict.fromkeys(RUNTIME_LIBRARY_DIRS))

############################################################
# Making the python-c binding module
############################################################

BINARY_C_PYTHON_API_MODULE = Extension(
    name="binarycpython._binary_c_bindings",
    sources=["src/binary_c_python.c"],
    include_dirs=INCLUDE_DIRS,
    libraries=LIBRARIES,
    library_dirs=LIBRARY_DIRS,
    runtime_library_dirs=RUNTIME_LIBRARY_DIRS,
    define_macros=[] + BINARY_C_DEFINE_MACROS,
    extra_objects=[],
    extra_compile_args=[],
    language="C",
)
headers = ["src/includes/header.h"]

############################################################
# Custom build command
############################################################

# Override build command
class CustomBuildCommand(distutils.command.build.build):
    def run(self):
        # Run the original build command
        distutils.command.build.build.run(self)

############################################################
# Main setup function call
############################################################

setup(
    name="binarycpython",
    version=version(),
    description="""This is a python API for binary_c (versions {}) by David Hendriks, Rob Izzard and collaborators. Based on the initial set up by Jeff andrews.""".format(
        ",".join(REQUIRED_BINARY_C_VERSIONS),
        ",".join(REQUIRED_BINARY_C_VERSIONS),
    ),
    author="David Hendriks",
    author_email="davidhendriks93@gmail.com",
    long_description=readme(),
    long_description_content_type="text/markdown",
    url="https://gitlab.eps.surrey.ac.uk/ri0005/binary_c-python",
    license="gpl",
    keywords=[
        "binary_c",
        "astrophysics",
        "stellar evolution",
        "population synthesis",
    ],  # Keywords that define your package best
    packages=[
        "binarycpython",
        "binarycpython.utils",
        "binarycpython.utils.population_extensions",
        "binarycpython.tests",
        "binarycpython.tests.tests_population_extensions",
    ],
    install_requires=requirements(this_file_dir),
    include_package_data=True,
    ext_modules=[BINARY_C_PYTHON_API_MODULE],  # binary_c must be loaded
    classifiers=[
        "Development Status :: 4 - Beta",
        "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
        "Intended Audience :: Developers",
        "Intended Audience :: Science/Research",
        "Operating System :: POSIX :: Linux",
        "Programming Language :: Python :: 3",
        "Programming Language :: C",
        "Topic :: Education",
        "Topic :: Scientific/Engineering :: Physics",
        "Topic :: Software Development :: Libraries :: Python Modules",
    ],
    cmdclass={"build": CustomBuildCommand},
)