CMake unexpected transitive private dependency in shared library - cmake

I have been trying to figure this out for a few days now based on the various existing SO questions & cmake mailing list archives. However, I can't quite put my finger on it.
I want to build a library named libA which will be a shared library that gets consumed by another executable. libA has many internal dependencies (libB, libC, libD, ...) which are built/present either as static libraries or cmake's object libraries. libA is supposed to contain all the code in order to be a self-consistent shared library that an application can link against without the need to propagate the internal dependencies.
libA is being built like this:
add_library(libA-objs OBJECT ${HEADERS_PUBLIC} ${HEADERS_PRIVATE} ${SOURCES_PRIVATE})
target_compile_options(libA-objs
PRIVATE
-o1
-Wall -Wextra -pedantic
-Wl,--whole-archive
)
target_link_libraries(libA-objs
PRIVATE
libB
libC
libD
)
...
# Build static library
add_library(libA-static STATIC)
target_link_libraries(libA-static PUBLIC libA-objs)
# Build shared library
add_library(libA-shared SHARED)
target_link_libraries(libA-shared PUBLIC libA-objs)
I am properly installing libA on the system. I also have everything working to export libA in a way that a consuming application can just use find_package().
The application consuming libA-shared is a shared library itself (a plugin for a 3rd-party application):
find_package(libA REQUIRED)
add_library(myplugin SHARED)
target_link_libraries(myplugin
PRIVATE
libA::libA-shared
)
Upon trying to build, the linker complains about not being able to find/resolve/link libB, libC and libD.
I checked the targets configuration file generated by cmake when installing the exported targets (the scripts that a consuming application pulls in via find_package()) and I see that libA-shared contains references to libB, libC and libD in the INTERFACE_LINK_LIBRARIES as LINK_ONLY.
Given that libA-objs links to the internal dependencies as PRIVATE and given that I am building a shared library (executable!) together with including the whole dependency archives I would have assumed for this to just work.
I do not understand why the internal dependencies show up in the linking commands of a consuming executable as they are marked as PRIVATE within the libA-objs target.
How do I solve this?
I am using CMake 3.15, GCC 9.2 and would like this to also work with clang 8.0.

Related

can Cmake pkg-config module say if the library is static or not

I have a program, built with cmake, that (optionally) depends on a library (openwsman), which itself depends on some libraries (libcurl, libssl, libxml-2.0 ...)
pkg_check_modules(winrm openwsman++ openwsman)
IF (${winrm_FOUND})
include_directories(${winrm_INCLUDE_DIRS})
add_definitions(-DHAVE_WINRM=openwsman)
target_sources(launch PRIVATE src/exec_impl_winrm_openwsman.cpp)
target_link_libraries(Tests PRIVATE ${winrm_LIBRARIES})
ENDIF()
If the openwsman library provides shared objects, the link runs fine, and the binary depends on libs the libwsman*.so depends on
But if only static libraries is provided, the linker must have access to libs used by the said static libraries (libcurl, libssl, libcrypto, libxml-2.0)
Is there a way, in cmake, to know the libraries provided by ${winrm_LIBRARIES} are the static ones ?
(BTW: openwsman does NOT provide "static" link options in its .pc file, like libcurl does)

CMake building multiple libraries in the same project

