本文将解释您在 Windows 中可能遇到的 Clang C 和 C++ 编译器的不同版本,并为您提供一些关于哪个版本适合您的建议,以及如何使用 CMake 和 Conan 使用它们的详细说明。

引言

过去几十年来,Microsoft C 和 C++ 编译器 (msvc 或 cl.exe) 一直是 Windows 上的主流编译器,虽然 MinGW 工具多年来一直在为 Windows 提供一个可用的 GNU 生态系统(使用 gcc/g++ 编译器),但它从未获得广泛的普及。

Clang 编译器建立在 LLVM 巨人的肩膀上,近年来已逐渐受到重视,首先为 C 和 C++ Apple 开发提供动力(现在它们有自己的 apple-clang 分支),然后因其出色的开发者工具、解析器基础设施、安全检查器、格式化程序以及代码质量和安全框架而受到许多开发者的关注。这种趋势最终也开始在 Windows 上流行起来,越来越多的开发者希望在所有操作系统中拥有一个通用的编译器基础设施,甚至将其捆绑和分发在 Microsoft Visual Studio IDE 中。

然而,Windows 中 Clang 编译器有几个不同的变体,并且它们之间的差异或何时/如何使用它们并不总是显而易见的,因此让我们更深入地了解它们。

不同的版本

在 Windows 上安装 Clang 编译器有几种不同的方法,让我们列举并命名它们

  • **LLVM/Clang**: LLVM 项目提供的 Clang 编译器,在 LLVM Github 上提供正式版本
  • **Visual Studio CangCL**: 来自 Visual Studio 16 2019 (v142) 或 Visual Studio 17 2022 (v143) 的 ClangCL 工具集 安装程序
  • **Msys2 Clang**: 由 Msys2 Windows 子系统 提供的 Clang 编译器,即 clang64 环境
  • **Msys2 MinGW Clang**: 由 Msys2 MinGW64 环境 提供的 Clang 编译器。请注意,这与上述 **Msys2 Clang** 编译器不同
  • **Cygwin Clang**: 使用 Cygwin 的图形用户界面安装的 Clang

我们可以将它们分为两大类

  • 基于 Visual Studio 的:**LLVM/Clang** 和 **Visual Studio ClangCL**,正如我们稍后将看到的,实际上是相同的,只是捆绑和分发方式不同。它们使用并链接 Visual Studio 运行时,并且旨在与 Visual Studio msvc 编译器兼容
  • 基于 Windows 子系统的:它们都是不同的编译器,使用不同的运行时库,并可能具有不同的编译/链接标志。它们不打算与 msvc 兼容,甚至不同子系统之间也不兼容。

不同的运行时

这些编译器之间的一个主要且重要的区别在于它们将使用哪个 C++ 标准库实现以及哪个运行时库。可以使用以下设置检查运行时依赖项(稍后将详细介绍如何使用 Conan 进行此操作)

一个实现单个方法 hello() 的静态库,该方法输出几个预处理器定义

#include <iostream>
#include "hello.h"
 
void hello(){
    // ARCHITECTURES
    #ifdef _M_X64
    std::cout << "  hello/0.1: _M_X64 defined\n";
    #endif
 
    // COMPILER VERSIONS
    #if _MSC_VER
    std::cout << "  hello/0.1: _MSC_VER" << _MSC_VER<< "\n";
    #endif
 
    #if _MSVC_LANG
    std::cout << "  hello/0.1: _MSVC_LANG" << _MSVC_LANG<< "\n";
    #endif
 
     // MORE FLAGS
}

一个使用该库的可执行文件

#include "hello.h"
 
int main() {
    hello();
}

一个 CMakeLists.txt 用于构建它

cmake_minimum_required(VERSION 3.15)
project(hello CXX)
 
add_library(hello src/hello.cpp)
target_include_directories(hello PUBLIC include)
 
add_executable(example src/example.cpp)
target_link_libraries(example hello)

当构建 example.exe 可执行文件时,可以使用 dumpbin 命令列出其所需的共享库

$ dumpbin /nologo /dependents "./path/to/example.exe"

默认情况下,dumpbin 命令不在 PATH 中,因此需要在 Visual Studio 提示符下运行它,或者使用 vcvars 批处理文件激活此类提示符(或任何其他解决方案,请参阅 dumpbin 参考以了解详细信息)

