How to abort processing CMakeLists for current directory - cmake

I have a project structure like:
src/CMakeLists.txt
src/test/component1/CMakeLists.txt
src/test/component2/CMakeLists.txt
For the testing, I'm using Qt - however, I want to make sure that if Qt (or some other test-specific package is not found) I simply skip the package.
I tried
find_package(Qt4 QUIET COMPONENTS QtCore QtTestLib)
if (NOT QT4_FOUND)
message(SEND_ERROR "Qt4 not found - skipping building tests")
endif (NOT QT4_FOUND)
but that doesn't work like I want to since that still prevents the generation of the Makefiles. The only way I can think is to put the entire body of the CMakeLists file into the body of the conditional.
Is there a way to say "skip processing the remainder of this CMakeLists"?

From the CMake documentation
return: Return from a directory or function.
return()
Returns from a directory or function.
When this command is encountered, it
caused process of the current function
or directory to stop and control is
return to the caller of the function,
or the parent directory if any. Note
that a macro is not a function and
does not handle return like a function
does.

Related

Successful build of Kicad 4.0.6 in Linux Mageia 5 via fixing a wx-3.0 symbol

I have managed to build the Kicad 4.0.6 in Linux Mageia 5.1 with gcc version 4.9.2. I first manually fixed two wxWidgets 3.0.2 header files in the /usr/include/wx-3.0/wx/ directory: regex.h and features.h. Kicad then compiled successfully. With the native wx-3.0 headers, the compiler generated the error in pcbnew/netlist_reader.cpp due to the undefined variable wxRE_ADVANCED.
The features.h header checks if the macro WX_NO_REGEX_ADVANCED is defined. If yes, features.h UNdefines wxHAS_REGEX_ADVANCED macro, and defines it, if no. The macro wxHAS_REGEX_ADVANCED, in turn, is used in regex.h to determine if among the enum constants wxRE_ADVANCED = 1 is present. The standard prebuilt Mageia 5 packages wxgtku3.0_0 and lib64wxgtku3.0-devel that I installed with the use of Mageia's software manager urpmi from Mageia repository WX_NO_REGEX_ADVANCED is defined, therefore wxHAS_REGEX_ADVANCED is undefined, and, hence, wxRE_ADVANCED is undefined either. Kicad 4.0.6 source package assumes wxRE_ADVANCED = 1, therefore the build process stops with the error.
Then I reverted /usr/include/wx-3.0/wx/regex.h and features.h to their original state and learned how to add the definition of wxRE_ADVANCED to CMakeLists.txt. However, I still have a question.
The recommended format of adding the definition to CMakeLists.txt I found at CMake command line for C++ #define is this:
if (NOT DEFINED wxRE_ADVANCED)
set(wxRE_ADVANCED 1)
endif()
add_definitions(-DwxRE_ADVANCED=$(wxRE_ADVANCED))
However, it did not work! The macro expansion for wxRE_ADVANCED in pcbnew/netlist_reader.cpp was empty. I printed it at compile time inserting the following lines into the netlist_reader.cpp file (this was hard to find, most of the recommended formats did not work. The correct one is in C preprocessor: expand macro in a #warning):
#define __STRINGIFY(TEXT) #TEXT
#define __WARNING(TEXT) __STRINGIFY(GCC warning TEXT)
#define WARNING(VALUE) __WARNING(__STRINGIFY(wxRE_ADVANCED = VALUE))
Pragma (WARNING(wxRE_ADVANCED))
Finally, I simplified the CMakeLists.txt definition down to this, and it was a success:
if (NOT DEFINED wxRE_ADVANCED)
set(wxRE_ADVANCED 1)
endif()
add_definitions(-DwxRE_ADVANCED=1)
My question: what is the meaning of "-DwxRE_ADVANCED=$(wxRE_ADVANCED)" if it does not work? Is it possible not to use set(wxRE_ADVANCED 1), and simply write add_definitions(-DwxRE_ADVANCED=1)? Thank you.
P.S. Yes, the Kicad 4.0.6 build process successfully finished with only one line added to the top level CMakeLists.txt file:
add_definitions(-DwxRE_ADVANCED=1)
A variable is called via $variable or ${variable}. Note the curly brackets, not parentheses.
Also, it is recommended to use:
target_compile_definitions(mytarget PUBLIC wxRE_ADVANCED=1)
on a target directly, rather than the general add_definitions() command.

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

CMake: Function is callable while script, defined it, is no longer included

I have three folders in a location, say A,B and C. I have two cmake files in folder A: FindABC.cmake and UseABC.cmake. The former is for finding the libraries and the latter contains a function, say run_command(). CMakelists.txt in folder B and folder C contains the following lines:
find_package(ABC)
include(UseABC)
run_command()
It works as intended. Now If I comment find_package() and include() in CMakelists of folder C, as far as I know, Cmake should give an error telling unknown command - run_command(). But, the controls goes into the function and executes in unpredictable manner.
How come the control goes to the function when the include line is commented? The root CMakelists that lists the sub-directories does not have any find_package or include lines in it.
Edit:
UseABC.cmake:
set(ABC_COMPILE_DEBUG FALSE)
set(ABC_COMPILE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/abc_gen")
message("USEABC1 - -> " ${ABC_COMPILE_OUTPUT_DIR})
function(run_command)
message("USEABC2 - File recurs -> " ${ABC_COMPILE_OUTPUT_DIR})
file(REMOVE_RECURSE "${ABC_COMPILE_OUTPUT_DIR}")
file(MAKE_DIRECTORY "${ABC_COMPILE_OUTPUT_DIR}")
add_custom_command() #command to be executed
endfunction()
Here, When nothing is commented(find_package and include is not commented in any CMakelists.txt), I get the correct path for the two messages I print.
When I comment include(UseABC) in the second CMakelists.txt, the configuration fails, the first message is not at all printed and the second message gets printed, but does not give the value of the variable. It also deletes all the files in Folder C (but the argument to REMOVE_RECURSE is empty).
If I correctly understand the situation, you have:
CMakeLists.txt:
add_subdirectory(B)
add_subdirectory(C)
B/CMakeLists.txt:
find_package(ABC)
include(UseABC)
In that case run_command function, defined in UseABC.cmake, is accessible in C/CMakeLists.txt, though this script doesn't define it.
In CMake function definitions are global.
By opposite, variable definitions are local to the scope, where they are defined. (Until variables are cached ones, in that case they have global visibility).
That is, variable ABC_COMPILE_DEBUG defined in UseABC.cmake is accessible in
UseABC.cmake script
B/CMakeLists.txt script, because it includes UseABC.cmake one, and include() command doesn't introduce a scope
but it is inaccessible in
CMakeLists.txt script, because add_subdirectory(B) does introduce a scope
C/CMakeLists.txt script
More details about variable's visibility can be found in documentation.

check_library_exists not works as it should

In CMakeLists.txt I write:
INCLUDE(CheckLibraryExists)
check_library_exists("libcurl" "" "" HAVE_CURL)
HAVE_CURL is always false, even if libcurl installed, and this function not causes fatal errors.
To check why a a try-compile fails, you can run CMake with the --debug-trycompile option, which will leave behind the buildsystem for the last try_compile command (these are used internally by all the Check... modules).
I didn't run the check, but I looked at the code of CheckLibraryExists, and it is apparently mandatory to specify a function to look for in that library (the second argument to check_library_exists).

How to use CHECK_INCLUDE_FILES macro in 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.