Skip to content

Commit 0949314

Browse files
committed
Remove custom bdist_wheel and prepare once OpenMP sources
1 parent 88f7f76 commit 0949314

1 file changed

Lines changed: 98 additions & 96 deletions

File tree

setup.py

Lines changed: 98 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from setuptools import setup, Extension
99
from setuptools import Command
1010
from setuptools.command.build_ext import build_ext
11-
from setuptools.command.bdist_wheel import bdist_wheel
1211

1312

1413
class CleanCommand(Command):
@@ -29,34 +28,21 @@ def run(self):
2928
shutil.rmtree(egg_info, ignore_errors=True)
3029

3130

32-
class CustomBdistWheel(bdist_wheel):
33-
def run(self):
34-
# Ensure all build steps are run before bdist_wheel
35-
self.run_command("build_ext")
36-
super().run()
37-
38-
3931
class CMakeExtension(Extension):
4032
def __init__(
4133
self,
4234
name,
4335
*,
36+
setup=None,
4437
source_dir=None,
4538
install_dir=None,
46-
url=None,
47-
sha256=None,
4839
cmake_args=[],
4940
):
5041
# Don't invoke the original build_ext for this special extension.
5142
super().__init__(name, sources=[])
52-
if source_dir and url:
53-
raise ValueError(
54-
"CMakeExtension should have either a source_dir or a url, not both."
55-
)
43+
self.setup = setup
5644
self.source_dir = source_dir
5745
self.install_dir = install_dir
58-
self.url = url
59-
self.sha256 = sha256
6046
self.cmake_args = cmake_args
6147

6248

@@ -81,9 +67,17 @@ def _build_cmake(self, ext: CMakeExtension):
8167
shutil.rmtree(build_dir, ignore_errors=True)
8268
build_dir.mkdir(parents=True, exist_ok=True)
8369

84-
lib_dir = Path(
85-
self.get_finalized_command("build_py").get_package_dir("numba.openmp.libs")
86-
)
70+
if ext.setup:
71+
ext.setup.setup()
72+
73+
if self.inplace:
74+
lib_dir = Path(
75+
self.get_finalized_command("build_py").get_package_dir(
76+
"numba.openmp.libs"
77+
)
78+
)
79+
else:
80+
lib_dir = Path(self.build_lib) / "numba/openmp/libs"
8781

8882
extra_cmake_args = self._env_toolchain_args(ext)
8983
# Set RPATH.
@@ -149,79 +143,90 @@ def _env_toolchain_args(self, ext):
149143
return args
150144

151145

