Are targets appended? - cmake

I'm not entirely clear if calling, for instance, target_link_libraries() more than once for the same target will append the target's dependencies. For example, can I add options for the target main as follows?
option(ASSERT "Evaluate assertions" ON)
find_package(MyLib REQUIRED)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE MyLib::MyLib)
if (ASSERT)
target_link_libraries(main PRIVATE MyLib::enable_assert)
endif()

Yes, from the target_link_libraries() documentation:
Repeated calls for the same <target> append items in the order called.
So in your example, this:
target_link_libraries(main PRIVATE MyLib::MyLib)
...
target_link_libraries(main PRIVATE MyLib::enable_assert)
is equivalent to this:
target_link_libraries(main PRIVATE MyLib::MyLib MyLib::enable_assert)
If you are using a simple boolean check to determine whether or not to include MyLib::enable_assert as an argument, you can use a conditional generator expression instead to simplify this logic:
target_link_libraries(main PRIVATE
MyLib::MyLib
$<IF:$<BOOL:${ASSERT}>, MyLib::enable_assert, >
)

Related

How to link same set of libraries to multiple targets/executables in cmake?

I have multiple executable/target files in my project structure, and they all use the same libraries. Is there a way to make this more compact? Perhaps something like a for-loop?
set(ALL_LIBS lib1 lib2 lib3) # etc.
add_executable(program1 program1.cpp)
target_link_libraries(program1 PRIVATE ${ALL_LIBS})
add_executable(program2 program2.cpp)
target_link_libraries(program2 PRIVATE ${ALL_LIBS})
add_executable(program3 program3.cpp)
target_link_libraries(program3 PRIVATE ${ALL_LIBS})
add_executable(program4 program4.cpp)
target_link_libraries(program4 PRIVATE ${ALL_LIBS})
I'm looking for a solution that achieves something similar or cleaner than this:
add_executable(program1 program1.cpp)
add_executable(program2 program2.cpp)
add_executable(program3 program3.cpp)
add_executable(program4 program4.cpp)
# somehow get list of target names
foreach(${TARGETS})
# link libraries to each target
target_link_libraries(${TARGET_NAME} PRIVATE ${ALL_LIBS})
endforeach()
You may define your own macro or function which defines an executable like normal add_executable but also links the executable with the common libraries.
set(ALL_LIBS lib1 lib2 lib3) # etc.
# A wrapper around add_executable which links all created executables with libraries
function(add_executable_common name)
# Forward all parameters to add_executable
add_executable(${ARGV})
# Perform additional actions
target_link_libraries(${name} PRIVATE ${ALL_LIBS})
endfunction()
# Created function can be used in the very same manner as add_executable.
add_executable_common(program1 program1.cpp)
add_executable_common(program2 program2.cpp)
add_executable_common(program3 program3.cpp)
add_executable_common(program4 program4.cpp)
CMake variables ARGV and ARGN provide a perfect way to forward parameters of one function/macro to another.
This allows to easily create wrappers to existed functions without needs to parse all parameters needed for the wrapped function.
E.g. while the function add_executable_common, defined above, doesn't parse parameters except the first one, it still can be used for define a STATIC or SHARED library and can be used for define a library with several sources:
add_executable_common(program5 SHARED program5.cpp additional_algos.cpp)
I found an okay solution to reduce line count. I just created a function to accept target name and file paths.
function(add_program_target TARGET_NAME)
add_executable(${TARGET_NAME} ${ARGN})
target_link_libraries(${TARGET_NAME} PRIVATE ${ALL_LIBS})
endfunction()
Then I just add targets like this:
add_program_target(program1 program1.cpp helper.cpp)
add_program_target(program2 program2.cpp)
add_program_target(program2 program3.cpp)
add_program_target(program2 program4.cpp)
# etc.

Cmake generator for all executables

I have several executables:
add_executable(exe1 ${DRIVERS_DIR}/exe1.cpp)
add_executable(exe2 ${DRIVERS_DIR}/exe2.cpp)
add_executable(exe3 ${DRIVERS_DIR}/exe3.cpp)
And I need to add a link library to all of them:
target_link_libraries(exe1 ${LIB_NAME})
target_link_libraries(exe2 ${LIB_NAME})
target_link_libraries(exe3 ${LIB_NAME})
How can I replace three target_link_libraries with a single one with generator expression for exe1, exe2, exe3 ?
with generator expression for exe1, exe2, exe3?
You cannot use a generator expression in the target argument of target_link_libraries, period. It simply is impossible.
How can I replace three target_link_libraries with a single one[?]
You can use a loop:
set(exes exe1 exe2 exe3)
foreach (exe IN LISTS exes)
add_executable("${exe}" "${DRIVERS_DIR}/${exe}.cpp")
target_link_libraries("${exe}" PRIVATE "${LIB_NAME}")
endforeach ()
This looks pretty clean to me.
You cannot use generator expressions here.
It's pretty simple to create a function for creating the executable and linking it though:
#[===[
Usage:
my_create_linked_exe(target source...)
]===]
function(my_create_linked_exe TARGET SRC)
add_executable(${TARGET} ${SRC} ${ARGN})
target_link_libraries(${TARGET} PRIVATE ${LIB_NAME})
endfunction()
my_create_linked_exe(exe1 ${DRIVERS_DIR}/exe1.cpp)
my_create_linked_exe(exe2 ${DRIVERS_DIR}/exe2.cpp)
# helper.cpp only added to demonstrate you could pass more than a single parameter
my_create_linked_exe(exe3 ${DRIVERS_DIR}/exe3.cpp ${DRIVERS_DIR}/helper.cpp)

CMake : target_compile_options() vs target_compile_definitions()

What is the different between target_compile_options() vs target_compile_definitions()?
As per CMake docs:
target_compile_options - Adds options to the COMPILE_OPTIONS or INTERFACE_COMPILE_OPTIONS target properties.
target_compile_definitions - The INTERFACE, PUBLIC and PRIVATE keywords are required to specify the scope of the following arguments. PRIVATE and PUBLIC items will populate the COMPILE_DEFINITIONS property of <target>. PUBLIC and INTERFACE items will populate the INTERFACE_COMPILE_DEFINITIONS property of <target>.
But I am not getting which one to use and when.
Use target_compile_definitions for definitions of preprocessor macros, use target_compile_options for other flags.
For target_compile_definitions cmake is able to choose the appropriate compiler flags based on the compiler used. Furthermore you save yourself the -D:
Example
target_compile_definitions(MyTarget PRIVATE
ADD_DEBUG_PRINTS=1
ABCDE
)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(MyTarget PRIVATE -Wall)
endif()
Note that the use of -Wall usually shouldn't be added in this place; it's just used as an example of a well known compiler flag...

CMake INTERFACE_LINK_LIBRARIES entries get decorated

