跳转到内容

Xmake v2.7.1 发布,更好的 C++ Modules 支持 ​

新特性介绍 ​

这个版本我们对 C++20 Modules 的实现进行了重构和改进,改进了模块文件的依赖图解析,新增了对 STL 和 User HeaderUnits 的支持,同时让 CMakelists/compile_commands 生成器也支持了 C++ Modules。

另外,我们新增了一个 xmake watch 插件,可以实时监控当前工程文件更新,自动触发增量构建,或者运行一些自定义的命令。

C++ Modules 改进 ​

Xmake 很早就已经支持 C++ Modules 的构建支持,并且能够自动分析模块间的依赖关系,实现最大化的并行编译。 另外,Xmake 采用 .mpp 作为默认的模块扩展名,但是也同时支持 .ixx, .cppm, .mxx 等扩展名。

例如:

lua
set_languages("c++20")
target("class")
    set_kind("binary")
    add_files("src/*.cpp", "src/*.mpp")

更多例子见:C++ Modules

但是之前的实现还存在很多不足之处:

  1. 不支持 HeaderUnits,因此也无法使用 stl 等模块
  2. 自己扫描源码实现模块依赖图解析,不支持编译器提供的依赖扫描,因此不完全可靠
  3. 不支持 CMakelists 生成
  4. 不支持 compile_commands.json 生成

而在新版中,我们对 C++20 模块的实现进行了重构和升级,上面提到的几点,我们都做了支持,新增了对 Headerunits 的支持,因此我们可以在模块中引入 STL 和 用户头文件模块。

同时,由于 msvc 和 gcc 高版本 都已经内置对模块依赖图的扫描分析,Xmake 会优先借助编译器实现模块依赖图分析,如果编译器不支持(clang),那么 Xmake 也会退化到自己的源码扫描实现上去。

相关的补丁见:#2641,非常感谢 @Arthapz 的贡献。

下面是一个使用了 STL HeaderUnits 的模块例子,例如:

bash
stl_headerunit$ xmake
[  0%]: generating.cxx.module.deps src/main.cpp
[  0%]: generating.cxx.module.deps src/hello.mpp
[ 20%]: generating.cxx.headerunit.bmi iostream
[ 60%]: generating.cxx.module.bmi hello
[ 70%]: cache compiling.release src/main.cpp
[ 80%]: linking.release stl_headerunit
[100%]: build ok!

对于首次编译,我们会扫描模块代码之间的依赖关系,然后预编译 iostream 等 stl 库作为 headerunit。

之后的重新编译,都会直接复用它们,实现编译加速。

注:通常我们至少需要添加一个 .mpp 文件,才能开启 C++20 modules 编译,如果只有 cpp 文件,默认是不会开启模块编译的。

但是,如果我们仅仅只是想在 cpp 文件中使用模块的 Headerunits 特性,比如引入一些 STL Headerunits 在 cpp 中使用, 那么我们也可以通过设置 set_policy("build.c++.modules", true) 来强行开启 C++ Modules 编译,例如:

lua
add_rules("mode.debug", "mode.release")

target("test")
    set_kind("binary")
    add_files("src/*.cpp")
    set_languages("c++20")
    set_policy("build.c++.modules", true)

工程文件监视和自动构建 ​

这个版本中,我们新增了 xmake watch 插件命令,可以自动监视项目文件更新,然后触发自动构建,或者运行一些自定义命令。

这通常用于个人开发时候,实现快速的实时增量编译,而不需要每次手动执行编译命令,提高开发效率。

项目更新后自动构建 ​

默认行为就是监视整个项目根目录,任何文件改动都会触发项目的增量编译。

