Xmake v2.7.1 發佈,更好的 C++ Modules 支持

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 插件,可以實時監控當前工程文件更新,自動觸發增量構建,或者運行一些自定義的命令。

C++ Modules 改進

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

但是之前的實現還存在很多不足之處:

  1. 不支持 HeaderUnits,因此也無法使用 stl 等模塊
  2. 自己掃描源碼實現模塊依賴圖解析,不支持編譯器提供的依賴掃描,因此不完全可靠
  3. 不支持 CMakelists 生成
  4. 不支持 compile_commands.json 生成

而在新版中,我們對 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 腳本

我們還可以監視文件更新後,運行指定的 lua 腳本,實現更加靈活複雜的命令定製。

$ xmake watch -s /tmp/test.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 上運行起來,而無需做任何改動。

$ 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 包查找

指定鏈接

對於 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)

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 加載路徑
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章