对比官方文档练习
一个基本的项目:从源文件构建可执行程序
文件目录如下
# 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@或者 VAR的变量值。每个变量引用将替换为该变量的当前值,如果未定义该变量,则为空字符串。可能有些绕头,再浅显一点:configurefile,复制一份输入文件到输出文件,替换输入文件中被@VAR@或者{VAR}引用的变量值。也就是说,让普通文件,也能使用CMake中的变量。
configure_file(
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++标准兼容性进行更好的约束,但对绝大多数人来说毫无意义。
// 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;
}
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
现在给项目添加库(library),在库中实现平方根函数
文件目录结构如下:
# 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...]
为对列表
变量操作需要使用的参数表,不同的子命令对应的参数也不一致。
# 生成库
add_library(MathFunctions mysqrt.cpp)
1.cpp 2.cpp 3.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;
}
// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
#cmakedefine USE_MYMATH
这个目录的文件就是自己实现了一下 mysqrt 这个函数
使用要求库对库和可执行程序的链接,包含命令行提供了更好的控制,也使 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不链接依赖项BINTERFACE
依赖项B并不链接到目标A,若有C链接了目标A,C会链接依赖项BPUBLIC
依赖项B链接到目标A,若有C链接了目标A,C也会链接依赖项B对于 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
检查系统中是否有某些函数
# 顶层 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
我们将项目发布给别人使用,多个平台提供二进制和源码包
顶层 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