首页 > 其他分享 >CMake教程

CMake教程

时间:2024-03-08 14:22:26浏览次数:13  
标签:教程 CMake target MathFunctions add include TODO Tutorial

学习使用一个工具最好的办法是从它的官方文档入手。
这篇CMake教程搭配的CMake版本是3.25,参考的文档地址:https://cmake.org/cmake/help/v3.25/guide/tutorial/index.html
在官网的教程完成以后,就是一日又一日地补充。

第1步 1️⃣从最基本的开始

练习 1、构建一个基本的工程

需要用到的命令/宏

cmake_minimum_required() #申明项目使用的最低CMake版本
project()                #为项目取一个名称
add_executable()         #将源文件编译成可执行文件

练习 2、确定C++标准

需要用到的命令/宏

CMAKE_CXX_STANDARD          #与set搭配,设置项目需要的C++标准
CMAKE_CXX_STANDARD_REQUIRED #与set搭配,设置为True表示必须要在机器中找到该C++标准
set()                       #“设置”

练习 3、加入版本号和已经配置好的头文件

<PROJECT-NAME>_VERSION_MAJOR #大版本变量
<PROJECT-NAME>_VERSION_MINOR #小版本变量
configure_file()             #从包含有define宏命令的.h.in文件中生成.h文件,具体如后图所示
target_include_directories() #指定目标所包含的头文件路径
#  target_include_directories()与include_dierctories()都是将指定目录
#  添加到编译器的头文件搜索路径之下
#  区别在于:include_directories()使当前CMakeLists.txt中所有目标都将具有此头文件搜索路径
#  target_include_directories()指定某个目标包含头某个文件搜索搜索路径

一级CMakeLists.txt文件:

# TODO 8: Use configure_file to configure and copy TutorialConfig.h.in to
#         TutorialConfig.h
configure_file(TutorialConfig.h.in TutorialConfig.h)

TutorialConfig.h.in文件:

// the configured options and settings for Tutorial
// TODO 10: Define Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@

在build文件夹下构建以后,会出现TutorialConfig.h的文件,包含了版本信息,并且在tutorial.cxx中可以使用。

// the configured options and settings for Tutorial
// TODO 10: Define Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0

第2步 2️⃣加入一个库

练习 1、创建一个库

需要用到的命令/宏

add_library()                #使用指定源文件向项目中添加目标库,类似与add_executable()
add_subdirectory()           #在此CMake项目中添加子目录(子目录中也有CMakeLists.txt文件)
target_include_directory()   #将指定的头文件目录加入到指定目标的编译过程中(PUBLIC等)
target_link_libraries()      #将指定目标链接到指定的库(PUBLIC等)
PROJECT_SOURCE_DIR           #CMAKE中的一个内置变量,实际上就是项目的路径,也就是
                             #“此CMakeLists.txt”文件的路径

在MathFunctions/CMakeLists.txt中,使用add_library指令将mysqrt.cxx文件(默认)编译成静态库

# TODO 1: Add a library called MathFunctions
# Hint: You will need the add_library command
add_library(MathFunctions mysqrt.cxx)

然后,我们一级CMakeLists.txt中,使用add_subdirectory将MathFunctions文件夹添加到项目中(在构建以后,build文件夹下也有了一个MathFunctions的子文件夹)

练习 2、让我们的库成为可选项

对于我们这个小项目来说,这可能是无关紧要的,但是对于更大的项目来说,这是一个很常见的做法。
需要用到的命令/宏:

if()
list()
option()
cmakedefine

首先,在一级CMakeLists.txt中使用option()创建一个变量:USE_MATH

# TODO 7: Create a variable USE_MYMATH using option and set default to ON
option(USE_MATH "Use tutorial provided math implementation" ON)
# 中间引号的文字是提醒开发者这一选项的意思,选项默认是ON

然后,我们就需要使构建和链接MathFunctions库变得是可选的

# TODO 8: Use list() and APPEND to create a list of optional libraries
# called  EXTRA_LIBS and a list of optional include directories called
# EXTRA_INCLUDES. Add the MathFunctions library and source directory to
# the appropriate lists.
if(USE_MYMATH)
    add_subdirectory(MathFunctions)
    list(APPEND EXTRA_LIBS MathFunctions) #将库名(MathFunctions)追加到变量EXTRA_LIBS中
    list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")#将库路径追加到变量EXTRA_INCLUDES中
endif()
# 注意:EXTRA_LIBS和 EXTRA_INCLUDES并不是CMake自带的变量

