CMake if(DEFINED MY_COMPILE_DEF) does not work. How can I use precompile definition as conditional? - cmake

I'm trying to use a compile definition as a conditional to build a Gtest executable. The problematic CMake code is as follows:
add_compile_definitions(TEST_BENCH)
if(DEFINED TEST_BENCH)
enable_testing()
endif()
This does not work though. I've read a few similar questions and answers that have been regarding the use of ${my_var} syntax which is not the case in my code.
Can compile definitions be used in conditionals, and if so how?

Use a cmake (cache) variable, which will also allow users to configure your project properly.
set(TEST_BENCH OFF CACHE BOOL "Enables testing of your project")
if(TEST_BENCH)
add_compile_definition(TEST_BENCH)
enable_testing()
endif()
Then the user (and you) can configure your project according to their needs with the help of ccmake or cmake-gui or with cmake -DTEST_BENCH=ON. I believe target_compile_definitions is generally preferred over global add_compile_definitions.
Still, I wouldn't advise it, you can match COMPILE_DEFINITIONS variable that is modified by add_compile_definition with the TEST_BENCH and that way check if the macro is set or not.

In short, add_compile_definitions() is for source files while set() is for CMake variables. if(DEFINED) is to check your CMake or env variables thus you need to use set().
Credit to #squareskittles
https://stackoverflow.com/a/61815468/2324483

Related

Does cmake has an option to avoid using undefined variables (like bash set-u)

