Skip to content

Commit 621906b

Browse files
authored
fix: support nvcc and test (#2461)
* fix: support nvcc and test * fixup! fix: support nvcc and test * docs: mention what compilers fail * fix: much simpler logic * refactor: slightly faster / clearer
1 parent fbc7563 commit 621906b

File tree

8 files changed

+89
-17
lines changed

8 files changed

+89
-17
lines changed

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,28 @@ jobs:
289289
- name: Interface test
290290
run: cmake --build build --target test_cmake_build
291291

292+
cuda:
293+
runs-on: ubuntu-latest
294+
name: "🐍 3.8 • CUDA 11 • Ubuntu 20.04"
295+
container: nvidia/cuda:11.0-devel-ubuntu20.04
296+
297+
steps:
298+
- uses: actions/checkout@v2
299+
300+
# tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND
301+
- name: Install 🐍 3
302+
run: apt-get update && DEBIAN_FRONTEND="noninteractive" apt-get install -y cmake python3-dev python3-pytest
303+
304+
- name: Configure
305+
run: cmake -S . -B build -DPYBIND11_CUDA_TESTS=ON -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON
306+
307+
- name: Build
308+
run: cmake --build build -j2 -v
309+
310+
- name: Python tests
311+
run: cmake --build build --target pytest
312+
313+
292314
install-classic:
293315
name: "🐍 3.5 • Debian • x86 • Install"
294316
runs-on: ubuntu-latest

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ In addition to the core functionality, pybind11 provides some extra goodies:
9898
4. Intel C++ compiler 17 or newer (16 with pybind11 v2.0 and 15 with pybind11
9999
v2.0 and a [workaround][intel-15-workaround])
100100
5. Cygwin/GCC (tested on 2.5.1)
101+
6. NVCC (CUDA 11 tested)
101102

102103
## About
103104

include/pybind11/cast.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,7 @@ template <typename CharT> using is_std_char_type = any_of<
10061006
std::is_same<CharT, wchar_t> /* std::wstring */
10071007
>;
10081008

1009+
10091010
template <typename T>
10101011
struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_type<T>::value>> {
10111012
using _py_type_0 = conditional_t<sizeof(T) <= sizeof(long), long, long long>;
@@ -1034,12 +1035,12 @@ struct type_caster<T, enable_if_t<std::is_arithmetic<T>::value && !is_std_char_t
10341035
: (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr());
10351036
}
10361037

1038+
// Python API reported an error
10371039
bool py_err = py_value == (py_type) -1 && PyErr_Occurred();
10381040

1039-
// Protect std::numeric_limits::min/max with parentheses
1040-
if (py_err || (std::is_integral<T>::value && sizeof(py_type) != sizeof(T) &&
1041-
(py_value < (py_type) (std::numeric_limits<T>::min)() ||
1042-
py_value > (py_type) (std::numeric_limits<T>::max)()))) {
1041+
// Check to see if the conversion is valid (integers should match exactly)
1042+
// Signed/unsigned checks happen elsewhere
1043+
if (py_err || (std::is_integral<T>::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) {
10431044
bool type_error = py_err && PyErr_ExceptionMatches(
10441045
#if PY_VERSION_HEX < 0x03000000 && !defined(PYPY_VERSION)
10451046
PyExc_SystemError

include/pybind11/numpy.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,14 @@ struct vectorize_arg {
14831483

14841484
template <typename Func, typename Return, typename... Args>
14851485
struct vectorize_helper {
1486+
1487+
// NVCC for some reason breaks if NVectorized is private
1488+
#ifdef __CUDACC__
1489+
public:
1490+
#else
14861491
private:
1492+
#endif
1493+
14871494
static constexpr size_t N = sizeof...(Args);
14881495
static constexpr size_t NVectorized = constexpr_sum(vectorize_arg<Args>::vectorize...);
14891496
static_assert(NVectorized >= 1,

tests/CMakeLists.txt

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools")
3030

3131
option(PYBIND11_WERROR "Report all warnings as errors" OFF)
3232
option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF)
33+
option(PYBIND11_CUDA_TESTS "Enable building CUDA tests (requires CMake 3.12+)" OFF)
3334
set(PYBIND11_TEST_OVERRIDE
3435
""
3536
CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests")
@@ -49,6 +50,14 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
4950
"RelWithDebInfo")
5051
endif()
5152

53+
if(PYBIND11_CUDA_TESTS)
54+
enable_language(CUDA)
55+
if(DEFINED CMAKE_CXX_STANDARD)
56+
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD})
57+
endif()
58+
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
59+
endif()
60+
5261
# Full set of test files (you can override these; see below)
5362
set(PYBIND11_TEST_FILES
5463
test_async.cpp
@@ -104,6 +113,16 @@ if((PYBIND11_TEST_FILES_ASYNC_I GREATER -1) AND (PYTHON_VERSION VERSION_LESS 3.5
104113
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_ASYNC_I})
105114
endif()
106115

116+
# Skip tests for CUDA check:
117+
# /pybind11/tests/test_constants_and_functions.cpp(125):
118+
# error: incompatible exception specifications
119+
list(FIND PYBIND11_TEST_FILES test_constants_and_functions.cpp PYBIND11_TEST_FILES_CAF_I)
120+
if((PYBIND11_TEST_FILES_CAF_I GREATER -1) AND PYBIND11_CUDA_TESTS)
121+
message(
122+
STATUS "Skipping test_constants_and_functions due to incompatible exception specifications")
123+
list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_CAF_I})
124+
endif()
125+
107126
string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}")
108127

109128
# Contains the set of test files that require pybind11_cross_module_tests to be
@@ -195,14 +214,16 @@ endif()
195214
function(pybind11_enable_warnings target_name)
196215
if(MSVC)
197216
target_compile_options(${target_name} PRIVATE /W4)
198-
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)")
217+
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)" AND NOT PYBIND11_CUDA_TESTS)
199218
target_compile_options(${target_name} PRIVATE -Wall -Wextra -Wconversion -Wcast-qual
200219
-Wdeprecated)
201220
endif()
202221

203222
if(PYBIND11_WERROR)
204223
if(MSVC)
205224
target_compile_options(${target_name} PRIVATE /WX)
225+
elseif(PYBIND11_CUDA_TESTS)
226+
target_compile_options(${target_name} PRIVATE "SHELL:-Werror all-warnings")
206227
elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)")
207228
target_compile_options(${target_name} PRIVATE -Werror)
208229
endif()
@@ -239,12 +260,22 @@ foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS})
239260
endif()
240261
endforeach()
241262