当USE_MATH为ON,就会生成一个列表加入到我们的项目中,当USE_MATH为OFF的时候列表就会保持为空。通过这种方式来有选择地编译库。
现在,我们有了两份列表,我们需要用它们更新target_link_libraries()和target_link_directories()

# TODO 9: Use EXTRA_LIBS instead of the MathFunctions specific values
# in target_link_libraries.
target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS})

# TODO 3: Use target_link_libraries to link the library to our executable
#target_link_libraries(Tutorial PUBLIC MathFunctions)

# TODO 4: Add MathFunctions to Tutorial's target_include_directories()
# Hint: ${PROJECT_SOURCE_DIR} is a path to the project source. AKA This folder!
# target_include_directories(Tutorial PUBLIC
#                         "${PROJECT_SOURCE_DIR}"
#                         "${PROJECT_SOURCE_DIR}/MathFunctions")

# TODO 10: Use EXTRA_INCLUDES instead of the MathFunctions specific values
# in target_include_directories.
target_include_directories(Tutorial PUBLIC
                            "${PROJECT_SOURCE_DIR}"
                            ${EXTRA_INCLUDES})

我们需要修改tutorial.cxx文件,如果定义了USE_MYMATH,就包含头文件MathFunctions.h

// TODO 11: Only include MathFunctions if USE_MYMATH is defined
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif

同时,我们用USE_MYMATH来控制使用哪一个计算平方根的函数

// TODO 12: Use mysqrt if USE_MYMATH is defined and sqrt otherwise
#ifdef USE_MYMATH
  const double outputValue = mysqrt(inputValue);
#else
  const double outputValue = sqrt(inputValue);
#endif

因为源代码中需要 USE_MYMATH ,我们把它加入到 TutorialConfig.h.in 中

// TODO 13: use cmakedefine to define USE_MYMATH
#cmakedefine USE_MYMATH

温馨提示:在改变USE_MYMATH为 ON 或 OFF ,测试代码的时候,一定要删除build下的文件再CMake。不然?嘿嘿,你试试呗。

第3步 3️⃣为一个库加入使用要求

练习 1、为一个库加入使用要求

在这一练习中,我们将使用现代CMake的方法重构上一步的代码
需要用到的命令/宏

CMAKE_CURRENT_SOURCE_DIR  # 当前正在处理的原目录的路径,也就是当前CMakeLists.txt的路径

我们需要在MathFunctions/CMakeLists.txt文件中加入target_include_directories()指令,属性为INTERFACE

# TODO 1: State that anybody linking to MathFunctions needs to include the
# current source directory, while MathFunctions itself doesn't.
# Hint: Use target_include_directories with the INTERFACE keyword
target_include_directories(MathFunctions INTERFACE
                            ${CMAKE_CURRENT_SOURCE_DIR})

简单来说,INTERFACE 此CMakeLists.txt的路径,以为着任何使用此库(MathFunctions)生成目标(库或可执行文件)的时候,头文件的搜索路径将包含${CMAKE_CURRENT_SOURCE_DIR}所表示的路径

然后去掉list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions"),原因就是上面所说

if(USE_MYMATH)
  add_subdirectory(MathFunctions)
  list(APPEND EXTRA_LIBS MathFunctions)
  #list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

接着去掉${EXTRA_INCLUDES}

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

Tips:"${PROJECT_BINARY_DIR}"的引号加和不加都不影响最终的编译

总结:在 CMake 中,INTERFACE 是用于指定目标属性的一种关键字。当在 target_include_directories 命令中使用 INTERFACE 关键字时,它表示这些包含目录是用于特定目标的接口。
接口目标是一种特殊类型的目标,它不会生成任何实际的构建输出,而是用于将属性传递给依赖于它的其他目标。通过为接口目标指定包含目录,可以确保这些包含目录也被传递给依赖于该接口目标的其他目标。
当一个目标依赖于一个具有接口属性的目标时,它会继承该目标的接口属性。这意味着依赖目标将自动获得与接口目标相同的包含目录设置。

具体来说,在上面的示例中,我们创建了一个库或可执行文件的目标 my_library 或 my_executable。然后,我们使用 target_include_directories 命令为 my_library 指定了包含目录。通过指定 INTERFACE 关键字,我们将这些包含目录设置为接口属性。
此时,如果其他目标依赖于 my_library,它们将继承 my_library 的接口属性,包括包含目录设置。这样,其他目标就能够在编译过程中找到所需的头文件。

归根结底,INTERFACE 关键字用于将包含目录(以及其他属性)指定为接口属性,以便在依赖于该接口目标的其他目标中继承这些属性。这样可以方便地管理和共享构建设置,确保各个目标之间的一致性。

