CMake自动添加的/Zc:__cplusplus被nvcc误判为文件名求助
我在Windows环境下开发C++项目,使用CUDA 12.0、CMake 3.31.6及vcpkg(已更新至提交a62ce77)。CMake配置阶段调用nvcc获取编译器信息时,执行命令如下:
"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.0\bin\nvcc.exe" -D_WINDOWS "-Xcompiler= /GR /EHsc" --std=c++17 --expt-relaxed-constexpr --expt-extended-lambda -Xcudafe --display_error_number,--diag_suppress=[186,611,1388,1394,2809,20011,20012,20014,20054] -Xptxas -dlcm=ca --resource-usage -Xcompiler=/arch:AVX2 "-Xcompiler= -Zi -Ob0 -Od /RTC1" -std=c++17 -arch=native -Xcompiler=-MDd -D_ITERATOR_DEBUG_LEVEL=0 /Zc:__cplusplus --dryrun -forward-unknown-to-host-compiler C:\Users\Me\AppData\Local\Temp\compiler-file17533896127583160454.cu
nvcc返回错误:
nvcc fatal : Don't know what to do with 'F:/Zc:__cplusplus'
/Zc:__cplusplus参数需要添加-Xcompiler前缀才能被nvcc识别为宿主编译器选项,但现在被当作文件名处理。我没有手动在CMakeLists.txt中指定该参数,推测是CMake自动添加,但找不到对应的变量。
通过最小复现案例发现问题与spdlog::spdlog_header_only相关,移除该依赖或替换为spdlog::spdlog可解决,但我需要使用header-only版本。
最小复现配置
CMakeLists.txt
cmake_minimum_required (VERSION 3.18) project(Foo LANGUAGES CXX CUDA) set(CMAKE_CXX_STANDARD 20) find_package(spdlog CONFIG REQUIRED) add_library(Foo STATIC empty.cu ) target_link_libraries(Foo PUBLIC spdlog::spdlog_header_only )
vcpkg.json
{ "name": "foo", "version-string": "0.0.1", "dependencies": [ "spdlog" ] }
vcpkg-configuration.json
{ "default-registry": { "kind": "git", "repository": "https://github.com/Microsoft/vcpkg", "baseline": "a62ce77d56ee07513b4b67de1ec2daeaebfae51a" } }
CMake调用命令
"C:\Program Files\CMake\bin\cmake.exe" -DCMAKE_BUILD_TYPE=Debug "-DCMAKE_MAKE_PROGRAM=C:/Program Files/JetBrains/CLion 2024.3.4/bin/ninja/win/x64/ninja.exe" -G Ninja -DCMAKE_TOOLCHAIN_FILE=F:\Libraries\vcpkg\scripts\buildsystems\vcpkg.cmake -S F:\Projects\MyProject -B F:\Projects\MyProject\cmake-build-debug
配置输出报错片段
-- Running vcpkg install ... All requested installations completed successfully in: 224 us -- Running vcpkg install - done -- Configuring done (1.4s) -- Generating done (0.0s) -- Build files have been written to: F:/Projects/MyProject/cmake-build-debug Cannot get compiler information: Compiler exited with error code 1: "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.0\bin\nvcc.exe" -DFMT_SHARED -DSPDLOG_FMT_EXTERNAL -DSPDLOG_FWRITE_UNLOCKED -isystem=F:/Projects/MyProject/cmake-build-debug/vcpkg_installed/x64-windows/include -D_WINDOWS "-Xcompiler= /GR /EHsc" "-Xcompiler= -Zi -Ob0 -Od /RTC1" -std=c++20 --generate-code=arch=compute_52,code=[compute_52,sm_52] -Xcompiler=-MDd /Zc:__cplusplus --dryrun -forward-unknown-to-host-compiler C:\Users\me\AppData\Local\Temp\compiler-file17655800990905755916.cu
解决方法
1. 修正spdlog_header_only的INTERFACE编译选项
由于spdlog::spdlog_header_only是INTERFACE目标,会将MSVC的CXX编译选项传递给依赖它的CUDA目标,但CMake不会自动为CUDA目标转换这些选项。可以手动修改该目标的选项,将/Zc:__cplusplus包装成nvcc可识别的形式:
find_package(spdlog CONFIG REQUIRED) # 遍历spdlog_header_only的INTERFACE_COMPILE_OPTIONS,修正/Zc:__cplusplus get_target_property(_spdlog_interface_opts spdlog::spdlog_header_only INTERFACE_COMPILE_OPTIONS) list(TRANSFORM _spdlog_interface_opts REPLACE "^/Zc:__cplusplus$" "$<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=/Zc:__cplusplus>$<$<NOT:$<COMPILE_LANGUAGE:CUDA>>:/Zc:__cplusplus>") set_target_properties(spdlog::spdlog_header_only PROPERTIES INTERFACE_COMPILE_OPTIONS "${_spdlog_interface_opts}")
2. 全局设置CUDA的编译选项
如果希望全局生效,可以直接将/Zc:__cplusplus添加到CMAKE_CUDA_FLAGS中,自动带上-Xcompiler前缀:
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=/Zc:__cplusplus")
注意:如果项目中同时有CXX和CUDA目标,CXX目标的/Zc:__cplusplus会由CMake自动处理,无需重复添加。
3. 针对单个CUDA目标修正选项
只针对出现问题的CUDA目标调整编译选项,移除错误的/Zc:__cplusplus并添加正确的带前缀版本:
add_library(Foo STATIC empty.cu) # 移除目标上的/Zc:__cplusplus选项 get_target_property(_foo_compile_opts Foo COMPILE_OPTIONS) list(REMOVE_ITEM _foo_compile_opts "/Zc:__cplusplus") set_target_properties(Foo PROPERTIES COMPILE_OPTIONS "${_foo_compile_opts}") # 添加正确的nvcc格式选项 target_compile_options(Foo PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:-Xcompiler=/Zc:__cplusplus>)
CMake中/Zc:__cplusplus的控制逻辑
自动添加触发条件:当CMake检测到使用MSVC编译器,且设置了
CMAKE_CXX_STANDARD(如20)并开启CMAKE_CXX_STANDARD_REQUIRED(默认开启)时,会自动添加/Zc:__cplusplus到CMAKE_CXX_FLAGS中,目的是让MSVC正确报告C++标准版本。INTERFACE目标的传递问题:
spdlog::spdlog_header_only作为INTERFACE目标,会将自身的INTERFACE_COMPILE_OPTIONS(包括从CMAKE_CXX_FLAGS继承的/Zc:__cplusplus)传递给所有依赖它的目标,包括CUDA语言目标。CUDA语言的处理缺陷:CMake在处理CUDA目标时,会自动将
CMAKE_CXX_FLAGS中的部分选项转换为-Xcompiler前缀形式,但对于通过INTERFACE目标传递的选项,可能没有正确处理,导致/Zc:__cplusplus直接被传递给nvcc,而非包装成宿主编译器选项。
内容的提问来源于stack exchange,提问作者CygnusX1

