CMake中的平台检测

37

我添加了一些boost::asio的功能,这导致了一些编译器的“警告”:

请适当定义_WIN32_WINNT或_WIN32_WINDOWS。

这个问题在这里得到了解决。我希望CMake能够检测我是否在Windows上构建,并进行相应的定义或命令行参数设置。


在cmake维基上找到了一个小宝藏:http://www.cmake.org/Wiki/CMake_Useful_Variables。 - 2NinerRomeo
2NinerRomeo:该页面已移至https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/Useful-Variables。 - David Stone
8个回答

66

在 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"),所以我预计编译器标志也会类似。我还没有学会添加预处理器定义。 - 2NinerRomeo

30

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

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

1
如果(WIN32)为真,即使在Windows下进行交叉编译以用于其他操作系统,也会得到-D_WIN32_WINNT。 - robUx4
3
针对 Windows 10 的问题,例如 Windows Kits\8.1\Include\shared\sdkddkver.h(255): error C2177: constant too big,请查看下方 @squareskittles 给出的回答。 - Tarc
多么无用的解决方案。第二个答案应该是正确的。 - Hitesh Kumar Saini

21

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

1
我可以确认这适用于Windows 10(之前使用@KneLL的,但在我更新系统后它就坏了)。 - Tarc
如果CMAKE_SYSTEM_VERSION比SDK版本更新(例如,CMAKE_SYSTEM_VERSION对应于Windows 10,但您只安装了带有Visual Studio 2010的Windows SDK),则可能会导致将_WIN32_WINNT定义为SDK未明确支持的值。这本身可能不是问题,但是如果您基于_WIN32_WINNT的值进行进一步检查,则在构建期间相应的功能可能不可用。 - Simon
确认这是在Windows 10上正确的做法。 - dgmz
3
是的,截至今天,CMake没有提供任何开箱即用的功能来获取WINNT十六进制版本号,因此需要像这样的解决方案来支持Windows 10和早期版本的Windows(在这里查看所有内容)。当然,如果您只需要支持Windows 10,则简单得多:-D_WIN32_WINNT = 0x0A00。 - Kevin

9
我想在这里澄清一个细节,目前没有人提到过。特别适用于交叉编译的情况,但通常也是适用的。
当在Windows上编译时,CMAKE_HOST_WIN32是设置的变量。 当编译为Windows目标平台时,WIN32是设置的变量。
因此,考虑到OP的问题字面上是“当我在Windows上构建时”,CMAKE_HOST_WIN32是正确的标志。在许多情况下,WIN32CMAKE_HOST_WIN32将是等效的,但并非所有情况都是如此。
当在Windows上交叉编译为非Windows时,初始时也会设置WIN32,但在CMake执行过程中的某个时候会被隐式取消(我相信是在project调用内部)。上一次我检查时,在执行toolchain.cmake时设置了WIN32,但后来没有设置。因此,在这些情况下,该标志可能是危险的,因为其状态在执行过程中会发生变化。你不应该在工具链文件中使用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"


5
正如karlphilip所指出的那样,您可以使用if(WIN32)进行平台检测。
传递预处理器定义到应用程序的可能性有很多:
- 使用一个由CMake的configure_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命令来实现这一点。 我通常更喜欢后者,但这主要是个人口味的问题。

1
一份改进版的KneLL答案:

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,并将其转换为正确的十六进制值。


1
由于这个话题仍然活跃,人们仍在搜索,请使用这个答案。我的解决方案是在Win10发布之前发布的。 - KneLL

1

可能是支持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()

-4

这是我想出来的:

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”定义,并且您还将Windows版本固定为XP。 - cmannett85
谢谢。自从我发布这个问题以来,我非常高兴其他人提出了更好的解决方案。由于你的提醒,我已经修改了我接受的答案。 - 2NinerRomeo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接