天天看点

xmake v2.5.9 发布,改进 C++20 模块,并支持 Nim, Keil MDK 和 Un

​​xmake​​ 是一个基于 Lua 的轻量级跨平台构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。

这个版本,我们增加了大量重量级的新特性,例如:Nim 语言项目的构建支持,Keil MDK,Circle 和 Wasi 工具链支持。

另外,我们对 C++20 Modules 进行了大改进,不仅支持最新 gcc-11, clang 和 msvc 编译器,而且还得模块间依赖做了自动分析,实现最大程度的并行化编译支持。

最后,还有一个比较有用的特性就是 Unity Build 支持,通过它我们可以对 C++ 代码的编译速度做到很大程度的提升。

​​项目源码​​

​​官方文档​​

​​入门课程​​

最近,我们新增了对 Nimlang 项目的构建支持,相关 issues 见:​​#1756​​

我们可以使用 ​<code>​xmake create​</code>​ 命令创建空工程。

完整例子见:​​Nimble Package Example​​

main.nim

完整例子见:​​Native Package Example​​

我们知道,C++ 代码编译速度通常很慢,因为每个代码文件都需要解析引入的头文件。

而通过 Unity Build,我们通过将多个 cpp 文件组合成一个来加速项目的编译,其主要好处是减少了解析和编译包含在多个源文件中的头文件内容的重复工作,头文件的内容通常占预处理后源文件中的大部分代码。

Unity 构建还通过减少编译链创建和处理的目标文件的数量来减轻由于拥有大量小源文件而导致的开销,并允许跨形成统一构建任务的文件进行过程间分析和优化(类似于效果链接时优化)。

它可以极大提升 C/C++ 代码的编译速度,通常会有 30% 的速度提升,不过根据项目的复杂程度不同,其带来的效益还是要根据自身项目情况而定。

xmake 在 v2.5.9 版本中,也已经支持了这种构建模式。相关 issues 见 ​​#1019​​。

我们提供了两个内置规则,分别处理对 C 和 C++ 代码的 Unity Build。

默认情况下,只要设置上述规则,就会启用 Batch 模式的 Unity Build,也就是 xmake 自动根据项目代码文件,自动组织合并。

我们可以额外通过设置 ​<code>​{batchsize = 2}​</code>​ 参数到规则,来指定每个合并 Batch 的大小数量,这里也就是每两个 C++ 文件自动合并编译。

编译效果大概如下:

由于我们仅仅启用了 C++ 的 Unity Build,所以 C 代码还是正常挨个编译。另外在 Unity Build 模式下,我们还是可以做到尽可能的并行编译加速,互不冲突。

如果没有设置 ​<code>​batchsize​</code>​ 参数,那么默认会吧所有文件合并到一个文件中进行编译。

如果上面的 Batch 模式自动合并效果不理想,我们也可以使用自定义分组,来手动配置哪些文件合并到一起参与编译,这使得用户更加地灵活可控。

我们使用 ​<code>​{unity_group = "foo"}​</code>​ 来指定每个分组的名字,以及包含了哪些文件,每个分组的文件都会单独被合并到一个代码文件中去。

另外,​<code>​batchsize = 0​</code>​ 也强行禁用了 Batch 模式,也就是说,没有设置 unity_group 分组的代码文件,我们还是会单独编译它们,也不会自动开启自动合并。

我们只要把上面的 ​<code>​batchsize = 0​</code>​ 改成非 0 值,就可以让分组模式下,剩余的代码文件继续开启 Batch 模式自动合并编译。

如果是 Batch 模式下,由于是自动合并操作,所以默认会对所有文件执行合并,但如果有些代码文件我们不想让它参与合并,那么我们也可以通过 ​<code>​{unity_ignored = true}​</code>​ 去忽略它们。

尽管 Unity Build 带啦的收益不错,但是我们还是会遇到一些意外的情况,比如我们的两个代码文件里面,全局命名空间下,都存在相同名字的全局变量和函数。

那么,合并编译就会带来编译冲突问题,编译器通常会报全局变量重定义错误。

为了解决这个问题,我们需要用户代码上做一些修改,然后配合构建工具来解决。

