cmake 配置文件示例解释
参考官网教程:CMake Tutorial 制作
包含可执行程序、链接库、测试及安装的主要功能
源码根目录 CMakeLists.txt:
可执行程序的编译安装
# 指定 cmake 最低版本,以保证兼容性
cmake_minimum_required(VERSION 3.15)
# 设置项目名称及版本
project(Tutorial VERSION 1.0)
# d 作为 Debug 库的后缀
set(CMAKE_DEBUG_POSTFIX d)
# set(CMAKE_CXX_STANDARD 11)
# set(CMAKE_CXX_STANDARD_REQUIRED True)
# 替代通过 CMAKE_CXX_STANDARD 变量的方式指定编译参数
# 通过 add_library 指定 INTERFACE 作用域创建接口库,要生成的目标都链接该接口库,以实现通过唯一源设置编译参数
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
# 表达式生成器 $<...>,如
# target_include_directories(tgt PRIVATE /opt/include/$<CXX_COMPILER_ID>)
# 根据所使用的c++编译器生成 /opt/include/GNU, /opt/include/Clang等。
# 它们支持条件链接、编译时使用的条件定义、条件包含目录等等。
# 条件可能基于构建配置、目标属性、平台信息或任何其他可查询信息。
# 文档:https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#introduction
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
# BUILD_INTERFACE 指定仅在编译项目时添加编译器警告,根据当前编译器类型生效
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
message(STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
# 用户选项:默认编译为动态库, cmake .. -DBUILD_SHARED_LIBS=OFF 编译为静态库
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
if(APPLE)
set(CMAKE_INSTALL_RPATH "@executable_path/../lib")
elseif(UNIX)
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
endif()
# 读取项目版本号从 .in 生成头文件
# #define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
configure_file(TutorialConfig.h.in TutorialConfig.h)
# 添加 cmake 子目录, 目录中需含有 CMakeLists.txt,会继承父级变量
add_subdirectory(MathFunctions)
# 通过用户选项 USE_LIB 使链接库作为可选项
# 在 .in 头文件模板中可通过 #cmakedefine USE_LIB 获取选项值,以进一步条件编译
#if(USE_LIB)
# add_subdirectory(LibName)
# list(APPEND EXTRA_LIBS LibName)
# list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/LibName")
#endif()
#target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
#target_include_directories(Tutorial PUBLIC
# "${PROJECT_BINARY_DIR}"
# ${EXTRA_INCLUDES}
# )
# 声明可执行程序目标
add_executable(Tutorial tutorial.cxx)
set_target_properties(Tutorial PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
# PUBLIC 之后的库和目标被链接,并成为链接接口的一部分。
# PRIVATE 后面的库和目标被链接,但不作为链接接口的一部分。
# INTERFACE 后面的库被附加到链接接口,不用于链接<目标>。
target_link_libraries(Tutorial PUBLIC MathFunctions tutorial_compiler_flags)
# 添加头文件路径,因为前面生成的 TutorialConfig.h 在项目编译目录中,需要指定该目录以找到该文件
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
)
# add the install targets
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
DESTINATION include
)
# cmake 3.15 开始可以通过 cmake --install <dir> [<options>] 命令安装
# 如 cmake --install . --config Release
# CMAKE_INSTALL_PREFIX 用于指定安装的根目录
# 可以通过 --prefix 覆盖安装位置, cmake --install . --prefix "/home/myuser/installdir"
# 启用测试功能
enable_testing()
# 添加 CTest 测试用例
add_test(NAME Runs COMMAND Tutorial 25)
# 添加 CTest 测试用例
add_test(NAME Usage COMMAND Tutorial)
# 通过正则表达式验证无参调用时提醒信息是否正确
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# 通过定义函数批量添加测试
function(do_test target arg result)
add_test(NAME Comp${arg} COMMAND ${target} ${arg})
set_tests_properties(Comp${arg}
PROPERTIES PASS_REGULAR_EXPRESSION ${result}
)
endfunction()
# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
# 设置 CPack 打包参数
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)
# 安装 MathFunctionsTargets.cmake
install(EXPORT MathFunctionsTargets
FILE MathFunctionsTargets.cmake
DESTINATION lib/cmake/MathFunctions
)
include(CMakePackageConfigHelpers)
# generate the config file that is includes the exports
configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake"
INSTALL_DESTINATION "lib/cmake/example"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)
# generate the version file for the config file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfigVersion.cmake"
VERSION "${Tutorial_VERSION_MAJOR}.${Tutorial_VERSION_MINOR}"
COMPATIBILITY AnyNewerVersion
)
# install the configuration file
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsConfig.cmake
DESTINATION lib/cmake/MathFunctions
)
# 以上为我们的项目生成了一个可重定位的 CMake 配置,可以在项目安装或打包后使用。
# 如果我们希望我们的项目也能在构建目录中使用需要在添加以下内容:
export(EXPORT MathFunctionsTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/MathFunctionsTargets.cmake"
)
# 示例理解目标属性 及 PRIVATE、PUBLIC、INTERFACE 范围关键字
# 0. INTERFACE_* 中的目标属性是 usage requirements :使用要求,明确被使用的条件,被其他目标使用时,会收到这些目标属性
# 1. 在编译二进制目标的源文件时,会使用 INCLUDE_DIRECTORIES、COMPILE_DEFINITIONS 和 COMPILE_OPTIONS 定义的目标属性内容。
# 2. target_include_directories()、target_compile_definitions()和target_compile_options()
# 指定编译目标的构建规范和使用要求。通过这些命令分别填充 INCLUDE_DIRECTORIES、COMPILE_DEFINITIONS 和 COMPILE_OPTIONS 目标属性及 INTERFACE_* 目标属性。
# PRIVATE 模式只填充目标属性的非 INTERFACE_* 变量,而 INTERFACE 模式只填充 INTERFACE_* 变量。
# PUBLIC模式填充各自目标属性的两个变体。
# 3. 使用要求可以在依赖目标中传播,通过 target_link_libraries 控制传播范围
#set(srcs archive.cpp zip.cpp)
#if (LZMA_FOUND)
# list(APPEND srcs lzma.cpp)
#endif()
#add_library(archive SHARED ${srcs})
#if (LZMA_FOUND)
# # PRIVATE模式仅填充 COMPILE_DEFINITIONS,相当于编译本目标 archive 时指定 -DBUILDING_WITH_LZMA,不会传播到依赖该库的目标
## target_compile_definitions(archive PRIVATE BUILDING_WITH_LZMA)
#endif()
## 填充 INTERFACE_COMPILE_DEFINITIONS,编译本目标 archive 时不使用 INTERFACE_* 中的目标属性。可传播到依赖该库的目标
#target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)
#add_executable(consumer)
## 链接 archive 到 consumer,并且收到 archive 的使用要求 INTERFACE_COMPILE_DEFINITIONS
## 编译可执行程序时带有 -DUSING_ARCHIVE_LIB
#target_link_libraries(consumer archive)
#add_library(archive archive.cpp)
#target_compile_definitions(archive INTERFACE USING_ARCHIVE_LIB)
#add_library(serialization serialization.cpp)
#target_compile_definitions(serialization INTERFACE USING_SERIALIZATION_LIB)
#add_library(archiveExtras extras.cpp)
#target_link_libraries(archiveExtras PUBLIC archive) # 会传播填充 INTERFACE_*
#target_link_libraries(archiveExtras PRIVATE serialization)
# 生成 archiveExtras 时会带有使用要求中的目标属性 -DUSING_ARCHIVE_LIB 和 -DUSING_SERIALIZATION_LIB
#add_executable(consumer consumer.cpp)
# 生成 consumer 会使用 -DUSING_ARCHIVE_LIB,接收到使用要求 INTERFACE_*
#target_link_libraries(consumer archiveExtras)
子目录 MathFunctions/CMakeLists.txt
2个链接库的编译安装
# 声明为库目标,默认为静态库 == add_library(MathFunctions STATIC MathFunctions.cxx)
# 启用 BUILD_SHARED_LIBS 变量会覆盖强制编译动态库
add_library(MathFunctions MathFunctions.cxx)
# 添加 INTERFACE 作用域声明任何使用该库的项目都包含当前源码路径,以查找头文件
# 说明:
# 如果依赖关系只被库的实现使用,而在头文件中,则使用 PRIVATE 关键字指定它。
# 如果在库的头文件中额外使用了依赖项(例如用于类继承),那么应该将其指定为 PUBLIC 依赖项。
# 如果不被库的实现使用,而只是像外部提供头文件的依赖项应该被指定为 INTERFACE 依赖项。
# 目标信息,编译为 ${CMAKE_CURRENT_SOURCE_DIR}
target_include_directories(MathFunctions
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>
)
# 用户选项,是否使用自定义的库
option(USE_MYMATH "Use tutorial provided math implementation" ON)
if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
target_link_libraries(MakeTable PRIVATE tutorial_compiler_flags)
# add the command to generate the source code
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
DEPENDS MakeTable
)
# library that just does sqrt
add_library(SqrtLibrary STATIC
mysqrt.cxx
${CMAKE_CURRENT_BINARY_DIR}/Table.h
)
# state that we depend on our binary dir to find Table.h
target_include_directories(SqrtLibrary PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
)
# state that SqrtLibrary need PIC when the default is shared libraries
set_target_properties(SqrtLibrary PROPERTIES
POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
)
target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()
target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
# define the symbol stating we are using the declspec(dllexport) when
# building on windows
target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
# setup the version numbering
set_property(TARGET MathFunctions PROPERTY VERSION "1.0.0")
set_property(TARGET MathFunctions PROPERTY SOVERSION "1")
# 通过变量 installable_libs 指定要安装的目标
set(installable_libs MathFunctions tutorial_compiler_flags)
if(TARGET SqrtLibrary)
# 开启 USE_MYMATH 时增加 SqrtLibrary 库到列表变量
list(APPEND installable_libs SqrtLibrary)
endif()
# EXPORT 关键字会生成一个CMake文件,包含 install 命令中列出的所有目标。
install(TARGETS ${installable_libs}
EXPORT MathFunctionsTargets
DESTINATION lib)
# 需要安装的头文件
install(FILES MathFunctions.h DESTINATION include)