152-
def _prepare_source_openmp(sha256=None):
146+
class PrepareOpenMP:
147+
setup_done = False
153148
LLVM_VERSION = os.environ.get("LLVM_VERSION", None)
154-
assert LLVM_VERSION is not None, "LLVM_VERSION environment variable must be set."
155-
url = f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{LLVM_VERSION}/llvm-project-{LLVM_VERSION}.src.tar.xz"
156-
157-
tmp = Path("_downloads/libomp") / f"llvm-project-{LLVM_VERSION}.tar.gz"
158-
tmp.parent.mkdir(parents=True, exist_ok=True)
159-
160-
# Download the source tarball if it does not exist.
161-
if not tmp.exists():
162-
print(f"Downloading llvm-project version {LLVM_VERSION} url:", url)
163-
with urllib.request.urlopen(url) as r:
164-
with tmp.open("wb") as f:
165-
f.write(r.read())
166-
else:
167-
print(f"Using downloaded llvm-project at {tmp}")
168-
169-
if sha256:
170-
import hashlib
171-
172-
hasher = hashlib.sha256()
173-
with tmp.open("rb") as f:
174-
hasher.update(f.read())
175-
if hasher.hexdigest() != sha256:
176-
raise ValueError(f"SHA256 mismatch for {url}")
177-
178-
print("Extracting llvm-project...")
179-
with tarfile.open(tmp) as tf:
180-
# The root dir llvm-project-20.1.8.src
181-
root_name = tf.getnames()[0]
182-
183-
# Extract only needed subdirectories
184-
members = [
185-
m
186-
for m in tf.getmembers()
187-
if m.name.startswith(f"{root_name}/openmp/")
188-
or m.name.startswith(f"{root_name}/offload/")
189-
or m.name.startswith(f"{root_name}/runtimes/")
190-
or m.name.startswith(f"{root_name}/cmake/")
191-
or m.name.startswith(f"{root_name}/llvm/cmake/")
192-
or m.name.startswith(f"{root_name}/llvm/utils/")
193-
or m.name.startswith(f"{root_name}/libc/")
194-
]
195-
196-
parentdir = tmp.parent
197-
# Base arguments for extractall.
198-
kwargs = {"path": parentdir, "members": members}
199-
200-
# Check if data filter is available.
201-
if hasattr(tarfile, "data_filter"):
202-
# If this exists, the 'filter' argument is guaranteed to work
203-
kwargs["filter"] = "data"
204-
205-
tf.extractall(**kwargs)
206-
207-
source_dir = parentdir / root_name
208-
print("Extracted llvm-project to:", source_dir)
209-
210-
print("Applying patches to llvm-project...")
211-
for patch in sorted(
212-
Path(f"src/numba/openmp/libs/openmp/patches/{LLVM_VERSION}")
213-
.absolute()
214-
.glob("*.patch")
215-
):
216-
print("applying patch", patch)
217-
subprocess.run(
218-
["patch", "-p1", "-i", str(patch)],
219-
cwd=source_dir,
220-
check=True,
221-
stdin=subprocess.DEVNULL,
149+
150+
@classmethod
151+
def setup(cls):
152+
if not cls.setup_done:
153+
cls._prepare_source_openmp()
154+
cls.setup_done = True
155+
156+
@classmethod
157+
def get_source_dir(cls):
158+
return Path(
159+
f"_downloads/libomp/llvm-project-{cls.LLVM_VERSION}.src/runtimes"
160+
).absolute()
161+
162+
@classmethod
163+
def _prepare_source_openmp(cls):
164+
assert cls.LLVM_VERSION is not None, (
165+
"LLVM_VERSION environment variable must be set."
222166
)
167+
url = f"https://github.com/llvm/llvm-project/releases/download/llvmorg-{cls.LLVM_VERSION}/llvm-project-{cls.LLVM_VERSION}.src.tar.xz"
223168

224-
return f"{source_dir}/runtimes"
169+
tmp = Path("_downloads/libomp") / f"llvm-project-{cls.LLVM_VERSION}.tar.gz"
170+
tmp.parent.mkdir(parents=True, exist_ok=True)
171+
172+
# Download the source tarball if it does not exist.
173+
if not tmp.exists():
174+
print(
175+
f"Downloading llvm-project version {cls.LLVM_VERSION} url:",
176+
url,
177+
flush=True,
178+
)
179+
with urllib.request.urlopen(url) as r:
180+
with tmp.open("wb") as f:
181+
f.write(r.read())
182+
else:
183+
print(f"Using downloaded llvm-project at {tmp}", flush=True)
184+
185+
print("Extracting llvm-project...", flush=True)
186+
with tarfile.open(tmp) as tf:
187+
# The root dir llvm-project-20.1.8.src
188+
root_name = tf.getnames()[0]
189+
190+
# Extract only needed subdirectories
191+
members = [
192+
m
193+
for m in tf.getmembers()
194+
if m.name.startswith(f"{root_name}/openmp/")
195+
or m.name.startswith(f"{root_name}/offload/")
196+
or m.name.startswith(f"{root_name}/runtimes/")
197+
or m.name.startswith(f"{root_name}/cmake/")
198+
or m.name.startswith(f"{root_name}/llvm/cmake/")
199+
or m.name.startswith(f"{root_name}/llvm/utils/")
200+
or m.name.startswith(f"{root_name}/libc/")
201+
]
202+
203+
parentdir = tmp.parent
204+
# Base arguments for extractall.
205+
kwargs = {"path": parentdir, "members": members}
206+
207+
# Check if data filter is available.
208+
if hasattr(tarfile, "data_filter"):
209+
# If this exists, the 'filter' argument is guaranteed to work
210+
kwargs["filter"] = "data"
211+
212+
tf.extractall(**kwargs)
213+
214+
source_dir = parentdir / root_name
215+
print("Extracted llvm-project to:", source_dir, flush=True)
216+
217+
print("Applying patches to llvm-project...", flush=True)
218+
for patch in sorted(
219+
Path(f"src/numba/openmp/libs/openmp/patches/{cls.LLVM_VERSION}")
220+
.absolute()
221+
.glob("*.patch")
222+
):
223+
print("applying patch", patch, flush=True)
224+
subprocess.run(
225+
["patch", "-p1", "-i", str(patch)],
226+
cwd=source_dir,
227+
check=True,
228+
stdin=subprocess.DEVNULL,
229+
)
225230

226231

227232
def _check_true(env_var):
@@ -234,18 +239,15 @@ def _check_true(env_var):
234239
ext_modules = [CMakeExtension("pass", source_dir="src/numba/openmp/libs/pass")]
235240

236241

237-
# Prepare source directory if either bundled libomp or libomptarget is enabled.
238-
if _check_true("ENABLE_BUNDLED_LIBOMP") or _check_true("ENABLE_BUNDLED_LIBOMPTARGET"):
239-
openmp_source_dir = _prepare_source_openmp()
240-
241242
# Optionally enable bundled libomp build via ENABLE_BUNDLED_LIBOMP=1. We want
242243
# to avoid bundling for conda builds to avoid duplicate OpenMP runtime conflicts
243244
# (e.g., numba 0.62+ and libopenblas already require llvm-openmp).
244245
if _check_true("ENABLE_BUNDLED_LIBOMP"):
245246
ext_modules.append(
246247
CMakeExtension(
247248
"libomp",
248-
source_dir=openmp_source_dir,
249+
setup=PrepareOpenMP,
250+
source_dir=PrepareOpenMP.get_source_dir(),
249251
install_dir="openmp",
250252
cmake_args=[
251253
"-DOPENMP_STANDALONE_BUILD=ON",
@@ -265,7 +267,8 @@ def _check_true(env_var):
265267
ext_modules.append(
266268
CMakeExtension(
267269
"libomptarget",
268-
source_dir=openmp_source_dir,
270+
setup=PrepareOpenMP,
271+
source_dir=PrepareOpenMP.get_source_dir(),
269272
install_dir="openmp",
270273
cmake_args=[
271274
"-DOPENMP_STANDALONE_BUILD=ON",
@@ -283,6 +286,5 @@ def _check_true(env_var):
283286
cmdclass={
284287
"clean": CleanCommand,
285288
"build_ext": BuildCMakeExt,
286-
**({"bdist_wheel": CustomBdistWheel} if CustomBdistWheel else {}),
287289
},
288290
)

0 commit comments

Comments
 (0)