Merge pull request #4250 from jon-turney/windows-clang-cl

Add support for clang-cl on Windows
pull/4471/head
Jussi Pakkanen 6 years ago committed by GitHub
commit 1eb455f869
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      azure-pipelines.yml
  2. 23
      ci/azure-steps.yml
  3. 4
      mesonbuild/backend/backends.py
  4. 19
      mesonbuild/backend/ninjabackend.py
  5. 2
      mesonbuild/build.py
  6. 4
      mesonbuild/compilers/__init__.py
  7. 33
      mesonbuild/compilers/c.py
  8. 11
      mesonbuild/compilers/cpp.py
  9. 2
      mesonbuild/compilers/d.py
  10. 4
      mesonbuild/dependencies/boost.py
  11. 31
      mesonbuild/environment.py
  12. 28
      mesonbuild/interpreter.py
  13. 2
      mesonbuild/modules/windows.py
  14. 24
      run_project_tests.py
  15. 42
      run_unittests.py
  16. 30
      test cases/common/100 manygen/subdir/manygen.py
  17. 5
      test cases/common/100 manygen/subdir/meson.build
  18. 2
      test cases/common/112 spaces backslash/meson.build
  19. 11
      test cases/common/123 llvm ir and assembly/meson.build
  20. 2
      test cases/common/124 cpp and asm/meson.build
  21. 2
      test cases/common/127 no buildincdir/meson.build
  22. 3
      test cases/common/13 pch/mixed/meson.build
  23. 4
      test cases/common/132 generated assembly/meson.build
  24. 2
      test cases/common/138 c cpp and asm/meson.build
  25. 5
      test cases/common/143 C and CPP link/meson.build
  26. 2
      test cases/common/186 has link arg/meson.build
  27. 3
      test cases/common/190 openmp/meson.build
  28. 6
      test cases/common/204 function attributes/meson.build
  29. 2
      test cases/common/206 argument syntax/meson.build
  30. 5
      test cases/common/91 default options/meson.build
  31. 10
      test cases/windows/16 gui app/meson.build

@ -44,6 +44,10 @@ jobs:
arch: x64
compiler: msvc2017
backend: vs2017
clangclx64ninja:
arch: x64
compiler: clang-cl
backend: ninja
steps:
- template: ci/azure-steps.yml

