CMake 官方文档入门

对比官方文档练习

step1 基本起始点

一个基本的项目:从源文件构建可执行程序

文件目录如下

CMake 官方文档入门_第1张图片

编写 CMakeLists.txt 如下:

# cmake 要求的最小版本
cmake_minimum_required(VERSION 3.24)
# 设置项目名称 可以设置版本号
project(Tutorial VERSION 1.0)
# 设置 c++ 的语言标准 C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

configure_file(TutorialConfig.h.in TutorialConfig.h)

# 添加可执行程序(前面的是可执行程序的名称 后面是所需要的 source 文件)
add_executable(Tutorial tutorial.cpp)

target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}")

解释:

  • 函数

  • configure_file():官方CMake教程对它的解释是:将文件复制到另一个位置并修改其内容。当然,这里的修改其内容也不是任意地修改,也是遵循一定的规则:将input文件复制到output文件,并在输入文件内容中的变量,替换引用为@VAR@或 V A R 的 变 量 值 。 每 个 变 量 引 用 将 替 换 为 该 变 量 的 当 前 值 , 如 果 未 定 义 该 变 量 , 则 为 空 字 符 串 。 可 能 有 些 绕 头 , 再 浅 显 一 点 : c o n f i g u r e f i l e , 复 制 一 份 输 入 文 件 到 输 出 文 件 , 替 换 输 入 文 件 中 被 @ V A R @ 或 者 {VAR}的变量值。每个变量引用将替换为该变量的当前值,如果未定义该变量,则为空字符串。可能有些绕头,再浅显一点:configure_file,复制一份输入文件到输出文件,替换输入文件中被@VAR@或者 VARconfigurefile@VAR@{VAR}引用的变量值。也就是说,让普通文件,也能使用CMake中的变量。

    configure_file( 
                   [COPYONLY] [ESCAPE_QUOTES] [@ONLY]
                   [NEWLINE_STYLE [UNIX|DOS|WIN32|LF|CRLF] ])
    
    
  • target_include_directories():指定目标包含的头文件路径

  • 内置变量

  • PROJECT_BINARY_DIR:全路径/build

  • PROJECT_SOURCE_DIR:全路径/代码在的地方

  • CMAKE_CXX_STANDARD_REQUIRED:可以为每个 Target 初始化 CXX_STANDARD_REQUIRED 属性。而 CXX_STANDARD_REQUIRED 存在的目的是如果用户没有对自己的 CXX_STANDARD 作出宣告的话,那它就会死——但 set(CMAKE_CXX_STANDARD 17) 可以令这些措施一律无意义。这是为了能够对编译器的C++标准兼容性进行更好的约束,但对绝大多数人来说毫无意义。

tutorial.cpp 如下

// Copyright (c) 2022 Xiguan Inc
// Author: xiguan
// Email: [email protected]
// Create on 22-12-2
// TODO:
//

#include 
#include 

#include "TutorialConfig.h"

using namespace std;

int main(int argc, char *argv[]) {
  if (argc < 2) {
    cout << argv[0] << "Version " << Tutorial_VERSION_MAJOR << "."
         << Tutorial_VERSION_MINOR << endl;

    cout << "Usage: " << argv[0] << " number" << endl;
    return 1;
  }

  const double inputValue = ::std::stod(argv[1]);

  const double outputValue = ::sqrt(inputValue);

  cout << "The square root of " << inputValue << " is " << outputValue << endl;
  return 0;
}

TutorialConfig.h.in 如下:

// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

step2 添加库

现在给项目添加库(library),在库中实现平方根函数

文件目录结构如下:

CMake 官方文档入门_第2张图片

编写顶层 CMakeLists.txt 如下:

# cmake 要求的最小版本
cmake_minimum_required(VERSION 3.10)
# 设置项目名称 可以设置版本号
project(Tutorial VERSION 1.0)
# 设置 c++ 的语言标准 C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

configure_file(TutorialConfig.h.in TutorialConfig.h)
# 设置 MyFunctions 可选
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 ()

## 添加库
#add_subdirectory(MathFunctions)


# 添加可执行程序(前面的是可执行程序的名称 后面是所需要的 source 文件)
add_executable(Tutorial tutorial.cpp)

target_link_libraries(Tutorial PUBLIC "${EXTRA_LIBS}")

target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" "${EXTRA_INCLUDES}")

# target_include_directories(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/MathFunctions")
# 链接这个库
# target_link_libraries(Tutorial PUBLIC MathFunctions)