$ set "VSCMD_START_DIR=%CD%" && call "C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Auxiliary/Build/vcvarsall.bat" amd64 && dumpbin /nologo /dependents "./path/to/example.exe"

这是不同版本的可执行文件的运行时依赖项摘要(包括 msvc 编译器作为参考)


版本 C++ 标准库 运行时 备注
msvc MSVCP140(D).dll
VCRUNTIME140(D).dll
VCRUNTIME140_1(D).dll
ucrtbased.dll(仅限调试)
api-ms-win-crt-*.dll 基础参考 Visual Studio cl.exe 编译器。api-ms-win-crt-*.dll 是现代的,在 Windows 10 中默认使用,但在旧版 Windows 版本中并非默认使用
LLVM/ Clang MSVCP140(D).dll
VCRUNTIME(D).dll
ucrtbased.dll
(仅限调试)
api-ms-win-crt-*.dll 与 msvc 相同的运行时,旨在实现二进制兼容性
Visual Studio ClangCL MSVCP140(D).dll
VCRUNTIME(D).dll
ucrtbased.dll(仅限调试)
api-ms-win-crt-*.dll 与 msvc 相同的运行时,旨在实现二进制兼容性(与 LLVM/Clang 为同一编译器)
Msys2 Clang libc++.dll
libunwind.dll
api-ms-win-crt-*.dll 依赖于 libc++ 特定的 Msys2 Clang 库,与 msvc 库不兼容
Msys2 MinGW Clang libstdc++6.dll msvcrt.dll 依赖于与 MinGW gcc 编译器相同的标准库,实际上可以使用 libstdc++ 和 libstdc++11 变体。它使用旧的 msvcrt.dll,它存在于旧版 Windows 中,也与 msvc 不兼容。
Cygwin Clang(已过时) cygstdc++-6.dll cygwin1.dll Cygwin 的特定标准库,与 msvc 和 msys2 的不同运行时都不兼容。

注意:KERNEL32.dll 始终是系统运行时依赖项,在所有情况下都已在表中省略。

让我们看一下并解释这些结果。第一个相关项目是所有 **msvc**、**LLVM/Clang** 和 **Visual Studio ClangCL** 都使用相同的运行时。这是因为 **LLVM/Clang** 编译器使用 MSVC API 和库实现。虽然这在 **Visual Studio ClangCL** 中更明显或更预期,但对于 **LLVM/Clang** 版本来说有点令人惊讶。这种情况是由于此类版本在系统中实现了 MSVC 库的位置。最新的 **LLVM/Clang** 版本默认为使用 Visual Studio 17 2022,但可以通过定义相应的 Visual Studio 环境来强制使用其他安装。

MSVC 库有不同的发行版和调试版。调试版本附加一个 D,例如在 MSVCP140D.dll 中,并且调试版本可能会引入对其他库(如 ucrtbased.dll)的运行时依赖项,这些库在发行版中不一定使用。

此外,**msvc**、**LLVM/Clang** 和 **Visual Studio ClangCL** 三个编译器在运行时使用 api-ms-win-crt-*.dll 库。这些库可以在 Windows 系统文件夹中找到,但 **LLVM/Clang** 还会重新分发一个副本,该副本可以在其“bin”文件夹中找到,以及 clang.execlang++.exe 可执行文件。

**Msys2 MinGW Clang** 与 GNU libstdc++ 链接(它可以与 libstdc++libstdc++11 链接),从而导致对 libstdc++6.dllmsvcrt.dll 的运行时依赖,而不是 **Msys2 Clang** 使用的 libc++.dll。值得注意的是,msvcrt.dll 被认为是比 api-ms-win-crt-*.dll 更旧的 Windows API,但也与旧版 Windows 操作系统版本(7、8)更向后兼容,而后者仅在 Windows 10 中默认提供。

可以注意到 Cygwin Clang 已经不再维护,那里可用的最新版本是 clang 8.0.1。它仍然在这里添加以供参考,以及对于那些仍然必须处理此类旧系统的人员。使用此 **Cygwin Clang** 编译器构建的可执行文件在被指示使用 libstdc++(或 libstdc++11)时,使用 cygstdc++-6.dllcygwin1.dll 运行时。但也可以告诉它们使用 libc++,这会导致使用不同的运行时,具体取决于 cygc++-1.dll, cygc++abi-1.dll, cyggcc_s-seh-1.dll, cygwin1.dll

