给定一个具有"app"one_answers"lib"同级目录的项目,其中"app"根据"lib"构建的(静态)库构建可执行文件。我想要的是,正常的构建过程只构建库,但如果我构建"应用程序",它会同时构建"lib"one_answers"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")
为了获得一个有效的例子,这里有一些源文件在库中拉入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
的文档,您将看到所描述的PRIVATE
、PUBLIC
和INTERFACE
关键字。这允许您描述构建库时所需的库的私有需求(编译定义、编译参数、依赖库等)。公共部分允许您指定库及其从属(使用者)需要的东西。接口部分允许您指定从属项所需的内容,但不能指定库本身。命令CCD_ 22和CCD_。
所有这些的结果是,在CMake中正确声明了依赖项后,该依赖项的客户端只需在自己的target_link_libraries
命令中将其添加到依赖项列表中,就可以自然地获取所有编译定义,包括成功编译和链接所需的目录和临时链接依赖项。
CppCon 2017演示文稿Modern CMake模块化设计对此进行了更详细的介绍。