How to print a message exactly once per cmake invocation? - cmake

Wanting to cause a package foobar to print where it was found, when using
find_package(foobar CONFIG)
I am using
find_package_message(foobar
"Found foobar: ${info} (version ${foobar_VERSION})"
"[${info}][${foobar_VERSION}]"
)
The idea of using find_package_message is to only
print this message once.
However, I want to print it every time cmake is run from the start.
I only want to avoid duplicates during the same run of cmake.
find_package_message stores a variable in the cache (FIND_PACKAGE_MESSAGE_DETAILS_foobar)
containing the value of the above third argument ("[${info}][${foobar_VERSION}]") and
prints the message again when that variable doesn't exist or changed.
So, the result of running cmake a second time is that nothing is printed: FIND_PACKAGE_MESSAGE_DETAILS_foobar already exists in the cache and didn't change.
How can I fix this to print a message once every new invocation of cmake?

Function find_package_message is intended for print the message once until "details" are changed. For achieve different semantic - print message once per cmake invocation - there are a little sense to use this function but implement your own one.
For differentiate the first function invocation from further ones one may check whether GLOBAL property is defined:
function(print_message_once name message)
# Name of the custom GLOBAL property to check
set(pname PRINT_MESSAGE_ONCE_DUMMY_${name})
get_property(prop_defined GLOBAL PROPERTY ${pname} DEFINED)
if (NOT prop_defined)
message(STATUS "${message}")
# Define a property so next time it will exist
define_property(GLOBAL PROPERTY ${pname} BRIEF_DOCS "${name}" FULL_DOCS "${name}")
endif()
endfunction()
Note, this function is no longer requires details argument. It is very unlikely that during a single cmake invocation one will clear the cache after the first finding the package and performs second search with different parameters.
Alternatively, instead of property check, one may check existence of the function:
function(print_message_once name message)
# Name of the custom function to check
set(fname _check_first_dummy_${name})
if (NOT COMMAND ${fname})
message(STATUS "${message}")
# Define a function so next time it will exist
function(${fname})
endfunction()
endif()
endfunction()
Usage of function print_message_once (defined above using any of 2 ways) "compatible" with find_package_message is
if(NOT foobar_FIND_QUIETLY)
print_message_once(foobar
"Found foobar: ${info} (version ${foobar_VERSION})"
)
endif()
If desired, checking for XXX_FIND_QUIETLY variable (which reflects QUIET option of find_package() call) could be incorporated into print_message_once function itself.

Related

CMake set variable

In the following CMake code snippet, I am confused by the if elseif check. My understanding is that BL will always be "Ei", so there is no need to check other values. Are there any scenarios where BL could be overwritten by something else? I am new to CMake so need some help here.
set(BL "Ei" CACHE STRING "library")
set_property(CACHE BL PROPERTY STRINGS "Ei;AT;Op")
message(STATUS "The backend of choice:" ${BL})
if(BL STREQUAL "Ei")
...
elseif(BL STREQUAL "AT")
...
elseif(BL STREQUAL "Op")
...
else()
message(FATAL_ERROR "Unrecognized option:" ${BL})
endif()
The code set(BL "Ei" CACHE STRING "library") defines a CMake cache variable. However, without a FORCE option in the set statement, that means that it will not be overwritten if the variable was previously defined in the cache.
One way for a user to set a different value for BL would be on the cmake command line. For example: cmake ../sourcedir -DBL:STRING=AT
By entering the variable in the cache as type STRING (as opposed to type INTERNAL) that also makes the variable available to be configured in cmake-gui or in ccmake. (Furthermore, the set_property(... STRINGS ...) directive tells cmake-gui to produce a drop-down list containing Ei, AT, and Op to select from. However, this isn't enforced for setting the variable from the command line, which is why it's still a good idea to have the default case signalling an error.)
See the section "Set Cache Entry" under CMake's documentation for set for more information.

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

Get full C++ compiler command line