所有版本都可以在可执行文件中静态链接 C++ 标准库,在这种情况下,C++ 运行时库不再是显式依赖项,并且只有系统运行时将由 dumpbin 报告。如果我们构建纯 C 应用程序,也会发生这种情况。但是,这并不意味着我们可以在应用程序中混合依赖于不同运行时的不同库,因为它们将是二进制不兼容的。

编译器差异

使用的运行时库并不是不同 Clang 编译器之间的唯一区别,版本之间还存在 ABI 差异,例如某些类型的大小,如 long double。还需要特别注意的是,每个版本都将使用不同的预处理器指令,这可能会对生成的二进制文件产生非常大的影响,特别是对于依赖于此类预处理器定义的多平台、多编译器优化代码。

下表总结了一些不同的预处理器定义(下面将解释如何重现此表)


版本 架构 版本 标准 子系统 备注
msvc _M_X64 _MSC_VER=1933 _MSVC_LANG=201402
__cplusplus=199711
默认情况下标准 __cplusplus 不正确,需要额外的 /Zc 标志
LLVM/ Clang _M_X64
__x86_64__
_MSC_VER=1933
__clang_major__=13
_MSVC_LANG=201402
__cplusplus=201402

默认情况下,VS 17 2022 中的 _MSC_VER 为 19.3/v143,但可以使用其他版本进行更改。
Visual Studio ClangCL _M_X64
__x86_64__
_MSC_VER=1933
__clang_major__=14
_MSVC_LANG=201402
__cplusplus=201402

与 LLVM/CLang 使用相同的标志。
Msys2 Clang _M_X64
__x86_64__
__GNUC__=4
__GNUC_MINOR__=2
__clang_major__=14
__cplusplus=201402

__MINGW32__=1
__MINGW64__=1
MSVC 相关的标志被替换为 GNU 和标准标志。使用 MinGW Makefile 进行构建。
Msys2 MinGW Clang _M_X64
__x86_64__
__GNUC__=4
__GNUC_MINOR__=2
__clang_major__=14
__cplusplus=201402


_GLIBCXX_USE_CXX11_ABI=1
__MINGW32__=1
__MINGW64__=1
_GLIBCXX_USE_CXX11_ABI 的定义表明使用了 libstdc++/libstdc++11 标准库。
Cygwin Clang(已过时) __x86_64__ __GNUC__=4
__GNUC_MINOR__=2
__clang_major__=8
__cplusplus=201402

__CYGWIN__=1 捆绑的 clang 版本为 8.0,这是一个旧版本。

在架构方面,除了过时的 Cygwin 版本之外,所有 Clang 版本都将声明 MSVC 特定的 _M_X64 和 GNU 和 Clang 的 __x86_64__

编译器版本在两个主要系列之间有所不同。基于 VS 的版本将定义 _MSC_VER=1933(属于 v143 或 19.3 编译器版本,Visual Studio 17 2022 中的默认版本),以及 Clang 编译器的 __clang_major__=13。请注意,**Visual Studio ClangCL** 和 **LLVM/Clang** 版本是不同的,在本例中,我们使用的是 **LLVM/Clang** 13.0 版本,因此我们可以手动检查我们的构建使用了正确的编译器(捆绑的 **Visual Studio ClangCL** 版本为 14)。

在使用 **LLVM/Clang** 时,完全可以使用不同的 VS 版本。默认情况下,它将使用最新的 v143 运行时,但通过激活环境(通过 vcvars 或在另一个 VS 版本提示符下运行),可以改用该版本。我们将在后面的测试中看到这一点,但本质上,它由类似 _MSC_VER=192X 的预处理器定义来证明。还可以使用编译器选项 -fms-compatibility-version 更改默认的 MSVC 兼容性行为,这使得 clang 的行为类似于其他 MSVC 版本,或者使用 -fmsc-version 仅覆盖 _MSC_VER 值。

