C++/Qt项目中的外部库使用CMake

3
我正在尝试构建一个小型的C++/Qt测试程序,用于读取各种编码的文件,并尝试使用Google的Compact Encoding Detection(CED)库:https://github.com/google/compact_enc_det。虽然有很多类似的问题,但是它们似乎都无法解决我的问题。
CED作为独立项目可以成功构建,但我无法将其作为外部库并入我的项目中。我习惯于使用QMake,在QtCreator中工作,但是这只适用于CMake,因此我开始四处寻找,但有点迷失了方向(在这方面,QMake似乎更简单)。
我设法让我的C++/Qt项目在QtCreator下使用CMake正常工作,并且还成功构建和测试了CED(然后CMake生成一个库:libced.a)。
以下是我的小型(可行的)CMakeLists文件:
cmake_minimum_required(VERSION 3.1.0)

project(FileTestCMakeCED)

set(CMAKE_AUTOMOC ON)

find_package(Qt5 COMPONENTS Core Widgets REQUIRED)

set(FileTest_src
    ${CMAKE_CURRENT_LIST_DIR}/src/mainwindow.cpp
    ${CMAKE_CURRENT_LIST_DIR}/src/main.cpp
)

add_executable(${PROJECT_NAME} ${FileTest_src})

# Use the Widgets module from Qt 5
target_link_libraries(${PROJECT_NAME} Qt5::Widgets)

我不会粘贴CED的CMakeLists,它有点长,可以在GitHub页面上找到:https://github.com/google/compact_enc_det/blob/master/CMakeLists.txt 我想要使用CED作为静态库(但我尝试了许多方法,都无法成功),或者将源代码合并到我的项目中,需要时进行编译(我已尝试将CED作为外部项目链接到我的源树之外,但如果更容易,我可以将其移动到同一源树中)。
我尝试过一种有前途的方法:
include_directories(../compact_enc_det)
add_library(ced STATIC IMPORTED)
set_target_properties(ced PROPERTIES IMPORTED_LOCATION ../compact_enc_det/lib/libced.a)
...
target_link_libraries(${PROJECT_NAME} Qt5::Widgets ced)

但是我遇到了一个“no rule to make target .../libced.a”的错误,尽管我只想使用预编译库。
我唯一成功的方法是从Google创建的CED项目开始,并将我的源代码粘贴在那里,让CED的CMakeLists为我的程序生成额外的可执行文件,但这与我想要的代码和项目结构相反。
如果我试着反过来做(将CED包含在我的项目中),我总是会遇到CED源代码和包含文件路径问题。
有人能告诉我这种情况的最佳实践是什么吗? 另外,如我所说,我新手上路CMake,希望能提供一个简洁的解决方案。 我使用的是Debian Stretch操作系统。
1个回答

1

一个快速的解决方案是给libced.a库提供完整路径:

target_link_libraries(${PROJECT_NAME} Qt5::Widgets /path/to/libced.a)

这种方法的缺点是你的CMakeLists.txt文件变得特定于你目前使用的机器。如果你要分发你的代码,其他人将无法在不更改源代码CMakeLists.txt的情况下配置他们的构建。

通常,使用find_package()在CMake中定位外部库。为了使此函数工作,您要尝试链接的软件包必须在CMake可以找到的地方提供<package>Config.cmakeFind<package>.cmake文件。这些文件已经存在于您的CMake安装中的许多流行软件包中(例如,在/usr/lib/cmake下查找)。但是,如果您刚开始使用CMake,编写自己的文件可能是一个非平凡的解决方案,因为在这些文件中,您需要编写一个脚本来执行许多系统相关的检查以定位软件包并设置适当的变量,以便在构建过程中进一步使用。您可以在链接的文档页面上阅读更多信息。

另一种解决方案是使用find_library(),您可以提供提示或环境变量来指定所需库的位置。如果找不到该库,则可以打印消息或错误,要求用户设置特定的提示变量,以便您的CMakeLists.txt可以定位该库。例如,在您的CMakeLists.txt中,您可以执行以下操作:

find_library(CED_LIBRARY ced HINTS ${CED_ROOT})
if( ${CED_LIBRARY} STREQUAL "CED_LIBRARY-NOTFOUND")
    message("Please set CED_LIBRARY variable to location of libced")
endif()

上面的代码片段指示CMake查找库文件ced的路径,并将该路径存储在CED_LIBRARY变量中。如果未找到文件,则会在该变量中存储CED_LIBRARY-NOTFOUND。如果找到库,则可以使用以下方式链接它:

target_link_libraries(${PROJECT_NAME} Qt5::Widgets ${CED_LIBRARY})

find_library() 足够聪明,可以自行尝试各种前缀和后缀,因此您不需要编写 libced。例如,在 Windows 上,它可能会尝试查找 libced.dll,对于 Unix 上的共享库,它将搜索 libced.so 等。


感谢您详细的回复。 我在 find_library() 代码片段中修改了两个小错误: STREQUAL(而不是 STREQUALS)和 endif()(而不是 end())。 我使用这段代码使其正常工作,然后添加了以下行以进行包含: include_directories(${CMAKE_CURRENT_LIST_DIR}/compact_enc_det) - LLL
还有一件事: 关于代码的兼容性和分发,将外部库作为可编译的内容(前提是它是开源的)是否更好呢?我的意思是,如果我要把我的项目交给别人,他们需要先编译这个外部库,对吧?然后把编译好的库放在正确的文件夹里。 在这种情况下,CED提供了自己的CMakeLists.txt,那么在我编译自己的项目时能否让它一起编译呢? - LLL
1
感谢您纠正错别字。是的,您是正确的,您可以将外部项目设置为子模块。有很多参考资料可以做到这一点,比如在此链接上 - user6764549

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接