cmake


cmake仓库存放了一些学习的例子

1. 为什么要cmake?

有很多的Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。CMake 就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件

2. make install

  • 自定义编译之后的.o文件和.h文件的路径

    2.1 主目录下的CMakeLists.txt

    cmake_minimum_required(VERSION 2.8)
    
    project(demo1)
    #PROJECT_SOURCE_DIR/CMAKE_SOURCE_DIR/_SOURCE_DIR: 为包含PROJECT()命令的最近一个CMakeLists.txt文件所在的文件夹路径。
    #PROJECT_BINARY_DIR/CMAKE_BINARY_DIR/_BINARY_DIR: 运行cmake命令的目录,即工程编译发生的路径。
    
    configure_file(
        "${PROJECT_SOURCE_DIR}/config.h.in"
        "${PROJECT_BINARY_DIR}/config.h"
    )
    option(USE_MYMATH "Use the provide library" ON)
    
    if(USE_MYMATH)
        include_directories("${PROJECT_SOURCE_DIR}/math")
        add_subdirectory(math)
        set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
    endif(USE_MYMATH)
    
    aux_source_directory(. DIR_SRCS)
    
    # 指定生成exe文件
    
    add_executable(Demo1 ${DIR_SRCS})
    target_link_libraries(Demo1 ${EXTRA_LIBS})
    
    # 指定生成的文件目录
    install(TARGETS Demo1 DESTINATION bin)
    install(FILES "${PROJECT_BINARY_DIR}/config.h" DESTINATION include)
    
    • cmake_minimum_required 最低cmake版本要求
    • project 项目名称
    • configure_file 读取配置文件,这里写config.h.in就行,config.h会自动生成
    • option 作为ON和OFF,两个选项
    • if里面的变量不需要${}
    • include_directories找头文件的路径
    • add_subdirectory添加子文件夹,下面存放了一些需要的类
    • set 把后面的value赋值给前面的变量,这里:
      • set(EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
      • EXTRA_LIBS ${EXTRA_LIBS} 是为了,如果EXTRA_LIBS有值,就加一块,没有就是空
      • 和 path=${path}:/usr/ 一样
    • aux_source_directory 找到当前文件夹的需要执行的文件
    • add_executable 生成的可执行文件的名字
    • target_link_libraries 链接子库(main需要用到的函数和类)
    • install(TARGETS Demo1 DESTINATION bin) .o文件的路径,默认值是/usr/local/,这里是/usr/local/bin
    • 可以使用set来修改内置变量修改CMAKE_INSTALL_PREFIX的值
    • install(FILES “${PROJECT_BINARY_DIR}/config.h” DESTINATION include) .h 的文件路径,这里是/usr/local/include’

    2.2 math目录下的CMakeLists.txt

    aux_source_directory(. DIR_LIB_SRCS)
    
    # 指定生成 MathFunctions 链接库
    add_library (MathFunctions ${DIR_LIB_SRCS})
    install(TARGETS MathFunctions DESTINATION bin)
    install(FILES MathFunctions.h DESTINATION include)
    

    2.3工程添加测试

    CMake 提供了一个称为 CTest 的测试工具。我们要做的只是在项目根目录的 CMakeLists 文件中调用一系列的 add_test 命令。

    # 启用测试
      enable_testing()
    
      # 测试程序是否成功运行
      add_test (test_run Demo 5 2)
    
      # 测试帮助信息是否可以正常提示
      add_test (test_usage Demo)
      set_tests_properties (test_usage
      PROPERTIES PASS_REGULAR_EXPRESSION "Usage: .* base exponent")
    
      # 测试 5 的平方
      add_test (test_5_2 Demo 5 2)
    
      set_tests_properties (test_5_2
      PROPERTIES PASS_REGULAR_EXPRESSION "is 25")
    
      # 测试 10 的 5 次方
      add_test (test_10_5 Demo 10 5)
    
      set_tests_properties (test_10_5
      PROPERTIES PASS_REGULAR_EXPRESSION "is 100000")
    
      # 测试 2 的 10 次方
      add_test (test_2_10 Demo 2 10)
    
      set_tests_properties (test_2_10
      PROPERTIES PASS_REGULAR_EXPRESSION "is 1024")
    

    或者使用宏定义

    # 定义一个宏,用来简化测试工作
      macro (do_test arg1 arg2 result)
      add_test (test_${arg1}_${arg2} Demo ${arg1} ${arg2})
      set_tests_properties (test_${arg1}_${arg2}
       PROPERTIES PASS_REGULAR_EXPRESSION ${result})
      endmacro (do_test)
      
      # 使用该宏进行一系列的数据测试
      do_test (5 2 "is 25")
      do_test (10 5 "is 100000")
      do_test (2 10 "is 1024")
      
    

3. configure_file

  • configure_file(input output options)
  • 将一个文件(由input参数指定)拷贝到指定位置(由output参数指定),并根据options修改其内容。
  •  configure_file命令一般用于自定义编译选项或者自定义宏的场景。configure_file命令会根据options指定的规则,自动对input文件中cmakedefine关键字及其内容进行转换
Demo

```
#cmakedefine var1
#cmakedefine var2 "@var2@" #注意:@@之间的名称要与cmakedefine后的变量名一样
#cmakedefine var3 "${var3}" #注意:${}之间的名称要与cmakedefine后的变量名一样

```

4. cmake获得git的hash和版本,综合例子

Github代码
有些时候,我们需要在项目中标明版本号、Git的hash号、编译时间等信息,但是显然,对于Git的hash号、编译时间我们不想自己手动填写。现在提供一种途径,将这些信息写入到头文件中,再编译到so库文件或者可执行程序中。

这样,就可以通过提供库文件的接口或者可执行程序的打印中得到这些值了

4.1 utils.cmake 文件

```
# 用于获取git的hash和分支
macro(get_git_hash _git_hash)
    find_package(GIT QUIET) 
    if(GIT_FOUND)# GIT_FOUND是内置变量
        execute_process( # cmake:execute_process执行外部命令
            # command 是命令行执行
            # GIT_EXECUTABLE 是git执行路径, -1 是最后一条
            #-—pretty=:使用其他格式显示历史提交信息,可选项有:oneline,short,medium,full,fuller,email,raw以及format:<string>,默认为medium,如:
            # --pretty=oneline:一行显示,只显示哈希值和提交说明(--online本身也可以作为单独的属性)
            # --pretty=format:” ":控制显示的记录格式,如:
            #     %H  提交对象(commit)的完整哈希字串
            #     %h  提交对象的简短哈希字串
            #     %T  树对象(tree)的完整哈希字串
            #     %t  树对象的简短哈希字串
            #     %P  父对象(parent)的完整哈希字串
            #     %p  父对象的简短哈希字串
            #     %an 作者(author)的名字
            #     %ae 作者的电子邮件地址
            #     %ad 作者修订日期(可以用 -date= 选项定制格式)
            #     %ar 作者修订日期,按多久以前的方式显示
            #     %cn 提交者(committer)的名字
            #         作者和提交者的区别不知道是啥?
            #         作者与提交者的关系:作者是程序的修改者,提交者是代码提交人(自己的修改不提交是怎么能让别人拉下来再提交的?)
            #         其实作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人。所以,当你为某个项目发布补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者(soga)
            #     %ce 提交者的电子邮件地址
            #     %cd 提交日期(可以用 -date= 选项定制格式)
            #     %cr 提交日期,按多久以前的方式显示
            #     %s  提交说明
            COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%h
            OUTPUT_VARIABLE ${_git_hash}        # 输出字符串存入变量
            # OUTPUT_VARIABLE :指定接收输出到标准输出 内容的变量;
            OUTPUT_STRIP_TRAILING_WHITESPACE    # 删除字符串尾的换行符
            ERROR_QUIET                         # 对执行错误静默
            WORKING_DIRECTORY                   # 执行路径
            ${CMAKE_CURRENT_SOURCE_DIR}
            )
        endif()
endmacro()

# get git branch
macro(get_git_branch _git_branch)   # 宏的开始
    find_package(Git QUIET)     # 查找Git,QUIET静默方式不报错
    if(GIT_FOUND)
    execute_process(          # 执行一个子进程
        COMMAND ${GIT_EXECUTABLE} symbolic-ref --short -q HEAD
        OUTPUT_VARIABLE ${_git_branch}        # 输出字符串存入变量
        OUTPUT_STRIP_TRAILING_WHITESPACE    # 删除字符串尾的换行符
        ERROR_QUIET                         # 对执行错误静默
        WORKING_DIRECTORY                   # 执行路径
        ${CMAKE_CURRENT_SOURCE_DIR}
        )
    endif()
endmacro()                      # 宏的结束

```
  • 使用宏定义来定义两个宏定义
  • 使用execute_process执行外部命令
  • OUTPUT_VARIABLE可以获得输出到命令行的东西并赋值给变量

4.2 CMakeLists.txt

```
cmake_minimum_required(VERSION 2.9)

include(utils.cmake)
project(demo_git_config)


string(TIMESTAMP BUILD_TIMESTAMP "%Y-%m-%d %H:%M:%S")
message("Build timestamp is ${BUILD_TIMESTAMP}")

set(VERSION_MAJOR 0)
set(VERSION_MINOR 0)
set(VERSION_PATCH 1)
message("Version is ${VERSION_MAJOR} ${VERSION_MINOR} ${VERSION_PATCH}")

set(GIT_HASH "")
get_git_hash(GIT_HASH)
message("Git hash is ${GIT_HASH}")

set(GIT_BRANCH "")
get_git_branch(GIT_BRANCH)
message("Git branch is ${GIT_BRANCH}")


configure_file(
    "${PROJECT_SOURCE_DIR}/include/utils.h.in"
    "${PROJECT_SOURCE_DIR}/include/utils.h"
)
add_executable(${PROJECT_NAME} main.cpp)

include_directories(
    ${PROJECT_SOURCE_DIR}/include
)

install(TARGETS ${PROJECT_NAME}
    DESTINATION ${PROJECT_SOURCE_DIR})

```
  • 调用了宏定义

4.3 utils.h.in

```
#ifndef UTILS_H_IN
#define UTILS_H_IN

#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@
#define VERSION_PATCH @VERSION_PATCH@

#define BUILD_TIMESTAMP "@BUILD_TIMESTAMP@"

#define GIT_BRANCH "@GIT_BRANCH@"
#define GIT_HASH "@GIT_HASH@"

#endif // UTILS_H_IN

```

4.4 执行cmake .

生成utils.h如下

```
#ifndef UTILS_H_IN
#define UTILS_H_IN

#define VERSION_MAJOR 0
#define VERSION_MINOR 0
#define VERSION_PATCH 1

#define BUILD_TIMESTAMP "2022-11-26 21:35:07"

#define GIT_BRANCH "master"
#define GIT_HASH "0e1f110"

#endif // UTILS_H_IN

``` 

4.5 使用make链接.o文件,生成可执行文件

输出如下

```
(base) moule@mouledeMacBook-Air demo_git_config % ./demo_git_config 
version is 0, 0, 1
timestamp is 2022-11-26 21:35:07
git is master, 0e1f110
```

Author: Moule Lin
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Moule Lin !
  TOC