cmake check if Mac OS X, use APPLE or ${APPLE} - cmake

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.

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

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.

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.

Related

What is the "VERSION" in "cmake_minimum_required(VERSION 3.10)”

I know the meaning of below CMake statement:
cmake_minimum_required(VERSION 3.10)
I am just wondering what the VERSION part is syntactically?
Is it a unquoted argument? If it is an argument, there must be some other argument choices.
But according to here, it seems VERSION is the only choice for the cmake_minimum_required() command.
If so, why do we even need to specify this argument explicitly??
And according to here, this command sets the variable CMAKE_MINIMUM_REQUIRED_VERSION. Is there some kind of string concatenation here? So I can use set_minimum_required(XXX <some_value>) to sent an arbitrary variable with the name CMAKE_MINIMUM_REQUIRED_XXX to <some_value>?
ADD 1
I just tried with below statement in the CMakeLists.txt:
cmake_minimum_required(XXX 123)
And cmake complains that:
CMake Error at CMakeLists.txt:2 (cmake_minimum_required):
cmake_minimum_required called with unknown argument "XXX".
So it seems to be an argument.
But according to here for the project() command, a similar VERSION string is designated as an option. Seems a bit inconsistent.
ADD 2
I just tried with below statement in the CMakeLists.txt:
cmake_minimum_required(3.10)
And cmake complains that:
CMake Error at CMakeLists.txt:1 (cmake_minimum_required):
cmake_minimum_required called with unknown argument "3.10".
So it seems CMake relies on the VERSION part to properly interpret the "3.10" argument. So I guess the VERSION is also an option here.
And since there's another possible option FATAL_ERROR, it is necessary to have a VERSION option.
So to summarize my current understanding:
The essential paradigm of CMake language is:
CMake commands just manipulate variables based on arguments and options.
Some variables are required to be manipulated and some are optional.
For required ones, their values are specified through arguments. (kind of like positional argument)
For optional ones, their values are specified through argument following corresponding options. (kind of like named argument)
ADD 3
From here, for the VERSION in cmake_minimum_required():
The VERSION is a special keyword for this function. And the value of
the version follows the keyword.
So here it is called keyword instead of option ...
Add 4
Some feelings about CMake...
What is the "VERSION" in "cmake_minimum_required(VERSION 3.10)”
I am just wondering what the VERSION part is syntactically?
A string. VERSION. Nothing special.
Is it a unquoted argument?
Yes.
why do we even need to specify this argument explicitly??
To preserve forward compatibility. The idea is that maybe in the feature there will be cmake_minimum_required(DATE 2021-11-03) or similar, for example.
Is there some kind of string concatenation here?
No. It is set explicitly.
So I can use set_minimum_required(XXX <some_value>) to sent an arbitrary variable with the name CMAKE_MINIMUM_REQUIRED_XXX to <some_value>?
No.
Some variables are required to be manipulated and some are optional.
CMake argument parsing is really crude and simple. It's basically in shell pseudocode if $1 == "VERSION" then check_version($2) else error. How you call those strings is really up to your interpretation that depends on the context, in the context of cmake_minimum_required, sure VERSION is a "keyword". Or a "special required argument". Or similar.
All function arguments are strings, and functions compare them to other strings and execute logic upon that. There are no other variable types. Lists are strings, list elements are separated by ; character in a string.
And the implementation of cmake_minimum_required is here: https://gitlab.kitware.com/cmake/cmake/-/blob/master/Source/cmCMakeMinimumRequired.cxx#L29 . Funny, it looks like the parsing is in a loop, so doing cmake_minimum_required(VERSION VERSION VERSION VERSION 3.11) works as fine.
I think the input to a CMake command can be classified as 3 types:
keyword argument (also called option)
positional argument
variable arguments (like ... in C)
For 1, Like the VERSION in cmake_minimum_required, whose value follows it immediately. It is called keyword or option.
For 2, Such argument has a formal name and is referenced through that name within the command body.
For 3, the argument can be referenced with ARGV, ARGN, ARGVC.
I also see marker argument somewhere. But I forget where. Horrible syntax...
ADD 1
OK I found the marker argument. It's in the book Mastering CMake by the CMake team:
...while the CMake official document says it's an option:
Search the paths specified by the PATHS option or in the short-hand
version of the command. These are typically hard-coded guesses.
What a chaotic wording...

