cmake Tutorial 摘要(1) Step1-6


0. 前言


Step 1 - A Basic Starting Point

1.1. 基本功能

  • cmake 的基本功能就是三个命令,下面简单介绍。
  • cmake_minimum_required(VERSION 3.10)
  • project(Tutorial)
    • 作用:提供项目名称、版本、使用编译语言等信息
    • 官方文档
project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])
  • add_executable(Toturial tutorial.cxx)
add_executable(<name> [WIN32] [MACOSX_BUNDLE]
               [EXCLUDE_FROM_ALL]
               [source1] [source2 ...])

1.2. 添加版本号

  • 设置版本好就是在 project 命中中指定 VERSION
  • 值得一提的是,在设置过VERSION属性后,几个cmake变量会自动定义:
    • PROJECT_VERSION, <PROJECT-NAME>_VERSION
    • PROJECT_VERSION_MAJOR, <PROJECT-NAME>_VERSION_MAJOR
    • PROJECT_VERSION_MINOR, <PROJECT-NAME>_VERSION_MINOR
    • PROJECT_VERSION_PATCH, <PROJECT-NAME>_VERSION_PATCH
    • PROJECT_VERSION_TWEAK, <PROJECT-NAME>_VERSION_TWEAK.
  • 之后,可通过 configure_file 命令,将版本号写入对应的头文件中。具体方法分为三步
    • 第一步:定义模版文件,即本例中的 TutorialConfig.h.in,如下。
    • 第二步:通过 configure_file(TutorialConfig.h.in TutorialConfig.h) 将模版中的参数转换为 PROJECT 命令中的VERSION。
      • configure_file 的功能就是将模版文件所有 @VAR@ 转换为cmake参数 ${VAR}
      • 下面源码中的 @Tutorial_VERSION_MAJOR@ 就是之前cmake自动定义的变量。
    • 第三步:将生成的 TutorialConfig.h 添加到项目中,即target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")
      • target_include_directories:将头文件目录添加到一个target中
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

1.3. 设置C++标准

  • 设置C++版本的本质就是指定若干cmake参数
    • CMAKE_CXX_STANDARD & CMAKE_CXX_STANDARD_REQUIRED
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

Step 2 - Adding a Library

2.1. 基本功能

  • 目标:添加第三方库。
    • 这个第三方库指的是源码(需要编译),而不是动态库、静态库。
  • 基本流程:
    • 第一步:准备好第三方库的源码,并在第三方项目中建立 CMakeLists.txt。
      • CMakeLists.txt文件中添加 add_library() 指令。
    • 第二步:在原始项目 CMakeLists.txt 文件中关联第三方项目以及添加头文件。
  • 项目根目录的 CMakeLists.txt 中添加依赖有以下几个内容
    • 通过 add_subdirectory 添加第三方库路径,这样才会编译、构建第三方项目。
    • 通过 target_link_libraries 添加库的名称。这个名称就是在第三方库的add_library中定义的。
    • 通过 target_include_directories 添加第三方库的头文件。

2.2. 其他功能

  • 本例中实现了一些其他功能:
    • 有两个版本的sqrt实现,一个是C++标准库,一个是我们自己实现的第三方库。
    • 在CMake中采用了一个option来指定sqrt的来源。
  • 为了实现这个功能,需要在 CMakeLists.txt 文件以及C++源码进行一些修改。
  • CMakeLists.txt 文件中
# 添加选项,用来选择是否使用自定义第三方库
option(USE_MYMATH "Use tutorial provided math implementation" ON)

# 通过条件语句,选择是否将第三方库添加到项目中
if(USE_MYMATH)
  add_subdirectory(MathFunctions) 
  list(APPEND EXTRA_LIBS MathFunctions)
  list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()
  • C++ 源码中,使用 #ifdef USE_MYMATH #endif 来指定使用的第三方库。

Step 3 - Adding Usage Requirements for Library

  • Usage requirements
    • 不知道该怎么翻译。直译就是“使用要求”。
    • 看后面的内容,大概意思就是在为target添加各类内容(如头文件、libs)的同时,做一些其他工作。
  • 主要影响的命令有:
    • target_compile_definitions()
    • target_compile_options()
    • target_include_directories()
    • target_link_libraries()
  • 下面举个例子:
    • 一个假设:对于所有要添加的libs,都要添加对应的头文件所在路径。
    • 上一个sample中存在的问题:需要分别手动添加libs与头文件目录。
    • 修改第三方库的CMakeLists.txt文件
      • 通过INTERFACE,为第三方库的target指定头文件。
target_include_directories(MathFunctions
          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
          )

Step 4 - Installing and Testing

  • 功能:添加 install 以及 test 相关内容

