How to do "link-time polymorphism" right?

In our project, we are doing “link-time polymorphism” to provide variants of the product and for testability. The second scenario is of interest here.

We have a library (.so). For it, there are two targets: one provides only headers, the other one (depending also on the first one) provides the .a for linking. On top of that, there is another implementation, test double, that provides a different .a to support stubs and mocks of the library in the testing code.

Perhaps having a proper dependency injection would prevent the need for link-time polymorphism, but for various reasons, it seems out of the picture for now.

Now, the approach is that all “intermediate targets” link (through target_link_libraries) only to the “library interface target” and this is enough for compilation. While only the “final targets” (typically executables) add the “library target”, either directly or indirectly through some other wrappers etc.

In theory, it should work since all the symbols are present while linking the final executable. And it did work… - so far. However, now while attempting to make a small change in the structure of the targets (part of a larger target is taken out and promoted to a target on its own) we got some linking errors with “undefined reference”.

Looking at the failed command line (as output by CMake itself) I can see the stubbing library is present there. So, I guess the problem might be that it is placed before the other library that is reported as the source of unresolved reference. However, how can I influence this?

Or is it so, that our approach of using “library interface targets” is outright wrong and we should be using always either the real library or its stub? This should perhaps be done by creating a “selector target” which links to either the real thing or the stub, depending on what build are we doing, while all other targets would then only use the selector, being free of (direct) “logic”.

Level: Hard

We have another instance of the same problem also with an external library, that we import using pkg-config. The extra level of complexity here stems from the fact, that we no longer can easily make this “library interface target” since PkgConfig::<target> has it all - both headers and linking.

I have asked about it on StackOverflow (How to do target_link_libraries in CMake without actual linking?), however, with no success.

And this is a problem even without our approach of “library interface targets” since even if we would always be using either the library or its stab, making the “stub target” still implies the same difficulties - how no nicely extract from PkgConfig::<target> the “interface part”?

I don’t have much feedback on the main part of your question right now other than to say that some genex logic could help here to give target A to executables and target B to everything else:


Somewhere I mentioned about a potential $<COMPILE_ONLY:> genex. I don’t know what happened to it though :frowning: .