本文的第一部分介绍了C/C++程序编译的流程与对应的基本元素:
- 成功的编译一个C/C++库或可执行文件,需要“告诉”编译器所用到的头文件、源文件、库文件以及这些文件的位置
- 常见的编译错误大多是由于这些基本元素中的一项或多项错误/缺失/不明确导致的
在程序只有一两个源文件构成的时候,可以直接调用编译器并传入正确的参数完成编译、链接过程。但是随着项目规模的增大,这种方式会越来越显繁琐和低效。使用Make工具,通过在makefile中定义编译的规则来完成同样的过程可以大大提高程序编译的自动化程度,提高工作效率。上一节仅给出了一个最基本的makefile编写例程,感兴趣的同学可以去网上找一找更详细的文档来更全面的学习makefile的写法,例如:
- https://seisman.github.io/how-to-write-makefile/index.html
实践过程中,makefile往往不需要每次开始新的项目都重头写起,可以利用开源项目的makefile作为模板,然后根据项目的具体要求修改makefile中相关的配置即可。
接下来的第二部分我们主要介绍CMake的使用。
从Make到CMake
值得一提的是makefile中的规则依然很底层,从第一部分的例子可以看出来使用gcc/g++直接编译程序所涉及到的命令依然会以几乎相同的形式出现在makefile中,make工具不过是把编译的过程像“文件批处理”一样给流程化了。如果你曾经尝试在linux下从源码编译、安装过应用程序的话,往往你会发现过程并不是直接make一下这么简单,最常见的安装流程可能有以下两种:
$ ./configure
$ make
$ sudo make install
或者
$ mkdir build && cd build
$ cmake ..
$ make
$ sudo make install
CMake默认使用的generator就是生成makefile。可以看到你还可以使用CMake生成Eclipse这类IDE的工程,如果在Windows平台下你还可以生成Visual Studio的Solution,这样你只要打开Visual Studio,导入自动生成的solution就可以build工程了。小结一下,CMake/Make在编译过程中分别起到的作用:
1. CMake根据用户编写的CMakeLists.txt自动生成makefile
2. Make按照makefile中的规则触发gcc/g++/ld等工具
3. gcc工具链完成实际的编译链接工作
可能这样的描述并不能直观的反映出来使用CMake的必要性,CMake以及CMakeLists.txt的使用到底抽象和简化了什么过程呢?可能最重要的一点就是:
- CMake可以根据用户配置的目标依据当前系统的环境自动生成编译所需的变量(如头文件/源文件/库文件所在的位置)并利用这些变量完成编译规则的自动生成
下面使用具体的例子来展示如何使用CMake同时直观的展示它如何build过程 (CMakeLists.txt的写法较为灵活,本文尽可能的使用现代CMake的推荐使用方法来编写CMakeLists.txt,你可能会在较老的教程里看到不一样的用法)。我们使用如下代码组织结构作为基本框架: