C++ Cmake:使用来自另一个目标的函数,而不使用直接目标链接库

C++ Cmake:使用来自另一个目标的函数,而不使用直接目标链接库,c++,cmake,C++,Cmake,这里有一个奇怪的问题。我有一个“渲染器前端”静态目标和几个“渲染器后端”静态目标。“渲染器前端”包含后端需要实现的头文件,前端中的代码将调用它们。后端通过调用相应的底层API(例如OpenGL或DirectX)来实现这些功能 到目前为止,一切顺利。我可以在编译时选择后端并将其链接到前端。但是,我决定将单元测试添加到前端,前端需要一个“虚拟后端”,而不是为主程序配置的后端 因此,与其这样做,不如: if(${RendererBackend} STREQUAL "OpenGL") targe

这里有一个奇怪的问题。我有一个“渲染器前端”静态目标和几个“渲染器后端”静态目标。“渲染器前端”包含后端需要实现的头文件,前端中的代码将调用它们。后端通过调用相应的底层API(例如OpenGL或DirectX)来实现这些功能

到目前为止,一切顺利。我可以在编译时选择后端并将其链接到前端。但是,我决定将单元测试添加到前端,前端需要一个“虚拟后端”,而不是为主程序配置的后端

因此,与其这样做,不如:

if(${RendererBackend} STREQUAL "OpenGL")
    target_link_libraries(Renderer RendererBackendGL)
elseif(...)
    ...
end
target_link_libraries(Application Renderer)
我想这样做:

target_link_libraries(Application Renderer)
target_link_libraries(RendererTest Renderer RendererBackendDummy)
if(${RendererBackend} STREQUAL "OPENGL")
    target_link_libraries(Application RendererBackendGL)
elseif(...)
    ...
end
但是,每当渲染器前端调用GCC下后端实现的函数时,我的程序就会出现“undefined reference”链接错误。不过,该程序在MSVC下运行良好

编辑:

我检查了链接命令,它是这样的:

/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

问题在于“RenderBackendGL”中定义的“Renderer”库调用函数。

您的依赖项建模不正确

如果
Renderer
RendererBackendGL
调用函数,则需要在CMake中对该关系建模:

target_link_libraries(Renderer PUBLIC RendererBackendGL)
target_link_libraries(Application PRIVATE Renderer)
是的,这意味着链接到
渲染器的每个人也需要链接到后端,包括测试。但这是您的软件体系结构的一个问题,即在
渲染器中存在对后端函数的调用,而不是CMake的问题

这在gcc中中断的原因是在链接器命令行上指定输入库的顺序很重要。这对于MSVC是不正确的(只要lib存在,它就会工作,不管它是最先出现还是最后出现)。由于此类顺序依赖关系是特定于工具链的,因此使其可移植的唯一方法是通过在CMake中明确地建模所有目标间依赖关系

那么你现在能做些什么来解决这个问题呢?我想到的一些选择:

  • 翻转依赖项。让后端依赖于前端,并且只有一个应用程序链接到它想要使用的后端
  • 转到插件概念。前端在运行时从共享库中动态加载要使用的后端,而不是静态链接
  • 在编译时选择一个特定的后端。这意味着,如果您仍然希望在同一构建步骤中使用GL后端构建应用程序,并使用虚拟后端构建测试,则需要构建前端两次。这可能是最接近您当前尝试的方法。您可以使用最小化前端的重新编译量,但最终仍会有多个前端库目标

请注意,所有这些都需要对代码进行一定程度的体系结构更改。因此,在开始重构之前,一定要仔细考虑所有的含义。

当您执行
make VERBOSE=1
时,您可以看到实际执行的编译和链接命令。检查命令中是否存在正确链接的所有参数。我不理解您的问题。