使用Makefile和CMake编译代码有什么区别?
使用Makefile和CMake编译代码有什么区别?
我使用C/C++编码并使用(GNU) Makefile编译代码。我也可以使用CMake做同样的事情并获得Makefile。不过,使用Makefile和CMake编译代码有什么区别呢?
关于CMake是“构建生成器”的说法是一个常见的误解。
它在技术上并没有错,只是描述了它的工作方式,而未描述它实际上是做什么。
在这个问题的上下文中,它们做的事情是一样的:将一堆C / C ++文件转换成二进制文件。
那么,真正的区别是什么呢?
-
CMake更高级。它专门编译C ++,你编写的构建代码更少,但也可以用于通用构建。
make
也有一些内置的C / C ++规则,但它们最多是无用的。 -
CMake
进行了两步构建:它生成了一个低级构建脚本,使用ninja
或make
或许多其他生成器,然后您运行它。通常堆积在Makefile
中的所有shell脚本块仅在生成阶段执行。因此,CMake
构建速度可以快几个数量级。 -
CMake
的语法易于支持外部工具,比make
更容易。 -
一旦
make
构建了一个工件,它就会忘记它是如何构建的。它是从什么源构建的,使用了什么编译器标志?CMake
追踪它,而make
则留给你自己决定。如果一个库源自上一个Makefile
版本以来被移除,make
不会重新构建它。 -
现代的
CMake
(从版本3开始)以“目标”之间的依赖关系为基础。目标仍然是单个输出文件,但它可以具有传递性(CMake术语中的“公共”/“接口”)依赖项。这些传递性依赖项可以向依赖包公开或隐藏。CMake
将为您管理目录。使用make
,您只能在逐个文件和手动管理目录的基础上工作。
你可以使用中间文件在make
中编写一些东西来处理最后两个空隙,但是你得自己完成。 make
包含了一个图灵完备的语言(有时候甚至包含两个或三个语言,如Guile),但前两个语言都很糟糕,而Guile几乎从未使用。
老实说,这就是CMake
和make
所共有的 —— 它们的语言都相当糟糕。我能想到的有:
- 它们没有用户定义的类型;
CMake
有三种数据类型:字符串、列表和带属性的目标。而make
只有一个:字符串;- 你通常通过设置全局变量将参数传递给函数。
- 现代的CMake部分地解决了这个问题 - 你可以设置一个目标的属性:
set_property(TARGET helloworld APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}")
;
- 现代的CMake部分地解决了这个问题 - 你可以设置一个目标的属性:
- 默认情况下,引用未定义的变量会被静默忽略;
Make(或更确切地说是Makefile)是一个构建系统——它驱动编译器和其他构建工具来构建您的代码。
CMake是一个构建系统生成器。它可以生成Makefile,也可以生成Ninja构建文件,可以生成KDevelop或Xcode项目,还可以生成Visual Studio方案。从相同的起点,相同的CMakeLists.txt文件出发。因此,如果您有一个跨平台项目,则CMake是使其构建系统无关的一种方法。
如果您有习惯于Visual Studio的Windows开发人员和发誓要使用GNU Make的Unix开发人员,则可以使用CMake。
我始终建议如果您的项目要多平台或者广泛使用,则使用CMake(或其他构建系统生成器,但CMake是我的个人首选)。CMake本身还提供了一些不错的功能,如依赖检测、库接口管理或与CTest、CDash和CPack的集成。
使用构建系统生成器可以使您的项目更具未来性。即使您现在只使用GNU-Make,如果您以后决定扩展到其他平台(无论是Windows还是嵌入式系统),或者只想使用IDE,也没有问题。