How to create a wrapper library without exposing the underlying library being used? - cmake

Using Cmake I would like to know how to create a wrapper library and let the users link their application with this library only. Users don't need to specify the original library in their linker flags.
For instance, I create a wrapper library for libwebsockets, named libcustomws.
add_library(customws main.c)
target_link_libraries(customws websockets)
I would like user (with no libwebsockets installed) to be able to do:
add_executable(user_app user_app.c)
target_link_libraries(user_app customws pthread)

Wrapper libraries without any additional code from within your project can best be implemented with small INTERFACE library targets with the IMPORTED tag. Example for your scenario:
add_library(customws INTERFACE IMPORTED)
target_include_directories(customws
INTERFACE
/some/include/path)
target_link_libraries(customws
INTERFACE
websockets)
This way, targets that use this library can just
add_executable(user_app user_app.c)
target_link_libraries(user_app customws pthread)
and get the usage requirements from the target customws, in this case an include directory and a linked library (websockets) are propagated through customws. This can be a good thing, as it might encapsulate implementation details of the dependency (different flags for different platforms etc.).
If you like to automatically link to compiled code (that is part of your project), this can be easily done by adding a small intermediate OBJECT library, e.g.
add_libraray(customwsenhanced
OBJECT
someCode.c)
target_link_library(customwsenhanced
PUBLIC
customws)
Depending on whether someCode.c depends on the usage requirements of customws, the target_link_library for customwsenhanced could also use INTERFACE propagation. Now, a client application can go with
add_executable(user_app user_app.c)
target_link_libraries(user_app customwsenhanced pthread)
and will get both the compiled object code of someCode.c as well as flags etc. from customws.

Related

Linking priority in cmake

Is there any way i can set a priority at linking some libs/classes in cmake?
I have a library BFciLib that i use in my class, and have added it in cmake like this
target_link_libraries(Main PRIVATE BFciLib GL ${GTKMM_LIBRARIES})
I had to overwrite some of the method of that lib so that i am able to develop on my pc (without having to connect my device for which the mentioned lib is used etc).
So i have C wrapped those methods, and basically now i have 2 definitions of the same method.
I need to link my newly created methods to be used in MyClass, instead of the original ones(the ones of the lib) without modifying the class. But still use the library since are more other methods used. So i believe that is to be done in the cmake file.
For that i have added another executable, but i fail to see what more should i do:
#new exe
add_executable(
NewMain
src/main.cpp
src/MyClass.hpp
src/MyClass.cpp
src/ClassWithCWrappers.hpp
src/ClassWithCWrappers.cpp
)
target_link_libraries(NewMain PRIVATE BFciLib GL ${GTKMM_LIBRARIES})
This still returns "duplicate definition" error.
How am i supposed to do?
Don't try to override global functions. Create an interface abstraction that can be implemented with either your device library or your PC alternative. Then have separate device and PC builds where you link in only one implementation each.

How to link a shared library without having to export its internal targets

The following repro cmake project fail to configure. The goal is to create a shared library that internally consists of a couple of static libraries. I want the internal symbols and include paths etc to be exported from the shared library. To do that I make the internal libs PUBLIC link libraries. But then cmake tells me I need to export my target. I don't want to "pollute" my package config with a bunch of internal targets. Is there no way to "merge" internal targets (static libs) into the public exported target (shared lib) or hide it in the config targets file?
The SystemC::systemc imported target does not show up in my target file and it also don't generate any error. I assume that is because this target is already exported in its own package? If so, would that mean I need to make my internal libs packages and so imported targets to make them go away from my exported targets list?
cmake_minimum_required(VERSION 3.16)
project(mylib)
find_package(SystemCLanguage 2.3.3 CONFIG REQUIRED)
add_library(mysublib STATIC mysublib.cpp)
add_library(mylib SHARED mylib.cpp)
target_link_libraries(mylib
PUBLIC SystemC::systemc
PUBLIC mysublib
)
export(
TARGETS mylib
NAMESPACE MyLib::
FILE MyLibTargets.cmake
)
Error generated at configure
CMake Error in CMakeLists.txt:
export called with target "mylib" which requires target "mysublib" that is
not in any export set.
The goal is to create a shared library that internally consists of a couple of static libraries. I want the internal symbols and include paths etc to be exported from the shared library. To do that I make the internal libs PUBLIC link libraries.
So, first, I'll note that PUBLIC has nothing at all to do with symbol visibility. Your example mysublib doesn't have any INTERFACE properties, so you could make it PRIVATE to mylib and avoid the need to export mysublib.
However, if you do need mysublib in the INTERFACE of mylib, then you do need to export it, period. There is no way to, as you say "'merge' internal targets [...] into the public exported target [...] or hide it in the config targets file". But this is also not a real problem.
If you're concerned about people relying on mysublib, then you can set the EXPORT_NAME property of mysublib to something that indicates it's not meant to be used, like _private_mysublib:
set_target_properties(mysublib PROPERTIES EXPORT_NAME _private_mysublib)
If you want to be really aggressive about it, you could even make the name random:
string(RANDOM LENGTH 12 mysublib_export)
set_target_properties(mysublib PROPERTIES EXPORT_NAME "x${mysublib_export}")
Prepending an x makes sure it doesn't start with a number. There is also a remote chance of collision if you do this a lot. If you want to be absolutely safe, you should write a function that records the names it's already returned in a global property and tries again if it chooses a collision.
The SystemC::systemc imported target does not show up in my target file and it also don't generate any error. I assume that is because this target is already exported in its own package? If so, would that mean I need to make my internal libs packages and so imported targets to make them go away from my exported targets list?
You are correct: because SystemC::systemc is imported, it does not need to be (and in fact cannot be) re-exported. You're expected to call find_dependency(SystemCLanguage 2.3.3) in your MyLibConfig.cmake file, both within the build tree (export) and after install (install(EXPORT)).
If you want this behavior to apply to mysublib, then yes, you will need to split your projects apart. I don't see a compelling reason to do that, though. Either make mysublib PRIVATE or just live with it being renamed in your package config. Targets aren't precious and they're namespaced, so there's no real reason to worry.

How can I make `target_link_libraries` dependencies transitive from OBJECT libraries in cmake?

If I specify a dependency chain of OBJECT libraries in CMake, only the target_link_libraries dependencies of the very last one is used in the target executable.
Minimal Example:
main depends on objB, which depends on objA.
Both objA and objB are OBJECT libraries in CMake.
I would expect main to link with both object files. It doesn't.
cmake_minimum_required(VERSION 3.13)
project(transitive-object-library-link-issue VERSION 1.0.0 LANGUAGES C)
add_library(objA OBJECT a.c)
add_library(objB OBJECT b.c)
target_link_libraries(objB PUBLIC objA)
# Should link with 'a.o', since 'objA' is "linked" by 'objB'
add_executable(main main.c)
target_link_libraries(main objB)
Note: A working set of files are available at https://github.com/scraimer/cmake-transitive-object-library-link-issue
If I change objA to a non-OBJECT library, the problem is eliminated by getting rid of the chain of OBJECT library dependencies. In other words, change the line
add_library(objA OBJECT a.c)
To be:
add_library(objA a.c)
So it's something specific to OBJECT libraries. How should I be specifying the dependencies to make main link with objA? (Without having to specify objA in every executable target that uses objB)
Object library targets are not real libraries, they are simply a collection of objects, but they are not really linked together until they are used to build a real target like an executable or shared/static library. Citing the linked documentation:
add_library(<name> OBJECT <src>...)
Creates an Object Library. An object library compiles source files but does not archive or link their object files into a library.
In despite of that, you can apply a target_link_library() command to an object library, but only to specify dependencies of their sources on other libraries. The linked documentation explains your problem:
The object library’s usage requirements are propagated transitively [...], but its object files are not.
Object Libraries may “link” to other object libraries to get usage requirements, but since they do not have a link step nothing is done with their object files.
So, the transitive propagation affects only to compile definitions and dependencies of other real libraries, not the objects themselves.
One solution is to define an INTERFACE library for each OBJECT library. Dependencies of interface libraries are transitive.
# Defines an object library named ${T}_obj,
# interface library named ${T} linked to it,
# and sets ${varname} to ${T}_obj.
macro(add_object varname T)
add_library(${T}_obj OBJECT ${ARGN})
add_library(${T} INTERFACE)
target_link_libraries(${T} INTERFACE ${T}_obj $<TARGET_OBJECTS:${T}_obj>)
set(${varname} ${T}_obj)
endmacro(add_object)
Usage
add_object(T a a.c)
add_object(T b b.c) # sets T to b_obj
target_link_libraries(${T} a)
This stems from another issue with OBJECT libraries: duplication of object code. As Brad King explains:
we'd need to be careful to de-duplicate use of [example item's] object files
If transitive propagation was the default, you could accidentally cause the linker to try linking multiple copies of the same code, thus dooming your project to be non-compilable by CMake.
That is a good enough reason not to allow transitive dependencies of OBJECT libraries. It's tricky, so best avoided. So much so that it's mentioned as a Good Practice in his excellent book "Professional CMake: A Practical Guide":
If a target uses something from a library, it should always link directly to that library. Even if the
library is already a link dependency of something else the target links to, do not rely on an indirect
link dependency for something a target uses directly.
He also added in another issue:
he transitive nature of object libraries is not the same as regular libraries. The build-system manual words it like this:
The link (or archiving) step of those other targets will use the object files from object libraries that are directly linked.
The key part of that is the "directly linked". In your example, main gets the objects from b because it links directly to b, but because it does not link directly to a, it does not get a's objects, even though b links to a.
The reason for this is related to problems where deep transivity can result in object files being added multiple times, which will cause an error. This particular aspect has come up a few times in the issue tracker, but I don't have the issue numbers at hand (you can probably search for them and track down the various discussions).
So the solution seems to be to avoid relying on target_link_library for OBJECT libraries. There are two ways: First is to simply not use OBJECT libraries. Second is to explicitly specify objects to link in the hierarchy, as proposed by Hiroshi Miura:
add_library(a OBJECT a.c)
add_library(b OBJECT b.c)
target_sources(b PUBLIC $<TARGET_OBJECTS:a>)
add_executable(main main.c)
target_sources(main PRIVATE $<TARGET_OBJECTS:b>)
This is explicit, but doesn't even solve the problem I have! I suspect this also might run into duplication issues due to diamond patterns, but I haven't tested that.
Maybe over the next few months I'll figure out a better solution using generator expressions based on the LINK_LIBRARIES property of the objA. Not sure how to do that, though.

