Skip to content
Snippets Groups Projects
Commit 9298e5f6 authored by Christophe Guillon's avatar Christophe Guillon
Browse files

[Setup] Add support for development install

Add support for pip -e development (editable) mode.
Add some environment vars for configuring the cmake build in
development mode:
- AIDGE_BUILD_GEN=<cmake_backend>: for instance Ninja to use
  the ninja backend which better supports incremental build
- AIDGE_BUILD_MODE=[Debug|Release]: define the build mode,
  defaults to Debug

Note that due to behavior changes depending on the
python/setuptools versions, the portable method to ensure
incremental build on C++ files is to use:

export AIDGE_BUILD_GEN=Ninja
pip install --no-build-isolation -v -e .

Refer to the doc string of setup.py for details.
parent 84bbbf7e
No related branches found
No related tags found
No related merge requests found
This commit is part of merge request !154. Comments created here will be created in the context of that merge request.
...@@ -19,6 +19,25 @@ pip install . -v ...@@ -19,6 +19,25 @@ pip install . -v
export AIDGE_INSTALL='<path_to_aidge>/install' export AIDGE_INSTALL='<path_to_aidge>/install'
``` ```
## Development Mode installation
Install in development mode (local changes to python files and .cpp files do not require full installation)
with:
```bash
pip install cmake ninja # install first build dependencies
export AIDGE_BUILD_GEN=Ninja
pip install --no-build-isolation -v -e .
```
After changes in a python file, no action is required, for changes in .cpp/.h file, re-execute:
```bash
pip install --no-build-isolation -v -e .
```
In this mode, the C++ compilation build system is located in the local `build/` directory.
Refer to the doc string in `setup.py`for more details on editable mode.
## Standard C++ Compilation ## Standard C++ Compilation
Create two directories ``build`` and ``ìnstall``. Create two directories ``build`` and ``ìnstall``.
......
#!/usr/bin/env python3 #!/usr/bin/env python3
""" Aidge """ Aidge
This setup file supports both install and develop (editable) mode, i.e.:
- either: pip install -v .
- or editable: pip install [--no-build-isolation] -v -e .
(use --no-build-isolation preferably for modern installation, see [1] below)
In editable mode, there is no need to reinstall after changes in the pythons files.
Also, when changing C++ source files, incremental compilation works and build
directory is located in build/, though one must re-run after C++ changes, either:
- reinstall; pip install [--no-build-isolation] -v -e .
- or cmake: cmake --build build && cmake --install build
- or direct build command: ninja -C build install / make -C build install
Note that in editable mode, one must first install build dependencies as they
will not be automatically installed:
- pip install cmake ninja
[1] on modern setuptools/python versions, isolation mode breaks the incremental
build due to the reinstall of build tools in different locations,
use --no-build_isolation to avoid this.
#TODO To change #TODO To change
POC of the next framework named Aidge POC of the next framework named Aidge
""" """
...@@ -14,7 +34,6 @@ import os ...@@ -14,7 +34,6 @@ import os
if sys.version_info[:2] < (3, 7): if sys.version_info[:2] < (3, 7):
raise RuntimeError("Python version >= 3.7 required.") raise RuntimeError("Python version >= 3.7 required.")
CLASSIFIERS = """\ CLASSIFIERS = """\
Development Status :: 2 - Pre-Alpha Development Status :: 2 - Pre-Alpha
""" """
...@@ -30,12 +49,21 @@ from setuptools import setup, Extension ...@@ -30,12 +49,21 @@ from setuptools import setup, Extension
from setuptools import find_packages from setuptools import find_packages
from setuptools.command.build_ext import build_ext from setuptools.command.build_ext import build_ext
SETUP_DIR = pathlib.Path(__file__).parent
def parse_requirements():
with open(SETUP_DIR / "requirements.txt", "r") as inf:
return [
line
for line in [l.strip() for l in inf.readlines()]
if line
]
def get_project_name() -> str: def get_project_name() -> str:
return open(pathlib.Path().absolute() / "project_name.txt", "r").read() return open(SETUP_DIR / "project_name.txt", "r").read().strip()
def get_project_version() -> str: def get_project_version() -> str:
aidge_root = pathlib.Path().absolute() version = open(SETUP_DIR / "version.txt", "r").read().strip()
version = open(aidge_root / "version.txt", "r").read().strip()
return version return version
...@@ -45,9 +73,19 @@ class CMakeExtension(Extension): ...@@ -45,9 +73,19 @@ class CMakeExtension(Extension):
class CMakeBuild(build_ext): 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): def run(self):
# This lists the number of processors available on the machine # This lists the number of processors available on the machine
# The compilation will use half of them # The compilation will use half of them
# Note that for ninja backend this is not necessary
max_jobs = str(ceil(multiprocessing.cpu_count() / 2)) max_jobs = str(ceil(multiprocessing.cpu_count() / 2))
cwd = pathlib.Path().absolute() cwd = pathlib.Path().absolute()
...@@ -60,37 +98,39 @@ class CMakeBuild(build_ext): ...@@ -60,37 +98,39 @@ class CMakeBuild(build_ext):
if not build_lib.exists(): if not build_lib.exists():
build_lib.mkdir(parents=True, exist_ok=True) build_lib.mkdir(parents=True, exist_ok=True)
os.chdir(str(build_temp))
# Impose to use the executable of the python # Impose to use the executable of the python
# used to launch setup.py to setup PythonInterp # used to launch setup.py to setup PythonInterp
param_py = "-DPYTHON_EXECUTABLE=" + sys.executable param_py = "-DPYTHON_EXECUTABLE=" + sys.executable
compile_type = 'Debug' build_gen = os.environ.get("AIDGE_BUILD_GEN", "")
cmake_type_opts = ['-G', build_gen] if build_gen else []
cmake_job_opts = ['-j', max_jobs] if build_gen != "Ninja" else []
compile_type = os.environ.get("AIDGE_BUILD_MODE", "Debug")
install_path = os.path.join(sys.prefix, "lib", "libAidge") if "AIDGE_INSTALL" not in os.environ else os.environ["AIDGE_INSTALL"] install_path = os.path.join(sys.prefix, "lib", "libAidge") if "AIDGE_INSTALL" not in os.environ else os.environ["AIDGE_INSTALL"]
self.spawn(['cmake', str(cwd), param_py, '-DTEST=OFF', f'-DCMAKE_INSTALL_PREFIX:PATH={install_path}', f'-DCMAKE_BUILD_TYPE={compile_type}']) os.chdir(str(build_temp))
self.spawn(['cmake', *cmake_type_opts, str(cwd), param_py, '-DTEST=OFF', f'-DCMAKE_INSTALL_PREFIX:PATH={install_path}', f'-DCMAKE_BUILD_TYPE={compile_type}'])
if not self.dry_run: if not self.dry_run:
self.spawn(['cmake', '--build', '.', '--config', compile_type, '-j', max_jobs]) self.spawn(['cmake', '--build', '.', '--config', compile_type, *cmake_job_opts])
self.spawn(['cmake', '--install', '.', '--config', compile_type]) self.spawn(['cmake', '--install', '.', '--config', compile_type])
os.chdir(str(cwd)) os.chdir(str(cwd))
aidge_package = build_lib / (get_project_name()) package_path_prefix = build_lib if not self.editable_mode else SETUP_DIR
aidge_package = (package_path_prefix / get_project_name()).absolute()
# Get "aidge core" package # Get "aidge core" package
# ext_lib = build_temp # ext_lib = build_temp
print(build_temp.absolute())
# Copy all shared object files from build_temp/lib to aidge_package # Copy all shared object files from build_temp/lib to aidge_package
for root, _, files in os.walk(build_temp.absolute()): for root, _, files in os.walk(build_temp):
for file in files: for file in files:
if (file.endswith('.so') or file.endswith('.pyd')) and (root != str(aidge_package.absolute())): if ((file.endswith('.so') or file.endswith('.pyd')) and
currentFile=os.path.join(root, file) root != str(aidge_package) and
shutil.copy(currentFile, str(aidge_package.absolute())) not root.startswith(str(build_lib))):
currentFile = pathlib.Path(root) / file
shutil.copy(str(currentFile), str(aidge_package))
# Copy version.txt in aidge_package # Copy version.txt in aidge_package
os.chdir(os.path.dirname(__file__)) shutil.copy(str(SETUP_DIR / "version.txt"), str(aidge_package))
shutil.copy("version.txt", str(aidge_package.absolute()))
if __name__ == '__main__': if __name__ == '__main__':
...@@ -105,6 +145,8 @@ if __name__ == '__main__': ...@@ -105,6 +145,8 @@ if __name__ == '__main__':
packages=find_packages(where="."), packages=find_packages(where="."),
include_package_data=True, include_package_data=True,
ext_modules=[CMakeExtension(get_project_name())], ext_modules=[CMakeExtension(get_project_name())],
install_requires=parse_requirements(),
setup_requires=['cmake', 'ninja'],
cmdclass={ cmdclass={
'build_ext': CMakeBuild, 'build_ext': CMakeBuild,
}, },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment