Reputation: 302
Here is a weird question. I have a "renderer frontend" static target and several "renderer backend" static targets. The "renderer frontend" contains a header file that backends need to implements, and codes in frontend will call them. The backends implement those function by calling underlying their corresponding APIs (for example, OpenGL or DirectX).
So far, so good. I can choose a backend at compile time and link it to the frontend. However, I decide to add unit tests to the frontend which need a "dummy backend" instead of the backend configured for the main program.
So instead of doing this:
if(${RendererBackend} STREQUAL "OpenGL")
target_link_libraries(Renderer RendererBackendGL)
elseif(...)
...
end
target_link_libraries(Application Renderer)
I want to do this:
target_link_libraries(Application Renderer)
target_link_libraries(RendererTest Renderer RendererBackendDummy)
if(${RendererBackend} STREQUAL "OPENGL")
target_link_libraries(Application RendererBackendGL)
elseif(...)
...
end
However, my program has "undefined reference" link error whenever the renderer frontend calls functions that are implemented by backends under GCC. The program runs fine under MSVC though.
Edit:
I checked the linking command, it is like this:
/usr/bin/g++ CMakeFiles/Demo.dir/src/main.cpp.o -o Demo -rdynamic -lm -ldl /usr/lib/x86_64-linux-gnu/libX11.so -lpthread ../Engine/OpenGL/libRendererBackendGL.a ../Engine/Graphics/libRenderer.a
The problem is "Renderer" library call functions defined in "RendererBackendGL".
Upvotes: 1
Views: 1116
Reputation: 54589
You have your dependencies modeled incorrectly.
If Renderer
calls functions from RendererBackendGL
, you need to model that relationship in CMake:
target_link_libraries(Renderer PUBLIC RendererBackendGL)
target_link_libraries(Application PRIVATE Renderer)
Yes, that implies that everyone linking to Renderer
will also need to link against the backend, including the test. But that is a problem of your software architecture, ie. there being calls to backend functions inside Renderer
, and not a problem of CMake.
The reason this breaks in gcc is that the order in which input libraries are specified on the linker command line is important. This is not true for MSVC (as long as the lib is present, it will works, no matter if it appears first or last). Since such order-dependencies are toolchain-specific, the only way to make this portable is by explicitly modelling all inter-target dependencies in CMake correctly.
So what can you do now to get around this? Some options that come to my mind:
Note that all of these will require some amount of architectural change to your code. So be sure you think all implications through before you start with the refactoring.
Upvotes: 3