Skip to content

Commit

Permalink
Better clang Windows support (#11492)
Browse files Browse the repository at this point in the history
* fix conftest

* new test clang-mingw backend

* wip

* wip

* test passing

* solved clang CMake verify

* wip

* C example

* wip

* wip

* wip

* wip

* wip

* fix settings.yml

* fix test

* review and update compiler versions

* fix

* Update conan/tools/microsoft/visual.py

Co-authored-by: Luis Caro Campos <3535649+jcar87@users.noreply.github.com>

* fix missing binary search message (#12184)

* add GCC 11.3 to the list of compilers in the settings file (#12215)

* review

Co-authored-by: Luis Caro Campos <3535649+jcar87@users.noreply.github.com>
Co-authored-by: Marian Klymov <nekto1989@gmail.com>
  • Loading branch information
3 people committed Sep 30, 2022
1 parent 47353ab commit 75329f6
Show file tree
Hide file tree
Showing 10 changed files with 392 additions and 64 deletions.
10 changes: 9 additions & 1 deletion conan/tools/_compilers.py
Expand Up @@ -18,7 +18,15 @@ def architecture_flag(settings):
if the_os == "Android":
return ""

if str(compiler) in ['gcc', 'apple-clang', 'clang', 'sun-cc']:
if compiler == "clang" and the_os == "Windows":
# LLVM/Clang and VS/Clang must define runtime. msys2 clang won't
runtime = settings.get_safe("compiler.runtime") # runtime is Windows only
if runtime is not None:
return ""
# TODO: Maybe Clang-Mingw runtime does, but with C++ is impossible to test
return {"x86_64": "-m64",
"x86": "-m32"}.get(arch, "")
elif str(compiler) in ['gcc', 'apple-clang', 'clang', 'sun-cc']:
if str(the_os) == 'Macos' and str(subsystem) == 'catalyst':
# FIXME: This might be conflicting with Autotools --target cli arg
apple_arch = to_apple_arch(arch)
Expand Down
31 changes: 24 additions & 7 deletions conan/tools/cmake/toolchain/blocks.py
Expand Up @@ -102,8 +102,15 @@ class VSRuntimeBlock(Block):
def context(self):
# Parsing existing toolchain file to get existing configured runtimes
settings = self._conanfile.settings
if settings.get_safe("os") != "Windows":
return

compiler = settings.get_safe("compiler")
if compiler not in ("Visual Studio", "msvc", "intel-cc"):
if compiler not in ("Visual Studio", "msvc", "clang", "intel-cc"):
return

runtime = settings.get_safe("compiler.runtime")
if runtime is None:
return

config_dict = {}
Expand All @@ -119,18 +126,26 @@ def context(self):
build_type = settings.get_safe("build_type") # FIXME: change for configuration
if build_type is None:
return None
runtime = settings.get_safe("compiler.runtime")

if compiler == "Visual Studio":
config_dict[build_type] = {"MT": "MultiThreaded",
"MTd": "MultiThreadedDebug",
"MD": "MultiThreadedDLL",
"MDd": "MultiThreadedDebugDLL"}[runtime]
if compiler == "msvc" or compiler == "intel-cc":
elif compiler == "msvc" or compiler == "intel-cc" or compiler == "clang":
runtime_type = settings.get_safe("compiler.runtime_type")
rt = "MultiThreadedDebug" if runtime_type == "Debug" else "MultiThreaded"
if runtime != "static":
rt += "DLL"
config_dict[build_type] = rt

# If clang is being used the CMake check of compiler will try to create a simple
# test application, and will fail because the Debug runtime is not there
if compiler == "clang":
if config_dict.get("Debug") is None:
clang_rt = "MultiThreadedDebug" + ("DLL" if runtime != "static" else "")
config_dict["Debug"] = clang_rt

return {"vs_runtimes": config_dict}


Expand Down Expand Up @@ -693,10 +708,11 @@ def _get_toolset(self, generator):
toolset = msvc_version_to_toolset_version(compiler_version)
elif compiler == "clang":
if generator and "Visual" in generator:
if "Visual Studio 16" in generator:
if "Visual Studio 16" in generator or "Visual Studio 17" in generator:
toolset = "ClangCL"
else:
raise ConanException("CMakeToolchain compiler=clang only supported VS 16")
raise ConanException("CMakeToolchain with compiler=clang and a CMake "
"'Visual Studio' generator requires VS16 or VS17")
toolset_arch = self._conanfile.conf.get("tools.cmake.cmaketoolchain:toolset_arch")
if toolset_arch is not None:
toolset_arch = "host={}".format(toolset_arch)
Expand All @@ -713,7 +729,7 @@ def _get_generator_platform(self, generator):
if settings.get_safe("os") == "WindowsCE":
return settings.get_safe("os.platform")

if (compiler in ("Visual Studio", "msvc") or compiler_base == "Visual Studio") and \
if (compiler in ("Visual Studio", "msvc", "clang") or compiler_base == "Visual Studio") and \
generator and "Visual" in generator:
return {"x86": "Win32",
"x86_64": "x64",
Expand All @@ -730,7 +746,8 @@ def _get_compiler(self, generator):
# TODO: Check if really necessary now that conanvcvars is used
if "Ninja" in str(generator) and is_msvc(self._conanfile):
compiler_c = compiler_cpp = "cl"
elif os_ == "Windows" and compiler == "clang" and "Visual" not in str(generator):
elif os_ == "Windows" and compiler == "clang" and "Visual" not in generator:
# definition of compiler only if not using the VS Clang toolset
compiler_rc = "clang"
compiler_c = "clang"
compiler_cpp = "clang++"
Expand Down
23 changes: 20 additions & 3 deletions conan/tools/microsoft/visual.py
Expand Up @@ -69,12 +69,29 @@ def generate(self, scope="build"):
return

compiler = conanfile.settings.get_safe("compiler")
if compiler != "Visual Studio" and compiler != "msvc":
if compiler not in ("Visual Studio", "msvc", "clang"):
return

vs_version = vs_ide_version(conanfile)
if compiler == "clang":
# The vcvars only needed for LLVM/Clang and VS ClangCL, who define runtime
if not conanfile.settings.get_safe("compiler.runtime"):
# NMake Makefiles will need vcvars activated, for VS target, defined with runtime
return
toolset_version = conanfile.settings.get_safe("compiler.runtime_version")
vs_version = {"v140": "14",
"v141": "15",
"v142": "16",
"v143": "17"}.get(toolset_version)
if vs_version is None:
raise ConanException("Visual Studio Runtime version (v140-v143) not defined")
vcvars_ver = {"v140": "14.0",
"v141": "14.1",
"v142": "14.2",
"v143": "14.3"}.get(toolset_version)
else:
vs_version = vs_ide_version(conanfile)
vcvars_ver = _vcvars_vers(conanfile, compiler, vs_version)
vcvarsarch = vcvars_arch(conanfile)
vcvars_ver = _vcvars_vers(conanfile, compiler, vs_version)

vs_install_path = conanfile.conf.get("tools.microsoft.msbuild:installation_path")
# The vs_install_path is like
Expand Down
1 change: 1 addition & 0 deletions conans/client/conf/__init__.py
Expand Up @@ -123,6 +123,7 @@
cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
runtime: [None, MD, MT, MTd, MDd, static, dynamic]
runtime_type: [None, Debug, Release]
runtime_version: [None, v140, v141, v142, v143]
apple-clang: &apple_clang
version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1", "10.0", "11.0", "12.0", "13", "13.0", "13.1", "14", "14.0"]
libcxx: [libstdc++, libc++]
Expand Down
1 change: 1 addition & 0 deletions conans/client/migrations_settings.py
Expand Up @@ -3985,6 +3985,7 @@
cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20, 23, gnu23]
runtime: [None, MD, MT, MTd, MDd, static, dynamic]
runtime_type: [None, Debug, Release]
runtime_version: [None, v140, v141, v142, v143]
apple-clang: &apple_clang
version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1", "10.0", "11.0", "12.0", "13", "13.0", "13.1", "14", "14.0"]
libcxx: [libstdc++, libc++]
Expand Down
126 changes: 125 additions & 1 deletion conans/test/assets/sources.py
Expand Up @@ -38,7 +38,7 @@
#if __x86_64__
std::cout << " {{ msg or name }} __x86_64__ defined\n";
#endif
#if __aarch64__
std::cout << " {{ msg or name }} __aarch64__ defined\n";
#endif
Expand Down Expand Up @@ -120,6 +120,130 @@ def gen_function_cpp(**context):
return t.render(**context)


_function_c = r"""
#define STRINGIFY_(x) #x
#define STRINGIFY(y) STRINGIFY_(y)
{% if name != "main" %}
#include "{{name}}.h"
{% endif %}
#include <stdio.h>
{% for it in includes -%}
#include "{{it}}.h"
{% endfor %}
int {{name}}(){
#ifdef NDEBUG
printf("{{ msg or name }}: Release!\n");
#else
printf("{{ msg or name }}: Debug!\n");
#endif
// ARCHITECTURES
#ifdef _M_X64
printf(" {{ msg or name }} _M_X64 defined\n");
#endif
#ifdef _M_IX86
printf(" {{ msg or name }} _M_IX86 defined\n");
#endif
#ifdef _M_ARM64
printf(" {{ msg or name }} _M_ARM64 defined\n");
#endif
#if __i386__
printf(" {{ msg or name }} __i386__ defined\n");
#endif
#if __x86_64__
printf(" {{ msg or name }} __x86_64__ defined\n");
#endif
#if __aarch64__
printf(" {{ msg or name }} __aarch64__ defined\n");
#endif
// Libstdc++
#if defined _GLIBCXX_USE_CXX11_ABI
printf(" {{ msg or name }} _GLIBCXX_USE_CXX11_ABI %s\n", STRINGIFY(_GLIBCXX_USE_CXX11_ABI));
#endif
// COMPILER VERSIONS
#if _MSC_VER
printf(" {{ msg or name }} _MSC_VER%s\n", STRINGIFY(_MSC_VER));
#endif
#if _MSVC_LANG
printf(" {{ msg or name }} _MSVC_LANG%s\n", STRINGIFY(_MSVC_LANG));
#endif
#if __cplusplus
printf(" {{ msg or name }} __cplusplus%s\n", STRINGIFY(__cplusplus));
#endif
#if __INTEL_COMPILER
printf(" {{ msg or name }} __INTEL_COMPILER%s\n", STRINGIFY(__INTEL_COMPILER));
#endif
#if __GNUC__
printf(" {{ msg or name }} __GNUC__%s\n", STRINGIFY(__GNUC__));
#endif
#if __GNUC_MINOR__
printf(" {{ msg or name }} __GNUC_MINOR__%s\n", STRINGIFY(__GNUC_MINOR__));
#endif
#if __clang_major__
printf(" {{ msg or name }} __clang_major__%s\n", STRINGIFY(__clang_major__));
#endif
#if __clang_minor__
printf(" {{ msg or name }} __clang_minor__%s\n", STRINGIFY(__clang_minor__));
#endif
#if __apple_build_version__
printf(" {{ msg or name }} __apple_build_version__%s\n", STRINGIFY(__apple_build_version__));
#endif
// SUBSYSTEMS
#if __MSYS__
printf(" {{ msg or name }} __MSYS__%s\n", STRINGIFY(__MSYS__));
#endif
#if __MINGW32__
printf(" {{ msg or name }} __MINGW32__%s\n", STRINGIFY(__MINGW32__));
#endif
#if __MINGW64__
printf(" {{ msg or name }} __MINGW64__%s\n", STRINGIFY(__MINGW64__));
#endif
#if __CYGWIN__
printf(" {{ msg or name }} __CYGWIN__%s\n", STRINGIFY(__CYGWIN__));
#endif
{% for it in preprocessor -%}
printf(" {{msg}} {{it}}: %s\n", {{it}} );
{%- endfor %}
{% for it in calls -%}
{{it}}();
{% endfor %}
return 0;
}
"""


def gen_function_c(**context):
t = Template(_function_c)
return t.render(**context)


_function_h = """
#pragma once
Expand Down
4 changes: 2 additions & 2 deletions conans/test/conftest.py
Expand Up @@ -136,14 +136,14 @@
"disabled": True,
"platform": "Windows",
"default": "system",
"exe": "mingw32-make",
"exe": "clang",
"system": {"path": {'Windows': "C:/msys64/clang64/bin"}},
},
'msys2_mingw64_clang64': {
"disabled": True,
"platform": "Windows",
"default": "system",
"exe": "mingw32-make",
"exe": "clang",
"system": {"path": {'Windows': "C:/msys64/mingw64/bin"}},
},
'cygwin': {
Expand Down
10 changes: 7 additions & 3 deletions conans/test/functional/toolchains/cmake/test_cmake.py
Expand Up @@ -31,13 +31,17 @@ def test_simple_cmake_mingw():
compiler.libcxx=libstdc++11
compiler.threads=win32
compiler.version=11.2
cppstd=17
compiler.cppstd=17
"""})
client.run("create . --profile=mingw")
# FIXME: Note that CI contains 10.X, so it uses another version rather than the profile one
# and no one notices. It would be good to have some details in confuser.py to be consistent
assert "hello/1.0: __GNUC__" in client.out
assert "hello/1.0: __MINGW" in client.out
check_exe_run(client.out, "hello/1.0:", "gcc", None, "Release", "x86_64", "17",
subsystem="mingw64", extra_msg="Hello World", cxx11_abi="1")
check_vs_runtime("test_package/build/Release/example.exe", client, "15", build_type="Release",
static_runtime=False, subsystem="mingw64")

# TODO: How to link with mingw statically?


@pytest.mark.tool_cmake
Expand Down

0 comments on commit 75329f6

Please sign in to comment.