xmake v2.5.7 發布,包依賴鎖定和 Vala/Metal 語言編譯支持


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

改進

  • #1540: 更好更方便地編譯自動生成的代碼
  • #1578: 改進 add_repositories 去更好地支持相對路徑
  • #1582: 改進安裝和 os.cp 支持符號鏈接

Bugs 修復

  • #1531: 修復 targets 加載失敗的錯誤信息提示錯誤


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM