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

Linking against built static libraries rather than using add_subdirectory?

本文关键字:add subdirectory 链接 构建 静态 是否      更新时间:2023-10-16

给定一个具有"app"answers"lib"同级目录的项目,其中"app"根据"lib"构建的(静态)库构建可执行文件。我想要的是,正常的构建过程只构建库,但如果我构建"应用程序",它会同时构建"lib"answers"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")

为了获得一个有效的例子,这里有一些源文件在库中拉入libssl(但该函数在应用程序中没有使用)-我把它们放在注册表中,因为它们只是为了完整性而包含的,我不想混淆问题文本:

  • 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文件。当我手动从链接器命令中删除它们时,任务构建和运行都很好。理想情况下,我会将构建的.a及其.pc文件(本例中未生成)拉入,如果必须拉入多余的依赖项,我可以手动调整链接行,但使用这种方法,链接器标志(可能还有其他东西)会以某种我不理解的方式从lib范围中泄漏出来。

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

CMake了解所有这些,并且设计为使您在不了解整个依赖关系图的情况下使用库变得简单。如果查看target_link_libraries的文档,您将看到所描述的PRIVATEPUBLICINTERFACE关键字。这允许您描述构建库时所需的库的私有需求(编译定义、编译参数、依赖库等)。公共部分允许您指定库及其从属(使用者)需要的东西。接口部分允许您指定从属项所需的内容,但不能指定库本身。命令CCD_ 22和CCD_。

所有这些的结果是,在CMake中正确声明了依赖项后,该依赖项的客户端只需在自己的target_link_libraries命令中将其添加到依赖项列表中,就可以自然地获取所有编译定义,包括成功编译和链接所需的目录和临时链接依赖项。

CppCon 2017演示文稿Modern CMake模块化设计对此进行了更详细的介绍。