cmake

cmake什么?第一次接触cmake还是在Harvard的cs50公开课上,在终端用cmake编译c语言,但是老师已经写好cmake并且封装成了make函数。

如今在GAMES101中闫老师留的作业都是用cmake编译的,所以有必要,也为了在c++领域能够走的更远,我也开始系统接触cmake,明白cmake是怎么工作的。

本篇博客并不是在我全部掌握或者说精通cmake后才写的,而是在我还并不太了解cmake是什么东西的情况下,利用互联网去搜集学习的过程,这篇文章更像是我在学习过程中的思考方式,记录我对陌生事物的学习方式,以此为鉴,训练自己的思考方式,希望能变得更加高效

什么是cmake

​ 我们首先在中文门户网站上搜索cmake是什么?

cmake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译)过程。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性。CMake的组态栏取名为CMakeLists.txt。CMake并不直接建构出最终的软件,而是产生标准的建构档(如Unix的Makefile),然后再依一般的建构方式使用。

他这个大意应该是cmake就是一个在不同平台,使项目能够用同一种方式进行编译的标准化工具。

CMake,即”cross platform make“(蹩脚翻译:跨平台制作hhh)的缩写。

CMake可以编译源代码、制作程序库、产生适配器(wrapper)、还可以用任意的顺序建构执行档。CMake支持in-place建构(二进档和源代码在同一个目录树中)和out-of-place(二进档在别的目录中)(我的内心吐槽:为什么二进制文档放的位置不同,还能变成一种建构形式),因此可以很容易从同一个源代码目录树中建构出多个二进档。CMake也支持静态与动态程式库的建构。

以上是中文词条对于cmake的解释,大概明白了cmake是干嘛的

接着上外文网站搜索cmake,找到了cmake的官方网站,原来cmake是kitware(一家开源软件公司)的东西,映入眼帘的赫然是:Build with CMake. Build with Confidence.这宣传语写的真霸气。

为什么你要使用cmake?

  • cmake高效
    • CMake lets developers spend more time writing code and less time figuring out the build system
    • CMake is open source and free to use for any project
  • cmake太强了
    • CMake supports multiple development environments and compilers on the same project (e.g., Visual Studio IDE, QtCreator, JetBrains, vim, emacs, gcc, MSVC, clang, Intel) 多语言多环境多ide真好
    • CMake supports multiple languages including C/C++/CUDA/Fortran/Python, and also supports running arbitrary custom commands as part of the build 我理解的是用任意自定义代码来写cmake规范
    • CMake supports continuous integration (CI) testing in concert with Jenkins, Travis, CircleCI, GitlabCI, and almost any CI system via CTest. Test results are displayed using CDash.很好的测试
    • CMake supports integration of 3rd party libraries into your project. 第三方支持
  • cmake是顶级的(这点我不清楚,但是没办法,必须要学)

基本上明白他是干什么的了,就是要自己写编译文件,那我们就开干吧。

学习先从在互联网上搜相应的解答开始,先上知乎搜,然后再vscode社区找官方支持的使用说明,然后再时间演练一下proj0的内容。

首先从知乎大学上找:

==2021年还不会cmake?你out啦== cmake使用命令行进行build,效率比IDE高多了!

答主推荐了背诵下面这段代码,说是有助于理解cmake的含义。我一看“啪”,很快啊,短短3、40行代码,说背就背,真是上嘴皮碰下嘴皮,痛快啊。那就给他背下来吧。

cmake_minimum_required(VERSION 3.20)

project(CMK-Hello
        VERSION 0.0.1
        DESCRIPTION "A Test Project"
        LANGUAGES CXX
)

# set language std
set(CMAKE_CXX_STANDARD 17)
# set language std and Disable fallback to a previous version
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Disable CXX Syntax EXTENSIONS
set(CMAKE_CXX_EXTENSIONS OFF)


set(STATIC_HEADER "include/static/")
add_library(myLib STATIC src/STD_Suggestion.cpp ${STATIC_HEADER}STD_Suggestion.h)

# for #include "static/Suggestion.h"
target_include_directories(mylib
    PUBLIC
        include/
)

set(SOURCES
    src/hello.cpp
)

set(SOURCES
    src/hello.cpp
)

add_executable(HelloExe ${SOURCES})

target_link_libraries(HelloExe
    PRIVATE
        myLib
)

在写完这部分后,清明节跑去深圳莲花山踏青,感觉很不错。


首先我们来看项目的目录设置:

build/

include/static

src/

clean.ps1

CMakeLists.txt

proceed.ps1

两个.ps1文件是他写的powershell脚本,用来自动执行cmake文件。

该作者建议将CMakeLists.txt放在最外层目录(可惜并没有说原因),下面来看procced文件的具体代码

function cmake-first-build {
    cmake -G "Visual Studio 16 2019" -A x64 -T ClangCL -S . -B build/
}

function mainflow {
    cmake-first-build
}

mainflow

我是这么理解这段脚本的,这段脚本就是将cmake函数封装起来(应该是这样)

然后cmake函数语句中 -G不知道是什么意思,但是后面跟的字符串是使用的IDE名字?

-A x64就是64位平台的意思,-T ClangCL就是用ClangCL来编译。

想要充分理解这个脚本的意思还要熟悉shell编程,但这个就是额外的学习成本了,我们还是将关注点集中在cmake咋用,怎么将cmake的功能发挥到最大。


下面看看cmake代码中的关键部分

set(STATIC_HEADER "include/static/")
add_library(mylib STATIC src/STD_Sugestion.cpp ${STATIC_HEADER}STD_Suggestion.h)

