I use CMake to generate some makefiles that will compile multiple targets using G++ on Linux.
I would like to create some metrics about the compilation such as :
Time needed to build each target
G++ warnings and errors
The only solution I came up with so far is to redirect the output of make and parse the whole file but this seems really heavy. I cannot use the G++ flag -fdiagnostics-format=json feature since I cannot upgrade to GCC-9.
Any suggestions? Thank you very much
Something you could do is set the compiler to be a program or shell script that wraps the G++ functionality and add some (similar to how some ccache integrations work):
# Makefile
CC := g++-with-metrics.sh
# g++-with-metric.sh
# 1. Start timer
# 2. Run command, save output (to variable or tmpfile)
# 3. Stop timer, log result
# 4. Parse output for warnings and errors, log result
As far as timing is concerned, consider using Ninja instead of Make. When you build it produces a file called ${CMAKE_BINARY_DIR}/.ninja_log that can be converted to a format compatible with Chrome's about:tracing flame graph format.
There's a script for doing that here: https://github.com/nico/ninjatracing
Related
Ninja redirects the stderr output to stdout. When used with colcon and its colcon-cmake extension, the error log is therefore not displayed when a ninja package build is failing.
How can I see the build error messages?
There is no direct solution to the problem (see Colcon does not show failed build output if using Ninja generator).
This GitHub issue mentions a workaround, though: Add --event-handlers console_cohesion+ to the colcon build call, either systematically, or only on a build failure, depending on how clean you want your build output to be. It reveals the stdout output (and thus the errors issued by ninja).
I would like to use CMake and clang-tidy in my project, however I see that build times are quite a bit higher when I use this in all the main cmake file:
set(CMAKE_CXX_CLANG_TIDY
clang-tidy-11;
-format-style='file';
-header-filter=${CMAKE_CURRENT_SOURCE_DIR};
)
It is working well, but I don't want to have this build-time penalty every time I build the project during development. Therefore I thought I would make a separate target that builds all, but uses clang-tidy. And when I do a regular debug or release build it does not do any checking. However I don't know how to do this in Cmake. Do I make a custom target with a command "cmake --build" with a target_set_property of CMAKE_CXX_CLANG_TIDY?
This feels rather clunky, so my question is, are there other ways to do this?
however I see that build times are quite a bit higher when I use this in all the main cmake file:
You're going to have to pay for the cost of running clang-tidy sometime or another. It's essentially running the first few phases of a compiler to analyze your code and look for errors.
Setting CMAKE_CXX_CLANG_TIDY runs clang-tidy in line with your build, as you have observed.
This feels rather clunky, so my question is, are there other ways to do this?
Yes. When using the Ninja or Makefile generators, you may set -DCMAKE_EXPORT_COMPILE_COMMANDS=ON at the command line. That will create a file called compile_commands.json in your build folder that the standalone clang-tidy can read.
In sum, at the command line, you would manually run:
$ cmake -G Ninja -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
$ clang-tidy-11 -format-style=file -header-filter=. -p build
The -p flag tells clang-tidy in which directory to find your compile_commands.json.
In CMake I currently have a simple Python script to generate a header, but if I update the script itself CMake won't re-run the script. Is there a way I can get CMake to do this?
It seems you are directly invoking your code generation script when cmake is run. While it is possible solution but it is definitely not a right way to use code generators with cmake.
I recommend you to use add_custom_command for your case:
add_custom_command(
OUTPUT generated.h
COMMAND ${PYTHON_EXECUTABLE} generator.py
DEPENDS generator.py
)
And next you can simple put your header to the list of source files passed to add_library/add_executable commands. cmake will automatically track all the dependencies and invoke your script.
Term DEPENDS generator.py informs cmake that it should regenerate header if script is changed.
With this approach file generated.h will be generated only at build time (when you run make or execute a build command in IDE). In contrast if you are running your script at cmake time (with execute_process command) then you have to rerun cmake to regenerate your file. Which is possible but you need to use some tricks to introduce a non-standard dependency.
I am trying to write a CMakeLists.txt to speed up compilation.
The executable depends on a script generated .cpp file: I use the cppcms web application library which has a templating system where .tmpl must be converted to .cpp files during the compilation like this:
cppcms_tmpl_cc page.tmpl -o page.cpp
There are related questions that cover the use of bash commands within cmake:
How to run a command at compile time within Makefile generated by CMake?
CMake : how to use bash command in CMakeLists.txt
These questions cover most of my needs.
What I want to know, now, is how to tell cmake to run the above command and re-generate page.cpp every time page.tmpl itself has changed, and only then?
The goal obviously is to improve the compile time and have an up to date binary with the latest template.
(can a moderator add the cppcms tag?)
[Edit: I am actually trying to convert the following Makefile to cmake:
LIBS=-lcppcms -lconfig++ -lboost_filesystem-mt
all: clean gitbrowser
gitbrowser: main.cpp view.cpp content.hpp gitbrowser.cpp
$(CXX) -Wall main.cpp gitbrowser.cpp view.cpp -o run ${LIBS}
view.cpp: page.tmpl content.hpp
cppcms_tmpl_cc page.tmpl -o view.cpp
[Edit2: I added a note about the solution in the official cppcms wiki:
http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_howto#How.to.compile.the.templates.with.cmake.
now = get_now_time()
time = get_last_upd_time()
if (now > time)
set (LAST_UPD_TIME time CACHE INTERNAL "Defines last update time of the file" FORCE)
# run bash command here
endif (now > time)
get_now_time and get_last_upd_time are fictional functions, returning timestamps (I guess you can use bash commands to get those timestamps). Then you can compare them and store last modification timestamp into cache.
However, this solution looks ugly for me, as I know if you properly define targets and dependencies between them CMake itself will take care of rebuilding only modified files, doesn't it? Could you show me target definitions?
edit
You can use following CMakeLists.txt (thougn I'm not sure, it's based on my project):
# add main target, the executable file
ADD_EXECUTABLE(gitbrowser main.cpp view.cpp content.hpp gitbrowser.cpp)
# linking it with necessary libraries
TARGET_LINK_LIBRARIES(gitbrowser "cppcms config++ boost_filesystem-mt")
# add page.cpp target
ADD_CUSTOM_COMMAND(
OUTPUT page.cpp
COMMAND "cppcms_tmpl_cc page.tmpl -o view.cpp"
DEPENDS page.tmpl content.hpp
)
# and finally add dependency of the main target
ADD_DEPENDENCIES(gitbrowser page.cpp)
Good luck
Take a look on this CMake file of Wikipp lines 66-72
You basically need something like this:
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/view.cpp
COMMAND cppcms_tmpl_cc
view.tmpl
-o ${CMAKE_CURRENT_BINARY_DIR}/view.cpp
DEPENDS view.tmpl)
Edit: Also if you want to improve compilation speed you may compile
the view into shared object and load it dynamically.
This would also allow you not to restart application if you only changed the view, the
shared object after recompilation would be automatically reloaded.
See: http://art-blog.no-ip.info/wikipp/en/page/cppcms_1x_config#views for more details.
How do I see the g++ invocations caused by running make? I am generating my makefile using cmake, so it is quite large.
Using verbose=1, cmake is still hiding the g++ invocations:
[ 0%] Building CXX object ui/CMakeFiles/ui.dir/mainwindow.cc.o
In file included from /Users/neil/nn/src/ui/mainwindow.h:6,
from /Users/neil/nn/src/ui/mainwindow.cc:9:
/Users/neil/nn/src/./core/globals.h:8:26: error: glog/logging.h: No such file or directory
I want to see if it's passing the right include directors to g++ because it's not finding glog/logging.h
Try:
make VERBOSE=TRUE
Peek in the CMakeFiles/ui.dir/ directory; you'll probably be interested in the flags.make file, though the non-includes portion of the g++ invocation is stored in build.make.
You can set the verbosity level with make VERBOSE="" (source).
Run CMake with -DCMAKE_VERBOSE_MAKEFILE="ON"
There is still a lot of noise though, so you may need to dump stdout & stderr to a file and grep for the name of the compiler...
From the shell prompt:
VERBOSE=1 make
(that is, run make with VERBOSE environment variable set to 1).