Unique's Blog

cmake示例

2022-10-29 · 2060字 · 10 min read
🏷️  CMake

官方教程:https://cmake.org/cmake/help/latest/guide/tutorial/index.html
一些总结:https://wangpengcheng.github.io/2019/08/13/learn_cmake/

基础

最小配置的 CMakeLists.txt

# Set the minimum required version of CMake to be 3.10
cmake_minimum_required(VERSION 3.10)

# Create a project named Tutorial
project(Tutorial VERSION 1.0)
# set(Tutorial_VERSION_MAJOR 1)
# set(Tutorial_VERSION_MINOR 0)

# Set the variable CMAKE_CXX_STANDARD to 11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# Add an executable called Tutorial to the project
add_executable(Tutorial tutorial.cpp)
  • 如果文件较多,aux_source_directory(. DIR_SRCS) 将当前目录所有源文件的文件名赋值给变量 DIR_SRCS,使用 add_executable(Tutorial ${DIR_SRCS})

可选:MacOS + gcc + clangd 最小配置

# Set the minimum required version of CMake to be 3.10
cmake_minimum_required(VERSION 3.10)

# Create a project named Tutorial
project(Tutorial VERSION 1.0)
# set(Tutorial_VERSION_MAJOR 1)
# set(Tutorial_VERSION_MINOR 0)

# Set the variable CMAKE_CXX_STANDARD to 11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# set -isysroot for macOS # default to macOS [according to gcc version]
if(APPLE)
    set(CMAKE_OSX_SYSROOT /Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk)
    set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0) # [according to gcc built for macos version]
endif()

# Add an executable called Tutorial to the project
aux_source_directory(. DIR_SRCS)
add_executable(Tutorial ${DIR_SRCS})

生成lib并可选使用

  • 生成lib,并在项目中可选地使用

项目中会生成 lib 库文件,并在其他部分可选的链接使用该库

cmake_minimum_required(VERSION 3.10)

# set the project name and version
project(Tutorial VERSION 1.0)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

option(USE_MYMATH "Use tutorial provided math implementation" ON)
configure_file(TutorialConfig.h.in TutorialConfig.h)

if(USE_MYMATH)
  add_subdirectory(MathFunctions) # 子目录会生成 MathFunctions lib
  list(APPEND EXTRA_LIBS MathFunctions)
  list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

# add the executable
add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           ${EXTRA_INCLUDES})
  1. 使用 option 命令,可以定义一个boolean变量

  2. 使用 configure_file 命令,将配置头文件,经过变量替换生成C++ header file:

    • @var@ 变量,使用 CMakeLists.txt 中定义个变量进行替换;
    • #cmakedefine 替换为 #define ,如果后面定义的boolean变量是 ON 则进行替换
    • 生成的头文件放置在 ${PROJECT_BINARY_DIR} 目录,即 cmake 的 build 目录
  3. 使用 if() 来控制添加 lib 生成,定义 EXTRA_LIBS 列表EXTRA_INCLUDES 列表,默认为空

  4. 使用 target_link_libraries 对 target 链接指定的库

  5. 使用 target_include_directories 对 target 添加指定的头文件目录

说明:可以在配置项目时,指定 cmake .. -DUSE_MYMATH=OFF 来覆盖定义的值

生成lib并可选使用(INTERFACE)

对上一个 cmake 改进,之前的做法需要链接 lib 库文件,并且在上层中需要显示的指定库对应的 include 头文件目录,非常繁琐。

通过 Usage Requirements 来控制库在链接时的传递属性,使用 INTERFACE 来表达该语义:任意链接该库 target 必须 include 当前/指定的源目录(用于搜索头文件)。INTERFACE 表示消费者需要,但是生产者不需要。(对于库而言,生成的 lib target 不需要,但是需要链接该库的 target 必须包含指定的目录)

子目录生成 lib 并指定 Usage RequirementsCMakeLists.txt

add_library(MathFunctions mysqrt.cxx)

# Hint: Use target_include_directories with the INTERFACE keyword
# 尽管 MathFunctions 库不需要包含当前源目录,但是任何链接到 MathFunctions 的库都需要包含当前源目录
target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

top-level CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

# set the project name and version
project(Tutorial VERSION 1.0)

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)

# configure a header file to pass some of the CMake settings
# to the source code
configure_file(TutorialConfig.h.in TutorialConfig.h)

# add the MathFunctions library
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
  list(APPEND EXTRA_LIBS MathFunctions)
endif()

# add the executable
add_executable(Tutorial tutorial.cxx)

target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})
# find TutorialConfig.h
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

使用生成器表达式

https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#manual:cmake-generator-expressions(7)

生成器表达式,主要可以生成复杂的条件表达式,用于条件编译,条件链接等等。

例如,使用一个 interface library 来指定编译选项,使用生成器表达式对不同的编译器选择不同的编译选项:

cmake_minimum_required(VERSION 3.15)

# set the project name and version
project(Tutorial VERSION 1.0)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# * Creating an interface library called tutorial_compiler_flags
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

# Create helper variables to determine which compiler we are using
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")

# With nested generator expressions, only use the flags for the build-tree
target_compile_options(tutorial_compiler_flags INTERFACE
  "$<${gcc_like_cxx}:$<BUILD_INTERFACE:-Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused>>"
  "$<${msvc_cxx}:$<BUILD_INTERFACE:-W3>>"
)

