Getting a list of tests in CMake - cmake

My CMake project is structured with a submodule that is controlled by a third party. They have a test that is broken, and I'd like to disable it.
I thought this would be something like
add_subdirectory(thirdparty)
set_tests_properties(their_broken_test PROPERTIES DISABLED true)
But not so simple, CMake tells me this test doesn't exist.
So I tried to step back and get a list of all of the tests that CMake does think exists. But this is even more baffling:
project(cmaketest)
cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
enable_testing()
add_test(ls /bin/ls)
get_property(LIST_OF_TESTS DIRECTORY . PROPERTY TESTS)
message(STATUS "Here are the tests: ${LIST_OF_TESTS}")
On CMake 3.10.2, this shows no tests. But on CMake 3.12.1, it shows the ls test.
Is this a bug? Or is there a way to do it with the older CMake? (3.10.2 is what is in the Ubuntu Bionic repos.)

The TESTS property was added in CMake 3.12 (release notes):
A TESTS directory property was added to hold the list of tests defined by the add_test() command.
You can do something like this to collect a list of test names as they're added.
if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
function(add_test)
_add_test(${ARGN})
# There are two signatures to the add_test function to handle
if(ARGV0 STREQUAL "NAME")
set(TEST_NAME "${ARGV1}")
else()
set(TEST_NAME "${ARGV0}")
endif()
list(APPEND ALL_TESTS "${TEST_NAME}")
endfunction()
endif()
However, it does not appear you can set properties on a test defined in another directory.

Related

How do I use find_package in CMakeLists.txt such that if an optional dependency is not installed, I still get a makefile?

My goal is to create a CMakeLists.txt file that builds a suite of programs for the local system. If the conditions for building some programs are not satisfied, I wish to be able to build the rest of the programs. The sticking point seems to be gtk, but bear in mind that I don't think a gtk-specific method is the best choice.
For many modules you'd want to build with, you can use
find_package(PkgConfig REQUIRED)
To set up the pkg-config wrapper pkg_check_Modules and if you don't use REQUIRED you can check the _FOUND variable:
pkg_check_Modules(FT REQUIRED freetype2)
if (FT_FOUND)
message(STATIC "Found freetype2")
#Continue and succeed, using FreeType library
else()
message(STATIC "Didn't find freetype2")
#Continue and succeed, without using FreeType library
endif()
Other libraries don't seem to work with pkg_check_modules so I have resorted to find_package
find_package(GTK2 2.0 QUIET gtk)
if (GTK2_FOUND)
...
endif()
Here's a complete reduction:
cmake_minimum_required(VERSION 3.6.2)
project (test C CXX)
set (SOURCE a.cpp b.cpp)
find_package(AMAZING 2.0 QUIET amazing_testing)
if (AMAZING_FOUND)
list(APPEND SOURCE c.cpp)
else()
list(APPEND SOURCE d.cpp)
endif()
add_executable(test ${SOURCE})
The above allows my whole CMakeLists.txt to proceed to the end, but I get an error, no makefile is created, and build/CMakeFiles/CMakeOutput.log doesn't even mention gtk:
CMake Error at gtk/CMakeLists.txt:4 (find_package):
find_package called with invalid argument "amazing_testing"
## here would be further cmake processing logs from other necessary setup
-- Configuring incomplete, errors occurred!
See also "/home/menright/bit/build/CMakeFiles/CMakeOutput.log".
How can I tweak such a usage of find_package so that if the optional package does not exist, the configure will complete successfully anyway? Alternatively, what should I use instead of find_package that will concisely set up usage of a library the way pkg_check_modules does and yet allow success if the library is not available?

Manually do the work of find_package by specifying the paths ons the command line

A colleague has a project which uses a hand-written Makefile with hard-coded library paths. So for instance the CXXFLAGS and the LDFLAGS are set like the following:
-I/home/personA/libraries/eigen-3.2.7
-I/home/personA/libraries/boost_1_60_0
-I/home/personB/hdf5-1.8.17/include
-L/home/personA/libraries/boost_1_60_0/stage/lib/
-L/home/personB/hdf5-1.8.17/lib
Nobody has direct administrative rights on this machine, so just installing the Debian packages with those libraries will involve nagging the administrator to install them. And even if he does, there might be a different dependency that is not in the repositories.
In my CMake file, I have this:
find_package(HDF5 REQUIRED COMPONENTS C CXX)
include_directories(${HDF5_INCLUDE_DIRS})
find_package(Boost REQUIRED COMPONENTS filesystem system program_options)
find_package(Eigen3 REQUIRED)
include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR})
find_package(OpenMP)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
On my Fedora workstation, this works just fine. On my Ubuntu 14.04 virtual machine, it also works, it also builds on Travis CI. However, this project runs on our compute cluster and the dependencies are in really odd places.
So I would like to invoke cmake in a way that tells it that I already know that include and library flags it needs and not even bother to look for a FindEigen3.cmake file (which is not there).
Is there some way to override the find_package and just specify the paths manually?
You can take advantage of the fact that find_package() does only look for the libraries/include paths until it has found the requested package and stores a successful finding fact in _FOUND variables.
So in your case - taken the Eigen3 example - you can do:
> cmake -D Eigen3_FOUND:BOOL=ON -D EIGEN3_INCLUDE_DIR:PATH=/home/personA/libraries/eigen-3.2.7 ..
Just let the user set the variables that find_package would set manually. Then skip the find_package altogether:
if(NOT DEFINED EIGEN3_INCLUDE_DIRS)
find_package(Eigen3 REQUIRED)
endif()
include_directories(SYSTEM ${EIGEN3_INCLUDE_DIRS})
This has the advantage that one does not even need an FindEigen3.cmake file.

cmake: how to check if preprocessor is defined

I can't get cmake to test if a preprocessor has been defined or not. Eg:
cmake_minimum_required(VERSION 2.8.9)
project (cmake-test)
add_definitions(-DOS=LINUX)
if(NOT <what condition goes here?>)
message(FATAL_ERROR "OS is not defined")
endif()
The following tests don't work:
if (NOT COMMAND OS)
if (NOT DEFINED OS)
if (NOT OS)
I can get it to work by using set() and just testing for a regular cmake variable and then defining the preprocessor macro. Eg:
set(OS LINUX)
if (OS)
add_definitions(-DOS=${OS})
else()
message(FATAL_ERROR "OS is not defined")
endif()
In case, you're wondering why I need to test it if the variable/preprocessor is in the same file, it's because in the final implementation these will come from an external file which is includeed in the main CMakeFile.txt Eg:
include(project_defs.txt)
if (OS)
....
This is to complete the answer by arrowd.
I also tried the COMPILE_DEFINITIONS option as mentioned above by arrowd unsuccessfully.
Following the documentation of CMake, at least for version 3.x, it turns out that when you call add_definitions() in CMake, it adds the definitions to the COMPILE_DEFINITIONS directory property.
Therefore, lets say you are defining the following as per your code:
add_definitions(-DOS=LINUX)
To retrieve the string with the definitions added into the variable "MYDEFS" you can use the following lines in CMake:
get_directory_property(MYDEFS COMPILE_DEFINITIONS)
MESSAGE( STATUS "Compile defs contain: " ${MYDEFS} )
Then you can check if in ${MYDEFS} the define you are looking for exists or not. For instance
if(MYDEFS MATCHES "^OS=" OR MYDEFS MATCHES ";OS=")
MESSAGE( STATUS "OS defined" )
else()
# You can define your OS here if desired
endif()
Normally, all definitions that are passed to the compiler are controlled by CMake. That is, you create a CMake variable with
option(SOMEFEATURE "Feature description" ON)
or
set(OS "" CACHE STRING "Select your OS")
User sets them via cmake -D OS=DOS or in the CMake GUI. Then you can use if() operator to conditionally add_definitions() to the compiler command line.
UPDATE:
If you really want to access preprocessor flags, there is a COMPILE_DEFINITIONS target property. You can access it this way:
get_target_property(OUTVAR target COMPILE_DEFINITIONS)

How to check if generator is a multi-config generator in a CMakeLists.txt

