diff options
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) |