# should we use our own math functions
option(USE_MYMATH "Use tutorial provided math implementation" ON)
configure_file(TutorialConfig.h.in TutorialConfig.h)

# add the MathFunctions library
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
  list(APPEND EXTRA_LIBS MathFunctions)
endif()

# add the executable
add_executable(Tutorial tutorial.cxx)

# Link to tutorial_compiler_flags
target_link_libraries(Tutorial PUBLIC tutorial_compiler_flags ${EXTRA_LIBS})
target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")
  • 使用生成器表达式,指定 cmake 最小版本为 3.15

  • 使用 INTERFACE 来创建一个接口库,用于传递/指定 Usage Requirements

  • 表达式:

    • $<COMPILE_LANG_AND_ID:CXX,...> 如果编译单元的语言是CXX,并且编译器ID与后面之一匹配,表达式为1否则为0
    • $<BUILD_INTERFACE: STRING> 在相同build为STRING,否则为空;上面的目的是:为了使得我们 installed project 的其他消费者不会继承编译选项;

install and test

安装 executable | lib | headers

set(installable_libs MathFunctions tutorial_compiler_flags)
install(TARGETS ${installable_libs} DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include/MathFunc)
# bin file
install(TARGETS Tutorial DESTINATION bin)

构建和安装:

cmake --build .
cmake --install . --prefix "/home/myuser/installdir" # 有默认的位置,例如 /usr/local

或者

cmake --build . --target install

简单的测试样例

# enable testing
enable_testing()
# basic test, return value is zero
add_test(NAME Runs COMMAND Tutorial 25)
# verify that the output of the test contains certain strings.
add_test(NAME Correctness COMMAND Tutorial 4 )
set_tests_properties(Correctness PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2")

# 定义函数 do_test 添加多个测试
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")

构建完成后,使用 ctest -VV 进行测试

添加系统检查

例如测试系统是否支持某个库/函数:

# does this system provide the log and exp functions?
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
  #include <cmath>
  int main() {
    std::log(1.0);
    return 0;
  }
" HAVE_LOG)
check_cxx_source_compiles("
  #include <cmath>
  int main() {
    std::exp(1.0);
    return 0;
  }
" HAVE_EXP)

if(HAVE_LOG AND HAVE_EXP)
  target_compile_definitions(MathFunctions
                             PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()

根据测试结果,添加编译定义 target_compile_definitions()

其最终指令结果:g++ -DHAVE_EXP -DHAVE_LOG

添加自定义命令

在build开始可以先生成 output,用于后续的 target build:

add_executable(MakeTable MakeTable.cxx)
# add a custom command that specifies how to produce Table.h by running MakeTable.
add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  )

build 会先指定自定义命令中的指定,生成 output 文件,在 cmake --build . 执行

【说明:在指定 add_library 等,可以指定依赖的头文件用于说明构建的依赖关系】

打包程序

使用 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") # selects a file format for the source package
include(CPack)

构建 build 后,使用 cpack 打包即可,主要工作:

  • 导入 InstallRequiredSystemLibraries 模块,以便之后导入 CPack 模块;

  • 设置一些 CPack 相关变量,包括版权信息,版本信息,压缩格式;

  • 导入 CPack 模块。

添加导出配置

https://cmake.org/cmake/help/latest/guide/tutorial/Adding Export Configuration.html#cmakelists-txt-install-export

便于其他 CMake 项目可以复用我们的项目,无论是通过我们的构建目录、本地安装还是打包时。

行为:在构建目录build,本地安装(/usr/local/lib/cmake)或者在打包的文件夹中生成cmake配置文件。

好处:不用特别指定项目/库的相关 include/lib 路径,方便使用(指定 package 目录)

生成的文件:

  • xxx.cmake 目标信息

  • xxxConfig.cmake 通过 Config.cmake.in 文件生成,以便 CMake **find_package()**命令可以找到该项目(用于修改包的 PREFIX_DIR 路径)

  • xxxConfigVersion.cmake 记录软件包的版本和兼容性

使用package,例如打包后的目录为:

Tutorial-1.0-Darwin
├── bin
│   └── Tutorial
├── include
│   ├── MathFunctions.h
│   └── TutorialConfig.h
└── lib
    ├── cmake
    │   └── MathFunctions
    │       ├── MathFunctionsConfig.cmake
    │       ├── MathFunctionsConfigVersion.cmake
    │       ├── MathFunctionsTargets-noconfig.cmake
    │       └── MathFunctionsTargets.cmake
    ├── libMathFunctions.dylib
    └── libSqrtLibrary.a

使用非常方便:

list(APPEND CMAKE_PREFIX_PATH  "${PROJECT_SOURCE_DIR}/Tutorial-1.0-Darwin")
find_package(MathFunctions REQUIRED) 

# add the executable
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial PUBLIC MathFunctions) # no namespace
  • 使用 INTERFACE 自动包含链接库的头文件目录

本文链接: cmake示例

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

发布日期: 2022-10-29

最新构建: 2024-12-26

本文已被阅读 0 次,该数据仅供参考

欢迎任何与文章内容相关并保持尊重的评论😊 !

共 43 篇文章 | Powered by Gridea | RSS
©2020-2024 Nuo. All rights reserved.