CMakeToolchain: 使用 LLVM/Clang Windows 编译器

Windows 中的 Clang 编译器可以来自 2 种不同的安装或发行版

  • 使用 MSVC 运行时的 LLVM/Clang 编译器

  • 使用 Msys2 运行时 (libstdc++6.dll) 的 Msys2 Clang 编译器

本示例解释了使用 MSVC 运行时的 LLVM/Clang。这种 Clang 发行版反过来可以用三种不同的方式使用

  • 使用 Visual Studio 安装程序作为 VS 一部分安装的 Clang 组件

  • 通过类似 GNU 的前端 clang 使用下载的 LLVM/Clang 编译器(它仍然使用 MSVC 运行时)

  • 通过类似 MSVC 的前端 clang-cl 使用下载的 LLVM/Clang 编译器(它仍然使用 MSVC 运行时)

让我们从一个简单的 cmake_exe 模板开始

$ conan new cmake_exe -d name=mypkg -d version=0.1

这会创建一个简单的基于 CMake 的项目和使用 CMakeToolchain 的 Conan 包配方(recipe)。

LLVM/Clang 和类似 GNU 的前端 clang

要构建此配置,我们将使用以下配置文件

llvm_clang
[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=clang
compiler.version=18
compiler.cppstd=14
compiler.runtime=dynamic
compiler.runtime_type=Release
compiler.runtime_version=v144

[buildenv]
PATH=+(path)C:/ws/LLVM/18.1/bin

[conf]
tools.cmake.cmaketoolchain:generator=Ninja
tools.compilation:verbosity=verbose

[tool_requires]
ninja/[*]

配置文件简要说明

  • compiler.runtime 定义是区分 Msys2-Clang 和使用 MSVC 运行时的 LLVM/Clang 的重要区别点。LLVM/Clang 定义了此 compiler.runtime,而 Msys2-Clang 没有。

  • MSVC 运行时可以是动态的或静态的。定义此运行时的运行时版本(工具集版本 v144)也很重要,因为可以使用不同的版本。

  • [buildenv] 允许指向 LLVM/Clang 编译器,以防它不在路径中。注意 PATH=+(path) 语法,用于预置该路径,使其具有更高的优先级,否则 CMake 可能会找到并使用安装在 Visual Studio 中的 Clang 组件。

  • 我们正在使用 Ninja CMake 生成器,并从 [tool_requires] 安装它,但如果 Ninja 已安装在您的系统中,则可能不需要这样做。

让我们构建它

$ conan build . -pr=llvm_clang
...
-- The CXX compiler identification is Clang 18.1.8 with GNU-like command-line
-- Check for working CXX compiler: C:/ws/LLVM/18.1/bin/clang++.exe - skipped
...
[1/3] C:\ws\LLVM\18.1\bin\clang++.exe   -O3 -DNDEBUG -std=c++14 -D_DLL -D_MT -Xclang --dependent-lib=msvcrt -MD -MT CMakeFiles/mypkg.dir/src/main.cpp.obj -MF CMakeFiles\mypkg.dir\src\main.cpp.obj.d -o CMakeFiles/mypkg.dir/src/main.cpp.obj -c C:/Users/Diego/conanws/kk/clang/src/main.cpp
[2/3] C:\ws\LLVM\18.1\bin\clang++.exe   -O3 -DNDEBUG -std=c++14 -D_DLL -D_MT -Xclang --dependent-lib=msvcrt -MD -MT CMakeFiles/mypkg.dir/src/mypkg.cpp.obj -MF CMakeFiles\mypkg.dir\src\mypkg.cpp.obj.d -o CMakeFiles/mypkg.dir/src/mypkg.cpp.obj -c C:/Users/Diego/conanws/kk/clang/src/mypkg.cpp
[3/3] cmd.exe /C "cd . && C:\ws\LLVM\18.1\bin\clang++.exe -fuse-ld=lld-link -nostartfiles -nostdlib -O3 -DNDEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrt -Xlinker /subsystem:console CMakeFiles/mypkg.dir/src/mypkg.cpp.obj CMakeFiles/mypkg.dir/src/main.cpp.obj -o mypkg.exe -Xlinker /MANIFEST:EMBED -Xlinker /implib:mypkg.lib -Xlinker /pdb:mypkg.pdb -Xlinker /version:0.0   -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -loldnames  && cd ."

查看如何使用安装在 C:/ws 文件夹中的所需 LLVM/Clang 编译器,以及如何使用类似 GNU 的命令行语法。类似 GNU 的语法需要 --dependent-lib=msvcrt(由 CMake 自动添加)编译器和链接器标志来定义链接到动态 MSVC 运行时,否则 LLVM/Clang 会将其静态链接。另请注意,-MD -MT 标志与 MSVC 运行时无关,在类似 GNU 的前端中,它们具有完全不同的含义。

我们可以运行我们的可执行文件,看看 Clang 编译器版本和 MSVC 运行时如何与定义的版本匹配

$ build\Release\mypkg.exe
mypkg/0.1: Hello World Release!
    mypkg/0.1: _M_X64 defined
    mypkg/0.1: __x86_64__ defined
    mypkg/0.1: MSVC runtime: MultiThreadedDLL
    mypkg/0.1: _MSC_VER1943
    mypkg/0.1: _MSVC_LANG201402
    mypkg/0.1: __cplusplus201402
    mypkg/0.1: __clang_major__18
    mypkg/0.1: __clang_minor__1

LLVM/Clang 和类似 MSVC 的前端 clang-cl

要构建此配置,我们将使用以下配置文件

llvm_clang_cl
[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=clang
compiler.version=18
compiler.cppstd=14
compiler.runtime=dynamic
compiler.runtime_type=Release
compiler.runtime_version=v144

[buildenv]
PATH=+(path)C:/ws/LLVM/18.1/bin

[conf]
tools.cmake.cmaketoolchain:generator=Ninja
tools.build:compiler_executables = {"c": "clang-cl", "cpp": "clang-cl"}
tools.compilation:verbosity=verbose

[tool_requires]
ninja/[*]

该配置文件与上面的几乎相同,主要区别在于定义了 tools.build:compiler_executables,指定了 clang-cl 编译器。

注意

使用 clang-cl 编译器定义 tools.build:compiler_executables 是 Conan 用于区分不同前端的方法,在其他构建系统中也是如此。此前端不是一个 setting,因为编译器仍然是相同的,并且生成的二进制文件应该是二进制兼容的。

让我们构建它

$ conan build . -pr=llvm_clang_cl
...
-- The CXX compiler identification is Clang 18.1.8 with MSVC-like command-line
-- Check for working CXX compiler: C:/ws/LLVM/18.1/bin/clang-cl.exe - skipped
...
[1/3] C:\ws\LLVM\18.1\bin\clang-cl.exe  /nologo -TP   /DWIN32 /D_WINDOWS /GR /EHsc /O2 /Ob2 /DNDEBUG -std:c++14 -MD /showIncludes /FoCMakeFiles\mypkg.dir\src\main.cpp.obj /FdCMakeFiles\mypkg.dir\ -c -- C:\project\src\main.cpp
[2/3] C:\ws\LLVM\18.1\bin\clang-cl.exe  /nologo -TP   /DWIN32 /D_WINDOWS /GR /EHsc /O2 /Ob2 /DNDEBUG -std:c++14 -MD /showIncludes /FoCMakeFiles\mypkg.dir\src\mypkg.cpp.obj /FdCMakeFiles\mypkg.dir\ -c -- C:\project\src\mypkg.cpp
[3/3] cmd.exe /C "cd . && C:\ws\cmake\cmake-3.27.9-windows-x86_64\bin\cmake.exe -E vs_link_exe --intdir=CMakeFiles\mypkg.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100226~1.0\x64\mt.exe --manifests  -- C:\ws\LLVM\18.1\bin\lld-link.exe /nologo CMakeFiles\mypkg.dir\src\mypkg.cpp.obj CMakeFiles\mypkg.dir\src\main.cpp.obj  /out:mypkg.exe /implib:mypkg.lib /pdb:mypkg.pdb /version:0.0 /machine:x64 /INCREMENTAL:NO /subsystem:console  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."

查看如何使用安装在 C:/ws 文件夹中的所需 LLVM/Clang 编译器,以及如何使用类似 MSVC 的命令行语法。这种类似 MSVC 的语法使用 -MD/-MT 标志来区分动态/静态 MSVC 运行时。

我们可以运行我们的可执行文件,看看 Clang 编译器版本和 MSVC 运行时如何与定义的版本匹配

$ build\Release\mypkg.exe
mypkg/0.1: Hello World Release!
    mypkg/0.1: _M_X64 defined
    mypkg/0.1: __x86_64__ defined
    mypkg/0.1: MSVC runtime: MultiThreadedDLL
    mypkg/0.1: _MSC_VER1943
    mypkg/0.1: _MSVC_LANG201402
    mypkg/0.1: __cplusplus201402
    mypkg/0.1: __clang_major__18
    mypkg/0.1: __clang_minor__1

正如预期,输出与之前的结果相同,因为除了编译器前端之外没有任何变化。

MSVC Clang 组件 (ClangCL Visual Studio 工具集)

要构建此配置,我们将使用以下配置文件

llvm_clang_vs
[settings]
os=Windows
arch=x86_64
build_type=Release
compiler=clang
compiler.version=19
compiler.cppstd=14
compiler.runtime=dynamic
compiler.runtime_type=Release
compiler.runtime_version=v144

[conf]
tools.cmake.cmaketoolchain:generator=Visual Studio 17
tools.compilation:verbosity=verbose

此配置文件将使用 CMake 的“Visual Studio”生成器。这表明 Clang 编译器将是 Visual Studio 提供的,并通过 Visual Studio 安装程序作为 Visual Studio 的一个组件安装。注意 compiler.version=19 与上面使用的版本不同,上面是 compiler.version=18,因为 Visual 内部的版本由其安装程序自动定义。

此设置将始终使用类似 MSVC 的 clang-cl 前端,并且 ClangCL 工具集将被激活,以便 Visual Studio 知道这是它应该使用的编译器。在此处不需要定义 tools.build:compiler_executable

让我们构建它

$ conan build . -pr=llvm_clang_vs
...
-- Conan toolchain: CMAKE_GENERATOR_TOOLSET=ClangCL
-- The CXX compiler identification is Clang 19.1.1 with MSVC-like command-line
...
ClCompile:
    C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin\clang-cl.exe /c /nologo /W1 /WX- /diagnostics:column /O2 /Ob2 /D _MBCS /D WIN
32 /D _WINDOWS /D NDEBUG /D "CMAKE_INTDIR=\"Release\"" /EHsc /MD /GS /fp:precise /GR /std:c++14 /Fo"mypkg.dir\Release\\" /Gd /TP --target=amd64-pc-windows-
msvc  C:\project\src\mypkg.cpp C:\project\src\main.cpp
Link:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\x64\bin\lld-link.exe /OUT:"C:\project\build\Release\mypkg.exe" /
INCREMENTAL:NO kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTUAC:
"level='asInvoker' uiAccess='false'" /manifest:embed /PDB:"C:/Users/Diego/conanws/kk/clang/build/Release/mypkg.pdb" /SUBSYSTEM:CONSOLE /DYNAMICBASE /NXCOMP
AT /IMPLIB:"C:/Users/Diego/conanws/kk/clang/build/Release/mypkg.lib"   /machine:x64 mypkg.dir\Release\mypkg.obj
mypkg.dir\Release\main.obj
mypkg.vcxproj -> C:\project\build\Release\mypkg.exe

定义了 CMAKE_GENERATOR_TOOLSET=ClangCL,并且使用了内部的 VS Clang 组件,也显示了 19.1.1 版本。然后,使用了常规的类似 MSVC 的语法,包括通过 /MD 标志定义运行时。

我们可以运行我们的可执行文件,看看 Clang 编译器版本 (19) 和 MSVC 运行时如何与定义的版本匹配

$ build\Release\mypkg.exe
mypkg/0.1: Hello World Release!
    mypkg/0.1: _M_X64 defined
    mypkg/0.1: __x86_64__ defined
    mypkg/0.1: MSVC runtime: MultiThreadedDLL
    mypkg/0.1: _MSC_VER1943
    mypkg/0.1: _MSVC_LANG201402
    mypkg/0.1: __cplusplus201402
    mypkg/0.1: __clang_major__19
    mypkg/0.1: __clang_minor__1