cmake、CMakeLists.txt、make、ninja
文章目录
- 一、概念
 - 0.cmake官网
 - 1.什么是cmake
 - 2.为什么使用cmake
 - 3.CMakeLists.txt
 
- 二、CMakeLists.txt语法:如何编写CMakeLists.txt,语法详解
 - (0)语法基本原则
 - (1)project关键字
 - (2)set关键字
 - (3)message关键字
 - (4)add_executable关键字
 - (5)add_subdirectory关键字
 
- 三、使用方法
 - 1.build步骤
 - (1)构建`CMakeLists.txt`
 - (2)在包含`CMakeLists.txt`的目录下使用cmake
 - (3)在包含`Makefile`的目录下使用`make`
 - (4)生成可执行文件。 输入`./可执行程序名`运行
 
- 2.示例一
 - (1)项目结构
 - (2)代码
 - (3)构建工具
 - ①make
 - ②ninja
 - ③make 与 ninja的比较
 - ④cmake选项:-S -B
 
- 3.示例二
 - (1)基础
 - ①先编写main.cpp
 - ②编写CMakeLists.txt
 - ③cmake
 - ④make
 - ⑤执行
 
- (2)让Hello,World看起来更像一个工程
 - ①在工程目录下,先编写main.cpp。创建src子目录,将main.cpp放入其中
 - ②在工程目录下编写CMakeLists.txt,包含子目录版本
 - ③在源文件目录src下编写子目录的CMakeLists.txt
 - ④在工程目录下构建子目录build,并在build中进行cmake。出现Makefile后再进行make
 
- (3)安装:INSTALL
 - ①安装文件
 - ②安装脚本
 - ③安装目录
 - ④可执行文件安装
 - ⑤执行
 
- (4)生成静态库和动态库
 - ①构建动态库(命令用SHARED) (构建静态库,命令用STATIC)
 - ②同时构建静态库和动态库
 - ③使用外部头文件和共享库
 
- 四、原理
 - 1.build的意义
 - 2.源内构建、源外构建
 - 3.`CMakeLists.txt` 与 `.cmake文件`的关系
 - (1)CMakeLists.txt
 - (2).cmake文件
 - (3)它们之间的关系
 - (4)如何使用
 - (5)总结
 
- 4.`.cmake`与`.cmake.in`文件的关系
 
一、概念
0.cmake官网
①官方文档网址:www.cmake.org
 ②cmake官网手册:https://cmake.org/cmake/help/latest/index.html
官网手册
 1.cmake的定义:高级编译配置工具
 当多个人用不同的语言或者编译器开发一个项目,最终要输出一个可执行文件或者共享库(dll、so),这时候就出现了神器CMake! 所有的操作都是通过编译CMakeLists.txt来完成的。
 学习CMake的目的,是为将来处理大型的C/C++/JAVA项目做准备。
2.安装
sudo apt-get install cmake
 
1.什么是cmake
CMake 是一个跨平台的自动化构建系统,使用简单的配置文件(CMakeLists.txt)来生成标准的构建文件(如Makefile、Visual Studio解决方案等)
CMake(Cross Platform Make)是一个开源的构建系统,它通过配置文件生成特定于平台的构建文件,使得项目可以在不同操作系统和编译器下轻松构建。CMake广泛用于C++项目,但也支持其他语言。

2.为什么使用cmake
为什么使用CMake?
 跨平台支持:一次编写,多处运行,支持Windows、Linux、macOS等。
 灵活性:支持复杂的项目结构,处理依赖关系和外部库。
 集成工具:与常见的IDE(如Visual Studio、CLion)无缝集成。
 社区支持:广泛的文档和社区资源。
