CMake exclude tests in subdirectories - cmake

I have a c++ project which includes libevent library. Project structure:
.
|_ CMakeLists.txt
|_ Makefile
|_ src
| |_ my_lib.cpp
|_ test
| |_ my_lib_test.cpp
|_ lib
|_ libevent
|_ CMakeLists.txt
|_ ...
When I build and run my tests, libevent tests are also executed. How can I exclude them and run only my own tests?

There is also a more general way to do it. Add a file named CTestCustom.cmake to your source tree and add a list of tests that you want CTest not to run:
set(CTEST_CUSTOM_TESTS_IGNORE
test1
....
testN
)
Then copy this file to the build directory where tests are executed:
configure_file(${CMAKE_SOURCE_DIR}/CTestCustom.cmake ${CMAKE_BINARY_DIR})
This will make CTest ignore the listed tests. See this for more info.

Looking at the available options in libevent's CMakeLists.txt file, it appears that you can disable these pretty easily by setting EVENT__DISABLE_TESTS to ON.
You can either do this in your own CMakeLists.txt before libevent is included:
set(EVENT__DISABLE_TESTS ON)
...
add_subdirectory(lib/libevent)
or when you invoke CMake on the command line:
cmake . -DEVENT__DISABLE_TESTS=ON

I've found another (better) way to solve the actual question.
TL;DR
Add to your CMakeLists.txt file:
configure_file(${CMAKE_SOURCE_DIR}/CTestCustom.cmake ${CMAKE_BINARY_DIR} #ONLY)
Add do your CTestCustom.cmake file in your source code:
file (STRINGS "#CMAKE_BINARY_DIR#/CTestTestfile.cmake" LINES)
# overwrite the file....
file(WRITE "#CMAKE_BINARY_DIR#/CTestTestfile.cmake" "")
# loop through the lines,
foreach(LINE IN LISTS LINES)
# remove unwanted parts
string(REGEX REPLACE ".*directory_to_remove.*" "" STRIPPED "${LINE}")
# and write the (changed) line ...
file(APPEND "#CMAKE_BINARY_DIR#/CTestTestfile.cmake" "${STRIPPED}\n")
endforeach()
(See https://stackoverflow.com/a/59506088/4166604 for a function form of removing a line)
Explanation:
The excluding per test method has many downsides, including dynamically (via a macro) added tests, wasted time, etc...
This was tested on CMake 3.x: When a CMake project with tests enabled is built, a CTestTestfile.cmake file is generate in the CMAKE_BINARY_DIR. Essentially every time add_subdirectory is called, an equivalent subdirs command is added to the CTestTestfile.cmake file. The CTestTestfile.cmake files will mimic an equivalent directory structure as the CMakeLists.txt files. (So if you need to remove a sub directory in another directory, that's the CTestTestfile.cmake you need to edit).
The CTestCustom.cmake is executed before the CTestTestfile.cmake, so all CTestCustom.cmake needs to do is remove the offending line from the CTestTestfile.cmake file. This could be done with a sed, but in the spirit of cross compatibility, it's all pure CMake now. The CTestCustom.cmake does not have CMAKE_BINARY_DIR set, so we use # substitution to get the value of CMAKE_BINARY_DIR at CMake generate time, and replace the values that will be in the CTestCustom.cmake copy in the build directory, using #ONLY replacement, to leave the other variables $ alone.

I agree with #Ivan Baidakou.
I'm not sure this is much better, but here's a non-portable hack (won't work on Windows by default) to get it done.
execute_process(COMMAND sh -c "echo 'set(CTEST_CUSTOM_TESTS_IGNORE'; find '${CMAKE_SOURCE_DIR}/relative_paths' -path '*tests/CMakeLists.txt' -exec sed -nE '/add_test.*NAME/{s|.*NAME *([^ ]+).*|\\1|; p}' {} +; echo ')'"
OUTPUT_FILE ${CMAKE_BINARY_DIR}/CTestCustom.cmake)
Where ${CMAKE_SOURCE_DIR}/relative_paths can be multiple paths for multiple libraries if you want.

Related

cmake execute process before anything else

I've a problem with CMake executing a process before doing anything else.
The following code snippet shows the situation:
if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/generated")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated")
execute_process(
# This tool generates library sources, which are not known before
COMMAND "source_file_generator_command"
# This tool iterates over the generated source tree and creates
# a proper CMakeLists.txt in the 'generated' directory from the
# source files found there
COMMAND "cmake_lists_generator_command"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated"
)
endif()
# At this point the generated subdirectory (with the also
# generated CMakeLists.txt file) shall be included
add_subdirectory(
"${CMAKE_CURRENT_BINARY_DIR}/generated"
"${CMAKE_CURRENT_BINARY_DIR}/generated_build"
)
# But the 'add_subdirectory' statement fails due to non-existing
# CMakeLists.txt in the 'generated' source directory at this point
The problem is, as commented above, that the CMakeLists.txt file in the subdirectory which should be added is generated on the fly by a special script (the generated sources are not known before) during the first CMake run. Literally, I need CMake to wait until all the statements within the if/else block are executed and process the add_subdirectory statement not until everything is done (the CMakeLists.txt is generated). Is there an adequate solution for such a use case?
Thanks for your help,
Felix
When several COMMANDs for execute_process are used, they are executed in pipe. From the description of this CMake command:
Runs the given sequence of one or more commands with the standard output of each process piped to the standard input of the next.
If you want true sequential execution of the commands, you need to use one call to execute_process per COMMAND:
execute_process(
COMMAND "source_file_generator_command"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated"
)
execute_process(
COMMAND "cmake_lists_generator_command"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated"
)

How to make cMake use Debug compilation mode for only a subset of directories in a project?

Rephrased the question.
I have the following problem:
My project has several binaries and libraries that reside in distinct sub-directories under the main project folder.
It is useful to be able to debug only a subset of them, without recompiling the whole project in Debug mode.
I want to be able to only change the compilation mode for this subset in a semi-automatic fashion.
How can I accomplish this using CMake?
If you change the build type, the whole project will be recompiled from scratch. Usually you keep 2 separated build tree, one configured debug and one configured release.
Note that CMAKE_BUILD_TYPE can be set from command line or from cmake-gui, you shouldn't set it in the CMakeLists.txt file.
To compile only some part of your project in debug mode, you can proceed as follow. In your main CMakeLists.txt, before including any subdirectory, define the following macro:
macro (CHECK_IF_DEBUG)
if (NOT (CMAKE_BUILD_TYPE MATCHES Debug))
get_filename_component(THIS_DIR ${CMAKE_CURRENT_SOURCE_DIR} NAME)
STRING(REGEX REPLACE " " "_" THIS_DIR ${THIS_DIR}) #Replace spaces with underscores
if (DEFINED DEBUG_${THIS_DIR})
message(STATUS "Note: Targets in directory ${THIS_DIR} will be built Debug") #A reminder
set (CMAKE_BUILD_TYPE Debug)
endif()
endif()
endmacro()
Then, in each subdirectory add (at the beginning of the CMakelists.txt) the macro call
CHECK_IF_DEBUG()
When you need to temporarily debug a part (subdirectory) of your project, open your project in cmake-gui, and define a variable ("Add Entry") with name DEBUG_<DirectoryName>. You can define multiple ones. If your directory name contains spaces, in the variable name replace them with underscores. Don't forget to click Configure and Generate from cmake-gui to make the change effective. The value assigned to the variable is not important, it can be left empty.
When you are finished debugging, go back to cmake-gui and remove the corresponding entries. Don't forget to Configure and Generate.
I have tested it in a small project and it seems to work properly.
Note: If you create more than one target (add_library or add_executable) in the same CMakeLists.txt (=in the same subdirectory), I haven't found a way to have one target built in one way and one target in another: the only thing that seems to count is the value of the CMAKE_BUILD_TYPE directory when CMake is finished parsing the file.
In answer to your comment, you can have a reminder printed at build time by adding in the block the following line:
add_custom_target(info_${THIS_DIR} ALL COMMAND echo Targets in directory ${THIS_DIR} are built Debug)
See add_custom_target.
The solution I came with is as follows:
I created a macro that will read a config file in the binary directory and set the compilation type based on its content.
When this file changes, cMake will rerun on that directory only and it will recompile it also, because its dependencies changed. This is exactly what I needed.
The macro follows:
macro(set_buildmode _local_app_name)
if(NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE ${CMAKE_BUILD_TYPE})
message(" *** Creating file 'BUILDMODE' with type '${CMAKE_BUILD_TYPE}' for '${_local_app_name}'")
endif()
configure_file( ${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE
${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE.junk)
file(READ ${CMAKE_CURRENT_BINARY_DIR}/BUILDMODE CMAKE_BUILD_TYPE)
string(STRIP ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
endmacro()
To make sure I always know if I have compiled in any other mode other than release, I also have this macro:
macro(add_buildmode_message _local_app_name_full)
get_filename_component(_local_app_name ${_local_app_name_full} NAME)
add_custom_target(Checking_BUILDMODE_for_${_local_app_name} ALL
COMMAND bash -c "if [ \"${CMAKE_BUILD_TYPE}\" != \"Release\" ] ; then echo -e \" ${BoldRed}${_local_app_name} was built with type '${CMAKE_BUILD_TYPE}'${ColourReset}\" ; fi"
VERBATIM)
endmacro()
Here is an example of its usage:
set(appname example)
###### Call set_buildmode here
set_buildmode(${appname})
set(${appname}_SRCS
example.cpp
main.cpp)
set(${appname}_HDRS
example.h)
set(${appname}_LIBS)
set(BUILD_SHARED_LIBS ON)
execute_process(COMMAND ln -frs ${${appname}_HDRS} ${CMAKE_BINARY_DIR}/include/
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set(MYLIB_VERSION_MAJOR 2)
set(MYLIB_VERSION_MINOR 1)
set(MYLIB_VERSION_PATCH 0)
set(MYLIB_VERSION_STRING
${MYLIB_VERSION_MAJOR}.${MYLIB_VERSION_MINOR}.${MYLIB_VERSION_PATCH})
add_library(${appname} ${${appname}_SRCS})
target_link_libraries (${appname} ${${appname}_LIBS} ${CMAKE_THREAD_LIBS_INIT})
##### Add the fake target for checking the build mode
add_buildmode_message(${appname})
set_target_properties(${appname} PROPERTIES VERSION ${MYLIB_VERSION_STRING}
SOVERSION ${MYLIB_VERSION_MAJOR})
install(TARGETS ${appname} DESTINATION lib)

CMake: adding custom resources to build directory

I am making a small program which requires an image file foo.bmp to run
so i can compile the program but to run it, i have to copy foo.bmp to 'build' subdirectory manually
what command should i use in CMakeLists.txt to automatically add foo.bmp to build subdirectory as the program compiles?
In case of this might help, I tried another solution using file command. There is the option COPY that simply copy a file or directory from source to dest.
Like this:
FILE(COPY yourImg.png DESTINATION "${CMAKE_BINARY_DIR}")
Relative path also works for destination (You can simply use . for instance)
Doc reference: https://cmake.org/cmake/help/v3.0/command/file.html
To do that you should use add_custom_command to generate build rules for file you needs in the build directory. Then add dependencies from your targets to those files: CMake only build something if it's needed by a target.
You should also make sure to only copy files if you're not building from the source directory.
Something like this:
project(foo)
cmake_minimum_required(VERSION 2.8)
# we don't want to copy if we're building in the source dir
if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
# list of files for which we add a copy rule
set(data_SHADOW yourimg.png)
foreach(item IN LISTS data_SHADOW)
message(STATUS ${item})
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${item}"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${item}" "${CMAKE_CURRENT_BINARY_DIR}/${item}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${item}"
)
endforeach()
endif()
# files are only copied if a target depends on them
add_custom_target(data-target ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/yourimg.png")
In this case I'm using a "ALL" custom target with a dependency on the yourimg.png file to force the copy, but you can also add dependency from one of your existing targets.

Create a CMake project from a directory of source and header files

Is there something equivalent to qmake -project that will automatically create a CMake project from a directory of source and header files?
Ideally, this should work recursively.
No, but it's an easy project to set up:
project(myProject)
enable_language(CXX)
file(GLOB SRC_FILES *.cpp)
include_directories(${PROJECT_SOURCE_DIR})
add_executable(myExe ${SRC_FILES})
Assuming you are making an executable. add_library should be used if you are making a library. And just change the paths if your project things in subdirectories like src and include.
(I know this was asked quite a while ago, but I'll post my answer anyway, for future reference.)
I think the better idea is not to have a CMakeLists.txt that automatically adds all sources with a glob every time it is run, but a static one instead. I guess that what was originally meant was a script that scans the current directory (recursively) for source files and adds them to the CMake file. This could possibly save a lot of time copying the name of every source file to the CMake file. Therefore: Let's automate it!
Create a file named cmake-project.sh with the following content:
#!/bin/bash
# use the first argument as project name
PROJECT_NAME=$1
# find source files, but exclude the build directory
PROJECT_SOURCES=$(find . -iname "*.cpp" -not -path "./build/*")
# find header files, but exclude the build directory;
# only print the name of the directory; only print unique names
PROJECT_SOURCE_DIR=$(find . -iname "*.h" -not -path "./build/*" \
-printf "%h\n" | sort -u)
# The standard content of the CMakeLists.txt can be edited here
cat << EOF > CMakeLists.txt
cmake_minimum_required (VERSION 2.8)
set(PROJ_NAME $PROJECT_NAME )
set(PROJ_SOURCES $PROJECT_SOURCES )
project(\${PROJ_NAME})
include_directories(${PROJECT_SOURCE_DIR})
add_executable(\${PROJ_NAME} \${PROJ_SOURCES})
EOF
Then, make the file executable with chmod +x cmake-project.sh. Now you can run ./cmake-project.sh [your_project_name] in the root directory to create a static (i.e. without glob) CMakeLists.txt automatically.
Of course you'll have to adapt things if necessary (e.g. use .cc instead of .cpp), but you get the idea.

CMake Compiling Generated Files

I have a list of files that get generated during the CMake build process. I want to compile these files using "add_library" afterward, but I won't know which files get generated until after they get generated. Is there anyway to build this into a CMake script?
Well, I think it is possible, so I'll share what I've done. My problem was that I had to compile several CORBA idls to use as part of a project's source and I didn't want to manually list every file. I thought it would be better to find the files. So I did it like this:
file(GLOB IDLS "idls/*.idl")
set(ACE_ROOT ${CMAKE_FIND_ROOT_PATH}/ace/ACE-${ACE_VERSION})
foreach(GENERATE_IDL ${IDLS})
get_filename_component(IDLNAME ${GENERATE_IDL} NAME_WE)
set(OUT_NAME ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/${IDLNAME})
list(APPEND IDL_COMPILED_FILES ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp)
add_custom_command(OUTPUT ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp
COMMAND ${ACE_ROOT}/bin/tao_idl -g ${ACE_ROOT}/bin/ace_gperf -Sci -Ssi -Wb,export_macro=TAO_Export -Wb,export_include=${ACE_ROOT}/include/tao/TAO_Export.h -Wb,pre_include=${ACE_ROOT}/include/ace/pre.h -Wb,post_include=${ACE_ROOT}/include/ace/post.h -I${ACE_ROOT}/include/tao -I${CMAKE_CURRENT_SOURCE_DIR} ${GENERATE_IDL} -o ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/
COMMENT "Compiling ${GENERATE_IDL}")
endforeach(GENERATE_IDL)
set_source_files_properties(${IDL_COMPILED_FILES}
PROPERTIES GENERATED TRUE)
set(TARGET_NAME ${PROJECT_NAME}${DEBUG_SUFFIX})
add_executable(
${TARGET_NAME}
${SOURCE}
${IDL_COMPILED_FILES}
)
The GENERATED properties is useful in case one of my idl compilation outputs (*C.cpp, *C.h, *S.cpp and *S.h) is not created, so that the build command doesn't complain that the file doesn't exist.
Well, it is possible to do so with CMake's CMAKE_CONFIGURE_DEPENDS directory property. This forces CMake to reconfigure if any of the given files changed.
Simple solution
The following code shows the approach for a single model file, that is used as input for the code generation:
set(MODEL_FILE your_model_file)
set_directory_properties(PROPERTIES CMAKE_CONFIGURE_DEPENDS ${MODEL_FILE})
set(GENERATED_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE})
file(REMOVE_RECURSE ${GENERATED_SOURCE_DIR})
file(MAKE_DIRECTORY ${GENERATED_SOURCE_DIR})
execute_process(COMMAND your_code_generation_tool -o ${GENERATED_SOURCE_DIR} ${MODEL_FILE})
file(GLOB LIBGENERATED_FILES ${GENERATED_SOURCE_DIR}/*)
add_library(libgenerated ${LIBGENERATED_FILES})
target_include_directories(libgenerated ${GENERATED_SOURCE_DIR})
With the above approach, each time the model file has changed CMake will reconfigure which results in the model being regenerated.
Advanced solution
The problem with the simple solution is that even for the smallest possible change in the model the entire dependencies of the generated files have to be rebuilt.
The advanced approach uses CMake's copy_if_different feature to let only generated files that are affected by the model change to appear modified which results in better build times. To achieve that we use a staging directory as destination for the generator and sync the contents subsequently with the generator output of the previous compile run:
set(MODEL_FILE your_model_file)
set(GENERATOR_STAGING_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE}.staging)
set(GENERATOR_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE})
set_directory_properties(PROPERTIES CMAKE_CONFIGURE_DEPENDS ${MODEL_FILE})
# Create fresh staging/final output directory
file(REMOVE_RECURSE ${GENERATOR_STAGING_DIR})
file(MAKE_DIRECTORY ${GENERATOR_STAGING_DIR})
file(MAKE_DIRECTORY ${GENERATOR_OUTPUT_DIR})
# Run code generation
execute_process(COMMAND your_code_generation_tool -o ${GENERATOR_STAGING_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_FILE}")
# Remove stale files from final generator output directory
file(GLOB GENERATED_FILES RELATIVE "${GENERATOR_OUTPUT_DIR}/" "${GENERATOR_OUTPUT_DIR}/*")
foreach(FILE ${GENERATED_FILES})
if(NOT EXISTS "${GENERATOR_STAGING_DIR}/${FILE}")
file(REMOVE "${GENERATOR_OUTPUT_DIR}/${FILE}")
endif()
endforeach()
# Copy modified files from staging to final generator output directory
file(GLOB GENERATED_FILES RELATIVE "${GENERATOR_STAGING_DIR}/" "${GENERATOR_STAGING_DIR}/*")
foreach(FILE ${GENERATED_FILES})
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GENERATOR_STAGING_DIR}/${FILE}" "${GENERATOR_OUTPUT_DIR}")
endforeach()
file(GLOB LIBGENERATED_FILES "${GENERATOR_OUTPUT_DIR}/*")
add_library(libgenerated ${LIBGENERATED_FILES})
target_include_directories(libgenerated PUBLIC ${GENERATOR_OUTPUT_DIR})
If you don't know the name of the files that will be generated, you can "glob" the folders where they reside.
file( GLOB_RECURSE MY_SRC dest_folder/*.cpp )
add_library( libname SHARED ${MY_SRC} )
Now I'm not sure what triggers the generation of these files. The "globbing" will happen only when you manually run cmake: it will not be able to detect automatically that new files are present.
Treat this as a non-answer, just more info:
I recently had to do something for one case where I had a .cpp file that was auto-generated, but I could not figure out how to get CMake to construct the Visual Studio project file that would then compile it. I had to resort to something quite stinky: I had to #include <the_generated.cpp> file from another file that resided under the ${CMAKE_CURRENT_SOURCE} directory. That won't help you much in your case because I suspect you have several .cpp files, so this approach is not scalable.
Also, I found that the GENERATED source file property, when added to the file, did not help at all.
I consider this condition either a bug in Visual Studio (in my case this was VS2008 SP1), or in how CMake generates the .vcproj files, or both.