Using build type in CMake 3.x - cmake

In the 2.x version of CMake, the cmake option CMAKE_BUILD_TYPE controlled options to be passed to the compiler. For example, if CMAKE_BUILD_TYPE=RelWithDebInfo then the options passed to the compiler was CMAKE_CXX_FLAGS_RELWITHDEBINFO for C++ and CMAKE_C_FLAGS_RELWITHDEBINFO for C (http://binglongx.wordpress.com/tag/cmake_build_type/).
In the new 3.x version of CMake there are commands add_compile_options and target_compiler_options for adding options to the compiler (What is the modern method for setting general compile flags in CMake?).
Questions
How do I define which build type CMake should use? Is it still, for example, cmake -DCMAKE_BUILD_TYPE=Debug?
How do I make use of this build type in my CMakeLists.txt?
Has CMake defined all the build types, or can I define custom, additional, build types?
Has CMake set any default compiler options for each of the build types (for example -g for the Debug build type)?

Please try to stick to a single question per question in the future. You might risk getting closed as too broad otherwise. Also, you are more likely to get good answers for well-defined, precise questions.
How do I define which build type CMake should use? Is it still, for
example, cmake -DCMAKE_BUILD_TYPE=Debug?
Yes, this did not change.
How do I make use of this build type in my CMakeLists.txt?
Use generator expressions. For example:
target_compile_options(my_program PUBLIC $<$<CONFIG:Debug>:-Werror>)
# Warnings are errors for debug build only
Has CMake defined all the build types, or can I define custom,
additional, build types?
You can add your own type if the defaults don't cut it for you. Note though that this can be a bit fiddly, so I wouldn't do it unless you have good reasons. Take a look at CMAKE_CONFIGURATION_TYPES.
Has CMake set any default compiler options for each of the build types
(for example -g for the Debug build type)?
CMake does a pretty good job at choosing the right defaults for the different configurations. In particular, Debug and RelWithDebInfo builds generate symbols correctly (-g on gcc) and Release builds are optimized quite well.

Related

CMake compile options for compile test only

I am using CMake to cross compile a C project for an embedded (heterogeneous) multi-core system. The compiler takes an mandatory argument (-t<type>, the target type). This flag has to be set to pass CMake's compiler test. I am adding this flag in a toolchain file as follows:
add_compile_options(-tMYPLATFORMTYPE)
The problem with this approach is, all project files will be compiled with this flag. Is there a way to configure compile flags for the test compilation only, without affecting the main project configuration? (Note: Within the project different files shall have different values for this flag.)
What I am looking for is something like:
set(CMAKE_TRY_COMPILE_COMPILE_OPTIONS "-tMYPLATFORMTYPE")
I could disabled the compile test, but I would prefer to keep it.
You can check the IN_TRY_COMPILE property and set the flag for try-compile configurations only:
get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
if(IS_IN_TRY_COMPILE)
add_compile_options(-tMYPLATFORMTYPE)
endif()

How to specify a compiler in CMake?

I would like to use the IAR compiler. I noticed CMake has already have a bunch of files about this compiler:
https://github.com/jevinskie/cmake/blob/master/Modules/Compiler/IAR.cmake
From what I read the common solution is to specify manually ALL the toolchain in my CMakeLists.txt:
set(CMAKE_C_COMPILER iccarm)
set(CMAKE_CPP_COMPILER iccarm)
How CMake can link these definitions with `Modules/Compiler/IAR.cmake"?
I thought I would just have to do
include("Modules/Compiler/IAR.cmake")
What is the correct way to specify my IAR compiler?
When I do
cmake .
It still tries to use gcc instead of my IAR compiler. Why?
To select a specific compiler, you have several solutions, as exaplained in CMake wiki:
Method 1: use environment variables
For C and C++, set the CC and CXX environment variables. This method is not guaranteed to work for all generators. (Specifically, if you are trying to set Xcode's GCC_VERSION, this method confuses Xcode.)
For example:
CC=gcc-4.2 CXX=/usr/bin/g++-4.2 cmake -G "Your Generator" path/to/your/source
Method 2: use cmake -D
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path on the command-line using cmake -D.
For example:
cmake -G "Your Generator" -D CMAKE_C_COMPILER=gcc-4.2 -D CMAKE_CXX_COMPILER=g++-4.2 path/to/your/source
Method 3 (avoid): use set()
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path in a list file using set(). This must be done before any language is set (ie: before any project() or enable_language() command).
For example:
set(CMAKE_C_COMPILER "gcc-4.2")
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.2")
project("YourProjectName")
The wiki doesn't provide reason why 3rd method should be avoided...
I see more and more people who set CMAKE_C_COMPILER and other compiler-related variables in the CMakeLists.txt after the project call and wonder why this approach breaks sometimes.
What happens actually
When CMake executes the project() call, it looks for a default compiler executable and determines the way for use it: default compiler flags, default linker flags, compile features, etc.
And CMake stores path to that default compiler executable in the CMAKE_C_COMPILER variable.
When one sets CMAKE_C_COMPILER variable after the project() call, this only changes the compiler executable: default flags, features all remains set for the default compiler.
AS RESULT: When the project is built, a build system calls the project-specified compiler executable but with parameters suitable for the default compiler.
As one could guess, this approach would work only when one replaces a default compiler with a highly compatible one. E.g. replacement of gcc with clang could work sometimes.
This approach will never work for replacement of cl compiler (used in Visual Studio) with gcc one. Nor this will work when replacing a native compiler with a cross-compiler.
What to do
Never set a compiler in CMakeLists.txt.
If you want, e.g., to use clang instead of defaulted gcc, then either:
Pass -DCMAKE_C_COMPILER=<compiler> to cmake when configure the project. That way CMake will use this compiler instead of default one and on the project() call it will adjust all flags for the specified compiler.
Set CC environment variable (CXX for C++ compiler). CMake checks this variable when selects a default compiler.
(Only in rare cases) Set CMAKE_C_COMPILER variable before the project() call. This approach is similar to the first one, but makes the project less flexible.
If the ways above do not work
If on setting CMAKE_C_COMPILER in the command line CMake errors that a compiler cannot "compile a simple project", then something wrong in your environment.. or you specify a compiler incompatible for chosen generator or platform.
Examples:
Visual Studio generators work with cl compiler but cannot work with gcc.
A MinGW compiler usually requires MinGW Makefiles generator.
Incompatible generator cannot be fixed in CMakeLists.txt. One need to pass the proper -G option to the cmake executable (or select the proper generator in CMake GUI).
Cross-compiling
Cross-compiling usually requires setting CMAKE_SYSTEM_NAME variable, and this setting should normally be done in the toolchain file. That toolchain file is also responsible for set a compiler.
Setting CMAKE_SYSTEM_NAME in the CMakeLists.txt is almost always an error.
You need to create a toolchain file, and use the CmakeForceCompiler module.
Here is an example toolchain file for bare-metal ARM development with IAR:
include(CMakeForceCompiler)
set(CMAKE_SYSTEM_NAME Generic) # Or name of your OS if you have one
set(CMAKE_SYSTEM_PROCESSOR arm) # Or whatever
set(CMAKE_CROSSCOMPILING 1)
set(CMAKE_C_COMPILER iccarm) # Change the arm suffix if appropriate
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # Required to make the previous line work for a target that requires a custom linker file
The last line is necessary because CMake will try to compile a test program with the compiler to make sure it works and to get some version information from preprocessor defines. Without this line, CMake will use add_executable() for the test program, and you will get the error "The C compiler "XXX" is not able to compile a simple test program." This is because the test program fails to link, as it doesn't have your custom linker file (I'm assuming bare-metal development since this is what IAR is usually used for). This line tells CMake to use add_library() instead, which makes the test succeed without the linker file. Source of this workaround: this CMake mailing list post.
Then, assuming that your toolchain file is named iar-toolchain.cmake, invoke CMake like this:
cmake -DCMAKE_TOOLCHAIN_FILE=iar-toolchain.cmake .
You can call cmake like this:
cmake -DCMAKE_C_COMPILER=iccarm ...
or
cmake -DCMAKE_CXX_COMPILER=...
If you don't want to use your PC's standard compiler, you have to give CMake the path to the compiler. You do this via environment variables, a toolchain file or direct definitions in the CMake command line (see e.g. CMake Error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found).
Putting the compiler's name/path into your CMakeLists.txt would stop your project from being cross-platform.
CMake does check for the compiler ids by compiling special C/C++ files. So no need to manually include from Module/Compiler or Module/Platform.
This will be automatically done by CMake based on its compiler and platform checks.
References
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
CMake GitLab Commit: Add support files for C, C++ and ASM for the IAR toolchain.
IAR Systems recently published a basic CMake tutorial with examples under their GitHub profile.
I like the the idea of a generic toolchain file which works seamlessly for both Windows and Linux compilers using find_program().
The following snippet will be used for when using C and can be used similarly for CXX:
# IAR C Compiler
find_program(CMAKE_C_COMPILER
NAMES icc${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{ProgramFiles}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
For ASM, I initially got puzzled with the NAMES but then I realized that the toolchain file was made that way for working with old Assemblers shipped with XLINK:
find_program(CMAKE_ASM_COMPILER
NAMES iasm${CMAKE_SYSTEM_PROCESSOR} a${CMAKE_SYSTEM_PROCESSOR}
PATHS ${TOOLKIT}
"$ENV{PROGRAMFILES}/IAR Systems/*"
"$ENV{ProgramFiles\(x86\)}/IAR Systems/*"
/opt/iarsystems/bx${CMAKE_SYSTEM_PROCESSOR}
PATH_SUFFIXES bin ${CMAKE_SYSTEM_PROCESSOR}/bin
REQUIRED )
Also, take a look at the full toolchain file. It will work automatically for "Arm" when the tools are installed on their default locations, otherwise it is just about updating the TOOLKIT variable and the compilers for all the supported languages should adjust automatically.
If your wanting to specify a compiler in cmake then just do ...
cmake_minimum_required(VERSION 3.22)
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")
Options 1 is only used if you want to specify what compiler you want to use as default for everything that you might compile on your computer. And I don't even think it would work on windows.
Option 2 would be used if you only want to use a different temporarily.
Option 3 is used if that's the compiler that should be used for that particular project. Also option 3 would be the most cross compatible.

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.

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.

How to apply different compiler options for different compilers in cmake?

I'm currently working on using cmake to build some projects, with the main platforms being Visual C++, MinGW GCC and Linux GCC. When building with GCC, I need to specify the -Wno-invalid-offsetof compiler option.
My current fix is as follows...
if ( "${CMAKE_GENERATOR}" MATCHES "^Visual Studio"
OR "${CMAKE_GENERATOR}" MATCHES "^NMake"
)
set (CPPLIB_COMPILER_OPTS "")
else ()
set (CPPLIB_COMPILER_OPTS "-Wno-invalid-offsetof")
endif ()
...
set_target_properties(sh_core PROPERTIES COMPILE_FLAGS "${CPPLIB_COMPILER_OPTS}")
# repeated for all targets
This works, but assuming that all generators other than the visual studio ones will build with gcc is obviously unsafe. For a start, there are IIRC generators for Borland compilers. More importantly, using make doesn't always mean using gcc.
Other compilers I'm likely to use are llvm-gcc and clang. Fortunately, I think even clang supports gcc-compatible options. But this logic is only good for as long as the relevant code is never released.
Cmake appears to check for available compilers and generate a makefile specifically for that compiler (raising the question - why not at least have the option of building the project directly, without the need for a middle-man like make?).
That being the case, I was hoping to be able to test directly for gcc in my CMakeLists.txt files. So far, though, I can't find an appropriate variable to test or any other obvious solution.
Is this possible?
To create a portable build system, it is best to not test for platforms, but to test for features.
Instead of testing "if Windows then do this", test for "if the -Wno-invalid-offsetof flag works then use it". You can do that with the CheckCCompilerFlag module, for example:
include(CheckCCompilerFlag)
check_c_compiler_flag(-Wno-invalid-offsetof HAS_NO_INVALID_OFFSETOF)
if (HAS_NO_INVALID_OFFSETOF)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-invalid-offsetof")
endif()
For C++ there is a similar CheckCXXCompilerFlag with a check_cxx_compiler_flag(flag var) command.