另一方面,基于 Msys2 的 Clang 编译器将定义 __GNUC__ 标志,同时仍然使用 __clang_major__=13 定义。这些定义可以通过 -fgnuc-version 编译器选项进行控制,但请注意,它实际上并没有激活或停用 Clang 中的 GNU 扩展,只是更改了预处理器定义的值。

C++ 标准也存在类似的情况。基于 VS 的 Clang 编译器将定义 VS 特定的 _MSVC_LANG=201402(C++14 标准)和 C++ 标准 __cplusplus=201402。但是,请注意,Msys2 MinGW Clang 也可能定义 _GLIBCXX_USE_CXX11_ABI 以指示使用 libstdc++libstdc++11 C++ 标准库。

请注意,即使 _MSVC_LANG=201402 正确地表示 C++14,普通的 **msvc** 也会报告 __cplusplus199711。您需要显式定义 /Zc:__cplusplus 才能使 __cplusplus 正确定义。

使用 CMake 和 Conan 配置和测试不同的设置。

本节说明了如何使用 CMake 和 Conan 测试不同的 Clang 版本,因为它提供了良好的抽象层,并允许总结所需的各种配置。

它要求系统中安装了 CMake>=3.23 和 Conan>=1.53。每个子部分将描述 Clang 安装的具体内容。

让我们从一个简单的 C++“hello”世界项目开始,可以使用预定义的模板创建该项目。

$ conan new hello/0.1 -m=cmake_lib

请注意,这是一个基于 CMake 的项目,其中包含与上面发布的代码非常相似的内容,并且这样的项目与 Conan 无关,其 CMakeLists.txt 不包含任何与 Conan 相关的内容。 conanfile.py 脚本将允许我们使用不同的配置(在本例中为不同的 Clang 变体)轻松构建项目。

让我们从“普通”的 **msvc** 构建开始,并在此学习必要的配置更改。我们将使用的配置文件为

msvc.profile