The Cmake FAQ
and
other
places
recommend to check CMAKE_CONFIGURATION_TYPES to recognize a multi-configuration generator. I have found several questions where this did not work (for example this one). The issue seems to be that the variable is not set the first time cmake is called.
I tested with the following file
cmake_minimum_required(VERSION 2.6)
if(CMAKE_CONFIGURATION_TYPES)
message("Multi-configuration generator")
else()
message("Single-configuration generator")
endif()
project(foo)
and called it like this
mkdir build
cd build
cmake -G "Visual Studio 12 2013" ..
and got Single-configuration generator.
How should I distinguish whether the current generator supports multiple configurations?
EDITED: Added information on checking and changing CMAKE_CONFIGURATION_TYPES
Check and Changing CMAKE_CONFIGURATION_TYPES
Taking the suggestions from this question you could check and change CMAKE_CONFIGURATION_TYPES, but be aware that there was a bug 0015577: The 'project' command overwrites CMAKE_CONFIGURATION_TYPES in CMake 3.2.2 that did break this behaviour for the initial VS solution generation (fixed with CMake 3.3.0):
cmake_minimum_required(VERSION 3.3)
project(foo NONE)
if(CMAKE_CONFIGURATION_TYPES)
message("Multi-configuration generator")
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "My multi config types" FORCE)
else()
message("Single-configuration generator")
endif()
enable_language(C CXX)
Preset CMAKE_CONFIGURATION_TYPES
If you just need a certain set of configurations for multi-configuration environments you can do (thanks to #Tsyvarev for the suggestion):
cmake_minimum_required(VERSION 2.8)
# NOTE: Only used in multi-configuration environments
set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "My multi config types" FORCE)
project(foo)
None multi-configuration environments will just ignore it. But be aware that other CMake modules like findBoost.cmake, findCUDA.cmake may rely on CMAKE_CONFIGURATION_TYPES being empty for single-configuration environments (thanks again #Tsyvarev for the hint).
So a better solution would be adding toolchain files for all your supported generators. They are generally useful, because there you can handle all the toolchain/generator specific parts.
Here is an extract of my VSToolchain.txt:
# Reduce the config types to only Debug and Release
SET(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
# Standard is a console app. If you need a windows app, use WIN32 define in add_executable
set(CMAKE_WIN32_EXECUTABLE 0 CACHE INTERNAL "")
CMAKE_WIN32_EXECUTABLE is just there to show what kind of settings I have put in my Visual Studio toolchain file.
Another CMake command line solution is suggested here: How to create cmake build configuration without debug symbols and without optimizations?
Only Checking CMAKE_CONFIGURATION_TYPES
If you only want do check what CMake does set in CMAKE_CONFIGURATION_TYPES:
I just tested your above code with Visual Studio 2013 and MinGW/GCC (both with empty build directories). You just need one small change and move the check after the project() command:
project(foo)
message("CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES}")
if(CMAKE_CONFIGURATION_TYPES)
message("Multi-configuration generator")
else()
message("Single-configuration generator")
endif()
And I get for VS2013:
CMAKE_CONFIGURATION_TYPES Debug;Release;MinSizeRel;RelWithDebInfo
Multi-configuration generator
And for GCC:
CMAKE_CONFIGURATION_TYPES
Single-configuration generator
For more details about what CMake does see:
CMAKE_CONFIGURATION_TYPES set by EnableLanguage() in cmGlobalVisualStudio7Generator.cxx
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
I see you are on CMake v2.6, but for anyone who is on v3.9+, v3.9 introduced the global property called GENERATOR_IS_MULTI_CONFIG:
Read-only property that is true on multi-configuration generators.
You can load the value into a CMake variable like so:
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
This very approach is recommended in "Professional CMake" by Craig Scott, along with explanations of the shortcomings of other approaches- especially those involving CMAKE_CONFIGURATION_TYPES. The book is $30 but the section I'm referring to is in the sample chapters.

Project build configuration in CMake

My question is very similar to CMake : Changing name of Visual Studio and Xcode exectuables depending on configuration in a project generated by CMake. In that post the output file name will change according to the project configuration (Debug, Release and so on). I want to go further. When I know the configuration of the project, I want to tell the executable program to link different library names depending on project configurations. I was wondering whether there is a variable in CMake that can tell the project configuration. If there exists such a variable, my task will become easier:
if (Project_Configure_Name STREQUAL "Debug")
#do some thing
elseif (Project_Configure_Name STREQUAL "Release")
#do some thing
endif()
According to http://cmake.org/cmake/help/v2.8.8/cmake.html#command:target_link_libraries, you can specify libraries according to the configurations, for example:
target_link_libraries(mytarget
debug mydebuglibrary
optimized myreleaselibrary
)
Be careful that the optimized mode means every configuration that is not debug.
Following is a more complicated but more controllable solution:
Assuming you are linking to an imported library (not compiled in your cmake project), you can add it using:
add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION_RELEASE c:/path/to/foo.lib)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION_DEBUG c:/path/to/foo_d.lib)
add_executable(myexe src1.c src2.c)
target_link_libraries(myexe foo)
See http://www.cmake.org/Wiki/CMake/Tutorials/Exporting_and_Importing_Targets for more details.
There is always another way:
if(CMAKE_BUILD_TYPE MATCHES "release")
SET(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE})
else(CMAKE_BUILD_TYPE MATCHES "debug")
SET(CMAKE_BUILD_TYPE "debug")
endif(CMAKE_BUILD_TYPE MATCHES "release")
We can use the variable CMAKE_BUILD_TYPE. We can also change this variable at the beginning of invoking CMAKE:
cmake .. -DCMAKE_BUILD_TYPE:STRING=debug
Then we can use this variable as an indicator of build configuration.