第4步 4️⃣加入生成器表达式

练习 1、用接口库设置C++标准

goal:加入一个接口库确定需要的C++标准
需要用到的命令/宏

add_library()
target_compile_features()
target_link_libraries()

在开始之前,要移除两处set()指令

#set(CMAKE_CXX_STANDARD 11)
#set(CMAKE_CXX_STANDARD_REQUIRED True)

我们需要创建一个接口库,tutorial_compiler_flags,并且使用target_compile_features()加入编译器特性cxx_std_11。

# TODO 1: Replace the following code by:
# * Creating an interface library called tutorial_compiler_flags
#   Hint: use add_library() with the INTERFACE signature
# * Add compiler feature cxx_std_11 to tutorial_compiler_flags
#   Hint: Use target_compile_features()
add_library(tutorial_compiler_flags INTERFACE)
target_compile_features(tutorial_compiler_flags INTERFACE cxx_std_11)

最后,我们需要链接我们的可执行目标和我们的MathFunctions库到我们新的库(tutorial_compiler_flags)。

target_link_libraries(Tutorial PUBLIC ${EXTRA_LIBS} tutorial_compiler_flags)
target_link_libraries(MathFunctions tutorial_compiler_flags)

至此,我们的代码还是用C++ 11构建。但是,使用这种方法能够让我们确定什么样的目标需要什么样的要求。

练习 2、在生成器表达式中添加编译器警告标志

生成器表达式的一个常见用法是有条件地添加编译器标志,例如用于语言级别或警告的标志。一个很好的模式是将此信息与允许传播此信息的INTERFACE目标关联。
goal:生成时添加编译器警告标志,但不针对已安装的版本。
需要用到的指令/宏

cmake_minimum_required()
set()
target_link_options()

首先需要将CMake支持的最低版本调整为3.15

# TODO 4: Update the minimum required version to 3.15
cmake_minimum_required(VERSION 3.15)

接着,我们需要确定我们的系统用于构建的编译器是哪一个,因为警告标志因为编译器的不同而不同。需要使用的编译器表达式:COMPILE_LANG_AND_ID.我们把结果设置到gcc_like_cxx和msvc_cxx两个变量中。

# TODO 5: Create helper variables to determine which compiler we are using:
# * Create a new variable gcc_like_cxx that is true if we are using CXX and
#   any of the following compilers: ARMClang, AppleClang, Clang, GNU, LCC
# * Create a new variable msvc_cxx that is true if we are using CXX and MSVC
# Hint: Use set() and COMPILE_LANG_AND_ID
set(gcc_like_cxx "$<COMPILE_LANG_AND_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>")
set(msvc_cxx "$<COMPILE_LANG_AND_ID:CXX,MSVC>")

然后,我们要为我们的项目加入我们想要的编译器警告标志。使用我们前面定义的变量gcc_like_cxx和msvc_cxx,当它们为真的时候,我们可以用另一个生成器表达式分别部署标志。我们需要用target_compile_options()将这些标志应用到我们的接口库上。

# TODO 6: Add warning flag compile options to the interface library
# tutorial_compiler_flags.
# * For gcc_like_cxx, add flags -Wall;-Wextra;-Wshadow;-Wformat=2;-Wunused
# * For msvc_cxx, add flags -W3
# Hint: Use target_compile_options()
target_compile_options(tutorial_compiler_flags INTERFACE
                        "$<${gcc_like_cxx}:-Wall;-Wetra;-Wshadow;-Wformat=2;-Wunused>"
                        "$<${msvc_cxx}:-W3>")

此target_compile_options命令为tutorial_compiler_flags目标添加编译选项,INTERFACE关键字意味着这些选项会被传递给链接到此目标的其他目标。
$<$<gcc_like_cxx>:-Wall;-Wetra;-Wshadow;-Wformat=2;-Wunused>的含义是: 如果编译的语言是C++(CXX),并且编译器ID是ARMClang、AppleClang、Clang、GNU或LCC中的任意一个,那么就添加-Wall -Wextra -Wshadow -Wformat=2 -Wunused这些编译选项。
● -Wall:开启所有警告信息。
● -Wextra:开启额外的警告信息。
● -Wshadow:警告任何变量声明遮蔽了另一个作用域中的变量。
● -Wformat=2:检查printf、scanf等格式字符串的错误。
● -Wunused:警告未使用的变量。
$<$<msvc_cxx>:-W3>的含义是:如果编译的语言是C++并且编译器是MSVC,那么就添加-W3这个编译选项。
● -W3:在MSVC中,这代表开启等级3的警告,等级3包含了大多数警告。相当于Linux下的-Wall
同样的,上面的指令可以写成:

