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
184 lines (149 loc) · 8.18 KB
/
setup.py
File metadata and controls
184 lines (149 loc) · 8.18 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
"""
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"
def _patch_root_init():
warning_patch = '''
import warnings
import textwrap
warnings.warn(
textwrap.dedent("""
This distribution of ROOT is in alpha stage. Feedback is welcome and appreciated. Feel free to reach out to the user forum for questions and general feedback at https://root-forum.cern.ch or to submit an issue at https://github.com/root-project/root/issues. Do not rely on this distribution for production purposes.
""")
)
'''
root_init_path = pathlib.Path(SOURCE_DIR) / "bindings/pyroot/pythonizations/python/ROOT/__init__.py"
with open(root_init_path) as init_file:
full_init_text = init_file.read()
new_init_text = full_init_text + warning_patch
with open(root_init_path, "w") as init_file:
init_file.write(new_init_text)
class ROOTBuild(_build):
def finalize_options(self):
# Normalize the distribution name before building
if self.distribution.metadata.name == "ROOT":
# Store original name for metadata
self.distribution.metadata._original_name = "ROOT"
# Use normalized name to comply with PEP625 and avoid errors
# caused by https://github.com/pypi/warehouse/pull/18924
self.distribution.metadata.name = "root"
super().finalize_options()
def run(self):
_build.run(self)
# The PyPI distribution is in alpha stage, signal this to the user
# with a warning at import ROOT time. We inject the warning in the
# __init__.py file of the package before the start of the build
_patch_root_init()
# 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 -Dmathmore=ON -Dbuiltin_fftw3=ON -Dbuiltin_gsl=ON "
f"-DCMAKE_INSTALL_PREFIX={INSTALL_DIR} -B {BUILD_DIR} -S {SOURCE_DIR} "
# Next 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_CMAKEDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/cmake "
f"-DCMAKE_INSTALL_FONTDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/fonts "
f"-DCMAKE_INSTALL_ICONDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/icons "
f"-DCMAKE_INSTALL_INCLUDEDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/include "
f"-DCMAKE_INSTALL_LIBDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/lib "
f"-DCMAKE_INSTALL_MACRODIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/macros "
f"-DCMAKE_INSTALL_MANDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/man "
f"-DCMAKE_INSTALL_PYTHONDIR={ROOT_BUILD_INTERNAL_DIRNAME} "
f"-DCMAKE_INSTALL_SYSCONFDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/etc "
f"-DCMAKE_INSTALL_TUTDIR={ROOT_BUILD_INTERNAL_DIRNAME}/ROOT/tutorials "
)
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 finalize_options(self):
# Normalize the distribution name before installing
if self.distribution.metadata.name == "ROOT":
# Store original name for metadata
self.distribution.metadata._original_name = "ROOT"
# Use normalized name to comply with PEP625 and avoid errors
# caused by https://github.com/pypi/warehouse/pull/18924
self.distribution.metadata.name = "root"
super().finalize_options()
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 files that still need to be copied
self.copy_tree(os.path.join(INSTALL_DIR, "README"), os.path.join(root_package_dir, "README"))
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},
)