forked from root-project/root
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.py
More file actions
138 lines (111 loc) · 6.32 KB
/
setup.py
File metadata and controls
138 lines (111 loc) · 6.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"""
setuptools-based build of ROOT.
This script uses setuptools API to steer a custom CMake build of ROOT. All build
configuration options are specified in the class responsible for building. A
custom extension module is injected in the setuptools setup to properly generate
the wheel with CPython extension metadata. Note that ROOT is first installed via
CMake into a temporary directory, then the ROOT installation artifacts are moved
to the final Python environment installation path, which often starts at
${ENV_PREFIX}/lib/pythonXX.YY/site-packages, before being packaged as a wheel.
"""
import os
import pathlib
import shlex
import subprocess
import tempfile
from setuptools import Extension, find_packages, setup
from setuptools.command.build import build as _build
from setuptools.command.install import install as _install
# Get the long description from the README file
SOURCE_DIR = pathlib.Path(__file__).parent.resolve()
LONG_DESCRIPTION = (SOURCE_DIR / "README.md").read_text(encoding="utf-8")
BUILD_DIR = tempfile.mkdtemp()
INSTALL_DIR = tempfile.mkdtemp()
# Name given to an internal directory within the build directory
# used to mimick the structure of the target installation directory
# in the user Python environment, usually named "site-packages"
ROOT_BUILD_INTERNAL_DIRNAME = "mock_site_packages"
class ROOTBuild(_build):
def run(self):
_build.run(self)
# Configure ROOT build
configure_command = shlex.split(
"cmake "
# gminimal=ON enables only a minimal set of components (cling+core+I/O+graphics)
"-Dgminimal=ON -Dasimage=ON -Dopengl=OFF "
"-Druntime_cxxmodules=ON -Dfail-on-missing=ON " # Generic build configuration
# Explicitly turned off components, even though they are already off because of gminimal, we want to keep
# them listed here for documentation purposes:
# - tmva-pymva, tpython: these components link against libPython, forbidden for manylinux compatibility,
# see https://peps.python.org/pep-0513/#libpythonx-y-so-1
# - thisroot_scripts: the thisroot.* scripts are broken if CMAKE_INSTALL_PYTHONDIR!=CMAKE_INSTALL_LIBDIR
"-Dtmva-pymva=OFF -Dtpython=OFF -Dthisroot_scripts=OFF "
"-Dbuiltin_nlohmannjson=ON -Dbuiltin_tbb=ON -Dbuiltin_xrootd=ON " # builtins
"-Dbuiltin_lz4=ON -Dbuiltin_lzma=ON -Dbuiltin_zstd=ON -Dbuiltin_xxhash=ON " # builtins
"-Dpyroot=ON -Ddataframe=ON -Dxrootd=ON -Dssl=ON -Dimt=ON "
"-Droofit=ON "
# Next 4 paths represent the structure of the target binaries/headers/libs
# as the target installation directory of the Python environment would expect
f"-DCMAKE_INSTALL_BINDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/bin "
f"-DCMAKE_INSTALL_INCLUDEDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/include "
f"-DCMAKE_INSTALL_LIBDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/lib "
f"-DCMAKE_INSTALL_PYTHONDIR={ROOT_BUILD_INTERNAL_DIRNAME} "
f"-DCMAKE_INSTALL_PREFIX={INSTALL_DIR} -B {BUILD_DIR} -S {SOURCE_DIR}"
)
subprocess.run(configure_command, check=True)
# Run build with CMake
build_command = f"cmake --build {BUILD_DIR} -j{os.cpu_count()}"
subprocess.run(shlex.split(build_command), check=True)
class ROOTInstall(_install):
def _get_install_path(self):
if hasattr(self, "bdist_dir") and self.bdist_dir:
install_path = self.bdist_dir
else:
install_path = self.install_lib
return install_path
def run(self):
_install.run(self)
install_cmd = f"cmake --build {BUILD_DIR} --target install"
subprocess.run(shlex.split(install_cmd), check=True)
install_path = self._get_install_path()
# Copy ROOT installation tree to the ROOT package directory in the pip installation path
self.copy_tree(os.path.join(INSTALL_DIR, ROOT_BUILD_INTERNAL_DIRNAME), install_path)
root_package_dir = os.path.join(install_path, "ROOT")
# After the copy of the "mock" package structure from the ROOT installations, these are the
# leftover directories that still need to be copied
self.copy_tree(os.path.join(INSTALL_DIR, "cmake"), os.path.join(root_package_dir, "cmake"))
self.copy_tree(os.path.join(INSTALL_DIR, "etc"), os.path.join(root_package_dir, "etc"))
self.copy_tree(os.path.join(INSTALL_DIR, "fonts"), os.path.join(root_package_dir, "fonts"))
self.copy_tree(os.path.join(INSTALL_DIR, "icons"), os.path.join(root_package_dir, "icons"))
self.copy_tree(os.path.join(INSTALL_DIR, "macros"), os.path.join(root_package_dir, "macros"))
self.copy_tree(os.path.join(INSTALL_DIR, "man"), os.path.join(root_package_dir, "man"))
self.copy_tree(os.path.join(INSTALL_DIR, "README"), os.path.join(root_package_dir, "README"))
self.copy_tree(os.path.join(INSTALL_DIR, "tutorials"), os.path.join(root_package_dir, "tutorials"))
self.copy_file(os.path.join(INSTALL_DIR, "LICENSE"), os.path.join(root_package_dir, "LICENSE"))
def get_outputs(self):
outputs = _install.get_outputs(self)
return outputs
class DummyExtension(Extension):
"""
Dummy CPython extension for setuptools setup.
In order to generate the wheel with CPython extension metadata (i.e.
producing one wheel per supported Python version), setuptools requires that
at least one CPython extension is declared in the `ext_modules` kwarg passed
to the `setup` function. Usually, declaring a CPython extension triggers
compilation of the corresponding sources, but in this case we already do
that in the CMake build step. This class defines a dummy extension that
can be declared to setuptools while avoiding any further compilation step.
"""
def __init__(_):
super().__init__(name="Dummy", sources=[])
pkgs = find_packages("bindings/pyroot/pythonizations/python") + find_packages(
"bindings/pyroot/cppyy/cppyy/python", include=["cppyy"]
)
s = setup(
long_description=LONG_DESCRIPTION,
package_dir={"": "bindings/pyroot/pythonizations/python", "cppyy": "bindings/pyroot/cppyy/cppyy/python"},
packages=pkgs,
# Crucial to signal this is not a pure Python package
ext_modules=[DummyExtension()],
cmdclass={"build": ROOTBuild, "install": ROOTInstall},
)