cmake
CMake Tutorial — CMake 3.27.1 Documentation
1. 构建可执行程序
其中 cmake_minimum_required project add_executable 三者是必须的
- 版本要求
- 项目名称、版本
- 设置 cmake 变量
- cpp读取cmake变量
- 设置搜索路径
cmake_minimum_required(VERSION 3.10)
project(ProjectName VERSION 1.0)
# 设置 CMkae 变量,指定c++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# 指定程序输出目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
# 配置头文件,以便能够访问到 CMake 设置的变量
# 将 TutorialConfig.h.in 复制到 TutorialConfig.h
# 并将其中的占位符 `@var@` 替换为 CMakeLists.txt 中定义的变量
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@ 对应project中定义的主版本
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
configure_file(TutorialConfig.h.in TutorialConfig.h)
add_executable(ProjectName main.cpp)
# 查找包含文件
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")
cmake ..
cmake --build .
# 以上两条也可换位这一条命令
cmake -H. -Bbuild # -H. 表示当前目录中搜索根CMakeLists.txt文件。-Bbuild告诉CMake在一个名为build的目录中生成所有的文件。
2. 从子目录源码构建 library
.
├── build/
├── CMakeLists.txt
├── MathFunctions/
│ ├── CMakeLists.txt
│ ├── MathFunctions.cxx
│ ├── MathFunctions.h
│ ├── mysqrt.cxx
│ └── mysqrt.h
├── TutorialConfig.h.in
└── tutorial.cxx
内部 CMakeLists.txt
- 指定库文件
- 定义cmake变量默认值
- 条件选择
- 指定c++宏
- 链接静态库
1
、ON
、YES
、true
、Y
或非零数,则逻辑变量为true
0
、OFF
、NO
、false
、N
、IGNORE、NOTFOUND
、空字符串,或者以-NOTFOUND
为后缀,则逻辑变量为false
add_library(MathFunctions MathFunctions.cxx)
option(USE_MYMATH "Use tutorial provided math implementation" ON) # 定义 CMake 变量的默认值,可通过 `CMake .. -DUSE_MYMATH=OFF` 定义
if(USE_MYMATH)
target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH") # 定义c++中的宏,cpp文件中根据宏定义确定使用的头文件和库函数
add_library(SqrtLibrary STATIC mysqrt.cxx) # 生成静态库
target_link_libraries(MathFunctions PRIVATE SqrtLibrary) # 链接静态库
endif(USE_MYMATH)
顶层 CMakeLists.txt
- 运行子cmake项目
- 指定cmake变量
cmake .. -DUSE_MYMATH=OFF
cmake_minimum_required(VERSION 3.10)
project(Tutorial VERSION 1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
configure_file(TutorialConfig.h.in TutorialConfig.h)
# 在项目中运行子 CMake 项目
add_subdirectory(MathFunctions)
add_executable(Tutorial tutorial.cxx)
# 指定链接库
target_link_libraries(Tutorial PUBLIC MathFunctions)
# 指定搜索路径
target_include_directories(Tutorial PUBLIC
"${PROJECT_BINARY_DIR}"
"${PROJECT_SOURCE_DIR}/MathFunctions"
)
3. 库的使用条件
子目录的 CMakeLists.txt
# 使用当前库MathFunctions的项目都要包含当前库的源码目录,但库项目本身不需要
# 如此顶层项目中就不需要手动添加库的搜索路径
target_include_directories(MathFunctions
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} # INTERFACE 代表消费者需要,但是生产者不需要
)
# MathFunctions 库链接了一个新的库,这个库仅仅起到了约束作用
target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
在顶层CMake文件中可删去对 MathFunctions 的搜索路径的指定
指定 c++ 版本
# specify the C++ standard
# 指定当前项目C++版本
# set(CMAKE_CXX_STANDARD 11)
# set(CMAKE_CXX_STANDARD_REQUIRED True)
# 定义一个 INTERFACE 类型的库,仅定义版本
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)
4. 生成器表达式 —— 编译器选项
# 根据使用编译器的不同,变量的值为true或false
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")
# 为不同的编译器添加不同的警告条件
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>"
"$<${msvc_cxx}:-W3>"
)
# 指定调用此 cmakefile 的项目不添加警告条件
target_compile_options(tutorial_compiler_flags INTERFACE
"$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
"$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)
5. 安装、测试
cmake --install .
cmake --install . --config Release
cmake --build . --target install --config Debug
cmake --install . --prefix "/home/myuser/installdir" # 重写安装目录变量 CMAKE_INSTALL_PREFIX
安装
set(CMAKE_INSTALL_PREFIX "/usr/local")
set(installable_libs MathFunctions tutorial_compiler_flags)
if(TARGET SqrtLibrary)
list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs} DESTINATION lib) # 安装到 ${CMAKE_INSTALL_PREFIX}/lib
set(CMAKE_INSTALL_PREFIX "/usr/local")
# 安装到 ${CMAKE_INSTALL_PREFIX}/bin ${CMAKE_INSTALL_PREFIX}/include 下
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h" DESTINATION include )
测试
使用 ctest,测试目标是可执行性程序。可将gtest编写的测试程序编译成一个文件,再用ctest运行。
ctest -N
ctest -VV
ctest -C Debug -VV
enable_testing()
# 测试程序能够成功运行并返回0
add_test(NAME Runs COMMAND Tutorial 25)
# 测试程序打印信息是否符合预期
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
)
# 测试:输入为4,输出为"4 is 2"
add_test(NAME StandardUse COMMAND Tutorial 4)
set_tests_properties(StandardUse
PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2"
)
# 定义函数,多次测试
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_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")
静态库 动态库
有多种选择:
STATIC
生成 name.aSHARED
生成 name.lib- 可以不指定静态或动态库,而是通过全局的
BUILD_SHARED_LIBS
的FALSE
或TRUE
来指定 MODULE
OBJECT
生成静态库并连接到程序中
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-03 LANGUAGES CXX)
# 自动添加前后缀 lib .a .lib
add_library(message
STATIC # SHARED 对应动态库;OBJECT/MODULE/...
Message.h
Message.cpp
)
# 根据全局变量生成静态或动态库
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
add_library(myMath myMath.cpp)
add_executable(hello-world hello-world.cpp)
# 将静态库 message 链接到可执行程序 hello-world 中
target_link_libraries(hello-world message)
打印信息 列表对象
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-04 LANGUAGES CXX)
set(USE_LIBRARY OFF)
# 打印消息
message(STATUS "Compile sources into a library? ${USE_LIBRARY}")
# cmake变量决定创建动态或静态库
set(BUILD_SHARED_LIBS OFF)
# 引入新局部变量 _sources 包含两个 Message.hpp Message.cpp
list(APPEND _sources Message.hpp Message.cpp)
if(USE_LIBRARY)
add_library(message ${_sources}) # 全局变量决定了创建静态库
add_executable(hello-world hello-world.cpp)
target_link_libraries(hello-world message)
else()
add_executable(hello-world hello-world.cpp ${_sources})
endif()