target_compile_options(tutorial_compiler_flags INTERFACE
                        "$<$<COMPILE_LANG_ANG_ID:CXX,ARMClang,AppleClang,Clang,GNU,LCC>:-Wall;-Wetra;-Wshadow;-Wformat=2;-Wunused>"
                        "$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:-W3>")

最后,我们只想这些警告标志只在构建的时候使用。我们项目的消费者(其他使用此目标的目标)不应该继承这些标志。为了达到这一点,我们使用BUILD_INTERFACE将标志包装在生成器表达式中。(BUILD_INTERFACE的含义: 当目标作为其他目标的依赖时,这些选项才会被应用 )

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

第5步 5️⃣安装与测试

练习 1、安装规则

在更多的时候,构建一个可执行程序是不够的,我们还要它是可安装的,在CMake中,我们可以用install指定我们的安装规则。
需要用到的命令/宏

install()

我们的这个项目的安装规则十分简单:
● 对于MathFunctions,我们想把库文件和头文件分别安装到lib文件夹和include文件夹中。
● 对于Tutorial,我们想把可执行程序和配置性头文件分别安装到bin和include文件夹中。
首先创建一个变量installable_libs存储MathFunctions库和tutorial_compiler_flags库
利用install()命令和TARGET、DESTINANTION变量将库文件安装到lib中

# TODO 1: Create a variable called installable_libs that is a list of all
# libraries we want to install (e.g. MathFunctions and tutorial_compiler_flags)
# Then install the installable libraries to the lib folder.
# Hint: Use the TARGETS and DESTINATION parameters
set(installable_libs MathFunctions tutorial_compiler_flags)
install(TARGET ${installable_libs} DESTINATION lib)

然后利用install命令和FILES、DESTINATION变量将头文件安装到include中

# TODO 2: Install the library headers to the include folder.
# Hint: Use the FILES and DESTINATION parameters
install(TARGET MathFunctions.h DESTINATION include)

同理,将Tutorial安装到bin中,将配置性头文件TutorialConfig.h安装到include中

# TODO 3: Install Tutorial in the bin directory
# Hint: Use the TARGETS and DESTINATION parameters
install(TARGETS Tutorial DESTINATION bin)

# TODO 4: Install Tutorial.h to the include directory
# Hint: Use the FILES and DESTINATION parameters
install(FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"
        DESTINATION include)

这样,我们就完成了基本的本地(local)安装。

所以说,我们安装到的文件夹有/usr/local/bin(可执行程序)、/usr/local/include(头文件)、和/usr/local/lib(库文件)
这就更好地理解了从github上面下载一些库执行cmake、make和sudo make install背后的含义了。

练习2、测试支持

goal:使用CTest为我们的可执行文件创建一个单元支持
需要用到的命令/宏:

enable_testing()
add_test()
function()
set_tests_properties()
ctest

首先我们要打开测试功能,然后使用add_test()命令为我们的项目加入测试。后面我们将加入3个简单的测试。
打开测试功能

# TODO 5: Enable testing
enable_testing()

在打开测试功能以后,我们将测试应用是否正常工作。
首先,我们使用add_test()创建一个带参数25运行Tutorial可执行程序的测试案例。这个测试并不是为了检查可执行程序计算得对不对,而是检查它有没有正常运行。这是CTest测试的基本模式。

# TODO 6: Add a test called Runs which runs the following command:
# $ Tutorial 25
add_test(NAME Runs COMMAND Tutorial 25)

接着,使用PASS_REGULAR_EXPRESSION测试属性来验证测试的输出是否包含某些字符串。在这种情况下,验证在提供了不正确数量的参数时是否打印了使用消息。
再接着,我们来测试计算结果是否正确。

# TODO 8: Add a test which runs the following command:
# $ Tutorial 4
# Make sure the result is correct.
# Hint: Use the PASS_REGULAR_EXPRESSION property with "4 is 2"
add_test(NAME StandardUse COMMAND Tutorial 4)
set_tests_properties(StandardUse
    PROPERTIES PASS_REGULAR_EXPRESSION "4 is 2")

就这一个测试是不够的。为了更简单地添加更多的测试,我们创建一个叫do_test()的函数,然后批量执行