3.CMakeLists.txt
每个使用CMake的项目都需要一个或多个CMakeLists.txt文件,用于定义项目的构建过程。通常,根目录有一个主要的CMakeLists.txt,子目录也可以有自己的配置文件
①cmake_minimum_required(VERSION x.x): 指定CMake的最低版本要求。
 ②project(ProjectName): 定义项目名称和语言。
 ③add_executable(target_name source_files): 添加可执行目标。
 ④add_library(target_name source_files): 添加库目标。
 ⑤target_include_directories(target_name PUBLIC/PRIVATE include_dirs): 指定包含目录。
 ⑥find_package(package REQUIRED): 查找并引入外部包。
二、CMakeLists.txt语法:如何编写CMakeLists.txt,语法详解
(0)语法基本原则
1.变量取值 ${ }
 2.分隔多个文件:空格或者分号
 3.大小写:指令大小写不敏感,参数和变量对大小写敏感
(1)project关键字
可以用来指定工程的名字和支持的语言,默认支持所有语言
 PROJECT(HELLO) 指定了工程的名字为HELLO,且支持所有语言(建议这样做)
 PROJECT(HELLO CXX)指定了工程的名字为HELLO,仅支持C++语言
 PROJECT(HELLO C CXX)指定了工程的名字为HELLO,仅支持C和C++语言
该指定隐式地定义了两个CMake变了
 <projectname>_BINARY_DIR
 <projectname>_SOURCE_DIR
 本例中是HELLO_BINARY_DIR、HELLO_SOURCE_DIR
 MESSAGE关键字就可以直接使用这两个变量。当前都指向当前的工作目录
(2)set关键字
SET用来显式的指定变量,可以为多个
 SET(SRC_LIST main.cpp) 意为SRC_LIST变量包含了main.cpp
 SET(SRC_LIST main.cpp test1.cpp test2.cpp) 意为SRC_LIST变量包含了main.cpp、test1.cpp、test2.cpp
(3)message关键字
向终端输出用户自定义的信息,主要包含三种:
- SEND_ERROR:产生错误,生成过程被跳过
 - STATUS:输出前缀为- -的信息
 - FATAL_ERROR:立即终止所有cmake过程
 
(4)add_executable关键字
生成可执行文件
 ADD_EXECUTABLE(hello $(SRC_LIST)),生成的可执行文件名为hello,源文件读取变量SRC_LIST中的内容
 也可直接写为 ADD_EXECUTABLE(hello main.cpp)
注意:工程名的HELLO和生成的可执行文件hello是没有任何关系的
所以,上例中的5行CMakeLists.txt可以简化为2行:
PROJECT(HELLO)
ADD_EXECUTABLE(hello main.cpp)
 
(5)add_subdirectory关键字
①有子目录时,要用ADD_SUBDIRECTORY关键字包含所有需要访问的子文件夹。除了工程目录外,每个被访问的子目录里也都需要有一个CMakeLists.txt说明
 ②将目标文件放入构建目录的bin子目录:ADD_SUBDIRECTOR(子目录名 bin):自动在子目录下新建一个名为bin的子目录,存放中间二进制文件、目标二进制文件、库文件。
注意:文件就是file。目录(文件夹)就是directory。
三、使用方法
1.build步骤
(1)构建CMakeLists.txt
 
CMake 构建脚本是一个纯文本文件,您必须将其命名为 CMakeLists.txt,并在其中包含 CMake 构建您的 C/C++ 库时需要使用的命令。如果您的原生源代码文件还没有 CMake 构建脚本,您需要自行创建一个,并在其中包含适当的 CMake 命令。
(2)在包含CMakeLists.txt的目录下使用cmake
 
cmake 放置CMakeList.txt路径
 
(1)“源内构建”(in-source build):
 构建放在当前目录下(内部构建):cmake . 即在当前目录cmake,在当前目录build。【不推荐!】
 (2)“源外构建”(out-of-source build):
 在当前目录下创建build文件夹存放构建文件(外部构建),build内输入cmake ..。即在上级目录cmake,在该目录下build。
 结果:生成4个东西:CMakeFiles文件夹、cmake_install.cmake、CMakeCache.txt、Makefile
 只要产生Makefile文件,就说明cmake成功了
 (3)指定路径:
 用-S -B选项,指定cmake源代码路径和build路径。不写-B,默认为 -B .,即build到当前工作目录
