链接构建的静态库而不是使用 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
中,我将 lib
与 add_subdirectory
包括在内,但无论出于何种原因,这都会引入 所有 lib
的间接通过我不知道的机制依赖于链接线。我喜欢是让我的应用程序只构建libmylib.a
和libmylib.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 中,因为它们只是为了完整性而包含在内,我没有不想弄乱问题文本:
问题是当我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
的文档;如果您不希望传播目标的依赖项,则需要将它们指定为 PRIVATE
或 PUBLIC
以外的其他内容,默认情况下假定您已使用它。
@legalize 啊啊啊!这使事情变得非常清楚,谢谢!如果您想做出这样的回答,它似乎是一个很好的答案。
【参考方案1】:
链接就是解析符号,以便最终目标(独立可执行文件或共享对象)拥有它需要启动的一切。当您仅依赖于一个库或一组库而这些库又不依赖于其他任何东西时,事情就很简单了。对于任何中等或较大规模的项目来说,这种情况不太可能发生。根本问题是如何处理传递依赖,例如程序直接使用的事物的依赖关系。
CMake 了解所有这些,旨在让您在不了解整个依赖关系图的情况下轻松使用库。如果您查看target_link_libraries
的文档,您将看到描述的PRIVATE
、PUBLIC
和INTERFACE
关键字。这允许您描述构建库时所需的库的私有要求(编译定义、编译参数、依赖库等)。公共部分允许您指定库及其依赖项(消费者)都需要的东西。接口部分允许您指定依赖项需要的东西,但不能指定库本身。命令target_compile_definitions
和target_include_directories
操作类似。
所有这一切的结果是,在 CMake 中正确声明了依赖项,该依赖项的客户端只需将其添加到其自己的 target_link_libraries
命令中的依赖项列表中,它自然会选择所有编译定义,包括目录以及成功编译和链接所需的临时链接依赖项。
CppCon 2017 演示文稿Modern CMake for modular design 对此进行了更详细的介绍。
【讨论】:
以上是关于链接构建的静态库而不是使用 add_subdirectory?的主要内容,如果未能解决你的问题,请参考以下文章
CMake:是不是可以仅从静态库而不是源代码构建可执行文件?