这是 Meson 的原始设计原理。它描述的语法与发布版本不匹配

对于软件开发人员来说,最重要的工具是编辑器。如果你问程序员他们使用的编辑器,你通常会得到他们热情的赞美。你会听到 Emacs 是最棒的工具,或者 vi 多么优雅,或者 Eclipse 的集成功能如何提高你的效率。你可以感受到人们对这些程序的热情和喜爱。

第二重要的工具,甚至比编译器更重要,是构建系统。

这些东西几乎都被人厌恶。

关于构建系统,你通常能听到的最正面的评价(可能需要你花点心思)是“好吧,这是一个糟糕的系统,但其他所有选择都更糟糕”。不难理解为什么会出现这种情况。首先,常用的免费构建系统语法晦涩难懂。它们大多使用全局变量,这些变量在随机的位置设置,因此你永远无法真正确定某行代码的作用。它们在每个转折点都做出奇怪而不可预测的事情。

让我们用一个简单的例子来说明这一点。假设我们要在 GDB 下运行一个用 GNU Autotools 构建的程序。本能的做法是直接运行 `gdb programname`。问题是这可能有效也可能无效。在某些情况下,可执行文件是一个二进制文件,而在其他情况下,它是一个包装器 shell 脚本,它调用位于隐藏子目录中的实际二进制文件。如果二进制文件是一个脚本,则 GDB 调用将失败,但如果不是,则会成功。用户必须记住每个可执行文件的类型(这是构建系统的实现细节),才能调试它们。这篇博客文章中列举了一些其他的痛点。

鉴于这些特性,难怪大多数人不想与构建系统打交道。他们只会复制粘贴在某个地方有效的代码(某种程度上),并希望一切顺利。他们主动地逃避理解这个系统,因为仅仅想到它就令人厌恶。这样做也提供了一种逆向的职业保障。如果你不知道工具 X,那么你被要求在你的组织中使用它的时候,就更容易逃脱责任。相反,你可以从事更令人愉快的事情。

这导致了一个恶性循环。由于人们回避这些工具,不想与它们打交道,因此很少有人致力于改进它们。结果是冷漠和停滞不前。

我们能做得更好吗?

从本质上讲,构建 C 和 C++ 代码并非一项非常困难的任务。事实上,编写文本编辑器要复杂得多,需要付出更多努力。然而,我们有很多高质量的编辑器,但只有少数质量和可用性堪忧的构建系统。

因此,本着“自己动手,丰衣足食”的精神,我决定进行一项科学实验。这项实验的目的是探索构建一个“良好”构建系统需要什么。什么样的语法适合这个问题?这个应用需要解决什么问题?什么类型的解决方案最合适?

为了开始,这里列出了任何现代跨平台构建系统都需要提供的要求。

1. 必须易于使用

Python 的一大优点是它非常易读。很容易看到一段代码的作用。它简洁、清晰易懂。提议的构建系统必须在语法和语义上干净。副作用、全局状态和相互关系必须降到最低,或者如果可能,完全消除。

2. 必须默认执行正确的事情

大多数构建都是由在代码上工作的开发人员完成的。因此,默认设置必须针对该用例进行调整。例如,该系统应在没有优化的情况下,并带有调试信息来构建对象。它应生成可以从构建目录直接运行的二进制文件,而无需链接器技巧、shell 脚本或魔法环境变量。

3. 必须强制执行已建立的最佳实践

真的没有理由在没有等效于 `-Wall` 的情况下编译源代码。因此,默认情况下启用它。另一种最佳实践是源目录和构建目录的完全分离。所有构建工件必须存储在构建目录中。在任何情况下都不允许在源目录中写入杂散文件。

4. 必须对常用平台提供原生支持

许多自由软件项目可以在非自由平台上使用,例如 Windows 或 OSX。该系统必须为这些平台上的首选工具提供原生支持。实际上,这意味着对 Visual Studio 和 XCode 的原生支持。让这些 IDE 调用外部构建器二进制文件不算是原生支持。

5. 必须不会因为过时的平台而增加复杂性

这个构建系统的开发工作始于 2012 年的圣诞假期。这提供了一个自然的分界线:2012 年 12 月 24 日。任何当时没有被积极使用的平台、工具或库都不被明确支持。这些包括 IRIX、SunOS、OSF-1 等 Unix 系统,12/10 之前的 Ubuntu 版本,4.7 之前的 GCC 版本等等。如果这些旧版本碰巧能工作,那很好。如果它们不能工作,系统将不会添加任何代码行来解决它们的错误。

6. 必须快速

在中等规模的项目上运行配置步骤不应超过 5 秒。在包含 1000 个源文件的完全最新的树上运行编译命令不应超过 0.1 秒。

7. 必须为现代软件开发功能提供易于使用的支持