cmake -S /path/to/source -B /path/to/build
 
(3)在包含Makefile的目录下使用make
 
直接在build出4个东西的目录下(命令针对的是Makefile)输入make
make
 
(4)生成可执行文件。 输入./可执行程序名运行
 
2.示例一
(1)项目结构
假设有一个简单的C++项目,结构如下:
.
├── CMakeLists.txt
├── include
│   └── hello.h
└── src
    ├── hello.cpp
    └── main.cpp
2 directories, 4 files
 
(2)代码
①src/main.cpp
#include "hello.h"
int main() {
    hello();
    return 0;
}
 
②src/hello.cpp
#include <iostream>
#include "hello.h"
using std::cout;
using std::endl;
void hello() {
    cout << "Hello, CMake!" << endl;
}
 
③include/hello.h
#ifndef HELLO_H
#define HELLO_H
void hello();
#endif // HELLO_H
 
④根目录的 CMakeLists.txt
cmake_minimum_required(VERSION 3.10)    #指定CMake的最低版本要求
project(MyProject LANGUAGES CXX)        #定义项目的名称(MyProject)、使用的编程语言(Cpp)
set(CMAKE_CXX_STANDARD 17)			    #指定C++标准
set(CMAKE_CXX_STANDARD_REQUIRED True)
include_directories(${PROJECT_SOURCE_DIR}/include)     #包含头文件目录
add_executable(MyProject src/main.cpp src/hello.cpp)   #添加可执行文件
#add_library(target_name source_files)                                #添加库目标
#target_include_directories(target_name PUBLIC/PRIVATE include_dirs)  #指定包含目录
#find_package(package REQUIRED)                                       #查找并引入外部包
 
(3)构建工具
make适合小型项目,ninja适合大型项目。(大型C++项目用make单线程会非常慢。4h→5分钟)
构建工具(Build Tool)是用来管理和自动化整个项目构建过程的工具,涉及:预处理、编译、链接、测试、打包、部署等多个步骤。
 构建工具的主要任务是根据项目的依赖关系图来确定需要执行的任务,并按照正确的顺序执行这些任务。
①make
mkdir build
cd build
cmake ..
make -j 56
sudo make install
 
在源外构建的build目录中,cmake .. 等价于cmake -S .. -B .
# 创建并进入构建目录
mkdir -p build && cd build
# 配置 CMake(如果你使用 CMake)
cmake .. #等价于cmake -S .. -B .
# 使用所有可用的核心进行并行编译
make -j$(nproc)
 
①cmake .. 后生成4个文件:(只要产生Makefile文件,就说明cmake成功了)
Makefile CMakeCache.txt  CMakeFiles  cmake_install.cmake  
 
②make后:
 编译文件Makefile,会在build目录下生成可执行文件MyProject
②ninja
mkdir build && cd build
cmake .. -G Ninja 
ninja -j 56
ninja install
ninja package_blt
 
①cmake -G Ninja ..后,生成四个文件:
build.ninja  CMakeCache.txt  CMakeFiles  cmake_install.cmake
 
②ninja后:
 编译文件build.ninja,会在build目录下生成可执行文件MyProject
 
③make 与 ninja的比较
1.构建命令不同
 ①cmake ..:使用Make进行构建
 ②cmake .. -G Ninja:使用Ninja进行构建
2.Ninja的构建速度比Make更快
 ①Ninja相比于传统的Make工具,通常能提供更快的构建速度,特别是在增量构建时,因为它采用了更智能的并行构建方式。
 ②ninja由于更合理的设计 (最小化构建工具本身的开销、并行构建优化、增量编译),本身编译速度就比make快2-10倍,多线程比单线程又快了10-56倍
 ③-j 56:最多可以同时运行的作业(jobs)数量为 56。ninja -j 56 意味着 Ninja 将尝试最多并行执行 56 个构建任务,但这并不直接对应于 56 个线程
