there is another (static) library named libraryB which has a link time dependency on libraryA (specified via target_link_libraries(libraryB PRIVATE libraryA))
there is a third (static) library named libraryAmockup which contains mockup versions of the functions in libraryA (with the same interface)
there is an executable named test_executable which has a link time dependency on libraryB (specified via target_link_libraries(test_executable PRIVATE libraryB))
test_executable does not directly use functionality from libraryA
As test_executable is for testing purposes, I’d like to link with libraryAmockup rather than libraryA.
Is it possible to (reliably) exchange the dependencies only for the test_executable target? I.e. use libraryB -> libraryAmockup when linking test_executable and libraryB -> libraryA in all other situations.
Thanks, @marc.chevrier, for this suggestion. I had not yet been aware of INTERFACE_LINK_LIBRARIES_DIRECT and INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE.
I might misunderstand the documentation, so please correct me if applicable: I’d have to set the INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE property of libraryB to libraryA and then explicitly link test_executable with libraryAmockup. The consequence would be that all normal users (aka dependent targets) of libraryB would get into trouble if they don’t know that they have to link with libraryA (which should be an implementation detail of libraryB which is usually dealt with by the normal CMake transitive closure).
My goal was to keep the normal transitive closure mechanism for users of libraryB and override it only when linking that special test_executable.
You do not need to change the dependency of libB if the mock symbols are linked before libB. The linker should only pull in the symbols from libA if such a symbol is needed by libB but not mocked.
So you just need to make sure that your mock of libA appears before libB in the linker command but linked either dynamic or the whole library.
So you just need to make sure that your mock of libA appears before libB in the linker command but linked either dynamic or the whole library.
Is there a way (in CMake) to reliably have libraryAmockup injected before the first occurence of libraryB and any libraryA occurrences (which might be caused by different dependencies)?
(I’d also need to wrap linking of libraryAmockup in a --whole-archive/--no-whole-archive pair (I’m using a GNU toolchain), but I know how to do that.)
The link order given by target_link_libraries() is kept.
Yes, you are right. I was assuming (wrongly) that CMake would delay linking if several involved targets depend on the same target, i.e. that for instance in the case e->{l1,l2}, l2->l1 CMake would link l2 first and then l1 because both e and l2 depend on l1. But apparently CMake just links l1 several times.
By the way, is there a reason you use static libraries?
I link libraries because the tests operate on the interfaces of the library under test (operating the public interface and mocking subordinates). The libraries are static because it’s an embedded environment without support for shared/dynamic libraries.
I think that one possible way to achieve that would be to split the libraries to few more targets. This is based on an assumption that the compilation units / source files of libraryB only depend on access to the public headers of libraryA. And that users of libraryB should be “forced” to use libraryA implementation instead of providing their own.
I implemented a structure like this once before when we had a need to test implementations in static libraries, and we wanted to mock the implementations of dependant libraries. Which feels to me like exactly what you are trying to achieve.
The structure with targets would look like this:
# Convenience interface library for accessing headers of libraryA without
# pulling in an implementation
add_library(libraryAheaders INTERFACE)
target_include_directories(libraryAheaders INTERFACE ${pathToLibAHeaders})
# Actual libraryA implementation
add_library(libraryA STATIC libAsource.cc)
target_link_libraries(libraryA PUBLIC libraryAheaders)
# Implementation of libraryB with dependency only to libraryA headers
add_library(libraryBimpl STATIC libBsource.cc)
target_link_libraries(libraryB PRIVATE libraryAheaders)
# Mock of libraryA
add_library(libraryAmockup STATIC libAmock.cc)
target_link_libraries(libraryAmockup PUBLIC libraryAheaders)
# Test executable for libraryB implementation using libraryAmockup
add_executable(test_executable testSource.cc)
target_link_libraries(test_executable PRIVATE libraryBimpl libraryAmockup)
# LibraryB to be used by consumers.
add_library(libraryB INTERFACE)
target_link_libraries(libraryB INTERFACE libraryBimpl libraryA)
The libraryB there could also be a static library implementing initialization or similar logic that connects libraryBimpl explicitly to libraryA. In which case the test_executable should contain similar logic for connecting libraryBimpl to libraryAmockup.
That’s not guaranteed. CMake will reconstruct a linker command line according to the dependency graph constructed by the project. While CMake might currently preserve the order of things you specify in target_link_libraries() calls, there’s no guarantee that a future CMake version won’t order the linker command line differently. The only guarantee is that inter-target dependencies specified by the project will be honoured.
The project should not rely on the ordering of items given to target_link_libraries(). If certain things need to appear in a specific order, the project should enforce that by wrapping the grouped items with SHELL:.... But that can’t be used to order CMake targets, only raw linker command line options.
I think the underlying problem you have here is that you’re trying to set up a dependency relationship without telling CMake about it. More accurately, you’re trying to switch out one or more dependencies with different things, but not tell CMake about that. You could potentially achieve what you’re after using generator expressions that evaluate to the real libraries when used with one set of targets, or to the mock libraries for others. The generator expression could look at a property on the consuming target to work out which of the two cases it should expand to. But it is likely to get a bit involved. You have to work out the expansion for the top level target (executable or shared library), not necessarily the immediate consumer. I think you’re basically looking for the link seaming technique, or at least some variation of it.