From 976d3afc6195ebb23eccf1a6db4f0b3784899196 Mon Sep 17 00:00:00 2001 From: Tatsuya Yatagawa Date: Thu, 26 Dec 2024 12:46:33 +0900 Subject: [PATCH 01/12] Merge pull request. --- CMakeLists.txt | 33 +++++++++++++++++---------------- cxx/CMakeLists.txt | 20 +++++++++++++++++--- pyproject.toml | 17 +++++++++++------ 3 files changed, 45 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f78943..c57848d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.18...3.27) +cmake_minimum_required(VERSION 3.18) project(${SKBUILD_PROJECT_NAME} VERSION ${SKBUILD_PROJECT_VERSION} LANGUAGES CXX) # General settings @@ -23,9 +23,7 @@ if(CMAKE_CUDA_COMPILER) message(STATUS "CMAKE_CUDA_COMPILER_VERSION = ${CUDA_V}") else() - message(STATUS "NO CUDA INSTALLATION FOUND, INSTALLING CPU VERSION ONLY!") - # execute_process(COMMAND ${Python_EXECUTABLE} -m pip uninstall -y torch) - # execute_process(COMMAND ${Python_EXECUTABLE} -m pip install -v torch --extra-index-url https://download.pytorch.org/whl/cpu) + message(WARNING "NO CUDA INSTALLATION FOUND, TRYING TO INSTALL CPU VERSION ONLY!") endif() set(CMAKE_CXX_STANDARD 17) @@ -33,25 +31,28 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(MSVC) - add_definitions(-DNOMINMAX) + add_compile_options("$<$:/utf-8>") + add_compile_options("$<$:-Xcompiler=/utf-8>") + add_definitions(-DNOMINMAX -D_CRT_SECURE_NO_WARNINGS) endif() # Find packages -find_package(Python REQUIRED COMPONENTS Interpreter Development) +find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) find_package(pybind11 CONFIG REQUIRED) - -# OpenMP find_package(OpenMP) -if (OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + +# Try nvToolsExt workaround +# https://discuss.pytorch.org/t/failed-to-find-nvtoolsext/179635/13 +if (CMAKE_CUDA_COMPILER) + if(NOT TARGET CUDA::nvToolsExt AND TARGET CUDA::nvtx3) + add_library(CUDA::nvToolsExt INTERFACE IMPORTED) + target_link_libraries(CUDA::nvToolsExt INTERFACE CUDA::nvtx3) + endif() endif() -# PyTorch settings -find_package(Torch REQUIRED) -find_library(TORCH_PYTHON_LIBRARY torch_python PATH "${TORCH_INSTALL_PREFIX}/lib") +find_package(Torch CONFIG REQUIRED) +find_library(TORCH_PYTHON_LIBRARY torch_python PATH "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}") add_subdirectory(cxx) - -install(TARGETS ${BUILD_TARGET} LIBRARY DESTINATION .) +install(TARGETS ${BUILD_TARGET} LIBRARY DESTINATION ${SKBUILD_PROJECT_NAME}) diff --git a/cxx/CMakeLists.txt b/cxx/CMakeLists.txt index 60fa047..bf367cf 100644 --- a/cxx/CMakeLists.txt +++ b/cxx/CMakeLists.txt @@ -1,4 +1,10 @@ -include_directories(${TORCH_INCLUDE_DIRS}) +foreach (DIR IN LISTS TORCH_INCLUDE_DIRS) + if (EXISTS "${DIR}") + include_directories(${DIR}) + else() + message(WARNING "Directory ${DIR} does not exist.") + endif() +endforeach() Python_add_library(${BUILD_TARGET} mcubes.cpp @@ -16,8 +22,16 @@ endif() target_link_libraries(${BUILD_TARGET} PRIVATE pybind11::headers - ${TORCH_LIBRARIES} - ${TORCH_PYTHON_LIBRARY}) + ${TORCH_PYTHON_LIBRARY} +) + +foreach (LIB IN LISTS TORCH_LIBRARIES) + if (EXISTS ${LIB} OR ${LIB} MATCHES "torch.*") + target_link_libraries(${BUILD_TARGET} PRIVATE ${LIB}) + else() + message(WARNING "Library ${LIB} does not exist.") + endif() +endforeach() if (OPENMP_FOUND) target_link_libraries(${BUILD_TARGET} PRIVATE OpenMP::OpenMP_CXX) diff --git a/pyproject.toml b/pyproject.toml index 9b3a347..4a80e71 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,19 +19,24 @@ dependencies=["numpy", "torch"] [tool.scikit-build] build-dir = "build" -wheel.license-files = [] -minimum-version = "0.2" -logging.level = "INFO" +minimum-version = "0.10" ninja.make-fallback = true +logging.level = "INFO" +build.verbose = true +messages.after-success = "SUCCESS!!" +messages.after-failure = "FAILURE!!" [tool.scikit-build.cmake] -minimum-version = "3.18" -verbose = true +version = ">=3.15" source-dir = "." args = [] +[tool.scikit-build.wheel] +license-files = ["LICENSE"] +exclude = ["**/.mypy_cache/**", "**/build/**", "**/.vscode/**"] + [build-system] -requires = ["scikit-build-core>=0.2", "pybind11>=2.10"] +requires = ["scikit-build-core>=0.10", "pybind11>=2.10", "cmake", "ninja"] build-backend = "scikit_build_core.build" [tool.isort] From d31514fb266c96d5fe348931dea28147e5029243 Mon Sep 17 00:00:00 2001 From: Tatsuya Yatagawa Date: Thu, 26 Dec 2024 14:22:18 +0900 Subject: [PATCH 02/12] Minor update. --- CMakeLists.txt | 12 +----------- README.md | 8 +++----- cxx/CMakeLists.txt | 18 ++---------------- 3 files changed, 6 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c57848d..b96e0e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,6 @@ if(CMAKE_CUDA_COMPILER) message(STATUS "CMAKE_CUDA_COMPILER = ${CMAKE_CUDA_COMPILER}") message(STATUS "CMAKE_CUDA_COMPILER_ID = ${CMAKE_CUDA_COMPILER_ID}") message(STATUS "CMAKE_CUDA_COMPILER_VERSION = ${CUDA_V}") - else() message(WARNING "NO CUDA INSTALLATION FOUND, TRYING TO INSTALL CPU VERSION ONLY!") endif() @@ -41,17 +40,8 @@ find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) find_package(pybind11 CONFIG REQUIRED) find_package(OpenMP) -# Try nvToolsExt workaround -# https://discuss.pytorch.org/t/failed-to-find-nvtoolsext/179635/13 -if (CMAKE_CUDA_COMPILER) - if(NOT TARGET CUDA::nvToolsExt AND TARGET CUDA::nvtx3) - add_library(CUDA::nvToolsExt INTERFACE IMPORTED) - target_link_libraries(CUDA::nvToolsExt INTERFACE CUDA::nvtx3) - endif() -endif() - find_package(Torch CONFIG REQUIRED) -find_library(TORCH_PYTHON_LIBRARY torch_python PATH "${TORCH_INSTALL_PREFIX}/lib" REQUIRED) +find_library(TORCH_PYTHON_LIBRARY torch_python PATH "${TORCH_INSTALL_PREFIX}/lib") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}") add_subdirectory(cxx) diff --git a/README.md b/README.md index 64ff5ab..3c48f78 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -torchmcubes: marching cubes for PyTorch -=== +# torchmcubes: marching cubes for PyTorch [![Build (CPU)](https://github.com/tatsy/torchmcubes/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/tatsy/torchmcubes/actions/workflows/build.yml) @@ -12,9 +11,9 @@ torchmcubes: marching cubes for PyTorch - Python (3.9 or later) - NumPy (1.x is preferable) - PyTorch -- cmake (3.18 or later) +- CMake (3.18 or later) -Make sure that you have nvcc CUDA compiler +Make sure that you have nvcc CUDA compiler with the following command. ```shell nvcc --version @@ -27,7 +26,6 @@ export CUDA_HOME=/usr/local/cuda/ export PATH=$CUDA_HOME/bin:$PATH ``` - ### Pip installation ```shell diff --git a/cxx/CMakeLists.txt b/cxx/CMakeLists.txt index bf367cf..b01e9d8 100644 --- a/cxx/CMakeLists.txt +++ b/cxx/CMakeLists.txt @@ -1,11 +1,4 @@ -foreach (DIR IN LISTS TORCH_INCLUDE_DIRS) - if (EXISTS "${DIR}") - include_directories(${DIR}) - else() - message(WARNING "Directory ${DIR} does not exist.") - endif() -endforeach() - +include_directories (${TORCH_INCLUDE_DIRS}) Python_add_library(${BUILD_TARGET} mcubes.cpp mcubes_cpu.cpp @@ -22,17 +15,10 @@ endif() target_link_libraries(${BUILD_TARGET} PRIVATE pybind11::headers + ${TORCH_LIBRARIES} ${TORCH_PYTHON_LIBRARY} ) -foreach (LIB IN LISTS TORCH_LIBRARIES) - if (EXISTS ${LIB} OR ${LIB} MATCHES "torch.*") - target_link_libraries(${BUILD_TARGET} PRIVATE ${LIB}) - else() - message(WARNING "Library ${LIB} does not exist.") - endif() -endforeach() - if (OPENMP_FOUND) target_link_libraries(${BUILD_TARGET} PRIVATE OpenMP::OpenMP_CXX) endif() From be4084bdc1aac29ffeeb0ed53efaacf5c4b545e0 Mon Sep 17 00:00:00 2001 From: Tatsuya Yatagawa Date: Thu, 26 Dec 2024 14:37:24 +0900 Subject: [PATCH 03/12] Minor update. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b96e0e7..3433480 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,4 +45,4 @@ find_library(TORCH_PYTHON_LIBRARY torch_python PATH "${TORCH_INSTALL_PREFIX}/lib set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}") add_subdirectory(cxx) -install(TARGETS ${BUILD_TARGET} LIBRARY DESTINATION ${SKBUILD_PROJECT_NAME}) +install(TARGETS ${BUILD_TARGET} LIBRARY DESTINATION .) From 273b9c504b613eb49097558341af11848f3e644f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 12:07:22 +0000 Subject: [PATCH 04/12] Document --no-build-isolation install and mirror it in CI torchmcubes must be compiled against the PyTorch already installed in the user's environment. PyTorch cannot be a build-system requirement because the right build (CPU / CUDA version) depends on the environment, so the documented install path now uses `pip install --no-build-isolation` with the build dependencies (scikit-build-core, pybind11, cmake, ninja) installed beforehand. The README explains why the flag is needed and what the typical "Torch not found" error means. The CI workflow now runs the same command as the README instead of an isolated build, adds an import smoke test from outside the source tree, runs on pull requests, and bumps setup-python to v5. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_011Lvzircq8GvNij9VCm8tZc --- .github/workflows/build.yml | 20 ++++++++++++++++---- README.md | 25 +++++++++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c648c36..4c2b4d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,10 +2,13 @@ name: Build (CPU) on: push: branches: [master, dev] + pull_request: + branches: [master, dev] jobs: build: strategy: + fail-fast: false matrix: os: - ubuntu-latest @@ -21,13 +24,22 @@ jobs: - name: Check out repository code uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.version }} - - name: Install requirements + - name: Install PyTorch (CPU) run: | python -m pip install --upgrade pip - python -m pip install numpy "pybind11[global]" + python -m pip install numpy python -m pip install torch --index-url https://download.pytorch.org/whl/cpu + - name: Install build dependencies + run: python -m pip install scikit-build-core pybind11 cmake ninja - name: Build - run: pip install . + # Same command as documented in README: build against the PyTorch + # installed above instead of an isolated build environment. + run: pip install --no-build-isolation . + - name: Import test + # Run outside the repository root so the installed package is imported, + # not the source directory. + working-directory: ${{ runner.temp }} + run: python -c "import torchmcubes; print(torchmcubes.__file__)" diff --git a/README.md b/README.md index 3c48f78..dd58e14 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ - Python (3.9 or later) - NumPy (1.x is preferable) - PyTorch -- CMake (3.18 or later) +- C++17 compiler (GCC, Clang, or MSVC) +- CMake (3.18 or later) and Ninja (both can be installed with pip, see below) Make sure that you have nvcc CUDA compiler with the following command. @@ -19,7 +20,7 @@ Make sure that you have nvcc CUDA compiler with the following command. nvcc --version ``` -If you have CUDA installed but not able to run nvcc, you migth need to add it to your path: +If you have CUDA installed but not able to run nvcc, you might need to add it to your path: ```shell export CUDA_HOME=/usr/local/cuda/ @@ -28,11 +29,27 @@ export PATH=$CUDA_HOME/bin:$PATH ### Pip installation +torchmcubes is compiled against the PyTorch installed in your environment. Install PyTorch first, then install the build dependencies and torchmcubes **without build isolation**. + ```shell -# Make sure that your environment meets the requirements above -pip install git+https://github.com/tatsy/torchmcubes.git +# 1. Install PyTorch (see https://pytorch.org/get-started/locally/ for the right command) +pip install torch + +# 2. Install build dependencies +pip install scikit-build-core pybind11 cmake ninja + +# 3. Build and install torchmcubes against the PyTorch installed above +pip install --no-build-isolation git+https://github.com/tatsy/torchmcubes.git ``` +To build from a local checkout, run `pip install --no-build-isolation .` in the repository root instead of the last command. + +#### Why `--no-build-isolation`? + +By default, pip builds packages in a temporary, isolated environment. PyTorch cannot be listed as a build dependency there because the correct build (CPU, CUDA version, ...) depends on your environment, and a PyTorch downloaded into the isolated environment would not necessarily match the one you use at runtime. `--no-build-isolation` tells pip to build directly in your environment so that CMake finds your PyTorch. This is also why the build dependencies in step 2 must be installed manually. + +If you see an error like `Could not find a package configuration file provided by "Torch"`, you most likely forgot `--no-build-isolation` or PyTorch is not installed in the active environment. + ## Usage See [mcubes.py](./mcubes.py) for more details. From ffeb1d21fcab98f6878c77d36230e3d82d65dfa8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 12:20:58 +0000 Subject: [PATCH 05/12] Build with C++20 as required by recent PyTorch headers Since torch 2.14 the headers refuse to compile without C++20 (`#error C++20 or later compatible compiler is required to use PyTorch`), which broke the build on every Python >= 3.10 job in CI. Raise CMAKE_CXX_STANDARD and CMAKE_CUDA_STANDARD to 20. Older torch versions (e.g. 2.8 on Python 3.9) compile fine with C++20 too. Document the compiler and CUDA Toolkit requirements in README, and bump actions/checkout and actions/setup-python to the Node 24 releases to clear the Node 20 deprecation warnings in the CI logs. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_011Lvzircq8GvNij9VCm8tZc --- .github/workflows/build.yml | 4 ++-- CMakeLists.txt | 7 +++++-- README.md | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c2b4d6..c5d31e8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,9 +22,9 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Check out repository code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.version }} - name: Install PyTorch (CPU) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3433480..986a96f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ if(CMAKE_CUDA_COMPILER) add_definitions(-DWITH_CUDA) find_package(CUDAToolkit REQUIRED) - set(CMAKE_CUDA_STANDARD 17) + set(CMAKE_CUDA_STANDARD 20) set(CMAKE_CUDA_STANDARD_REQUIRED ON) message(STATUS "INSTALLING EXTENSIONS WITH CUDA!") @@ -25,7 +25,10 @@ else() message(WARNING "NO CUDA INSTALLATION FOUND, TRYING TO INSTALL CPU VERSION ONLY!") endif() -set(CMAKE_CXX_STANDARD 17) +# Recent PyTorch headers require C++20 (torch/all.h and ATen/ATen.h emit +# #error otherwise; torch 2.14 requires it, torch 2.9 still accepted C++17). +# Older torch versions compile fine with C++20 as well. +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) diff --git a/README.md b/README.md index dd58e14..61fc5f0 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ - Python (3.9 or later) - NumPy (1.x is preferable) - PyTorch -- C++17 compiler (GCC, Clang, or MSVC) +- C++20 compiler (GCC 10+, Clang 10+, or Visual Studio 2019 16.11+), required by recent PyTorch headers +- CUDA Toolkit 12 or later (optional, only for GPU support; nvcc needs CUDA 12 for C++20) - CMake (3.18 or later) and Ninja (both can be installed with pip, see below) Make sure that you have nvcc CUDA compiler with the following command. From f3091c1de7f373b58510cefb212609d57e380f8f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 13:02:40 +0000 Subject: [PATCH 06/12] Make version and torch requirement dynamic metadata The version now lives only in torchmcubes/__init__.py (__version__) and is read by scikit-build-core's regex provider. The torch runtime requirement is generated at build time by _torch_dep.py: the extension is bound to the C++ ABI of the torch it is compiled against, so the wheel now records `torch==X.Y.*` (minor series of the torch found during the build) instead of an unconditional `torch`. Patch releases keep the ABI and local versions such as 2.14.0+cu126 still satisfy the pin. If torch is not importable at build time the requirement falls back to `torch`; CMake fails with a clear message in that case anyway. Also drop numpy from the runtime dependencies (only the example script uses it), remove cmake/ninja from build-system.requires as scikit-build-core injects them itself, require scikit-build-core >= 1.0 for the [[tool.dynamic-metadata]] table, and align the CMake minimum with CMakeLists.txt. CI now prints the installed version and recorded requirements. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_011Lvzircq8GvNij9VCm8tZc --- .github/workflows/build.yml | 4 +- README.md | 5 ++- _torch_dep.py | 29 ++++++++++++++ pyproject.toml | 26 ++++++++++--- torchmcubes/__init__.py | 76 +++++++++++++++++++------------------ 5 files changed, 95 insertions(+), 45 deletions(-) create mode 100644 _torch_dep.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c5d31e8..a1cfda0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,4 +42,6 @@ jobs: # Run outside the repository root so the installed package is imported, # not the source directory. working-directory: ${{ runner.temp }} - run: python -c "import torchmcubes; print(torchmcubes.__file__)" + run: | + python -c "import torchmcubes; print(torchmcubes.__file__, torchmcubes.__version__)" + python -m pip show torchmcubes diff --git a/README.md b/README.md index 61fc5f0..af243a6 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ ### Requirements - Python (3.9 or later) -- NumPy (1.x is preferable) - PyTorch - C++20 compiler (GCC 10+, Clang 10+, or Visual Studio 2019 16.11+), required by recent PyTorch headers - CUDA Toolkit 12 or later (optional, only for GPU support; nvcc needs CUDA 12 for C++20) @@ -51,9 +50,11 @@ By default, pip builds packages in a temporary, isolated environment. PyTorch ca If you see an error like `Could not find a package configuration file provided by "Torch"`, you most likely forgot `--no-build-isolation` or PyTorch is not installed in the active environment. +The built package records the PyTorch it was compiled against as its requirement (for example `torch==2.14.*`), because the extension module is bound to that PyTorch's C++ ABI. If you later upgrade PyTorch to a different minor version, reinstall torchmcubes to rebuild it. + ## Usage -See [mcubes.py](./mcubes.py) for more details. +See [mcubes.py](./mcubes.py) for more details (the example additionally needs `numpy` and `matplotlib`). ```python import time diff --git a/_torch_dep.py b/_torch_dep.py new file mode 100644 index 0000000..7d18649 --- /dev/null +++ b/_torch_dep.py @@ -0,0 +1,29 @@ +"""scikit-build-core dynamic-metadata provider for the torch runtime requirement. + +torchmcubes links against the C++ ABI of the PyTorch it is compiled with, so the +built wheel must not claim to work with any torch version. This provider pins +the runtime requirement to the minor series of the torch found at build time +(e.g. ``torch==2.14.*``). Patch releases keep the ABI, so they stay allowed; +CUDA/CPU local versions such as ``2.14.0+cu126`` also satisfy the pin. +""" + +from __future__ import annotations + +import importlib.metadata + + +def torch_requirement() -> str: + try: + version = importlib.metadata.version("torch") + except importlib.metadata.PackageNotFoundError: + # No torch at build time: CMake will fail later with a clear message, + # so just fall back to an unpinned requirement here. + return "torch" + major, minor = version.split(".")[:2] + return f"torch=={major}.{minor}.*" + + +class Provider: + @staticmethod + def dynamic_metadata(settings, project): + return {"dependencies": [torch_requirement()]} diff --git a/pyproject.toml b/pyproject.toml index 4a80e71..308cb26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,5 @@ [project] name = "torchmcubes" -version = "0.1.0" description = "torchmcubes: Marching Cubes for PyTorch" readme = "README.md" authors = [ @@ -15,11 +14,24 @@ classifiers = [ 'Programming Language :: Python :: 3.12', ] requires-python = ">=3.9" -dependencies=["numpy", "torch"] +# version: read from torchmcubes/__init__.py (__version__). +# dependencies: torch, pinned at build time to the minor series of the torch +# the extension is compiled against (see _torch_dep.py). +dynamic = ["version", "dependencies"] + +[[tool.dynamic-metadata]] +provider = "scikit_build_core.metadata.regex" +field = "version" +input = "torchmcubes/__init__.py" + +[[tool.dynamic-metadata]] +provider = { path = ".", module = "_torch_dep:Provider" } [tool.scikit-build] build-dir = "build" -minimum-version = "0.10" +minimum-version = "1.0" +# Required for the local dynamic-metadata provider in _torch_dep.py. +experimental = true ninja.make-fallback = true logging.level = "INFO" build.verbose = true @@ -27,7 +39,7 @@ messages.after-success = "SUCCESS!!" messages.after-failure = "FAILURE!!" [tool.scikit-build.cmake] -version = ">=3.15" +version = ">=3.18" source-dir = "." args = [] @@ -36,7 +48,11 @@ license-files = ["LICENSE"] exclude = ["**/.mypy_cache/**", "**/build/**", "**/.vscode/**"] [build-system] -requires = ["scikit-build-core>=0.10", "pybind11>=2.10", "cmake", "ninja"] +# cmake and ninja are injected by scikit-build-core when needed, so they are +# not listed here. torch is deliberately absent: the correct torch build +# (CPU / CUDA version) depends on the environment, so torchmcubes must be built +# with --no-build-isolation against the torch already installed (see README). +requires = ["scikit-build-core>=1.0", "pybind11>=2.10"] build-backend = "scikit_build_core.build" [tool.isort] diff --git a/torchmcubes/__init__.py b/torchmcubes/__init__.py index 6d20b7a..86a9359 100644 --- a/torchmcubes/__init__.py +++ b/torchmcubes/__init__.py @@ -1,37 +1,39 @@ -import os - -os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" - -from typing import Tuple - -import torch -import torchmcubes_module as mc - - -def marching_cubes(vol: torch.Tensor, thresh: float) -> Tuple[torch.Tensor, torch.Tensor]: - """ - vol: 3D torch tensor - thresh: threshold - """ - - if vol.is_cuda: - return mc.mcubes_cuda(vol, thresh) - else: - return mc.mcubes_cpu(vol, thresh) - - -def grid_interp(vol: torch.Tensor, points: torch.Tensor) -> torch.Tensor: - """ - Interpolate volume data at given points - - Inputs: - vol: 4D torch tensor (C, Nz, Ny, Nx) - points: point locations (Np, 3) - Outputs: - output: interpolated data (Np, C) - """ - - if vol.is_cuda: - return mc.grid_interp_cuda(vol, points) - else: - return mc.grid_interp_cpu(vol, points) +import os + +__version__ = "0.1.0" + +os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE" + +from typing import Tuple + +import torch +import torchmcubes_module as mc + + +def marching_cubes(vol: torch.Tensor, thresh: float) -> Tuple[torch.Tensor, torch.Tensor]: + """ + vol: 3D torch tensor + thresh: threshold + """ + + if vol.is_cuda: + return mc.mcubes_cuda(vol, thresh) + else: + return mc.mcubes_cpu(vol, thresh) + + +def grid_interp(vol: torch.Tensor, points: torch.Tensor) -> torch.Tensor: + """ + Interpolate volume data at given points + + Inputs: + vol: 4D torch tensor (C, Nz, Ny, Nx) + points: point locations (Np, 3) + Outputs: + output: interpolated data (Np, C) + """ + + if vol.is_cuda: + return mc.grid_interp_cuda(vol, points) + else: + return mc.grid_interp_cpu(vol, points) From 2b08c990f905464d30cfe0d548ee1cb1e5535dee Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 13:08:31 +0000 Subject: [PATCH 07/12] CI: print exact Requires-Dist instead of pip show `pip show` lists requirement names without version specifiers and dumps the full license text, so it could not confirm the torch pin recorded in the wheel. Read the requirement strings via importlib.metadata instead. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_011Lvzircq8GvNij9VCm8tZc --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a1cfda0..e283f18 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,4 +44,4 @@ jobs: working-directory: ${{ runner.temp }} run: | python -c "import torchmcubes; print(torchmcubes.__file__, torchmcubes.__version__)" - python -m pip show torchmcubes + python -c "import importlib.metadata as m; print('Requires-Dist:', m.requires('torchmcubes'))" From 4dc00943e990950bafcf612a61b6dfac9953de71 Mon Sep 17 00:00:00 2001 From: Tatsuya Yatagawa Date: Thu, 10 Sep 2026 22:54:55 +0900 Subject: [PATCH 08/12] Update .gitignore and README for clarity; modify pyproject.toml license and classifiers. --- .gitignore | 2 ++ README.md | 16 ++++------------ pyproject.toml | 11 ++++------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index c3d108a..4d26f49 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ __pycache__ *.pyd *.so *.egg-info +.venv +uv.lock diff --git a/README.md b/README.md index af243a6..09298d3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - PyTorch - C++20 compiler (GCC 10+, Clang 10+, or Visual Studio 2019 16.11+), required by recent PyTorch headers - CUDA Toolkit 12 or later (optional, only for GPU support; nvcc needs CUDA 12 for C++20) -- CMake (3.18 or later) and Ninja (both can be installed with pip, see below) +- CMake (3.18 or later) Make sure that you have nvcc CUDA compiler with the following command. @@ -32,11 +32,11 @@ export PATH=$CUDA_HOME/bin:$PATH torchmcubes is compiled against the PyTorch installed in your environment. Install PyTorch first, then install the build dependencies and torchmcubes **without build isolation**. ```shell -# 1. Install PyTorch (see https://pytorch.org/get-started/locally/ for the right command) +# 1. Install PyTorch (if you need GPU support, choose the correct CUDA version) pip install torch # 2. Install build dependencies -pip install scikit-build-core pybind11 cmake ninja +pip install scikit-build-core pybind11 # 3. Build and install torchmcubes against the PyTorch installed above pip install --no-build-isolation git+https://github.com/tatsy/torchmcubes.git @@ -44,14 +44,6 @@ pip install --no-build-isolation git+https://github.com/tatsy/torchmcubes.git To build from a local checkout, run `pip install --no-build-isolation .` in the repository root instead of the last command. -#### Why `--no-build-isolation`? - -By default, pip builds packages in a temporary, isolated environment. PyTorch cannot be listed as a build dependency there because the correct build (CPU, CUDA version, ...) depends on your environment, and a PyTorch downloaded into the isolated environment would not necessarily match the one you use at runtime. `--no-build-isolation` tells pip to build directly in your environment so that CMake finds your PyTorch. This is also why the build dependencies in step 2 must be installed manually. - -If you see an error like `Could not find a package configuration file provided by "Torch"`, you most likely forgot `--no-build-isolation` or PyTorch is not installed in the active environment. - -The built package records the PyTorch it was compiled against as its requirement (for example `torch==2.14.*`), because the extension module is bound to that PyTorch's C++ ABI. If you later upgrade PyTorch to a different minor version, reinstall torchmcubes to rebuild it. - ## Usage See [mcubes.py](./mcubes.py) for more details (the example additionally needs `numpy` and `matplotlib`). @@ -104,4 +96,4 @@ visualize(verts, faces, colors) ## Copyright -MIT License 2019-2024 (c) Tatsuya Yatagawa +MIT License 2019-2026 (c) Tatsuya Yatagawa diff --git a/pyproject.toml b/pyproject.toml index 308cb26..d91b60f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,18 +5,17 @@ readme = "README.md" authors = [ {name = "Tatsuya Yatagawa", email = "tatsy.mail@gmail.com"} ] -license = {file = "LICENSE"} +license = "MIT" +license-files = ["LICENSE"] classifiers = [ - "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', ] requires-python = ">=3.9" -# version: read from torchmcubes/__init__.py (__version__). -# dependencies: torch, pinned at build time to the minor series of the torch -# the extension is compiled against (see _torch_dep.py). dynamic = ["version", "dependencies"] [[tool.dynamic-metadata]] @@ -30,7 +29,6 @@ provider = { path = ".", module = "_torch_dep:Provider" } [tool.scikit-build] build-dir = "build" minimum-version = "1.0" -# Required for the local dynamic-metadata provider in _torch_dep.py. experimental = true ninja.make-fallback = true logging.level = "INFO" @@ -44,7 +42,6 @@ source-dir = "." args = [] [tool.scikit-build.wheel] -license-files = ["LICENSE"] exclude = ["**/.mypy_cache/**", "**/build/**", "**/.vscode/**"] [build-system] From 569274d7a09a757a99e36445ad84c72de8cd0556 Mon Sep 17 00:00:00 2001 From: Tatsuya Yatagawa Date: Thu, 10 Sep 2026 22:59:39 +0900 Subject: [PATCH 09/12] Update build workflow to include Python 3.13 and simplify build dependencies. --- .github/workflows/build.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e283f18..4931ab5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,21 +19,26 @@ jobs: - "3.10" - "3.11" - "3.12" + - "3.13" runs-on: ${{ matrix.os }} steps: - name: Check out repository code uses: actions/checkout@v5 + - name: Setup Python uses: actions/setup-python@v6 with: python-version: ${{ matrix.version }} + - name: Install PyTorch (CPU) run: | python -m pip install --upgrade pip python -m pip install numpy python -m pip install torch --index-url https://download.pytorch.org/whl/cpu + - name: Install build dependencies - run: python -m pip install scikit-build-core pybind11 cmake ninja + run: python -m pip install scikit-build-core pybind11 + - name: Build # Same command as documented in README: build against the PyTorch # installed above instead of an isolated build environment. From 4502c63b554d917f7bff3e5f5c70d06885cca75a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 14:07:39 +0000 Subject: [PATCH 10/12] Unify license metadata on MPL-2.0 The LICENSE file has always been the Mozilla Public License 2.0, but README and the new PEP 639 license field said MIT. Make pyproject.toml and README match the license text that is actually shipped. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_011Lvzircq8GvNij9VCm8tZc --- README.md | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 09298d3..0bc5302 100644 --- a/README.md +++ b/README.md @@ -96,4 +96,4 @@ visualize(verts, faces, colors) ## Copyright -MIT License 2019-2026 (c) Tatsuya Yatagawa +Mozilla Public License 2.0, 2019-2026 (c) Tatsuya Yatagawa diff --git a/pyproject.toml b/pyproject.toml index d91b60f..bb09263 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ readme = "README.md" authors = [ {name = "Tatsuya Yatagawa", email = "tatsy.mail@gmail.com"} ] -license = "MIT" +license = "MPL-2.0" license-files = ["LICENSE"] classifiers = [ "Development Status :: 5 - Production/Stable", From 4f980e745ef5ea57a0dbd4f40a8b42c0c23d0b15 Mon Sep 17 00:00:00 2001 From: Tatsuya Yatagawa Date: Thu, 10 Sep 2026 23:30:43 +0900 Subject: [PATCH 11/12] Minor update. --- README.md | 2 +- pyproject.toml | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/README.md b/README.md index 09298d3..8b194d1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ - Python (3.9 or later) - PyTorch -- C++20 compiler (GCC 10+, Clang 10+, or Visual Studio 2019 16.11+), required by recent PyTorch headers +- C++20 compiler (GCC 10+, Clang 12+, or Visual Studio 2019 16.11+), required by recent PyTorch headers - CUDA Toolkit 12 or later (optional, only for GPU support; nvcc needs CUDA 12 for C++20) - CMake (3.18 or later) diff --git a/pyproject.toml b/pyproject.toml index d91b60f..981021c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,10 +45,6 @@ args = [] exclude = ["**/.mypy_cache/**", "**/build/**", "**/.vscode/**"] [build-system] -# cmake and ninja are injected by scikit-build-core when needed, so they are -# not listed here. torch is deliberately absent: the correct torch build -# (CPU / CUDA version) depends on the environment, so torchmcubes must be built -# with --no-build-isolation against the torch already installed (see README). requires = ["scikit-build-core>=1.0", "pybind11>=2.10"] build-backend = "scikit_build_core.build" From 8ee4738f831bbb7cfcead585b5371cc8c326d754 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 10 Sep 2026 14:36:24 +0000 Subject: [PATCH 12/12] Fail fast when torch is missing at build time Instead of silently falling back to an unpinned `torch` requirement, abort metadata generation with an error that explains the documented install procedure. Without torch the build cannot succeed anyway, and the unpinned fallback could only produce wrong wheel metadata. Addresses the Copilot review comment on PR #33. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_011Lvzircq8GvNij9VCm8tZc --- _torch_dep.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/_torch_dep.py b/_torch_dep.py index 7d18649..021c4cc 100644 --- a/_torch_dep.py +++ b/_torch_dep.py @@ -5,6 +5,8 @@ the runtime requirement to the minor series of the torch found at build time (e.g. ``torch==2.14.*``). Patch releases keep the ABI, so they stay allowed; CUDA/CPU local versions such as ``2.14.0+cu126`` also satisfy the pin. +If torch is not importable at build time the build is aborted with an +explanatory error instead of producing unpinned metadata. """ from __future__ import annotations @@ -16,9 +18,15 @@ def torch_requirement() -> str: try: version = importlib.metadata.version("torch") except importlib.metadata.PackageNotFoundError: - # No torch at build time: CMake will fail later with a clear message, - # so just fall back to an unpinned requirement here. - return "torch" + # Fail fast: without torch the build cannot succeed anyway (CMake needs + # its config files), and emitting an unpinned requirement would produce + # wrong metadata. Point the user at the documented install procedure. + raise RuntimeError( + "torchmcubes must be built against an installed PyTorch, but the " + "'torch' package was not found in the build environment. Install " + "torch first and build without isolation, e.g. " + "`pip install --no-build-isolation .` (see README)." + ) from None major, minor = version.split(".")[:2] return f"torch=={major}.{minor}.*"