3.make适合小项目,ninja适合大项目编译
④cmake选项:-S -B
-S /path/to/source:指定 /path/to/source 为源代码目录。
 -B /path/to/build:指定 /path/to/build 为构建输出目录。
cmake -S /path/to/source -B /path/to/build
 
cmake .. 等价于cmake -S .. -B .
3.示例二
(1)基础
①先编写main.cpp
#include <iostream>
using namespace std;
int main(){
	cout<<"hello,world"<<endl;
	return 0;
}
 
②编写CMakeLists.txt
PROJECT(HELLO)            #工程名为HEELO
SET(SRC_LIST main.cpp)    #变量SRC_LIST包含main.cpp
MESSAGE(STATUS "This is BINARY dir " ${HELLO_BINARY_DIR})
MESSAGE(STATUS "This is SOURCE dir " ${HELLO_SOURCE_DIR})
ADD_EXECUTABLE(hello ${SRC_LIST}) #生成可执行程序文件名为hello,源文件读取变量SRC_LIST中内容
 
③cmake
cmake .
 
生成了4个东西:CMakeFiles文件夹、cmake_install.cmake、CMakeCache.txt、Makefile
④make
make
 
生成了可执行文件hello
⑤执行
./hello
 
显示"hello,world"
(2)让Hello,World看起来更像一个工程
- 为工程添加一个
子目录src,用来放置工程源代码 - 为工程添加一个
子目录doc,用来放置工程的文档hello.txt - 为工程添加文本文件
使用文档README,版权COPYRIGHT - 在工程目录中添加一个
runhello.sh脚本,用来调用hello二进制 - 为工程添加一个
子目录bin,用来放置生成的目标文件 
①在工程目录下,先编写main.cpp。创建src子目录,将main.cpp放入其中
#include <iostream>
using namespace std;
int main(){
	cout<<"hello,world"<<endl;
	return 0;
}
 
②在工程目录下编写CMakeLists.txt,包含子目录版本
PROJECT(HELLO )            #工程名为HEELO
ADD_SUBDIRECTORY(src bin)  #源文件包含子目录src,目标文件放在构建文件的bin目录下
 
③在源文件目录src下编写子目录的CMakeLists.txt
ADD_EXECUTABLE(hello main.cpp)
 
④在工程目录下构建子目录build,并在build中进行cmake。出现Makefile后再进行make
mkdir build        
cd build
cmake ..  #出现5个东西:bin  CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile
make      #bin目录中出现了可执行文件hello
 
只进行到mkdir build时,对工程目录进行tree。效果(目录结构)应该是这样的:
 .
 ├── build
 ├── CMakeLists.txt
 └── src
  ├── CMakeLists.txt
  └── main.cpp
(3)安装:INSTALL
①安装文件
INSTALL(FILES 文件名 DESTINATION 目标地址)
 举例3:
 工程目录下的CMakeLists.txt添加一行:
INSTALL(FILES COPYRIGHT README DESTINATION share/doc/cmake)   #会自动安装到/usr/local/share/doc/cmake下
 
DESTINATION后面:
 1.写绝对路径
 2.相对路径:
 CMAKE_INSTALL_PREFIX 默认是在/usr/local/
 若想要更改,可以手动设置
set(CMAKE_INSTALL_PREFIX "目标路径")
 
这时候再 DESTINATION ${CMAKE_INSTALL_PREFIX},就导出到目标路径上了。
②安装脚本
PROGRAMS:非目标文件的可执行程序安装(比如脚本之类)
INSTALL(PROGRAMS runhello.sh DESTINATION bin)    #实际安装到/usr/bin
 
③安装目录
安装doc中的hello.txt
 方法一:通过在doc目录中建立CMakeLists.txt,通过install下的file
 方法二:直接在工程目录中写入