解释:

  • 函数

  • option:用于控制编译流程,相当于C语言中的宏条件编译

    option( "" [value])
    

    variable:定义选项名称;help_text:说明选项的含义;value:定义选项默认状态,一般是OFF或者ON,除去ON之外,其他所有值都为认为是OFF。

  • target_link_libraries:将之前打包的库,链接到生成的目标上,不然会出现光声明,没定义的错误,注意也可以直接指定库名,如target_link_libraries(main XXX.so)target_link_libraries(main XXX.a)

  • add_subdirectory:添加一个子目录并构建该子目录

    add_subdirectory (source_dir [binary_dir] [EXCLUDE_FROM_ALL])
    

    source_dir
    必选参数。该参数指定一个子目录,子目录下应该包含CMakeLists.txt文件和代码文件。子目录可以是相对路径也可以是绝对路径,如果是相对路径,则是相对当前目录的一个相对路径。

    binary_dir
    可选参数。该参数指定一个目录,用于存放输出文件。可以是相对路径也可以是绝对路径,如果是相对路径,则是相对当前输出目录的一个相对路径。如果该参数没有指定,则默认的输出目录使用source_dir

    EXCLUDE_FROM_ALL
    可选参数。当指定了该参数,则子目录下的目标不会被父目录下的目标文件包含进去,父目录的CMakeLists.txt不会构建子目录的目标文件,必须在子目录下显式去构建。例外情况:当父目录的目标依赖于子目录的目标,则子目录的目标仍然会被构建出来以满足依赖关系(例如使用了target_link_libraries)

  • list:

    list (subcommand  [args...])
    

    subcommand为具体的列表操作子命令,例如读取查找修改排序等。为待操作的列表变量,[args...]为对列表变量操作需要使用的参数表,不同的子命令对应的参数也不一致。

编写 MathFunctions CMakeLists.txt 如下

# 生成库
add_library(MathFunctions mysqrt.cpp)

解释:

  • 函数
  • 将指定的一些源文件打包成动态库和静态库,第一个参数就是生成库的名字,第二个参数是生成库的类型,后面的参数就都是源文件了,可以是之前定义的列表,也可以用1.cpp 2.cpp 3.cpp去指定。

tutorial.cpp 如下

// Copyright (c) 2022 Xiguan Inc
// Author: xiguan
// Email: [email protected]
// Create on 22-12-2
// TODO:
//

#include 
#include 

#include "TutorialConfig.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif

using namespace std;

int main(int argc, char *argv[]) {
  if (argc < 2) {
    cout << argv[0] << "Version " << Tutorial_VERSION_MAJOR << "."
         << Tutorial_VERSION_MINOR << endl;

    cout << "Usage: " << argv[0] << " number" << endl;
    return 1;
  }

  const double inputValue = ::std::stod(argv[1]);
#ifdef USE_MYMATH
  const double outputValue = mysqrt(inputValue);
#else
  const double outputValue = ::sqrt(inputValue);
#endif
  cout << "The square root of " << inputValue << " is " << outputValue << endl;
  return 0;
}

TutorialConfig.h.in 如下:

// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

#cmakedefine USE_MYMATH

MathFunctions 目录

这个目录的文件就是自己实现了一下 mysqrt 这个函数

step3 为库添加使用要求

使用要求库对库和可执行程序的链接,包含命令行提供了更好的控制,也使 CMake 内传递目标属性更加可控

这儿我们说明实现的是:任何使用 MathFunctions 的实体都需要包含其代码目录,但是 MathFunctions 自己不需要。这个概念叫做 INTERFACE

target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

在 MathFunctions 中添加这个,可以实现这个需求

target_link_libraries(A B)命令为例,从理解的角度解释:

  • PRIVATE 依赖项B仅链接到目标A,若有C链接了目标A,C不链接依赖项B
  • INTERFACE 依赖项B并不链接到目标A,若有C链接了目标A,C会链接依赖项B
  • PUBLIC 依赖项B链接到目标A,若有C链接了目标A,C也会链接依赖项B

step4 安装与测试

安装规则

对于 MathFunctions 我们希望安装库文件以及头文件,对于应用程序,我们希望安装可执行文件和配置头文件

在 MathFunctions/CMakeLists.txt 添加

install(TARGETS MathFunctions DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)

在顶层 CMakeLists.txt 添加

测试支持

先在顶层的 CMakeLists.txt 开启测试,然后添加一些基本的测试来验证

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_REGUKAR_EXPRESSION ${result})
endfunction()

do_test(Tutorial 4 "4 is 2")

在二进制目录 运行 ctest -N 和 ctest -vv

step5 添加系统内省

检查系统中是否有某些函数

# 顶层 CMakeLists.txt 
include(CheckSymbolExists)
set(CMAKE_REQUIRED_LIBRARIES "m")
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)

# MathFunctions/CMakeLists.txt 
target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${CMAKE_BINARY_DIR})
// TutorialConfig.h.in 文件添加
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP

step6 生成一个安装器

我们将项目发布给别人使用,多个平台提供二进制和源码包

顶层 CMakeLists.txt 添加

# 这儿会把当前平台上所需要运行的库全部包含近来
include(InstallRequiredSystemLibraries)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set(CPACK_PACKGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set(CPACK_PACKGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
include(CPack)
# 构建生成一个二进制发布包
cpack

# -G 选项可以制定打包哪一种结构
cpack -G ZIP -C Debug

# 创建一个源码发布
cpack --config CPackSourceConfig.cmake

你可能感兴趣的:(随笔,linux,cmake)