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 预处理的配置标头。这种方法的优点是所有#defines 实际上都是代码的一部分,而不是构建环境的一部分。缺点是它需要 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_VERSION6.3.9600 导致 $ver=0x060306090000

不过,此版本在 Windows 10 及更高版本上会失败。必须检查第一个数字是否大于 9 并将其转换为正确的十六进制值。

【讨论】:

由于该主题仍然处于活动状态并且人们仍在搜索它,请使用此答案。我的解决方案是在Win10发布之前发布的。【参考方案6】:

我想在这里澄清一个尚未有人提及的细节。 这一点在交叉编译时尤其适用,但在其他情况下也有效。

CMAKE_HOST_WIN32 是在 Windows 上编译时设置的变量。

WIN32 是编译 FOR Windows 目标平台时设置的变量。

所以CMAKE_HOST_WIN32 是考虑到OP 的问题“当我在Windows 上构建时”的正确标志。在许多情况下,WIN32CMAKE_HOST_WIN32 是等价的,但并非在所有情况下都一样。

WIN32 将在开始时在 Windows 上为非 Windows 进行交叉编译时设置,但在 CMake 执行期间的某个时候会隐式取消设置(我相信在project 调用中)。上次我检查 WIN32 是在执行 toolchain.cmake 期间设置的,但后来没有设置。所以在这些情况下,这个标志可能是危险的,因为它的状态在执行期间发生了变化。你不应该在工具链文件中使用WIN32

如果您需要有关正在构建的平台的详细信息,可以尝试变量 CMAKE_HOST_SYSTEM_NAMECMAKE_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 中的平台检测的主要内容,如果未能解决你的问题,请参考以下文章

CMake 检测到错误版本的 OpenCL

虽然安装了CMake并将其添加到Path,但CLion无法检测到CMake

由于警告,CMake 未能检测到 pthread

cmake 将 clang-cl 检测为 clang

Jest 和 React Native 中的模拟平台检测

检测传输中的文件?