How can a CMake variable be hidden?

I have a CMake project which lets a globally set variable (set with -DARDUINO_SDK_PATH=/a/b/c on command line) disappear i.e. suddenly the given value is gone which leads to a fatal error.
I know there are different ways to "hide" a variable (e.g. inside functions or external projects)
In my case:
the variable is not being set explicitly anywhere in the code (e.g. via set() or find_path())
the access which leads to the error is on top level (i.e. not inside a function)
there are instructions (i.e. same file/line) where in one case the variable has the value it's been given and the next time it's gone
Tracing the variable with variable_watch(ARDUINO_SDK_PATH) I can see that everything works fine before the compiler is being checked:
cmake -DARDUINO_SDK_PATH=/a/b/c <path>
...
... everything fine, ${DARDUINO_SDK_PATH} == '/a/b/c' everywhere
...
-- Check for working C compiler: /usr/bin/avr-gcc
...
... here the variable is empty and not being traced any more
...
Here is my suggestion:
Does the compiler check (indicated by check for working C compiler .. on the terminal) have it's own variable space and does not know variables provided on command line?
Note: This question is a generalization of this question, which has become way too specialized but might offer some useful background information.
That any modification to variable is not traced after the variable_watch() command seems like a bug somewhere in CMake to me.
Generally speaking a "cached CMake variable" can be hidden by a "normal CMake variable" with the same name. But e.g. find_path() won't run again or modify a variable if already set.
Here is an example:
cmake_minimum_required(VERSION 2.4)
project(VariableWatchTest NONE)
variable_watch(MY_TEST_VAR)
set(MY_TEST_VAR "something" CACHE INTERNAL "")
message("${MY_TEST_VAR}")
set(MY_TEST_VAR "hiding something")
message("${MY_TEST_VAR}")
unset(MY_TEST_VAR)
message("${MY_TEST_VAR}")
find_path(MY_TEST_VAR NAMES "CMakeLists.txt" HINTS "${CMAKE_CURRENT_LIST_DIR}")
message("${MY_TEST_VAR}")
Would give (without the variable_watch() messages:
-- something
-- hiding something
-- something
-- something
References
What's the CMake syntax to set and use variables?
I'm not sure whether this is a bug or a feature but (at least some) CMake variables are not available in certain steps of the CMake configuration procedure.
You can check this by adding something like this to your toolchain file:
MESSAGE("FOO: ${FOO}")
and run CMake like this
cd build-dir
cmake -DFOO=TEST ..
You will likely see FOO printed with value TEST once in the beginning of the configuration process and later printed again but being empty.
Just don't access variables from the global space inside a toolchain file (doesn't belong there anyway).

Building opencv_contrib on Windows: "CMake Error at cmake/OpenCVDetectPython.cmake:78"