4.1. install

  • 本质就是将文件复制/移动到目标位置。
  • 本例中就是将动态库so文件与头文件移动到对应的 lib 以及 include 文件夹中
    • install 命令官方文档
    • 实现的功能:
      • 将第三方库中的so文件放到lib目录下,头文件放到include目录下。
      • 将根目录中头文件放到include目录下,可执行文件放到bin目录下。
    • 其中,DESTINATION 指定的 lib/include/bin 目录,如果是绝对路径则直接使用,如果是相对路径则要与 CMAKE_INSTALL_PREFIX 配合使用。
# 第三方库,末尾添加
install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)

# 根目录,末尾添加
install(TARGETS Tutorial DESTINATION bin)
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  DESTINATION include
  )

4.2. Testing

  • 简单说,就是测试构建出来的可执行文件。
  • 主要实现方式就是 add_test 命令
add_test(NAME <name> COMMAND <command> [<arg>...]
         [CONFIGURATIONS <config>...]
         [WORKING_DIRECTORY <dir>]
         [COMMAND_EXPAND_LISTS])
  • 在根目录CMakeLists.txt后添加以下内容
    • 重新构建项目后,可通过 ctest 命令进行测试,如 ctest -N 判断一共有那几个测试,ctest -VV 执行测试并输出过程结果。
    • 下面有的测试是判断程序是否能够正常运行,有的判断输出结果。
enable_testing()

# does the application run
add_test(NAME Runs COMMAND Tutorial 25)

# does the usage message work?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
  )

# define a function to simplify adding tests
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)

# 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")

Step 5 - Adding System Introspection

  • 目标:根据某些库、函数是否存在进行不同的操作。
    • 例如,如果某库不存在,则到某处加载。

5.1. 基本实现

  • 主要命令:CheckSymbolExists
    • 格式:check_symbol_exists(<symbol> <files> <variable>)
    • 功能:判断symbol在files中是否存在,将结果保存到variable中。
  • CMakeLists.txt 中的修改
# 根目录下添加
# 注意,要放到 add_subdirectory(MathFunctions) 后,configure_file(TutorialConfig.h.in TutorialConfig.h)前
include(CheckSymbolExists)
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
if(NOT (HAVE_LOG AND HAVE_EXP))
  unset(HAVE_LOG CACHE)
  unset(HAVE_EXP CACHE)
  set(CMAKE_REQUIRED_LIBRARIES "m")
  check_symbol_exists(log "math.h" HAVE_LOG)
  check_symbol_exists(exp "math.h" HAVE_EXP)
  if(HAVE_LOG AND HAVE_EXP)
    target_link_libraries(MathFunctions PRIVATE m)
  endif()
endif()
  • 之后通过 #cmakedefine HAVE_EXP 来定义宏。

5.2. 进阶实现

  • 前一种方法不太方便:
    • 需要通过 #cmakedefine 在头文件中定义宏
    • 需要在原文件中引入头文件
  • 建议使用 target_compile_definitions
    • 作用:为目标添加宏
    • 基本形式如下
target_compile_definitions(<target>
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
  • 本例中的代码如下
# add compile definitions
if(HAVE_LOG AND HAVE_EXP)
  target_compile_definitions(MathFunctions
                             PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()

6. Adding a Custom Command and Generated File

  • 目标:通过自定义命令行生成文件。
  • 实现细节:
    • 编写C++程序生成文件。
    • 通过配置cmake命令,先编译上述c++程序,并通过命令行调用该程序生成文件。
    • 本例中,生成的是一个头文件,所以需要修改cmake将该头文件导入到项目中。
  • 要实现上述功能,主要就是修改第三方库的 CMakeLists.txt。
# 生成命令,并调用该命令生成文件
add_executable(MakeTable MakeTable.cxx)
add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  )

# 将上述文件导入项目中
add_library(MathFunctions
            mysqrt.cxx
            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
            )
target_include_directories(MathFunctions
          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
          PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
          )
add_custom_command(OUTPUT output1 [output2 ...]
                   COMMAND command1 [ARGS] [args1...]
                   [COMMAND command2 [ARGS] [args2...] ...]
                   [MAIN_DEPENDENCY depend]
                   [DEPENDS [depends...]]
                   [BYPRODUCTS [files...]]
                   [IMPLICIT_DEPENDS <lang1> depend1
                                    [<lang2> depend2] ...]
                   [WORKING_DIRECTORY dir]
                   [COMMENT comment]
                   [DEPFILE depfile]
                   [JOB_POOL job_pool]
                   [VERBATIM] [APPEND] [USES_TERMINAL]
                   [COMMAND_EXPAND_LISTS])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值