@ -48,13 +48,34 @@ steps:
# add downloads to PATH
$env:Path = "$env:SYSTEM_WORKFOLDER;$env:Path"
$origPath = $env:Path
# import visual studio variables
if ($env:compiler -eq 'msvc2015') {
$vsver = $env:compiler.Replace('msvc', '')
} else {
$vsver = '2017'
}
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module Pscx -Scope CurrentUser -AllowClobber
Install-Module VSSetup -Scope CurrentUser
$vsver = $env:compiler.Replace('msvc', '')
Import-VisualStudioVars -VisualStudioVersion $vsver -Architecture $(arch)
if ($env:compiler -eq 'clang-cl') {
# drop visual studio from PATH
# (but leave INCLUDE, LIB and WindowsSdkDir environment variables set)
$env:Path = $origPath
# install llvm for clang-cl builds
DownloadFile -Source 'http://releases.llvm.org/7.0.0/LLVM-7.0.0-win64.exe' -Destination LLVM-7.0.0-win64.exe
Start-Process .\LLVM-7.0.0-win64.exe -ArgumentList '/S' -Wait
$env:Path = "C:\Program Files\LLVM\bin;$env:Path"
$env:CC = "clang-cl"
$env:CXX = "clang-cl"
# and use Windows SDK tools
$env:Path = "$env:WindowsSdkDir\bin\$env:Arch;$env:Path"
}
# add .NET framework tools to path for resgen for C# tests
# (always use 32-bit tool, as there doesn't seem to be a 64-bit tool)
if ((Get-Command "resgen.exe" -ErrorAction SilentlyContinue) -eq $null) {

@ -23,7 +23,7 @@ import subprocess
from ..mesonlib import MesonException, OrderedSet
from ..mesonlib import classify_unity_sources
from ..mesonlib import File
from ..compilers import CompilerArgs
from ..compilers import CompilerArgs, VisualStudioCCompiler
from collections import OrderedDict
import shlex
from functools import lru_cache
@ -491,7 +491,7 @@ class Backend:
return args
extra_args = []
# Compiler-specific escaping is needed for -D args but not for any others
if compiler.get_id() == 'msvc':
if isinstance(compiler, VisualStudioCCompiler):
# MSVC needs escaping when a -D argument ends in \ or \"
for arg in args:
if arg.startswith('-D') or arg.startswith('/D'):

@ -29,7 +29,7 @@ from .. import build
from .. import mlog
from .. import dependencies
from .. import compilers
from ..compilers import CompilerArgs, CCompiler
from ..compilers import CompilerArgs, CCompiler, VisualStudioCCompiler
from ..linkers import ArLinker
from ..mesonlib import File, MesonException, OrderedSet
from ..mesonlib import get_compiler_for_source, has_path_sep
@ -169,7 +169,7 @@ class NinjaBackend(backends.Backend):
Detect the search prefix to use.'''
for compiler in self.build.compilers.values():
# Have to detect the dependency format
if compiler.id == 'msvc':
if isinstance(compiler, VisualStudioCCompiler):
break
else:
# None of our compilers are MSVC, we're done.
@ -185,7 +185,8 @@ int dummy;
# and locale dependent. Any attempt at converting it to
# Python strings leads to failure. We _must_ do this detection
# in raw byte mode and write the result in raw bytes.
pc = subprocess.Popen(['cl', '/showIncludes', '/c', 'incdetect.c'],
pc = subprocess.Popen([compiler.get_exelist(),
'/showIncludes', '/c', 'incdetect.c'],
cwd=self.environment.get_scratch_dir(),
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdo, _) = pc.communicate()
@ -195,7 +196,7 @@ int dummy;
# different locales have different messages with a different
# number of colons. Match up to the the drive name 'd:\'.
matchre = re.compile(rb"^(.*\s)[a-zA-Z]:\\.*stdio.h$")
for line in stdo.split(b'\r\n'):
for line in re.split(rb'\r?\n', stdo):
match = matchre.match(line)
if match:
with open(tempfilename, 'ab') as binfile:
@ -1604,7 +1605,7 @@ rule FORTRAN_DEP_HACK%s
compile_only_args=' '.join(compiler.get_compile_only_args())
)
description = ' description = Compiling %s object $out.\n' % compiler.get_display_language()
if compiler.get_id() == 'msvc':
if isinstance(compiler, VisualStudioCCompiler):
deps = ' deps = msvc\n'
else:
deps = ' deps = gcc\n'
@ -1636,7 +1637,7 @@ rule FORTRAN_DEP_HACK%s
if d != '$out' and d != '$in':
d = quote_func(d)
quoted_depargs.append(d)
if compiler.get_id() == 'msvc':
if isinstance(compiler, VisualStudioCCompiler):
output = ''
else:
output = ' '.join(compiler.get_output_args('$out'))
@ -1648,7 +1649,7 @@ rule FORTRAN_DEP_HACK%s
compile_only_args=' '.join(compiler.get_compile_only_args())
)
description = ' description = Precompiling header %s.\n' % '$in'
if compiler.get_id() == 'msvc':
if isinstance(compiler, VisualStudioCCompiler):
deps = ' deps = msvc\n'
else:
deps = ' deps = gcc\n'
@ -1839,7 +1840,7 @@ rule FORTRAN_DEP_HACK%s
return compiler.get_no_stdinc_args()
def get_compile_debugfile_args(self, compiler, target, objfile):
if compiler.id != 'msvc':
if not isinstance(compiler, VisualStudioCCompiler):
return []
# The way MSVC uses PDB files is documented exactly nowhere so
# the following is what we have been able to decipher via
@ -2203,7 +2204,7 @@ rule FORTRAN_DEP_HACK%s
''.format(target.get_basename())
raise InvalidArguments(msg)
compiler = target.compilers[lang]
if compiler.id == 'msvc':
if isinstance(compiler, VisualStudioCCompiler):
src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[-1])
(commands, dep, dst, objs) = self.generate_msvc_pch_command(target, compiler, pch)
extradep = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0])

@ -1168,7 +1168,7 @@ You probably should put it in link_with instead.''')
'''
linker, _ = self.get_clink_dynamic_linker_and_stdlibs()
# Mixing many languages with MSVC is not supported yet so ignore stdlibs.
if linker and linker.get_id() in ['msvc', 'llvm', 'dmd']:
if linker and linker.get_id() in ['msvc', 'clang-cl', 'llvm', 'dmd']:
return True
return False

@ -45,6 +45,8 @@ __all__ = [
'ClangCPPCompiler',
'ClangObjCCompiler',
'ClangObjCPPCompiler',
'ClangClCCompiler',
'ClangClCPPCompiler',
'CompilerArgs',
'CPPCompiler',
'DCompiler',
@ -114,6 +116,7 @@ from .c import (
ArmCCompiler,
ArmclangCCompiler,
ClangCCompiler,
ClangClCCompiler,
GnuCCompiler,
ElbrusCCompiler,
IntelCCompiler,
@ -124,6 +127,7 @@ from .cpp import (
ArmCPPCompiler,
ArmclangCPPCompiler,
ClangCPPCompiler,
ClangClCPPCompiler,
GnuCPPCompiler,
ElbrusCPPCompiler,
IntelCPPCompiler,

@ -170,7 +170,7 @@ class CCompiler(Compiler):
else:
# GNU ld and LLVM lld
return ['-Wl,--allow-shlib-undefined']
elif self.id == 'msvc':
elif isinstance(self, VisualStudioCCompiler):
# link.exe
return ['/FORCE:UNRESOLVED']
# FIXME: implement other linkers
@ -392,6 +392,8 @@ class CCompiler(Compiler):
return self.compiles(t.format(**fargs), env, extra_args, dependencies)
def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'):
if callable(extra_args):
extra_args = extra_args(mode)
if extra_args is None:
extra_args = []
elif isinstance(extra_args, str):
@ -890,7 +892,7 @@ class CCompiler(Compiler):
stlibext = ['a']
# We've always allowed libname to be both `foo` and `libfoo`,
# and now people depend on it
if strict and self.id != 'msvc': # lib prefix is not usually used with msvc
if strict and not isinstance(self, VisualStudioCCompiler): # lib prefix is not usually used with msvc
prefixes = ['lib']
else:
prefixes = ['lib', '']
@ -900,7 +902,7 @@ class CCompiler(Compiler):
elif for_windows(env.is_cross_build(), env):
# FIXME: .lib files can be import or static so we should read the
# file, figure out which one it is, and reject the wrong kind.
if self.id == 'msvc':
if isinstance(self, VisualStudioCCompiler):
shlibext = ['lib']
else:
shlibext = ['dll.a', 'lib', 'dll']
@ -1296,7 +1298,7 @@ class VisualStudioCCompiler(CCompiler):
def get_buildtype_args(self, buildtype):
args = compilers.msvc_buildtype_args[buildtype]
if version_compare(self.version, '<18.0'):
if self.id == 'msvc' and version_compare(self.version, '<18.0'):
args = [arg for arg in args if arg != '/Gw']
return args
@ -1314,6 +1316,8 @@ class VisualStudioCCompiler(CCompiler):
def get_pch_use_args(self, pch_dir, header):
base = os.path.basename(header)
if self.id == 'clang-cl':
base = header
pchname = self.get_pch_name(header)
return ['/FI' + base, '/Yu' + base, '/Fp' + os.path.join(pch_dir, pchname)]
@ -1341,7 +1345,12 @@ class VisualStudioCCompiler(CCompiler):
return []
def get_linker_exelist(self):
return ['link'] # FIXME, should have same path as compiler.
# FIXME, should have same path as compiler.
# FIXME, should be controllable via cross-file.
if self.id == 'clang-cl':
return ['lld-link']
else:
return ['link']
def get_linker_always_args(self):
return ['/nologo']
@ -1449,6 +1458,8 @@ class VisualStudioCCompiler(CCompiler):
# http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t
def has_arguments(self, args, env, code, mode):
warning_text = '4044' if mode == 'link' else '9002'
if self.id == 'clang-cl' and mode != 'link':
args = args + ['-Werror=unknown-argument']
with self._build_wrapper(code, env, extra_args=args, mode=mode) as p:
if p.returncode != 0:
return False
@ -1464,7 +1475,7 @@ class VisualStudioCCompiler(CCompiler):
# build obviously, which is why we only do this when PCH is on.
# This was added in Visual Studio 2013 (MSVC 18.0). Before that it was
# always on: https://msdn.microsoft.com/en-us/library/dn502518.aspx
if pch and version_compare(self.version, '>=18.0'):
if pch and self.id == 'msvc' and version_compare(self.version, '>=18.0'):
args = ['/FS'] + args
return args
@ -1481,7 +1492,7 @@ class VisualStudioCCompiler(CCompiler):
def get_instruction_set_args(self, instruction_set):
if self.is_64:
return vs64_instruction_set_args.get(instruction_set, None)
if self.version.split('.')[0] == '16' and instruction_set == 'avx':
if self.id == 'msvc' and self.version.split('.')[0] == '16' and instruction_set == 'avx':
# VS documentation says that this exists and should work, but
# it does not. The headers do not contain AVX intrinsics
# and the can not be called.
@ -1489,6 +1500,10 @@ class VisualStudioCCompiler(CCompiler):
return vs32_instruction_set_args.get(instruction_set, None)
def get_toolset_version(self):
if self.id == 'clang-cl':
# I have no idea
return '14.1'
# See boost/config/compiler/visualc.cpp for up to date mapping
try:
version = int(''.join(self.version.split('.')[0:2]))
@ -1546,6 +1561,10 @@ class VisualStudioCCompiler(CCompiler):
def get_argument_syntax(self):
return 'msvc'
class ClangClCCompiler(VisualStudioCCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap, is_64):
super().__init__(exelist, version, is_cross, exe_wrap, is_64)
self.id = 'clang-cl'
class ArmCCompiler(ArmCompiler, CCompiler):
def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, **kwargs):

@ -19,7 +19,7 @@ from .. import coredata
from .. import mlog
from ..mesonlib import MesonException, version_compare
from .c import CCompiler, VisualStudioCCompiler
from .c import CCompiler, VisualStudioCCompiler, ClangClCCompiler
from .compilers import (
CompilerType,
gnu_winlibs,
@ -310,6 +310,9 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
def get_options(self):
cpp_stds = ['none', 'c++11', 'vc++11']
if self.id == 'clang-cl':
cpp_stds.extend(['c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'])
else:
# Visual Studio 2015 and later
if version_compare(self.version, '>=19'):
cpp_stds.extend(['c++14', 'vc++14', 'c++latest', 'vc++latest'])
@ -356,7 +359,7 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
# which means setting the C++ standard version to C++14, in compilers that support it
# (i.e., after VS2015U3)
# if one is using anything before that point, one cannot set the standard.
if version_compare(self.version, '>=19.00.24210'):
if self.id == 'clang-cl' or version_compare(self.version, '>=19.00.24210'):
mlog.warning('MSVC does not support C++11; '
'attempting best effort; setting the standard to C++14')
args.append('/std:c++14')
@ -378,6 +381,10 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
# so just use the plain C args.
return VisualStudioCCompiler.get_compiler_check_args(self)
class ClangClCPPCompiler(VisualStudioCPPCompiler, ClangClCCompiler):
def __init__(self, exelist, version, is_cross, exe_wrap, is_64):
VisualStudioCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap, is_64)
self.id = 'clang-cl'
class ArmCPPCompiler(ArmCompiler, CPPCompiler):
def __init__(self, exelist, version, compiler_type, is_cross, exe_wrap=None, **kwargs):

@ -274,6 +274,8 @@ class DCompiler(Compiler):
return ['-Wl,-rpath,{}'.format(paths)]
def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'):
if callable(extra_args):
extra_args = extra_args(mode)
if extra_args is None:
extra_args = []
elif isinstance(extra_args, str):

@ -288,7 +288,7 @@ class BoostDependency(ExternalDependency):
tag = None
compiler = self.env.detect_cpp_compiler(self.want_cross)
if mesonlib.for_windows(self.want_cross, self.env):
if compiler.get_id() == 'msvc':
if compiler.get_id() in ['msvc', 'clang-cl']:
comp_ts_version = compiler.get_toolset_version()
compiler_ts = comp_ts_version.split('.')
# FIXME - what about other compilers?
@ -320,7 +320,7 @@ class BoostDependency(ExternalDependency):
def arch_tag(self):
# currently only applies to windows msvc installed binaries
if self.env.detect_cpp_compiler(self.want_cross).get_id() != 'msvc':
if self.env.detect_cpp_compiler(self.want_cross).get_id() not in ['msvc', 'clang-cl']:
return ''
# pre-compiled binaries only added arch tag for versions > 1.64
if float(self.version) < 1.65:

@ -39,6 +39,8 @@ from .compilers import (
ClangCPPCompiler,
ClangObjCCompiler,
ClangObjCPPCompiler,
ClangClCCompiler,
ClangClCPPCompiler,
G95FortranCompiler,
GnuCCompiler,
GnuCPPCompiler,
@ -190,6 +192,8 @@ def detect_windows_arch(compilers):
platform = os.environ.get('Platform', 'x86').lower()
if platform == 'x86':
return platform
if compiler.id == 'clang-cl' and not compiler.is_64:
return 'x86'
if compiler.id == 'gcc' and compiler.has_builtin_define('__i386__'):
return 'x86'
return os_arch
@ -344,8 +348,8 @@ class Environment:
# List of potential compilers.
if mesonlib.is_windows():
self.default_c = ['cl', 'cc', 'gcc', 'clang']
self.default_cpp = ['cl', 'c++', 'g++', 'clang++']
self.default_c = ['cl', 'cc', 'gcc', 'clang', 'clang-cl']
self.default_cpp = ['cl', 'c++', 'g++', 'clang++', 'clang-cl']
else:
self.default_c = ['cc', 'gcc', 'clang']
self.default_cpp = ['c++', 'g++', 'clang++']
@ -359,6 +363,7 @@ class Environment:
self.default_rust = ['rustc']
self.default_static_linker = ['ar']
self.vs_static_linker = ['lib']
self.clang_cl_static_linker = ['llvm-lib']
self.gcc_static_linker = ['gcc-ar']
self.clang_static_linker = ['llvm-ar']
@ -537,7 +542,7 @@ This is probably wrong, it should always point to the native compiler.''' % evar
for compiler in compilers:
if isinstance(compiler, str):
compiler = [compiler]
if 'cl' in compiler or 'cl.exe' in compiler:
if not set(['cl', 'cl.exe', 'clang-cl', 'clang-cl.exe']).isdisjoint(compiler):
# Watcom C provides it's own cl.exe clone that mimics an older
# version of Microsoft's compiler. Since Watcom's cl.exe is
# just a wrapper, we skip using it if we detect its presence
@ -606,6 +611,18 @@ This is probably wrong, it should always point to the native compiler.''' % evar
compiler_type = CompilerType.ARM_WIN
cls = ArmclangCCompiler if lang == 'c' else ArmclangCPPCompiler
return cls(ccache + compiler, version, compiler_type, is_cross, exe_wrap, full_version=full_version)
if 'CL.EXE COMPATIBILITY' in out:
# if this is clang-cl masquerading as cl, detect it as cl, not
# clang
arg = '--version'
try:
p, out, err = Popen_safe(compiler + [arg])
except OSError as e:
popen_exceptions[' '.join(compiler + [arg])] = e
version = search_version(out)
is_64 = 'Target: x86_64' in out
cls = ClangClCCompiler if lang == 'c' else ClangClCPPCompiler
return cls(compiler, version, is_cross, exe_wrap, is_64)
if 'clang' in out:
if 'Apple' in out or mesonlib.for_darwin(want_cross, self):
compiler_type = CompilerType.CLANG_OSX
@ -903,7 +920,7 @@ This is probably wrong, it should always point to the native compiler.''' % evar
if evar in os.environ:
linkers = [shlex.split(os.environ[evar])]
elif isinstance(compiler, compilers.VisualStudioCCompiler):
linkers = [self.vs_static_linker]
linkers = [self.vs_static_linker, self.clang_cl_static_linker]
elif isinstance(compiler, compilers.GnuCompiler):
# Use gcc-ar if available; needed for LTO
linkers = [self.gcc_static_linker, self.default_static_linker]
@ -913,14 +930,14 @@ This is probably wrong, it should always point to the native compiler.''' % evar
elif isinstance(compiler, compilers.DCompiler):
# Prefer static linkers over linkers used by D compilers
if mesonlib.is_windows():
linkers = [self.vs_static_linker, compiler.get_linker_exelist()]
linkers = [self.vs_static_linker, self.clang_cl_static_linker, compiler.get_linker_exelist()]
else:
linkers = [self.default_static_linker, compiler.get_linker_exelist()]
else:
linkers = [self.default_static_linker]
popen_exceptions = {}
for linker in linkers:
if 'lib' in linker or 'lib.exe' in linker:
if not set(['lib', 'lib.exe', 'llvm-lib', 'llvm-lib.exe']).isdisjoint(linker):
arg = '/?'
else:
arg = '--version'
@ -929,7 +946,7 @@ This is probably wrong, it should always point to the native compiler.''' % evar
except OSError as e:
popen_exceptions[' '.join(linker + [arg])] = e
continue
if '/OUT:' in out or '/OUT:' in err:
if '/OUT:' in out.upper() or '/OUT:' in err.upper():
return VisualStudioLinker(linker)
if p.returncode == 0 and ('armar' in linker or 'armar.exe' in linker):
return ArmarLinker(linker)

