13

I would like check whether I am in Mac OS X or not, and have the following code

cmake_minimum_required (VERSION 3.0)
project (test)
set (FOO 1)
if (${FOO} AND ${APPLE})
  message ("MAC OS X")
endif ()

It failed on non-OSX system with error message

CMake Error at CMakeLists.txt:4 (if):
  if given arguments:

    "1" "AND"

  Unknown arguments specified

If I replace ${APPLE} with APPLE, the error went away. But I am a little puzzled by this. When should we refer to a variable with ${VAR} and when should we not to?

Thanks in advance.

Ying Xiong
  • 4,578
  • 8
  • 33
  • 69

3 Answers3

45

Not 100% relevant but when googling for how to check for OSx in CMake this is the top post. For others who land here asking the same question this worked for me.

if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
    set(MACOSX TRUE)
endif()
Matthew Hoggan
  • 7,402
  • 16
  • 75
  • 140
  • 2
    Thanks, this is what I am looking for! – Wilson Chen Feb 02 '19 at 14:20
  • 2
    The CMake mailing lists mentions that this is not technically foolproof: *"Darwin is the Unix underpinnings of OS X and it is Open Source. I could run a Darwin system and have it NOT be OS X."*. Other than `APPLE`, I couldn't find any strong alternatives. – Xeverous Jan 08 '20 at 20:46
12

To put it shortly: Everything inside the if parentheses is evaluated as an expression, this is the semantic of the if keyword. So if you put APPLE there, it gets evaluated as a variable name and yields the correct result.

Now if you put ${APPLE} there, ${} will evaluate its contents before if evaluates the expression. Therefore, it's the same as if you'd written

if (1 AND )

(in the case that the variable APPLE isn't set, which is the case on non-OSX systems). This is invalid syntax and yields the error you get. You should write:

if (FOO AND APPLE)

Quoting from the CMake Documentation:

The if command was written very early in CMake’s history, predating the ${} variable evaluation syntax, and for convenience evaluates variables named by its arguments as shown in the above signatures. Note that normal variable evaluation with ${} applies before the if command even receives the arguments. Therefore code like:

set(var1 OFF)
set(var2 "var1")
if(${var2})

appears to the if command as:

if(var1)

and is evaluated according to the if() case documented above. The result is OFF which is false. However, if we remove the ${} from the example then the command sees:

if(var2)

which is true because var2 is defined to “var1” which is not a false constant.

flyx
  • 35,506
  • 7
  • 89
  • 126
3

This also works to test for Windows, Mac and *Nix

if(WIN32)
   message("https://cmake.org/cmake/help/latest/variable/WIN32.html?highlight=win32")
elseif(APPLE)
   message("https://cmake.org/cmake/help/latest/variable/APPLE.html?highlight=apple")
elseif(UNIX)
    message("https://cmake.org/cmake/help/latest/variable/UNIX.html?highlight=unix")
endif()

APPLE should be tested before UNIX because UNIX is set to true for Mac OS.

Testing the CMAKE_SYSTEM_NAME variable, as proposed by @Matthew Hogan allows you to test for something more specific.

MelS
  • 79
  • 2