CMake 中的平台检测
Posted
技术标签:
【中文标题】CMake 中的平台检测【英文标题】:Platform detection in CMake 【发布时间】:2012-04-02 06:37:03 【问题描述】:我从boost::asio
添加了一些功能,这引发了一些编译器“警告”:
请适当定义_WIN32_WINNT或_WIN32_WINDOWS。
here 已解决该问题。我想让 CMake 检测我何时在 Windows 上构建并做出适当的定义或命令行参数。
【问题讨论】:
在 cmake wiki 上发现了一个小金矿:cmake.org/Wiki/CMake_Useful_Variables.. 2NinerRomeo:该页面已移至gitlab.kitware.com/cmake/community/wikis/doc/cmake/… 【参考方案1】:在 CMakeLists.txt 文件中你可以这样做:
IF (WIN32)
# set stuff for windows
ELSE()
# set stuff for other systems
ENDIF()
【讨论】:
到了一半,我正在研究如何定义预处理器定义和编译器命令行标志。我认为链接器语法是这样的set(CMAKE_EXE_LINK_FLAGS "$CMAKE_EXE_LINK_FLAGS -linkerflag")
所以我希望编译器标志会类似。我还没有学会添加预处理器定义。【参考方案2】:
这是一个简单的解决方案。
macro(get_WIN32_WINNT version)
if (WIN32 AND CMAKE_SYSTEM_VERSION)
set(ver $CMAKE_SYSTEM_VERSION)
string(REPLACE "." "" ver $ver)
string(REGEX REPLACE "([0-9])" "0\\1" ver $ver)
set($version "0x$ver")
endif()
endmacro()
get_WIN32_WINNT(ver)
add_definitions(-D_WIN32_WINNT=$ver)
【讨论】:
if (WIN32) 如果为真,即使您在 Windows 下为 Windows 以外的其他东西进行交叉编译。即使您正在为其他东西构建,您最终也会得到 -D_WIN32_WINNT。 对于 Windows 10 问题,例如Windows Kits\8.1\Include\shared\sdkddkver.h(255): error C2177: constant too big
,请查看下面@squareskittles 的答案。
多么无用的解决方案。第二个答案应该是正确的。【参考方案3】:
这是 KneLL 答案的扩展版本,也检查 Windows 10。
if(WIN32)
macro(get_WIN32_WINNT version)
if(CMAKE_SYSTEM_VERSION)
set(ver $CMAKE_SYSTEM_VERSION)
string(REGEX MATCH "^([0-9]+).([0-9])" ver $ver)
string(REGEX MATCH "^([0-9]+)" verMajor $ver)
# Check for Windows 10, b/c we'll need to convert to hex 'A'.
if("$verMajor" MATCHES "10")
set(verMajor "A")
string(REGEX REPLACE "^([0-9]+)" $verMajor ver $ver)
endif()
# Remove all remaining '.' characters.
string(REPLACE "." "" ver $ver)
# Prepend each digit with a zero.
string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver $ver)
set($version "0x$ver")
endif()
endmacro()
get_WIN32_WINNT(ver)
add_definitions(-D_WIN32_WINNT=$ver)
endif()
【讨论】:
我可以确认这适用于 Windows 10(正在使用 @KneLL,但在我更新系统时它坏了)。 如果 CMAKE_SYSTEM_VERSION 恰好比 SDK 版本新(例如 CMAKE_SYSTEM_VERSION 对应于 Windows 10,但您只安装了 Visual Studio 2010 的 Windows SDK),这可能导致将 _WIN32_WINNT 定义为SDK 未明确支持。这可能不是问题,但是如果您碰巧根据 _WIN32_WINNT 的值进行进一步检查,则相应的功能可能在构建期间不可用。 确认这是适用于 Windows 10 的正确方法。 @fr4nk 是的,截至今天,CMake 没有提供任何开箱即用的东西来获取 WINNT 十六进制版本,因此需要这样的解决方案来支持 Windows 10 和更早的 Windows 版本(查看全部here)。当然,如果只需要支持Windows 10,那就简单多了:-D_WIN32_WINNT=0x0A00
【参考方案4】:
正如 karlphilip 指出的,您可以使用if(WIN32)
进行平台检测。
您可以通过多种方式将预处理器定义传递给应用程序:
使用由 CMake 的 configure_file 预处理的配置标头。这种方法的优点是所有#define
s 实际上都是代码的一部分,而不是构建环境的一部分。缺点是它需要 CMake 的(自动)预处理步骤
使用add_definitions。这将为项目中的所有源文件添加预处理器标志,因此它更像是一种快速而肮脏的方法。
使用COMPILE_DEFINITIONS 属性。这允许在每个文件(甚至每个配置)的基础上对定义进行细粒度控制:
set_property(SOURCE $YOUR_SOURCE_FILES APPEND PROPERTY
COMPILE_DEFINITIONS YOUR_FLAG1 YOUR_FLAG2)
在现代 CMake 版本(2.8.12 及更高版本)中,您还可以为此使用更方便的 target_compile_definitions
命令。
我通常更喜欢后者,但这主要是个人喜好问题。
【讨论】:
【参考方案5】:KneLLs 答案的改进版本:
macro(get_WIN32_WINNT version)
if (WIN32 AND CMAKE_SYSTEM_VERSION)
set(ver $CMAKE_SYSTEM_VERSION)
string(REGEX REPLACE "^([0-9])[.]([0-9]).*" "0\\10\\2" ver $ver)
set($version "0x$ver")
endif()
endmacro()
get_WIN32_WINNT(ver)
add_definitions(-D_WIN32_WINNT=$ver)
KneLLs 版本在我的情况下不起作用,因为 CMAKE_SYSTEM_VERSION
是 6.3.9600
导致 $ver=0x060306090000
不过,此版本在 Windows 10 及更高版本上会失败。必须检查第一个数字是否大于 9 并将其转换为正确的十六进制值。
【讨论】:
由于该主题仍然处于活动状态并且人们仍在搜索它,请使用此答案。我的解决方案是在Win10发布之前发布的。【参考方案6】:我想在这里澄清一个尚未有人提及的细节。 这一点在交叉编译时尤其适用,但在其他情况下也有效。
CMAKE_HOST_WIN32
是在 Windows 上编译时设置的变量。
WIN32
是编译 FOR Windows 目标平台时设置的变量。
所以CMAKE_HOST_WIN32
是考虑到OP 的问题“当我在Windows 上构建时”的正确标志。在许多情况下,WIN32
和 CMAKE_HOST_WIN32
是等价的,但并非在所有情况下都一样。
WIN32
将在开始时在 Windows 上为非 Windows 进行交叉编译时设置,但在 CMake 执行期间的某个时候会隐式取消设置(我相信在project
调用中)。上次我检查 WIN32
是在执行 toolchain.cmake 期间设置的,但后来没有设置。所以在这些情况下,这个标志可能是危险的,因为它的状态在执行期间发生了变化。你不应该在工具链文件中使用WIN32
!
如果您需要有关正在构建的平台的详细信息,可以尝试变量 CMAKE_HOST_SYSTEM_NAME
和 CMAKE_HOST_SYSTEM_VERSION
MESSAGE("CMAKE_HOST_SYSTEM_NAME $CMAKE_HOST_SYSTEM_NAME")
MESSAGE("CMAKE_HOST_SYSTEM_VERSION $CMAKE_HOST_SYSTEM_VERSION")
给我以下输出(在 CMake 版本 3.19.2 上)
CMAKE_HOST_SYSTEM_NAME Windows
CMAKE_HOST_SYSTEM_VERSION 10.0.19044
还有CMAKE_HOST_SYSTEM
,里面的工具链文件给了我空白,但是在project
调用之后它给了“Windows-10.0.19044”
【讨论】:
【参考方案7】:可能是最简洁的支持 Win10 的版本,需要 CMake 3.13+
macro(get_win_hex outvar)
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)" $outvar $CMAKE_SYSTEM_VERSION)
math(EXPR $outvar "($CMAKE_MATCH_1 << 8) + $CMAKE_MATCH_2" OUTPUT_FORMAT HEXADECIMAL)
endmacro()
if(WIN32)
get_win_hex(winver)
add_compile_definitions(_WIN32_WINNT=$winver)
endif()
【讨论】:
【参考方案8】:这是我想出的:
if($WIN32)
#this method adds the necessary compiler flag
set(CMAKE_CXX_FLAGS "$CMAKE_CXX_FLAGS -D_WIN32_WINNT=0x0501")
#this adds a preprocessor definition to the project
add_definitions(-D_WIN32_WINNT=0x0501)
endif()
为了处理 boost::asio “警告”,预处理器定义或命令行标志都可以完成工作。
【讨论】:
这是错误的。您添加了两次_WIN32_WINNT
定义,并且还修复了 XP 的 Windows 版本。
谢谢。自从我发布此问题以来,我很高兴其他人提出了出色的解决方案。既然你引起了我的注意,我已经改变了我接受的答案。以上是关于CMake 中的平台检测的主要内容,如果未能解决你的问题,请参考以下文章