# TODO 9: Add more tests. Create a function called do_test to avoid copy +
# paste. Test the following values: 4, 9, 5, 7, 25, -25 and 0.00001.
function(do_test target arg result)
  add_test(NAME test_${arg} COMMAND ${target} ${arg})
  set_tests_properties(test_${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endfunction()

#test
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")

(NAME,而不是Name)

标签:教程,CMake,target,MathFunctions,add,include,TODO,Tutorial
From: https://www.cnblogs.com/qizhengxun/p/18058896

相关文章

  • eNSP 安装教程
    一、安装 a)先安装步骤1依赖组件中的四个组件,其中VBox不要安装在中文目录; b)安装步骤2中的主程序(eNSP1.3.00.100Setup); c)导入步骤3设备包(可选操作)。下载链接:阿里云盘(设备包分享失败,关注加群文件) 二、使用 a)参考eNSP基础操作视频; b)防火墙默认用户名、密......
  • 如何将应用一键部署至多个环境?丨Walrus教程
    在Walrus平台上,运维团队在资源定义(ResourceDefinition)中声明提供的资源类型,通过设置匹配规则,将不同的资源部署模板应用到不同类型的环境、项目等。与此同时,研发人员无需关注底层具体实现方式,通过创建Resource对象声明需要使用的资源类型及基本信息,就可以灵活地在各种环境中自......
  • 【教程】 iOS构建版本无效问题解决方案
     引言在进行iOS应用上架时,有时会遇到构建版本无效的问题,即通过XCode上传成功后,但在AppStoreConnect的TestFlight中无法显示构建版本,或者显示一会儿后就消失了。本文将介绍可能的原因分析,并提供解决问题的方法。问题描述最近一次上传新版本至AppStore后,发现在AppStoreCon......
  • CentOS7安装python3详细教程
    1.检查CentOS7自带python环境centos一般自带Python2,先使用python-V来查看python版本建议大家在保留python2的基础上安装一个python3,因为python2和python3还是有一些区别的,同时安装python2和python3的环境,以便不时之需或者对比学习。如果想要删除原有的python环境,可以通过下面......
  • 【教程】HBuilderX开发实践:隐私合规检测问题解决方案
    文章目录摘要引言正文1、违规收集个人信息2、APP强制、频繁、过度索取权限知识点补充总结 摘要本篇博客介绍了在使用HBuilderX进行开发过程中,常遇到的隐私合规问题,并提供了相应的解决方案。主要包括违规收集个人信息和APP强制、频繁、过度索取权限两方面。......
  • Ubuntu安装zabbix配置教程
    Ubuntu镜像下载:https://mirrors.aliyun.com/ubuntu-releases/20.04.6/ubuntu-20.04.6-live-server-amd64.isoUbuntu在vm虚拟机安装(如下未提及项均保持默认选项)1、配置软件源为阿里云,http://mirrors.aliyun.com/ubuntu 2、配置服务器名称和用户名密码 3、安装ssh服务,......
  • WinRadius 企业版安装教程
    下载方法:WinRadius标准版只支持5个用户WinRadius企业版支持5000个用户安装方法:启动WinRadius。按照WinRadius的提示配置ODBC,然后重新启动WinRadius。把接入服务器(NAS)的Radius设置为WinRadius服务器,缺省端口为:1812(认证)、1813(计费),密钥为:WinRadius。具体配......
  • HTML开发工具和环境介绍,内附超详细的VS code安装教程!
    工欲善其事必先利其器,一款好的开发工具可以让我们事半功倍。前面我们对HTML的相关概念和基本结构已经有了基本的了解,下面我们就来安装在前端开发中的需要使用的开发工具及环境。在众多HTML编辑器中,选择一个适合自己的工具至关重要。今天我们就来认识一下前端开发工作中使用的最广......
  • 【教程】uni-app iOS打包解决profile文件与私钥证书不匹配问题
    摘要当在uni-app中进行iOS打包时,有时会遇到profile文件与私钥证书不匹配的问题。本文将介绍如何解决这一问题,以及相关的技术细节和操作步骤。引言在uni-app开发过程中,iOS打包是一个常见的操作。然而,有时会出现profile文件与私钥证书不匹配的错误提示,导致打包失败。为了解决这一......
  • ps破解版百度云网盘下载+部署教程+更新补丁
    Photoshop是一款由Adobe公司开发的图像处理软件,它是目前业界最为常用的图像处理软件之一。它拥有丰富的功能和强大的操作性,可以处理各种类型的图像,包括照片、绘画、图表等等。本文将介绍Photoshop的基本功能和应用范围。只要你有最低的系统要求,可以有效和平稳地运行......