From 9b54fa60d999eaf0e62a731f2d0c75078439b920 Mon Sep 17 00:00:00 2001
From: Christophe Guillon <christophe.guillon@inria.fr>
Date: Thu, 18 Jul 2024 15:24:57 +0200
Subject: [PATCH] [Setup] Add support for editable mode

Add support for python editable (alias development) mode through
pip install --no-build-isolation -e .

Detects editable mode and in this case installs the python binding
library directly in the source python package directory.

Update README for usage of development mode.
---
 README.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 setup.py  | 13 ++++++++++++-
 2 files changed, 62 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index ef2191699..4b7954d41 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/setup.py b/setup.py
index 3c8411352..f0c41626f 100644
--- a/setup.py
+++ b/setup.py
@@ -12,12 +12,23 @@ from setuptools.command.build_ext import build_ext
 
 PROJECT_NAME = "aidge_core"
 
+SETUP_DIR = pathlib.Path(__file__).parent
+
 class CMakeExtension(Extension):
     def __init__(self, name):
         super().__init__(name, sources=[])
 
 
 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
@@ -33,7 +44,7 @@ class CMakeBuild(build_ext):
         if not build_lib.exists():
             build_lib.mkdir(parents=True, exist_ok=True)
 
-        package_prefix = build_lib
+        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))
-- 
GitLab