How to use CHECK_INCLUDE_FILES macro in cmake? - cmake

I need link my program against Kerberos authentication library (gssapi_krb5) with the corresponding headers gssapi/gssapi.h and gssapi/gssapi_krb5.h included in the source file.
Currently, the compilation will continue if headers are absent and stop with a compile time error saying header files not found.
What I want to implement in the cmake file is to check the existence of the header file and stop compiling if not found.
I add the following code into my CMakeList.txt file.
INCLUDE(CheckIncludeFiles)
CHECK_INCLUDE_FILES(gssapi/gssapi.h;gssapi/gssapi_krb5.h HAVE_KRB_HEADERS)
IF (NOT HAVE_KRB_HEADERS)
RETURN()
ENDIF (NOT HAVE_KRB_HEADERS)
But it still does not act as I expected.
I would like the following lines:
-- Looking for gssapi/gssapi.h - found
-- Looking for gssapi/gssapi_krb5.h - not found
but fail.
Also, the variable HAVE_KRB_HEADERS is empty when output with message macro.
Compile continues until the error described above occurs.
I read somewhere on the Web, this may be because CMake cache.
I'm very new to CMake and not quite clear with that concept.
My CMake version is 2.6.
How could I make this code work? Thank you!

I can't say I'm a huge fan of CheckIncludeFiles because of its difficulty to get right. In principal it's good - it actually creates tiny c files which #include the requested headers and tries to compile them, but it seems to be too easy to get wrong.
I generally prefer just using find_path and/or find_file for this job. This doesn't check the contents of any files found, but usually if you find the required header, its contents are good!
I would use find_path if I needed to know the folder where the header lived. This would usually be because I need to check for other files in the same folder (as in your case), or more commonly because I need to add the folder to an include_directories call.
find_file yields the full path to the file (if found). For headers, normally I don't need the path elsewhere in the CMakeLists - it's just used immediately after the find_file to check the file was actually found.
So, here's how I'd go about checking for "gssapi/gssapi.h" and "gssapi/gssapi_krb5.h"
find_path(GssApiIncludes gssapi.h PATHS <list of folders you'd expect to find it in>)
if(NOT GssApiIncludes)
message(FATAL_ERROR "Can't find folder containing gssapi.h")
endif()
find_file(GssKrb gssapi_krb5.h PATHS ${GssApiIncludes} NO_DEFAULT_PATH)
if(NOT GssKrb)
message(FATAL_ERROR "Can't find gssapi_krb5.h in ${GssApiIncludes}")
endif()
If you do this, then if required you could add
include_directories(${GssApiIncludes})
so that in your source code you can do
#include "gssapi.h"
#include "gssapi_krb5.h"

For anyone who has to work with CHECK_INCLUDE_FILES, the documentation lists a variable called CMAKE_REQUIRED_INCLUDES where you can set additional include paths apart from the default headers.
In a CMake file:
LIST(APPEND CMAKE_REQUIRED_INCLUDES "gssapi")
From the command line:
cmake . --DCMAKE_REQUIRED_INCLUDES="gssapi"
If all else fails, you can set the -I<dir> flag manually. However, this is not recommended as it not portable across compilers.
# note the extra space before `-I`
STRING(APPEND CMAKE_C_FLAGS " -Igssapi")
STRING(APPEND CMAKE_CXX_FLAGS " -Igssapi") # for C++
Also note that C++ headers have a different macro called CheckIncludeFileCXX.

Related

CMake: show all modified variables

I would like to have a command or option to list all the modified cache variables of the current build configuration. While cmake -L[AH] is nice, it is also quite overwhelming and doesn't show which are non-default values.
There seems to be a variable property MODIFIED that sounds exactly like what I'm looking for - but the documentation is not very reassuring:
Internal management property. Do not set or get.
This is an internal cache entry property managed by CMake to track interactive user modification of entries. Ignore it.
This question also didn't help: CMAKE: Print out all accessible variables in a script
There are so many ways you could change or initialize variables in CMake (command line, environment variables, script files, etc.) that you won't be able to cover them all.
I just came up with the following script that covers the command line switches. Put the following file in your CMake project's root folder and you get the modified variables printed:
PreLoad.cmake
set(_file "${CMAKE_BINARY_DIR}/UserModifiedVars.txt")
get_directory_property(_vars CACHE_VARIABLES)
list(FIND _vars "CMAKE_BACKWARDS_COMPATIBILITY" _idx)
if (_idx EQUAL -1)
list(REMOVE_ITEM _vars "CMAKE_COMMAND" "CMAKE_CPACK_COMMAND" "CMAKE_CTEST_COMMAND" "CMAKE_ROOT")
file(WRITE "${_file}" "${_vars}")
else()
file(READ "${_file}" _vars)
endif()
foreach(_var IN LISTS _vars)
message(STATUS "User modified ${_var} = ${${_var}}")
endforeach()
This will load before anything else and therefore can relatively easily identify the user modified variables and store them into a file for later reference.
The CMAKE_BACKWARDS_COMPATIBILITY is a cached variable set by CMake at the end of a configuration run and therefor is used here to identify an already configured CMake project.
Reference
CMake: In which Order are Files parsed (Cache, Toolchain, …)?

Can we know the directory where the macro or functions are located in cmake

In CMAKE, it defines the following variables to indicate the directories of files:
CMAKE_CURRENT_LIST_DIR
CMAKE_CURRENT_BINARY_DIR
CMAKE_CURRENT_SOURCE_DIR
They are useful when you process CMake scripts. However, none of them can tell you the directory where MACROs or functions are defined. Give the following example CMakeLists.txt to illustrate my question
project(Hello)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/my_macro.cmake)
test_macro()
Then for the my_macro.cmake, we have definitions for test_macro():
macro(test_macro)
message("hello")
#?? Can we know the folder of this macro is located?
#?? the macro definition file's location
endmacro()
I don't think there's an off-the-shelf variable for that, but you could easily make your own:
my_macro.cmake:
set(test_macro__internal_dir ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "")
macro(test_macro)
message(STATUS "Defined in: ${test_macro__internal_dir}")
endmacro()
The set() line will be processed when the file is included, and the value of CMAKE_CURRENT_LIST_DIR from that processing cached for future use inside the macro.
With CMake 3.17 there is CMAKE_CURRENT_FUNCTION_LIST_DIR that can be used in functions, see https://cmake.org/cmake/help/v3.17/variable/CMAKE_CURRENT_FUNCTION_LIST_DIR.html
Unfortunately, there is no such thing for macros yet.

