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++宏
  • 链接静态库

1ONYEStrueY或非零数,则逻辑变量为true
0OFFNOfalseNIGNORE、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.a
  • SHARED 生成 name.lib
  • 可以不指定静态或动态库,而是通过全局的BUILD_SHARED_LIBSFALSETRUE来指定
  • 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()
posted @ 2022-10-21 19:20  某某人8265  阅读(291)  评论(0编辑  收藏  举报