aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/External-commands.md8
-rw-r--r--docs/markdown/Reference-manual.md6
-rw-r--r--docs/markdown/i18n-module.md1
-rw-r--r--docs/markdown/snippets/cuda.md7
-rw-r--r--docs/markdown/snippets/introspect_breaking_format.md2
-rw-r--r--docs/markdown/snippets/run_command_env.md9
-rw-r--r--mesonbuild/backend/vs2010backend.py3
-rw-r--r--mesonbuild/compilers/__init__.py2
-rw-r--r--mesonbuild/compilers/compilers.py22
-rw-r--r--mesonbuild/compilers/cuda.py202
-rw-r--r--mesonbuild/coredata.py7
-rw-r--r--mesonbuild/dependencies/base.py4
-rw-r--r--mesonbuild/dependencies/data/CMakeLists.txt29
-rw-r--r--mesonbuild/dependencies/dev.py3
-rw-r--r--mesonbuild/environment.py44
-rw-r--r--mesonbuild/interpreter.py50
-rw-r--r--mesonbuild/interpreterbase.py43
-rw-r--r--mesonbuild/mesonlib.py4
-rw-r--r--mesonbuild/modules/i18n.py6
-rwxr-xr-xrun_project_tests.py1
-rwxr-xr-xrun_unittests.py58
-rw-r--r--test cases/common/36 run program/meson.build6
-rw-r--r--test cases/cuda/1 simple/meson.build5
-rw-r--r--test cases/cuda/1 simple/prog.cu30
-rw-r--r--test cases/cuda/2 split/lib.cu13
-rw-r--r--test cases/cuda/2 split/main.cpp7
-rw-r--r--test cases/cuda/2 split/meson.build7
-rw-r--r--test cases/cuda/2 split/static/lib.cu13
-rw-r--r--test cases/cuda/2 split/static/libsta.cu13
-rw-r--r--test cases/cuda/2 split/static/main_static.cpp7
-rw-r--r--test cases/cuda/2 split/static/meson.build4
31 files changed, 550 insertions, 66 deletions
diff --git a/docs/markdown/External-commands.md b/docs/markdown/External-commands.md
index 9336ec3..4c8c8e4 100644
--- a/docs/markdown/External-commands.md
+++ b/docs/markdown/External-commands.md
@@ -16,6 +16,14 @@ output = r.stdout().strip()
errortxt = r.stderr().strip()
```
+Additionally, since 0.50.0, you can pass the command [`environment`](Reference-manual.html#environment-object) object:
+
+```meson
+env = environment()
+env.set('FOO', 'bar')
+run_command('command', 'arg1', 'arg2', env: env)
+```
+
The `run_command` function returns an object that can be queried for
return value and text written to stdout and stderr. The `strip` method
call is used to strip trailing and leading whitespace from
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index e913e25..db43813 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -1183,12 +1183,14 @@ and Meson will set three environment variables `MESON_SOURCE_ROOT`,
directory, build directory and subdirectory the target was defined in,
respectively.
-This function has one keyword argument.
+This function supports the following keyword arguments:
- `check` takes a boolean. If `true`, the exit status code of the command will
be checked, and the configuration will fail if it is non-zero. The default is
`false`.
Since 0.47.0
+ - `env` an [environment object](#environment-object) to use a custom environment
+ Since 0.50.0
See also [External commands](External-commands.md).
@@ -2175,7 +2177,7 @@ and has the following methods:
This object is returned by [`environment()`](#environment) and stores
detailed information about how environment variables should be set
during tests. It should be passed as the `env` keyword argument to
-tests. It has the following methods.
+tests and other functions. It has the following methods.
- `append(varname, value1, value2, ...)` appends the given values to
the old value of the environment variable, e.g. `env.append('FOO',
diff --git a/docs/markdown/i18n-module.md b/docs/markdown/i18n-module.md
index 88f059b..9053edc 100644
--- a/docs/markdown/i18n-module.md
+++ b/docs/markdown/i18n-module.md
@@ -29,6 +29,7 @@ argument which is the name of the gettext module.
[source](https://github.com/mesonbuild/meson/blob/master/mesonbuild/modules/i18n.py)
for for their value
* `install`: (*Added 0.43.0*) if false, do not install the built translations.
+* `install_dir`: (*Added 0.50.0*) override default install location, default is `localedir`
This function also defines targets for maintainers to use:
**Note**: These output to the source directory
diff --git a/docs/markdown/snippets/cuda.md b/docs/markdown/snippets/cuda.md
new file mode 100644
index 0000000..a4a92cd
--- /dev/null
+++ b/docs/markdown/snippets/cuda.md
@@ -0,0 +1,7 @@
+## Cuda support
+
+Compiling Cuda source code is now supported, though only with the
+Ninja backend. This has been tested only on Linux for now.
+
+Because NVidia's Cuda compiler does not produce `.d` dependency files,
+dependency tracking does not work.
diff --git a/docs/markdown/snippets/introspect_breaking_format.md b/docs/markdown/snippets/introspect_breaking_format.md
index c96c82c..a0fa29a 100644
--- a/docs/markdown/snippets/introspect_breaking_format.md
+++ b/docs/markdown/snippets/introspect_breaking_format.md
@@ -7,5 +7,5 @@ affects the `filename` key in the targets introspection and the output of
Furthermore, the `filename` and `install_filename` keys in the targets
introspection are now lists of strings with identical length.
-The `--traget-files` option is now deprecated, since the same information
+The `--target-files` option is now deprecated, since the same information
can be acquired from the `--tragets` introspection API.
diff --git a/docs/markdown/snippets/run_command_env.md b/docs/markdown/snippets/run_command_env.md
new file mode 100644
index 0000000..dfa5ac5
--- /dev/null
+++ b/docs/markdown/snippets/run_command_env.md
@@ -0,0 +1,9 @@
+## `run_command` accepts `env` kwarg
+
+You can pass [`environment`](Reference-manual.html#environment-object) object to [`run_command`](Reference-manual.html#run-command), just like to `test`:
+
+```meson
+env = environment()
+env.set('FOO', 'bar')
+run_command('command', 'arg1', 'arg2', env: env)
+```
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 939f7b4..4ae41f8 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -509,6 +509,9 @@ class Vs2010Backend(backends.Backend):
elif isinstance(i, File):
relfname = i.rel_to_builddir(self.build_to_src)
cmd.append(os.path.join(self.environment.get_build_dir(), relfname))
+ elif isinstance(i, str):
+ # Escape embedded quotes, because we quote the entire argument below.
+ cmd.append(i.replace('"', '\\"'))
else:
cmd.append(i)
cmd_templ = '''"%s" ''' * len(cmd)
diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py
index c568a98..60cca93 100644
--- a/mesonbuild/compilers/__init__.py
+++ b/mesonbuild/compilers/__init__.py
@@ -72,6 +72,7 @@ __all__ = [
'JavaCompiler',
'LLVMDCompiler',
'MonoCompiler',
+ 'NvidiaCudaCompiler',
'VisualStudioCsCompiler',
'NAGFortranCompiler',
'ObjCCompiler',
@@ -153,6 +154,7 @@ from .d import (
GnuDCompiler,
LLVMDCompiler,
)
+from .cuda import CudaCompiler
from .fortran import (
FortranCompiler,
G95FortranCompiler,
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index 016e704..b1f3cc2 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -37,6 +37,7 @@ lib_suffixes = ('a', 'lib', 'dll', 'dylib', 'so')
lang_suffixes = {
'c': ('c',),
'cpp': ('cpp', 'cc', 'cxx', 'c++', 'hh', 'hpp', 'ipp', 'hxx'),
+ 'cuda': ('cu',),
# f90, f95, f03, f08 are for free-form fortran ('f90' recommended)
# f, for, ftn, fpp are for fixed-form fortran ('f' or 'for' recommended)
'fortran': ('f90', 'f95', 'f03', 'f08', 'f', 'for', 'ftn', 'fpp'),
@@ -58,7 +59,7 @@ clib_langs = ('objcpp', 'cpp', 'objc', 'c', 'fortran',)
# List of languages that can be linked with C code directly by the linker
# used in build.py:process_compilers() and build.py:get_dynamic_linker()
# XXX: Add Rust to this?
-clink_langs = ('d',) + clib_langs
+clink_langs = ('d', 'cuda') + clib_langs
clink_suffixes = ()
for _l in clink_langs + ('vala',):
clink_suffixes += lang_suffixes[_l]
@@ -69,6 +70,7 @@ soregex = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$')
# Environment variables that each lang uses.
cflags_mapping = {'c': 'CFLAGS',
'cpp': 'CXXFLAGS',
+ 'cuda': 'CUFLAGS',
'objc': 'OBJCFLAGS',
'objcpp': 'OBJCXXFLAGS',
'fortran': 'FFLAGS',
@@ -143,6 +145,13 @@ armclang_buildtype_args = {'plain': [],
'custom': [],
}
+cuda_buildtype_args = {'plain': [],
+ 'debug': [],
+ 'debugoptimized': [],
+ 'release': [],
+ 'minsize': [],
+ }
+
arm_buildtype_args = {'plain': [],
'debug': ['-O0', '--debug'],
'debugoptimized': ['-O1', '--debug'],
@@ -345,6 +354,17 @@ msvc_optimization_args = {'0': [],
's': ['/O1'], # Implies /Os.
}
+cuda_optimization_args = {'0': [],
+ 'g': ['-O0'],
+ '1': ['-O1'],
+ '2': ['-O2'],
+ '3': ['-O3', '-Otime'],
+ 's': ['-O3', '-Ospace']
+ }
+
+cuda_debug_args = {False: [],
+ True: ['-g']}
+
clike_debug_args = {False: [],
True: ['-g']}
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
new file mode 100644
index 0000000..b5f16ab
--- /dev/null
+++ b/mesonbuild/compilers/cuda.py
@@ -0,0 +1,202 @@
+# Copyright 2012-2017 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 subprocess, os.path
+
+from .. import mlog
+from ..mesonlib import EnvironmentException, Popen_safe
+from .compilers import Compiler, cuda_buildtype_args, cuda_optimization_args, cuda_debug_args
+
+class CudaCompiler(Compiler):
+ def __init__(self, exelist, version, is_cross, exe_wrapper=None):
+ if not hasattr(self, 'language'):
+ self.language = 'cuda'
+ super().__init__(exelist, version)
+ self.is_cross = is_cross
+ self.exe_wrapper = exe_wrapper
+ self.id = 'nvcc'
+ default_warn_args = []
+ self.warn_args = {'1': default_warn_args,
+ '2': default_warn_args + ['-Wextra'],
+ '3': default_warn_args + ['-Wextra', '-Wpedantic']}
+
+ def needs_static_linker(self):
+ return False
+
+ def get_display_language(self):
+ return 'Cuda'
+
+ def get_no_stdinc_args(self):
+ return []
+
+ def sanity_check(self, work_dir, environment):
+ source_name = os.path.join(work_dir, 'sanitycheckcuda.cu')
+ binary_name = os.path.join(work_dir, 'sanitycheckcuda')
+ extra_flags = self.get_cross_extra_flags(environment, link=False)
+ if self.is_cross:
+ extra_flags += self.get_compile_only_args()
+
+ code = '''
+__global__ void kernel (void) {
+
+}
+
+ int main(int argc,char** argv){
+ return 0;
+ }
+ '''
+
+ with open(source_name, 'w') as ofile:
+ ofile.write(code)
+ pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name])
+ pc.wait()
+ if pc.returncode != 0:
+ raise EnvironmentException('Cuda compiler %s can not compile programs.' % self.name_string())
+ if self.is_cross:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ pe = subprocess.Popen(binary_name)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by Cuda compiler %s are not runnable.' % self.name_string())
+
+ def get_compiler_check_args(self):
+ return super().get_compiler_check_args() + []
+
+ def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None):
+ if super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies):
+ return True
+ if extra_args is None:
+ extra_args = []
+ fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
+ t = '''{prefix}
+ #include <{header}>
+ using {symbol};
+ int main () {{ return 0; }}'''
+ return self.compiles(t.format(**fargs), env, extra_args, dependencies)
+
+ def sanity_check_impl(self, work_dir, environment, sname, code):
+ mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist))
+ mlog.debug('Is cross compiler: %s.' % str(self.is_cross))
+
+ extra_flags = []
+ source_name = os.path.join(work_dir, sname)
+ binname = sname.rsplit('.', 1)[0]
+ if self.is_cross:
+ binname += '_cross'
+ if self.exe_wrapper is None:
+ # Linking cross built apps is painful. You can't really
+ # tell if you should use -nostdlib or not and for example
+ # on OSX the compiler binary is the same but you need
+ # a ton of compiler flags to differentiate between
+ # arm and x86_64. So just compile.
+ extra_flags += self.get_cross_extra_flags(environment, link=False)
+ extra_flags += self.get_compile_only_args()
+ else:
+ extra_flags += self.get_cross_extra_flags(environment, link=True)
+ # Is a valid executable output for all toolchains and platforms
+ binname += '.exe'
+ # Write binary check source
+ binary_name = os.path.join(work_dir, binname)
+ with open(source_name, 'w') as ofile:
+ ofile.write(code)
+ # Compile sanity check
+ cmdlist = self.exelist + extra_flags + [source_name] + self.get_output_args(binary_name)
+ pc, stdo, stde = Popen_safe(cmdlist, cwd=work_dir)
+ mlog.debug('Sanity check compiler command line:', ' '.join(cmdlist))
+ mlog.debug('Sanity check compile stdout:')
+ mlog.debug(stdo)
+ mlog.debug('-----\nSanity check compile stderr:')
+ mlog.debug(stde)
+ mlog.debug('-----')
+ if pc.returncode != 0:
+ raise EnvironmentException('Compiler {0} can not compile programs.'.format(self.name_string()))
+ # Run sanity check
+ if self.is_cross:
+ if self.exe_wrapper is None:
+ # Can't check if the binaries run so we have to assume they do
+ return
+ cmdlist = self.exe_wrapper + [binary_name]
+ else:
+ cmdlist = [binary_name]
+ mlog.debug('Running test binary command: ' + ' '.join(cmdlist))
+ pe = subprocess.Popen(cmdlist)
+ pe.wait()
+ if pe.returncode != 0:
+ raise EnvironmentException('Executables created by {0} compiler {1} are not runnable.'.format(self.language, self.name_string()))
+
+ def get_output_args(self, target):
+ return ['-o', target]
+
+ def name_string(self):
+ return ' '.join(self.exelist)
+
+ def get_dependency_gen_args(self, outtarget, outfile):
+ return []
+
+ def get_compile_only_args(self):
+ return ['-c']
+
+ def get_no_optimization_args(self):
+ return ['-O0']
+
+ def get_optimization_args(self, optimization_level):
+ return cuda_optimization_args[optimization_level]
+
+ def get_debug_args(self, is_debug):
+ return cuda_debug_args[is_debug]
+
+ def get_linker_exelist(self):
+ return self.exelist[:]
+
+ def get_linker_output_args(self, outputname):
+ return ['-o', outputname]
+
+ def get_warn_args(self, level):
+ return self.warn_args[level]
+
+ def get_buildtype_args(self, buildtype):
+ return cuda_buildtype_args[buildtype]
+
+ def get_include_args(self, path, is_system):
+ if path == '':
+ path = '.'
+ return ['-I' + path]
+
+ def depfile_for_object(self, objfile):
+ return objfile + '.' + self.get_depfile_suffix()
+
+ def get_depfile_suffix(self):
+ return 'd'
+
+ def get_buildtype_linker_args(self, buildtype):
+ return []
+
+ def get_std_exe_link_args(self):
+ return []
+
+ def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
+ return []
+
+ def get_linker_search_args(self, dirname):
+ return ['/LIBPATH:' + dirname]
+
+ def linker_to_compiler_args(self, args):
+ return ['/link'] + args
+
+ def get_pic_args(self):
+ return []
+
+ def compute_parameters_with_absolute_paths(self, parameter_list, build_dir):
+ return []
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index c7e3689..9850722 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -643,6 +643,9 @@ def update_cmd_line_file(build_dir, options):
with open(filename, 'w') as f:
config.write(f)
+def major_versions_differ(v1, v2):
+ return v1.split('.')[0:2] != v2.split('.')[0:2]
+
def load(build_dir):
filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
load_fail_msg = 'Coredata file {!r} is corrupted. Try with a fresh build tree.'.format(filename)
@@ -658,7 +661,7 @@ def load(build_dir):
"version of meson.".format(filename))
if not isinstance(obj, CoreData):
raise MesonException(load_fail_msg)
- if obj.version != version:
+ if major_versions_differ(obj.version, version):
raise MesonException('Build directory has been generated with Meson version %s, '
'which is incompatible with current version %s.\n' %
(obj.version, version))
@@ -668,7 +671,7 @@ def save(obj, build_dir):
filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
prev_filename = filename + '.prev'
tempfilename = filename + '~'
- if obj.version != version:
+ if major_versions_differ(obj.version, version):
raise MesonException('Fatal version mismatch corruption.')
if os.path.exists(filename):
import shutil
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 64c5100..f5eb513 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -328,6 +328,10 @@ class NotFoundDependency(Dependency):
self.name = 'not-found'
self.is_found = False
+ def get_partial_dependency(self, *, compile_args=False, link_args=False,
+ links=False, includes=False, sources=False):
+ return copy.copy(self)
+
class ConfigToolDependency(ExternalDependency):
diff --git a/mesonbuild/dependencies/data/CMakeLists.txt b/mesonbuild/dependencies/data/CMakeLists.txt
index 144ffda..6f51681 100644
--- a/mesonbuild/dependencies/data/CMakeLists.txt
+++ b/mesonbuild/dependencies/data/CMakeLists.txt
@@ -1,24 +1,31 @@
cmake_minimum_required(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION} )
# Inspired by CMakeDetermineCompilerABI.cmake to set CMAKE_LIBRARY_ARCHITECTURE
+set(LIB_ARCH_LIST)
if(CMAKE_LIBRARY_ARCHITECTURE_REGEX)
- if(NOT DEFINED CMAKE_LIBRARY_ARCHITECTURE)
- file(GLOB implicit_dirs RELATIVE /lib /lib/*-linux-gnu* )
- foreach(dir ${implicit_dirs})
- if("${dir}" MATCHES "${CMAKE_LIBRARY_ARCHITECTURE_REGEX}")
- set(CMAKE_LIBRARY_ARCHITECTURE "${dir}")
- break()
- endif()
- endforeach()
- endif()
+ file(GLOB implicit_dirs RELATIVE /lib /lib/*-linux-gnu* )
+ foreach(dir ${implicit_dirs})
+ if("${dir}" MATCHES "${CMAKE_LIBRARY_ARCHITECTURE_REGEX}")
+ list(APPEND LIB_ARCH_LIST "${dir}")
+ endif()
+ endforeach()
endif()
-find_package("${NAME}" QUIET)
-
set(PACKAGE_FOUND FALSE)
set(_packageName "${NAME}")
string(TOUPPER "${_packageName}" PACKAGE_NAME)
+while(TRUE)
+ find_package("${NAME}" QUIET)
+
+ if(${_packageName}_FOUND OR ${PACKAGE_NAME}_FOUND OR "${LIB_ARCH_LIST}" STREQUAL "")
+ break()
+ endif()
+
+ list(GET LIB_ARCH_LIST 0 CMAKE_LIBRARY_ARCHITECTURE)
+ list(REMOVE_AT LIB_ARCH_LIST 0)
+endwhile()
+
if(${_packageName}_FOUND OR ${PACKAGE_NAME}_FOUND)
set(PACKAGE_FOUND TRUE)
diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py
index 47beb4e..103ca74 100644
--- a/mesonbuild/dependencies/dev.py
+++ b/mesonbuild/dependencies/dev.py
@@ -212,6 +212,7 @@ class LLVMDependency(ConfigToolDependency):
# Debian and the latter is used by FreeBSD.
tools = [
'llvm-config', # base
+ 'llvm-config-8', # No FreeBSD release for 8 yet
'llvm-config-7', 'llvm-config70',
'llvm-config-6.0', 'llvm-config60',
'llvm-config-5.0', 'llvm-config50',
@@ -221,7 +222,7 @@ class LLVMDependency(ConfigToolDependency):
'llvm-config-3.7', 'llvm-config37',
'llvm-config-3.6', 'llvm-config36',
'llvm-config-3.5', 'llvm-config35',
- 'llvm-config-8', 'llvm-config-devel', # development snapshot
+ 'llvm-config-9', 'llvm-config-devel', # development snapshot
]
tool_name = 'llvm-config'
__cpp_blacklist = {'-DNDEBUG'}
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 0809ac5..d853020 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -59,6 +59,7 @@ from .compilers import (
IntelFortranCompiler,
JavaCompiler,
MonoCompiler,
+ CudaCompiler,
VisualStudioCsCompiler,
NAGFortranCompiler,
Open64FortranCompiler,
@@ -99,6 +100,17 @@ known_cpu_families = (
'x86_64'
)
+# Environment variables that each lang uses.
+cflags_mapping = {'c': 'CFLAGS',
+ 'cpp': 'CXXFLAGS',
+ 'cu': 'CUFLAGS',
+ 'objc': 'OBJCFLAGS',
+ 'objcpp': 'OBJCXXFLAGS',
+ 'fortran': 'FFLAGS',
+ 'd': 'DFLAGS',
+ 'vala': 'VALAFLAGS'}
+
+
def detect_gcovr(version='3.1', log=False):
gcovr_exe = 'gcovr'
try:
@@ -410,6 +422,7 @@ class Environment:
self.default_d = ['ldc2', 'ldc', 'gdc', 'dmd']
self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77', 'ifort', 'pgfortran']
self.default_java = ['javac']
+ self.default_cuda = ['nvcc']
self.default_rust = ['rustc']
self.default_swift = ['swiftc']
self.default_vala = ['valac']
@@ -417,6 +430,7 @@ class Environment:
self.default_strip = ['strip']
self.vs_static_linker = ['lib']
self.clang_cl_static_linker = ['llvm-lib']
+ self.cuda_static_linker = ['nvlink']
self.gcc_static_linker = ['gcc-ar']
self.clang_static_linker = ['llvm-ar']
self.default_pkgconfig = ['pkg-config']
@@ -737,6 +751,25 @@ class Environment:
def detect_cpp_compiler(self, want_cross):
return self._detect_c_or_cpp_compiler('cpp', want_cross)
+ def detect_cuda_compiler(self, want_cross):
+ popen_exceptions = {}
+ compilers, ccache, is_cross, exe_wrap = self._get_compilers('cuda', want_cross)
+ for compiler in compilers:
+ if isinstance(compiler, str):
+ compiler = [compiler]
+ else:
+ raise EnvironmentException()
+ arg = '--version'
+ try:
+ p, out, err = Popen_safe(compiler + [arg])
+ except OSError as e:
+ popen_exceptions[' '.join(compiler + [arg])] = e
+ continue
+ version = search_version(out)
+ cls = CudaCompiler
+ return cls(ccache + compiler, version, is_cross, exe_wrap)
+ raise EnvironmentException('Could not find suitable CUDA compiler: "' + ' '.join(compilers) + '"')
+
def detect_fortran_compiler(self, want_cross):
popen_exceptions = {}
compilers, ccache, is_cross, exe_wrap = self._get_compilers('fortran', want_cross)
@@ -999,6 +1032,10 @@ class Environment:
comp = self.detect_objc_compiler(False)
if need_cross_compiler:
cross_comp = self.detect_objc_compiler(True)
+ elif lang == 'cuda':
+ comp = self.detect_cuda_compiler(False)
+ if need_cross_compiler:
+ cross_comp = self.detect_cuda_compiler(True)
elif lang == 'objcpp':
comp = self.detect_objcpp_compiler(False)
if need_cross_compiler:
@@ -1057,7 +1094,12 @@ class Environment:
if linker is not None:
linkers = [linker]
else:
- if isinstance(compiler, compilers.VisualStudioCCompiler):
+ evar = 'AR'
+ if isinstance(compiler, compilers.CudaCompiler):
+ linkers = [self.cuda_static_linker, self.default_static_linker]
+ elif evar in os.environ:
+ linkers = [shlex.split(os.environ[evar])]
+ elif isinstance(compiler, compilers.VisualStudioCCompiler):
linkers = [self.vs_static_linker, self.clang_cl_static_linker]
elif isinstance(compiler, compilers.GnuCompiler):
# Use gcc-ar if available; needed for LTO
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index d2c1ffe..aff46d1 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -143,31 +143,32 @@ class TryRunResultHolder(InterpreterObject):
class RunProcess(InterpreterObject):
- def __init__(self, cmd, args, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
+ def __init__(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir=False, check=False, capture=True):
super().__init__()
if not isinstance(cmd, ExternalProgram):
raise AssertionError('BUG: RunProcess must be passed an ExternalProgram')
self.capture = capture
- pc, self.stdout, self.stderr = self.run_command(cmd, args, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
+ pc, self.stdout, self.stderr = self.run_command(cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check)
self.returncode = pc.returncode
self.methods.update({'returncode': self.returncode_method,
'stdout': self.stdout_method,
'stderr': self.stderr_method,
})
- def run_command(self, cmd, args, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
+ def run_command(self, cmd, args, env, source_dir, build_dir, subdir, mesonintrospect, in_builddir, check=False):
command_array = cmd.get_command() + args
- env = {'MESON_SOURCE_ROOT': source_dir,
- 'MESON_BUILD_ROOT': build_dir,
- 'MESON_SUBDIR': subdir,
- 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]),
- }
+ menv = {'MESON_SOURCE_ROOT': source_dir,
+ 'MESON_BUILD_ROOT': build_dir,
+ 'MESON_SUBDIR': subdir,
+ 'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in mesonintrospect]),
+ }
if in_builddir:
cwd = os.path.join(build_dir, subdir)
else:
cwd = os.path.join(source_dir, subdir)
child_env = os.environ.copy()
- child_env.update(env)
+ child_env.update(menv)
+ child_env = env.get_env(child_env)
stdout = subprocess.PIPE if self.capture else subprocess.DEVNULL
mlog.debug('Running command:', ' '.join(command_array))
try:
@@ -743,7 +744,8 @@ class BuildTargetHolder(TargetHolder):
mlog.warning('extract_all_objects called without setting recursive '
'keyword argument. Meson currently defaults to '
'non-recursive to maintain backward compatibility but '
- 'the default will be changed in the future.')
+ 'the default will be changed in the future.',
+ location=self.current_node)
return GeneratedObjectsHolder(gobjs)
@noPosargs
@@ -1597,7 +1599,7 @@ ModuleState = namedtuple('ModuleState', [
'build_to_src', 'subproject', 'subdir', 'current_lineno', 'environment',
'project_name', 'project_version', 'backend', 'compilers', 'targets',
'data', 'headers', 'man', 'global_args', 'project_args', 'build_machine',
- 'host_machine', 'target_machine'])
+ 'host_machine', 'target_machine', 'current_node'])
class ModuleHolder(InterpreterObject, ObjectHolder):
def __init__(self, modname, module, interpreter):
@@ -1640,6 +1642,7 @@ class ModuleHolder(InterpreterObject, ObjectHolder):
build_machine=self.interpreter.builtin['build_machine'].held_object,
host_machine=self.interpreter.builtin['host_machine'].held_object,
target_machine=self.interpreter.builtin['target_machine'].held_object,
+ current_node=self.current_node
)
if self.held_object.is_snippet(method_name):
value = fn(self.interpreter, state, args, kwargs)
@@ -1952,7 +1955,7 @@ permitted_kwargs = {'add_global_arguments': {'language', 'native'},
'install_subdir': {'exclude_files', 'exclude_directories', 'install_dir', 'install_mode', 'strip_directory'},
'jar': build.known_jar_kwargs,
'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'},
- 'run_command': {'check', 'capture'},
+ 'run_command': {'check', 'capture', 'env'},
'run_target': {'command', 'depends'},
'shared_library': build.known_shlib_kwargs,
'shared_module': build.known_shmod_kwargs,
@@ -2257,6 +2260,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if not isinstance(actual, wanted):
raise InvalidArguments('Incorrect argument type.')
+ @FeatureNewKwargs('run_command', '0.50.0', ['env'])
@FeatureNewKwargs('run_command', '0.47.0', ['check', 'capture'])
@permittedKwargs(permitted_kwargs['run_command'])
def func_run_command(self, node, args, kwargs):
@@ -2275,6 +2279,8 @@ external dependencies (including libraries) must go to "dependencies".''')
if not isinstance(check, bool):
raise InterpreterException('Check must be boolean.')
+ env = self.unpack_env_kwarg(kwargs)
+
m = 'must be a string, or the output of find_program(), files() '\
'or configure_file(), or a compiler object; not {!r}'
if isinstance(cmd, ExternalProgramHolder):
@@ -2324,7 +2330,7 @@ external dependencies (including libraries) must go to "dependencies".''')
if not a.startswith('..'):
if a not in self.build_def_files:
self.build_def_files.append(a)
- return RunProcess(cmd, expanded_args, srcdir, builddir, self.subdir,
+ return RunProcess(cmd, expanded_args, env, srcdir, builddir, self.subdir,
self.environment.get_build_command() + ['introspect'],
in_builddir=in_builddir, check=check, capture=capture)
@@ -2365,7 +2371,8 @@ external dependencies (including libraries) must go to "dependencies".''')
if os.path.isabs(dirname):
raise InterpreterException('Subproject name must not be an absolute path.')
if has_path_sep(dirname):
- mlog.warning('Subproject name has a path separator. This may cause unexpected behaviour.')
+ mlog.warning('Subproject name has a path separator. This may cause unexpected behaviour.',
+ location=self.current_node)
if dirname in self.subproject_stack:
fullstack = self.subproject_stack + [dirname]
incpath = ' => '.join(fullstack)
@@ -2469,7 +2476,8 @@ external dependencies (including libraries) must go to "dependencies".''')
mlog.warning('Option {0!r} of type {1!r} in subproject {2!r} cannot yield '
'to parent option of type {3!r}, ignoring parent value. '
'Use -D{2}:{0}=value to set the value for this option manually'
- '.'.format(raw_optname, opt_type, self.subproject, popt_type))
+ '.'.format(raw_optname, opt_type, self.subproject, popt_type),
+ location=self.current_node)
return opt
except KeyError:
pass
@@ -2924,7 +2932,8 @@ external dependencies (including libraries) must go to "dependencies".''')
has_fallback = 'fallback' in kwargs
if 'default_options' in kwargs and not has_fallback:
- mlog.warning('The "default_options" keyworg argument does nothing without a "fallback" keyword argument.')
+ mlog.warning('The "default_options" keyworg argument does nothing without a "fallback" keyword argument.',
+ location=self.current_node)
# writing just "dependency('')" is an error, because it can only fail
if name == '' and required and not has_fallback:
@@ -2989,7 +2998,7 @@ external dependencies (including libraries) must go to "dependencies".''')
command_templ = '\nmeson wrap promote {}'
for l in found:
message.append(mlog.bold(command_templ.format(l[len(self.source_root) + 1:])))
- mlog.warning(*message)
+ mlog.warning(*message, location=self.current_node)
def get_subproject_infos(self, kwargs):
fbinfo = kwargs['fallback']
@@ -3141,7 +3150,7 @@ external dependencies (including libraries) must go to "dependencies".''')
kwargs['input'] = self.source_strings_to_files(extract_as_list(kwargs, 'input'))
except mesonlib.MesonException:
mlog.warning('''Custom target input \'%s\' can\'t be converted to File object(s).
-This will become a hard error in the future.''' % kwargs['input'])
+This will become a hard error in the future.''' % kwargs['input'], location=self.current_node)
tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, self.subproject, kwargs), self)
self.add_target(name, tg.held_object)
return tg
@@ -3799,7 +3808,8 @@ different subdirectory.
self.coredata.base_options['b_sanitize'].value != 'none'):
mlog.warning('''Trying to use {} sanitizer on Clang with b_lundef.
This will probably not work.
-Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_sanitize'].value))
+Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_sanitize'].value),
+ location=self.current_node)
def evaluate_subproject_info(self, path_from_source_root, subproject_dirname):
depth = 0
@@ -3993,7 +4003,7 @@ Try setting b_lundef to false instead.'''.format(self.coredata.base_options['b_s
# path declarations.
if os.path.normpath(i).startswith(self.environment.get_source_dir()):
mlog.warning('''Building a path to the source dir is not supported. Use a relative path instead.
-This will become a hard error in the future.''')
+This will become a hard error in the future.''', location=self.current_node)
i = os.path.relpath(i, os.path.join(self.environment.get_source_dir(), self.subdir))
i = self.build_incdir_object([i])
cleaned_items.append(i)
diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py
index 48c5220..45a4cb0 100644
--- a/mesonbuild/interpreterbase.py
+++ b/mesonbuild/interpreterbase.py
@@ -47,14 +47,14 @@ def _get_callee_args(wrapped_args, want_subproject=False):
if want_subproject and n == 2:
if hasattr(s, 'subproject'):
# Interpreter base types have 2 args: self, node
- node_or_state = wrapped_args[1]
+ node = wrapped_args[1]
# args and kwargs are inside the node
args = None
kwargs = None
subproject = s.subproject
elif hasattr(wrapped_args[1], 'subproject'):
# Module objects have 2 args: self, interpreter
- node_or_state = wrapped_args[1]
+ node = wrapped_args[1].current_node
# args and kwargs are inside the node
args = None
kwargs = None
@@ -63,7 +63,7 @@ def _get_callee_args(wrapped_args, want_subproject=False):
raise AssertionError('Unknown args: {!r}'.format(wrapped_args))
elif n == 3:
# Methods on objects (*Holder, MesonMain, etc) have 3 args: self, args, kwargs
- node_or_state = None # FIXME
+ node = s.current_node
args = wrapped_args[1]
kwargs = wrapped_args[2]
if want_subproject:
@@ -73,30 +73,32 @@ def _get_callee_args(wrapped_args, want_subproject=False):
subproject = s.interpreter.subproject
elif n == 4:
# Meson functions have 4 args: self, node, args, kwargs
- # Module functions have 4 args: self, state, args, kwargs; except,
- # PythonInstallation methods have self, interpreter, args, kwargs
- node_or_state = wrapped_args[1]
+ # Module functions have 4 args: self, state, args, kwargs
+ if isinstance(s, InterpreterBase):
+ node = wrapped_args[1]
+ else:
+ node = wrapped_args[1].current_node
args = wrapped_args[2]
kwargs = wrapped_args[3]
if want_subproject:
if isinstance(s, InterpreterBase):
subproject = s.subproject
else:
- subproject = node_or_state.subproject
+ subproject = wrapped_args[1].subproject
elif n == 5:
# Module snippets have 5 args: self, interpreter, state, args, kwargs
- node_or_state = wrapped_args[2]
+ node = wrapped_args[2].current_node
args = wrapped_args[3]
kwargs = wrapped_args[4]
if want_subproject:
- subproject = node_or_state.subproject
+ subproject = wrapped_args[2].subproject
else:
raise AssertionError('Unknown args: {!r}'.format(wrapped_args))
# Sometimes interpreter methods are called internally with None instead of
# empty list/dict
args = args if args is not None else []
kwargs = kwargs if kwargs is not None else {}
- return s, node_or_state, args, kwargs, subproject
+ return s, node, args, kwargs, subproject
def flatten(args):
if isinstance(args, mparser.StringNode):
@@ -164,19 +166,10 @@ class permittedKwargs:
def __call__(self, f):
@wraps(f)
def wrapped(*wrapped_args, **wrapped_kwargs):
- s, node_or_state, args, kwargs, _ = _get_callee_args(wrapped_args)
- loc = types.SimpleNamespace()
- if hasattr(s, 'subdir'):
- loc.subdir = s.subdir
- loc.lineno = s.current_lineno
- elif node_or_state and hasattr(node_or_state, 'subdir'):
- loc.subdir = node_or_state.subdir
- loc.lineno = node_or_state.current_lineno
- else:
- loc = None
+ s, node, args, kwargs, _ = _get_callee_args(wrapped_args)
for k in kwargs:
if k not in self.permitted:
- mlog.warning('''Passed invalid keyword argument "{}".'''.format(k), location=loc)
+ mlog.warning('''Passed invalid keyword argument "{}".'''.format(k), location=node)
mlog.warning('This will become a hard error in the future.')
return f(*wrapped_args, **wrapped_kwargs)
return wrapped
@@ -320,6 +313,9 @@ class BreakRequest(BaseException):
class InterpreterObject:
def __init__(self):
self.methods = {}
+ # Current node set during a method call. This can be used as location
+ # when printing a warning message during a method call.
+ self.current_node = None
def method_call(self, method_name, args, kwargs):
if method_name in self.methods:
@@ -366,6 +362,9 @@ class InterpreterBase:
self.variables = {}
self.argument_depth = 0
self.current_lineno = -1
+ # Current node set during a function call. This can be used as location
+ # when printing a warning message during a method call.
+ self.current_node = None
def load_root_meson_file(self):
mesonfile = os.path.join(self.source_root, self.subdir, environment.build_filename)
@@ -759,6 +758,7 @@ The result of this is undefined and will become a hard error in a future Meson r
if not getattr(func, 'no-args-flattening', False):
posargs = flatten(posargs)
+ self.current_node = node
return func(node, posargs, kwargs)
else:
self.unknown_function_called(func_name)
@@ -795,6 +795,7 @@ The result of this is undefined and will become a hard error in a future Meson r
return Disabler()
if method_name == 'extract_objects':
self.validate_extraction(obj.held_object)
+ obj.current_node = node
return obj.method_call(method_name, args, kwargs)
def bool_method_call(self, obj, method_name, args):
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 98c2366..2170fec 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -729,7 +729,7 @@ def do_mesondefine(line, confdata):
def do_conf_file(src, dst, confdata, format, encoding='utf-8'):
try:
- with open(src, encoding=encoding) as f:
+ with open(src, encoding=encoding, newline='') as f:
data = f.readlines()
except Exception as e:
raise MesonException('Could not read input file %s: %s' % (src, str(e)))
@@ -763,7 +763,7 @@ def do_conf_file(src, dst, confdata, format, encoding='utf-8'):
result.append(line)
dst_tmp = dst + '~'
try:
- with open(dst_tmp, 'w', encoding=encoding) as f:
+ with open(dst_tmp, 'w', encoding=encoding, newline='') as f:
f.writelines(result)
except Exception as e:
raise MesonException('Could not write output file %s: %s' % (dst, str(e)))
diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py
index aeab813..4deb437 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -102,7 +102,8 @@ class I18nModule(ExtensionModule):
return ModuleReturnValue(ct, [ct])
@FeatureNewKwargs('i18n.gettext', '0.37.0', ['preset'])
- @permittedKwargs({'po_dir', 'data_dirs', 'type', 'languages', 'args', 'preset', 'install'})
+ @FeatureNewKwargs('i18n.gettext', '0.50.0', ['install_dir'])
+ @permittedKwargs({'po_dir', 'data_dirs', 'type', 'languages', 'args', 'preset', 'install', 'install_dir'})
def gettext(self, state, args, kwargs):
if len(args) != 1:
raise coredata.MesonException('Gettext requires one positional argument (package name).')
@@ -151,10 +152,11 @@ class I18nModule(ExtensionModule):
install = kwargs.get('install', True)
if install:
+ install_dir = kwargs.get('install_dir', state.environment.coredata.get_builtin_option('localedir'))
script = state.environment.get_build_command()
args = ['--internal', 'gettext', 'install',
'--subdir=' + state.subdir,
- '--localedir=' + state.environment.coredata.get_builtin_option('localedir'),
+ '--localedir=' + install_dir,
pkg_arg]
if lang_arg:
args.append(lang_arg)
diff --git a/run_project_tests.py b/run_project_tests.py
index 02897ce..4c6ca3b 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -542,6 +542,7 @@ def detect_tests_to_run():
('objective c++', 'objcpp', backend not in (Backend.ninja, Backend.xcode) or mesonlib.is_windows() or not have_objcpp_compiler()),
('fortran', 'fortran', backend is not Backend.ninja or not shutil.which('gfortran')),
('swift', 'swift', backend not in (Backend.ninja, Backend.xcode) or not shutil.which('swiftc')),
+ ('cuda', 'cuda', backend not in (Backend.ninja, Backend.xcode) or not shutil.which('nvcc')),
('python3', 'python3', backend is not Backend.ninja),
('python', 'python', backend is not Backend.ninja),
('fpga', 'fpga', shutil.which('yosys') is None),
diff --git a/run_unittests.py b/run_unittests.py
index cde5589..abedf4a 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -30,6 +30,7 @@ import functools
from itertools import chain
from unittest import mock
from configparser import ConfigParser
+from contextlib import contextmanager
from glob import glob
from pathlib import (PurePath, Path)
from distutils.dir_util import copy_tree
@@ -193,6 +194,24 @@ def skip_if_not_base_option(feature):
return actual
+@contextmanager
+def temp_filename():
+ '''A context manager which provides a filename to an empty temporary file.
+
+ On exit the file will be deleted.
+ '''
+
+ fd, filename = tempfile.mkstemp()
+ os.close(fd)
+ try:
+ yield filename
+ finally:
+ try:
+ os.remove(filename)
+ except OSError:
+ pass
+
+
class PatchModule:
'''
Fancy monkey-patching! Whee! Can't use mock.patch because it only
@@ -1299,6 +1318,21 @@ class AllPlatformTests(BasePlatformTests):
prefix = opt['value']
self.assertEqual(prefix, '/absoluteprefix')
+ def test_do_conf_file_preserve_newlines(self):
+
+ def conf_file(in_data, confdata):
+ with temp_filename() as fin:
+ with open(fin, 'wb') as fobj:
+ fobj.write(in_data.encode('utf-8'))
+ with temp_filename() as fout:
+ mesonbuild.mesonlib.do_conf_file(fin, fout, confdata, 'meson')
+ with open(fout, 'rb') as fobj:
+ return fobj.read().decode('utf-8')
+
+ confdata = {'VAR': ('foo', 'bar')}
+ self.assertEqual(conf_file('@VAR@\n@VAR@\n', confdata), 'foo\nfoo\n')
+ self.assertEqual(conf_file('@VAR@\r\n@VAR@\r\n', confdata), 'foo\r\nfoo\r\n')
+
def test_absolute_prefix_libdir(self):
'''
Tests that setting absolute paths for --prefix and --libdir work. Can't
@@ -2986,12 +3020,16 @@ recommended as it is not supported on some platforms''')
self.wipe()
self.init(testdir, extra_args=['-Dstart_native=true'])
- def __reconfigure(self):
+ def __reconfigure(self, change_minor=False):
# Set an older version to force a reconfigure from scratch
filename = os.path.join(self.privatedir, 'coredata.dat')
with open(filename, 'rb') as f:
obj = pickle.load(f)
- obj.version = '0.47.0'
+ if change_minor:
+ v = mesonbuild.coredata.version.split('.')
+ obj.version = '.'.join(v[0:2] + [str(int(v[2]) + 1)])
+ else:
+ obj.version = '0.47.0'
with open(filename, 'wb') as f:
pickle.dump(obj, f)
@@ -3032,6 +3070,22 @@ recommended as it is not supported on some platforms''')
with Path(self.builddir):
self.init(testdir, extra_args=['--wipe'])
+ def test_minor_version_does_not_reconfigure_wipe(self):
+ testdir = os.path.join(self.unit_test_dir, '46 reconfigure')
+ self.init(testdir, extra_args=['-Dopt1=val1'])
+ self.setconf('-Dopt2=val2')
+
+ self.__reconfigure(change_minor=True)
+
+ out = self.init(testdir, extra_args=['--reconfigure', '-Dopt3=val3'])
+ self.assertNotRegex(out, 'WARNING:.*Regenerating configuration from scratch')
+ self.assertRegex(out, 'opt1 val1')
+ self.assertRegex(out, 'opt2 val2')
+ self.assertRegex(out, 'opt3 val3')
+ self.assertRegex(out, 'opt4 default4')
+ self.build()
+ self.run_tests()
+
def test_target_construct_id_from_path(self):
# This id is stable but not guessable.
# The test is supposed to prevent unintentional
diff --git a/test cases/common/36 run program/meson.build b/test cases/common/36 run program/meson.build
index a05cea3..93897e3 100644
--- a/test cases/common/36 run program/meson.build
+++ b/test cases/common/36 run program/meson.build
@@ -65,6 +65,12 @@ ret = run_command(py3, '-c', 'print("some output")', capture : false)
assert(ret.returncode() == 0, 'failed to run python3: ' + ret.stderr())
assert(ret.stdout() == '', 'stdout is "@0@" instead of empty'.format(ret.stdout()))
+c_env = environment()
+c_env.append('CUSTOM_ENV_VAR', 'FOOBAR')
+ret = run_command(py3, '-c', 'import os; print(os.environ.get("CUSTOM_ENV_VAR"))', env : c_env)
+assert(ret.returncode() == 0, 'failed to run python3: ' + ret.stderr())
+assert(ret.stdout() == 'FOOBAR\n', 'stdout is "@0@" instead of FOOBAR'.format(ret.stdout()))
+
dd = find_program('dd', required : false)
if dd.found()
ret = run_command(dd, 'if=/dev/urandom', 'bs=10', 'count=1', capture: false)
diff --git a/test cases/cuda/1 simple/meson.build b/test cases/cuda/1 simple/meson.build
new file mode 100644
index 0000000..19af734
--- /dev/null
+++ b/test cases/cuda/1 simple/meson.build
@@ -0,0 +1,5 @@
+project('simple', 'cuda', version : '1.0.0')
+
+exe = executable('prog', 'prog.cu')
+test('cudatest', exe)
+
diff --git a/test cases/cuda/1 simple/prog.cu b/test cases/cuda/1 simple/prog.cu
new file mode 100644
index 0000000..7eab673
--- /dev/null
+++ b/test cases/cuda/1 simple/prog.cu
@@ -0,0 +1,30 @@
+#include <iostream>
+
+int main(int argc, char **argv) {
+ int cuda_devices = 0;
+ std::cout << "CUDA version: " << CUDART_VERSION << "\n";
+ cudaGetDeviceCount(&cuda_devices);
+ if(cuda_devices == 0) {
+ std::cout << "No Cuda hardware found. Exiting.\n";
+ return 0;
+ }
+ std::cout << "This computer has " << cuda_devices << " Cuda device(s).\n";
+ cudaDeviceProp props;
+ cudaGetDeviceProperties(&props, 0);
+ std::cout << "Properties of device 0.\n\n";
+
+ std::cout << " Name: " << props.name << "\n";
+ std::cout << " Global memory: " << props.totalGlobalMem << "\n";
+ std::cout << " Shared memory: " << props.sharedMemPerBlock << "\n";
+ std::cout << " Constant memory: " << props.totalConstMem << "\n";
+ std::cout << " Block registers: " << props.regsPerBlock << "\n";
+
+ std::cout << " Warp size: " << props.warpSize << "\n";
+ std::cout << " Threads per block: " << props.maxThreadsPerBlock << "\n";
+ std::cout << " Max block dimensions: [ " << props.maxThreadsDim[0] << ", " << props.maxThreadsDim[1] << ", " << props.maxThreadsDim[2] << " ]" << "\n";
+ std::cout << " Max grid dimensions: [ " << props.maxGridSize[0] << ", " << props.maxGridSize[1] << ", " << props.maxGridSize[2] << " ]" << "\n";
+ std::cout << "\n";
+
+ return 0;
+}
+
diff --git a/test cases/cuda/2 split/lib.cu b/test cases/cuda/2 split/lib.cu
new file mode 100644
index 0000000..c0471d0
--- /dev/null
+++ b/test cases/cuda/2 split/lib.cu
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <iostream>
+
+__global__ void kernel (void){
+}
+
+int do_cuda_stuff() {
+ kernel<<<1,1>>>();
+
+ printf("Hello, World!\n");
+ return 0;
+}
+
diff --git a/test cases/cuda/2 split/main.cpp b/test cases/cuda/2 split/main.cpp
new file mode 100644
index 0000000..e5e6bda
--- /dev/null
+++ b/test cases/cuda/2 split/main.cpp
@@ -0,0 +1,7 @@
+#include<iostream>
+
+int do_cuda_stuff();
+
+int main(int argc, char **argv) {
+ return do_cuda_stuff();
+}
diff --git a/test cases/cuda/2 split/meson.build b/test cases/cuda/2 split/meson.build
new file mode 100644
index 0000000..51bf6ce
--- /dev/null
+++ b/test cases/cuda/2 split/meson.build
@@ -0,0 +1,7 @@
+project('simple', 'cuda', 'cpp')
+
+exe = executable('prog', 'main.cpp', 'lib.cu')
+test('cudatest', exe)
+
+subdir('static')
+
diff --git a/test cases/cuda/2 split/static/lib.cu b/test cases/cuda/2 split/static/lib.cu
new file mode 100644
index 0000000..c0471d0
--- /dev/null
+++ b/test cases/cuda/2 split/static/lib.cu
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <iostream>
+
+__global__ void kernel (void){
+}
+
+int do_cuda_stuff() {
+ kernel<<<1,1>>>();
+
+ printf("Hello, World!\n");
+ return 0;
+}
+
diff --git a/test cases/cuda/2 split/static/libsta.cu b/test cases/cuda/2 split/static/libsta.cu
new file mode 100644
index 0000000..c0471d0
--- /dev/null
+++ b/test cases/cuda/2 split/static/libsta.cu
@@ -0,0 +1,13 @@
+#include <stdio.h>
+#include <iostream>
+
+__global__ void kernel (void){
+}
+
+int do_cuda_stuff() {
+ kernel<<<1,1>>>();
+
+ printf("Hello, World!\n");
+ return 0;
+}
+
diff --git a/test cases/cuda/2 split/static/main_static.cpp b/test cases/cuda/2 split/static/main_static.cpp
new file mode 100644
index 0000000..e5e6bda
--- /dev/null
+++ b/test cases/cuda/2 split/static/main_static.cpp
@@ -0,0 +1,7 @@
+#include<iostream>
+
+int do_cuda_stuff();
+
+int main(int argc, char **argv) {
+ return do_cuda_stuff();
+}
diff --git a/test cases/cuda/2 split/static/meson.build b/test cases/cuda/2 split/static/meson.build
new file mode 100644
index 0000000..9078198
--- /dev/null
+++ b/test cases/cuda/2 split/static/meson.build
@@ -0,0 +1,4 @@
+l = static_library('clib', 'lib.cu')
+exe = executable('staexe', 'main_static.cpp',
+ link_with : l)
+test('static Cuda test', exe)