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

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)

Related

CMake unexpected transitive private dependency in shared library

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.

How to model transitive dependencies between static libraries in CMake?

Given an executable myExe, and 2 static libraries myLib1 and myLib2. Given the following dependencies myExe -> myLib1 -> myLib2, how should you model transitive dependency between myLib2 and myLib1?
It seems that the correct way to do it may be:
target_link_libraries(myLib2 myLib1)
But, according to the documentation:
Specify libraries or flags to use when linking a given target and/or its dependents
Also, add_dependencies does not seem to be transitive.
So I find this confusing to use target_link_libraries and I am wondering if there is another "cleaner" way.
For express usage dependency myLib1 -> myLib2 (that is, library myLib1 uses functions defined in myLib2), use
target_link_libraries(myLib2 myLib1)
While target_link_libraries doesn't affect on the file myLib2.a (because static libraries are never linked), an effect will be seen when myLib2 will be linked into shared library or executable:
target_link_libraries(myExe myLib2)
will automatically link myExe with myLib1.
Note again, that such linkage propagation for static libraries works only when myLib2 is used in the same project which calls target_link_libraries(myLib2 myLib1).
Attempt to target_link_libraries(myExe myLib2) from another project will link just with myLib2.a file, which doesn't contain information about myLib2.

Why use add_library({tgt} IMPORTED) versus target_link_libraries( -l {.so | .a})?

What is the purpose of using the statement:
add_library(<tgt> [SHARED|STATIC] IMPORTED)
From what I have found even if you create an imported library target above you still would need to specify the specific location of the actual .so or .a. This would take at least 3 cmake commands to link to an executable and the compiler still would not automatically search through the common include directories on your OS.
Example:
cmake code to link IMPORTED lib
From the CMake documentation I understand there are really 3 ways to link a library that is not built as a target in a subproject of your overall application/library.
CMake target_link_libraries() documentation
Using a CMake package for one of the shipped package scripts.
Using a linker flag:
target_link_libraries(<tgt> [SHARED|STATIC|...] -lncursesw)
Or using the IMPORTED library method (showcased in code at top).
A major difference when using the second method is that it only takes a single line of code and will search through all of your compiler's predefined include directories on you OS. Could anyone help me understand why the add_library() method is used?
Additional Realated SO Posts:
Include directories for IMPORTED libs
CMake imported library behavior
You should use add_library(<tgt> [SHARED|STATIC] IMPORTED) whenever you need to set properties such as dependencies, compile definitions, compile flags etc for <tgt>, and/or by extension, any targets that are linking against <tgt>.
Let's say you have two static libraries; libfoobar.a and libraboof.a, where libfoobar.a requires libraboof.a. Let's also say that these libraries contain some features that are enabled by -DSOME_FEATURE.
add_library(raboof STATIC IMPORTED)
set_target_properties(raboof PROPERTIES
IMPORTED_LOCATION <path-to-libraboof.a>
INTERFACE_COMPILE_DEFINITIONS "SOME_FEATURE"
)
add_library(foobar STATIC IMPORTED)
set_target_properties(foobar PROPERTIES
IMPORTED_LOCATION <path-to-libfoobar.a>
INTERFACE_LINK_LIBRARIES raboof
)
So when you link against libfoobar.a:
add_executable(my_app main.cpp)
target_link_libraries(my_app foobar)
CMake will make sure to link all dependencies in the correct order and will in this case also append -DSOME_FEATURE to the compile flags when you build my_app. Note that since we added libraboof.a as a dependency to libfoobar.a, -DSOME_FEATURE is added to any target that link against libfoobar.a through the transitive property.
If you don't use add_library(<tgt> <SHARED|STATIC> IMPORTED) in a scenario like this, you would have to manage any dependencies and required build options yourself for each target, which is quite error-prone.
This method is also often used in Config-modules for multi-component libraries to manage dependencies between the components.

How to make CMake find real static libraries instead of dynamic wrappers?

I use find_package to include external library into my CMake project. Because I wanted to add support for static linking, I set set(BUILD_SHARED_LIBS FALSE). However, I still get libraries like libglew32.dll.a which are just wrappers that make dynamic linking easier. Instead, I want CMake to find libglew32.a which exists in the same directory. This is the module to find GLEW I use.
You can always link to an exact library using the filename. Here are the flags you would use
-l:[filename]
For cmake
target_link_libraries(target :libglew32.a)
Doing this on linux will use all static libraries
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
if you are building external libraries, usually i just include them in my target
target_link_libraries(myprogram
${LIBROCKET_LIBS_DIRS}/libRocketCore.a
${LIBROCKET_LIBS_DIRS}/libRocketControls.a)

How to build object files only once with cmake?

I have a CMakeLists.txt file that looks like this:
add_executable(exec1 exec1.c source1.c source2.c source3.c)
add_executable(exec2 exec2.c source1.c source2.c source3.c)
add_executable(exec3 exec3.c source1.c source2.c source3.c)
The source1.o source2.o source3.o files take a really long time to build, and since they are common to all the executables, I want each of them to be built only once. The current behavior for cmake, however, is to rebuild them for each exec target separately, which is an unnecessary duplication of effort.
Is there a way to tell cmake to build object files only once?
No. This would be difficult to achieve since the source files could be compiled with different compiler options, post-build steps, etc.
What you can do is to put the object files into a static library and link with that instead:
add_library(mylib STATIC source1.c source2.c)
add_executable(myexe source3.c)
target_link_libraries(myexe mylib)
EDIT: of course, you can put it in a shared library as well.
Yes, in CMake 2.8.8 you can use an object library, which is a kind of virtual library that has the same organizational and dependency properties of a real static or shared library, but does not produce a file on disk. See CMake Tutorials: Object Library.
In recent CMake versions, you can use an object library. It will mostly behave like any libraries but won't archive it into a static library:
add_library(my_cpps OBJECT a.cpp b.cpp c.cpp)
You can then "link" it to other targets:
add_library(my_lib1 d.cpp e.cpp f.cpp)
target_link_libraries(my_lib1 PUBLIC my_cpps)
add_library(my_lib2 f.cpp g.cpp h.cpp)
target_link_libraries(my_lib2 PUBLIC my_cpps)