xmake 是一個基於 Lua 的輕量級跨平臺構建工具,使用 xmake.lua 維護專案構建,相比 makefile/CMakeLists.txt,配置語法更加簡潔直觀,對新手非常友好,短時間內就能快速入門,能夠讓使用者把更多的精力集中在實際的專案開發上。
這個版本,我們新增了很多新特性,不僅增加了對 Vala 和 Metal 語言的編譯支援,另外我們還改進了包依賴管理,能夠像 npm/package.lock 那樣支援對依賴包的鎖定和更新,使得使用者的專案不會受到上游包倉庫的更新變動影響。
此外,我們還提供了一些比較實用的規則, 比如 utils.bin2c
可以讓使用者方便快速的內嵌一些二進位制資原始檔到程式碼中去,以標頭檔案的方式獲取相關資料。
新特性介紹
新增 Vala 語言支援
這個版本,我們已經可以初步支援構建 Vala 程式,只需要應用 add_rules("vala")
規則。
同時,我們需要新增一些依賴包,其中 glib 包是必須的,因為 vala 自身也會用到它。
add_values("vala.packages")
用於告訴 valac,專案需要哪些包,它會引入相關包的 vala api,但是包的依賴整合,還是需要通過 add_requires("lua")
下載整合。
例如:
add_rules("mode.release", "mode.debug")
add_requires("lua", "glib")
target("test")
set_kind("binary")
add_rules("vala")
add_files("src/*.vala")
add_packages("lua", "glib")
add_values("vala.packages", "lua")
更多例子:Vala examples
新增包依賴鎖定支援
這個特性類似 npm 的 package.lock, cargo 的 cargo.lock。
比如,我們引用一些包,預設情況下,如果不指定版本,那麼 xmake 每次都會自動拉取最新版本的包來整合使用,例如:
add_requires("zlib")
但如果上游的包倉庫更新改動,比如 zlib 新增了一個 1.2.11 版本,或者安裝指令碼有了變動,都會導致使用者的依賴包發生改變。
這容易導致原本編譯通過的一些專案,由於依賴包的變動出現一些不穩定因素,有可能編譯失敗等等。
為了確保使用者的專案每次使用的包都是固定的,我們可以通過下面的配置去啟用包依賴鎖定。
set_policy("package.requires_lock", true)
這是一個全域性設定,必須設定到全域性根作用域,如果啟用後,xmake 執行完包拉取,就會自動生成一個 xmake-requires.lock
的配置檔案。
它包含了專案依賴的所有包,以及當前包的版本等資訊。
{
__meta__ = {
version = "1.0"
},
["macosx|x86_64"] = {
["cmake#31fecfc4"] = {
repo = {
branch = "master",
commit = "4498f11267de5112199152ab030ed139c985ad5a",
url = "https://github.com/xmake-io/xmake-repo.git"
},
version = "3.21.0"
},
["glfw#31fecfc4"] = {
repo = {
branch = "master",
commit = "eda7adee81bac151f87c507030cc0dd8ab299462",
url = "https://github.com/xmake-io/xmake-repo.git"
},
version = "3.3.4"
},
["opengl#31fecfc4"] = {
repo = {
branch = "master",
commit = "94d2eee1f466092e04c5cf1e4ecc8c8883c1d0eb",
url = "https://github.com/xmake-io/xmake-repo.git"
}
}
}
}
當然,我們也可以執行下面的命令,強制升級包到最新版本。
$ xmake require --upgrade
upgrading packages ..
zlib: 1.2.10 -> 1.2.11
1 package is upgraded!
option 支援程式碼片段的執行時檢測
option 本身有提供 add_csnippets/add_cxxsnippets
兩個介面,用於快速檢測特定一段 c/c++ 程式碼是否通過編譯,如果編譯通過就會啟用對應 option 選項。
但之前的版本僅僅只能提供編譯期檢測,而新版本中,我們還新增了執行時檢測支援。
我們可以通過設定 {tryrun = true}
和 {output = true}
兩個引數,用於嘗試執行檢測和捕獲輸出。
嘗試執行檢測
設定 tryrun 可以嘗試執行來檢測
option("test")
add_cxxsnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0 : -1;", {tryrun = true})
如果編譯執行通過,test 選項就會被啟用。
執行時檢測並捕獲輸出
設定 output 也會嘗試去檢測,並且額外捕獲執行的輸出內容。
option("test")
add_cxxsnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true})
如果編譯執行通過,test 選項就會被啟用,同時能獲取到對應的輸出內容作為 option 的值。
注:設定為捕獲輸出,當前 option 不能再設定其他 snippets
我們也可以通過 is_config
獲取繫結到option的輸出。
if is_config("test", "8") tben
-- xxx
end
另外,我們也對 includes("check_csnippets")
的輔助檢測介面,也做了改進來支援執行時檢測。
includes("check_csnippets.lua")
target("test")
set_kind("binary")
add_files("*.c")
add_configfiles("config.h.in")
check_csnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0 : -1;", {tryrun = true})
check_csnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true})
configvar_check_csnippets("HAS_LONG_8", "return (sizeof(long) == 8)? 0 : -1;", {tryrun = true})
configvar_check_csnippets("PTR_SIZE", 'printf("%d", sizeof(void*)); return 0;', {output = true, number = true})
如果啟用捕獲輸出,config.h.in
的 ${define PTR_SIZE}
會自動生成 #define PTR_SIZE 4
。
其中,number = true
設定,可以強制作為 number 而不是字串值,否則預設會定義為 #define PTR_SIZE "4"
快速內嵌二進位制資原始檔到程式碼
我們可以通過 utils.bin2c
規則,在專案中引入一些二進位制檔案,並見他們作為 c/c++ 標頭檔案的方式提供開發者使用,獲取這些檔案的資料。
比如,我們可以在專案中,內嵌一些 png/jpg 資原始檔到程式碼中。
target("console")
set_kind("binart")
add_rules("utils.bin2c", {extensions = {".png", ".jpg"}})
add_files("src/*.c")
add_files("res/*.png", "res/*.jpg")
注:extensions 的設定是可選的,預設字尾名是 .bin
然後,我們就可以通過 #include "filename.png.h"
的方式引入進來使用,xmake 會自動幫你生成對應的標頭檔案,並且新增對應的搜尋目錄。
static unsigned char g_png_data[] = {
#include "image.png.h"
};
int main(int argc, char** argv)
{
printf("image.png: %s, size: %d\n", g_png_data, sizeof(g_png_data));
return 0;
}
生成標頭檔案內容類似:
cat build/.gens/test/macosx/x86_64/release/rules/c++/bin2c/image.png.h
0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x78, 0x6D, 0x61, 0x6B, 0x65, 0x21, 0x0A, 0x00
新增 iOS/macOS 應用 Metal 編譯支援
我們知道 xcode.application
規則可以編譯 iOS/macOS 應用程式,生成 .app/.ipa 程式包,並同時完成簽名操作。
不過之前它不支援對帶有 .metal 程式碼的編譯,而新版本中,我們新增了 xcode.metal
規則,並預設關聯到 xcode.application
規則中去來預設支援 metal 編譯。
xmake 會自動編譯 .metal 然後打包生成 default.metallib 檔案,並且自動內建到 .app/.ipa 裡面。
如果使用者的 metal 是通過 [_device newDefaultLibrary]
來訪問的,那麼就能自動支援,就跟使用 xcode 編譯一樣。
這裡是我們提供的一個完整的:專案例子。
add_rules("mode.debug", "mode.release")
target("HelloTriangle")
add_rules("xcode.application")
add_includedirs("Renderer")
add_frameworks("MetalKit")
add_mflags("-fmodules")
add_files("Renderer/*.m", "Renderer/*.metal") ------- 新增 metal 檔案
if is_plat("macosx") then
add_files("Application/main.m")
add_files("Application/AAPLViewController.m")
add_files("Application/macOS/Info.plist")
add_files("Application/macOS/Base.lproj/*.storyboard")
add_defines("TARGET_MACOS")
add_frameworks("AppKit")
elseif is_plat("iphoneos") then
add_files("Application/*.m")
add_files("Application/iOS/Info.plist")
add_files("Application/iOS/Base.lproj/*.storyboard")
add_frameworks("UIKit")
add_defines("TARGET_IOS")
比如,在 macOS 上,編譯執行後,就會通過 metal 渲染出需要的效果。
如果,我們的專案沒有使用預設的 metal library,我們也可以通過上面提到的 utils.bin2c
規則,作為原始檔的方式內嵌到程式碼庫中,例如:
add_rules("utils.bin2c", {extensions = ".metal"})
add_files("Renderer/*.metal")
然後程式碼中,我們就能訪問了:
static unsigned char g_metal_data[] = {
#include "xxx.metal.h"
};
id<MTLLibrary> library = [_device newLibraryWithSource:[[NSString stringWithUTF8String:g_metal_data]] options:nil error:&error];
改進 add_repositories
如果我們通過內建在專案中的本地倉庫,我們之前是通過 add_repositories("myrepo repodir")
的方式來引入。
但是,它並不像 add_files()
那樣是基於當前 xmake.lua 檔案目錄的相對目錄,也沒有路徑的自動轉換,因此容易遇到找不到 repo 的問題。
因此,我麼你改進了下它,可以通過額外的 rootdir 引數指定對應的根目錄位置,比如相對當前 xmake.lua 的指令碼目錄。
add_repositories("myrepo repodir", {rootdir = os.scriptdir()})
os.cp 支援符號連結
之前的版本,os.cp
介面不能很好的處理符號連結的複製,他會自動展開連結,複製實際的檔案內容,只會導致複製後,符號連結丟失。
如果想要複製後,原樣保留符號連結,只需要設定下引數:{symlink = true}
os.cp("/xxx/symlink", "/xxx/dstlink", {symlink = true})
更方便地編譯自動生成的程式碼
有時候,我們會有這樣一個需求,在編譯前,自動生成一些原始檔參與後期的程式碼編譯。但是由於 add_files
新增的檔案在執行編譯時候,就已經確定,無法在編譯過程中動態新增它們(因為需要並行編譯)。
因此,要實現這個需求,我們通常需要自定義一個 rule,然後裡面主動呼叫編譯器模組去處理生成程式碼的編譯,物件檔案的注入,依賴更新等一系列問題。
這對於 xmake 開發者本身沒什麼大問題,但是對於使用者來說,這還是比較繁瑣了,不好上手。
新版本中,我們改進了對 add_files
的支援,並新增了 {always_added = true}
配置來告訴 xmake 我們始終需要新增指定的原始檔,即使它還不存在。
這樣我們就可以依靠xmake的預設編譯過程來編譯自動生成的程式碼了,像這樣:
add_rules("mode.debug", "mode.release")
target("autogen_code")
set_kind("binary")
add_files("$(buildir)/autogen.cpp", {always_added = true})
before_build(function (target)
io.writefile("$(buildir)/autogen.cpp", [[
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
cout << "hello world!" << endl;
return 0;
}
]])
end)
都不需要額外的 rule 定義,只需要保證編譯順序,在正確的階段生成程式碼檔案就可以了。
但是,我們也需要注意,由於當前自動生成的原始檔可能還不存在,我們不能在 add_files
裡面使用模式匹配,只能顯式新增每個原始檔路徑。
更新內容
新特性
- #1534: 新增對 Vala 語言的支援
- #1544: 新增 utils.bin2c 規則去自動從二進位制資原始檔產生 .h 標頭檔案並引入到 C/C++ 程式碼中
- #1547: option/snippets 支援執行檢測模式,並且可以獲取輸出
- #1567: 新增 xmake-requires.lock 包依賴鎖定支援
- #1597: 支援編譯 metal 檔案到 metallib,並改進 xcode.application 規則去生成內建的 default.metallib 到 app
改進
Bugs 修復
- #1531: 修復 targets 載入失敗的錯誤資訊提示錯誤