CMake INTERFACE_LINK_LIBRARIES entries get decorated - cmake

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.

Related

How to include target include directories transitive through multiple linked libraries

we are working on an embedded project in C/C++ and currently some special needs appeared. Background is there are two compiled libraries which define the same symbols. The compiler allows to create relocatable output modules (with partial linking) and to hide symbols for other compilation units when linking. This also means the output module does not need to have all the symbols defined, this will be done in the final linking. Compiler used is TI LTS1.3.0. I will link directly to the relocatable-section of the manual: https://software-dl.ti.com/codegen/docs/tiarmclang/rel1_3_0_LTS/compiler_manual/linker_description/04_linker_options/linker-output-options.html#stdz0756429
The other part of the project is hardly built on CMake with static libraries which are linked against each other via target_link_libraries.
To get this working I created an "add_executable"-target for each of those both output modules with the same symbols. To those I pass the static-libraries by CMake and get the linked with target_link_libraries.
But now I have a problem. All contents of the static libraries are compiled in each of those output modules. This is unwanted behaviour since as said the final linking does the job of linking the missing stuff - so the static-libraries - to it. This should be done with another add_executable command via CMake as well.
using the target include directories property is not suitable since it only adds the include directories of the given target itself but not of the target the target will include and link against.
So e.g. if you have (pseudo code):
#library A
function( create_libA )
add_library( libA src/A.c )
target_include_directories( libA PUBLIC /inc ) #contains A.h
endfunction()
#library B. different location
function( create_libB LIBA )
add_library( libB src/B.c )
target_link_libraries( libB PUBLIC ${LIBA} )
target_include_directories( libB PUBLIC /inc ) #contains B.h
endfunction()
#target output module with partial linking. Only should link and compile LIBTOBELINKEDIN, not libB. different location.
function( build_part_module LIBB LIBTOBELINKEDIN )
add_executable( outputModuleA src/func.c ) #func.c does include A.h
#following would cause libA and libB also to be compiled and linked in the output due to transitive stuff as I understood, which is unwanted.
target_link_libraries( outputModuleA PUBLIC ${LIBB} ${LIBTOBELINKEDIN} )
#trying this
get_target_property(libBInc ${LIBB} INTERFACE_INCLUDE_DIRECTORIES)
#will only include B.h but not A.h. compilation will fail.
target_include_directories(outputModuleA /inc ${libBInc})
I did not find any solution in Cmake itself to solve this problem. It's confusing me since all the include-directories must be known when the libraries are passed transitive, which is stated in the documentation. But I understand that getting the target include directories of just the passed lib does not include the other ones.
Since target_link_libraries does also not work this way I can only think of a maybe recursive solution? But for that my knowledge is just non-existent.
target_link_libraries with something like HEADERS_ONLY would be helpfull for this job.
Also one can say: if the output module contains all the definitions it won't be a problem, since the linker then knows them and will do its magic.
But this is also unwanted, since we use the generated static-libraries to place them into sections in different regions of the RAM directly. This would then mean to create another linker-script for partial linking which defines sections which then can be again moved. But the more we go this direction, the less we need CMake for it.
Instead of get_target_property use $<TARGET_PROPERTY> generator expression: the property's value, extracted by that expression, already includes transitive propagation:
target_include_directories(outputModuleA PRIVATE
$<TARGET_PROPERTY:libB,INTERFACE_INCLUDE_DIRECTORIES>
)
Note, that generator expressions has limited usage: not all functions expects them. Documentation for target_include_directories clearly states that the command supports generator expressions.

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: find a shared(module) library

Why this statement works if mylib is a static library and does not work if it's a dynamic(shared) one:
find_library(_found mylib PATHS ${mydirs})
where mydir keeps a list of directories, containing libraries (static and shared).
I'd like to find a dynamic library in the same manner as it is done for the static libraries (in order to get it close to the executable). There would be a list of directories but for simplicity sake, first this must be made working with a trivial test case:
cmake_minimum_required(VERSION 3.21)
project(myproj)
add_executable(myproj main.cpp)
find_library(a a${CMAKE_STATIC_LIBRARY_SUFFIX} PATHS "c:/work/libs/debug")
find_library(b b${CMAKE_SHARED_LIBRARY_SUFFIX} PATHS "c:/work/libs/debug")
message("dll=" "${CMAKE_SHARED_LIBRARY_SUFFIX}")
message("dll=" "${CMAKE_STATIC_LIBRARY_SUFFIX}")
message("a=" "${a}")
message("b=" "${b}")
And, the directory structure is as this:
c:/work (CMakeLists.txt_main.cpp)
\_libs
\_debug (a.lib b.dll)
\_release (a.lib b.dll)
The output:
1>a=C:/work/libs/debug/a.lib
1>b=b-NOTFOUND
1>dll=.dll
1>dll=.lib
Anything?

How to control the order that CMAKE builds subdirectories when involving options?

I know that CMake automatically somehow figures out the dependency tree. It will build things in the right order normally.
I decided to use options and that has caused it to guess poorly.
I have a variant of this SO question following:
/CMakeLists.txt
/src/CMakeLists.txt
/testpackage1/CMakeLists.txt
/testpackage2/CMakeLists.txt
In my case, mylib has two kinds -- a static and a dynamic.
OPTION(MYLIB_BUILD_STATIC "Build static library" TRUE)
IF(MYLIB_BUILD_STATIC)
ADD_LIBRARY(mylib_static STATIC ${MyLib_SRCS})
...
SET_TARGET_PROPERTIES(mylib_static PROPERTIES OUTPUT_NAME "mylib")
SET(MYLIB_LIB_TARGET mylib_static)
ENDIF(MYLIB_BUILD_STATIC)
OPTION(MYLIB_BUILD_SHARED "Build shared library" TRUE)
IF(MYLIB_BUILD_SHARED)
ADD_LIBRARY(mylib SHARED ${MyLib_SRCS})
...
SET(MYLIB_LIB_TARGET mylib)
ENDIF(MYLIB_BUILD_SHARED)
At this point ${MYLIB_LIB_TARGET} has either mylib or mylib_static in it
In the unit test packages....
OPTION(MYLIB_BUILD_TEST "Build test program." TRUE)
IF(MYLIB_BUILD_TEST)
INCLUDE_DIRECTORIES(${MYLIB_SOURCE_DIR})
ADD_EXECUTABLE(test_mylib main.c)
IF(NOT MYLIB_LIB_TARGET)
MESSAGE(FATAL_ERROR "MYLIB_LIB_TARGET is not specified")
ENDIF()
TARGET_LINK_LIBRARIES(test_mylib ${MYLIB_LIB_TARGET})
ENDIF(MYLIB_BUILD_TEST)
In the parent:
ADD_SUBDIRECTORY("src")
ADD_SUBDIRECTORY("testpackage1")
ADD_SUBDIRECTORY("testpackage2")
What I get is: MYLIB_LIB_TARGET is not specified from the unit test project
I think it's because the options are messing with the automatic dependency tree logic in CMake.

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)