CMAKE: find a shared(module) library - cmake

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?

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.

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.

CMake: Is there a way to get a list of imported targets that belong to a package

Sometimes I wish I could get a list of the imported targets that belong to a package. Is there a variable that holds them?
This would allow me to write something like this
find_package(Qt5 CONFIG REQUIRED)
message("Imported Qt5 targets: ${Qt5_IMPORTED_TARGETS}") # speculative code
With my current knowledge I have to rely on the documentation of the package to give me the names of all imported targets. Reading them from a variable or property would be easier.
CMake 3.21 introduced the directory property IMPORTED_TARGETS that can be used to get a list of all imported targets. This can be used to derive a list of targets that were imported by a single find_package() call when it is queried before and after the call to find_package(). The code could look something like this:
...
get_property(importTargets DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
get_property(importTargetsAfter DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)
list(REMOVE_ITEM importTargetsAfter ${importTargets})
message("${importTargetsAfter}")
...
Usually it is good enough to only print the list of all imported targets and guess from the names which of them were imported by the package of interest.
Not precisely what you asked for, but for Qt5, one can do:
cmake_minimum_required(VERSION 3.14)
project(so)
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
get_cmake_property(_variableNames VARIABLES)
foreach(_variableName ${_variableNames})
if(_variableName MATCHES "^Qt5.*LIBRARIES")
message(STATUS "${_variableName}")
message(STATUS "\t${${_variableName}}")
endif()
endforeach()
Example output:
-- Qt5Core_LIBRARIES
-- Qt5::Core
-- Qt5Gui_EGL_LIBRARIES
-- Qt5::Gui_EGL
-- Qt5Gui_LIBRARIES
-- Qt5::Gui
-- Qt5Gui_OPENGL_LIBRARIES
-- Qt5::Gui_GL
-- Qt5Widgets_LIBRARIES
-- Qt5::Widgets
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
Caveat with approach: One needs to know the component names.

CMake: collecting libraries

I am using CMake to build a simple C++ project, hereafter named P. The structure of P is quite simple:
P/src/
P/src/package1
P/src/packege2
P/src/...
P/src/main-app
I would like to collect the libraries in package1, package2, ... in a variable called P_LIBS.
In a first attempt, I tried to collect the libraries available in package1, package2, ... in the variable called P_LIBS initially set in the src/CMakeLists.txt file. However, the updates to P_LIBS made in the CMakeLists.txt of the subfolders are not propagated to the parent folder.
I would rather not write a list of libraries in the main CMakeLists.txt file. I would rather modify such variable while moving in the directory tree.
After a search on the internet I could not find any valid suggestion. If I look at the various Find files, I only see long listings of libraries in their main CMakeLists.txt file.
Is there a way to do what (I hope) I explained above?
Thanks to sakra's link I was able to 'propagate' names up to the parent folder. However, the names I add to the P_LIBS variable are later interpreted as 'library' names, not as reference to CMake targets. In other words, if
P_LIBS = {a, b}
the 'a' and 'b' are interpreted as the library names, i.e. CMake generates:
gcc [...] -l a -o exe
instead of
gcc [...] /path/to/a.o -o exe
(.o or other extensions)
You are propably constructing the targets list as a string, try to make them a list instead. For example:
# in package1/CMakeLists.txt
set(P_LIBS ${P_LIBS} a b PARENT_SCOPE)

Removing files from a set

I have a directory with files that either belong to a set that makes up a Qt project, and other files that do not. That is, files A.cxx, ADriver.cxx and A.ui all belong to a set that needs to be compiled with Qt options. I then have a file B.cxx that is non-qt. Then C.cxx, CDriver, and C.ui are another Qt set. There are tens of these, so I want to use globs rather than write each add_executable manually. I was thinking of doing something like
for(all ui files)
create an executable from the ui and its matching .cxx and *Driver.cxx"
end
Then all cxx files that "remain" (not used in the above loop) are non-Qt, and need to be compiled by themselves. My question is how to "subtract" files from a "set". That is, to use the method described above I'd have to have a set of all cxx files, and remove the ones that get used in the .ui file loop. Is this possible? Is there a better way to do something like this?
First, gather all files with a glob:
file(GLOB ALL_SRCS *)
Then select ui files and create Qt targets for them, substracting them from the ALL_SRCS list at the same time:
file(GLOB UIS *.ui)
foreach(ui ${UIS})
get_filename_component(f ${ui} NAME_WE)
# Do Qt stuff
qt4_wrap_ui( ${f}uis ${ui} )
qt4_wrap_cpp( ${f}srcs ${f}.cpp ${f}Driver.cpp )
add_executable( ${f} ${f}uis ${f}srcs )
list(REMOVE_ITEM ALL_SRCS ${ui} ${f}.cpp ${f}Driver.cpp)
endforeach()
After this you'll have all non-qt sources in ALL_SRCS.