CMake toolchain file - setting CMAKE_CXX_FLAGS - c++

I have seen the following way of setting CMAKE_CXX_FLAGS in the toolchain file:
SET(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags" FORCE)
Should I use it in the toolchain file instead of
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
?
What are differences between them ?

tl;dr: there are two acceptable ways of doing this.
First, and most of the time (90%+), you can use the _INIT variables as suggested by the documentation:
set(CMAKE_CXX_FLAGS_INIT "-m32")
Second, if CMake is adding incorrect/conflicting flags for your compiler/platform combination, you can override it completely by setting the cache variable without FORCE.
set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags")
Read on for further details.
Let's run a few experiments. We'll use the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.23)
project(test LANGUAGES CXX)
message(STATUS "CMAKE_CXX_FLAGS_DEBUG = ${CMAKE_CXX_FLAGS_DEBUG}")
On most systems, CMake leaves CMAKE_CXX_FLAGS blank by default. The main exception is Windows with MSVC, where it adds /EHsc and (on older versions) /GR to ensure that standard C++ exception handling and RTTI are enabled.
Since I don't have ready access to a Windows system, I use CMAKE_CXX_FLAGS_DEBUG, which does have default-initialized flags on most compilers. The same principles apply, though, since it is the responsibility of the platform module to set these in both cases.
Experiment 1: No toolchain file
$ cmake -S . -B build
-- The CXX compiler identification is GNU 11.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- CMAKE_CXX_FLAGS_DEBUG = -g
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
So on this compiler, CMAKE_CXX_FLAGS_DEBUG is set to -g. This is our baseline.
Experiment 2: Set-cache with force
Now we'll create a toolchain file called set-cache-force.cmake:
# set-cache-force.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags" FORCE)
We'll configure the project with this toolchain:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
As we can see, the original -g flag was suppressed and the -DMY_DEBUG cache value "won". Of course, this isn't really a debug mode anymore, which should illustrate why overriding all the flags isn't always what we want.
Even worse, using FORCE here disables a user's ability to override CMAKE_CXX_FLAGS_DEBUG themselves:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
This is highly undesirable behavior. A user would need to edit your toolchain file to work around a bug or add further customizations.
Experiment 3: Set-cache without force
If we run the same experiment as before without FORCE setting it, then we still get the same flags, but we retain the ability to incrementally override the toolchain file.
# set-cache.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags")
Now we can see that it works:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
And that it can still be overridden:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
And it can even be overridden again:
$ cmake -S . -B build -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE2"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE2
...
Experiment 4: Set normal variable
Now we'll try to set this as a normal variable. Again, we'll create a toolchain file called set-normal.cmake:
# set-normal.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG")
Again, running this shows that -DMY_DEBUG "wins", overriding CMake's default flags:
$ cmake -S . -B build --toolchain set-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Like experiment 2, this prevents users from overriding it... bad!
$ cmake -S . -B build --toolchain set-normal.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Experiment 5: Append normal variable
Now we'll try with the code in your post. Again, we'll use a toolchain called append-normal.cmake:
# append-normal.cmake
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DMY_DEBUG")
Now we get a very different result:
$ rm -rf build
$ cmake -S . -B build --toolchain append-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -DMY_DEBUG
...
This is just completely wrong! What happened here? Well, the toolchain file gets read multiple times during project initialization, and here this causes the -DMY_DEBUG flag to be appended twice. At least that's what happens on the first run:
$ cmake -S . -B build
...
-- CMAKE_CXX_FLAGS_DEBUG = -g -DMY_DEBUG
...
After the first run, the CMake default gets cached and so we append to that on subsequent runs. Furthermore, CMake only reads your toolchain file once now.
You must always make your toolchain files idempotent. That means that running it twice does the same thing as running it once.
Experiment 6: Using _INIT variables
This is the developer-intended way of doing things per the documentation. See the documentation here: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_INIT.html
Value used to initialize the CMAKE_<LANG>_FLAGS cache entry the first time a build tree is configured for language <LANG>. This variable is meant to be set by a toolchain file. CMake may prepend or append content to the value based on the environment and target platform.
Now we use a toolchain file called init-var.cmake:
# init-var.cmake
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-DMY_DEBUG")
And we re-run the build:
$ rm -rf build
$ cmake -S . -B build --toolchain init-var.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -g
...
Now we can see that CMake appended its default flags to the initial ones we provided. And indeed this still allows users to override things:
$ cmake -S . -B build --toolchain init-var.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
In my experience, 90%+ of the time it's correct to let CMake add in its extra flags using Experiment 6 (the _INIT variables). But every so often you'll want to completely override CMake using Experiment 3 (set(CACHE) without FORCE).
What you do not want to do is anything that behaves differently on subsequent runs (like experiment 5) or that disables key CMake functionality (ie. respecting the cache variable, like experiments 2 and 4).

