i'm currently working on a large software project which uses cmake as build system. But i have a problem to check if another target exists (or will exist).
For example there is root CMakeLists.txt and two modules that can optionally added to the software project as subfolders.
.
├── A
│ └── CMakeLists.txt
├── B
│ └── CMakeLists.txt
└── CMakeLists.txt
In the root CMakeLists these modules are added with the add_subdirectory command:
cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)
project(root)
add_subdirectory(./A)
add_subdirectory(./B)
In some cases i want to check in module A if module B exists and add an define to the compile options in module A:
cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR)
project(A)
add_libary(A a.cpp a.hpp)
if (TARGET B)
target_compile_definitions(A PUBLIC HAVE_B)
endif()
The
if(TARGET target-name)
command will return false because this will only work if the modules are added in the right order to the root CMakeLists.txt.
Is there another check in cmake that doesn't depend on the order of the targets?
Greetings Perry
This can be solved using a macro.
The idea is that each subdirectory must have an associated top-level variable that other subdirectories can check, so they must be declared on the top CMakeLists.txt file. Also all variables must be defined before the start of the
add_subdirectory
rules.So our macro will take as parameters the list of subdirectories, first output the list of variable declarations, and then output the list of subdirectories.
Here is the code for such a macro:
It can be used like this:
And it will expand into the following code:
This will result into the variables
TARGET_A
,TARGET_B
, andTARGET_C
to be declared and set to yes, and they will be available from all modules.Adding new modules is just a matter of adding new parameters to the
declare_modules
call.Because the root
CMakeLists.txt
file have to know the project's subdirectories, I normally put my optional settings there:CMakeLists.txt:
A/CMakeLists.txt:
B/CMakeLists.txt:
There are a lot of possibile places you could set the compiler definitions and necessary include directories. In this example I've choosen to propagade both with
target_compile_definitions(... PUBLIC ...)
andtarget_include_directories(... PUBLIC ...)
. Then you just have to setup the dependency withtarget_link_libraries()
and CMake handles the rest (andtarget_link_libraries()
does accept forward declarations).There is no available
$<TARGET_EXISTS:...>
generator expression where you could write this:Although I am proposing this to cmake, here. As a workaround, you can create shadow targets to track the targets from other directories:
This creates two functions, and a custom property to track if the target exists or not. The
shadow_notify
will create a shadow target if it doesn't exists and then set theINTERFACE_TARGET_EXISTS
property to true if it does exists.The
shadow_exists
target will create a generator expression to query if the target exists. It does this by creating a new shadow target if doesn't exists with theINTERFACE_TARGET_EXISTS
property set to 0. Then it will create a generator expression that will query theINTERFACE_TARGET_EXISTS
property on the shadow target. The shadow target will always exists, and if the actual target exists then the property will be set to 1 byshadow_notify
.So then you can write this in your cmake:
When you create library
B
you will need to callshadow_notify(B)
as well.If you don't like these extra
shadow_notify
calls, you could also overrideadd_library
to do it for you: