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. 23
      mesonbuild/compilers/cpp.py
  9. 2
      mesonbuild/compilers/d.py
  10. 4
      mesonbuild/dependencies/boost.py
  11. 31
      mesonbuild/environment.py
  12. 30
      mesonbuild/interpreter.py
  13. 2
      mesonbuild/modules/windows.py
  14. 26
      run_project_tests.py
  15. 42
      run_unittests.py
  16. 32
      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 arch: x64
compiler: msvc2017 compiler: msvc2017
backend: vs2017 backend: vs2017
clangclx64ninja:
arch: x64
compiler: clang-cl
backend: ninja
steps: steps:
- template: ci/azure-steps.yml - template: ci/azure-steps.yml

@ -48,13 +48,34 @@ steps:
# add downloads to PATH # add downloads to PATH
$env:Path = "$env:SYSTEM_WORKFOLDER;$env:Path" $env:Path = "$env:SYSTEM_WORKFOLDER;$env:Path"
$origPath = $env:Path
# import visual studio variables # import visual studio variables
if ($env:compiler -eq 'msvc2015') {
$vsver = $env:compiler.Replace('msvc', '')
} else {
$vsver = '2017'
}
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-Module Pscx -Scope CurrentUser -AllowClobber Install-Module Pscx -Scope CurrentUser -AllowClobber
Install-Module VSSetup -Scope CurrentUser Install-Module VSSetup -Scope CurrentUser
$vsver = $env:compiler.Replace('msvc', '')
Import-VisualStudioVars -VisualStudioVersion $vsver -Architecture $(arch) 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 # 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) # (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) { if ((Get-Command "resgen.exe" -ErrorAction SilentlyContinue) -eq $null) {

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

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

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

@ -170,7 +170,7 @@ class CCompiler(Compiler):
else: else:
# GNU ld and LLVM lld # GNU ld and LLVM lld
return ['-Wl,--allow-shlib-undefined'] return ['-Wl,--allow-shlib-undefined']
elif self.id == 'msvc': elif isinstance(self, VisualStudioCCompiler):
# link.exe # link.exe
return ['/FORCE:UNRESOLVED'] return ['/FORCE:UNRESOLVED']
# FIXME: implement other linkers # FIXME: implement other linkers
@ -392,6 +392,8 @@ class CCompiler(Compiler):
return self.compiles(t.format(**fargs), env, extra_args, dependencies) return self.compiles(t.format(**fargs), env, extra_args, dependencies)
def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): 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: if extra_args is None:
extra_args = [] extra_args = []
elif isinstance(extra_args, str): elif isinstance(extra_args, str):
@ -890,7 +892,7 @@ class CCompiler(Compiler):
stlibext = ['a'] stlibext = ['a']
# We've always allowed libname to be both `foo` and `libfoo`, # We've always allowed libname to be both `foo` and `libfoo`,
# and now people depend on it # 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'] prefixes = ['lib']
else: else:
prefixes = ['lib', ''] prefixes = ['lib', '']
@ -900,7 +902,7 @@ class CCompiler(Compiler):
elif for_windows(env.is_cross_build(), env): elif for_windows(env.is_cross_build(), env):
# FIXME: .lib files can be import or static so we should read the # 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. # file, figure out which one it is, and reject the wrong kind.
if self.id == 'msvc': if isinstance(self, VisualStudioCCompiler):
shlibext = ['lib'] shlibext = ['lib']
else: else:
shlibext = ['dll.a', 'lib', 'dll'] shlibext = ['dll.a', 'lib', 'dll']
@ -1296,7 +1298,7 @@ class VisualStudioCCompiler(CCompiler):
def get_buildtype_args(self, buildtype): def get_buildtype_args(self, buildtype):
args = compilers.msvc_buildtype_args[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'] args = [arg for arg in args if arg != '/Gw']
return args return args
@ -1314,6 +1316,8 @@ class VisualStudioCCompiler(CCompiler):
def get_pch_use_args(self, pch_dir, header): def get_pch_use_args(self, pch_dir, header):
base = os.path.basename(header) base = os.path.basename(header)
if self.id == 'clang-cl':
base = header
pchname = self.get_pch_name(header) pchname = self.get_pch_name(header)
return ['/FI' + base, '/Yu' + base, '/Fp' + os.path.join(pch_dir, pchname)] return ['/FI' + base, '/Yu' + base, '/Fp' + os.path.join(pch_dir, pchname)]
@ -1341,7 +1345,12 @@ class VisualStudioCCompiler(CCompiler):
return [] return []
def get_linker_exelist(self): 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): def get_linker_always_args(self):
return ['/nologo'] 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 # 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): def has_arguments(self, args, env, code, mode):
warning_text = '4044' if mode == 'link' else '9002' 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: with self._build_wrapper(code, env, extra_args=args, mode=mode) as p:
if p.returncode != 0: if p.returncode != 0:
return False return False
@ -1464,7 +1475,7 @@ class VisualStudioCCompiler(CCompiler):
# build obviously, which is why we only do this when PCH is on. # 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 # 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 # 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 args = ['/FS'] + args
return args return args
@ -1481,7 +1492,7 @@ class VisualStudioCCompiler(CCompiler):
def get_instruction_set_args(self, instruction_set): def get_instruction_set_args(self, instruction_set):
if self.is_64: if self.is_64:
return vs64_instruction_set_args.get(instruction_set, None) 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 # VS documentation says that this exists and should work, but
# it does not. The headers do not contain AVX intrinsics # it does not. The headers do not contain AVX intrinsics
# and the can not be called. # and the can not be called.
@ -1489,6 +1500,10 @@ class VisualStudioCCompiler(CCompiler):
return vs32_instruction_set_args.get(instruction_set, None) return vs32_instruction_set_args.get(instruction_set, None)
def get_toolset_version(self): 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 # See boost/config/compiler/visualc.cpp for up to date mapping
try: try:
version = int(''.join(self.version.split('.')[0:2])) version = int(''.join(self.version.split('.')[0:2]))
@ -1546,6 +1561,10 @@ class VisualStudioCCompiler(CCompiler):
def get_argument_syntax(self): def get_argument_syntax(self):
return 'msvc' 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): class ArmCCompiler(ArmCompiler, CCompiler):
def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, **kwargs): def __init__(self, exelist, version, compiler_type, is_cross, exe_wrapper=None, **kwargs):

@ -19,7 +19,7 @@ from .. import coredata
from .. import mlog from .. import mlog
from ..mesonlib import MesonException, version_compare from ..mesonlib import MesonException, version_compare
from .c import CCompiler, VisualStudioCCompiler from .c import CCompiler, VisualStudioCCompiler, ClangClCCompiler
from .compilers import ( from .compilers import (
CompilerType, CompilerType,
gnu_winlibs, gnu_winlibs,
@ -310,12 +310,15 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
def get_options(self): def get_options(self):
cpp_stds = ['none', 'c++11', 'vc++11'] cpp_stds = ['none', 'c++11', 'vc++11']
# Visual Studio 2015 and later if self.id == 'clang-cl':
if version_compare(self.version, '>=19'): cpp_stds.extend(['c++14', 'vc++14', 'c++17', 'vc++17', 'c++latest'])
cpp_stds.extend(['c++14', 'vc++14', 'c++latest', 'vc++latest']) else:
# Visual Studio 2017 and later # Visual Studio 2015 and later
if version_compare(self.version, '>=19.11'): if version_compare(self.version, '>=19'):
cpp_stds.extend(['c++17', 'vc++17']) cpp_stds.extend(['c++14', 'vc++14', 'c++latest', 'vc++latest'])
# Visual Studio 2017 and later
if version_compare(self.version, '>=19.11'):
cpp_stds.extend(['c++17', 'vc++17'])
opts = CPPCompiler.get_options(self) opts = CPPCompiler.get_options(self)
opts.update({'cpp_eh': coredata.UserComboOption('cpp_eh', opts.update({'cpp_eh': coredata.UserComboOption('cpp_eh',
@ -356,7 +359,7 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
# which means setting the C++ standard version to C++14, in compilers that support it # which means setting the C++ standard version to C++14, in compilers that support it
# (i.e., after VS2015U3) # (i.e., after VS2015U3)
# if one is using anything before that point, one cannot set the standard. # 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; ' mlog.warning('MSVC does not support C++11; '
'attempting best effort; setting the standard to C++14') 'attempting best effort; setting the standard to C++14')
args.append('/std:c++14') args.append('/std:c++14')
@ -378,6 +381,10 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler):
# so just use the plain C args. # so just use the plain C args.
return VisualStudioCCompiler.get_compiler_check_args(self) 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): class ArmCPPCompiler(ArmCompiler, CPPCompiler):
def __init__(self, exelist, version, compiler_type, is_cross, exe_wrap=None, **kwargs): 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)] return ['-Wl,-rpath,{}'.format(paths)]
def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): 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: if extra_args is None:
extra_args = [] extra_args = []
elif isinstance(extra_args, str): elif isinstance(extra_args, str):

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

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

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

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

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

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

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

@ -3,7 +3,8 @@ py3_bin = import('python3').find_python()
buildtype = get_option('buildtype') buildtype = get_option('buildtype')
buildtype_args = '-Dfooxxx' # a useless compiler argument 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. # 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. # Taken from compilers.py since build files do not have access to this.
if buildtype == 'debug' if buildtype == 'debug'
@ -21,5 +22,5 @@ endif
generated = custom_target('manygen', generated = custom_target('manygen',
output : outfiles, output : outfiles,
input : ['funcinfo.def'], 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' include_dir = meson.current_source_dir() + '/include'
default_c_args = ['-I' + include_dir] 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\\'] default_c_args += ['/Faasm output\\']
# Hack to create the 'asm output' directory in the builddir # Hack to create the 'asm output' directory in the builddir
subdir('asm output') subdir('asm output')

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

@ -15,7 +15,7 @@ endif
sources = ['trivial.cc'] sources = ['trivial.cc']
# If the compiler cannot compile assembly, don't use it # 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'] sources += ['retval-' + cpu + '.S']
cpp_args = ['-DUSE_ASM'] cpp_args = ['-DUSE_ASM']
message('Using ASM') message('Using ASM')

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

@ -5,8 +5,9 @@ exe = executable(
cpp_pch : ['pch/main_pch.cc', 'pch/main.h'], 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') cc = meson.get_compiler('c')
if cc.get_id() != 'msvc' if not ['msvc', 'clang-cl'].contains(cc.get_id())
exe2 = executable( exe2 = executable(
'prog2', 'prog2',
files('main.cc', 'func.c'), files('main.cc', 'func.c'),

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

@ -9,7 +9,7 @@ if not supported_cpus.contains(cpu)
error('MESON_SKIP_TEST unsupported cpu:' + cpu) error('MESON_SKIP_TEST unsupported cpu:' + cpu)
endif 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') error('MESON_SKIP_TEST MSVC can\'t compile assembly')
endif 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(). # ourselves at configure time and then 'find' it with cxx.find_library().
cxx = meson.get_compiler('cpp') 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@'] compile_cmd = ['/c', '@INPUT@', '/Fo@OUTPUT@']
stlib_cmd = ['lib', '/OUT:@OUTPUT@', '@INPUT@'] stlib_cmd = [static_linker, '/OUT:@OUTPUT@', '@INPUT@']
else else
compile_cmd = ['-c', '-fPIC', '@INPUT@', '-o', '@OUTPUT@'] compile_cmd = ['-c', '-fPIC', '@INPUT@', '-o', '@OUTPUT@']
stlib_cmd = ['ar', 'csr', '@OUTPUT@', '@INPUT@'] stlib_cmd = ['ar', 'csr', '@OUTPUT@', '@INPUT@']

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

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

@ -19,7 +19,7 @@ project('gcc func attributes', ['c', 'cpp'])
c = meson.get_compiler('c') c = meson.get_compiler('c')
cpp = meson.get_compiler('cpp') 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? # 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 # 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' attributes += 'weakref'
endif endif
expected_result = c.get_id() != 'msvc'
# These are unsupported on darwin with apple clang 9.1.0 # These are unsupported on darwin with apple clang 9.1.0
if host_machine.system() != 'darwin' if host_machine.system() != 'darwin'
attributes += 'alias' attributes += 'alias'
@ -97,7 +95,7 @@ foreach a : ['dllexport', 'dllimport']
endforeach endforeach
message('checking get_supported_function_attributes') message('checking get_supported_function_attributes')
if c.get_id() != 'msvc' if not ['msvc', 'clang-cl'].contains(c.get_id())
multi_expected = attributes multi_expected = attributes
else else
multi_expected = [] multi_expected = []

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

@ -6,11 +6,9 @@ project('default options', 'cpp', 'c', default_options : [
'warning_level=3', 'warning_level=3',
]) ])
cpp_id = meson.get_compiler('cpp').get_id()
assert(get_option('buildtype') == 'debugoptimized', 'Build type default value wrong.') 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') cpp_eh = get_option('cpp_eh')
assert(cpp_eh == 'none', 'MSVC eh value is "' + cpp_eh + '" instead of "none"') assert(cpp_eh == 'none', 'MSVC eh value is "' + cpp_eh + '" instead of "none"')
else 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(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.') # assert(cc.compiles('int foobar;', no_builtin_args : true), 'No_builtin did not disable builtins.')
# endif # endif

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