前言
对于C/C++代码来说,不使用任何构建工具来辅助编译C/C++代码的话,就需要自己手动指定使用的编译器(clang、gcc/g++或者其他编译器),一般来说需要在编译命令下指定这些:
- 指定需要编译的源文件
- 指定头文件的搜索目录
- 指定库文件的搜索目录
- 需要链接的动态库和静态库
- 指定需要额外定义的宏或者取消定义的宏来控制编译
- 指定编译优化选项
- 指定语言标准
- 等等
直接使用gcc/g++命令在使用代码文件不多,库直接的依赖关系不复杂的情况下是可以接受的。但是在一些大型项目中,源文件上千上万,各个文件和库直接的依赖关系复杂,如果还手动输入编译命令的话,复杂度就会变得很大。
什么是构建工具
简单来讲,代码变成可执行文件,叫做编译(compile);先编译这个,还是先编译那个(即编译的安排),编译过程中调用哪些库和文件,叫做构建(build)。
CMake
CMake是一个跨平台的自动化构建工具,支持多种编译平台(Makefile、XCode、Visual Studio等),其输出标准构建档(比如,可以输出Visual Studio的“.sln”项目文件,然后再用Visual Studio打开编译即可),不直接输出二进制文件。具有一定的依赖自动分析能力。
CMake 支持语言: C
, CXX
(i.e. C++), CUDA
, OBJC
(i.e. Objective-C), OBJCXX
, Fortran
, HIP
, ISPC
, and ASM
(project — CMake 3.24.0-rc3 Documentation);对Java有部分支持,但是并不完善。
分布式编译:CMake只输出构建档,所以它本身是不支持分布式编译的。一般需要配合分布式编译工具,例如distcc等,来实现分布式编译。
hello world
以hello world为例,演示一个cmake的简单项目。首先创建如下的目录结构
目录
cpp_test
|---src
|---hello_world.cc
hello_world.cc代码如下:
#include <iostream>
int main() {
std::cout << "hello world" << std::endl;
return 0;
}
我们代码就构建完成了
然后再根目录下创建CmakeLists.txt文件,代码如下:
# cmake 最低版本需求
cmake_minimum_required(VERSION 3.13)
# 工程名称
project(cmake_study)
# 设置
set(CMAKE_CXX_STANDARD 11)
# 编译源码生成目标
add_executable(cmake_study src/main.cc)
具体意义后面再讲,先跟着做一遍。然后在根目录下创建一个空文件夹build,添加完CmakeLists.txt后目录结构如下:
cpp_test
|--src
| |--hello_world.cc
|
|--build
|
|--CmakeLists.txt
然后一次执行以下命令:
cd build
cmake ..
make
可以看到build文件夹中会生成如下的文件
其中cmake_study 是我们最终生成的可执行文件,输入
./cmake_study
即可运行该文件。
下面来解释做的这些事情是为什么。
CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译源码生成可执行程序或共享库
CMakeLists.txt中记录了编译所需要的所有信息,我们来看一下我们都设置了什么信息:
cmake_minimum_required(VERSION 3.13)
# cmake 最低版本需求,这个要求和你的cmake版本对应
project(cmake_study)
# 给你的工程设置一个名称,这个名字一般用于编译中的目录搜索,对我们用户是透明的,所以随便起就行
set(CMAKE_CXX_STANDARD 11)
# 指定编译器版本
add_executable(cmake_study src/main.cc)
# 编译源码生成目标,这个比较重要,用于指定我们编译的文件和生成的可执行文件文件名。我们的main函数最终生成一个cmake_study 就是因为这个设置。
我们刚刚编译过程中,一共用到了三个命令
cd build
cmake ..
make
除了第一个切换到build路径下不谈,我们来看执行cmake会发生什么?
可以看到,cmake运行之后,生成了3个文件一个文件夹, 其中CMakeFiles文件夹是一个多层嵌套的文件夹。这也是为什么要切换到build文件夹的原因,如果在根目录下,会生成大量的编译中间文件,使目录结构看起来混乱无比。
在生成的文件中,makefile是最重要的文件,其中记录了编译的文件,其中依赖等等。在cmake之前与一个构建工具叫make,make就是通过编写makefiles来编译项目。cmake把编写makefiles的过程也进行了简化,通过设置CMakeLists.txt文件,就可以自动生成makefiles。然后调用make 就实现编译。
这里需要注意的是cmake处理对象是文件夹,所以cmake后面一定要跟着一个文件夹的路径,且这个路径下要有CMakeLists.txt文件
那么执行make以后会发生什么呢?我们注意cpp_test/build/CMakeFiles/cmake_study.dir/src 这个路径
在执行完cmake以后还是空的
执行完make以后出现了main.cc.o和 main.cc.o同时再build文件夹下也出现了cmake_study文件。 这说明 make命令才是真正执行了编译和链接。而cmake只是提供了makefiles而已。
这也说明了,如果我们没有修改代码的引用和依赖,就不用重新cmake。
当然,cmake工具自己也可以打印hello world。不过是在在生成makefile文件的时候,而不是编译的时候。
# cmake 最低版本需求
cmake_minimum_required(VERSION 3.13)
# 工程名称
project(cmake_study1)
# 设置
set(CMAKE_CXX_STANDARD 11)
message("hello world ..... ")