链接构建的静态库而不是使用 add_subdirectory?

Posted

技术标签:

【中文标题】链接构建的静态库而不是使用 add_subdirectory?【英文标题】:Linking against built static libraries rather than using add_subdirectory? 【发布时间】:2017-10-12 20:54:21 【问题描述】:

给定一个具有“app”和“lib”同级目录的项目,其中“app”根据“lib”构建的(静态)库构建可执行文件。我想要的是正常构建过程只构建库的情况,但如果我构建“app”,它会构建“lib”和“app”。

我现在正在做的是,在 app 中,我将 libadd_subdirectory 包括在内,但无论出于何种原因,这都会引入 所有 lib 的间接通过我不知道的机制依赖于链接线。我喜欢是让我的应用程序只构建libmylib.alibmylib.pc,然后app 可以从libmylib.pc 计算自己的链接线(或手动指定),但是我不确定这是怎么做到的。

这是我现在设置的最小工作示例:

lib/CMakeLists.txt

cmake_minimum_required(VERSION 3.8.0)
project(mylib CXX)

find_package(PkgConfig REQUIRED)
pkg_check_modules("mylib" "libssl")

find_package(Boost REQUIRED)
set(LIBDIR "$PROJECT_SOURCE_DIR")


set(HEADERS "$LIBDIR/map_printer.hpp")
set(SOURCES "$LIBDIR/map_printer.cpp")

add_library("mylib" "$SOURCES")

target_include_directories("mylib" PUBLIC "$LIBDIR"
                                          "$Boost_INCLUDE_DIR"
                                          "$mylib_INCLUDE_DIRS")
target_link_libraries("mylib" "$Boost_LIBRARIES" "$mylib_LIBRARIES")

install(TARGETS "mylib" ARCHIVE DESTINATION "lib")
install(FILES $HEADERS DESTINATION "include")

app/CMakeLists.txt

cmake_minimum_required(VERSION 3.8.0)
project(mylib CXX)

set(APPDIR "$PROJECT_SOURCE_DIR")
set(LIBDIR "$APPDIR/../lib")

set(SOURCES "$APPDIR/main.cpp")

add_subdirectory("$LIBDIR" "build")
list(APPEND LIBS "mylib")

add_executable("myapp" "$SOURCES")

target_include_directories("myapp" PUBLIC "$LIBDIR")
target_link_libraries("myapp" "$LIBS")

install(TARGETS "myapp" DESTINATION "bin")

为了获得一个工作示例,这里有一些源文件在 lib 中拉入 libssl(但此功能未在应用程序中使用) - 我将它们放在 gists 中,因为它们只是为了完整性而包含在内,我没有不想弄乱问题文本:

lib/map_printer.cpp lib/map_printer.hpp app/main.cpp

问题是当我cmake app再做make VERBOSE=1时,生成的链接器命令是:

/usr/lib/hardening-wrapper/bin/c++     CMakeFiles/myapp.dir/main.cpp.o  -o myapp build/libmylib.a -lssl 

但我没有在app 的任何地方指定-lssl。通常这会很好,但在我的 real 应用程序中,-lssl 和其他几个不必要的符号由于间接依赖关系而被包含为 .so 文件。当我手动从链接器命令中删除它们时,任务构建并运行得很好。理想情况下,我将使用其.pc 文件(未在此示例中生成)拉入构建的.a,如果必须拉入过多的依赖项,我可以手动调整链接线,但使用这种方法,链接器标志(可能还有其他东西)以某种我不明白的方式从 lib 范围外泄漏。

【问题讨论】:

查看target_link_libraries的文档;如果您不希望传播目标的依赖项,则需要将它们指定为 PRIVATEPUBLIC 以外的其他内容,默认情况下假定您已使用它。 @legalize 啊啊啊!这使事情变得非常清楚,谢谢!如果您想做出这样的回答,它似乎是一个很好的答案。 【参考方案1】:

链接就是解析符号,以便最终目标(独立可执行文件或共享对象)拥有它需要启动的一切。当您仅依赖于一个库或一组库而这些库又不依赖于其他任何东西时,事情就很简单了。对于任何中等或较大规模的项目来说,这种情况不太可能发生。根本问题是如何处理传递依赖,例如程序直接使用的事物的依赖关系。

CMake 了解所有这些,旨在让您在不​​了解整个依赖关系图的情况下轻松使用库。如果您查看target_link_libraries 的文档,您将看到描述的PRIVATEPUBLICINTERFACE 关键字。这允许您描述构建库时所需的库的私有要求(编译定义、编译参数、依赖库等)。公共部分允许您指定库及其依赖项(消费者)都需要的东西。接口部分允许您指定依赖项需要的东西,但不能指定库本身。命令target_compile_definitionstarget_include_directories 操作类似。

所有这一切的结果是,在 CMake 中正确声明了依赖项,该依赖项的客户端只需将其添加到其自己的 target_link_libraries 命令中的依赖项列表中,它自然会选择所有编译定义,包括目录以及成功编译和链接所需的临时链接依赖项。

CppCon 2017 演示文稿Modern CMake for modular design 对此进行了更详细的介绍。

【讨论】:

以上是关于链接构建的静态库而不是使用 add_subdirectory?的主要内容,如果未能解决你的问题,请参考以下文章

在链接/编译时静态包含所有库而不是库的一部分

CMake:是不是可以仅从静态库而不是源代码构建可执行文件?

当目标是静态库而目标链接是静态库时,target_link_libraries 会做啥

如何防止我的makefile由于静态库而重新链接

静态链接库的动态 DLL

如何强制 CMake 链接到系统库而不是同名目标