diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 49c096260b73..a3ae1997edf6 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -285,6 +285,22 @@ environment variables. You can set the argument `threading` to `single` to use boost libraries that have been compiled for single-threaded use instead. +## CUDA + +*(added 0.53.0)* + +Enables compiling and linking against the CUDA Toolkit. The `version` +and `modules` keywords may be passed to request the use of a specific +CUDA Toolkit version and/or additional CUDA libraries, correspondingly: + +```meson +dep = dependency('cuda', version : '>=10', modules : ['cublas']) +``` + +Note that explicitly adding this dependency is only necessary if you are +using CUDA Toolkit from a C/C++ file or project, or if you are utilizing +additional toolkit libraries that need to be explicitly linked to. + ## CUPS `method` may be `auto`, `config-tool`, `pkg-config`, `cmake` or `extraframework`. diff --git a/docs/markdown/snippets/cuda_dependency.md b/docs/markdown/snippets/cuda_dependency.md new file mode 100644 index 000000000000..1cc9ae60feff --- /dev/null +++ b/docs/markdown/snippets/cuda_dependency.md @@ -0,0 +1,11 @@ +## CUDA dependency + +Native support for compiling and linking against the CUDA Toolkit using +the `dependency` function: + +```meson +project('CUDA test', 'cpp', meson_version: '>= 0.53.0') +exe = executable('prog', 'prog.cc', dependencies: dependency('cuda')) +``` + +See [the CUDA dependency](Dependencies.md#cuda) for more information. diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py index a484e7ff7bd2..088a7e73c832 100644 --- a/mesonbuild/compilers/cuda.py +++ b/mesonbuild/compilers/cuda.py @@ -18,7 +18,7 @@ from .. import coredata from .. import mlog -from ..mesonlib import EnvironmentException, MachineChoice, Popen_safe, OptionOverrideProxy, is_windows +from ..mesonlib import EnvironmentException, MachineChoice, Popen_safe, OptionOverrideProxy, is_windows, LibType from .compilers import (Compiler, cuda_buildtype_args, cuda_optimization_args, cuda_debug_args) @@ -294,6 +294,9 @@ def get_output_args(self, target: str) -> typing.List[str]: def get_std_exe_link_args(self) -> typing.List[str]: return self._cook_link_args(self.host_compiler.get_std_exe_link_args()) + def find_library(self, libname, env, extra_dirs, libtype: LibType = LibType.PREFER_SHARED): + return ['-l' + libname] # FIXME + def get_crt_compile_args(self, crt_val, buildtype): return self._to_host_flags(self.host_compiler.get_crt_compile_args(crt_val, buildtype)) diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index 631451c37d1b..bbd3f8ccf407 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -13,6 +13,7 @@ # limitations under the License. from .boost import BoostDependency +from .cuda import CudaDependency from .base import ( # noqa: F401 Dependency, DependencyException, DependencyMethods, ExternalProgram, EmptyExternalProgram, NonExistingExternalProgram, ExternalDependency, NotFoundDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency, @@ -30,9 +31,11 @@ 'llvm': LLVMDependency, 'valgrind': ValgrindDependency, + 'boost': BoostDependency, + 'cuda': CudaDependency, + # From misc: 'blocks': BlocksDependency, - 'boost': BoostDependency, 'coarray': CoarrayDependency, 'mpi': MPIDependency, 'hdf5': HDF5Dependency, diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 3bdc41328592..201b53ff8ca5 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -2134,6 +2134,7 @@ def get_dep_identifier(name, kwargs) -> Tuple: display_name_map = { 'boost': 'Boost', + 'cuda': 'CUDA', 'dub': 'DUB', 'gmock': 'GMock', 'gtest': 'GTest', diff --git a/mesonbuild/dependencies/cuda.py b/mesonbuild/dependencies/cuda.py new file mode 100644 index 000000000000..67b361f3ef0a --- /dev/null +++ b/mesonbuild/dependencies/cuda.py @@ -0,0 +1,253 @@ +# Copyright 2013-2019 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import glob +import re +import os + +from .. import mlog +from .. import mesonlib +from ..environment import detect_cpu_family + +from .base import (DependencyException, ExternalDependency, HasNativeKwarg) + + +class CudaDependency(ExternalDependency): + + supported_languages = ['cuda', 'cpp', 'c'] # see also _default_language + + def __init__(self, environment, kwargs): + HasNativeKwarg.__init__(self, kwargs) # initialize self.for_machine + compilers = environment.coredata.compilers[self.for_machine] + language = self._detect_language(compilers) + if language not in self.supported_languages: + raise DependencyException('Language \'{}\' is not supported by the CUDA Toolkit. Supported languages are {}.'.format(language, self.supported_languages)) + + super().__init__('cuda', environment, language, kwargs) + self.requested_modules = self.get_requested(kwargs) + if 'cudart' not in self.requested_modules: + self.requested_modules = ['cudart'] + self.requested_modules + + (self.cuda_path, self.version, self.is_found) = self._detect_cuda_path_and_version() + if not self.is_found: + return + + if not os.path.isabs(self.cuda_path): + raise DependencyException('CUDA Toolkit path must be absolute, got \'{}\'.'.format(self.cuda_path)) + + # nvcc already knows where to find the CUDA Toolkit, but if we're compiling + # a mixed C/C++/CUDA project, we still need to make the include dir searchable + if self.language != 'cuda' or len(compilers) > 1: + self.incdir = os.path.join(self.cuda_path, 'include') + self.compile_args += ['-I{}'.format(self.incdir)] + + if self.language != 'cuda': + arch_libdir = self._detect_arch_libdir() + self.libdir = os.path.join(self.cuda_path, arch_libdir) + mlog.debug('CUDA library directory is', mlog.bold(self.libdir)) + else: + self.libdir = None + + self.is_found = self._find_requested_libraries() + + @classmethod + def _detect_language(cls, compilers): + for lang in cls.supported_languages: + if lang in compilers: + return lang + return list(compilers.keys())[0] + + def _detect_cuda_path_and_version(self): + self.env_var = self._default_path_env_var() + mlog.debug('Default path env var:', mlog.bold(self.env_var)) + + version_reqs = self.version_reqs + if self.language == 'cuda': + nvcc_version = self._strip_patch_version(self.get_compiler().version) + mlog.debug('nvcc version:', mlog.bold(nvcc_version)) + if version_reqs: + # make sure nvcc version satisfies specified version requirements + (found_some, not_found, found) = mesonlib.version_compare_many(nvcc_version, version_reqs) + if not_found: + msg = 'The current nvcc version {} does not satisfy the specified CUDA Toolkit version requirements {}.'.format(nvcc_version, version_reqs) + return self._report_dependency_error(msg, (None, None, False)) + + # use nvcc version to find a matching CUDA Toolkit + version_reqs = ['={}'.format(nvcc_version)] + else: + nvcc_version = None + + paths = [(path, self._cuda_toolkit_version(path), default) for (path, default) in self._cuda_paths()] + if version_reqs: + return self._find_matching_toolkit(paths, version_reqs, nvcc_version) + + defaults = [(path, version) for (path, version, default) in paths if default] + if defaults: + return (*defaults[0], True) + + platform_msg = 'set the CUDA_PATH environment variable' if self._is_windows() \ + else 'set the CUDA_PATH environment variable/create the \'/usr/local/cuda\' symbolic link' + msg = 'Please specify the desired CUDA Toolkit version (e.g. dependency(\'cuda\', version : \'>=10.1\')) or {} to point to the location of your desired version.'.format(platform_msg) + return self._report_dependency_error(msg, (None, None, False)) + + def _find_matching_toolkit(self, paths, version_reqs, nvcc_version): + # keep the default paths order intact, sort the rest in the descending order + # according to the toolkit version + defaults, rest = mesonlib.partition(lambda t: not t[2], paths) + defaults = list(defaults) + paths = defaults + sorted(rest, key=lambda t: mesonlib.Version(t[1]), reverse=True) + mlog.debug('Search paths: {}'.format(paths)) + + if nvcc_version and defaults: + default_src = "the {} environment variable".format(self.env_var) if self.env_var else "the \'/usr/local/cuda\' symbolic link" + nvcc_warning = 'The default CUDA Toolkit as designated by {} ({}) doesn\'t match the current nvcc version {} and will be ignored.'.format(default_src, os.path.realpath(defaults[0][0]), nvcc_version) + else: + nvcc_warning = None + + for (path, version, default) in paths: + (found_some, not_found, found) = mesonlib.version_compare_many(version, version_reqs) + if not not_found: + if not default and nvcc_warning: + mlog.warning(nvcc_warning) + return (path, version, True) + + if nvcc_warning: + mlog.warning(nvcc_warning) + return (None, None, False) + + def _default_path_env_var(self): + env_vars = ['CUDA_PATH'] if self._is_windows() else ['CUDA_PATH', 'CUDA_HOME', 'CUDA_ROOT'] + env_vars = [var for var in env_vars if var in os.environ] + user_defaults = set([os.environ[var] for var in env_vars]) + if len(user_defaults) > 1: + mlog.warning('Environment variables {} point to conflicting toolkit locations ({}). Toolkit selection might produce unexpected results.'.format(', '.join(env_vars), ', '.join(user_defaults))) + return env_vars[0] if env_vars else None + + def _cuda_paths(self): + return ([(os.environ[self.env_var], True)] if self.env_var else []) \ + + (self._cuda_paths_win() if self._is_windows() else self._cuda_paths_nix()) + + def _cuda_paths_win(self): + env_vars = os.environ.keys() + return [(os.environ[var], False) for var in env_vars if var.startswith('CUDA_PATH_')] + + def _cuda_paths_nix(self): + # include /usr/local/cuda default only if no env_var was found + pattern = '/usr/local/cuda-*' if self.env_var else '/usr/local/cuda*' + return [(path, os.path.basename(path) == 'cuda') for path in glob.iglob(pattern)] + + toolkit_version_regex = re.compile(r'^CUDA Version\s+(.*)$') + path_version_win_regex = re.compile(r'^v(.*)$') + path_version_nix_regex = re.compile(r'^cuda-(.*)$') + + def _cuda_toolkit_version(self, path): + version = self._read_toolkit_version_txt(path) + if version: + return version + + mlog.debug('Falling back to extracting version from path') + path_version_regex = self.path_version_win_regex if self._is_windows() else self.path_version_nix_regex + m = path_version_regex.match(os.path.basename(path)) + if m: + return m[1] + + mlog.warning('Could not detect CUDA Toolkit version for {}'.format(path)) + return '0.0' + + def _read_toolkit_version_txt(self, path): + # Read 'version.txt' at the root of the CUDA Toolkit directory to determine the tookit version + version_file_path = os.path.join(path, 'version.txt') + try: + with open(version_file_path) as version_file: + version_str = version_file.readline() # e.g. 'CUDA Version 10.1.168' + m = self.toolkit_version_regex.match(version_str) + if m: + return self._strip_patch_version(m[1]) + except Exception as e: + mlog.debug('Could not read CUDA Toolkit\'s version file {}: {}'.format(version_file_path, str(e))) + + return None + + @classmethod + def _strip_patch_version(cls, version): + return '.'.join(version.split('.')[:2]) + + def _detect_arch_libdir(self): + arch = detect_cpu_family(self.env.coredata.compilers.host) + machine = self.env.machines[self.for_machine] + msg = '{} architecture is not supported in {} version of the CUDA Toolkit.' + if machine.is_windows(): + libdirs = {'x86': 'Win32', 'x86_64': 'x64'} + if arch not in libdirs: + raise DependencyException(msg.format(arch, 'Windows')) + return os.path.join('lib', libdirs[arch]) + elif machine.is_linux(): + libdirs = {'x86_64': 'lib64', 'ppc64': 'lib'} + if arch not in libdirs: + raise DependencyException(msg.format(arch, 'Linux')) + return libdirs[arch] + elif machine.is_osx(): + libdirs = {'x86_64': 'lib64'} + if arch not in libdirs: + raise DependencyException(msg.format(arch, 'macOS')) + return libdirs[arch] + else: + raise DependencyException('CUDA Toolkit: unsupported platform.') + + def _find_requested_libraries(self): + self.lib_modules = {} + all_found = True + + for module in self.requested_modules: + args = self.clib_compiler.find_library(module, self.env, [self.libdir] if self.libdir else []) + if args is None: + self._report_dependency_error('Couldn\'t find requested CUDA module \'{}\''.format(module)) + all_found = False + else: + mlog.debug('Link args for CUDA module \'{}\' are {}'.format(module, args)) + self.lib_modules[module] = args + + return all_found + + def _is_windows(self): + return self.env.machines[self.for_machine].is_windows() + + def _report_dependency_error(self, msg, ret_val=None): + if self.required: + raise DependencyException(msg) + + mlog.debug(msg) + return ret_val + + def log_details(self): + module_str = ', '.join(self.requested_modules) + return 'modules: ' + module_str + + def log_info(self): + return self.cuda_path if self.cuda_path else '' + + def get_requested(self, kwargs): + candidates = mesonlib.extract_as_list(kwargs, 'modules') + for c in candidates: + if not isinstance(c, str): + raise DependencyException('CUDA module argument is not a string.') + return candidates + + def get_link_args(self, **kwargs): + args = [] + if self.libdir: + args += self.clib_compiler.get_linker_search_args(self.libdir) + for lib in self.requested_modules: + args += self.lib_modules[lib] + return args diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index db7ac485e4c5..d289ff2f9d59 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -21,6 +21,7 @@ import collections from enum import Enum from functools import lru_cache, update_wrapper +from itertools import tee, filterfalse import typing import uuid @@ -1051,6 +1052,12 @@ def expand_arguments(args): return None return expended_args +def partition(pred, iterable): + 'Use a predicate to partition entries into false entries and true entries' + # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) + def Popen_safe(args: typing.List[str], write: typing.Optional[str] = None, stdout: typing.Union[typing.BinaryIO, int] = subprocess.PIPE, stderr: typing.Union[typing.BinaryIO, int] = subprocess.PIPE, diff --git a/test cases/cuda/10 cuda dependency/c/meson.build b/test cases/cuda/10 cuda dependency/c/meson.build new file mode 100644 index 000000000000..921bc43a02d3 --- /dev/null +++ b/test cases/cuda/10 cuda dependency/c/meson.build @@ -0,0 +1,2 @@ +exe = executable('prog', 'prog.c', dependencies: dependency('cuda')) +test('cudatest', exe) diff --git a/test cases/cuda/10 cuda dependency/c/prog.c b/test cases/cuda/10 cuda dependency/c/prog.c new file mode 100644 index 000000000000..9d279a95f9e9 --- /dev/null +++ b/test cases/cuda/10 cuda dependency/c/prog.c @@ -0,0 +1,19 @@ +#include +#include + +int cuda_devices() { + int result = 0; + cudaGetDeviceCount(&result); + return result; +} + +int main() { + int n = cuda_devices(); + if (n == 0) { + printf("No CUDA hardware found. Exiting.\n"); + return 0; + } + + printf("Found %i CUDA devices.\n", n); + return 0; +} diff --git a/test cases/cuda/10 cuda dependency/cpp/meson.build b/test cases/cuda/10 cuda dependency/cpp/meson.build new file mode 100644 index 000000000000..a661b88754c4 --- /dev/null +++ b/test cases/cuda/10 cuda dependency/cpp/meson.build @@ -0,0 +1,2 @@ +exe = executable('prog', 'prog.cc', dependencies: dependency('cuda')) +test('cudatest', exe) diff --git a/test cases/cuda/10 cuda dependency/cpp/prog.cc b/test cases/cuda/10 cuda dependency/cpp/prog.cc new file mode 100644 index 000000000000..728debc83d2d --- /dev/null +++ b/test cases/cuda/10 cuda dependency/cpp/prog.cc @@ -0,0 +1,19 @@ +#include +#include + +int cuda_devices() { + int result = 0; + cudaGetDeviceCount(&result); + return result; +} + +int main() { + int n = cuda_devices(); + if (n == 0) { + std::cout << "No CUDA hardware found. Exiting.\n"; + return 0; + } + + std::cout << "Found " << n << " CUDA devices.\n"; + return 0; +} diff --git a/test cases/cuda/10 cuda dependency/meson.build b/test cases/cuda/10 cuda dependency/meson.build new file mode 100644 index 000000000000..3e602b6686b0 --- /dev/null +++ b/test cases/cuda/10 cuda dependency/meson.build @@ -0,0 +1,6 @@ +project('cuda dependency', 'c', 'cpp') + +subdir('c') +subdir('cpp') +subdir('modules') +subdir('version_reqs') diff --git a/test cases/cuda/10 cuda dependency/modules/meson.build b/test cases/cuda/10 cuda dependency/modules/meson.build new file mode 100644 index 000000000000..0da43f29de49 --- /dev/null +++ b/test cases/cuda/10 cuda dependency/modules/meson.build @@ -0,0 +1,2 @@ +exe = executable('prog', 'prog.cc', dependencies: dependency('cuda', modules: ['cublas'])) +test('cudatest', exe) diff --git a/test cases/cuda/10 cuda dependency/modules/prog.cc b/test cases/cuda/10 cuda dependency/modules/prog.cc new file mode 100644 index 000000000000..7c52b3fb74ad --- /dev/null +++ b/test cases/cuda/10 cuda dependency/modules/prog.cc @@ -0,0 +1,33 @@ +#include +#include +#include + +int cuda_devices() { + int result = 0; + cudaGetDeviceCount(&result); + return result; +} + +int main() { + int n = cuda_devices(); + if (n == 0) { + std::cout << "No CUDA hardware found. Exiting.\n"; + return 0; + } + + std::cout << "Found " << n << " CUDA devices.\n"; + + cublasHandle_t handle; + if (cublasCreate(&handle) != CUBLAS_STATUS_SUCCESS) { + std::cout << "cuBLAS initialization failed. Exiting.\n"; + return -1; + } + + std::cout << "Initialized cuBLAS\n"; + if (cublasDestroy(handle) != CUBLAS_STATUS_SUCCESS) { + std::cout << "cuBLAS de-initialization failed. Exiting.\n"; + return -1; + } + + return 0; +} diff --git a/test cases/cuda/10 cuda dependency/version_reqs/meson.build b/test cases/cuda/10 cuda dependency/version_reqs/meson.build new file mode 100644 index 000000000000..45b5daa1cfa5 --- /dev/null +++ b/test cases/cuda/10 cuda dependency/version_reqs/meson.build @@ -0,0 +1,2 @@ +exe = executable('prog', 'prog.cc', dependencies: dependency('cuda', version: ['>=8.5', '<10'], required: false, disabler: true)) +test('cudatest', exe) diff --git a/test cases/cuda/10 cuda dependency/version_reqs/prog.cc b/test cases/cuda/10 cuda dependency/version_reqs/prog.cc new file mode 100644 index 000000000000..e3adabfde4d3 --- /dev/null +++ b/test cases/cuda/10 cuda dependency/version_reqs/prog.cc @@ -0,0 +1,28 @@ +#include +#include + +int cuda_devices() { + int result = 0; + cudaGetDeviceCount(&result); + return result; +} + +int main() { + std::cout << "Compiled against CUDA version: " << CUDART_VERSION << "\n"; + int runtime_version = 0; + cudaError_t r = cudaRuntimeGetVersion(&runtime_version); + if (r != cudaSuccess) { + std::cout << "Couldn't obtain CUDA runtime version (error " << r << "). Exiting.\n"; + return -1; + } + std::cout << "CUDA runtime version: " << runtime_version << "\n"; + + int n = cuda_devices(); + if (n == 0) { + std::cout << "No CUDA hardware found. Exiting.\n"; + return 0; + } + + std::cout << "Found " << n << " CUDA devices.\n"; + return 0; +} diff --git a/test cases/cuda/11 cuda dependency (nvcc)/meson.build b/test cases/cuda/11 cuda dependency (nvcc)/meson.build new file mode 100644 index 000000000000..67b6568c5afd --- /dev/null +++ b/test cases/cuda/11 cuda dependency (nvcc)/meson.build @@ -0,0 +1,4 @@ +project('cuda dependency', 'cuda') + +subdir('modules') +subdir('version_reqs') diff --git a/test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build b/test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build new file mode 100644 index 000000000000..c0fed8349d16 --- /dev/null +++ b/test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build @@ -0,0 +1,2 @@ +exe = executable('prog', 'prog.cu', dependencies: dependency('cuda', modules: ['cublas'])) +test('cudatest', exe) diff --git a/test cases/cuda/11 cuda dependency (nvcc)/modules/prog.cu b/test cases/cuda/11 cuda dependency (nvcc)/modules/prog.cu new file mode 100644 index 000000000000..7c52b3fb74ad --- /dev/null +++ b/test cases/cuda/11 cuda dependency (nvcc)/modules/prog.cu @@ -0,0 +1,33 @@ +#include +#include +#include + +int cuda_devices() { + int result = 0; + cudaGetDeviceCount(&result); + return result; +} + +int main() { + int n = cuda_devices(); + if (n == 0) { + std::cout << "No CUDA hardware found. Exiting.\n"; + return 0; + } + + std::cout << "Found " << n << " CUDA devices.\n"; + + cublasHandle_t handle; + if (cublasCreate(&handle) != CUBLAS_STATUS_SUCCESS) { + std::cout << "cuBLAS initialization failed. Exiting.\n"; + return -1; + } + + std::cout << "Initialized cuBLAS\n"; + if (cublasDestroy(handle) != CUBLAS_STATUS_SUCCESS) { + std::cout << "cuBLAS de-initialization failed. Exiting.\n"; + return -1; + } + + return 0; +} diff --git a/test cases/cuda/11 cuda dependency (nvcc)/version_reqs/meson.build b/test cases/cuda/11 cuda dependency (nvcc)/version_reqs/meson.build new file mode 100644 index 000000000000..6644c9e2b0e5 --- /dev/null +++ b/test cases/cuda/11 cuda dependency (nvcc)/version_reqs/meson.build @@ -0,0 +1,2 @@ +exe = executable('prog', 'prog.cu', dependencies: dependency('cuda', version: ['>=10.1'], required: false, disabler: true)) +test('cudatest', exe) diff --git a/test cases/cuda/11 cuda dependency (nvcc)/version_reqs/prog.cu b/test cases/cuda/11 cuda dependency (nvcc)/version_reqs/prog.cu new file mode 100644 index 000000000000..e3adabfde4d3 --- /dev/null +++ b/test cases/cuda/11 cuda dependency (nvcc)/version_reqs/prog.cu @@ -0,0 +1,28 @@ +#include +#include + +int cuda_devices() { + int result = 0; + cudaGetDeviceCount(&result); + return result; +} + +int main() { + std::cout << "Compiled against CUDA version: " << CUDART_VERSION << "\n"; + int runtime_version = 0; + cudaError_t r = cudaRuntimeGetVersion(&runtime_version); + if (r != cudaSuccess) { + std::cout << "Couldn't obtain CUDA runtime version (error " << r << "). Exiting.\n"; + return -1; + } + std::cout << "CUDA runtime version: " << runtime_version << "\n"; + + int n = cuda_devices(); + if (n == 0) { + std::cout << "No CUDA hardware found. Exiting.\n"; + return 0; + } + + std::cout << "Found " << n << " CUDA devices.\n"; + return 0; +} diff --git a/test cases/cuda/12 cuda dependency (mixed)/kernel.cu b/test cases/cuda/12 cuda dependency (mixed)/kernel.cu new file mode 100644 index 000000000000..a7490b59e804 --- /dev/null +++ b/test cases/cuda/12 cuda dependency (mixed)/kernel.cu @@ -0,0 +1,8 @@ +#include + +__global__ void kernel (void){ +} + +void do_cuda_stuff() { + kernel<<<1,1>>>(); +} diff --git a/test cases/cuda/12 cuda dependency (mixed)/meson.build b/test cases/cuda/12 cuda dependency (mixed)/meson.build new file mode 100644 index 000000000000..5df4f846e44d --- /dev/null +++ b/test cases/cuda/12 cuda dependency (mixed)/meson.build @@ -0,0 +1,4 @@ +project('cuda dependency', 'cpp', 'cuda') + +exe = executable('prog', 'prog.cpp', 'kernel.cu', dependencies: dependency('cuda', modules: ['cublas'])) +test('cudatest', exe) diff --git a/test cases/cuda/12 cuda dependency (mixed)/prog.cpp b/test cases/cuda/12 cuda dependency (mixed)/prog.cpp new file mode 100644 index 000000000000..daf910207414 --- /dev/null +++ b/test cases/cuda/12 cuda dependency (mixed)/prog.cpp @@ -0,0 +1,37 @@ +#include +#include +#include + +void do_cuda_stuff(); + +int cuda_devices() { + int result = 0; + cudaGetDeviceCount(&result); + return result; +} + +int main() { + int n = cuda_devices(); + if (n == 0) { + std::cout << "No CUDA hardware found. Exiting.\n"; + return 0; + } + + std::cout << "Found " << n << " CUDA devices.\n"; + + do_cuda_stuff(); + + cublasHandle_t handle; + if (cublasCreate(&handle) != CUBLAS_STATUS_SUCCESS) { + std::cout << "cuBLAS initialization failed. Exiting.\n"; + return -1; + } + + std::cout << "Initialized cuBLAS\n"; + if (cublasDestroy(handle) != CUBLAS_STATUS_SUCCESS) { + std::cout << "cuBLAS de-initialization failed. Exiting.\n"; + return -1; + } + + return 0; +}