bash
$ xmake watch
watching /private/tmp/test/src/** ..
watching /private/tmp/test/* ..
/private/tmp/test/src/main.cpp modified
[ 25%]: ccache compiling.release src/main.cpp
[ 50%]: linking.release test
[100%]: build ok!

监视指定目录 ​

我们也可以监视指定的代码目录,缩小监视范围,提升监视性能。

bash
$ xmake watch -d src
$ xmake watch -d "src;tests/*"

上面的命令,会去递归监视所有子目录,如果想要仅仅监视当前目录下的文件,不进行递归监视,可以使用下面的命令。

bash
$ xmake watch -p src
$ xmake watch -p "src;tests/*"

监视并运行指定命令 ​

如果想在自动构建后,还想自动运行构建的程序,我们可以使用自定义的命令集。

bash
$ xmake watch -c "xmake; xmake run"

上面的命令列表是作为字符串传递,这对于复杂命令参数,需要转义比较繁琐不够灵活,那么我们可以使用下面的方式进行任意命令的设置。

bash
$ xmake watch -- echo hello xmake!
$ xmake watch -- xmake run --help

监视并运行目标程序 ​

尽管我们可以通过自定义命令来实现目标程序的自动运行,但是我们也提供了更加方便的参数来实现这个行为。

bash
$ xmake watch -r
$ xmake watch --run
[100%]: build ok!
hello world!

监视并运行 lua 脚本 ​

我们还可以监视文件更新后,运行指定的 lua 脚本,实现更加灵活复杂的命令定制。

bash
$ xmake watch -s /tmp/test.lua

我们还可以再脚本中获取所有更新的文件路径列表和事件。

lua
function main(events)
    -- TODO handle events
end

Mac Catalyst 支持 ​

MAc Catalyst 是苹果后来新推的一项让 iPad App 带入 Mac 的方案,通过 Mac Catalyst 构建的 Mac App 与您的 iPad App 共享代码,而且您可以单独为 Mac 添加更多功能。

新版本中,我们新增了 Mac Catalyst 目标的构建支持,在 macOS 平台上,我们只需要添加 --appledev=catalyst 配置选项,就可以支持编译现有的 iOS 代码,并让它在 macOS 上运行起来,而无需做任何改动。

bash
$ xmake f --appledev=catalyst
$ xmake

我们可以在 iosapp_with_framework 这个测试项目中体验 Mac Catalyst 程序的编译运行。

bash
$ xmake
[ 36%]: processing.xcode.release src/framework/Info.plist
[ 40%]: cache compiling.release src/framework/test.m
[ 44%]: linking.release test
[ 48%]: generating.xcode.release test.framework
[ 56%]: compiling.xcode.release src/app/Assets.xcassets
[ 56%]: processing.xcode.release src/app/Info.plist
[ 60%]: cache compiling.release src/app/ViewController.m
[ 60%]: cache compiling.release src/app/SceneDelegate.m
[ 60%]: cache compiling.release src/app/main.m
[ 60%]: cache compiling.release src/app/AppDelegate.m
[ 60%]: compiling.xcode.release src/app/Base.lproj/LaunchScreen.storyboard
[ 60%]: compiling.xcode.release src/app/Base.lproj/Main.storyboard
[ 88%]: linking.release demo
[ 92%]: generating.xcode.release demo.app
[100%]: build ok!
$ xmake run
2022-08-26 15:11:03.581 demo[86248:9087199] add(1, 2): 3
2022-08-26 15:11:03.581 demo[86248:9087199] hello xmake!

改进远程编译 ​

拉取远程构建文件 ​

对于远程编译,我们新增加了一个拉取远程文件的命令,通常可用于远程编译完成后,下载远程的目标生成文件,库文件到本地。

bash
$ xmake service --pull 'build/**' outputdir

我们可以通过 --pull 'build/**' 模式匹配需要下载的文件,可以是构建文件,也可以是其他文件。

注:文件是按项目隔离的,只能指定下载当前项目下的文件,并不会让用户下载服务器上其他目录下的文件,避免一些安全隐患。

实时回显输出 ​

先前的版本在使用远程编译的时候,客户端是无法实时输出服务端的编译信息的,由于缓存的存在,本地看到的编译进度信息都是一块一块刷新出来,体验不是很好。

因此我们加上了行缓冲刷新支持,提高了输出回显的实时性,使得用户在远程编译时,更接近本地编译的体验。

改进分布式编译调度算法 ​

我们对 xmake 的分布式编译的服务器节点调度也做了进一步改进,加上了 cpu 负载和内存资源的权重,而不仅仅通过 cpu core 数量来分配任务。

因此,如果某些节点负载过高,我们会优先将编译任务调度到相当比较空闲的节点上去,充分利用所有编译资源。

更灵活的 cmake 包查找 ​

指定链接 ​

对于 cmake 包,我们新增了 link_libraries 配置选项,让用户在查找使用 cmake 包的时候,可以自定义配置包依赖的链接库,甚至对 target 链接的支持。

lua
add_requires("cmake::xxx", {configs = {link_libraries = {"abc::lib1", "abc::lib2"}}})

xmake 在查找 cmake 包的时候,会自动追加下面的配置,改进对 links 库的提取。

cmake
target_link_libraries(test PRIVATE ABC::lib1 ABC::lib2)

指定搜索模式 ​

另外,我们增加的搜索模式配置:

lua
add_requires("cmake::xxx", {configs = {search_mode = "config"}})
add_requires("cmake::xxx", {configs = {search_mode = "module"}})
add_requires("cmake::xxx") -- both

比如指定 config 搜索模式,告诉 cmake 从 XXXConfig.cmake 中查找包。

xmake 在查找 cmake 包的时候,内部会自动追加下面的配置。

cmake
find_package(ABC CONFIG REQUIRED)

armcc/armclang/rc 增量编译支持 ​

在新版本中,我们对 keil 的 armcc/armclang 编译器也进行头文件依赖分析,来支持增量编译。

另外,msvc 的 rc.exe 资源编译器本身是无法提供头文件依赖分析的,但是 cl.exe 的预处理器却是可以处理资源文件的。 因此我们可以通过 cl.exe /E test.rc 去预处理资源文件,从中提取依赖信息,来实现资源文件的增量编译支持。

目前测试下来,效果还不错,同时我们也对内部 ICON/BITMAP 的资源引用依赖也做了支持。

其他问题修复 ​

我们对构建缓存也做了很多修复,它将比之前的版本更加的稳定。另外我们也精简了 CMakelists 的生成。

更多细节改进见下面的更新日志:

更新内容 ​

新特性 ​

  • #2555: 添加 fwatcher 模块和 xmake watch 插件命令
  • 添加 xmake service --pull 'build/**' outputdir 命令去拉取远程构建服务器上的文件
  • #2641: 改进 C++20 模块, 支持 headerunits 和 project 生成
  • #2679: 支持 Mac Catalyst 构建

改进 ​

  • #2576: 改进从 cmake 中查找包,提供更过灵活的可选配置
  • #2577: 改进 add_headerfiles(),增加 {install = false} 支持
  • #2603: 为 ccache 默认禁用 -fdirectives-only
  • #2580: 设置 stdout 到 line 缓冲输出
  • #2571: 改进分布式编译的调度算法,增加 cpu/memory 状态权重
  • #2410: 改进 cmakelists 生成
  • #2690: 改机传递 toolchains 到包
  • #2686: 改进 armcc/armclang 支持增量编译
  • #2562: 改进 rc.exe 对引用文件依赖的解析和增量编译支持
  • 改进默认的并行构建任务数

Bugs 修复 ​

  • #2614: 为 msvc 修复构建 submodules2 测试工程
  • #2620: 修复构建缓存导致的增量编译问题
  • #2177: 修复 python.library 在 macOS 上段错误崩溃
  • #2708: 修复 mode.coverage 规则的链接错误
  • 修复 ios/macOS framework 和 application 的 rpath 加载路径