一个例子是预编译头文件。目前,没有免费软件构建系统为此提供原生支持。其他例子可能包括对 Valgrind 和单元测试的轻松集成、测试覆盖率报告等等。

8. 必须允许覆盖默认值

有时你只需要用给定的编译器标志编译文件,而没有其他标志,或者在奇怪的地方安装文件。如果用户真的想要这样做,该系统必须允许他们这样做。

解决方案概述

回顾这些需求,很明显,唯一可行的途径与 CMake 采取的途径大致相同:使用特定领域语言来声明构建系统。从这个声明中生成后端构建系统的配置。这可以是 Makefile、Visual Studio 或 XCode 项目,也可以是其他任何东西。

提议的 DSL 与现有 DSL 之间的区别在于,新的 DSL 是声明式的。它还尝试在比现有系统更高的抽象级别上工作。例如,在当前构建系统中使用外部库意味着手动提取和传递编译器标志和链接器标志。在提议的系统中,用户只需声明一个给定的构建目标使用给定的外部依赖项。然后,构建系统负责将所有标志和设置传递到它们适当的位置。这意味着用户可以专注于自己的代码,而不是将命令行参数从一个地方传递到另一个地方。

DSL 比 SCons 采取的方法需要更多工作,SCons 的方法是将系统作为 Python 库提供。但是,它允许我们使语法更具表现力,并通过例如使某些对象真正不可变来防止某些类型的错误。最终结果仍然相同:减少用户的负担。

Unix 的后端需要更多考虑。默认选择是 Make。但是它非常慢。在大型代码库中,Make 花几分钟时间才能确定不需要做任何事情,这种情况并不少见。我们使用 Ninja 而不是 Make,Ninja 非常快。后端代码从核心抽象出来,因此可以相对轻松地添加其他后端。

示例代码

设计讨论已经足够了,让我们来看看代码。在查看示例之前,我们要强调,这绝不是最终代码。它是概念验证代码,在当前系统中有效(2013 年 2 月),但可能会随时更改。

让我们从简单的开始。以下是编译单个可执行二进制文件的代码。

project('compile one', 'c')
executable('program', 'prog.c')

这几乎是你可以得到的最简单的代码。首先,你声明项目名称和它使用的语言。然后,你指定要构建的二进制文件及其源代码。构建系统将完成所有其他工作。它将添加适当的后缀(例如,Windows 上的 '.exe'),设置默认的编译器标志等等。

通常,程序不止一个源文件。在函数调用中列出所有文件可能会变得很麻烦。这就是为什么系统支持关键字参数的原因。它们看起来像这样。

project('compile several', 'c')
sourcelist = ['main.c', 'file1.c', 'file2.c', 'file3.c']
executable('program', sources : sourcelist)

外部依赖项易于使用。

project('external lib', 'c')
libdep = find_dep('extlibrary', required : true)
sourcelist = ['main.c', 'file1.c', 'file2.c', 'file3.c']
executable('program', sources : sourcelist, dep : libdep)

在其他构建系统中,你必须手动将外部依赖项的编译器标志和链接器标志添加到目标。在这个系统中,你只需声明 extlibrary 是必需的,并且生成的程序使用它。构建系统为你完成所有管道工作。

这是一个稍微复杂的定义。它仍然应该可以理解。

project('build library', 'c')
foolib = shared_library('foobar', sources : 'foobar.c',\
install : true)
exe = executable('testfoobar', 'tester.c', link : foolib)
add_test('test library', exe)

首先,我们构建一个名为 foobar 的共享库。它被标记为可安装的,因此运行 `meson install` 会将它安装到库目录(系统知道是哪个目录,因此用户无需担心)。然后,我们构建一个链接到该库的测试可执行文件。它不会被安装,而是被添加到单元测试列表中,可以使用命令 `meson test` 运行。

上面我们提到了预编译头文件,这是一个其他构建系统不支持的功能。以下是使用它的方法。

project('pch demo', 'cxx')
executable('myapp', 'myapp.cpp', pch : 'pch/myapp.hh')

其他构建系统无法如此轻松地提供 pch 支持的主要原因是它们没有强制执行某些最佳实践。由于包含路径的工作方式,不可能提供始终适用于源内构建和源外构建的 pch 支持。强制使用单独的构建目录和源目录使这个问题和其他许多问题变得容易得多。

获取代码

该实验的代码可以在 Meson 存储库 中找到。需要注意的是(在撰写本文时),它还不是一个构建系统。它只是一个构建系统的提议。它还不稳定。你可能不应该将它用作你项目的构建系统。

尽管如此,我希望这个实验最终能成为一个完整的构建系统。为此,我需要你的帮助。评论和补丁尤其受欢迎。

搜索结果是