比如,我们的 foo.cpp 和 bar.cpp 都有全局变量 i。

foo.cpp

bar.cpp

那么,我们合并编译就会冲突,我们可以引入一个 Unique ID 来隔离全局的匿名空间。

接下来,我们还需要保证代码合并后, ​<code>​MY_UNITY_ID​</code>​ 在 foo 和 bar 中的定义完全不同,可以按文件名算一个唯一 ID 值出来,互不冲突,也就是实现下面的合并效果:

这看上去似乎很麻烦,但是用户不需要关心这些,xmake 会在合并时候自动处理它们,用户只需要指定这个 Unique ID 的名字就行了,例如下面这样:

处理全局变量,还有全局的重名宏定义,函数什么的,都可以采用这种方式来避免冲突。

xmake 采用 ​<code>​.mpp​</code>​ 作为默认的模块扩展名,但是也同时支持 ​<code>​.ixx​</code>​, ​<code>​.cppm​</code>​, ​<code>​.mxx​</code>​ 等扩展名。

早期,xmake 试验性支持过 C++ Modules TS,但是那个时候,gcc 还不能很好的支持,并且模块间的依赖也不支持。

最近,我们对 xmake 做了大量改进,已经完整支持 gcc-11/clang/msvc 的 C++20 Modules 构建支持,并且能够自动分析模块间的依赖关系,实现最大化并行编译。

同时,对新版本的 clang/msvc 也做了更好地处理。

更多例子见:​​C++ Modules​​

上个版本,我们增加了对 Lua5.3 运行时支持,而在这个版本中,我们进一步升级 Lua 运行时到 5.4,相比 5.3,运行性能和内存利用率上都有很大的提升。

不过,目前 xmake 的默认运行时还是 luajit,预计 2.6.1 版本(也就是下个版本),会正式切到 Lua5.4 作为默认的运行时。

尽管切换了 Lua 运行时,但是对于用户端,完全是无感知的,并且完全兼容现有工程配置,因为 xmake 原本就对暴露的 api 提供了一层封装, 对于 lua 版本之间存在兼容性问题的接口,例如 setfenv, ffi 等都隐藏在内部,原本就没有暴露给用户使用。

我们在这个版本中,还新增了 Keil/MDK 嵌入式编译工具链的支持,相关例子工程:​​Example​​

xmake 会自动探测 Keil/MDK 安装的编译器,相关 issues ​​#1753​​。

之前我们支持了 wasm 平台的 emcc 工具链来构建 wasm 程序,而这里,我们新加了另外一个启用了 WASI 的 Wasm 工具链来替换 emcc。

我们还新增了 circle 编译器的支持,这是个新的 C++20 编译器,额外附带了一些有趣的编译期元编程特性,有兴趣的同学可以到官网查看:https://www.circle-lang.org/

如果用户额外安装了 gcc-11, gcc-10 等特定版本的 gcc 工具链,在本地的 gcc 程序命名可能是 ​<code>​/usr/bin/gcc-11​</code>​。

一种办法是通过 ​<code>​xmake f --cc=gcc-11 --cxx=gcc-11 --ld=g++-11​</code>​ 挨个指定配置来切换,但非常繁琐。

所以,xmake 也提供了更加快捷的切换方式:

只需要指定 ​<code>​gcc-11​</code>​ 对应的版本名,就可以快速切换整个 gcc 工具链。

xmake 提供了 check_features 辅助接口来检测编译器特性。

config.h.in

config.h

而在 2.5.9 版本中,我们新增了 c++17 特性检测:

<col>

特性名

cxx_aggregate_bases

cxx_aligned_new

cxx_capture_star_this

cxx_constexpr

cxx_deduction_guides

cxx_enumerator_attributes

cxx_fold_expressions

cxx_guaranteed_copy_elision

cxx_hex_float

cxx_if_constexpr

cxx_inheriting_constructors

cxx_inline_variables

cxx_namespace_attributes

cxx_noexcept_function_type

cxx_nontype_template_args

cxx_nontype_template_parameter_auto

cxx_range_based_for

cxx_static_assert

cxx_structured_bindings

cxx_template_template_args

cxx_variadic_using

还新增了 c++20 特性检测:

cxx_aggregate_paren_init

cxx_char8_t

cxx_concepts

cxx_conditional_explicit

cxx_consteval

cxx_constexpr_dynamic_alloc

cxx_constexpr_in_decltype

cxx_constinit

cxx_designated_initializers

cxx_generic_lambdas

cxx_impl_coroutine

cxx_impl_destroying_delete

cxx_impl_three_way_comparison

cxx_init_captures

cxx_modules

cxx_using_enum

xmake 自带的 xrepo 包管理工具,现在已经可以很好的支持包虚拟机环境管理,类似 nixos 的 nixpkgs。

我们可以通过在当前目录下,添加 xmake.lua 文件,定制化一些包配置,然后进入特定的包虚拟环境。

我们也可以在 xmake.lua 配置加载对应的工具链环境,比如加载 vs 的编译环境。

我们可以使用下面的命令,把指定的虚拟环境配置全局注册到系统中,方便快速切换。

这个时候,我们就保存了一个名叫 base 的全局虚拟环境,我们可以通过 list 命令去查看它。

我们也可以删除它。

如果我们注册了多个虚拟环境,我们也可以快速切换它们。

或者直接加载指定虚拟环境运行特定命令

​<code>​xrepo env -b/--bind​</code>​ 就是绑定指定的虚拟环境,更多详情见:​​#1762​​

对于 target,我们新增了 ​<code>​headeronly​</code>​ 目标类型,这个类型的目标程序,我们不会实际编译它们,因为它没有源文件需要被编译。

但是它包含了头文件列表,这通常用于 headeronly 库项目的安装,IDE 工程的文件列表生成,以及安装阶段的 cmake/pkgconfig 导入文件的生成。

例如:

更多详情见:​​#1747​​

现在 cmake 已经是事实上的标准,所以 CMake 提供的 find_package 已经可以查找大量的库和模块,我们完全复用 cmake 的这部分生态来扩充 xmake 对包的集成。

我们可以通过 ​<code>​find_package("cmake::xxx")​</code>​ 去借助 cmake 来找一些包,xmake 会自动生成一个 cmake 脚本来调用 cmake 的 find_package 去查找一些包,获取里面包信息。

mydir/cmake_modules/FindFoo.cmake

相关 issues: ​​#1632​​

我们进一步改进了 cmake 生成器,现在可以将 rule 里面自定义的脚本序列化成命令列表,一起生成到 CMakelists.txt

不过目前只能支持 batchcmds 系列脚本的序列化。

它将会生成类似如下的 CMakelists.txt

不过 cmake 的 ​<code>​ADD_CUSTOM_COMMAND​</code>​ PRE_BUILD 实际效果在不同生成器上,差异比较大,无法满足我们的需求,因此我们做了很多处理来支持它。

相关 issues: ​​#1735​​

我们还改进了 get.sh 安装脚本,来更好地支持 nixOS。

​​#1736​​: 支持 wasi-sdk 工具链

支持 Lua 5.4 运行时

添加 gcc-8, gcc-9, gcc-10, gcc-11 工具链

​​#1623​​: 支持 find_package 从 cmake 查找包

​​#1747​​: 添加 ​<code>​set_kind("headeronly")​</code>​ 更好的处理 headeronly 库的安装

​​#1019​​: 支持 Unity build

​​#1438​​: 增加 ​<code>​xmake l cli.amalgamate​</code>​ 命令支持代码合并

​​#1765​​: 支持 nim 语言

​​#1762​​: 为 ​<code>​xrepo env​</code>​ 管理和切换指定的环境配置

​​#1767​​: 支持 Circle 编译器

​​#1753​​: 支持 Keil/MDK 的 armcc/armclang 工具链

​​#1774​​: 添加 table.contains api

​​#1735​​: 添加自定义命令到 cmake 生成器

​​#1781​​: 改进 get.sh 安装脚本支持 nixos

​​#1528​​: 检测 c++17/20 特性

​​#1729​​: 改进 C++20 modules 对 clang/gcc/msvc 的支持,支持模块间依赖编译和并行优化

​​#1779​​: 改进 ml.exe/x86,移除内置的 ​<code>​-Gd​</code>​ 选项

继续阅读