CMake中的平台检测

Platform detection in CMake

本文关键字:检测 平台 CMake      更新时间:2023-10-16

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

请适当定义_WIN32_WINNT或_WIN32_PINDOWS。

这个问题在这里得到了解决。当我在Windows上构建时,我想让CMake检测,并做出适当的定义或命令行参数。

在CMakeLists.txt文件中,您可以执行以下操作:

IF (WIN32)
  # set stuff for windows
ELSE()
  # set stuff for other systems
ENDIF()

这里有一个简单的解决方案。

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})

这是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()

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

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

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

因此CCD_ 4是考虑OP的问题时使用的正确标志;当我在Windows上构建时";。在许多情况下,WIN32CMAKE_HOST_WIN32是等效的,但并非在所有情况下都是等效的。

在非Windows的Windows上交叉编译时,WIN32也会在一开始被设置,但在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调用后,它给出了"0";Windows-10.0.19044";

正如karlphilip所指出的,您可以使用if(WIN32)进行平台检测。

您将有许多将预处理器定义传递给应用程序的可能性:

  • 使用由CMake的config_file预处理的配置标头。这种方法的优点是,所有#define实际上都是代码的一部分,而不是构建环境的一部分。缺点是它需要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命令进行此操作。

我通常更喜欢后者,但这主要是个人品味的问题。

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的最简洁的版本,需要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()

这就是我所想到的:

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"警告",预处理器定义或命令行标志可以完成任务。