[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=msvc
compiler.version=193
compiler.cppstd=14
compiler.runtime=dynamic
compiler.runtime_type=Release

其中 compiler.version=193 是 Visual Studio 17 2022 的默认编译器版本(19.3,工具集 v143)。

此配置文件可以通过命令行传递。

$ conan create . -pr=msvc.profile
...

hello/0.1: Hello World Release!
  hello/0.1: _M_X64 defined
  hello/0.1: _MSC_VER1933
  hello/0.1: _MSVC_LANG201402
  hello/0.1: __cplusplus199711

LLVM/Clang

要安装它,请访问 LLVM Github 下载,获取 Windows 安装程序,例如本博文中使用的 LLVM/Clang 13 版本,并将其安装到某个位置,例如 C:/ws/LLVM/Clang13。无需将其放入系统 PATH 中,如果您使用的是不同的编译器,可能不建议这样做。

要构建和测试此项目,我们将使用以下配置文件(如下所述)。

llvm_clang.profile

[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=clang
compiler.version=13
compiler.cppstd=gnu14
compiler.runtime=dynamic
compiler.runtime_type=Release
compiler.runtime_version=v143
 
[buildenv]
PATH=+(path)C:/ws/**LLVM/Clang**13/bin
PATH+=(path)C:/ws/msys64/mingw64/bin
 
[conf]
tools.env.virtualenv:auto_use=True

并使用以下命令应用它:

$ conan create . -pr=llvm_clang.profile

...
hello/0.1: Hello World Release!
  hello/0.1: _M_X64 defined
  hello/0.1: __x86_64__ defined
  hello/0.1: _MSC_VER1933
  hello/0.1: _MSVC_LANG201402
  hello/0.1: __cplusplus201402
  hello/0.1: __clang_major__13

让我们解释配置文件中的一些有趣细节。架构、编译器和编译器版本应该不言而喻。

  • compiler.cppstd=gnu14。这可能与 **msvc** 不同,因为 clang 编译器支持 GNU 扩展。如果我们不使用这些扩展,也可以将 compiler.cppstd=14 保留。
  • compiler.runtime=dynamic。在 Windows 中,可以静态或动态地链接运行时。Conan 将其转换为 CMake 的 CMAKE_MSVC_RUNTIME_LIBRARY,它可以采用 MultiThreadedDLLMultiThreadedDebug 等值,这些值将转换为相应的 VS 标志 /MT /MTd /MD /MDd。此配置文件将选择静态和动态运行时。
  • compiler.runtime_type=Release。为了补充静态/动态运行时,此配置文件设置将定义运行时是调试版还是发布版。在一般情况下,这应该遵循 build_type 设置。
  • compiler.runtime_version=v143。如上所述,**LLVM/Clang** 将使用来自 v143 的最新的 Visual Studio 17 运行时。我们稍后将对其进行更改并查看效果。

[buildenv] 部分所示,需要向系统 PATH 添加一些内容。

  • Clang 编译器本身的路径,即可执行文件所在的位置。请注意,这使用的是 PATH=+(path) 语法,这意味着将其 **添加到开头** 到 PATH。由于 Visual Studio 可能安装了 Clang 编译器,并且可能启用了 Visual Studio 环境,因此可能会意外地使用 Visual Studio 捆绑的 ClangCL 工具集,而不是我们自己安装的工具集。
  • MinGW 的路径。要构建此 CMake 项目,我们可以使用 MinGW MakefilesNinjaNMake Makefiles CMake 生成器。如果我们尝试使用 Visual Studio CMake 生成器,它将假定它将使用 Visual Studio 捆绑的 Clang,而不是我们自己的 Clang。理论上,可以使用以下方法在 MSBuild 中定义自定义位置:
    <Project>
      <PropertyGroup>
        <LLVMInstallDir>C:\MyLLVMRootDir</LLVMInstallDir>
        <LLVMToolsVersion>15.0.0</LLVMToolsVersion>
      </PropertyGroup>
    </Project> 
    

    但据我们所知,仅使用 CMake 无法做到这一点。添加到 PATH 中的内容将使 mingw32-make 可用于构建,因为这是必要的,而且我们也没有在系统级别安装它,因为这也会以其他方式交互,例如引入它自己的编译器。因此,此值附加到 PATH 的末尾,而不是添加到开头。

    在配置文件中定义此类 PATH 环境变量的好处是,它们不会永久定义,并且仅对使用此配置文件的命令应用。

最后,conf tools.env.virtualenv:auto_use=True 有助于模拟 Conan 2.0 的行为,在该行为中,无需在脚本中显式定义环境生成器。这将等同于在 conanfiles 中添加 generators = "VirtualBuildEnv", "VirtualRunEnv"

使用不同生成器和运行时的 LLVM/Clang

可以通过传递相应的 conf 来更改 CMake 生成器,无论是通过命令行还是在配置文件中。

$ conan create . -pr=llvm_clang.profile -c tools.cmake.cmaketoolchain:generator=Ninja

结果应该与上面相同,默认情况下使用的是 MinGW Makefiles。也可以使用 -c tools.cmake.cmaketoolchain:generator="NMake Makefiles" 定义,结果相同。

如果我们想静态链接运行时,可以执行以下操作:

$ conan create . -pr=llvm_clang.profile -s compiler.runtime=static

程序输出将保持不变,但如果我们使用 dumpbin 检查生成的 \test_package\build\Release\example.exe 可执行文件,我们将意识到它现在仅依赖于 KERNEL32.dll,而不依赖于其他任何运行时动态库。

最后,如果我们想使用相同的编译器,但使用 Visual Studio 16 2019 运行时(v142 工具集),我们可以执行以下操作:

$ conan create . -pr=llvm_clang.profile -s compiler.runtime_version=v142
hello/0.1: Hello World Release!
  …
  hello/0.1: _MSC_VER1929
  …

请注意,MSVC 版本现在是我们想要的 19.2,而不是上面默认的 19.3。

Visual Studio ClangCL

在 Visual Studio 中安装 Clang 编译器的方法是使用他们自己的 VS Installer 应用程序,并在其中选择“Clang”编译器。阅读 Microsoft Clang 安装文档 以了解更多详细信息。它将通过与 MSVC 编译器命令行参数兼容的 clang-cl.exe 编译器驱动程序使用它。编译器在内部仍然相同(与 **LLVM/Clang** 相同),只是它将接受与 **msvc** cl.exe 编译器兼容的 Visual Studio 参数。

在这种情况下,我们将使用的配置文件保留了许多与我们之前看到的相同的设置和配置。

vs_clang.profile

[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=clang
compiler.version=14
compiler.cppstd=gnu14
compiler.runtime=dynamic
compiler.runtime_type=Release
compiler.runtime_version=v143
 
[conf]
tools.env.virtualenv:auto_use=True
tools.cmake.cmaketoolchain:generator=Visual Studio 17

但有一些区别:它不需要定义编译器的 PATH,也不需要定义 mingw32-make 的路径,因为它将使用“Visual Studio”CMake 生成器。定义它的方法将使用 **Visual Studio ClangCL**,在 tools.cmake.cmaketoolchain:generator=Visual Studio 17 中指定它,然后 CMake 就会知道在哪里找到所有内容。

请注意 [settings] 与之前的 **LLVM/Clang** 配置文件相同,并且确实它们会导致相同的最终 package_id,因为最终的二进制文件应该相同(不一定逐位相同,请参阅 此关于不确定性构建的博文 中的内容)。关于运行时的设置 compiler.runtime=dynamiccompiler.runtime_type=Releasecompiler.runtime_version=v143 仍然是必要的,并且应该与正在使用的 CMake Visual Studio 生成器匹配。即使从工具链的角度来看它不是完全必要的,它仍然是识别二进制文件并获取相同的 package_id 所必需的。

使用以下命令:

$ conan create . -pr=vs_clang.profile

我们将获得与上面使用 **LLVM/Clang** 工具获得的相同输出。

Msys2 Clang

使用此编译器的第一步是安装 Msys2,您可以按照Msys2 网站上的说明进行操作。为了在不同的 Msys2 环境中安装和卸载软件,可以使用 pacman 系统包管理器。在 clang64 环境中安装开发版 Clang 工具链,可以使用以下命令:$ pacman -S mingw-w64-clang-x86_64-toolchain

本例中将使用此配置文件

msys2_clang.profile

[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=clang
compiler.version=14
compiler.cppstd=gnu14
compiler.libcxx=libc++
 
[buildenv]
PATH+=(path)C:/ws/msys64/clang64/bin
 
[runenv]
PATH+=(path)C:/ws/msys64/clang64/bin
 
[conf]
tools.env.virtualenv:auto_use=True

与之前的基于 MSVC 的 Clang 编译器相比,有一些重要的区别:不再定义 compiler.runtime 设置。此类运行时设置指的是 Windows 和 MSVC 运行时,与 GNU 工具链无关。在 Clang 和 GNU 编译器中,stdlib 由 compiler.libcxx 管理,在本例中设置为 libc++。除了 [buildenv] PATH 指向 Msys2 中 Clang 编译器的位置外,还需要在运行时设置等效的 [runenv],以便在 conanfile.py 执行可执行文件时,可以找到 libc++.dlllibunwind.dll 动态库。上述编译器运行时位于系统中,并自动加载。

执行 conan create 时,我们应该看到

$ conan create . -pr=msys2_clang.profile
hello/0.1: Hello World Release!
  hello/0.1: _M_X64 defined
  hello/0.1: __x86_64__ defined
  hello/0.1: __cplusplus201402
  hello/0.1: __GNUC__4
  hello/0.1: __GNUC_MINOR__2
  hello/0.1: __clang_major__14
  hello/0.1: __MINGW32__1
  hello/0.1: __MINGW64__1

Msys2 MinGW Clang

此编译器通过在 Msys2 MinGW64 终端中使用 pacman -S mingw-w64-x86_64-clang 安装。请注意,此命令与上述命令(使用 mingw-w64-clang-x86_64-toolchain 包)略有不同。

此配置使用的配置文件为

msys2_mingw_clang.profile

[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=clang
compiler.version=14
compiler.cppstd=gnu14
compiler.libcxx=libstdc++11
 
[buildenv]
PATH+=(path)C:/ws/msys64/mingw64/bin
 
[runenv]
PATH+=(path)C:/ws/msys64/mingw64/bin
 
[conf]
tools.env.virtualenv:auto_use=True

与上述 **Msys2 Clang** 的主要区别在于

  • PATH+=(path)C:/ws/msys64/mingw64/bin 指向不同的位置,即 MinGW64 的位置,而不是 clang 的位置
  • compiler.libcxx=libstdc++11 现在使用 gcc 的 libstdc++libstdc++1 C++ 标准库,而不是 **Msys2 Clang** 使用的 libc++

使用该配置文件运行 conan create 将导致

$ conan create . -pr=msys2_mingw_clang.profile
hello/0.1: Hello World Release!
  hello/0.1: _M_X64 defined
  hello/0.1: __x86_64__ defined
  hello/0.1: _GLIBCXX_USE_CXX11_ABI 1
  hello/0.1: __cplusplus201402
  hello/0.1: __GNUC__4
  hello/0.1: __GNUC_MINOR__2
  hello/0.1: __clang_major__14
  hello/0.1: __MINGW32__1
  hello/0.1: __MINGW64__1

我们可以看到,除了预处理器定义 _GLIBCXX_USE_CXX11_ABI 1 指示使用 libstdc++11 外,其他所有内容都相同。

Cygwin Clang

Cygwin 本身可以从Cygwin 网站安装,并在其中安装 Clang 编译器,可以使用其图形界面安装程序,选择 clang 编译器并点击“安装”按钮。

本例中使用的配置文件也类似

cygwin_clang.profile

[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=clang
compiler.version=8
compiler.cppstd=gnu14
compiler.libcxx=libstdc++
 
[buildenv]
PATH+=(path)C:/ws/cygwin64/bin
 
[runenv]
PATH+=(path)C:/ws/cygwin64/bin
 
[conf]
tools.env.virtualenv:auto_use=True
tools.cmake.cmaketoolchain:generator=Unix Makefiles

但有一些细微的改动

  • 环境的路径位置指向 cygwin 的“bin”文件夹
  • 需要说明的是,必须使用 Unix Makefiles,因为默认的 MinGW Makefiles 将无法工作
  • 编译器版本为 8,因为这与 cygwin 中安装的版本相匹配
  • 此处的 compiler.libcxxlibstdc++,但也可以是 libstdc++11,甚至 libc++,在后一种情况下会导致使用不同的运行时库(请查看“不同的运行时”部分)

创建此包的结果显示

$ conan create . -pr=cygwin_clang.profile
hello/0.1: Hello World Debug!
  hello/0.1: __x86_64__ defined
  hello/0.1: _GLIBCXX_USE_CXX11_ABI 0
  hello/0.1: __cplusplus201402
  hello/0.1: __GNUC__4
  hello/0.1: __GNUC_MINOR__2
  hello/0.1: __clang_major__8
  hello/0.1: __CYGWIN__1

可以看出,消息打印的是 hello/0.1: Hello World Debug!,而不是像其他版本那样打印 Release,这是因为此环境中未定义常见的 NDEBUG 预处理器定义(但在所有其他环境中都已定义)。

结论

由于 **Cygwin Clang** 已被 Msys2 版本大量取代,因此我们建议开始使用其他版本进行新的工作。

要决定是在基于 MSVC 的 Clang 版本(**LLVM/Clang** 和 **Visual Studio ClangCL**)和基于 Msys2 的版本之间做出选择,应该根据项目的其他约束条件来决定。一般来说,基于 MSVC 的 Clang 与其他 Windows 二进制库和系统本身具有更好的兼容性,而使用 Msys2 子系统版本可以在存在一些严重依赖于 GNU 工具且与 MSVC 编译器不兼容的其他依赖项时效果更好。

在 **Msys2 Clang** 和 **Msys2 MinGW Clang** 之间做出选择,也应该采取类似的方法。如果项目的其他依赖项依赖于必须协同工作的 libstdc++ 运行时,那么 **Msys2 MinGW Clang** 可能是最佳选择,否则,使用 libc++ 的 **Msys2 Clang** 可能是更好的选择。

在基于 MSVC 的 Clang 中,在 **LLVM/Clang** 和 **Visual Studio ClangCL** 之间做出最终决定,可能取决于 IDE。如果大量的开发工作在 Windows 上进行,并且开发人员希望使用 Visual Studio IDE,那么 **Visual Studio ClangCL** 会更好。相反,如果大部分开发工作在 Linux 上进行,并且 Windows 开发人员对 VS Code 编辑器和使用 Ninja(许多开发人员因为其速度快而喜欢它)感到满意,那么 **LLVM/Clang** 可能是更好的选择。