aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Dependencies.md16
-rw-r--r--docs/markdown/snippets/cuda_dependency.md11
-rw-r--r--mesonbuild/compilers/cuda.py5
-rw-r--r--mesonbuild/dependencies/__init__.py5
-rw-r--r--mesonbuild/dependencies/base.py1
-rw-r--r--mesonbuild/dependencies/cuda.py253
-rw-r--r--mesonbuild/mesonlib.py7
-rw-r--r--test cases/cuda/10 cuda dependency/c/meson.build2
-rw-r--r--test cases/cuda/10 cuda dependency/c/prog.c19
-rw-r--r--test cases/cuda/10 cuda dependency/cpp/meson.build2
-rw-r--r--test cases/cuda/10 cuda dependency/cpp/prog.cc19
-rw-r--r--test cases/cuda/10 cuda dependency/meson.build6
-rw-r--r--test cases/cuda/10 cuda dependency/modules/meson.build2
-rw-r--r--test cases/cuda/10 cuda dependency/modules/prog.cc33
-rw-r--r--test cases/cuda/10 cuda dependency/version_reqs/meson.build2
-rw-r--r--test cases/cuda/10 cuda dependency/version_reqs/prog.cc28
-rw-r--r--test cases/cuda/11 cuda dependency (nvcc)/meson.build4
-rw-r--r--test cases/cuda/11 cuda dependency (nvcc)/modules/meson.build2
-rw-r--r--test cases/cuda/11 cuda dependency (nvcc)/modules/prog.cu33
-rw-r--r--test cases/cuda/11 cuda dependency (nvcc)/version_reqs/meson.build2
-rw-r--r--test cases/cuda/11 cuda dependency (nvcc)/version_reqs/prog.cu28
-rw-r--r--test cases/cuda/12 cuda dependency (mixed)/kernel.cu8
-rw-r--r--test cases/cuda/12 cuda dependency (mixed)/meson.build4
-rw-r--r--test cases/cuda/12 cuda dependency (mixed)/prog.cpp37
24 files changed, 527 insertions, 2 deletions
diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md
index 49c0962..a3ae199 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 0000000..1cc9ae6
--- /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 a484e7f..088a7e7 100644
--- a/mesonbuild/compilers/cuda.py
+++ b/mesonbuild/compilers/cuda.py
@@ -18,7 +18,7 @@ from functools import partial
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 @@ class CudaCompiler(Compiler):
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 631451c..bbd3f8c 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 @@ packages.update({
'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 3bdc413..201b53f 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 0000000..67b361f
--- /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 db7ac48..d289ff2 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -21,6 +21,7 @@ import platform, subprocess, operator, os, shlex, shutil, re
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 0000000..921bc43
--- /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 0000000..9d279a9
--- /dev/null
+++ b/test cases/cuda/10 cuda dependency/c/prog.c
@@ -0,0 +1,19 @@
+#include <cuda_runtime.h>
+#include <stdio.h>
+
+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 0000000..a661b88
--- /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 0000000..728debc
--- /dev/null
+++ b/test cases/cuda/10 cuda dependency/cpp/prog.cc
@@ -0,0 +1,19 @@
+#include <cuda_runtime.h>
+#include <iostream>
+
+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 0000000..3e602b6
--- /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 0000000..0da43f2
--- /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 0000000..7c52b3f
--- /dev/null
+++ b/test cases/cuda/10 cuda dependency/modules/prog.cc
@@ -0,0 +1,33 @@
+#include <cuda_runtime.h>
+#include <cublas_v2.h>
+#include <iostream>
+
+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 0000000..45b5daa
--- /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 0000000..e3adabf
--- /dev/null
+++ b/test cases/cuda/10 cuda dependency/version_reqs/prog.cc
@@ -0,0 +1,28 @@
+#include <cuda_runtime.h>
+#include <iostream>
+
+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 0000000..67b6568
--- /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 0000000..c0fed83
--- /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 0000000..7c52b3f
--- /dev/null
+++ b/test cases/cuda/11 cuda dependency (nvcc)/modules/prog.cu
@@ -0,0 +1,33 @@
+#include <cuda_runtime.h>
+#include <cublas_v2.h>
+#include <iostream>
+
+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 0000000..6644c9e
--- /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 0000000..e3adabf
--- /dev/null
+++ b/test cases/cuda/11 cuda dependency (nvcc)/version_reqs/prog.cu
@@ -0,0 +1,28 @@
+#include <cuda_runtime.h>
+#include <iostream>
+
+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 0000000..a7490b5
--- /dev/null
+++ b/test cases/cuda/12 cuda dependency (mixed)/kernel.cu
@@ -0,0 +1,8 @@
+#include <cuda_runtime.h>
+
+__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 0000000..5df4f84
--- /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 0000000..daf9102
--- /dev/null
+++ b/test cases/cuda/12 cuda dependency (mixed)/prog.cpp
@@ -0,0 +1,37 @@
+#include <cuda_runtime.h>
+#include <cublas_v2.h>
+#include <iostream>
+
+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;
+}