I recently did some CMake cleanup on a library I have, the main change being that instead of exporting my targets to a config file directly. At the same time I wanted to move adding the target to the toplevel CMakeLists.txt and then just add sources to that single target as I recurse down into subdirectories.
So I have the toplevel CMakelists.txt that contains:
add_library(Foo "")
add_library(Bar::Foo ALIAS Foo)
add_subdirectory(src)
in src the CMakeLists.txt has
add_subdirectory(Buz)
add_subdirectory(Fuz)
in Fuz the CMakeLists.txt has
target_sources(Foo
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/FooSource.hpp>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/FooSource.hpp>
PRIVATE
FooSource.cpp
)
target_include_directories(Foo
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>
)
target_link_libraries(Foo
PUBLIC
Bar::Buz # target from first sub dir add...eventually to be merged in like manner
PRIVATE
avcodec
avformat
avutil
swscale
)
Before the change the resulting FooConfig.cmake that would be installed would contain these lines:
set_target_properties(Foo::Foo PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include/Foo/klv_parser;${_IMPORT_PREFIX}/include/Foo/frame_source"
INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:avcodec>;\$<LINK_ONLY:avformat>;\$<LINK_ONLY:avutil>;\$<LINK_ONLY:swscale>;\$<LINK_ONLY:Foo::foo_common>"
INTERFACE_SOURCES "${_IMPORT_PREFIX}/include/Foo/klv_parser/KlvParser.hpp;${_IMPORT_PREFIX}/include/Foo/frame_source/FrameSource.hpp"
where the LINK_ONLY libraries are ones I listed as PRIVATE in a target_link_libraries. However since the change, these private libraries have been decorated with a seemingly random hex number:
set_target_properties(Foo::Foo PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include/Foo/klv_parser;${_IMPORT_PREFIX}/include/Foo/frame_source"
INTERFACE_LINK_LIBRARIES "\$<LINK_ONLY:avcodec::#<0x2aaeb30>>;\$<LINK_ONLY:avformat::#<0x2aaeb30>>;\$<LINK_ONLY:avutil::#<0x2aaeb30>>;\$<LINK_ONLY:swscale::#<0x2aaeb30>>;\$<LINK_ONLY:Foo::foo_common::#<0x2aaeb30>>"
INTERFACE_SOURCES "${_IMPORT_PREFIX}/include/Foo/klv_parser/KlvParser.hpp;${_IMPORT_PREFIX}/include/Foo/frame_source/FrameSource.hpp"
)
Libraries in the PUBLIC section of target_link_libraries are not decorated--only ones in the PRIVATE section are.
I've been able to narrow it down to moving the add_library command to a higher level directory. i.e. if my CMakeLists.txt in the Fuz directory has:
add_library(Foo "")
add_library(Bar::Foo ALIAS Foo)
target_sources(Foo
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/FooSource.hpp>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/FooSource.hpp>
PRIVATE
FooSource.cpp
)
target_include_directories(Foo
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}>
)
target_link_libraries(Foo
PUBLIC
Bar::Buz # target from first sub dir add...eventually to be merged in like manner
PRIVATE
avcodec
avformat
avutil
swscale
)
(and no add_library commands are in the top level CMakeLists.txt) they all works as it should. But if the only change I make is to move the two lines
add_library(Foo "")
add_library(Bar::Foo ALIAS Foo)
to either the toplevel CMakeLists.txt or even the one in src then this weird decoration occurs. To me this really seems like a bug in CMake, but maybe not?
I'm using CMake 3.15.5 on Ubuntu 18.04. Full minimal example is here: https://github.com/jasonbeach/cmake_bug.git
This appears to be a bug within CMake. I was originally using CMake 3.15.5 and in the course of troubleshooting, updated to CMake 3.19.6 and it started working as it should. It appears to have been fixed somewhere in there.

External library specific COMPILE_DEFINITIONS in cmake

I've written a cmake module for finding QCustomPlot. However, to use the shared library, one needs to #define QCUSTOMPLOT_USE_LIBRARY. I'd like to provide this define through cmake, automatically adding the definition to any project that uses QCustomPlot.
Here is a snippet of my cmake module and my current attempted solution:
SET(QCP_FOUND "NO")
IF(QCP_LIBRARY AND QCP_INCLUDE_DIR)
SET(QCP_FOUND "YES")
SET_PROPERTY(
GLOBAL
APPEND
PROPERTY COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
ENDIF(QCP_LIBRARY AND QCP_INCLUDE_DIR)
However, no linkers append the -DQCUSTOMPLOT_USE_LIBRARY flag in my compilations. What's the right way to approach this problem?
There is no global property COMPILE_DEFINITIONS. But there are such properties for directory, target and source file (see documentation). So probably the closest commands for you are:
set(QCP_FOUND "NO")
if(QCP_LIBRARY AND QCP_INCLUDE_DIR)
set(QCP_FOUND "YES")
set_directory_properties(
PROPERTIES COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
endif()
But I think that this kind of job is for imported targets:
add_library(QCP::qcp UNKNOWN IMPORTED)
set_target_properties(
QCP::qcp
PROPERTIES
IMPORTED_LOCATION "${QCP_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${QCP_INCLUDE_DIR}"
INTERFACE_COMPILE_DEFINITIONS QCUSTOMPLOT_USE_LIBRARY
)
and usage:
add_executable(foo ...)
target_link_libraries(foo PUBLIC QCP::qcp)