这个函数的意义是从CMakeLists所在目录开始找两个文件,这两个文件描述了一个class,这两个文件描述了一个class(这句话听起来怪怪的,描述了什么样的class呢?)而且后面跟着的==${STATIC_HEADER}==这个是什么东西呀?(应该也是shell编程的东西,可能是占位符的意思)

我们都知道".h"和".cpp"文件有着密不可分的关系,这行代码的意义就是将两个相互依赖的文件称为一个library,然后用add_library函数生成一个名字叫做myLib的库 在build/下创建一个文件夹myLib.dir

再加上一行代码

target_include_directories(myLib
    PUBLIC
        include/
)

为什么要加这行代码,在我们执行cmake的过程中,我们要搞清楚如何将hell.cpp和这个myLib进行连接。

hello.cpp里面的main函数是程序的entry,所以hello.cpp中要写#include "STD_Suggestion.h"

但是,我们告诉程序要去找STD_Suggestion.h这个文件,但是hello.cpp如何找到这个文件呢?

// hellp.cpp的源码
#include <iostream>

#include "static/STD_Suggestion.h"

int main() {
    std::cout << "hello world\n";

    ```
}

Linker就是通过上面加上的那行代码找到,即

target_include_directories(myLib
    PUBLIC
        include/
)

就是上面这行代码,中间的PUBLIC

target_include_directories adds an include directory to a target. PUBLIC doesn't mean much for an executable; for a library it lets CMake know that any targets that link to this target must also need that include directory. Other options are PRIVATE (only affect the current target, not dependencies), and INTERFACE (only needed for dependencies).

这个函数没啥,仅仅是告诉CMake,任何链接到这个库的目标(可执行文件或者库),必须包含“include/”文件夹。

所以我们可以通过#include "static/STD_Suggestion.h"这种相对路径去找名为myLib的库。

那么后面的语法就更简单容易理解了,

set(SOURCES
    src/hello.cpp
)

add_executable(HelloExe ${SOURCES})

target_link_libraries(HelloExe
    PRIVATE
        myLib
)

这就很好理解了,add_executable就是从hello.cpp生成.exe文件

然后我们将打包好的myLib(包括静态库,STD_Suggestion.cpp + STD_Suggestion.h),链接到目标文件(HelloExe),最后生成可执行文件,名字为HelloExe.exe、

到这里,我们实现了,编译,链接,生成exe文件的步骤,接下来需要搞清楚PRIVATE和PUBLIC的意思

  • target_*(A PUBLIC B): 任何链接到A的目标必须链接到B, 且A本身需要链接B
  • PRIVATE: 任何链接到A的目标,不会链接到B,但A本身需要链接B
  • INTERFACE: 任何链接到A的目标,都会链接到B,但A本身不链接B

介绍完关键字之后我们可以修改代码:

set(STATIC_HEADER "include/static/")
add_library(myLib STATIC src/STD_Suggestion.cpp ${STATIC_HEADER}STD_Suggestion.h)

# for #include "static/Suggestion.h"
target_include_directories(myLib
    INTERFACE  // 我们把这部分从PRIVATE换成了INTERFACE,因为知道myLib不需要包含include/
        include/
)

set(SOURCES
    src/hello.cpp
)

add_executable(HelloExe ${SOURCES})

target_link_libraries(HelloExe
    PRIVATE
        myLib
)

基本的cmake文件怎么写就到这里结束了,下面我们来看看官网教程,如何在vscode的ide下使用cmake编译跨平台的软件。

Get started with CMake Tools on Linux

官网链接https://code.visualstudio.com/docs/cpp/cmake-linux

In this tutorial, you'll use the CMake Tools extension for Visual Studio Code to configure, build, and debug a simple C++ CMake project on Linux. Aside from installing CMake, your compiler, debugger, and build tools, the steps in this tutorial apply generally to how you'd use CMake on other platforms, like Windows.

这个教程,主要是让我们学会使用vscode中的CMake扩展功能,configure,build,and debug一个简单的C++项目==在Linux环境==中。

首先,需要准备

  • vscode 这不废话吗
  • C++ extension for VS Code
  • CMake Tools

在这个教程中,我们会使用GCC编译器GDB来debug和make命令去build一个项目。

创建一个CMake项目

如果你有一个已经存在的CMake project那么其中在根目录下应该有CMakeLists.txt,就可以跳过这步到 Select a kit步骤

为项目创建一个空的文件夹

mkdir cmakeQuickStart
cd cmakeQuickStart
code .

code.命令可以在当前文件夹打开vscode,也就是说这就是你的workspace

接下来,创建一个CMake hello world项目

CMake Tools能够为你的cmake项目创建一些基本的文件,打开command palette并且执行cmake:quick start命令。

正常这个过程会要求输入项目的名称和可执行类别,但是我到这一步的时候没有出现弹窗,而是到了后面才会找不到cmakelists.txt文件的时候才弹出来的

  1. select a kit 选择一个你喜欢的compiler
  2. select a variant 选择你想要的build你的项目的方式总共有四种,Debug,Release, MinRelSizeRelWithDebInfo

基本的选择都已经做好了,接下来就是CMake: Configure这一步就会在你项目的文件夹里根据你之前的选项产生build files。

可以用CMake: Build命令来生成你的文件

You can select which targets you'd like to build by selecting CMake: Set Build Target from the Command Palette. By default, CMake Tools builds all targets. The selected target will appear in the Status bar next to the Build button.

当然这些都是可以定制化的,生成什么也可以选择

可以用 CMake: Debug来编译文件,真不错!

那么课前的关于cmake的学习就到这里了,下一篇博客应该就是proj0的初体验了。