xmake 是一個基於 Lua 的輕量級跨平臺構建工具,使用 xmake.lua 維護專案構建,相比 makefile/CMakeLists.txt,配置語法更加簡潔直觀,對新手非常友好,短時間內就能快速入門,能夠讓使用者把更多的精力集中在實際的專案開發上。
這個版本,我們正式將預設的 Luajit 執行時切換到 Lua5.4 執行時,並且新增了 Rust 和 C++ 的混合編譯支援,我們也整合了 Cargo 的包管理支援。
另外,我們新增了一個實用的 utils.glsl2spv
規則,用於實現對 glsl shader 的編譯支援,並自動生成對應的 C 程式碼標頭檔案,方便快速內嵌編譯後的 .spv 檔案資料到程式碼中。
新特性介紹
預設切換到 Lua5.4 執行時
歷經幾個版本的迭代測試,我們在 2.6.1 版本,正式切換到 Lua5.4 執行時。
不過,這對於使用者來說是完全無感知的,基本上沒有任何相容性問題,因為 xmake 對大部分介面都是封裝過的,完全消除了 Lua 版本間的相容性問題。
對於構建效能方面,由於構建的效能瓶頸主要來自編譯器,Lua 自身的效能損耗完全可以忽略,而且 xmake 用 c 重寫了 lua 原生的所有 io 介面,並且對耗時的介面都用 c 實現了優化。
因此,通過對比測試,不管是使用 Lua 還是 Luajit,構建專案的耗時基本一致,沒有明顯差異。
為什麼要切換?
因為 Luajit 對一些新架構基本不支援,例如:riscv, lonngarch,而且 luajit 作者基本已經不怎麼維護它了,一些新架構支援和穩定性修復進展屬於停滯狀態。
為了能夠更好的支援更多的平臺,已經獲取更快的迭代維護,我們選擇使用 Lua 會帶來非常多的好處。
新增 Cargo 包依賴
我們在這個版本中,新增了 Cargo 包依賴管理器的支援,不過目前主要用於 Rust 專案。
例子: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cargo_deps
add_rules("mode.release", "mode.debug")
add_requires("cargo::base64 0.13.0")
add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}})
target("test")
set_kind("binary")
add_files("src/main.rs")
add_packages("cargo::base64", "cargo::flate2")
Rust 和 C++ 混合編譯
使用 cxxbridge 在 c++ 中呼叫 rust
add_rules("mode.debug", "mode.release")
add_requires("cargo::cxx 1.0")
target("foo")
set_kind("static")
add_files("src/foo.rs")
set_values("rust.cratetype", "staticlib")
add_packages("cargo::cxx")
target("test")
set_kind("binary")
add_rules("rust.cxxbridge")
add_deps("foo")
add_files("src/main.cc")
add_files("src/bridge.rsx")
foo.rs
#[cxx::bridge]
mod foo {
extern "Rust" {
fn add(a: i32, b: i32) -> i32;
}
}
pub fn add(a: i32, b: i32) -> i32 {
return a + b;
}
我們還需要在 c++ 專案中新增橋接檔案 bridge.rsx
#[cxx::bridge]
mod foo {
extern "Rust" {
fn add(a: i32, b: i32) -> i32;
}
}
main.cc
#include <stdio.h>
#include "bridge.rs.h"
int main(int argc, char** argv) {
printf("add(1, 2) == %d\n", add(1, 2));
return 0;
}
在 Rust 中呼叫 C++
add_rules("mode.debug", "mode.release")
target("foo")
set_kind("static")
add_files("src/foo.cc")
target("test")
set_kind("binary")
add_deps("foo")
add_files("src/main.rs")
main.rs
extern "C" {
fn add(a: i32, b: i32) -> i32;
}
fn main() {
unsafe {
println!("add(1, 2) = {}", add(1, 2));
}
}
foo.cc
extern "C" int add(int a, int b) {
return a + b;
}
新增 glsl shader 編譯規則
我們新增了一個 utils.glsl2spv
編譯規則,可以在專案中引入 *.vert/*.frag
等 glsl shader 檔案,然後實現自動編譯生成 *.spv
檔案。
另外,我們還支援以 C/C++ 標頭檔案的方式,二進位制內嵌 spv 檔案資料,方便程式使用。
編譯生成 spv 檔案
xmake 會自動呼叫 glslangValidator 或者 glslc 去編譯 shaders 生成 .spv 檔案,然後輸出到指定的 {outputdir = "build"}
目錄下。
add_rules("mode.debug", "mode.release")
add_requires("glslang", {configs = {binaryonly = true}})
target("test")
set_kind("binary")
add_rules("utils.glsl2spv", {outputdir = "build"})
add_files("src/*.c")
add_files("src/*.vert", "src/*.frag")
add_packages("glslang")
注,這裡的 add_packages("glslang")
主要用於引入和繫結 glslang 包中的 glslangValidator,確保 xmake 總歸能夠使用它。
當然,如果使用者自己系統上已經安裝了它,也可以不用額外繫結這個包,不過我還是建議新增一下。
編譯生成 c/c++ 標頭檔案
我們也可以內部藉助 bin2c 模組,將編譯後的 spv 檔案生成對應的二進位制標頭檔案,方便使用者程式碼中直接引入,我們只需要啟用 {bin2c = true}
。:w
add_rules("mode.debug", "mode.release")
add_requires("glslang", {configs = {binaryonly = true}})
target("test")
set_kind("binary")
add_rules("utils.glsl2spv", {bin2c = true})
add_files("src/*.c")
add_files("src/*.vert", "src/*.frag")
add_packages("glslang")
然後我們可以在程式碼這麼引入:
static unsigned char g_test_vert_spv_data[] = {
#include "test.vert.spv.h"
};
static unsigned char g_test_frag_spv_data[] = {
#include "test.frag.spv.h"
};
跟 bin2c 規則的使用方式類似,完整例子見:glsl2spv example
改進 C++ Modules 構建
上個版本,我們重構了 C++20 Modules 構建支援,而在這個版本中,我們繼續對它做了改進。
對於 msvc 編譯器,我們已經能夠在模組中匯入 std 標準庫模組,另外,我們修復了多個 target 之間存在依賴時,模組匯入編譯失敗的問題。
改進 MDK 程式構建配置
上個版本,我們新增了 MDK 程式的構建支援,需要注意的是,目前一些 mdk 程式都使用了 microlib 庫執行時,它需要編譯器加上 __MICROLIB
巨集定義,連結器加上 --library_type=microlib
等各種配置。
而在這個版本中,我們可以通過 set_runtimes("microlib")
直接設定到 microlib 執行時庫,可以自動設定上所有相關選項。
控制檯程式
target("hello")
add_deps("foo")
add_rules("mdk.console")
add_files("src/*.c", "src/*.s")
add_includedirs("src/lib/cmsis")
set_runtimes("microlib")
靜態庫程式
add_rules("mode.debug", "mode.release")
target("foo")
add_rules("mdk.static")
add_files("src/foo/*.c")
set_runtimes("microlib")
改進 OpenMP 專案配置
我們也改進了 openmp 專案的配置,更加簡化和統一,我們不再需要額外配置 rules,僅僅通過一個通用的 openmp 包就可以實現相同的效果。
add_requires("openmp")
target("loop")
set_kind("binary")
add_files("src/*.cpp")
add_packages("openmp")
在之前的版本,我們需要這麼配置,對比一下,就能看出新的配置更加的簡潔。
add_requires("libomp", {optional = true})
target("loop")
set_kind("binary")
add_files("src/*.cpp")
add_rules("c++.openmp")
add_packages("libomp")
更新內容
新特性
- #1799: 支援混合 Rust 和 C++ 程式,以及整合 Cargo 依賴庫
- 新增
utils.glsl2spv
規則去編譯 .vert/.frag shader 檔案生成 spirv 檔案和二進位制 C 標頭檔案
改進
- 預設切換到 Lua5.4 執行時
- #1776: 改進 system::find_package,支援從環境變數中查詢系統庫
- #1786: 改進 apt:find_package,支援查詢 alias 包
- #1819: 新增預編譯頭到 cmake 生成器
- 改進 C++20 Modules 為 msvc 支援 std 標準庫
- #1792: 新增自定義命令到 vs 工程生成器
- #1835: 改進 MDK 程式構建支援,增加
set_runtimes("microlib")
- #1858: 改進構建 c++20 modules,修復跨 target 構建問題
- 新增 $XMAKE_BINARY_REPO 和 $XMAKE_MAIN_REPO 倉庫設定環境變數
- #1865: 改進 openmp 工程
- #1845: 為靜態庫安裝 pdb 檔案