It's my fist time using cmake-gui to make the source code of opencv_contrib. And I get an error below. I've searched for it for many times,but do not get any useful help. Even I don't know what the error means. I'm not sure whether merely copying some lines of the error to Google to search is right. I'm sincerely hoping your help.
The error is :
CMake Error at cmake/OpenCVDetectPython.cmake:78 (if):
if given arguments:
"NOT" "optimized" "C:/Program Files/Python35/libs/python35.lib" "debug" "C:/Program Files/Python35/libs/python35_d.lib" "EQUAL" ""
Unknown arguments specified
Call Stack (most recent call first):
cmake/OpenCVDetectPython.cmake:219 (find_python)
CMakeLists.txt:562 (include)
The cmake/OpenCVDetectPython.cmake:73-80 is
if(_found)
set(_version_major_minor "${_version_major}.${_version_minor}")
if(NOT ANDROID AND NOT APPLE_FRAMEWORK)
ocv_check_environment_variables(${library_env} ${include_dir_env})
if(NOT ${${library_env}} EQUAL "")
set(PYTHON_LIBRARY "${${library_env}}")
endif()
note:
I get the source code (cmake/OpenCVDetectPython.cmake:73-80) from my own file. And the error comes when I try to click the generate button.
My environment is :
CPU : Intel Pentium 2020E(64X)
OS : Windows10(64X)
opencv_version : 3.1.0
cmake_version : 3.7.2(win-64X)
python_version : 3.5
VisualStudio_version : 2015
Drives me mad.....
It seems that code in cmake/OpenCVDetectPython.cmake is incorrect. (It isn't corrected in the repo too).
Proper way for check variable's non-emptiness:
if(NOT "${VAR}" STREQUAL "")
In your case original lines in cmake/OpenCVDetectPython.cmake:
if(NOT ${${library_env}} EQUAL "")
set(PYTHON_LIBRARY "${${library_env}}")
should be rewritten as:
if(NOT "${${library_env}}" STREQUAL "")
set(PYTHON_LIBRARY ${${library_env}})
Explanations are below.
The line, to which error message refers
if(NOT ${${library_env}} EQUAL "")
is a part of function's definition find_python(), and library_env is the parameter of this function.
The function is called twice: one for Python2 and one for Python3. According to error message, it is second call which fails, and it passes PYTHON3_LIBRARY as library_env argument.
So errorneous line can be read as:
if(NOT ${PYTHON3_LIBRARY} EQUAL "")
It tries to check, whether variable is not empty ... but does this wrong:
If the variable is actually empty, CMake completely omits its dereference, so the line would be read as
if(NOT EQUAL "")
which is incorrect call to if() command.
In you case, content of the variable PYTHON3_LIBRARY is a list:
optimized "C:/Program Files/Python35/libs/python35.lib" debug "C:/Program Files/Python35/libs/python35_d.lib"
It is valid value for a library, as long as it linked using target_link_libraries command.
But again, in case of list, the if() line becomes incorrect. That is why you get the error message.
Finally, EQUAL compares integers, but strings are compared with STREQUAL.
Both 1 and 2 problems can be fixed by adding qoutes around variable's dereference.
As for assignment
set(PYTHON_LIBRARY "${${library_env}}")
it incorrectly process list variables, which is exactly you case. List variables should be referenced without quotes around them:
set(A_list ${B_list})

What's the CMake syntax to set and use variables?

I'm asking this as a reminder to myself the next time I use CMake. It never sticks, and Google results aren't great.
What's the syntax to set and use variables in CMake?
When writing CMake scripts there is a lot you need to know about the syntax and how to use variables in CMake.
The Syntax
Strings using set():
set(MyString "Some Text")
set(MyStringWithVar "Some other Text: ${MyString}")
set(MyStringWithQuot "Some quote: \"${MyStringWithVar}\"")
Or with string():
string(APPEND MyStringWithContent " ${MyString}")
Lists using set():
set(MyList "a" "b" "c")
set(MyList ${MyList} "d")
Or better with list():
list(APPEND MyList "a" "b" "c")
list(APPEND MyList "d")
Lists of File Names:
set(MySourcesList "File.name" "File with Space.name")
list(APPEND MySourcesList "File.name" "File with Space.name")
add_excutable(MyExeTarget ${MySourcesList})
The Documentation
CMake/Language Syntax
CMake: Variables Lists Strings
CMake: Useful Variables
CMake set() Command
CMake string()Command
CMake list() Command
Cmake: Generator Expressions
The Scope or "What value does my variable have?"
First there are the "Normal Variables" and things you need to know about their scope:
Normal variables are visible to the CMakeLists.txt they are set in and everything called from there (add_subdirectory(), include(), macro() and function()).
The add_subdirectory() and function() commands are special, because they open-up their own scope.
Meaning variables set(...) there are only visible there and they make a copy of all normal variables of the scope level they are called from (called parent scope).
So if you are in a sub-directory or a function you can modify an already existing variable in the parent scope with set(... PARENT_SCOPE)
You can make use of this e.g. in functions by passing the variable name as a function parameter. An example would be function(xyz _resultVar) is setting set(${_resultVar} 1 PARENT_SCOPE)
On the other hand everything you set in include() or macro() scripts will modify variables directly in the scope of where they are called from.
Second there is the "Global Variables Cache". Things you need to know about the Cache:
If no normal variable with the given name is defined in the current scope, CMake will look for a matching Cache entry.
Cache values are stored in the CMakeCache.txt file in your binary output directory.
The values in the Cache can be modified in CMake's GUI application before they are generated. Therefore they - in comparison to normal variables - have a type and a docstring. I normally don't use the GUI so I use set(... CACHE INTERNAL "") to set my global and persistant values.
Please note that the INTERNAL cache variable type does imply FORCE
In a CMake script you can only change existing Cache entries if you use the set(... CACHE ... FORCE) syntax. This behavior is made use of e.g. by CMake itself, because it normally does not force Cache entries itself and therefore you can pre-define it with another value.
You can use the command line to set entries in the Cache with the syntax cmake -D var:type=value, just cmake -D var=value or with cmake -C CMakeInitialCache.cmake.
You can unset entries in the Cache with unset(... CACHE).
The Cache is global and you can set them virtually anywhere in your CMake scripts. But I would recommend you think twice about where to use Cache variables (they are global and they are persistant). I normally prefer the set_property(GLOBAL PROPERTY ...) and set_property(GLOBAL APPEND PROPERTY ...) syntax to define my own non-persistant global variables.
Variable Pitfalls and "How to debug variable changes?"
To avoid pitfalls you should know the following about variables:
Local variables do hide cached variables if both have the same name
The find_... commands - if successful - do write their results as cached variables "so that no call will search again"
Lists in CMake are just strings with semicolons delimiters and therefore the quotation-marks are important
set(MyVar a b c) is "a;b;c" and set(MyVar "a b c") is "a b c"
The recommendation is that you always use quotation marks with the one exception when you want to give a list as list
Generally prefer the list() command for handling lists
The whole scope issue described above. Especially it's recommended to use functions() instead of macros() because you don't want your local variables to show up in the parent scope.
A lot of variables used by CMake are set with the project() and enable_language() calls. So it could get important to set some variables before those commands are used.
Environment variables may differ from where CMake generated the make environment and when the the make files are put to use.
A change in an environment variable does not re-trigger the generation process.
Especially a generated IDE environment may differ from your command line, so it's recommended to transfer your environment variables into something that is cached.
Sometimes only debugging variables helps. The following may help you:
Simply use old printf debugging style by using the message() command. There also some ready to use modules shipped with CMake itself: CMakePrintHelpers.cmake, CMakePrintSystemInformation.cmake
Look into CMakeCache.txt file in your binary output directory. This file is even generated if the actual generation of your make environment fails.
Use variable_watch() to see where your variables are read/written/removed.
Look into the directory properties CACHE_VARIABLES and VARIABLES
Call cmake --trace ... to see the CMake's complete parsing process. That's sort of the last reserve, because it generates a lot of output.
Special Syntax
Environment Variables
You can can read $ENV{...} and write set(ENV{...} ...) environment variables
Generator Expressions
Generator expressions $<...> are only evaluated when CMake's generator writes the make environment (it comparison to normal variables that are replaced "in-place" by the parser)
Very handy e.g. in compiler/linker command lines and in multi-configuration environments
References
With ${${...}} you can give variable names in a variable and reference its content.
Often used when giving a variable name as function/macro parameter.
Constant Values (see if() command)
With if(MyVariable) you can directly check a variable for true/false (no need here for the enclosing ${...})
True if the constant is 1, ON, YES, TRUE, Y, or a non-zero number.
False if the constant is 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND, the empty string, or ends in the suffix -NOTFOUND.
This syntax is often use for something like if(MSVC), but it can be confusing for someone who does not know this syntax shortcut.
Recursive substitutions
You can construct variable names using variables. After CMake has substituted the variables, it will check again if the result is a variable itself. This is very powerful feature used in CMake itself e.g. as sort of a template set(CMAKE_${lang}_COMPILER ...)
But be aware this can give you a headache in if() commands. Here is an example where CMAKE_CXX_COMPILER_ID is "MSVC" and MSVC is "1":
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") is true, because it evaluates to if("1" STREQUAL "1")
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") is false, because it evaluates to if("MSVC" STREQUAL "1")
So the best solution here would be - see above - to directly check for if(MSVC)
The good news is that this was fixed in CMake 3.1 with the introduction of policy CMP0054. I would recommend to always set cmake_policy(SET CMP0054 NEW) to "only interpret if() arguments as variables or keywords when unquoted."
The option() command
Mainly just cached strings that only can be ON or OFF and they allow some special handling like e.g. dependencies
But be aware, don't mistake the option with the set command. The value given to option is really only the "initial value" (transferred once to the cache during the first configuration step) and is afterwards meant to be changed by the user through CMake's GUI.
References
How is CMake used?
cmake, lost in the concept of global variables (and PARENT_SCOPE or add_subdirectory alternatives)
Looping over a string list
How to store CMake build settings
CMake compare to empty string with STREQUAL failed
When should I quote CMake variables?
Here are a couple basic examples to get started quick and dirty.
One item variable
Set variable:
SET(INSTALL_ETC_DIR "etc")
Use variable:
SET(INSTALL_ETC_CROND_DIR "${INSTALL_ETC_DIR}/cron.d")
Multi-item variable (ie. list)
Set variable:
SET(PROGRAM_SRCS
program.c
program_utils.c
a_lib.c
b_lib.c
config.c
)
Use variable:
add_executable(program "${PROGRAM_SRCS}")
CMake docs on variables
$ENV{FOO} for usage, where FOO is being picked up from the environment variable. otherwise use as ${FOO}, where FOO is some other variable. For setting, SET(FOO "foo") would be used in CMake.

Getting CMake CHECK_CXX_COMPILER_FLAG to work

Note: This is my first time using CMake. I don't know much about it, so I'm just posting a bunch of information to see if anyone can see my problem.
I would like the ability to automatically determine which c++11 flag is appropriate, given my compiler. There are many examples of this line. Here is my CMakeLists.txt following such an example:
cmake_minimum_required (VERSION 2.8)
#Add the c++11 flag, whatever it is
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG(-std=c++11 COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG(-std=c++0x COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
project(AnalyzeGames)
set(AnalyzeGames_SRCS AnalyzeGames.cpp)
add_executable(AnalyzeGames ${AnalyzeGames_SRCS})
Here is my cmake output when trying to use this file: http://pastebin.com/3AUwqffD
Here is CMakeError.log: http://pastebin.com/EbNKvGt8
Here is CMakeOutput.log: http://pastebin.com/kVJ0enJC
echo $CC: /usr/bin/gcc
echo $CXX: /usr/bin/g++
I can compile a simple test executable with g++ using either flag manually.
cmake --version: cmake version 2.8.12.2
For some reason CMake is not recognizing that my compiler does support both of those flags.
The cmake output tells you that it does not recognize the '.cxx' extension because it doesn't know that your project is a C++ project. To fix this, you should enable C++ in the project command. Try to change the following line:
project(AnalyzeGames)
to:
project(AnalyzeGames CXX)
and then move it to the 2nd line of the CMakeLists.txt, right under cmake_minimum_required. The configuration should work as expected after this.
TLDR
Compiler checks are only performed in the variable passed is not previously defined, which includes in the cache from previous failed attempts. Use unset(my_var CACHE) to force checking to always occur, or just be aware of this behaviour and clear the cache manually when needed.
Detail
I too had this problem (with cmake 2.8.12.2) and I had to turn on trace output, and step through the code to get a similar toy build to work I had make sure the variables I used (COMPILER_SUPPORTS_CXX11_*) in these calls:
CHECK_CXX_COMPILER_FLAG(-std=c++11 COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG(-std=c++0x COMPILER_SUPPORTS_CXX0X)
Were set such that they named themselves:
set(COMPILER_SUPPORTS_CXX11 "COMPILER_SUPPORTS_CXX11")
The other posters solution didn't work for me, it mainly just seemed to limit the detecting of compilers to just CXX and ignored the C compiler.
The issue appears to be with this line of code in the cmake module:
if("${VAR}" MATCHES "^${VAR}$")
Which in the trace output is:
/usr/share/cmake/Modules/CheckCXXSourceCompiles.cmake(30): if(COMPILER_SUPPORTS_CXX0X MATCHES ^COMPILER_SUPPORTS_CXX0X$ )
It looks as if the expression on the left of the MATCHES is replaced with the variables value, but the expression on the right is assumed to be plain text.
If the MATCH fails then the main part of the macro is skipped and according the to the log the check fails.
Looking at later versions of this macro online it looks as if this line has changed to only perform the compile check if the variable is undefined.
It as at this point that I realise that this is the intent / hack of the original code; if the X is undefined then "X" MATCHES "^X$" will be true, but then the compile check can be performed, fail for some other reason and then never be performed again.
So the solution is either force unset of variable in cache before calling the macro using:
unset(COMPILER_SUPPORTS_CXX0X CACHE)
Or clear the cache manually and be prepared for this behaviour.