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
新特性介紹
快速切換臨時虛擬環境
Xmake 很早就支持了包的虛擬環境管理,可以通過配置文件的方式,實現不同包環境之間的切換。
我們可以通過在當前目錄下,添加 xmake.lua 文件,定製化一些包配置,然後進入特定的包虛擬環境。
add_requires("zlib 1.2.11")
add_requires("python 3.x", "luajit")
$ xrepo env shell
> python --version
> luajit --version
也可以通過導入自定義環境配置文件,來切換環境:
$ xrepo env --add /tmp/base.lua
$ xrepo env -b base shell
而在新版本中,我們進一步做了改進,讓 Xrepo 能夠直接在命令行臨時指定需要綁定的環境包列表,實現快速切換,無需任何配置。
並且支持同時指定多個包環境。
例如,我們想進入一個帶有 python 3.0, luajit 和 cmake 的環境,只需要執行:
$ xrepo env -b "python 3.x,luajit,cmake" shell
[python,luajit,cmake] $ python --version
Python 3.10.6
[python,luajit,cmake] $ cmake --version
cmake version 3.25.3
Xmake 會自動安裝相關依賴,然後開啓一個新的 shell 環境,新環境終端左邊也有 prompt 提示。
如果我們想退出當前環境,僅僅需要執行
[python,luajit,cmake] $ xrepo env quit
$
改進代碼特性檢測
has_cfuncs/check_cxxsnippets 等系列檢測接口,在 option 中已經有提供,並且有對應的輔助 API 來幫助檢測。
相關文檔可以參考:輔助檢測接口。
但是目前 option 提供的檢測接口僅僅針對全局平臺工具鏈,無法根據每個特定的 target 配置在針對性做一些檢測。
因爲 target 本身可能還會附帶依賴包,不同的工具鏈,編譯宏等差異性,檢測結果也會有一些差異。
因此,如果用戶想要更加靈活細粒度的檢測每個 target 目標的編譯特性,可以通過新版本提供的 target 目標實例接口。
- target:has_cfuncs
- target:has_cxxfuncs
- target:has_ctypes
- target:has_cxxtypes
- target:has_cincludes
- target:has_cxxincludes
- target:has_cflags
- target:has_cxxflags
- target:has_features
- target:check_csnippets
- target:check_cxxsnippets
這裏,僅僅針對其中一些比較常用的接口,稍微展開介紹下使用方式。
target:has_cfuncs
- 檢測目標編譯配置能否獲取給定的 C 函數
這應該在 on_config
中使用,比如可以用它來判斷當前目標能否獲取到 zlib 依賴包的一些函數接口,然後自動定義 HAVE_INFLATE
:
add_requires("zlib")
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib")
on_config(function (target)
if target:has_cfuncs("inflate", {includes = "zlib.h"}) then
target:add("defines", "HAVE_INFLATE")
end
end)
儘管 option 也提供了類似的檢測功能,但 option 的檢測使用的是全局的平臺工具鏈,它無法附帶上 target 相關的一些編譯配置,
也無法根據 target 設置不同編譯工具鏈來適配檢測,並且無法檢測包裏面的一些接口。
如果我們僅僅是想粗粒度的檢測函數接口,並且 target 沒有額外設置不同的工具鏈,那麼 option 提供的檢測功能已經足夠使用了。
如果想要更細粒度控制檢測,可以使用 target 實例接口提供的檢測特性。
target:has_cxxfuncs
- 檢測目標編譯配置能否獲取給定的 C++ 函數
用法跟 target:has_cfuncs 類似,只是這裏主要用於檢測 C++ 的函數。
不過,在檢測函數的同時,我們還可以額外配置 std languages,來輔助檢測。
target:has_cxxfuncs("foo", {includes = "foo.h", configs = {languages = "cxx17"}})
target:has_ctypes
- 檢測目標編譯配置能否獲取給定的 C 類型
這應該在 on_config
中使用,如下所示:
add_requires("zlib")
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib")
on_config(function (target)
if target:has_ctypes("z_stream", {includes = "zlib.h"}) then
target:add("defines", "HAVE_ZSTEAM_T")
end
end)
target:has_cflags
- 檢測目標編譯配置能否獲取給定的 C 編譯 flags
target("test")
set_kind("binary")
add_files("src/*.cpp")
on_config(function (target)
if target:has_cxxflags("-fPIC") then
target:add("defines", "HAS_PIC")
end
end)
target:has_cincludes
- 檢測目標編譯配置能否獲取給定的 C 頭文件
這應該在 on_config
中使用,比如可以用它來判斷當前目標能否獲取到 zlib 依賴包的 zlib.h 頭文件,然後自動定義 HAVE_INFLATE
:
add_requires("zlib")
target("test")
set_kind("binary")
add_files("src/*.c")
add_packages("zlib")
on_config(function (target)
if target:has_cincludes("zlib.h") then
target:add("defines", "HAVE_ZLIB_H")
end
end)
target:check_cxxsnippets
- 檢測是否可以編譯和鏈接給定的 C++ 代碼片段
這應該在 on_config
中使用,如下所示:
add_requires("libtins")
target("test")
set_kind("binary")
add_files("src/*.cpp")
add_packages("libtins")
on_config(function (target)
local has_snippet = target:check_cxxsnippets({test = [[
#include <string>
using namespace Tins;
void test() {
std::string name = NetworkInterface::default_interface().name();
printf("%s\n", name.c_str());
}
]]}, {configs = {languages = "c++11"}, includes = {"tins/tins.h"}}))
if has_snippet then
target:add("defines", "HAS_XXX")
end
end)
默認僅僅檢測編譯鏈接是否通過,如果想要嘗試運行時檢測,可以再設置 tryrun = true
。
target("test")
set_kind("binary")
add_files("src/*.cpp")
on_config(function (target)
local has_int_4 = target:check_cxxsnippets({test = [[
return (sizeof(int) == 4)? 0 : -1;
]]}, {configs = {languages = "c++11"}, tryrun = true}))
if has_int_4 then
target:add("defines", "HAS_INT4")
end
end)
我們也可以繼續通過設置 output = true
來捕獲檢測的運行輸出,並且加上自定義的 main
入口,實現完整的測試代碼,而不僅僅是代碼片段。
target("test")
set_kind("binary")
add_files("src/*.cpp")
on_config(function (target)
local int_size = target:check_cxxsnippets({test = [[
#include <stdio.h>
int main(int argc, char** argv) {
printf("%d", sizeof(int)); return 0;
return 0;
}
]]}, {configs = {languages = "c++11"}, tryrun = true, output = true}))
end)
target:has_features
- 檢測是否指定的 C/C++ 編譯特性
它相比使用 check_cxxsnippets
來檢測,會更加快一些,因爲它僅僅執行一次預處理就能檢測所有的編譯器特性,而不是每次都去調用編譯器嘗試編譯。
target("test")
set_kind("binary")
add_files("src/*.cpp")
on_config(function (target)
if target:has_features("c_static_assert") then
target:add("defines", "HAS_STATIC_ASSERT")
end
if target:has_features("cxx_constexpr") then
target:add("defines", "HAS_CXX_CONSTEXPR")
end
end)
優化編譯性能
Xmake 的 build cache 加速類似 ccache,採用預處理器計算 hash 後緩存編譯對象文件來實現加速,它在 linux/mac 上提速效果非常明顯。
而由於 msvc 的預處理器很慢,也可能是起進程相比 linux/mac 下更重,導致開啓 build cache 後,windows 上使用 msvc 的整體編譯效率反而慢了非常多。
嘗試使用第三方的 ccache 來測試對比,也是一樣的問題,因此我暫時針對 msvc 默認禁用了 build cache,使得整體構建速度恢復到正常水平。
clang-tidy 自動修復
上個版本,我們新增了對 clang-tidy 支持,可以通過 xmake check clang.tidy
來檢測代碼。
而在這個版本中,我們繼續對它做了改進,新增了 --fix
參數,可以讓 clang-tidy 去自動修復檢測出來的問題代碼。
$ xmake check clang.tidy --fix
$ xmake check clang.tidy --fix_errors
$ xmake check clang.tidy --fix_notes
Swig/Java 模塊構建支持
另外,其他用戶也幫忙貢獻了 Swig/Java 模塊的構建支持。
add_rules("mode.release", "mode.debug")
target("example")
set_kind('shared')
-- set moduletype to java
add_rules("swig.c", {moduletype = "java"})
-- use swigflags to provider package name and output path of java files
add_files("src/example.i", {swigflags = {
"-package",
"com.example",
"-outdir",
"build/java/com/example/"
}})
add_files("src/example.c")
before_build(function()
-- ensure output path exists before running swig
os.mkdir("build/java/com/example/")
end)
完整例子見:Swig/Java Example
開源之夏 2023
今年 Xmake 社區繼續參加了開源之夏 2023 活動,它是由中科院軟件所“開源軟件供應鏈點亮計劃”發起並長期支持的一項暑期開源活動
旨在鼓勵在校學生積極參與開源軟件的開發維護。
如果有感興趣的同學,歡迎報名參與 Xmake 社區發佈的項目開發(具體項目待定中),相關詳情進展,請關注:Xmake 開源之夏。
更新內容
新特性
改進
- #3433: 改進 QT 在 msys2/mingw64 和 wasm 上的構建支持
- #3419: 支持 fish shell 環境
- #3455: Dlang 增量編譯支持
- #3498: 改進綁定包虛擬環境
- #3504: 添加 swig java 支持
- #3508: 改進 trybuild/cmake 去支持工具鏈切換
- 爲 msvc 禁用 build cache 加速,因爲 msvc 的預處理器太慢,反而極大影響構建性能。