@ -38,6 +38,7 @@ import subprocess
from collections import namedtuple
from pathlib import PurePath
import traceback
import functools
import importlib
@ -965,7 +966,7 @@ class CompilerHolder(InterpreterObject):
def cmd_array_method(self, args, kwargs):
return self.compiler.exelist
def determine_args(self, kwargs):
def determine_args(self, kwargs, mode='link'):
nobuiltins = kwargs.get('no_builtin_args', False)
if not isinstance(nobuiltins, bool):
raise InterpreterException('Type of no_builtin_args not a boolean.')
@ -981,6 +982,7 @@ class CompilerHolder(InterpreterObject):
if not nobuiltins:
opts = self.environment.coredata.compiler_options
args += self.compiler.get_option_compile_args(opts)
if mode == 'link':
args += self.compiler.get_option_link_args(opts)
args += mesonlib.stringlistify(kwargs.get('args', []))
return args
@ -1039,7 +1041,7 @@ class CompilerHolder(InterpreterObject):
testname = kwargs.get('name', '')
if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs, endl=None)
result = self.compiler.run(code, self.environment, extra_args, deps)
if len(testname) > 0:
@ -1094,7 +1096,7 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_member must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
had = self.compiler.has_members(typename, [membername], prefix,
self.environment, extra_args, deps)
@ -1122,7 +1124,7 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_members must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
had = self.compiler.has_members(typename, membernames, prefix,
self.environment, extra_args, deps)
@ -1175,7 +1177,7 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_type must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
had = self.compiler.has_type(typename, prefix, self.environment, extra_args, deps)
if had:
@ -1213,7 +1215,7 @@ class CompilerHolder(InterpreterObject):
raise InterpreterException('High argument of compute_int must be an int.')
if guess is not None and not isinstance(guess, int):
raise InterpreterException('Guess argument of compute_int must be an int.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
res = self.compiler.compute_int(expression, low, high, guess, prefix, self.environment, extra_args, deps)
mlog.log('Computing int of', mlog.bold(expression, True), msg, res)
@ -1234,7 +1236,7 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of sizeof must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
esize = self.compiler.sizeof(element, prefix, self.environment, extra_args, deps)
mlog.log('Checking for size of', mlog.bold(element, True), msg, esize)
@ -1256,7 +1258,7 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of get_define() must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
value = self.compiler.get_define(element, prefix, self.environment, extra_args, deps)
mlog.log('Fetching value of define', mlog.bold(element, True), msg, value)
@ -1281,7 +1283,7 @@ class CompilerHolder(InterpreterObject):
testname = kwargs.get('name', '')
if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs, endl=None)
result = self.compiler.compiles(code, self.environment, extra_args, deps)
if len(testname) > 0:
@ -1311,7 +1313,7 @@ class CompilerHolder(InterpreterObject):
testname = kwargs.get('name', '')
if not isinstance(testname, str):
raise InterpreterException('Testname argument must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs, endl=None)
result = self.compiler.links(code, self.environment, extra_args, deps)
if len(testname) > 0:
@ -1338,7 +1340,7 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_header must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
haz = self.compiler.check_header(hname, prefix, self.environment, extra_args, deps)
if haz:
@ -1363,7 +1365,7 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_header must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
haz = self.compiler.has_header(hname, prefix, self.environment, extra_args, deps)
if haz:
@ -1389,7 +1391,7 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_header_symbol must be a string.')
extra_args = self.determine_args(kwargs)
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment, extra_args, deps)
if haz:

@ -60,7 +60,7 @@ class WindowsModule(ExtensionModule):
if not rescomp or not rescomp.found():
comp = self.detect_compiler(state.compilers)
if comp.id == 'msvc':
if comp.id == 'msvc' or comp.id == 'clang-cl':
rescomp = ExternalProgram('rc', silent=True)
else:
rescomp = ExternalProgram('windres', silent=True)

@ -132,17 +132,17 @@ def platform_fix_name(fname, compiler, env):
if fname.startswith('?msvc:'):
fname = fname[6:]
if compiler != 'cl':
if compiler != 'msvc':
return None
if fname.startswith('?gcc:'):
fname = fname[5:]
if compiler == 'cl':
if compiler == 'msvc':
return None
if fname.startswith('?cygwin:'):
fname = fname[8:]
if compiler == 'cl' or not mesonlib.for_cygwin(env.is_cross_build(), env):
if compiler == 'msvc' or not mesonlib.for_cygwin(env.is_cross_build(), env):
return None
return fname
@ -687,14 +687,18 @@ def check_meson_commands_work():
def detect_system_compiler():
global system_compiler
if shutil.which('cl'):
system_compiler = 'cl'
elif shutil.which('cc'):
system_compiler = 'cc'
elif shutil.which('gcc'):
system_compiler = 'gcc'
else:
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
env = environment.Environment(None, build_dir, get_fake_options('/'))
try:
comp = env.detect_c_compiler(env.is_cross_build())
except:
raise RuntimeError("Could not find C compiler.")
system_compiler = comp.get_id()
# canonicalize for platform_fix_name()
if system_compiler == 'clang-cl':
system_compiler = 'msvc'
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Run the test suite of Meson.")

