ChatGPT解决这个技术问题 Extra ChatGPT

使用 Makefile 和 CMake 编译代码有什么区别?

我用 C/C++ 编写代码并使用 (GNU) Makefile 来编译代码。我可以用 CMake 做同样的事情并获得一个 Makefile。但是,使用 Makefile 和 CMake 编译代码有什么区别?


I
Iulian Onofrei

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 怎么办?


谢谢你!只是为了确认一下,如果我只在 Linux 环境中编程,那么只有 makefile 就足够了,但是如果我希望 Linux 中的程序在 Mac 中运行,而不是 cmake 是更好的选择,因为我知道,我们不需要在上创建新的 makefile Mac,而只是运行 cmake。这就是重点吗?
@rish 是的,这就是要点。但是请注意,在 Linux 上编程的方法比 Makefile 更多——参见 QtCreator、KDEvelop、Ninja。对于其中的每一个,它要么是“创建一个项目并使其与 Makefile 保持同步”,要么是“重新运行 CMake”。而且,正如答案所提到的,CMake 还具有其他功能,例如依赖项发现(例如 find_package())或测试/打包支持。
我读到 CMake 无法创建非递归生成文件。这仍然是真的吗?
@Angew Non-recursive 是使用完整的项目依赖树调用一次 make 。与顶级 makefile 以特定顺序调用子项目 makefile 时的递归相反。
这是 CMake 的一个重要弱点 - GNU make 有其缺陷,但如果您花时间学习它,它非常强大且用途广泛,并且可以在大量平台上运行。没有完整的依赖树来分析是一个主要缺陷,只是谷歌的“递归使被认为有害”。
V
Victor Sergienko

关于 CMake 是“构建生成器”的说法是一个常见的误解。

这在技术上并没有错。它只是描述了它是如何工作的,而不是它的作用。

在问题的上下文中,他们做同样的事情:获取一堆 C/C++ 文件并将它们转换为二进制文件。

那么,真正的区别是什么?

CMake 更高级。它专为编译 C++ 而定制,您为此编写的构建代码要少得多,但也可用于通用构建。 make 也有一些内置的 C/C++ 规则,但它们充其量是无用的。

CMake 进行两步构建:它在 ninja 或 make 或许多其他生成器中生成低级构建脚本,然后运行它。通常堆积到 Makefile 中的所有 shell 脚本片段仅在生成阶段执行。因此,CMake 构建速度可以快几个数量级。

CMake 的语法比 make 更容易支持外部工具。

一旦 make 构建了一个工件,它就会忘记它是如何构建的。它是从什么来源构建的,什么编译器标志? CMake 跟踪它,让它由你决定。如果自上一版本的 Makefile 以来删除了其中一个库源,make 将不会重建它。

现代 CMake(从版本 3.something 开始)根据“目标”之间的依赖关系工作。目标仍然是单个输出文件,但它可以具有传递(CMake 术语中的“公共”/“接口”)依赖关系。这些传递依赖可以暴露给依赖包,也可以隐藏在依赖包中。 CMake 将为您管理目录。使用 make,您会被困在逐个文件和手动管理目录的级别。

您可以使用中间文件在 make 中编写代码来弥补最后两个空白,但您只能靠自己。 make 确实包含一个 Turing complete language(即使是两个,有时也包含三个 Guile);前两个太可怕了,Guile 实际上从未使用过。

老实说,这就是 CMakemake 的共同点——他们的语言非常糟糕。这就是我想到的:

它们没有用户定义的类型;

CMake 具有三种数据类型:字符串、列表和具有属性的目标。 make 有一个:字符串;

您通常通过设置全局变量将参数传递给函数。这在现代 CMake 中得到了部分处理 - 您可以设置目标的属性: set_property(TARGET helloworld APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}");

这在现代 CMake 中得到了部分处理 - 您可以设置目标的属性: set_property(TARGET helloworld APPEND PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}");

默认情况下,对未定义变量的引用会被忽略;


这里有一些很好的信息,但有一点是完全错误的:cmake 有一个 LIST 类型,因为它具有适当的 LIST 函数,这对于许多构建系统任务至关重要,有点不同:cmake.org/cmake/help/git-master/command/list.html
@VictorSergienko,在OP的问题中没有被问到,但我在等待妙语......你用什么感觉比任何一个都好?
由于历史原因,我使用 make,我讨厌它。 CMake 也不是没有问题(尝试用它进行交叉编译),但是对于我自己的项目,CMake 会赢,因为你编写的代码更少,而且它得到了工具的支持。
让我们永远埋葬 automake 和 autoconf