263+
# Support CUDA testing by forcing the target file to compile with NVCC
264+
if(PYBIND11_CUDA_TESTS)
265+
set_property(SOURCE ${PYBIND11_TEST_FILES} PROPERTY LANGUAGE CUDA)
266+
endif()
267+
242268
foreach(target ${test_targets})
243269
set(test_files ${PYBIND11_TEST_FILES})
244270
if(NOT "${target}" STREQUAL "pybind11_tests")
245271
set(test_files "")
246272
endif()
247273

274+
# Support CUDA testing by forcing the target file to compile with NVCC
275+
if(PYBIND11_CUDA_TESTS)
276+
set_property(SOURCE ${target}.cpp PROPERTY LANGUAGE CUDA)
277+
endif()
278+
248279
# Create the binding library
249280
pybind11_add_module(${target} THIN_LTO ${target}.cpp ${test_files} ${PYBIND11_HEADERS})
250281
pybind11_enable_warnings(${target})
@@ -354,8 +385,10 @@ add_custom_command(
354385
$<TARGET_FILE:pybind11_tests>
355386
${CMAKE_CURRENT_BINARY_DIR}/sosize-$<TARGET_FILE_NAME:pybind11_tests>.txt)
356387

357-
# Test embedding the interpreter. Provides the `cpptest` target.
358-
add_subdirectory(test_embed)
388+
if(NOT PYBIND11_CUDA_TESTS)
389+
# Test embedding the interpreter. Provides the `cpptest` target.
390+
add_subdirectory(test_embed)
359391

360-
# Test CMake build using functions and targets from subdirectory or installed location
361-
add_subdirectory(test_cmake_build)
392+
# Test CMake build using functions and targets from subdirectory or installed location
393+
add_subdirectory(test_cmake_build)
394+
endif()

tests/test_constants_and_functions.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# -*- coding: utf-8 -*-
2-
from pybind11_tests import constants_and_functions as m
2+
import pytest
3+
4+
m = pytest.importorskip("pybind11_tests.constants_and_functions")
35

46

57
def test_constants():

tests/test_copy_move.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,20 @@ TEST_SUBMODULE(copy_move_policies, m) {
175175
m.attr("has_optional") = false;
176176
#endif
177177

178-
// #70 compilation issue if operator new is not public
178+
// #70 compilation issue if operator new is not public - simple body added
179+
// but not needed on most compilers; MSVC and nvcc don't like a local
180+
// struct not having a method defined when declared, since it can not be
181+
// added later.
179182
struct PrivateOpNew {
180183
int value = 1;
181184
private:
182-
#if defined(_MSC_VER)
183-
# pragma warning(disable: 4822) // warning C4822: local class member function does not have a body
184-
#endif
185-
void *operator new(size_t bytes);
185+
void *operator new(size_t bytes) {
186+
void *ptr = std::malloc(bytes);
187+
if (ptr)
188+
return ptr;
189+
else
190+
throw std::bad_alloc{};
191+
}
186192
};
187193
py::class_<PrivateOpNew>(m, "PrivateOpNew").def_readonly("value", &PrivateOpNew::value);
188194
m.def("private_op_new_value", []() { return PrivateOpNew(); });

tests/test_virtual_functions.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class NCVirt {
139139
std::string print_movable(int a, int b) { return get_movable(a, b).get_value(); }
140140
};
141141
class NCVirtTrampoline : public NCVirt {
142-
#if !defined(__INTEL_COMPILER)
142+
#if !defined(__INTEL_COMPILER) && !defined(__CUDACC__)
143143
NonCopyable get_noncopyable(int a, int b) override {
144144
PYBIND11_OVERLOAD(NonCopyable, NCVirt, get_noncopyable, a, b);
145145
}
@@ -205,7 +205,7 @@ TEST_SUBMODULE(virtual_functions, m) {
205205
.def(py::init<int, int>());
206206

207207
// test_move_support
208-
#if !defined(__INTEL_COMPILER)
208+
#if !defined(__INTEL_COMPILER) && !defined(__CUDACC__)
209209
py::class_<NCVirt, NCVirtTrampoline>(m, "NCVirt")
210210
.def(py::init<>())
211211
.def("get_noncopyable", &NCVirt::get_noncopyable)

0 commit comments

Comments
 (0)