diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e8c2979f0750ffccb22326ce923f8b1638099bd..776c4e3be35b6a2044015774c760d7b5b0d3956c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ message(STATUS "Project version: ${version}") # Note : project name is {project} and python module name is also {project} set(module_name _${CMAKE_PROJECT_NAME}) # target name +set(pybind_module_name ${CMAKE_PROJECT_NAME}) # name of submodule for python bindings ############################################## # Define options @@ -84,10 +85,14 @@ endif() # PYTHON BINDING if (PYBIND) - # Handles Python + pybind11 headers dependencies + # Python binding lib is by default installed in <prefix>/python_packages/<package>/ + # When installed from python, setup.py should set it to the python package dir + set(PYBIND_INSTALL_PREFIX python_packages/${pybind_module_name} CACHE PATH "Python package install prefix") + include(PybindModuleCreation) - generate_python_binding(${CMAKE_PROJECT_NAME} ${module_name}) + generate_python_binding(${pybind_module_name} ${module_name}) + # Handles Python + pybind11 headers dependencies target_link_libraries(${module_name} PUBLIC pybind11::pybind11 @@ -156,6 +161,12 @@ install(TARGETS ${module_name} EXPORT ${CMAKE_PROJECT_NAME}-targets install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +if (PYBIND) + install(TARGETS ${pybind_module_name} + DESTINATION ${PYBIND_INSTALL_PREFIX} + ) +endif() + #Export the targets to a script install(EXPORT ${CMAKE_PROJECT_NAME}-targets diff --git a/README.md b/README.md index ef2191699d0f650ee714ff73a31c9f5c36f170fd..4b7954d410bce0de1fb1f07c5a268cc962445d29 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,55 @@ To install aidge_core using pip, run the following command in your python enviro ``` bash pip install . -v ``` -> **TIPS :** Use environment variables to change compilation options : -> - `AIDGE_INSTALL` : to set the installation folder. Defaults to /usr/local/lib -> - `AIDGE_PYTHON_BUILD_TYPE` : to set the compilation mode to **Debug** or **Release** -> - `AIDGE_BUILD_GEN` : to set the build backend +> **TIPS :** Use environment variables to change compilation options: +> - `AIDGE_INSTALL` : to set the installation folder. Defaults to `<python_prefix>/lib/libAidge` +> - `AIDGE_PYTHON_BUILD_TYPE` : to set the compilation mode to **Debug** or **Release** or "" (for default flags). Defaults to **Release**. +> - `AIDGE_BUILD_GEN` : to set the build backend (for development mode) or "" for the cmake default. Default to "". + + +## Pip installation for development + +To setup aidge_core using pip in development (or editable mode), use the `--no-build-isolation -e` options to pip. + +For instance run the following command in your python environnement for a typical setup : +``` bash +export AIDGE_PYTHON_BUILD_TYPE= # default flags (no debug info but fastest build time) +export AIDGE_PYTHON_BUILD_TYPE=Debug # or if one really need to debug the C++ code +pip install setuptools setuptools_scm[toml] cmake # Pre-install build requirements (refer to the pyproject.toml [build-system] section) +pip install -v --no-build-isolation -e . +``` + +In this configuration python files can be modified directly without re-installation. + +The C++ build dir will be created in `build/` and recompilation and install of python bindings can be done directly with: +```bash +make -C build install -j $(nproc) +# or with cmake +cmake --build build -j $(nproc) && cmake --install build +``` + +One can also use an alternate cmake build backend such as ninja which can be installed easily though pip, for instance : +``` bash +pip install ninja +export AIDGE_BUILD_GEN=Ninja +pip install -v --no-build-isolation -e . +``` + +In this case ninja is used instead of make as build backend, and recompilation when needed is done with: +```bash +ninja -C build install # note that by default ninja use available parallelism, no need for -j option +# or with cmake +cmake --build build && cmake --install build +``` + +Note that python development (or editable mode) is not always robust to changes in the python package setup, +or when changing the build backend with `AIDGE_BUILD_GEN`. +In order to re-install when the build breaks, re-execute the commands: +```bash +rm -rf *-egg-info build/ +pip install -v --no-build-isolation -e . +``` + ## Standard C++ Compilation @@ -38,7 +83,7 @@ make all install | Option | Value type | Description | |:----------:|:----------:|:-----------:| | *-DCMAKE_INSTALL_PREFIX:PATH* | ``str`` | Path to the install folder | -| *-DCMAKE_BUILD_TYPE* | ``str`` | If ``Debug``, compile in debug mode, ``Release`` compile with highest optimisations, default= ``Release`` | +| *-DCMAKE_BUILD_TYPE* | ``str`` | If ``Debug``, compile in debug mode, ``Release`` compile with highest optimisations or "" (empty) , default= ``Release`` | | *-DWERROR* | ``bool`` | If ``ON`` show warning as error during compilation phase, default=``OFF`` | | *-DPYBIND* | ``bool`` | If ``ON`` activate python binding, default=``ON`` | diff --git a/pyproject.toml b/pyproject.toml index 7d875bb35985b9ee9fcf272b1c394b249638e3ea..b820759982252b69790cde89c500e3b11f9a52da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,8 +23,7 @@ test = [ requires = [ "setuptools>=64", "setuptools_scm[toml]==7.1.0", - "cmake>=3.15.3.post1", - "toml" + "cmake>=3.15.3.post1" ] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 852da8432c29cf6b7370946289c429479d506228..f0c41626f2fa348ac5d52778d0d865a31b4c344c 100644 --- a/setup.py +++ b/setup.py @@ -6,17 +6,13 @@ import multiprocessing from math import ceil -import toml - from setuptools import setup, Extension from setuptools.command.build_ext import build_ext -def get_project_name() -> str: - with open(pathlib.Path().absolute() / "pyproject.toml", "r") as file: - project_toml = toml.load(file) - return project_toml["project"]["name"] +PROJECT_NAME = "aidge_core" +SETUP_DIR = pathlib.Path(__file__).parent class CMakeExtension(Extension): def __init__(self, name): @@ -24,6 +20,15 @@ class CMakeExtension(Extension): class CMakeBuild(build_ext): + def __init__(self, dist, *args, **kwargs): + super().__init__(dist, *args, **kwargs) + # Detect editable_mode for old versions of setuptools + if not hasattr(self, "editable_mode"): + if hasattr(dist, "commands"): + self.editable_mode = "develop" in dist.commands + else: + self.editable_mode = False + def run(self): # This lists the number of processors available on the machine # The compilation will use half of them @@ -39,34 +44,34 @@ class CMakeBuild(build_ext): if not build_lib.exists(): build_lib.mkdir(parents=True, exist_ok=True) + package_prefix = build_lib if not self.editable_mode else SETUP_DIR + pybind_install_prefix = (package_prefix / PROJECT_NAME).absolute() + os.chdir(str(build_temp)) - compile_type = ( - "Release" - if "AIDGE_PYTHON_BUILD_TYPE" not in os.environ - else os.environ["AIDGE_PYTHON_BUILD_TYPE"] - ) + compile_type = os.environ.get("AIDGE_PYTHON_BUILD_TYPE", "Release") install_path = ( os.path.join(sys.prefix, "lib", "libAidge") if "AIDGE_INSTALL" not in os.environ else os.environ["AIDGE_INSTALL"] ) - - build_gen = ( - ["-G", os.environ["AIDGE_BUILD_GEN"]] - if "AIDGE_BUILD_GEN" in os.environ + build_gen = os.environ.get("AIDGE_BUILD_GEN", "") + build_gen_opts = ( + ["-G", build_gen] + if build_gen else [] ) self.spawn( [ "cmake", - *build_gen, + *build_gen_opts, str(cwd), "-DTEST=OFF", f"-DCMAKE_INSTALL_PREFIX:PATH={install_path}", f"-DCMAKE_BUILD_TYPE={compile_type}", "-DPYBIND=ON", + f"-DPYBIND_INSTALL_PREFIX:PATH={pybind_install_prefix}", "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON", "-DCOVERAGE=OFF", ] @@ -79,28 +84,10 @@ class CMakeBuild(build_ext): self.spawn(["cmake", "--install", ".", "--config", compile_type]) os.chdir(str(cwd)) - aidge_package = build_lib / (get_project_name()) - - # Get "aidge core" package - # ext_lib = build_temp - print(build_temp.absolute()) - # Copy all shared object files from build_temp/lib to aidge_package - for root, _, files in os.walk(build_temp.absolute()): - for file in files: - if (file.endswith(".so") or file.endswith(".pyd")) and ( - root != str(aidge_package.absolute()) - ): - currentFile = os.path.join(root, file) - shutil.copy(currentFile, str(aidge_package.absolute())) - - # Copy version.txt in aidge_package - os.chdir(os.path.dirname(__file__)) - shutil.copy("version.txt", str(aidge_package.absolute())) - if __name__ == "__main__": setup( - ext_modules=[CMakeExtension(get_project_name())], + ext_modules=[CMakeExtension(PROJECT_NAME)], cmdclass={ "build_ext": CMakeBuild, },