written on Wednesday, May 7, 2008
相比其它语言,C/C++ 的编译过程比较复杂,依赖具体的编译器、操作系统、硬件环境,所以一直以来缺乏一个简单易用、跨平台、支持多种编译器的通用构建工具。
由于工作需要,我前不久在找一个合适的通用构建工具。个人观点是,一个好的通用构建工具应该符合下面五个标准:
对目前主流、仍然活跃的通用构建工具做了番仔细的对比和试用后, CMake 是最符合以上要求。
GNU make、NMake、Autotools 等传统构建工具,它们大都历史悠久、使用广泛、稳定可靠,但难用、不够智能。
它们在使用场合也有很大的局限性(比如 NMake 只支持 VC,GNU make、Autotools 只能跑在 Linux/Cygwin 下),所以不在考虑之列。
出于对 Python 的偏好, 我用了一段时间的 SCons. SCons 是一个优秀的编译工具,不过距离构建还有一些差距:项目小的话还好,规模一大,依赖分析速度急速下降,而且自动配置功能很弱 (跨平台构建能力不足)。
对 Waf 和 CMake 的了解都来自同一条消息: KDE4 将采用 CMake 来替换 autotools(KDE4 也曾考虑过 SCons)。Waf 尝试解决 SCons 所暴露出的一些问题。和 SCons 一样,Waf 使用 Python 语言作为构建脚本。
不过对于构建这样简单而重要的工作而言,它显得太灵活了。一个任务可能有多种途径可以解决 -- 用 Python 语句或 Waf 自定义规则。构建脚本用 Python 这么强大的语言,多少感觉有些杀鸡用牛刀 ... 而且你要努力说服一些 Ruby funs 去学 Python。
跨平台工具,Boost 用 Jam 来维护整个库的编译和测试。
没有深入使用过,没有特别的杀手锏,最初的目标是取代 make 工具。没有自动配置功能,能适应简单的构建任务。我个人感觉它的语法同样很不直观。
看完 KDE 团队关于为何选择 CMake 的文章、评论后,我在一个项目中开始尝试使用 CMake。令人吃惊的是:和 KDE 组织所描述的一样,我只花了一天便掌握了 CMake 的基本用法。两天后我都快成了 CMake 半桶水专家!这比学习 GNU Make 工具的用法还快很多.
我之前想当然的认为 Waf 学习成本很低(我熟悉 Python 语法),但事实并非如此。Waf 新定义的一套规则仍然会占用你一段时间来适应 -- 而且不比学习 CMake 的简单语法来的更短,里面夹杂着标准库会让不熟悉 Python 的成员很头痛。
CMake 的语法和规则足够简单,不需要花太多的时间,能很好的应付构建中碰到的各种问题。它不是最强大的,但它是最适合,也是最简单的。更复杂的功能可以用其它脚本语言(如 Python,Perl 等)配合完成。
这种方式明确了每个工具的职责 -- CMake 只解决构建问题,而且重点是 C/C++ 的构建。相对于 Waf 这种能在构建脚本里面实现许多令人惊叹,只用 CMake 根本无法实现的超级工具而言,CMake 更直接、清晰,更擅长解决构建问题,并和其它工具协调工作。
有时候我们不需要一个超级工具,需要的只是一个最能解决当前问题的工具;过度的灵活常常导致过度的复杂 (设计和使用上)。
CMake 是用 C/C++ 写的。这当然是它的好处:处理速度很快,对于日常工作,效率是很重要的。不过要是我扩展 CMake 的内置功能,比如编写一个新的模块用于生成 Eclipse CDT(2.6 提供了)的工程,我想我会更乐意用 Python 或其它脚本语言来实现。
不过幸好 CMake 的宏机制能做一些比较简单的扩展,比如支持新的平台和编译器(PCLint、vxWorks GCC 等)。
如果能修补一些小问题, Bakefile 也是一个比较不错的工具。它的原理和 CMake 类似,只是它使用 XML 格式而不是 CMake 专用语法(不过个人感觉 CMake 的宏语法比机器友好的 XML 标记更清晰)。它是用 Python 写的,这也意味着它更容易扩展。不过它缺乏自动配置功能。
开发和日构建需要的构建脚本不同:日构建的时候我们要的是 Makefile,但开发的时候我们需要工程文件。
和 SCons/Waf 等工具不同,CMake 可以用来生成 Makefile,还能生成 Visual Studio 等 IDE 的工程文件。
在项目上用 CMake 替换原来的 Makefile + 工程文件的方式后,整个构建和维护过程都比原先顺畅了许多。
配合使用 .bat 文件,我们用一份结构清晰的 CMake 构建脚本同时维护着 VC 6.0/2005 工程文件、NMake file 和 vxWorks GCC 交叉编译 Makefile,源文件的增删和编译参数的修改非常方便,节省了开发人员在软件构建上的时间成本。