CMake/CTest - Where was defined test XYZ? - cmake

After building a CMake/CTest setup with many tests I see a growing problem.
If ctest -R test_some_side_corner_item_XYZ fails, how can I systematically track back to the place where somebody added the test test_some_side_corner_item_XYZ?
I understand that there is the problem that we can add macros on top of add_test() which makes it a bit harder to make any clear answer - but still.
It appears cmake does not have any obvious ways to achieve this - and the same seems for ctest.

You can use the ctest command line options to find the exact line in your CMakeLists.txt hierarchy where the add_test() call was made. We can use the --show-only=json-v1 option to display JSON-formatted meta-data about a test:
ctest -R test_some_side_corner_item_XYZ --show-only=json-v1
An example of what this prints would be:
{
"backtraceGraph" :
{
"commands" :
[
"add_test"
],
"files" :
[
"C:/workspace/myproject/CMakeLists.txt"
],
"nodes" :
[
{
"file" : 0
},
{
"command" : 0,
"file" : 0,
"line" : 34,
"parent" : 0
}
]
},
...
This lists the CMakeLists.txt file where add_test() was called for this test, and the line number ("line" : 34) where it was called.
From the CMake documentation, the --show-only option will not actually run the test, but will only display its information:
-N,--show-only[=<format>]
Disable actual execution of tests.
This option tells CTest to list the tests that would be run but not actually run them. Useful in conjunction with the -R and -E options.
Note, the -R option is a regex to match the test(s) you want, so to get an exact match, you can anchor the test name with ^ and $:
ctest -R ^test_some_side_corner_item_XYZ$ --show-only=json-v1

Related

A cmake option that automatically specifies new values for other cmake options

Currently my CMakeLists.txt file contains lots of little options for including or excluding particular pieces of functionality from the build; by default everything is built, but the user remove a feature from the build by passing an argument like -DWITH_FEATURE_X=OFF on the command line:
option(WITH_FEATURE_X "Enable feature X" ON)
if (WITH_FEATURE_X)
file(GLOB FEATURE_X_SRCS "${TOP_DIR}/feature_x/*.cpp")
list(APPEND ALL_SRCS ${FEATURE_X_SRCS})
else ()
message("CMake: -DWITH_FEATURE_X=OFF argument was specified: building without feature X.")
add_definitions(-DAVOID_FEATURE_X)
endif ()
... this works fine, but it's a little tedious in some cases where the caller has to specify a dozen -DWITH_FEATURE_X=OFF type arguments on the command line.
Since I know in advance of several common uses-cases where the user would like to specify a known set of features to be disabled, I'd like to be able to specify that with a single argument, something like this:
cmake -DWITH_MINIMAL_CONFIGURATION=ON ..
.... and have the CMakeLists.file act as if the user had specified:
cmake -DWITH_FEATURE_X=OFF -DWITH_FEATURE_Y=OFF -DWITH_FEATURE_Z=OFF [...and so on...] ..
Is there a way to do that, that won't overcomplicate the implementation of the CMakeLists.txt file?
CMake presets (since version 3.19) could be a good option here.
You could have a minimal_configuration configuration preset that would set those variables as cacheVariables. And then a minimal_configuration build preset that would use the minimal_configuration configuration preset.
"configurePresets": [
{
"name": "minimal_configuration",
...
"cacheVariables": {
"WITH_FEATURE_X": "OFF",
"WITH_FEATURE_Y": "OFF",
"WITH_FEATURE_Z": "OFF",
...
},
}
],
"buildPresets": [
{
"name": "minimal_configuration",
"configurePreset": "minimal_configuration"
}
]
Notice you can define a hierarchy of configuration presets, and let each level of that hierarchy manage different settings. For example, you could have a common_config first level, a windows_config and unixlike_config second level, a windows_debug_config, windows_release_config (and so on) third level...
In order to run CMake for a given preset, just pass it as a command line option:
~/your_project> cmake --preset minimal_configuration
~/your_project> cmake --build --preset minimal_configuration

meson and git information

I need to provide to the binary built with meson build system some git information regarding branch and version used:
git describe --tags
git descibe --help
the problem I have is how retrieve this information with meson,
with the make build I use the following instruction:
GITREF = $(shell git describe --all)
LIB1_VER = $(shell cd ../../lib1;git describe --tags;cd - &>NULL)
so in meson for GITREF I've tried
info_dep = vcs_tag(command : ['git descibe --all'],
input : 'infoBuild.h.in',
output : 'infoBuild.h',
replace_string : 'BRANCHNAME')
where infobuild.h.in is:
#define GITREF "BRANCHNAME"
but when I go to compile with ninja I got
/usr/local/bin/meson --internal vcstagger ../../src/prog1/info/infoBuild.h.in src/prog1/info/infoBuild.h 1.1.0 /home/mariano/clonesIntel/projMes/src/prog1/info BRANCHNAME '(.*)' '/home/mariano/clonesIntel/ProjMes/src/prog1/info/git describe --all'
but I don't find any infoBuild.h,
more over for the LIB1_VER is more difficult because it is in an external folder,
I could overcome this issue with a bash script but is there a way to retrieve both information in meson build?
I see an immediate problem in that it's going to try to run a command 'git describe --all', which is not what you want, as meson will be sure to escape the spaces in your shell so that it treats that as the a single filename, you want ['git', 'describe', '--all']. Of course, that could just be a type in your example.
One option you might consider is a run_command and configure_file, which is a command run at compile time, and produces a result object that you can get string values from. The disadvantage of this compared to vcs_tag (or a custom_target) is that it happens at configure time, as opposed to build time, so you need to reconfigure to update your tags:
res = run_command(['git', 'describe', '--all'], capture : true, check : true)
describe = res.stdout()
version_h = configure_file(
input : 'version.h.in',
output : 'version.h',
configuration : {'PLACEHOLDER' : describe}
)

How to run post build commands in meson?

How can I do in meson to run a command after building a target?
Eg. I have an executable:
executable('target.elf', 'source1.c', 'source2.c')
And after target.elf built I want to execute a command (eg. chmod -x target.elf) on it.
I tried custom_target(), but that requires an output. I don't have new output, I just have target.elf. I tried run_command() but I didn't know how to execute it after the building.
executable now has an argument install_mode (added 0.47.0) to specify the file mode in symbolic format and optionally the owner/uid and group/gid for the installed files.
I just noticed that yasushi-shoji has provided this answer already.
The following code should do.
project('tutorial', 'c')
exec = executable('target.elf', 'main.c', build_by_default : false)
custom_target('final binary',
depends : exec,
input : exec,
output : 'fake',
command : ['chmod', '+x', '#INPUT#'],
build_by_default : true)
Note that because I want to always run the fake target, I'm using custom_target(). However, the command chmod + x demo doesn't generate the file fake specified in custom_target(), successive ninja command will always run the target.
If you don't want this behaviour, there are two ways:
You can write a script which chmod the target.elf and then copies it to target, thus effectively creates the target file. Make sure to change the output file in the meson.build if you do so.
If you don't mind typing ninja chmod instead of ninja, you can use run_target().
# optional
run_target('chmod',
command : ['chmod', '+x', exec])
Another alternative is to use install_mode for executable().
Also note that you should always use find_program() instead of plain chmod. This example doesn't use it for simplicity.

Generating compilation database for a single target with cmake

I'm using CMAKE_EXPORT_COMPILE_COMMANDS variable of cmake to obtain a json compilation database that I can then parse to identify the options that are given to the compiler for each source file. Now, the project I'm working on has several targets, and there are several occurrences of the source files that are used in different targets in the database, as can be shown by the example below:
f.c:
int main () { return MACRO; }
CMakeLists.txt:
cmake_minimum_required (VERSION 2.6)
project (Test)
add_executable(test1 f.c)
add_executable(test2 f.c)
target_compile_options(test1 PUBLIC -DMACRO=1)
target_compile_options(test2 PUBLIC -DMACRO=2)
running cmake . -DCMAKE_EXPORT_COMPILE_COMMANDS=1 will produce the following compile-commands.json file, with two entries for f.c, and no easy way to distinguish between them.
[
{
"directory": "/home/virgile/tmp/cmakefile",
"command": "/usr/bin/cc -DMACRO=1 -o CMakeFiles/test1.dir/f.c.o -c /home/virgile/tmp/cmakefile/f.c",
"file": "/home/virgile/tmp/cmakefile/f.c"
},
{
"directory": "/home/virgile/tmp/cmakefile",
"command": "/usr/bin/cc -DMACRO=2 -o CMakeFiles/test2.dir/f.c.o -c /home/virgile/tmp/cmakefile/f.c",
"file": "/home/virgile/tmp/cmakefile/f.c"
}
]
I'm looking for a way to specify that I'm only interested in e.g. target test1, as what you can do in build tool mode with --target, preferably without having to modify CMakeLists.txt, but this is not a major issue. What I'd like to avoid, on the other hand, is to read the argument of -o in the "command" entry and discriminate between the test1.dir and test2.dir path component.
Apparently this feature has been implemented in a merge-request about 3 months ago: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/5651
From there I quote:
The new target property EXPORT_COMPILE_COMMANDS associated with the
existing global variable can be used to optionally configure targets for
their compile commands to be exported.
So it seems that you now can set a property on the respective targets in order to control whether or not they will be included in the generated DB.
This feature is part of cmake 3.20. Official docs: https://cmake.org/cmake/help/latest/prop_tgt/EXPORT_COMPILE_COMMANDS.html
Based on the docs the approach for limiting the compile DB to only a specific target should be to set CMAKE_EXPORT_COMPILE_COMMANDS to OFF and then use target_set_properties to set the EXPORT_COMPILE_COMMANDS property on the desired target to ON. E.g.
set(CMAKE_EXPORT_COMPILE_COMMANDS OFF)
...
set_target_properties(test1 PROPERTIES EXPORT_COMPILE_COMMANDS ON)
It seems that this is not supported at the moment. There is a request for this in the CMake issue tracker: https://gitlab.kitware.com/cmake/cmake/issues/19462

Can't run ctest unless the test executable is named "tests"

I am trying to get CMake/CTest to work. My problem is that CTest does not seem to pick up a test executable unless it is named tests.
In my minimal example project I have a single CMakeLists.txt file:
cmake_minimum_required(VERSION 3.0)
project(myproject C)
enable_testing()
set(TEST_EXE_NAME tests)
add_executable(${TEST_EXE_NAME} test_main.c)
add_test(NAME "My tests" COMMAND ${TEST_EXE_NAME})
...and a very simple test program, test_main.c, that always passes:
int main() {
return 0;
}
I can run make && make test, and all is fine as long as TEST_EXE_NAME is set to tests. However, when I change the executable name to something else, e.g. mytests, I get the following error:
Could not find executable tests
Looked in the following places:
tests
tests
Release/tests
Release/tests
Debug/tests
...
What am I missing?
According to the CMake manual for add_test() the test name may not contain spaces:
The test name may not contain spaces, quotes, or other
characters special in CMake syntax.
In the problematic example, the test was named "My tests". Changing the test name to "My_tests" solves the problem.
Apparently, the part of the test name after the space character (i.e. "tests") was interpreted as the test executable name.