Transitive dependencies and OBJECT libraries

A somewhat similar question was asked here, Transitive target_include_directories on OBJECT libraries, but there was no real solution.
If I have a project b that depends on a project a I can build them as follows
add_library(a OBJECT ${a_srcs})
add_library(b OBJECT ${b_srcs})
When I want to build an excutable using I them I can write
add_executable(p ${p_srcs} $<TARGET_OBJECTS:b> $<TARGET_OBJECTS:a>)
Is there any way to not have to specify $<TARGET_OBJECTS:a>? I assume this means telling CMake in some way that there is a dependency. If I was building SHARED libraries rather than OBJECT ones the b project would contain
target_link_libraries(b a)
which creates this dependency, but I can't find some equivalent way for OBJECT libraries.
Insofar as I understand it, in the current setup, no. The add_executable for target p can either
Link against some library (shared or static), or
Merge object sources into itself.
You have chosen (2). The only other option I see here is create a third library c that merges in a and b into a full-blown library (see Usage section at the bottom, which is likely where you were already looking).
When you do that, you could then target_link_libraries(c). The compiled OBJECTs cannot be linked against on their own. You have to merge the sources into either an executable or a library for them to be used.
Your add_executable call could be thought of basically doing add_executable(p ${p_srcs} ${a_srcs} ${b_srcs}), only instead of compiling a_srcs and b_srcs (which has been done previously), just copy in the compiled objects instead of redoing the work. That's a really simple / bad explanation, but that's the general idea.
The best way I have found to do this is to wrap the OBJECT library in an INTERFACE library.
add_library(a-objects OBJECT ${a_srcs})
add_library(b-objects OBJECT ${b_srcs})
add_library(a INTERFACE)
add_library(b INTERFACE)
target_link_libraries(a INTERFACE a-objects)
target_link_libraries(b INTERFACE b-objects)
target_sources(a INTERFACE $<TARGET_OBJECTS:a-objects>)
target_sources(b INTERFACE $<TARGET_OBJECTS:b-objects>)
My rule is to use the OBJECT library to set requirements, but only ever link against the INTERFACE library.
target_link_libraries(b-objects INTERFACE a)
Setting it up this way should allow you to link against both libraries like this:
add_executable(p ${p_srcs})
target_link_libraries(p PRIVATE b)

How to make sure my reusable static library doesn't force my project to include multiple frameworks?

I try to put some reusable functions into my own static library but i noticed one problem.
Let's say the static library has many functions and some requires quartz core framework, some requires messgeUI etc.
When I build a new project, I include this static library project into my code so that I can reuse those functions.
Now even if I only use a simple function that doesn't require any frameworks, I am forced to include all the quartzcore, message UI frameworks or I will face build errors because the static library requires them- but I do not need all of them!
How can I design a better general purpose static library that doesn't force people to include frameworks that is not used by them at all?
You can use weak linking - if weak linkage is used, unused symbols (functions, methods, global variables) don't have to be present at linking time.