Skip to content

Commit 3958a72

Browse files
authored
pypi: update setup.py and pyproject.toml for cibuildwheel (#4639)
Corresponds to llvm/torch-mlir-release#32 Cf. #4603 This can be tested locally with: ``` python3 -m venv venv source venv/bin/activate pip install cibuildwheel uv CIBW_BUILD="cp312-manylinux_x86_64" cibuildwheel --platform linux ``` I also went ahead and added abi3 support for better cross-platform compatibility (in particular, the gLinux box I was testing it on could not install the wheel without abi3, though I didn't dig into why). However, that decision adds a constraint: LLVM pins the stable ABI to Python3.12, so that would limit this wheel to python3.12 or later. Is that OK? Python 3.11 is EOL on October 31, 2027, so there is an argument to be made that it would be useful to keep support for it around until then. But if LLVM as a whole agreed on python3.12 (I wouldn't know why) then maybe it's better to be consistent with that. --------- Co-authored-by: Jeremy Kun <j2kun@users.noreply.github.com>
1 parent a220e1c commit 3958a72

4 files changed

Lines changed: 116 additions & 3 deletions

File tree

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ if(POLICY CMP0116)
2121
cmake_policy(SET CMP0116 OLD)
2222
endif()
2323

24+
if(POLICY CMP0079)
25+
cmake_policy(SET CMP0079 NEW)
26+
endif()
27+
2428
project(torch-mlir LANGUAGES CXX C)
2529
set(CMAKE_C_STANDARD 11)
2630
set(CMAKE_CXX_STANDARD 17)

build_tools/smoke_test.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2+
# See https://llvm.org/LICENSE.txt for license information.
3+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
# Also available under a BSD-style license. See LICENSE.
5+
6+
import torch
7+
import torch_mlir
8+
import torch_mlir.fx
9+
10+
11+
class MLPModule(torch.nn.Module):
12+
def __init__(self):
13+
super().__init__()
14+
self.linear = torch.nn.Linear(4, 4)
15+
self.relu = torch.nn.ReLU()
16+
17+
def forward(self, x):
18+
return self.relu(self.linear(x))
19+
20+
21+
module_to_compile = MLPModule()
22+
backends = ["linalg-on-tensors", "tosa", "stablehlo"]
23+
24+
for backend in backends:
25+
print(f"Compiling MLPModule via FX to {backend}...")
26+
try:
27+
torch_mlir.fx.export_and_import(
28+
module_to_compile, torch.ones(2, 4), output_type=backend
29+
)
30+
print(f"Compilation to {backend} successful!")
31+
except Exception as e:
32+
print(f"Compilation to {backend} FAILED!")
33+
raise e

pyproject.toml

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,60 @@
11
[build-system]
2-
requires = ["setuptools", "wheel"]
2+
# These must be enough to drive setup.py's CMake build of the MLIR Python
3+
# bindings under build isolation (cibuildwheel). Kept in sync with the
4+
# [dependency-groups].build table below and
5+
# externals/llvm-project/mlir/python/requirements.txt (re-sync on LLVM bumps).
6+
requires = [
7+
"setuptools",
8+
"wheel",
9+
"cmake",
10+
"ninja<1.13.0; sys_platform == 'win32'",
11+
"ninja; sys_platform != 'win32'",
12+
"packaging",
13+
"nanobind>=2.9, <3.0",
14+
"PyYAML>=5.4.0, <=6.0.1",
15+
"typing_extensions>=4.12.2",
16+
"numpy>=2.1.0, <=2.1.2",
17+
"ml_dtypes>=0.5.0, <=0.6.0",
18+
]
319
build-backend = "setuptools.build_meta"
420

521
[tool.black]
622
line-length = 88
723
target-version = ['py38']
24+
25+
# NOTE: torch-mlir's Python package is produced by the CMake build (see the
26+
# custom `build_py` in setup.py), not by setuptools' source-tree discovery.
27+
# Auto-discovery is disabled via `packages=[]` passed directly in setup.py.
28+
# We intentionally do NOT define a `[tool.setuptools]` (or `[project]`) table
29+
# here: doing so makes modern setuptools treat pyproject.toml as the metadata
30+
# source and reject the setup.py-driven build with
31+
# "`project` must contain ['version'] properties".
32+
33+
[tool.cibuildwheel]
34+
build = "cp310-* cp311-* cp312-*"
35+
skip = "*-musllinux* *_i686 *-win32"
36+
build-frontend = "build[uv]"
37+
test-requires = ["-r pytorch-requirements.txt"]
38+
test-command = "python {project}/build_tools/smoke_test.py"
39+
40+
# Build with the manylinux default gcc toolchain. ccache reuses the LLVM/MLIR
41+
# object cache across the per-Python-version builds; CMake reads the
42+
# *_COMPILER_LAUNCHER cache vars from these env vars. CCACHE_DIR lives inside
43+
# the mounted /project so the host's actions/cache can persist it.
44+
[tool.cibuildwheel.linux]
45+
archs = "auto64"
46+
manylinux-x86_64-image = "manylinux_2_28"
47+
manylinux-aarch64-image = "manylinux_2_28"
48+
before-all = "dnf install -y --quiet epel-release || true && dnf install -y --quiet ccache || dnf install -y ccache"
49+
environment-pass = ["TORCH_MLIR_PYTHON_PACKAGE_VERSION"]
50+
environment = { TORCH_MLIR_ENABLE_ONLY_MLIR_PYTHON_BINDINGS = "1", TORCH_MLIR_ENABLE_JIT_IR_IMPORTER = "0", TORCH_MLIR_ENABLE_LTC = "0", CMAKE_GENERATOR = "Ninja", CMAKE_C_COMPILER_LAUNCHER = "ccache", CMAKE_CXX_COMPILER_LAUNCHER = "ccache", CCACHE_DIR = "/project/.ccache", CCACHE_MAXSIZE = "2G" }
51+
52+
[tool.cibuildwheel.macos]
53+
archs = "auto64"
54+
before-all = "brew install ccache ninja"
55+
environment = { MACOSX_DEPLOYMENT_TARGET = "11.0", TORCH_MLIR_ENABLE_ONLY_MLIR_PYTHON_BINDINGS = "1", TORCH_MLIR_ENABLE_JIT_IR_IMPORTER = "0", TORCH_MLIR_ENABLE_LTC = "0", CMAKE_GENERATOR = "Ninja", CMAKE_C_COMPILER_LAUNCHER = "ccache", CMAKE_CXX_COMPILER_LAUNCHER = "ccache" }
56+
57+
[tool.cibuildwheel.windows]
58+
before-build = "pip install delvewheel"
59+
repair-wheel-command = "delvewheel repair --add-path {project}/build/cmake_build/tools/torch-mlir/python_packages/torch_mlir/torch_mlir/_mlir_libs --add-dll TorchMLIRAggregateCAPI.dll --no-dll \"c10.dll;torch_python.dll;torch_cpu.dll\" -w {dest_dir} {wheel}"
60+
environment = { TORCH_MLIR_ENABLE_ONLY_MLIR_PYTHON_BINDINGS = "1", TORCH_MLIR_ENABLE_JIT_IR_IMPORTER = "0", TORCH_MLIR_ENABLE_LTC = "0", CMAKE_GENERATOR = "Ninja", TORCH_MLIR_CMAKE_BUILD_DIR = "build/cmake_build" }

setup.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@
3939
# CMAKE_GENERATOR=Ninja CMAKE_C_COMPILER_LAUNCHER=ccache CMAKE_CXX_COMPILER_LAUNCHER=ccache
4040
# ```
4141
#
42+
# For release builds, use cibuildwheel. Except for
43+
# TORCH_MLIR_PYTHON_PACKAGE_VERSION, the environment variables needed for
44+
# cibuildwheel are set in pyproject.toml. For an example local run:
45+
#
46+
# ```
47+
# CIBW_BUILD="cp312-manylinux_x86_64" cibuildwheel --platform linux
48+
# ```
49+
#
4250
# Implementation notes:
4351
# The contents of the wheel is just the contents of the `python_packages`
4452
# directory that our CMake build produces. We go through quite a bit of effort
@@ -104,6 +112,9 @@ def run(self):
104112
self.run_command("build_scripts")
105113

106114

115+
use_stable_abi = sys.version_info >= (3, 12)
116+
117+
107118
class CMakeBuild(build_py):
108119
def cmake_build(self, cmake_build_dir):
109120
llvm_dir = str(SRC_DIR / "externals" / "llvm-project" / "llvm")
@@ -125,6 +136,7 @@ def cmake_build(self, cmake_build_dir):
125136
f"-DCMAKE_CXX_VISIBILITY_PRESET=hidden",
126137
f"-DTORCH_MLIR_ENABLE_LTC={'ON' if TORCH_MLIR_ENABLE_LTC else 'OFF'}",
127138
f"-DTORCH_MLIR_ENABLE_PYTORCH_EXTENSIONS={'OFF' if TORCH_MLIR_ENABLE_ONLY_MLIR_PYTHON_BINDINGS else 'ON'}",
139+
f"-DMLIR_ENABLE_PYTHON_STABLE_ABI={'ON' if use_stable_abi else 'OFF'}",
128140
]
129141
if LLVM_INSTALL_DIR:
130142
cmake_config_args += [
@@ -214,7 +226,7 @@ def run(self):
214226

215227
class CMakeExtension(Extension):
216228
def __init__(self, name, sourcedir=""):
217-
Extension.__init__(self, name, sources=[])
229+
Extension.__init__(self, name, sources=[], py_limited_api=use_stable_abi)
218230
self.sourcedir = os.path.abspath(sourcedir)
219231

220232

@@ -255,6 +267,10 @@ def build_extension(self, ext):
255267
)
256268

257269

270+
setup_options = {}
271+
if use_stable_abi:
272+
setup_options["bdist_wheel"] = {"py_limited_api": "cp312"}
273+
258274
setup(
259275
name=NAME,
260276
version=f"{PACKAGE_VERSION}",
@@ -266,10 +282,16 @@ def build_extension(self, ext):
266282
include_package_data=True,
267283
cmdclass={
268284
"build": CustomBuild,
269-
"built_ext": NoopBuildExtension,
285+
"build_ext": NoopBuildExtension,
270286
"build_py": CMakeBuild,
271287
},
272288
ext_modules=EXT_MODULES,
289+
# The Python package contents are placed into the build dir by the custom
290+
# CMake `build_py` above; there are no source-tree packages to discover.
291+
# An explicit empty list disables setuptools auto-discovery (which would
292+
# otherwise trip over top-level non-Python dirs like lib/, include/,
293+
# projects/, ...).
294+
packages=[],
273295
python_requires=">=3.8",
274296
install_requires=INSTALL_REQUIRES,
275297
extras_require={
@@ -284,4 +306,5 @@ def build_extension(self, ext):
284306
],
285307
},
286308
zip_safe=False,
309+
options=setup_options,
287310
)

0 commit comments

Comments
 (0)