@ -601,7 +601,7 @@ class InternalTests(unittest.TestCase):
elif is_cygwin():
self._test_all_naming(cc, env, patterns, 'cygwin')
elif is_windows():
if cc.get_id() == 'msvc':
if cc.get_argument_syntax() == 'msvc':
self._test_all_naming(cc, env, patterns, 'windows-msvc')
else:
self._test_all_naming(cc, env, patterns, 'windows-mingw')
@ -624,10 +624,6 @@ class InternalTests(unittest.TestCase):
with PatchModule(mesonbuild.compilers.c.for_windows,
'mesonbuild.compilers.c.for_windows', true):
self._test_all_naming(cc, env, patterns, 'windows-mingw')
cc.id = 'msvc'
with PatchModule(mesonbuild.compilers.c.for_windows,
'mesonbuild.compilers.c.for_windows', true):
self._test_all_naming(cc, env, patterns, 'windows-msvc')
def test_pkgconfig_parse_libs(self):
'''
@ -679,7 +675,7 @@ class InternalTests(unittest.TestCase):
bar_dep = PkgConfigDependency('bar', env, kwargs)
self.assertEqual(bar_dep.get_link_args(), [(p2 / 'libbar.a').as_posix()])
internal_dep = PkgConfigDependency('internal', env, kwargs)
if compiler.get_id() == 'msvc':
if compiler.get_argument_syntax() == 'msvc':
self.assertEqual(internal_dep.get_link_args(), [])
else:
link_args = internal_dep.get_link_args()
@ -1602,6 +1598,7 @@ class AllPlatformTests(BasePlatformTests):
clang = mesonbuild.compilers.ClangCompiler
intel = mesonbuild.compilers.IntelCompiler
msvc = mesonbuild.compilers.VisualStudioCCompiler
clangcl = mesonbuild.compilers.ClangClCCompiler
ar = mesonbuild.linkers.ArLinker
lib = mesonbuild.linkers.VisualStudioLinker
langs = [('c', 'CC'), ('cpp', 'CXX')]
@ -1623,6 +1620,9 @@ class AllPlatformTests(BasePlatformTests):
if ebase.startswith('g') or ebase.endswith(('-gcc', '-g++')):
self.assertIsInstance(ecc, gnu)
self.assertIsInstance(elinker, ar)
elif 'clang-cl' in ebase:
self.assertIsInstance(ecc, clangcl)
self.assertIsInstance(elinker, lib)
elif 'clang' in ebase:
self.assertIsInstance(ecc, clang)
self.assertIsInstance(elinker, ar)
@ -1696,6 +1696,8 @@ class AllPlatformTests(BasePlatformTests):
wrapperlinker_s += shlex.quote(w) + ' '
os.environ['AR'] = wrapperlinker_s
wlinker = env.detect_static_linker(wcc)
# Pop it so we don't use it for the next detection
evalue = os.environ.pop('AR')
# Must be the same type since it's a wrapper around the same exelist
self.assertIs(type(cc), type(wcc))
self.assertIs(type(linker), type(wlinker))
@ -1994,7 +1996,7 @@ int main(int argc, char **argv) {
def pbcompile(self, compiler, source, objectfile, extra_args=[]):
cmd = compiler.get_exelist()
if compiler.id == 'msvc':
if compiler.get_argument_syntax() == 'msvc':
cmd += ['/nologo', '/Fo' + objectfile, '/c', source] + extra_args
else:
cmd += ['-c', source, '-o', objectfile] + extra_args
@ -2016,7 +2018,7 @@ int main(int argc, char **argv) {
def build_static_lib(self, compiler, linker, source, objectfile, outfile, extra_args=None):
if extra_args is None:
extra_args = []
if compiler.id == 'msvc':
if compiler.get_argument_syntax() == 'msvc':
link_cmd = ['lib', '/NOLOGO', '/OUT:' + outfile, objectfile]
else:
link_cmd = ['ar', 'csr', outfile, objectfile]
@ -2049,9 +2051,10 @@ int main(int argc, char **argv) {
def build_shared_lib(self, compiler, source, objectfile, outfile, impfile, extra_args=None):
if extra_args is None:
extra_args = []
if compiler.id == 'msvc':
link_cmd = ['link', '/NOLOGO', '/DLL', '/DEBUG',
'/IMPLIB:' + impfile, '/OUT:' + outfile, objectfile]
if compiler.get_argument_syntax() == 'msvc':
link_cmd = compiler.get_linker_exelist() + [
'/NOLOGO', '/DLL', '/DEBUG', '/IMPLIB:' + impfile,
'/OUT:' + outfile, objectfile]
else:
extra_args += ['-fPIC']
link_cmd = compiler.get_exelist() + ['-shared', '-o', outfile, objectfile]
@ -2069,7 +2072,7 @@ int main(int argc, char **argv) {
source = os.path.join(tdir, 'alexandria.c')
objectfile = os.path.join(tdir, 'alexandria.' + object_suffix)
impfile = os.path.join(tdir, 'alexandria.lib')
if cc.id == 'msvc':
if cc.get_argument_syntax() == 'msvc':
shlibfile = os.path.join(tdir, 'alexandria.' + shared_suffix)
elif is_cygwin():
shlibfile = os.path.join(tdir, 'cygalexandria.' + shared_suffix)
@ -2107,7 +2110,7 @@ int main(int argc, char **argv) {
objectfile = os.path.join(testdir, 'foo.' + objext)
stlibfile = os.path.join(testdir, 'libfoo.a')
impfile = os.path.join(testdir, 'foo.lib')
if cc.id == 'msvc':
if cc.get_argument_syntax() == 'msvc':
shlibfile = os.path.join(testdir, 'foo.' + shext)
elif is_cygwin():
shlibfile = os.path.join(testdir, 'cygfoo.' + shext)
@ -2449,7 +2452,7 @@ recommended as it is not supported on some platforms''')
testdirlib = os.path.join(testdirbase, 'lib')
extra_args = None
env = get_fake_env(testdirlib, self.builddir, self.prefix)
if env.detect_c_compiler(False).get_id() != 'msvc':
if env.detect_c_compiler(False).get_id() not in ['msvc', 'clang-cl']:
# static libraries are not linkable with -l with msvc because meson installs them
# as .a files which unix_args_to_native will not know as it expects libraries to use
# .lib as extension. For a DLL the import library is installed as .lib. Thus for msvc
@ -3058,7 +3061,7 @@ class WindowsTests(BasePlatformTests):
testdir = os.path.join(self.platform_test_dir, '1 basic')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = env.detect_c_compiler(False)
if cc.id != 'msvc':
if cc.get_argument_syntax() != 'msvc':
raise unittest.SkipTest('Not using MSVC')
# To force people to update this test, and also test
self.assertEqual(set(cc.ignore_libs), {'c', 'm', 'pthread', 'dl', 'rt'})
@ -3070,7 +3073,7 @@ class WindowsTests(BasePlatformTests):
# resource compiler depfile generation is not yet implemented for msvc
env = get_fake_env(testdir, self.builddir, self.prefix)
depfile_works = env.detect_c_compiler(False).get_id() != 'msvc'
depfile_works = env.detect_c_compiler(False).get_id() not in ['msvc', 'clang-cl']
self.init(testdir)
self.build()
@ -3097,9 +3100,14 @@ class WindowsTests(BasePlatformTests):
self.utime(os.path.join(testdir, 'res', 'resource.h'))
self.assertRebuiltTarget('prog_1')
@unittest.skipIf(shutil.which('cl') is None, 'Test only applies to VS')
def test_msvc_cpp17(self):
testdir = os.path.join(self.unit_test_dir, '45 vscpp17')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = env.detect_c_compiler(False)
if cc.get_argument_syntax() != 'msvc':
raise unittest.SkipTest('Test only applies to MSVC-like compilers')
try:
self.init(testdir)
except subprocess.CalledProcessError:

@ -6,38 +6,30 @@ from __future__ import print_function
# file and a header file.
import sys, os
import shutil, subprocess
import subprocess
with open(sys.argv[1]) as f:
funcname = f.readline().strip()
outdir = sys.argv[2]
buildtype_args = sys.argv[3]
compiler_type = sys.argv[4]
compiler = sys.argv[5:]
if not os.path.isdir(outdir):
print('Outdir does not exist.')
sys.exit(1)
# Emulate the environment.detect_c_compiler() logic
compiler = os.environ.get('CC', None)
if not compiler:
compiler = shutil.which('cl') or \
shutil.which('gcc') or \
shutil.which('clang') or \
shutil.which('cc')
compbase = os.path.basename(compiler)
if 'cl' in compbase and 'clang' not in compbase:
if compiler_type == 'msvc':
libsuffix = '.lib'
is_vs = True
compiler = 'cl'
if any(['clang-cl' in c for c in compiler]):
linker = 'llvm-lib'
else:
linker = 'lib'
else:
libsuffix = '.a'
is_vs = False
linker = 'ar'
if compiler is None:
print('No known compilers found.')
sys.exit(1)
objsuffix = '.o'
@ -70,9 +62,9 @@ with open(tmpc, 'w') as f:
''' % funcname)
if is_vs:
subprocess.check_call([compiler, '/nologo', '/c', buildtype_args, '/Fo' + outo, tmpc])
subprocess.check_call(compiler + ['/nologo', '/c', buildtype_args, '/Fo' + outo, tmpc])
else:
subprocess.check_call([compiler, '-c', '-o', outo, tmpc])
subprocess.check_call(compiler + ['-c', '-o', outo, tmpc])
with open(tmpc, 'w') as f:
f.write('''int %s_in_lib() {
@ -81,10 +73,10 @@ with open(tmpc, 'w') as f:
''' % funcname)
if is_vs:
subprocess.check_call([compiler, '/nologo', '/c', '/Fo' + tmpo, tmpc])
subprocess.check_call(compiler + ['/nologo', '/c', '/Fo' + tmpo, tmpc])
subprocess.check_call([linker, '/NOLOGO', '/OUT:' + outa, tmpo])
else:
subprocess.check_call([compiler, '-c', '-o', tmpo, tmpc])
subprocess.check_call(compiler + ['-c', '-o', tmpo, tmpc])
subprocess.check_call([linker, 'csr', outa, tmpo])
os.unlink(tmpo)

@ -3,7 +3,8 @@ py3_bin = import('python3').find_python()
buildtype = get_option('buildtype')
buildtype_args = '-Dfooxxx' # a useless compiler argument
if meson.get_compiler('c').get_id() == 'msvc'
cc = meson.get_compiler('c')
if cc.get_argument_syntax() == 'msvc'
# We need our manually generated code to use the same CRT as the executable.
# Taken from compilers.py since build files do not have access to this.
if buildtype == 'debug'
@ -21,5 +22,5 @@ endif
generated = custom_target('manygen',
output : outfiles,
input : ['funcinfo.def'],
command : [py3_bin, gen[0], '@INPUT@', '@OUTDIR@', buildtype_args],
command : [py3_bin, gen[0], '@INPUT@', '@OUTDIR@', buildtype_args, cc.get_argument_syntax(), cc.cmd_array()],
)

@ -7,7 +7,7 @@ project('comparer', 'c')
include_dir = meson.current_source_dir() + '/include'
default_c_args = ['-I' + include_dir]
if meson.get_compiler('c').get_id() == 'msvc'
if meson.get_compiler('c').get_argument_syntax() == 'msvc'
default_c_args += ['/Faasm output\\']
# Hack to create the 'asm output' directory in the builddir
subdir('asm output')

@ -28,15 +28,18 @@ foreach lang : ['c', 'cpp']
# MSVC cannot directly compile assembly files, so we pass it through the
# cl.exe pre-processor first and then assemble it with the ml.exe assembler.
# Then we can link it into the executable.
if cc_id == 'msvc'
cl = find_program('cl')
if cc.get_argument_syntax() == 'msvc'
cl = cc.cmd_array()
if cpu == 'x86'
ml = find_program('ml')
ml = find_program('ml', required: false)
elif cpu == 'x86_64'
ml = find_program('ml64')
ml = find_program('ml64', required: false)
else
error('Unsupported cpu family: "' + cpu + '"')
endif
if not ml.found()
error('MESON_SKIP_TEST: ML (masm) not found')
endif
# Preprocess file (ml doesn't support pre-processing)
preproc_name = lang + square_base + '.i'
square_preproc = custom_target(lang + square_impl + 'preproc',

@ -15,7 +15,7 @@ endif
sources = ['trivial.cc']
# If the compiler cannot compile assembly, don't use it
if meson.get_compiler('cpp').get_id() != 'msvc'
if not ['msvc', 'clang-cl'].contains(meson.get_compiler('cpp').get_id())
sources += ['retval-' + cpu + '.S']
cpp_args = ['-DUSE_ASM']
message('Using ASM')

@ -1,5 +1,5 @@
project('nobuilddir', 'c',
default_options : 'werror=true')
default_options : ['werror=true', 'buildtype=plain'])
cc = meson.get_compiler('c')

@ -5,8 +5,9 @@ exe = executable(
cpp_pch : ['pch/main_pch.cc', 'pch/main.h'],
)
# test pch when only a header is given (not supported by msvc)
cc = meson.get_compiler('c')
if cc.get_id() != 'msvc'
if not ['msvc', 'clang-cl'].contains(cc.get_id())
exe2 = executable(
'prog2',
files('main.cc', 'func.c'),

@ -2,8 +2,8 @@ project('generated assembly', 'c')
cc = meson.get_compiler('c')
if cc.get_id() == 'msvc'
error('MESON_SKIP_TEST: assembly files cannot be compiled directly by MSVC')
if ['msvc', 'clang-cl'].contains(cc.get_id())
error('MESON_SKIP_TEST: assembly files cannot be compiled directly by the compiler')
endif
cpu = host_machine.cpu_family()

@ -9,7 +9,7 @@ if not supported_cpus.contains(cpu)
error('MESON_SKIP_TEST unsupported cpu:' + cpu)
endif
if meson.get_compiler('c').get_id() == 'msvc'
if meson.get_compiler('c').get_argument_syntax() == 'msvc'
error('MESON_SKIP_TEST MSVC can\'t compile assembly')
endif

@ -25,9 +25,10 @@ libc = static_library('cfoo', ['foo.c', 'foo.h'])
# ourselves at configure time and then 'find' it with cxx.find_library().
cxx = meson.get_compiler('cpp')
if cxx.get_id() == 'msvc'
if cxx.get_argument_syntax() == 'msvc'
static_linker = find_program('lib', 'llvm-lib')
compile_cmd = ['/c', '@INPUT@', '/Fo@OUTPUT@']
stlib_cmd = ['lib', '/OUT:@OUTPUT@', '@INPUT@']
stlib_cmd = [static_linker, '/OUT:@OUTPUT@', '@INPUT@']
else
compile_cmd = ['-c', '-fPIC', '@INPUT@', '-o', '@OUTPUT@']
stlib_cmd = ['ar', 'csr', '@OUTPUT@', '@INPUT@']

@ -3,7 +3,7 @@ project('has link arg', 'c', 'cpp')
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
if cc.get_id() == 'msvc'
if cc.get_argument_syntax() == 'msvc'
is_arg = '/OPT:REF'
useless = '/DEBUG'
isnt_arg = '/iambroken'

@ -10,6 +10,9 @@ endif
if cc.get_id() == 'msvc' and cc.version().version_compare('<17')
error('MESON_SKIP_TEST msvc is too old to support OpenMP.')
endif
if cc.get_id() == 'clang-cl'
error('MESON_SKIP_TEST clang-cl does not support OpenMP.')
endif
if host_machine.system() == 'darwin'
error('MESON_SKIP_TEST macOS does not support OpenMP.')
endif

@ -19,7 +19,7 @@ project('gcc func attributes', ['c', 'cpp'])
c = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
expected_result = c.get_id() != 'msvc'
expected_result = not ['msvc', 'clang-cl'].contains(c.get_id())
# Q: Why is ifunc not in this list or any of the below lists?
# A: It's too damn hard to figure out if you actually support it, since it
@ -57,8 +57,6 @@ if c.get_id() != 'intel'
attributes += 'weakref'
endif
expected_result = c.get_id() != 'msvc'
# These are unsupported on darwin with apple clang 9.1.0
if host_machine.system() != 'darwin'
attributes += 'alias'
@ -97,7 +95,7 @@ foreach a : ['dllexport', 'dllimport']
endforeach
message('checking get_supported_function_attributes')
if c.get_id() != 'msvc'
if not ['msvc', 'clang-cl'].contains(c.get_id())
multi_expected = attributes
else
multi_expected = []

@ -7,7 +7,7 @@ cc = meson.get_compiler('c')
if ['gcc', 'lcc', 'clang'].contains(cc.get_id())
expected = 'gcc'
elif cc.get_id() == 'msvc'
elif ['msvc', 'clang-cl'].contains(cc.get_id())
expected = 'msvc'
elif cc.get_id() == 'intel'
if host_machine.system() == 'windows'

@ -6,11 +6,9 @@ project('default options', 'cpp', 'c', default_options : [
'warning_level=3',
])
cpp_id = meson.get_compiler('cpp').get_id()
assert(get_option('buildtype') == 'debugoptimized', 'Build type default value wrong.')
if cpp_id == 'msvc'
if meson.get_compiler('cpp').get_argument_syntax() == 'msvc'
cpp_eh = get_option('cpp_eh')
assert(cpp_eh == 'none', 'MSVC eh value is "' + cpp_eh + '" instead of "none"')
else
@ -33,4 +31,3 @@ assert(w_level == '3', 'warning level "' + w_level + '" instead of "3"')
# assert(not cc.compiles('int foobar;'), 'Default arg not used in test.')
# assert(cc.compiles('int foobar;', no_builtin_args : true), 'No_builtin did not disable builtins.')
# endif

@ -17,6 +17,10 @@ console_prog = executable('console_prog', 'console_prog.c', gui_app: false)
tester = find_program('gui_app_tester.py')
tool = find_program('objdump', 'dumpbin')
test('is_gui', tester, args: [tool.path(), gui_prog, '2'])
test('not_gui', tester, args: [tool.path(), console_prog, '3'])
tool = find_program('objdump', 'dumpbin', required: false)
# TODO: when 'llvm-objdump -f' emits the subsystem type, we could use that also
if tool.found()
test('is_gui', tester, args: [tool.path(), gui_prog, '2'])
test('not_gui', tester, args: [tool.path(), console_prog, '3'])
endif

Loading…
Cancel
Save