In CMake, the flags for the C++ compiler can be influenced in various ways: setting CMAKE_CXX_FLAGS manually, using add_definitions(), forcing a certain C++ standard, and so forth.
In order to compile a target in the same project with different rules (a precompiled header, in my case), I need to reproduce the exact command that is used to compile files added by a command like add_executable() in this directory.
Reading CMAKE_CXX_FLAGS only returns the value set to it explicitly, CMAKE_CXX_FLAGS_DEBUG and siblings only list default Debug/Release options. There is a special functions to retrieve the flags from add_definitions() and add_compiler_options(), but none seem to be able to return the final command line.
How can I get all flags passed to the compiler into a CMake variable?
To answer my own question: It seems like the only way of getting all compiler flags is to reconstruct them from the various sources. The code I'm working with now is the following (for GCC):
macro (GET_COMPILER_FLAGS TARGET VAR)
if (CMAKE_COMPILER_IS_GNUCXX)
set(COMPILER_FLAGS "")
# Get flags form add_definitions, re-escape quotes
get_target_property(TARGET_DEFS ${TARGET} COMPILE_DEFINITIONS)
get_directory_property(DIRECTORY_DEFS COMPILE_DEFINITIONS)
foreach (DEF ${TARGET_DEFS} ${DIRECTORY_DEFS})
if (DEF)
string(REPLACE "\"" "\\\"" DEF "${DEF}")
list(APPEND COMPILER_FLAGS "-D${DEF}")
endif ()
endforeach ()
# Get flags form include_directories()
get_target_property(TARGET_INCLUDEDIRS ${TARGET} INCLUDE_DIRECTORIES)
foreach (DIR ${TARGET_INCLUDEDIRS})
if (DIR)
list(APPEND COMPILER_FLAGS "-I${DIR}")
endif ()
endforeach ()
# Get build-type specific flags
string(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE_SUFFIX)
separate_arguments(GLOBAL_FLAGS UNIX_COMMAND
"${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BUILD_TYPE_SUFFIX}}")
list(APPEND COMPILER_FLAGS ${GLOBAL_FLAGS})
# Add -std= flag if appropriate
get_target_property(STANDARD ${TARGET} CXX_STANDARD)
if ((NOT "${STANDARD}" STREQUAL NOTFOUND) AND (NOT "${STANDARD}" STREQUAL ""))
list(APPEND COMPILER_FLAGS "-std=gnu++${STANDARD}")
endif ()
endif ()
set(${VAR} "${COMPILER_FLAGS}")
endmacro ()
This could be extended to also include options induced by add_compiler_options() and more.
Easiest way is to use make VERBOSE=1 when compiling.
cd my-build-dir
cmake path-to-my-sources
make VERBOSE=1
This will do a single-threaded build, and make will print every shell command it runs just before it runs it. So you'll see output like:
[ 0%] Building CXX object Whatever.cpp.o
<huge scary build command it used to build Whatever.cpp>
There actually is a fairly clean way to do this at compile time using CXX_COMPILER_LAUNCHER:
If you have a script print_args.py
#!/usr/bin/env python
import sys
import argparse
print(" ".join(sys.argv[1:]))
# we need to produce an output file so that the link step does not fail
p = argparse.ArgumentParser()
p.add_argument("-o")
args, _ = p.parse_known_args()
with open(args.o, "w") as f:
f.write("")
You can set the target's properties as follows:
add_library(${TARGET_NAME} ${SOURCES})
set_target_properties(${TARGET_NAME} PROPERTIES
CXX_COMPILER_LAUNCHER
${CMAKE_CURRENT_SOURCE_DIR}/print_args.py
)
# this tells the linker to not actually link. Which would fail because output file is empty
set_target_properties(${TARGET_NAME} PROPERTIES
LINK_FLAGS
-E
)
This will print the exact compilation command at compile time.
Short answer
It's not possible to assign final value of compiler command line to variable in CMake script, working in all use cases.
Long answer
Unfortunately, even solution accepted as answer still not gets all compiler flags. As gets noted in comments, there are Transitive Usage Requirements. It's a modern and proper way to write CMake files, getting more and more popular. Also you may have some compile options defined using generator expressions (they look like variable references but will not expand when needed).
Consider having following example:
add_executable(myexe ...);
target_compile_definitions(myexe PRIVATE "PLATFORM_$<PLATFORM_ID>");
add_library(mylib ...);
target_compile_definitions(mylib INTERFACE USING_MY_LIB);
target_link_libraries(myexe PUBLIC mylib);
If you try to call proposed GET_COMPILER_FLAGS macro with myexe target, you will get resulting output -DPLATFORM_$<PLATFORM_ID> instead of expected -DPLATFORM_Linux -DUSING_MY_LIB.
This is because there are two stages between invoking CMake and getting build system generated:
Processing. At this stage CMake reads and executes commands from cmake script(s), particularly, variable values getting evaluated and assigned. At this moment CMake just collecting all required info and being prepared to generate build system (makefiles).
Generating. CMake uses values of special variables and properties, being left at end of processed scripts to finally decide and form generated output. This is where it constructs final command line for compiler according to its internal algorithm, not avaliable for scripting.
Target properties which might be retrieved at processing stage with get_target_property(...) or get_property(... TARGET ...) aren't complete (even when invoked at the end of script). At generating stage CMake walks through each target dependency tree (recursively) and appends properties values according to transitive usage requirements (PUBLIC and INTERFACE tagged values gets propagated).
Although, there are workarounds, depending on what final result you aiming to achieve. This is possible by applying generator expressions, which allows use final values of properties of any target (defined at processing stage)... but later!
Two general possibilites are avaliable:
Generate any output file based on template, which content contains variable references and/or generator expressions, and defined as either string variable value, or input file. It's not flexible due to very limited support of conditional logic (i.e. you cannot use complex concatenations available only with nested foreach() loops), but has advantages, that no further actions required and content described in platform-independent way. Use file(GENERATE ...) command variant. Note, that it behaves differently from file (WRITE ...) variant.
Add custom target (and/or custom command) which implements further usage of expanded value. It's platform dependent and requires user to additionally invoke make (either with some special target, or include to all target), but has advantage, that it's flexible enough because you may implement shell script (but without executable bit).
Example demonstrating solution with combining these options:
set(target_name "myexe")
file(GENERATE OUTPUT script.sh CONTENT "#!/bin/sh\n echo \"${target_name} compile definitions: $<TARGET_PROPERTY:${target_name},COMPILE_DEFINITIONS>\"")
add_custom_target(mycustomtarget
COMMAND echo "\"Platform: $<PLATFORM_ID>\""
COMMAND /bin/sh -s < script.sh
)
After calling CMake build directory will contain file script.sh and invoking make mycustomtarget will print to console:
Platform: Linux
myexe compile definitions: PLATFORM_Linux USING_MY_LIB
Use
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
and get compile_commands.json

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.