CMake to find path to a file which its name contains a certain string

So I am writing a CMake module to find some libraries which is being used a lot in our company. These libraries all have different versions and are such a mess here. In a certain library, even the name of header files and binary files don't match. So I am writing a CMake script to handle all the problems of finding each library once and for all. What I have in my mind is to write something like how we find boost packages but include the version of each component as well. Something like this:
find_package(OrgName COMPONENTS librarya-1.4.3 libraryb-2.3.1 libraryc-3.6.0)
So I created a FindOrgName.cmake file and iterated on the requested components, then I processed the string which is passed and gained the library name along with its version information something like this (never mind the difference between include and binary files):
IF(OrgName_FIND_COMPONENTS)
FOREACH(comp ${OrgName_FIND_COMPONENTS})
SET(OrgName_${comp}_FOUND 0)
STRING(FIND ${comp} "-" dashind REVERSE)
STRING(LENGTH ${comp} length)
STRING(SUBSTRING ${comp} 0 ${dashind} name)
MATH(EXPR s "${dashind}+1")
MATH(EXPR l "${length}-${dashind}-1")
STRING(SUBSTRING ${comp} ${s} ${l} version)
SET(OrgName_${name}_INCLUDE_DIR "/usr/local/include/OrgName/${comp}/")
find_library(OrgName_${comp}_LIBRARIES NAMES "${comp}" HINTS "/usr/lib")
IF(OrgName_${comp}_INCLUDE_DIR AND OrgName_${comp}_LIBRARIES)
SET(OrgName_${comp}_FOUND 1)
ENDIF()
IF(NOT OrgName_${comp}_FOUND AND OrgName_FIND_REQUIRED_${comp})
MESSAGE(FATAL_ERROR "OrgName ${comp} not available.")
ENDIF()
SET (OrgName_INCLUDE_DIR ${OrgName_INCLUDE_DIR} ${OrgName_${comp}_INCLUDE_DIR})
SET (OrgName_LIBRARIES ${OrgName_LIBRARIES} ${OrgName_${comp}_LIBRARIES})
ENDFOREACH()
ENDIF()
Now here is the problem, imagine someone didn't enter the version part in components names. I want to search for the versions which he has installed (assume the path to library is always the same) and use the last version it can find, so I have to search for the files which their name contains the library name (${name}). First of all how can I do this? Second, Am I doing things right? I mean is there an easier way to do this task?
OK, after a lot of searching and getting exhausted I finally found a solution. I just need to use the following command:
file(GLOB files "Mylib*")
Which will create a list named files and adds each file that its name matches the pattern "Mylib*" to it. I really don't know why they didn't implement it in find_file command but never mind, I'll put it here in case anyone got confused like me.

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.

how to get visual c++ default include path using cmake?

I try to use cmake determine whether exist inttypes.h header file for generate project of visual c++ 11.
Initially, i wrote the following sentence in CMakeLists.txt
FIND_FILE(HAVE_INTTYPES_H "inttypes.h" DOC "Does the inttypes.h exist?")
Unfortunately, the HAVE_INTTYPES_H variable is HAVE_INTTYPES_H-NOTFOUND.
Afterwards, i looked up cmake documentation about find_file, which mentioned the need to some search path. But i can not get the c standard header files in any place in cmake?
Thanks.
Your find_file call is correct. The problem is there's no inttypes.h on Visual Studio. So keep your test the same, but when it's not found include another headers, for instance provided by: http://code.google.com/p/msinttypes/
Something like:
FIND_FILE(HAVE_INTTYPES_H "inttypes.h" DOC "Does the inttypes.h exist?")
if (HAVE_INTTYPES_H)
add_definitions(-DHAVE_INTTYPES_H=1)
endif()
and in your code:
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#else
#include "path/to/inttypes.h"
#endif
Now, to detect headers, you may also want to try using the CheckIncludeFile standard CMake module, it's trying to detect include files using your target compiler instead of searching the file system:
include(CheckIncludeFile)
check_include_file("stdint.h" STDINT_H_FOUND)
if (STDINT_H_FOUND)
add_definitions(-DHAVE_STDINT_H=1)
endif()