Platform detection in CMake
Asked Answered
V

8

39

I've added some functionality from boost::asio, which has precipitated some compiler "warnings":

Please define _WIN32_WINNT or _WIN32_WINDOWS appropriately.

That problem was dealt with here. I'd like to have CMake detect when I am building on Windows and make the appropriate definitions or command line arguments.

Vogul answered 16/3, 2012 at 17:41 Comment(2)
Found a little goldmine on the cmake wiki: cmake.org/Wiki/CMake_Useful_Variables..Vogul
2NinerRomeo: That page has moved to gitlab.kitware.com/cmake/community/wikis/doc/cmake/…Dregs
C
30

Here is a simple solution.

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})
Convector answered 24/7, 2013 at 21:54 Comment(3)
if (WIN32) if true even if you're cross compiling under Windows for something else than Windows. You'll end up with -D_WIN32_WINNT even if you're building for something else.Improbable
For Windows 10 issues, such as Windows Kits\8.1\Include\shared\sdkddkver.h(255): error C2177: constant too big, check @squareskittles' answer below.Liman
what a useless solution. Second answer deserves to be correct.Territoriality
S
72

Inside the CMakeLists.txt file you can do:

IF (WIN32)
  # set stuff for windows
ELSE()
  # set stuff for other systems
ENDIF()
Serge answered 16/3, 2012 at 18:1 Comment(1)
That's partway there, I'm poking around for how to define preprocessor definitions and compiler command line flags. I think the linker syntax is like this set(CMAKE_EXE_LINK_FLAGS "${CMAKE_EXE_LINK_FLAGS} -linkerflag") so I expect it will be similar for compiler flags. I also have not learned to add preprocessor definitions.Vogul
C
30

Here is a simple solution.

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})
Convector answered 24/7, 2013 at 21:54 Comment(3)
if (WIN32) if true even if you're cross compiling under Windows for something else than Windows. You'll end up with -D_WIN32_WINNT even if you're building for something else.Improbable
For Windows 10 issues, such as Windows Kits\8.1\Include\shared\sdkddkver.h(255): error C2177: constant too big, check @squareskittles' answer below.Liman
what a useless solution. Second answer deserves to be correct.Territoriality
L
22

Here's an expanded version of KneLL's answer, also checking for 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()
Lightproof answered 24/10, 2016 at 11:31 Comment(4)
I can confirm this works for Windows 10 (was using @KneLL's but it got broken when I updated my system).Liman
If CMAKE_SYSTEM_VERSION happens to be newer than the SDK version (e.g. CMAKE_SYSTEM_VERSION corresponds to Windows 10 but you only have the Windows SDK installed with Visual Studio 2010), this may lead to defining _WIN32_WINNT to a value that is not explicitly supported by the SDK. This is probably not a problem as such, but if you happen to do further checks based on the value of _WIN32_WINNT, the corresponding features might not be available during the build.Colwin
confirming that this is the proper way to do for Windows 10.Uptrend
@fr4nk Yes, as of today, CMake does not provide anything out-of-the-box for getting the WINNT hexadecimal version, so a solution like this is required to support Windows 10 and older Windows version (see all here). Of course, if you only need to support Windows 10, it is much simpler: -D_WIN32_WINNT=0x0A00Lightproof
C
13

I would like to clarify one detail here that nobody has mentioned yet. This point especially applies when cross compiling, but is valid otherwise too.

CMAKE_HOST_WIN32 is the variable that is set when compiling ON Windows.

WIN32 is the variable that is set when compiling FOR a Windows target platform.

So CMAKE_HOST_WIN32 is the correct flag to use considering OP's question literally "when I am building on Windows". In many cases WIN32 and CMAKE_HOST_WIN32 will be equivalent, but not in all cases.

WIN32 will be set too in the beginning when cross-compiling ON Windows FOR non-Windows, but will be implicitly unset at some point during CMake execution (I believe inside project call). Last time I checked WIN32 was set during executing toolchain.cmake, but was not set at a later point. So in these cases this flag can be dangerous as its state changes during the execution. You should NOT use WIN32 inside a toolchain file!

If you need details about the platform you are building ON, you can try variables CMAKE_HOST_SYSTEM_NAME and CMAKE_HOST_SYSTEM_VERSION

MESSAGE("CMAKE_HOST_SYSTEM_NAME ${CMAKE_HOST_SYSTEM_NAME}")
MESSAGE("CMAKE_HOST_SYSTEM_VERSION ${CMAKE_HOST_SYSTEM_VERSION}")

gives me the following output (on CMake version 3.19.2)

CMAKE_HOST_SYSTEM_NAME Windows
CMAKE_HOST_SYSTEM_VERSION 10.0.19044

There is also CMAKE_HOST_SYSTEM which inside toolchain file gave me blank, but after project call it gave "Windows-10.0.19044"

Cerberus answered 16/12, 2021 at 16:29 Comment(0)
A
5

As karlphilip pointed out, you can use if(WIN32) for platform detection.

You'll have a number of possibilities for passing preprocessor defines to the application:

  • Use a configure header that gets preprocessed by CMake's configure_file. This approach has the advantage that all #defines are actually part of the code and not of the build environment. The disadvantage is that it requires an (automatic) preprocessing step by CMake
  • Use add_definitions. This will add the preprocessor flag for all source files in the project, so it's more of a quick and dirty approach.
  • Use the COMPILE_DEFINITIONS property. This allows fine grained control over the defines on a per-file (and even per-config) basis: set_property(SOURCE ${YOUR_SOURCE_FILES} APPEND PROPERTY COMPILE_DEFINITIONS YOUR_FLAG1 YOUR_FLAG2)

    In modern CMake versions (2.8.12 and higher) you can also use the more convenient target_compile_definitions command for this.

I usually prefer the latter, but that's mostly a matter of personal taste.

Attis answered 16/3, 2012 at 18:35 Comment(0)
C
1

An improved version of KneLLs answer:

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 version did no work in my case, because CMAKE_SYSTEM_VERSION was 6.3.9600 which resulted in ${ver}=0x060306090000

This version will fail for Windows 10 and later, though. One has to check if the first number is bigger than 9 and convert it to the correct hexadecimal value.

Carrollcarronade answered 28/1, 2016 at 16:35 Comment(1)
Since this topic is still active and people still search for it please use this answer. My solution was posted before Win10 was released.Convector
W
1

Probably the most concise version with Win10 support, requires 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()
Wholewheat answered 12/3, 2022 at 20:16 Comment(0)
V
-4

This is what I have come up with:

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

For dealing with the boost::asio "warning" either the preprocessor definition or the command line flag gets the job done.

Vogul answered 16/3, 2012 at 18:41 Comment(2)
This is wrong. You're adding the _WIN32_WINNT define twice, and you're also fixing the Windows version at XP.Robey
Thanks. I am so pleased that others have come up with superior solutions since I posted this question. Since you've brought my attention to it, I've changed my accepted answer.Vogul

© 2022 - 2024 — McMap. All rights reserved.