Xmake 是一个基于 Lua 的轻量级跨平台构建工具。
它非常的轻量,没有任何依赖,因为它内置了 Lua 运行时。
它使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt ,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。
我们能够使用它像 Make/Ninja 那样可以直接编译项目,也可以像 CMake/Meson 那样生成工程文件,另外它还有内置的包管理系统来帮助用户解决 C/C++ 依赖库的集成使用问题。
目前,Xmake 主要用于 C/C++ 项目的构建,但是同时也支持其他 native 语言的构建,可以实现跟 C/C++ 进行混合编译,同时编译速度也是非常的快,可以跟 Ninja 持平。
Xmake = Build backend + Project Generator + Package Manager + [Remote|Distributed] Build + Cache
尽管不是很准确,但我们还是可以把 Xmake 按下面的方式来理解:
Xmake ~= Make/Ninja + CMake/Meson + Vcpkg/Conan + distcc + ccache/sccache
这个版本我们对 C++20 Modules 的实现进行了重构和改进,改进了模块文件的依赖图解析,新增了对 STL 和 User HeaderUnits 的支持,同时让 CMakelists/compile_commands 生成器也支持了 C++ Modules 。
另外,我们新增了一个 xmake watch
插件,可以实时监控当前工程文件更新,自动触发增量构建,或者运行一些自定义的命令。
Xmake 很早就已经支持 C++ Modules 的构建支持,并且能够自动分析模块间的依赖关系,实现最大化的并行编译。
另外,Xmake 采用 .mpp
作为默认的模块扩展名,但是也同时支持 .ixx
, .cppm
, .mxx
等扩展名。
例如:
set_languages("c++20")
target("class")
set_kind("binary")
add_files("src/*.cpp", "src/*.mpp")
更多例子见:C++ Modules
但是之前的实现还存在很多不足之处:
而在新版中,我们对 C++20 模块的实现进行了重构和升级,上面提到的几点,我们都做了支持,新增了对 Headerunits 的支持,因此我们可以在模块中引入 STL 和 用户头文件模块。
同时,由于 msvc 和 gcc 高版本 都已经内置对模块依赖图的扫描分析,Xmake 会优先借助编译器实现模块依赖图分析,如果编译器不支持( clang ),那么 Xmake 也会退化到自己的源码扫描实现上去。
相关的补丁见:#2641,非常感谢 @Arthapz 的贡献。
下面是一个使用了 STL HeaderUnits 的模块例子,例如:
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 编译,例如:
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
插件命令,可以自动监视项目文件更新,然后触发自动构建,或者运行一些自定义命令。
这通常用于个人开发时候,实现快速的实时增量编译,而不需要每次手动执行编译命令,提高开发效率。
默认行为就是监视整个项目根目录,任何文件改动都会触发项目的增量编译。
$ 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!
我们也可以监视指定的代码目录,缩小监视范围,提升监视性能。
$ xmake watch -d src
$ xmake watch -d "src;tests/*"
上面的命令,会去递归监视所有子目录,如果想要仅仅监视当前目录下的文件,不进行递归监视,可以使用下面的命令。
$ xmake watch -p src
$ xmake watch -p "src;tests/*"
如果想在自动构建后,还想自动运行构建的程序,我们可以使用自定义的命令集。
$ xmake watch -c "xmake; xmake run"
上面的命令列表是作为字符串传递,这对于复杂命令参数,需要转义比较繁琐不够灵活,那么我们可以使用下面的方式进行任意命令的设置。
$ xmake watch -- echo hello xmake!
$ xmake watch -- xmake run --help
尽管我们可以通过自定义命令来实现目标程序的自动运行,但是我们也提供了更加方便的参数来实现这个行为。
$ xmake watch -r
$ xmake watch --run
[100%]: build ok!
hello world!
我们还可以监视文件更新后,运行指定的 lua 脚本,实现更加灵活复杂的命令定制。
$ xmake watch -s /tmp/test.lua
我们还可以再脚本中获取所有更新的文件路径列表和事件。
function main(events)
-- TODO handle events
end
MAc Catalyst 是苹果后来新推的一项让 iPad App 带入 Mac 的方案,通过 Mac Catalyst 构建的 Mac App 与您的 iPad App 共享代码,而且您可以单独为 Mac 添加更多功能。
新版本中,我们新增了 Mac Catalyst 目标的构建支持,在 macOS 平台上,我们只需要添加 --appledev=catalyst
配置选项,就可以支持编译现有的 iOS 代码,并让它在 macOS 上运行起来,而无需做任何改动。
$ xmake f --appledev=catalyst
$ xmake
我们可以在 iosapp_with_framework 这个测试项目中体验 Mac Catalyst 程序的编译运行。
$ 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!
对于远程编译,我们新增加了一个拉取远程文件的命令,通常可用于远程编译完成后,下载远程的目标生成文件,库文件到本地。
$ xmake service --pull 'build/**' outputdir
我们可以通过 --pull 'build/**'
模式匹配需要下载的文件,可以是构建文件,也可以是其他文件。
注:文件是按项目隔离的,只能指定下载当前项目下的文件,并不会让用户下载服务器上其他目录下的文件,避免一些安全隐患。
先前的版本在使用远程编译的时候,客户端是无法实时输出服务端的编译信息的,由于缓存的存在,本地看到的编译进度信息都是一块一块刷新出来,体验不是很好。
因此我们加上了行缓冲刷新支持,提高了输出回显的实时性,使得用户在远程编译时,更接近本地编译的体验。
我们对 xmake 的分布式编译的服务器节点调度也做了进一步改进,加上了 cpu 负载和内存资源的权重,而不仅仅通过 cpu core 数量来分配任务。
因此,如果某些节点负载过高,我们会优先将编译任务调度到相当比较空闲的节点上去,充分利用所有编译资源。
对于 cmake 包,我们新增了 link_libraries
配置选项,让用户在查找使用 cmake 包的时候,可以自定义配置包依赖的链接库,甚至对 target 链接的支持。
add_requires("cmake::xxx", {configs = {link_libraries = {"abc::lib1", "abc::lib2"}}})
xmake 在查找 cmake 包的时候,会自动追加下面的配置,改进对 links 库的提取。
target_link_libraries(test PRIVATE ABC::lib1 ABC::lib2)
另外,我们增加的搜索模式配置:
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 包的时候,内部会自动追加下面的配置。
find_package(ABC CONFIG REQUIRED)
在新版本中,我们对 keil 的 armcc/armclang 编译器也进行头文件依赖分析,来支持增量编译。
另外,msvc 的 rc.exe 资源编译器本身是无法提供头文件依赖分析的,但是 cl.exe 的预处理器却是可以处理资源文件的。
因此我们可以通过 cl.exe /E test.rc
去预处理资源文件,从中提取依赖信息,来实现资源文件的增量编译支持。
目前测试下来,效果还不错,同时我们也对内部 ICON/BITMAP 的资源引用依赖也做了支持。
我们对构建缓存也做了很多修复,它将比之前的版本更加的稳定。另外我们也精简了 CMakelists 的生成。
更多细节改进见下面的更新日志:
xmake watch
插件命令xmake service --pull 'build/**' outputdir
命令去拉取远程构建服务器上的文件{install = false}
支持-fdirectives-only
1
L4Linux 2022-08-27 14:06:10 +08:00 via Android
初次知道这个项目,有几个疑问:
对 target 设置 flag 之前,会检查编译器支持这个 flag 吗?可以通过变量设置 flag 的内容吗?后面这点是考虑到不同编译器实现相同功能的 flag 不一样。还有,有没有可选的 flag ,检测到编译器不支持就不设置,而不是报错? 多个优化等级可以同时设置吗? O2 和 Os 经常一起用。 给一段代码,检查编译器是否能编译出正确的二进制文件,这种功能有吗? 可以检查某头文件存不存在某个符号之类的吗? |
2
L4Linux 2022-08-27 14:08:29 +08:00 via Android
还有,如何区分编译时的 flag 和链接时的 flag 呢?
|
3
waruqi OP > 对 target 设置 flag 之前,会检查编译器支持这个 flag 吗?可以通过变量设置 flag 的内容吗?后面这点是考虑到不同编译器实现相同功能的 flag 不一样。还有,有没有可选的 flag ,检测到编译器不支持就不设置,而不是报错?
目前默认行为就是自动检测,不支持的 flags 自动忽略,部分 flags 还能自动映射到对应编译器 > 多个优化等级可以同时设置吗? O2 和 Os 经常一起用。 原本这个就不能同时支持,我没听说 gcc 还是 clang 能同时设置两个的,即使你同时设置了 -O2 -Os ,最后生效的也是最右边的 -Os 另外,你非要同时设置,自己 add_cxflags("-O2", "-Os") 加上就行了 > 给一段代码,检查编译器是否能编译出正确的二进制文件,这种功能有吗? 支持,代码片段检测,接口检测,运行检测什么的都有 > 可以检查某头文件存不存在某个符号之类的吗? 当然有 |
4
waruqi OP > 还有,如何区分编译时的 flag 和链接时的 flag 呢?
编译 add_cxflags add_cflags 链接 add_ldflags 动态链接 add_shflags 具体你可以看文档 什么都有 |
5
Chipmunker 2022-08-27 15:28:38 +08:00
之前看到过这个工具,想在项目中使用,但是不知道怎么实现在系统中找安装好的库。找了一下也没发现类似的示例代码(很久之前了,不知道现在什么情况)。
|
6
waruqi OP |
7
waruqi OP system = true ,刚敲错了
|