CMake SET command not working as expected (CMake not setting value of predefined cached variable)

I'm using CMake in my builds. I'm trying to set compiler flags like this:
set(CMAKE_C_FLAGS_DEBUG "-option1 -option2 -option3")
however, my build fails because one of the compiler flags is not set.
When I check the value of the variable in CMake-GUI is empty:
Can someone point out what is happening?
What you see with cmake-gui is the cache status. You only see there variables that you explicitly cache, or predefined cmake cached variables.
Now, when you do:
set(CMAKE_C_FLAGS_DEBUG "-option1 -option2 -option3")
You are doing something particular: you are setting a "local" (not cached) variable which has the same name of a predefined cmake cached variable. In C++ it would be like defining a local variable with the same name of a global variable: the local variable will hide your global variable.
From the set documentation. (The documentation calls "normal" what I called "local")
Both types can exist at the same time with the same name but different
values. When ${FOO} is evaluated, CMake first looks for a normal
variable 'FOO' in scope and uses it if set. If and only if no normal
variable exists then it falls back to the cache variable 'FOO'.
You are already effectively setting CMAKE_C_FLAGS_DEBUG, and the compiler will use the flags you have specified. You can check it with:
message(STATUS "CMAKE_C_FLAGS_DEBUG = ${CMAKE_C_FLAGS_DEBUG}")
So, your building is failing for another reason. Check which command line make generates: make VERBOSE=1.
By the way, I suggest you append your flags to the predefined ones, by doing:
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -option1 -option2 -option3")
Also consider using the other predefined variable CMAKE_C_FLAGS, in case you want those settings to be propagated to all build types.
Edit:
After some iterations it turned out the problem was that the build type (CMAKE_BUILD_TYPE) was not set from cmake. This can either be done through the cmake-gui interface, or adding something like -DCMAKE_BUILD_TYPE=Debug or -DCMAKE_BUILD_TYPE=Release to the cmake command line.