INSTALL(DIRECTORY doc/ DESTINATION share/doc/cmake)
 
④可执行文件安装
在src的CMakeLists.txt里输入
INSTALL(TARGETS hello DESTINATION bin) 
 
⑤执行
cd build
cmake ..
make
make install
 
(4)生成静态库和动态库
SET(变量 源文件名)
ADD_LIBRARY(库名 STATIC或SHARED ${变量})
 
①构建动态库(命令用SHARED) (构建静态库,命令用STATIC)
举例4:
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})    #生成动态库libhello.so
 
1)构建目录树
 .
 ├── build
 ├── CMakeLists.txt
 └── src
 ├── CMakeLists.txt
 ├── hello.cpp
 └── hello.h
2 directories, 4 files
2)编写CMakeLists.txt
 ①工程目录CMakeLists.txt内容为:
cmake_minimum_required(VERSION 3.13)
PROJECT(HELLO)
ADD_SUBDIRECTORY(src bin)
 
②lib目录CMakeLists.txt内容为:
SET(LIBHELLO_SRC hello.cpp)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
 
3)lib下源文件内容
 ①hello.h内容
#ifndef HELLO_H
#define HELLO_H
void HelloFunc();
#endif
 
②hello.cpp内容
#include "hello.h"
#include <iostream>
using namespace std;
void HelloFunc(){
        cout<<"Hello,world!"<<endl;
}
 
4)在build目录下进行cmake … 、make 。在build/bin下即出现了libhello.so
②同时构建静态库和动态库
SET_TARGET_PROPERTIES
 
3行命令。视频里补充
③使用外部头文件和共享库
1.添加头文件搜索路径:
INCLUDE_DIRECTORIES(路径)
 
2.添加需要链接的共享库
TARGET_LINK_LIBRARIES(源文件名 库名)
 
TARGET_LINK_LIBRARIES(main libhello.so)    #链接动态库
 
TARGET_LINK_LIBRARIES(main libhello.a)     #链接静态库
 
四、原理
1.build的意义
在C++代码中,build (构建)的过程是将源代码转化为可执行程序或库的过程。这一过程的意义包括:
1.编译和链接:C++代码通常由多个源文件组成。每个源文件包含程序的不同部分。构建过程通常包括两个主要步骤:
- 编译 (Compile):将源代码文件(.cpp文件)转化为目标文件(.o或.obj文件)。在编译时,编译器会检查语法错误并生成机器代码,这些代码是特定于计算机架构的。
 - 链接 (Link):将编译后的目标文件以及任何依赖的库文件(如标准库或第三方库)合并成一个单一的可执行文件或库文件。链接器会处理不同模块间的符号引用,确保程序的各个部分能够正确地相互调用。
 
2.优化:构建过程通常还会进行优化,使得生成的程序在运行时更高效。例如,编译器可能会优化循环,减少冗余代码,或者选择更高效的数据结构。这些优化有助于提高程序的执行速度和减少内存使用。
3.调试信息和错误检查:在构建过程中,如果代码有语法错误、类型不匹配或者其他编译时错误,编译器会报告错误信息。通过构建过程,开发人员可以发现并修复代码中的问题。
4.生成可执行文件:最终的目标是生成一个可执行文件(如.exe文件)或库文件(如.dll或.so文件)。这些文件可以直接在操作系统上运行或被其他程序引用。
在C++代码中,build(构建)的过程是将源代码转化为可执行程序或库的过程。包括:
①编译、链接:将代码从高级语言转为汇编语言、二进制机器码
②转换为汇编语言时,编译器会适当的进行优化,以减少冗余代码,提高效率。
总的来说,build就是为了将源代码转为二进制可执行程序
编译再探:预编译、编译、汇编、链接:https://blog.csdn.net/Edward1027/article/details/125103585