I have a specific CMake project I would like to build as both a static lib and a shared object (Linux only). The relevant part of the CMake file:
...
# Static lib
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
# Build shared object in Linux only
if(UNIX)
set_target_properties(${PROJECT_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_library("${PROJECT_NAME}_SHARED" SHARED ${SOURCES} ${HEADERS})
endif(UNIX)
link_directories(${ILI_EXTERNAL_LIB_DIRS})
target_link_libraries(${PROJECT_NAME} ${ILI_EXTERNAL_LIBS} IliLib PlantModel)
I can figure out why the shared object is not being built, when I add a message within the Unix block, it gets printed during the make build. The rest of this cmake file only sets variables and declares the project name.
Any tips for how to go about debugging this issue? On both Windows and Linux, it only builds the static lib at present.
Edit:
Link to CMake and Make log outputs.
The solution was suggested by #Tsyvarev, trivial error on my part.
The static lib was being built because it was a dependency of another build target. I had not explicitly run "Make" on the SimulatorLib project.
CMake messages appear in the CMake log irrespective of whether that particular project is being built.

CMake build and install shared library from subdirectory before building main directory

Here is my source code structure:
cd my_git_repo/
CMakeLists.txt
src/
main.cpp
mylibrary/
a.hpp
b.hpp
a.cpp
b.cpp
CMakeLists.txt
Root CMakeLists.txt:
cmake_minimum_required(VERSION 3.9)
project(myexe CXX)
add_subdirectory(src/mylibrary)
find_library(mylib NAMES mylibrary.so PATHS "./src/mylibrary/mylibrary.so")
add_executable(myexe src/main.cpp)
target_link_libraries(myexe ${mylib})
mylibrary/CMakeLists.txt is very simple. It builds a shared library and installs them.
Ideally, mylibrary target should be built and installed before myexe is built. But this doesn't happen. mylibrary is built followed by myexe. Installation happens later. Because of this, find_library fails. pkg_check_modules() works for other shared libraries but fails here because of the same reason.
I appreciate your help.
Edit:
This question differs from the duplicate because the answers posted to that question seem to be statically linking the library target_link_libraries(game engine). I want to dynamically link the .so library.
The idea in CMake is to build modules and then link them together.
You haven't shared the CMakeLists.txt for my library, so we cannot tell what it is doing. However, assuming that it is something like:
ADD_LIBRARY(mylibrary
file1.cpp
file2.cpp
)
Since you specified that you want mylibrary to always be linked as shared, you need to tell CMake that as well by either setting BUILD_SHARED_LIBS TO ON or by specifying SHARED in add_library:
ADD_LIBRARY(mylibrary SHARED
file1.cpp
file2.cpp
)
This is your library module. We will keep it simple for now and not worry about packing the library archive and installation here.
Now, back to your main CMakeLists.txt and how to make myexe consume it. Since you have already add_subdirectory(src/mylibrary), CMake knows about mylibrary. So simply link it using the module name. There is no need to find_library as you have already defined the module.
add_executable(myexe src/main.cpp)
target_link_libraries(myexe mylibrary)
This should suffice.
Do note, however, this is a very basic example to explain to you how CMake is designed to work. If you aren't building the library, and it is already installed, you would call find_library. Modern CMake is a bit more sophisticated and uses generator expressions, so be sure to read up on that as you progress to more complex projects.

cmake - two targets that depends on single static library that should be compiled based on the target that is being built

I have a static library and two target executables, let's call them libA, EXE1, EXE2.
libA has pre-processor macros which needs to be enabled or disabled and another static library which needs to be linked or ignored based on the target executable that I am building.
Let's say, if I am building EXE1. Then I need to enable the macros in libA and link another static library to it.
If I am building EXE2, I need to disabled the macros in libA and don't link to another library.
I am confused on how to solve this issue. Please kindly help in solving this issue.
You can make use of an interface library as follows:
cmake_minimum_required(VERSION 3.10)
project(test)
add_library(libA INTERFACE)
target_sources(libA INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/liba.c)
add_executable(exe1 exe1.c)
target_link_libraries(exe1 libA)
target_compile_definitions(exe1 PUBLIC -DENABLE_THE_MACROS)
add_executable(exe2 exe2.c)
target_link_libraries(exe2 libA libOtherStatic)
target_compile_definitions(exe1 PUBLIC -DDISABLE_THE_MACROS)
libA is a "virtual" target that does not produce any output, but it can be linked to other targets (here exe1 and exe2)
Any target that links to libA will automatically receive the sources of libA as well. Note that I had to make the path absolute to prevent a warning.

Linking Shared library (which has dependency on other shared library) in CMake

I am trying to build an executable which links to a shared library (named 'caffe'). The shared library is dependent on another shared library (named 'cblas'). When I try to link to caffe in my CMake file it shows the following error:
libcblas.so.3, needed by libcaffe.so, not found (try using -rpath or
-rpath-link)
I am using the following statements in my CMakeLists.txt:
link_directories(${BINARIES}/lib)
add_library(CAFFE_LIBRARY SHARED IMPORTED)
set_target_properties(CAFFE_LIBRARY PROPERTIES IMPORTED_LOCATION ${BINARIES}/lib/libcaffe.so)
target_link_libraries(${PROJECT_NAME} CAFFE_LIBRARY)
Both 'cblas' and 'caffe' libraries are present in ${BINARIES}/lib folder.
Do I need to add cblas.so to target_link_libraries also? Also, i am not building caffe.so so building it via CMake and keeping it as a dependency is not an option
Is there any other feasible solution for the same problem where dependency tree of shared library needs to be resolved while linking?
Browsing through the library's GitHub tree, it seems to me that it provides a package config file. Therefore, if you have installed it in the normal way, you should be able to find it as a package, instead of defining the imported target yourself:
find_package(Caffe)
include_directories(${Caffe_INCLUDE_DIRS})
add_definitions(${Caffe_DEFINITIONS}) # ex. -DCPU_ONLY
add_executable(caffeinated_application main.cpp)
target_link_libraries(caffeinated_application ${Caffe_LIBRARIES})
The example above comes from the Caffe documentation on the topic.