【问题标题】:Platform detection in CMakeCMake 中的平台检测
【发布时间】:2012-04-02 06:37:03
【问题描述】:

我从boost::asio 添加了一些功能,这引发了一些编译器“警告”:

请适当定义_WIN32_WINNT或_WIN32_WINDOWS。

here 已解决该问题。我想让 CMake 检测我何时在 Windows 上构建并做出适当的定义或命令行参数。

【问题讨论】:

标签: c++ cmake boost-asio


【解决方案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 版本。
        • 谢谢。自从我发布此问题以来,我很高兴其他人提出了出色的解决方案。既然你引起了我的注意,我已经改变了我接受的答案。
        猜你喜欢
        • 2015-01-11
        • 1970-01-01
        • 1970-01-01
        • 2018-06-25
        • 2014-07-11
        • 1970-01-01
        • 2023-03-04
        • 2020-09-28
        相关资源
        最近更新 更多