Skip to content
This repository was archived by the owner on Apr 6, 2026. It is now read-only.

Commit 64c1959

Browse files
committed
WIP
1 parent 2897889 commit 64c1959

19 files changed

Lines changed: 558 additions & 18 deletions

File tree

build2cmake/src/config/v2.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ impl Build {
3232
self.kernels
3333
.values()
3434
.map(|kernel| match kernel {
35+
Kernel::Cpu { .. } => Backend::Cpu,
3536
Kernel::Cuda { .. } => Backend::Cuda,
3637
Kernel::Metal { .. } => Backend::Metal,
3738
Kernel::Rocm { .. } => Backend::Rocm,
@@ -96,6 +97,13 @@ impl Torch {
9697
#[derive(Debug, Deserialize, Serialize)]
9798
#[serde(deny_unknown_fields, rename_all = "kebab-case", tag = "backend")]
9899
pub enum Kernel {
100+
#[serde(rename_all = "kebab-case")]
101+
Cpu {
102+
cxx_flags: Option<Vec<String>>,
103+
depends: Vec<Dependencies>,
104+
include: Option<Vec<String>>,
105+
src: Vec<String>,
106+
},
99107
#[serde(rename_all = "kebab-case")]
100108
Cuda {
101109
cuda_capabilities: Option<Vec<String>>,
@@ -135,7 +143,8 @@ pub enum Kernel {
135143
impl Kernel {
136144
pub fn cxx_flags(&self) -> Option<&[String]> {
137145
match self {
138-
Kernel::Cuda { cxx_flags, .. }
146+
Kernel::Cpu { cxx_flags, .. }
147+
| Kernel::Cuda { cxx_flags, .. }
139148
| Kernel::Metal { cxx_flags, .. }
140149
| Kernel::Rocm { cxx_flags, .. }
141150
| Kernel::Xpu { cxx_flags, .. } => cxx_flags.as_deref(),
@@ -144,7 +153,8 @@ impl Kernel {
144153

145154
pub fn include(&self) -> Option<&[String]> {
146155
match self {
147-
Kernel::Cuda { include, .. }
156+
Kernel::Cpu { include, .. }
157+
| Kernel::Cuda { include, .. }
148158
| Kernel::Metal { include, .. }
149159
| Kernel::Rocm { include, .. }
150160
| Kernel::Xpu { include, .. } => include.as_deref(),
@@ -153,6 +163,7 @@ impl Kernel {
153163

154164
pub fn backend(&self) -> Backend {
155165
match self {
166+
Kernel::Cpu { .. } => Backend::Cpu,
156167
Kernel::Cuda { .. } => Backend::Cuda,
157168
Kernel::Metal { .. } => Backend::Metal,
158169
Kernel::Rocm { .. } => Backend::Rocm,
@@ -162,7 +173,8 @@ impl Kernel {
162173

163174
pub fn depends(&self) -> &[Dependencies] {
164175
match self {
165-
Kernel::Cuda { depends, .. }
176+
Kernel::Cpu { depends, .. }
177+
| Kernel::Cuda { depends, .. }
166178
| Kernel::Metal { depends, .. }
167179
| Kernel::Rocm { depends, .. }
168180
| Kernel::Xpu { depends, .. } => depends,
@@ -171,7 +183,8 @@ impl Kernel {
171183

172184
pub fn src(&self) -> &[String] {
173185
match self {
174-
Kernel::Cuda { src, .. }
186+
Kernel::Cpu { src, .. }
187+
| Kernel::Cuda { src, .. }
175188
| Kernel::Metal { src, .. }
176189
| Kernel::Rocm { src, .. }
177190
| Kernel::Xpu { src, .. } => src,
@@ -182,6 +195,7 @@ impl Kernel {
182195
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
183196
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
184197
pub enum Backend {
198+
Cpu,
185199
Cuda,
186200
Metal,
187201
Rocm,
@@ -191,6 +205,7 @@ pub enum Backend {
191205
impl Display for Backend {
192206
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
193207
match self {
208+
Backend::Cpu => write!(f, "cpu"),
194209
Backend::Cuda => write!(f, "cuda"),
195210
Backend::Metal => write!(f, "metal"),
196211
Backend::Rocm => write!(f, "rocm"),
@@ -204,6 +219,7 @@ impl FromStr for Backend {
204219

205220
fn from_str(s: &str) -> Result<Self, Self::Err> {
206221
match s.to_lowercase().as_str() {
222+
"cpu" => Ok(Backend::Cpu),
207223
"cuda" => Ok(Backend::Cuda),
208224
"metal" => Ok(Backend::Metal),
209225
"rocm" => Ok(Backend::Rocm),

build2cmake/src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use minijinja::Environment;
1010

1111
mod torch;
1212
use torch::{
13-
write_torch_ext_cuda, write_torch_ext_metal, write_torch_ext_universal, write_torch_ext_xpu,
13+
write_torch_ext_cpu, write_torch_ext_cuda, write_torch_ext_metal, write_torch_ext_universal,
14+
write_torch_ext_xpu,
1415
};
1516

1617
mod config;
@@ -178,6 +179,7 @@ fn generate_torch(
178179
};
179180

180181
let file_set = match backend {
182+
Backend::Cpu => write_torch_ext_cpu(&env, &build, target_dir.clone(), ops_id)?,
181183
Backend::Cuda | Backend::Rocm => {
182184
write_torch_ext_cuda(&env, backend, &build, target_dir.clone(), ops_id)?
183185
}
@@ -376,6 +378,7 @@ fn get_generated_files(
376378

377379
for backend in build.backends() {
378380
let set = match backend {
381+
Backend::Cpu => write_torch_ext_cpu(env, build, target_dir.clone(), ops_id.clone())?,
379382
Backend::Cuda | Backend::Rocm => {
380383
write_torch_ext_cuda(env, backend, build, target_dir.clone(), ops_id.clone())?
381384
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
set({{kernel_name}}_SRC
2+
{{ sources }}
3+
)
4+
5+
{% if includes %}
6+
# TODO: check if CLion support this:
7+
# https://youtrack.jetbrains.com/issue/CPP-16510/CLion-does-not-handle-per-file-include-directories
8+
set_source_files_properties(
9+
{{'${' + kernel_name + '_SRC}'}}
10+
PROPERTIES INCLUDE_DIRECTORIES "{{ includes }}")
11+
{% endif %}
12+
13+
{% if cxx_flags %}
14+
foreach(_KERNEL_SRC {{'${' + kernel_name + '_SRC}'}})
15+
set_property(
16+
SOURCE ${_KERNEL_SRC}
17+
APPEND PROPERTY
18+
COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:CXX>:{{ cxx_flags }}>"
19+
)
20+
endforeach()
21+
{% endif %}
22+
23+
# Add C++ sources to main source list
24+
list(APPEND SRC {{'"${' + kernel_name + '_CPP_SRC}"'}})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
cmake_minimum_required(VERSION 3.26)
2+
project({{name}} LANGUAGES CXX)
3+
4+
set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0" CACHE STRING "Minimum macOS deployment version")
5+
6+
install(CODE "set(CMAKE_INSTALL_LOCAL_ONLY TRUE)" ALL_COMPONENTS)
7+
8+
include(FetchContent)
9+
file(MAKE_DIRECTORY ${FETCHCONTENT_BASE_DIR}) # Ensure the directory exists
10+
message(STATUS "FetchContent base directory: ${FETCHCONTENT_BASE_DIR}")
11+
12+
include(${CMAKE_CURRENT_LIST_DIR}/cmake/utils.cmake)
13+
14+
if(DEFINED Python3_EXECUTABLE)
15+
# Allow passing through the interpreter (e.g. from setup.py).
16+
find_package(Python3 COMPONENTS Development Development.SABIModule Interpreter)
17+
if (NOT Python3_FOUND)
18+
message(FATAL_ERROR "Unable to find python matching: ${EXECUTABLE}.")
19+
endif()
20+
else()
21+
find_package(Python3 REQUIRED COMPONENTS Development Development.SABIModule Interpreter)
22+
endif()
23+
24+
append_cmake_prefix_path("torch" "torch.utils.cmake_prefix_path")
25+
26+
find_package(Torch REQUIRED)
27+
28+
add_compile_definitions(CPU_KERNEL)
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import logging
2+
import os
3+
from shutil import which, move
4+
import subprocess
5+
import sys
6+
from pathlib import Path
7+
8+
from setuptools import Extension, find_packages, setup
9+
from setuptools.command.build_ext import build_ext
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
def is_sccache_available() -> bool:
15+
return which("sccache") is not None
16+
17+
18+
def is_ccache_available() -> bool:
19+
return which("ccache") is not None
20+
21+
22+
def is_ninja_available() -> bool:
23+
return which("ninja") is not None
24+
25+
26+
class CMakeExtension(Extension):
27+
def __init__(self, name: str, sourcedir: str = "") -> None:
28+
super().__init__(name, sources=[], py_limited_api=True)
29+
self.sourcedir = os.fspath(Path(sourcedir).resolve())
30+
31+
32+
class CMakeBuild(build_ext):
33+
def build_extension(self, ext: CMakeExtension) -> None:
34+
ext_fullpath = Path.cwd() / self.get_ext_fullpath(ext.name)
35+
extdir = ext_fullpath.parent.resolve()
36+
37+
debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug
38+
cfg = "Debug" if debug else "Release"
39+
40+
cmake_generator = os.environ.get("CMAKE_GENERATOR", "")
41+
42+
# Set Python3_EXECUTABLE instead if you use PYBIND11_FINDPYTHON
43+
# EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code
44+
# from Python.
45+
cmake_args = [
46+
f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}{os.sep}",
47+
f"-DPython3_EXECUTABLE={sys.executable}",
48+
f"-DCMAKE_BUILD_TYPE={cfg}", # not used on MSVC, but no harm
49+
]
50+
build_args = []
51+
if "CMAKE_ARGS" in os.environ:
52+
cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item]
53+
54+
if not cmake_generator or cmake_generator == "Ninja":
55+
try:
56+
import ninja
57+
58+
ninja_executable_path = Path(ninja.BIN_DIR) / "ninja"
59+
cmake_args += [
60+
"-GNinja",
61+
f"-DCMAKE_MAKE_PROGRAM:FILEPATH={ninja_executable_path}",
62+
]
63+
except ImportError:
64+
pass
65+
66+
if is_sccache_available():
67+
cmake_args += [
68+
"-DCMAKE_C_COMPILER_LAUNCHER=sccache",
69+
"-DCMAKE_CXX_COMPILER_LAUNCHER=sccache",
70+
"-DCMAKE_HIP_COMPILER_LAUNCHER=sccache",
71+
"-DCMAKE_OBJC_COMPILER_LAUNCHER=sccache",
72+
"-DCMAKE_OBJCXX_COMPILER_LAUNCHER=sccache",
73+
]
74+
elif is_ccache_available():
75+
cmake_args += [
76+
"-DCMAKE_C_COMPILER_LAUNCHER=ccache",
77+
"-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",
78+
"-DCMAKE_HIP_COMPILER_LAUNCHER=ccache",
79+
"-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache",
80+
"-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache",
81+
]
82+
83+
num_jobs = os.getenv("MAX_JOBS", None)
84+
if num_jobs is not None:
85+
num_jobs = int(num_jobs)
86+
logger.info("Using MAX_JOBS=%d as the number of jobs.", num_jobs)
87+
else:
88+
try:
89+
# os.sched_getaffinity() isn't universally available, so fall
90+
# back to os.cpu_count() if we get an error here.
91+
num_jobs = len(os.sched_getaffinity(0))
92+
except AttributeError:
93+
num_jobs = os.cpu_count()
94+
95+
build_temp = Path(self.build_temp) / ext.name
96+
if not build_temp.exists():
97+
build_temp.mkdir(parents=True)
98+
99+
subprocess.run(
100+
["cmake", ext.sourcedir, *cmake_args], cwd=build_temp, check=True
101+
)
102+
subprocess.run(
103+
["cmake", "--build", ".", *build_args], cwd=build_temp, check=True
104+
)
105+
106+
107+
setup(
108+
name="{{ name }}",
109+
# The version is just a stub, it's not used by the final build artefact.
110+
version="0.1.0",
111+
ext_modules=[CMakeExtension("{{ name }}.{{ ops_name }}")],
112+
cmdclass={"build_ext": CMakeBuild},
113+
packages=find_packages(where="torch-ext", include=["{{ name }}*"]),
114+
package_dir={"": "torch-ext"},
115+
{% if data_globs %}
116+
package_data={"{{ name }}": [ {{ data_globs }} ]},
117+
{% endif %}
118+
zip_safe=False,
119+
install_requires=["torch"],
120+
python_requires=">=3.9",
121+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
set(TORCH_{{name}}_SRC
2+
{{ src|join(' ') }}
3+
)
4+
5+
{% if includes %}
6+
# TODO: check if CLion support this:
7+
# https://youtrack.jetbrains.com/issue/CPP-16510/CLion-does-not-handle-per-file-include-directories
8+
set_source_files_properties(
9+
{{'${TORCH_' + name + '_SRC}'}}
10+
PROPERTIES INCLUDE_DIRECTORIES "{{ includes }}")
11+
{% endif %}
12+
13+
list(APPEND SRC {{'"${TORCH_' + name + '_SRC}"'}})
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
define_gpu_extension_target(
2+
{{ ops_name }}
3+
DESTINATION {{ ops_name }}
4+
LANGUAGE ${GPU_LANG}
5+
SOURCES ${SRC}
6+
COMPILE_FLAGS ${GPU_FLAGS}
7+
ARCHITECTURES ${GPU_ARCHES}
8+
USE_SABI 3
9+
WITH_SOABI)

0 commit comments

Comments
 (0)