Does cmake have a mechanism to generate an error when using a undefined variable, a bit like set -u option in bash.
I have a big project composed of several CMakeLists.txt files, representing ~1500 lines, so it is quite difficult to use this construction: if(NOT DEFINED VAR_NAME)
In a ideal world, the following CMakeLists.txt whould fail.
cmake_minimal_required(VERSION 3.13)
message(STATUS "Will delete ${DIR}/${FILE})
Does such option exist? (I don't think so, but the cmake documentation is huge, I may have missed it)
Is such behavior on the project roadmap?
As mentioned by KamilCuk, there is the --warn-uninitialized flag which you can pass when invoking cmake to configure the project.

Proper way to compile project with debug symbols and cmake

Here is recommended to pass CMAKE_BUILD_TYPE as an argument to cmake when I want to obtain debug or release project builds. I'm trying to compile libharu with cmake and I would like to compile it with debug symbols. I've searched CMakeLists.txt included in libharu for following strings:
CMAKE_BUILD_TYPE
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_DEBUG
but I've found nothing. My question is that if it does make any sense to specify CMAKE_BUILD_TYPE when libharu's CMakeLists.txt doesn't mention it? If not, how can I compile libharu with debug symbols?
PS: I've noticed that project that was generated for Visual Studio 2013 with cmake had set Debug/Win32, is this sufficient? Where in CMakeLists.txt is specified this info?
PPS: I guess this question is highly depending on particular project but is there some way to do this in general? I mean, does CMAKE_BUILD_TYPE=Debug always create Debug build or is there something else that I should be aware of?
Thanks
Setting configuration type via CMAKE_BUILD_TYPE switches set of additional options for compiler to one, which normally reflects meaning of the configuration. That is, passing
-DCMAKE_BUILD_TYPE=Debug
to cmake tells compiler to generate debugging information unless CMakeLists.txt modifies that behavior.
Config-dependent compiler options are contained in variables CMAKE_<LANG>_FLAGS_<CONFIG>. For example, variable CMAKE_C_FLAGS_DEBUG contains additional options for C compiler in "Debug" configuration. These variables are filled by CMake automatically, and CMakeLists.txt itself rare modifies them.
So, if you found that CMakeLists.txt doesn't play with variables CMAKE_BUILD_TYPE and CMAKE_<LANG>_FLAGS_<CONFIG>, then it follows common conventions about configuration type.
This doesn't mean that CMakeLists.txt shouldn't play with that variables.
Often CMakeLists.txt sets CMAKE_BUILD_TYPE to some default value, provided the variable is not set by the user and single-config generator is used. CMakeLists.txt also may set some of variables CMAKE_<LANG>_FLAGS_<CONFIG>, if default setting for compiler is not suited for the project.
But even if CMakeLists.txt does not touch these variables, they work.

Display CMake variables without running CMake on a CMakeLists.txt file or manually inspecting config.cmake?

Suppose I have a package called Foo. If I run CMake on a CMakeLists.txt file that contains find_package(Foo), then I can print out the values of variables such as ${Foo_LIBRARIES} and ${Foo_INCLUDES}.
Is there an easy way to display these variables without having to run CMake on a CMakeLists.txt file, and without having to manually inspect the config.cmake file?
You asked: (1) Is there an easy way to display these variables without having to run cmake on a CMakeLists.txt file, and (2) without having to manually inspect the config.cmake file?
I can give you a yes answer to (2) but it does require that you (re)run cmake. But since you can re-run your cmake configure step by simply executing cmake . in the build directory, re-running cmake should not keep you from trying this approach. My answer is given in this SO answer and uses the get_cmake_property command. Here is that code encapsulated into a cmake macro, print_all_variables, so I can use it when debugging my cmake scripts.
macro(print_all_variables)
message(STATUS "print_all_variables------------------------------------------{")
get_cmake_property(_variableNames VARIABLES)
foreach (_variableName ${_variableNames})
message(STATUS "${_variableName}=${${_variableName}}")
endforeach()
message(STATUS "print_all_variables------------------------------------------}")
endmacro()
The macros are invoked with same syntax as cmake functions:
print_all_variables()
To simply print a value, you could do something like this:
message(STATUS "foo include dir: ${foo_INCLUDE}")
where ${foo_INCLUDE} is the value you desire to print.
Note: I'm using cmake > 3.14
Run CMake and have a look at the cache with the ccmake GUI tool. Then you'll get all the variables.
Or run CMake with -LH then you will get all variables printed after configuration.
So I think it is not possible to get the variables without running CMake.
Run cmake in find-package mode. Example to display a package`s include directories:
cmake -DNAME=ZLIB -DCOMPILER_ID=GNU -DLANGUAGE=C -DMODE=COMPILE --find-package
Example to display the libraries:
cmake -DNAME=ZLIB -DCOMPILER_ID=GNU -DLANGUAGE=C -DMODE=LINK --find-package
The NAME must be set to the package name. You can obtain your COMPILER_ID on this page. LANGUAGE can be C, CXX or Fortran.
I am always suspicious of variables changing values throughout a script somewhere so I like to see the value of a variable at a particular point in the running script. Combining the ideas from both Phil and Aaron B. this is what I'm using:
function(PRINT_VAR VARNAME)
message(STATUS "${VARNAME}: ${${VARNAME}}")
endfunction()
PRINT_VAR("CMAKE_CXX_COMPILER")
Then I can just litter PRINT_VAR statements around like I'm debugging code back in 1980
These variables are generally hardcoded into FindFoo.cmake so that it is not possible to extract them without running the function first. Note that sometimes the value of Foo_LIBRARIES depends on the system configuration, which is unknown until find_package(Foo) is run.

How to get CMake to build a Fortran program with MPI support?

I was trying to parallelize a Fortran program using MPI. I use CMake to do the build of my program. It was difficult to find support on getting CMake to create a working makefile for Fortran with MPI support on google, but from what I could gather, I added the following commands to my CMakeLists.txt script:
find_package(MPI REQUIRED)
add_definitions(${MPI_Fortran_COMPILE_FLAGS})
include_directories(${MPI_Fortran_INCLUDE_DIRS})
link_directories(${MPI_FortranLIBRARY_DIRS})
This will locate MPI on my system and then set the variables found in the following three commands. In my linking line, I added the MPI libraries variable to the list of the other libraries that my program needed to build.
target_link_libraries(${exe_name} otherlibs ${MPI_FortranLIBRARY_DIRS})
Doing cmake and then make worked to build the program and the program ran; however, when I tried to add more to the source which required me to include the mpif.h include file, my compilation failed due to not being able to find this header file. I also could not use mpi because the compiler cannot find the mpi.mod file in the path.
I inserted "message" commands into my CMakeLists.txt file and printed out the values of the variables that I was using for including and linking. It turns out that the variables, MPI_Fortran_INCLUDE_DIRS and MPI_FortranLIBRARY_DIRS weren't set to anything. A check of the module that CMake is actually using to set these variables (FindMPI.cmake) showed these variables to be non-existent. I changed my CMakeLists.txt file to use the correct variables:
find_package(MPI REQUIRED)
add_definitions(${MPI_Fortran_COMPILE_FLAGS})
include_directories(${MPI_Fortran_INCLUDE_PATH})
link_directories(${MPI_Fortran_LIBRARIES})
target_link_libraries(${exe_name} otherlibs ${MPI_Fortran_LIBRARIES})
Now when I execute make, the compiler could find both mpif.h as well as mpi.mod.
UPDATE:
I want to mention that this solution worked for cmake version 2.8.10.1. When I moved my CMakeLists.txt scripts to a different machine that has cmake version 2.8.4, I get the same error about mpi.mod missing during the link stage. I checked the FindMPI.cmake module and, sure enough, there are no variables that specify the language (i.e. there is no MPI_Fortran_LIBRARIES variable, just a MPI_LIBRARIES variable, and this variable is not getting set to the correct location of the mpi library on that system. So this solution will be dependent on cmake version.
Sounds like you are not using the mpi compiler. That is fine, but you have to add a lot of flags then. There is not really an mpi compiler but a wrapper that sets the flags to be able to use mpi. With cmake I was able to do this by defining the fortran compiler I was going to use BEFORE the call to cmake. It's not a nice solution since you loose portability, but it works. I'm trying to find a better solution and define inside cmake what compiler to use, but have not been able to do so. In other words, this works for me:
FC=mpif90 cmake ../.
make
I was having the same problem as you. Hope this solves the issue. If anybody finds how to define the fortran compiler INSIDE cmake please post it!
as you've already noticed, you misspelled the name of two variables, namely MPI_Fortran_LIBRARIES and MPI_Fortran_LIBRARIES
It is useful also to add:
cmake_minimum_required(VERSION 2.8.10)
at the very beginning of your CMake to be sure that these variables will be defined.

CMake basic usage

I'm trying to build my library with CMake. I'm working on CMakeLists.txt .
I want to be able to do the following:
I have a directories called
include
src
Then inside of these there is
Agui folder.
And in that folder are the sub folders of the library.
So far from what I've gathered:
I'll need to do:
set(AGUI_SOURCES
src/Agui/Rectangle.cpp
src/Agui/xxx.cpp (and so on)
)
Then I think need to do:
include_directories(./include)
And then I'm not quite sure.
I know that add_library will be involved but I'm not sure how.
The thing is, I want to create 2 options: DLL, or static.
If it is DLL, then AGUI_BUILD_DLL must be defined.
So based on this information:
Am I on the right track?
How do I create the options
What do I put in add_library
How do I add the preprocessor AGUI_BUILD_DLL if the user wants the DLL version?
If any or all of these could be answered I would greatly appreciate it.
Thanks
I would recommend taking a look at the CMake tutorial which covers adding executables, libraries, system introspection etc. The cmake command also self documents, so on the command line entering,
cmake --help-command add_library
would give you the documentation for the add_library command. You could use the --help-commands to get a full listing of all CMake command documentation. So you can use the option command to add an option for building shared or static, and use the add_definitions command to add a preprocessor definition.
For example,
option(BUILD_SHARED "Build shared libraries" OFF)
if(BUILD_SHARED)
add_definitions(-DAGUI_BUILD_DLL)
add_library(agui SHARED ${AGUI_SOURCES})
else()
add_library(agui STATIC ${AGUI_SOURCES})
endif()
You should note targetName_EXPORTS will be defined when building the library, and so can be used in the declspec logic. This is done by CMake, and is also useful when using visibility support in GCC on Unix systems.
Maybe , do not know :)
OPTION(AGUI_BUILD_DLL "Build agui DLL" OFF)
somewhere near the start of CMakeLists.txt. You may want to use ON instead of OFF - depends on whether you want DLL build to de default.
3,4.
if(AGUI_BUILD_DLL)
add_library(agui SHARED ${AGUI_SOURCES})
set_target_properties(agui PROPERTIES DEFINE_SYMBOL "AGUI_BUILD_DLL")
else()
add_library(agui STATIC ${AGUI_SOURCES})
endif()