2.源内构建、源外构建
内部构建:例1就是内部构建,产生的临时文件和源文件堆在一起,不方便清理。BINARY DIR和SOURCE DIR在同一个目录下。
 外部构建:把生成的临时文件放在build目录下,方便清理(直接删除build整个文件夹)。BINARY DIR在build目录下,SOURCE DIR在CMakeLists.txt目录下(一般CMakeLists.txt是在build的上级目录,这样可以直接cmake …,然后make)。将build放在CMakeLists.txt同级目录下。推荐使用外部构建。
 
3.CMakeLists.txt 与 .cmake文件的关系
 
(1)CMakeLists.txt
1.作用:
 CMakeLists.txt 是 CMake 项目的核心配置文件。它包含了 CMake 的构建指令,定义了如何编译和链接项目的源代码文件。每个 CMake 项目通常会有一个或多个 CMakeLists.txt 文件,通常位于项目的根目录和子目录中。
2.内容:
 在 CMakeLists.txt 中,你可以指定:
 ①项目的名称和版本(project())
 ②编译选项(add_compile_options())
 ③要包含的目录(include_directories())
 ④要编译的源文件(add_executable() 或 add_library())
 ⑤库的链接(target_link_libraries())
 ⑥添加子目录(add_subdirectory())
 ⑦自定义的构建步骤等。
3.示例:
cmake_minimum_required(VERSION 3.10)
project(MyProject)
add_executable(MyExecutable main.cpp)
target_link_libraries(MyExecutable SomeLibrary)
 
(2).cmake文件
1.作用:
 .cmake 文件通常是CMake的模块文件,用于存放可重用的配置逻辑、函数、宏等。它们可以被多个 CMakeLists.txt 文件共享,用于封装常用的配置逻辑。你可以将一组常见的 CMake 命令、库查找、构建选项等封装在 .cmake 文件中,然后在 CMakeLists.txt 中通过 include() 命令引入使用。
2.内容:
 .cmake 文件通常包含定义 CMake 函数、宏、查找库等内容。它们不直接执行项目的构建,但为 CMakeLists.txt 文件提供了支持。
3.示例:FindSomeLibrary.cmake 文件:
# FindSomeLibrary.cmake
find_package(SomeLibrary REQUIRED)
 
(3)它们之间的关系
CMakeLists.txt 是项目的主配置文件,用于定义构建流程和其他重要的构建配置。
 .cmake 文件通常是模块化配置文件,封装了一些可复用的逻辑,可以通过 include() 命令引入到 CMakeLists.txt 中。
 在 CMakeLists.txt 中,你可以通过 include() 或 find_package() 来引用 .cmake 文件,实现项目构建的扩展和模块化。
(4)如何使用
在 CMakeLists.txt 中引用 .cmake 文件的方式:
include(path/to/some_file.cmake)
 
你也可以通过 find_package() 来加载 .cmake 文件,它会查找并执行某些标准的 CMake 模块(例如查找第三方库等):
find_package(SomeLibrary REQUIRED)
 
(5)总结
CMakeLists.txt 是 CMake 项目的配置文件,定义了项目的构建方式。
 .cmake 文件 是模块化的 CMake 配置文件,用于封装可重用的构建逻辑,可以在多个 CMakeLists.txt 中引用。
 它们通过 include() 或 find_package() 连接起来,CMakeLists.txt 通常会引入 .cmake 文件来简化构建配置。
4..cmake与.cmake.in文件的关系
 
.cmake是CMake脚本文件,而.cmake.in是模板文件。
①.cmake 文件是 CMake 的脚本文件,通常用于执行具体的构建、配置或打包任务。在 CMake 构建过程中,这些文件被 CMake 解释和执行,控制整个构建过程或其他相关任务。
 ②.cmake.in 文件是一个 CMake 模板文件,通常用于在构建过程中生成一个 .cmake 文件,或者生成其他类型的配置文件。它本身并不是一个 CMake 脚本,而是一个包含占位符的模板文件。CMake 会根据指定的值替换模板中的占位符,从而生成一个有效的 .cmake 文件或其他配置文件。