When you use set(variable "value" CACHE STRING "..." FORCE), the variable is set for all the projects built in the current session (including those that are in the sub-directories).
But simply using set(variable "value") without the cache part only adds the flags for the immediate project scope (current CMakeLists.txt) and not the upper directories that have their own CMakeLists.txt.

Related

How to integrate clang-tidy to CMake and GCC?

I want to integrate clang-tidy to our C and C++, CMake based project which is compiled using a custom GCC toolchain.
I've tried following this tutorial, setting CMAKE_CXX_CLANG_TIDY. I've also tried generating a compilation database by setting CMAKE_EXPORT_COMPILE_COMMANDS to ON and pointing run-clang-tidy.py to its directory.
In both cases, I've encountered (the same) few errors that are probably related to differences between Clang and GCC:
Some warning flags that are enabled in the CMake files are not supported in Clang but are supported in GCC (like -Wlogical-op). As the compiler is GCC, the file builds correctly, and the flag is written to the compilation database, but clang-tidy complains about it.
clang-tidy complains some defines and functions are unavailable, even though the code compiles just fine. As an example, the android-cloexec-open check suggested using O_CLOEXEC to improve security and force the closing of files, but trying to use this define leads to an undefined identifier error (even though our GCC compiles the code).
As an example to a function that is not found, there is clock_gettime.
Our code compiles with the C11 standard and C++14 standard, without GNU extensions:
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_CXX_EXTENSIONS OFF)
The custom toolchain is a cross-compilation toolchain which runs on Linux and compiles to FreeBSD.
Is there a way to disable the passing of some flags by CMake to clang-tidy? Am I using clang-tidy wrong?
I suspect this issue is related to disabling GNU extensions, using a cross-compilation toolchain, and some feature-test-macro which is not defined by default in Clang but is defined with GCC (e.g. _GNU_SOURCE/_POSIX_SOURCE). If this is the case, how can I check it? If not, should I use clang-tidy differently?
EDIT
As #pablo285 asked, here are 2 warnings I get for a single file, and then as I added --warnings-as-errors=*, the build stops:
error: unknown warning option '-Wlogical-op' ; did you mean '-Wlong-long'? [clang-diagnostic-error]
<file path>: error: use of undeclared identifier 'O_CLOEXEC' [clang-diagnostic-error]
O_WRONLY | O_CLOEXEC
^
I decided to write a python script that will replace clang-tidy, receive the commandline from CMake and edit it to fix various errors. Here are the modification to the commandline I tried:
Remove none clang compile flags
This helps with things like the first warning, as now I don't pass flags that clang doesn't know. It seems like I can't configure CMake to pass different set of flags to GCC and to clang-tidy, so if anyone is familiar with some solution to this problem, I'll be happy to hear!
I changed the include directories that are passed to clang-tidy
As mentioned in the post, I use a custom toolchain (which cross-compiles). I used this post and Python to extract the list of standard include directories, and added them to the flag list as a list of -isystem <dir>. I also added -nostdinc so that clang-tidy won't try to look on his own headers instead of mine
This helped with the issue above, as now various defines such as O_CLOEXEC is defined in the toolchain's headers, but as my toolchain is based on GCC, clang couldn't parse the <type_traits> header which includes calls to many compiler intrinsics
I'm not sure what's the best approach in this case
#shycha: Thanks for the tip, I'll try disabling this specific check and I'll edit this post again
Ok, I think that I have a solution. After a couple of evenings I was able to make it work.
In general I compile like this
rm -rf build
mkdir build
cd build
cmake -C ../cmake-scripts/clang-tidy-all.cmake .. && make
Where cmake-scripts directory contains:
clang-tidy-all.cmake
toolchain_arm_clang.cmake
The two important files are listed below.
But what is more important, is how you need to compile this.
First, toolchain_arm_clang.cmake is referenced directly from clang-tidy-all.cmake via set(CMAKE_TOOLCHAIN_FILE ...). It must be, however, referenced from the point of view of the building directory, so if you use multiple levels of build-dirs, e.g.: build/x86, build/arm, build/darwin, etc., then you must modify that path accordingly.
Second, the purpose of set(CONFIG_SCRIPT_PRELOADED ...) is to be sure that the config script was pre-loaded, i.e., cmake -C ../cmake-scripts/clang-tidy-all.cmake ..
Typically, you would want to have something like this somewhere in your CMakeLists.txt file:
message(STATUS "CONFIG_SCRIPT_PRELOADED: ${CONFIG_SCRIPT_PRELOADED}")
if(NOT CONFIG_SCRIPT_PRELOADED)
message(FATAL_ERROR "Run cmake -C /path/to/cmake.script to preload a config script!")
endif()
Third, there is /lib/ld-musl-armhf.so.1 hard-coded in set(CMAKE_LINKER_ARM_COMPAT_STATIC ...); on the development box that I use, it points to /lib/libc.so, so it might by OK to use /lib/libc.sh instead. I've never tried.
Fourth, using set(CMAKE_C_LINK_EXECUTABLE ...) and set(CMAKE_LINKER_ARM_COMPAT_STATIC ...) was because CMake was complaining about some linking problems during checking the compiler, i.e., before even running make.
Fifth, I was only compiling C++ code, so if you need to compile some C, then it might be required to also properly configure set(CMAKE_C_CREATE_SHARED_LIBRARY ...), but I have no idea whether there is such a config option.
General Advice
Do not integrate it immediately. First test some simple CMake project with one library (preferably a C++ one) and make it work, then add the second library, but in C, tweak it again. And only after that incorporate it into the code base.
Toolchain
I used a custom toolchain with GCC 8.3.0 and musl C library, so locations of some files might be different for other toolchains.
Custom CMake
Some variables, like (already mentioned) CONFIG_SCRIPT_PRELOADED, EXPORT_PACKAGE_TO_GLOBAL_REGISTRY, DO_NOT_BUILD_TESTS, or DO_NOT_BUILD_BENCHMARKS are not generic CMake options, i.e., I use them only in my CMakeLists.txt, so you can safely ignore them.
Variables that are unset at the end of each *.cmake file, e.g., build_test, extra_clang_tidy_unchecks_for_tests_only, don't need to be present in the project's main CMakeLists.txt.
Clang
$ clang --version
clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4650b2f36949407ef25686440e3d65ac47709deb)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /opt/local/bin
Files
clang-tidy-all.cmake:
set(ALL_CXX_WARNING_FLAGS --all-warnings -Weverything -Wno-c++98-compat -Wno-c++98-c++11-compat -Wno-c++98-c++11-c++14-compat -Wno-padded -Wno-c++98-compat-pedantic)
set(CXX_COMPILE_OPTIONS "-std=c++17;-O3;${ALL_CXX_WARNING_FLAGS}" CACHE INTERNAL "description")
set(CMAKE_CROSSCOMPILING True)
set(CMAKE_TOOLCHAIN_FILE "../cmake-scripts/toolchain_arm_clang.cmake" CACHE FILEPATH "CMake toolchain file")
set(CONFIG_SCRIPT_PRELOADED true CACHE BOOL "Ensures that config script was preloaded")
set(build_test False)
if(build_test)
message(STATUS "Using test mode clang-tidy checks!")
set(extra_clang_tidy_unchecks_for_tests_only ",-google-readability-avoid-underscore-in-googletest-name,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-special-member-functions")
endif()
set(CMAKE_CXX_CLANG_TIDY "clang-tidy;--enable-check-profile;--checks=-*,abseil-string-find-startswith,bugprone-*,cert-*,clang-analyzer-*,cppcoreguidelines-*,google-*,hicpp-*,llvm-*,misc-*,modernize-*,-modernize-use-trailing-return-type,performance-*,readability-*,-readability-static-definition-in-anonymous-namespace,-readability-simplify-boolean-expr,portability-*${extra_clang_tidy_unchecks_for_tests_only}" CACHE INTERNAL "clang-tidy")
message(STATUS "build_test: ${build_test}")
message(STATUS "extra_clang_tidy_unchecks_for_tests_only: ${extra_clang_tidy_unchecks_for_tests_only}")
message(STATUS "CMAKE_CXX_CLANG_TIDY: ${CMAKE_CXX_CLANG_TIDY}")
# We want to skip building tests when clang-tidy is run (it takes too much time and serves nothing)
if(DEFINED CMAKE_CXX_CLANG_TIDY AND NOT build_test)
set(DO_NOT_BUILD_TESTS true CACHE BOOL "Turns OFF building tests")
set(DO_NOT_BUILD_BENCHMARKS true CACHE BOOL "Turns OFF building benchmarks")
endif()
unset(build_test)
unset(extra_clang_tidy_unchecks_for_tests_only)
set(EXPORT_PACKAGE_TO_GLOBAL_REGISTRY "OFF" CACHE INTERNAL "We don't export clang-tidy-all version to global register")
toolchain_arm_clang.cmake:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 4.14.0)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(gcc_version 8.3.0)
set(x_tools "/opt/zynq/xtl")
set(CMAKE_C_COMPILER "clang" CACHE INTERNAL STRING)
set(CMAKE_CXX_COMPILER "clang++" CACHE INTERNAL STRING)
set(CMAKE_RANLIB "llvm-ranlib" CACHE INTERNAL STRING)
set(CMAKE_AR "llvm-ar" CACHE INTERNAL STRING)
set(CMAKE_AS "llvm-as" CACHE INTERNAL STRING)
set(CMAKE_LINKER "ld.lld" CACHE INTERNAL STRING)
execute_process(
COMMAND bash -c "dirname `whereis ${CMAKE_LINKER} | tr -s ' ' '\n' | grep ${CMAKE_LINKER}`"
OUTPUT_VARIABLE cmake_linker_dir
)
string(REGEX REPLACE "\n$" "" cmake_linker_dir "${cmake_linker_dir}")
set(cmake_linker_with_dir "${cmake_linker_dir}/${CMAKE_LINKER}" CACHE INTERNAL STRING)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -iwithsysroot /include/c++/${gcc_version} -iwithsysroot /include/c++/${gcc_version}/arm-linux-musleabihf" CACHE INTERNAL STRING)
set(CMAKE_SYSROOT ${x_tools}/arm-linux-musleabihf)
set(CMAKE_FIND_ROOT_PATH ${x_tools}/arm-linux-musleabihf)
set(CMAKE_INSTALL_PREFIX ${x_tools}/arm-linux-musleabihf)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER)
set(triple arm-linux-musleabihf)
set(CMAKE_LIBRARY_ARCHITECTURE ${triple})
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER_TARGET ${triple})
set(lib_path_arm ${x_tools}/arm-linux-musleabihf/lib)
## Bootstrap library stuff:
set(Scrt1_o ${lib_path_arm}/Scrt1.o)
set(crti_o ${lib_path_arm}/crti.o)
set(crtn_o ${lib_path_arm}/crtn.o)
set(lib_path_gcc ${x_tools}/lib/gcc/${triple}/${gcc_version})
set(crtbeginS_o ${lib_path_gcc}/crtbeginS.o)
set(crtendS_o ${lib_path_gcc}/crtendS.o)
# Clang as linker
# --no-pie disable position independent executable, which is required when building
# statically linked executables.
set(CMAKE_CXX_LINK_EXECUTABLE "clang++ --target=${triple} -Wl,--no-pie --sysroot=${CMAKE_SYSROOT} ${CMAKE_CXX_FLAGS} -fuse-ld=${cmake_linker_with_dir} <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> <OBJECTS> -o <TARGET> ")
set(CMAKE_CXX_CREATE_SHARED_LIBRARY "clang++ -Wl, --target=${triple} --sysroot=${CMAKE_SYSROOT} ${CMAKE_CXX_FLAGS} -fuse-ld=${cmake_linker_with_dir} -shared <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> <OBJECTS> -o <TARGET> ")
#
# Do not use CMAKE_CXX_CREATE_STATIC_LIBRARY -- it is created automatically
# by cmake using ar and ranlib
#
#set(CMAKE_CXX_CREATE_STATIC_LIBRARY "clang++ -Wl,--no-pie,--no-export-dynamic,-v -v --target=${triple} --sysroot=${CMAKE_SYSROOT} ${CMAKE_CXX_FLAGS} -fuse-ld=ld.lld <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> <OBJECTS> -o <TARGET> ")
## Linker as linker
set(CMAKE_LINKER_ARM_COMPAT_STATIC "-pie -EL -z relro -X --hash-style=gnu --eh-frame-hdr -m armelf_linux_eabi -dynamic-linker /lib/ld-musl-armhf.so.1 ${Scrt1_o} ${crti_o} ${crtbeginS_o} -lstdc++ -lm -lgcc_s -lgcc -lc ${crtendS_o} ${crtn_o}")
set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_LINKER} ${CMAKE_LINKER_ARM_COMPAT_STATIC} <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> <OBJECTS> -o <TARGET>")
# Debian bug 708744(?)
#include_directories("${CMAKE_SYSROOT}/usr/include/")
#include_directories("${CMAKE_SYSROOT}/usr/include/c++/${gcc_version}")
#include_directories("${CMAKE_SYSROOT}/usr/include/c++/${gcc_version}/${triple}")
## Clang workarounds:
set(toolchain_lib_dir_0 "${CMAKE_SYSROOT}/lib")
set(toolchain_lib_dir_1 "${CMAKE_SYSROOT}/../lib")
set(toolchain_lib_dir_2 "${CMAKE_SYSROOT}/../lib/gcc/${triple}/${gcc_version}")
set(CMAKE_TOOLCHAIN_LINK_FLAGS "-L${toolchain_lib_dir_0} -L${toolchain_lib_dir_1} -L${toolchain_lib_dir_2}")
## CMake workarounds
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_TOOLCHAIN_LINK_FLAGS} CACHE INTERNAL "exe link flags")
set(CMAKE_MODULE_LINKER_FLAGS ${CMAKE_TOOLCHAIN_LINK_FLAGS} CACHE INTERNAL "module link flags")
set(CMAKE_SHARED_LINKER_FLAGS ${CMAKE_TOOLCHAIN_LINK_FLAGS} CACHE INTERNAL "shared link flags")
unset(cmake_linker_with_dir)
unset(cmake_linker_dir)
Maybe not exactly what you're looking for but I'm using this in CMakeLists.txt:
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_custom_target(lint
COMMAND sh -c "run-clang-tidy -header-filter=.* -checks=`tr '\\n' , <${CMAKE_SOURCE_DIR}/checks.txt` >lint.out 2>lint.err"
COMMAND sh -c "grep warning: lint.out || true"
COMMAND ls -lh ${CMAKE_BINARY_DIR}/lint.out
VERBATIM
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
This creates a separate build target (make lint) for the clang-tidy check. clang-tidy takes a long time for my project so I don't want to run it during every build; make lint can be run manually if required, and it's also executed in a CI job after every push to the repo (in a way that makes the CI pipeline fail, blocking the merge, if there are any findings).
The output of make lint is the list of clang-tidy findings with as little context as possible. The full output, including context for findings, is in lint.out, and error messages are in lint.err, both of which I'm saving as CI artefacts.
checks.txt is a text file in the project root that defines which clang-tidy checks to activate, like so:
*
-altera-id-dependent-backward-branch
-altera-struct-pack-align
-altera-unroll-loops
-android-*
The first line enables all available checks, the other lines disable checks that I don't want.
Will only work in a Unix-like system of course.

Set GCC path in makefile

Whenever I am building my package it uses /usr/bin/g++ (system compiler).
I want to build my package with C++11 constructs. I have tried -std=c++11 option but with system compiler it says unrecognized option.
I want to build my package from a different gcc compiler which will get downloaded as part of my package dependency.
So, how can I specify the location of gcc compiler in Makefile?
There are multiple ways to achieve what you are looking for:
Setting the environment variable CXX just for the process that will run make:
$ CXX=/path-to-your-compiler/g++ make
Exporting the environment variable CXX in your shell:
$ CXX=/path-to-your-compiler/g++
$ export CXX
$ make
Setting CXX at make's command-line:
$ make CXX=/path-to-your-compiler/g++
Inside your makefile:
CXX := /path-to-your-compiler/g++
Note that setting the variable at make's command line overrides the other values, and variables set inside the makefile override the ones obtained from the environment (unless the command-line option -e or --environment-overrides is provided).
Inside your makefile, you can still override any value set by other means by using the override directive:
override CXX := /path-to-your-compiler/g++

Code coverage warrnings spam output

In CMake tests configuration I added flags to generate codecoverage
IF( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR
"${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage ")
endif()
Each time I run tests - my output is spammed with those messages:
profiling: /xxxx/xxxxxj/projects/build-xxxxx-CLang-Debug/tests/CMakeFiles/xxxxxxxxxxxxxt_ut.dir/tests/gui/ship_design/ut_ship_stats_header.cpp.gcda: cannot merge previous GCDA file: mismatched number of counters (14)
profiling:
/xxxx/xxxxxx/projects/build-xxxxxxxxxxxxxxx-CLang-Debug/tests/CMakeFiles/xxxxxxxxxxxxxxx_ut.dir/tests/gui/ship_design/ut_ship_stats_header.cpp.gcda:
cannot merge previous GCDA file: corrupt arc tag (0x2b8e100f)
profiling:
/xxxx/xxxxxx/projects/build-xxxxxxxxxxxxxxx-CLang-Debug/tests/CMakeFiles/xxxxxxxxxxxxxxx_ut.dir/tests/ut_generate_hex_path.cpp.gcda:
cannot merge previous GCDA file: corrupt arc tag (0x65646f6e)
I don't have to say that this make reading test run results at least difficult. Above problem vanished when I remove all gcda files generated previously. So there are two possible solutions.
I may miss some valuable configuration of code coverage data (ie. call lcov --zerocounters
FIND_PROGRAM( LCOV_PATH lcov )
COMMAND ${LCOV_PATH} -z --directory ${PROJECT_BINARY_DIR}}
I need to add custom target removing all gcda files before test run is committed.
How should I approach this problem?
I think you might be doing something unusual in your workflow, as the problem you're seeing shouldn't happen all of the time. But, what I've added here should help you figure that out, or work around it completely.
Firstly, you should take advantage of CMake's build-types and create your own "coverage type".
## coverage flags
set(CMAKE_CXX_FLAGS_COVERAGE "-g -O0 -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the C++ compiler during coverage builds.")
set(CMAKE_C_FLAGS_COVERAGE "-g -O0 -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the C compiler during coverage builds.")
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "-g -O0 -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used for linking binaries during coverage builds.")
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "-g -O0 -fprofile-arcs -ftest-coverage" CACHE STRING "Flags used by the shared libraries linker during coverage builds.")
mark_as_advanced(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE)
## Update the documentation string of CMAKE_BUILD_TYPE for GUIs
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel RelWithAssert Coverage." FORCE)
Then create a custom target.
## create our "make coverage" target
add_custom_target(coverage
COMMAND if test ! -d ../output \; then mkdir ../output\; fi
COMMAND find ${CMAKE_BINARY_DIR} -name \*.gcda -delete
COMMAND lcov -b CMakeFiles/ -d . -z
COMMAND lcov -b -d . -c -i -o test_base.info
COMMAND ./env-shell.sh ctest -j2 || true
COMMAND lcov -b CMakeFiles/ -d . -c -o test_run.info
COMMAND lcov -b CMakeFiles/ -d . -a test_base.info -a test_run.info -o test_total.info
COMMAND lcov -o reports.info -r test_total.info '/usr/include/*' '/usr/local/*' '/cvmfs/*' '*/numpy' '/usr/lib/gcc/*' ${p} '${CMAKE_BINARY_DIR}/CMakeFiles/' '${CMAKE_BINARY_DIR}/steamshovel/*'
COMMAND genhtml --ignore-errors source --legend -o ../output/`date +%Y-%m-%d` reports.info
)
Now, taking advantage of out-of-source builds, run cmake in a "coverage" directory parallel to your source.
$ pwd
/home/user/my_project/src
$ mkdir ../coverage
$ cd ../coverage
$ cmake -DCMAKE_BUILD_TYPE=Coverage ../src
[ ... cmake's output here ...]
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/my_project/coverage
$
Now, build your project, and your tests, and run your "coverage" target.
$ make
$ make test-bins
$ make coverage
[ ... make's output here ... ]
$
The make coverage target we defined in our CMakeLists.txt will:
if it doesn't exist, create an "output" directory parallel to our source and build directories
find and delete all *.gcda files in our current build directory
zero and initialize our lcov counters and output file
run our tests via ctest
"compile" the lcov output and generate our HTML coverage report
Note that you may need to adjust things for your particular project.
At this point I highly recommend automating all of this. Add it to your continuous integration, if you have some, or even set up a cron job so that this runs overnight and you have a fresh new coverage report to start your day. (These examples are from a working project that does a nightly coverage report handled by buildbot.)

How to config cmake for strip file

when I use cmake in Release mode I have the following binary:
64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=485ac09b0a3aa879f88b7f5db6c00ea8d8e1eaf6, not stripped
I want the binary to be stripped.
How can I say to cmake in a clean way to add the -s option to my compiler to make it stripped?
Why did the Default Release mode not strip my binary?
Cleanest possible way is to modify CFLAGS or CXXFLAGS (depending on C or C++ code)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
But there is one more hack if you do not want to change your build system (figuring out exact place where to put above lines might be tricky). You may just use strip as standalone application, like:
strip -s a.out
and do this after executable is ready to release as a post-build step. I found this way cleaner, then disturbing compiler flags.
You can try
set_target_properties(TARGET_NAME PROPERTIES LINK_FLAGS_RELEASE -s)
Using add_link_options() or set_target_properties() to add -s should work fine, additionally, CMake creates an install/strip target which also could be used for striping the binary if you have at least one install() command for your target (reference).
Example:
$ cmake --build . --config Release --target install/strip
This works fine:
add_link_options($<$<CONFIG:RELEASE>:-s>)

Cmake doesn't honour -D CMAKE_CXX_COMPILER=g++

I'm trying to force cmake to build my cpp code with g++, as by default it uses clang instead. So I use: cmake -D CMAKE_CXX_COMPILER=g++ ../src/CMakeLists.txt after which cmake checks for gcc and g++ (with success), but nonetheless make VERBOSE=1 yields
/usr/bin/c++ -o CMakeFiles/trial_cpp.dir/trial.cpp.o -c "/Users/Kuba/Code/Sketchpad/Trial project/src/trial.cpp"
Linking CXX executable trial_cpp
/opt/etlocal/bin/cmake -E cmake_link_script CMakeFiles/trial_cpp.dir/link.txt --verbose=1
/usr/bin/c++ -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/trial_cpp.dir/trial.cpp.o -o trial_cpp
As it calls /usr/bin/c++ not /usr/bin/g++ I concur it still uses clang. Any idea what's the problem? I know I have g++ and it's in /usr/bin/.
I'm running Mac OS X 10.8.2
CMAKE_CXX_COMPILER can only be set the first time cmake is run in a given build directory. On subsequent runs it is ignored. In order to change CMAKE_CXX_COMPILER you first need to delete the contents of the build directory and then run cmake again with that option.
Source: http://www.cmake.org/Wiki/CMake_Useful_Variables
I believe the reasoning for only using that variable on the first run is because changing it later would potentially invalidate everything already built including the configuration checks so cmake would have to start from scratch anyway.
I do it like this instead:
CXX=/usr/bin/g++ cmake ../src/CMakeLists.txt