xmake v2.5.8 發佈,新增 Pascal/Swig 程序和 Lua53 運行時支持

xmake 是一個基於 Lua 的輕量級跨平台構建工具,使用 xmake.lua 維護項目構建,相比 makefile/CMakeLists.txt,配置語法更加簡潔直觀,對新手非常友好,短時間內就能快速入門,能夠讓用戶把更多的精力集中在實際的項目開發上。

這個版本,我們主要增加了對 Pascal 語言項目和 Swig 模塊的構建支持,而對於上個版本新增的 Vala 語言支持,我們也做了進一步改進,增加了對動態庫和靜態庫的構建支持。

除此之外,xmake 現在也已經支持了可選的 Lua5.3 運行時,提供更好的跨平台支持能力,目前 xmake 已經能夠在 LoongArch 架構上正常運行。

新特性介紹

Pascal 語言支持

目前,我們可以使用跨平台的 Free pascal 工具鏈 fpc 去編譯構建 Pascal 程序,例如:

控制台程序

add_rules("mode.debug", "mode.release")
target("test")
    set_kind("binary")
    add_files("src/*.pas")

動態庫程序

add_rules("mode.debug", "mode.release")
target("foo")
    set_kind("shared")
    add_files("src/foo.pas")

target("test")
    set_kind("binary")
    add_deps("foo")
    add_files("src/main.pas")

我們也可以通過 add_fcflags() 接口添加 Pascal 代碼相關的編譯選項。

更多例子見:Pascal examples

Vala 庫編譯支持

上個版本,我們新增了對 Vala 語言的支持,但是之前只能支持控制台程序的編譯,無法生成庫文件。而這個版本中,我們額外增加了對靜態庫和動態庫的編譯支持。

靜態庫程序

我們能夠通過 add_values("vala.header", "mymath.h") 設置導出的接口頭文件名,通過 add_values("vala.vapi", "mymath-1.0.vapi") 設置導出的 vapi 文件名。

add_rules("mode.release", "mode.debug")

add_requires("glib")

target("mymath")
    set_kind("static")
    add_rules("vala")
    add_files("src/mymath.vala")
    add_values("vala.header", "mymath.h")
    add_values("vala.vapi", "mymath-1.0.vapi")
    add_packages("glib")

target("test")
    set_kind("binary")
    add_deps("mymath")
    add_rules("vala")
    add_files("src/main.vala")
    add_packages("glib")

動態庫程序

add_rules("mode.release", "mode.debug")

add_requires("glib")

target("mymath")
    set_kind("shared")
    add_rules("vala")
    add_files("src/mymath.vala")
    add_values("vala.header", "mymath.h")
    add_values("vala.vapi", "mymath-1.0.vapi")
    add_packages("glib")

target("test")
    set_kind("binary")
    add_deps("mymath")
    add_rules("vala")
    add_files("src/main.vala")
    add_packages("glib")

更多例子見:Vala examples

Swig 模塊支持

我們提供了 swig.cswig.cpp 規則,可以對指定的腳本語言,調用 swig 程序生成 c/c++ 模塊接口代碼,然後配合 xmake 的包管理系統實現完全自動化的模塊和依賴包整合。

相關 issues: #1622

Lua/C 模塊

add_rules("mode.release", "mode.debug")
add_requires("lua")

target("example")
    add_rules("swig.c", {moduletype = "lua"})
    add_files("src/example.i", {swigflags = "-no-old-metatable-bindings"})
    add_files("src/example.c")
    add_packages("lua")

其中,swigflags 可以設置傳遞一些 swig 特有的 flags 選項。

Python/C 模塊

add_rules("mode.release", "mode.debug")
add_requires("python 3.x")

target("example")
    add_rules("swig.c", {moduletype = "python"})
    add_files("src/example.i", {scriptdir = "share"})
    add_files("src/example.c")
    add_packages("python")

如果設置了 scriptdir,那麼我們執行安裝的時候,會將對應模塊的 python wrap 腳本安裝到指定目錄。

Python/C++ 模塊

add_rules("mode.release", "mode.debug")
add_requires("python 3.x")

target("example")
    add_rules("swig.cpp", {moduletype = "python"})
    add_files("src/example.i", {scriptdir = "share"})
    add_files("src/example.cpp")
    add_packages("python")

Lua5.3 運行時支持

xmake 之前一直使用的 Luajit 作為默認的運行時,因為當初考慮到 Luajit 相對更加快速,並且固定的 lua 5.1 語法更加適合 xmake 內部實現的需要。

但是考慮到 Luajit 的更新不給力,作者維護不是很積極,並且它的跨平台性比較差,對於一些新出的架構,比如:Loongarch,riscv 等支持不及時,這多少限制了 xmake 的平台支持力度。

為此,新版本中,我們也將 Lua5.3 作為可選的運行時內置了進來,我們只需要通過下面的命令編譯安裝 xmake,就可以從 Luajit 切換到 Lua5.3 運行時:

Linux/macOS

$ make RUNTIME=lua

Windows

$ cd core
$ xmake f --runtime=lua
$ xmake

目前,當前版本還是默認採用的 luajit 運行時,用戶可以根據自己的需求切換到 Lua5.3 運行時,但這對於用戶的項目 xmake.lua 配置腳本幾乎沒有任何兼容性影響。

因為 xmake 的配置接口都已經做了一層的抽象封裝,一些 Luajit/Lua5.3 存在兼容性差異的原生接口是不會開放給用戶使用的,所以對項目構建來說,是完全無感知的。

唯一的區別就是,帶有 Lua5.3 的 xmake 支持更多的平台和架構。

性能對比

我做過一些基礎構建測試,不管是啟動時間,構建性能還是內存佔用,Lua5.3 和 Luajit 的 xmake 都幾乎沒有任何差別。因為對於構建系統,主要的性能瓶頸是在編譯器上,自身腳本的損耗佔比是非常小的。

而且 xmake 內部的一些底層 Lua 模塊,比如 io,字符編碼,字符串操作等,都自己用 c 代碼全部重寫過的,完全不依賴特定的 Lua 運行時引擎。

是否會考慮默認切換到 Lua?

由於我們剛剛支持 Lua5.3,儘管目前測試下來已經比較穩定,但是為了確保用戶環境不受到任何影響,我們還需要再觀察一段時間,短期還是默認使用 Luajit。

等到 2.6.1 版本開始,我們會全面開始切換到 Lua5.3 作為默認的運行時環境,大家有興趣的話,也可以線幫忙測試下,如果遇到問題,歡迎到 issues 上反饋。

LoongArch 架構支持

由於我們增加了 Lua5.3 運行時支持,所以現在我們已經可以支持再 LoongArch 架構上運行 xmake,並且所有測試例子都已經測試通過。

Lua 5.4

目前,我們對 Lua 5.4 還保持觀望狀態,如果後面等 lua5.4 穩定了,我們也會嘗試考慮繼續升級到 Lua5.4。

第三方源碼混合編譯支持

集成 CMake 代碼庫

新版本中,我們已經能夠通過 xmake 的包模式直接集成自己項目中帶有 CMakeLists.txt 的代碼庫,而不是通過遠程下載安裝。

相關 issues: #1714

例如,我們有如下項目結構:

├── foo
│   ├── CMakeLists.txt
│   └── src
│       ├── foo.c
│       └── foo.h
├── src
│   └── main.c
├── test.lua
└── xmake.lua

foo 目錄下是一個使用 cmake 維護的靜態庫,而根目錄下使用了 xmake 來維護,我們可以在 xmake.lua 中通過定義 package("foo") 包來描述如何構建 foo 代碼庫。

add_rules("mode.debug", "mode.release")

package("foo")
    add_deps("cmake")
    set_sourcedir(path.join(os.scriptdir(), "foo"))
    on_install(function (package)
        local configs = {}
        table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release"))
        table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF"))
        import("package.tools.cmake").install(package, configs)
    end)
    on_test(function (package)
        assert(package:has_cfuncs("add", {includes = "foo.h"}))
    end)
package_end()

add_requires("foo")

target("demo")
    set_kind("binary")
    add_files("src/main.c")
    add_packages("foo")

其中,我們通過 set_sourcedir() 來設置 foo 包的代碼目錄位置,然後通過 import 導入 package.tools.cmake 輔助模塊來調用 cmake 構建代碼,xmake 會自動獲取生成的 libfoo.a 和對應的頭文件。

!> 如果僅僅本地源碼集成,我們不需要額外設置 add_urlsadd_versions

關於包的配置描述,詳情見:包描述說明

定義完包後,我們就可以通過 add_requires("foo")add_packages("foo") 來集成使用它了,就跟集成遠程包一樣的使用方式。

另外,on_test 是可選的,如果想要嚴格檢測包的編譯安裝是否成功,可以在裏面做一些測試。

完整例子見:Library with CMakeLists

集成 autoconf 代碼庫

我們也可以使用 package.tools.autoconf 來本地集成帶有 autoconf 維護的第三方代碼庫。

package("pcre2")

    set_sourcedir(path.join(os.scriptdir(), "3rd/pcre2"))

    add_configs("jit", {description = "Enable jit.", default = true, type = "boolean"})
    add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}})

    on_load(function (package)
        local bitwidth = package:config("bitwidth") or "8"
        package:add("links", "pcre2-" .. bitwidth)
        package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth)
        if not package:config("shared") then
            package:add("defines", "PCRE2_STATIC")
        end
    end)

    on_install("macosx", "linux", "mingw", function (package)
        local configs = {}
        table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no"))
        table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes"))
        if package:debug() then
            table.insert(configs, "--enable-debug")
        end
        if package:config("pic") ~= false then
            table.insert(configs, "--with-pic")
        end
        if package:config("jit") then
            table.insert(configs, "--enable-jit")
        end
        local bitwidth = package:config("bitwidth") or "8"
        if bitwidth ~= "8" then
            table.insert(configs, "--disable-pcre2-8")
            table.insert(configs, "--enable-pcre2-" .. bitwidth)
        end
        import("package.tools.autoconf").install(package, configs)
    end)

    on_test(function (package)
        assert(package:has_cfuncs("pcre2_compile", {includes = "pcre2.h"}))
    end)

package.tools.autoconfpackage.tools.cmake 模塊都是可以支持 mingw/cross/iphoneos/android 等交叉編譯平台和工具鏈的,xmake 會自動傳遞對應的工具鏈進去,用戶不需要做任何其他事情。

集成其他構建系統

我們還支持集成 Meson/Scons/Make 等其他構建系統維護的代碼庫,僅僅只需要導入對應的構建輔助模塊,這裡就不一一細講了,我們可以進一步查閱文檔:集成本地第三方源碼庫

改進編譯器特性檢測

在之前的版本中,我們可以通過 check_features 輔助接口來檢測指定的編譯器特性,比如:

includes("check_features.lua")

target("test")
    set_kind("binary")
    add_files("*.c")
    add_configfiles("config.h.in")
    configvar_check_features("HAS_CONSTEXPR", "cxx_constexpr")
    configvar_check_features("HAS_CONSEXPR_AND_STATIC_ASSERT", {"cxx_constexpr", "c_static_assert"}, {languages = "c++11"})

config.h.in

${define HAS_CONSTEXPR}
${define HAS_CONSEXPR_AND_STATIC_ASSERT}

config.h

/* #undef HAS_CONSTEXPR */
#define HAS_CONSEXPR_AND_STATIC_ASSERT 1

如果當前 cxx_constexpr 特性支持,就會在 config.h 中啟用 HAS_CONSTEXPR 宏。

新增 C/C++ 標準支持檢測

2.5.8 之後,我們繼續新增了對 cstd 和 c++ std 版本檢測支持,相關 issues: #1715

例如:

configvar_check_features("HAS_CXX_STD_98", "cxx_std_98")
configvar_check_features("HAS_CXX_STD_11", "cxx_std_11", {languages = "c++11"})
configvar_check_features("HAS_CXX_STD_14", "cxx_std_14", {languages = "c++14"})
configvar_check_features("HAS_CXX_STD_17", "cxx_std_17", {languages = "c++17"})
configvar_check_features("HAS_CXX_STD_20", "cxx_std_20", {languages = "c++20"})
configvar_check_features("HAS_C_STD_89", "c_std_89")
configvar_check_features("HAS_C_STD_99", "c_std_99")
configvar_check_features("HAS_C_STD_11", "c_std_11", {languages = "c11"})
configvar_check_features("HAS_C_STD_17", "c_std_17", {languages = "c17"})

新增編譯器內置宏檢測

我們還能檢測編譯器存在一些內置的宏定義,比如:__GNUC__ 等,我們可以通過 check_macrosconfigvar_check_macros 輔助腳本來檢測它們是否存在。

相關 issues: #1715

-- 檢測宏是否定義
configvar_check_macros("HAS_GCC", "__GNUC__")
-- 檢測宏沒有被定義
configvar_check_macros("NO_GCC", "__GNUC__", {defined = false})
-- 檢測宏條件
configvar_check_macros("HAS_CXX20", "__cplusplus >= 202002L", {languages = "c++20"})

增加對 Qt 4.x 的支持

除了 Qt 5.x 和 6.x,我們對於一些基於 Qt 4.x 的老項目,xmake 也增加了支持。

增加對 Android NDK r23 的支持

由於 google 對 Android NDK 的一些結構改動,導致 r23 影響了 xmake 對 android 項目部分編譯特性的支持,在這個版本中,我們也做了修復。

修復 vsxmake 插件 Unicode 編碼問題

另外,如果基於 Unicode 作為項目目錄,那麼生成的 vsxmake 項目會收到影響,導致 vs 項目編譯和訪問上存在很多問題,我們也在新版本中做了修復。

更新內容

新特性

  • #388: Pascal 語言支持,可以使用 fpc 來編譯 free pascal
  • #1682: 添加可選的額lua5.3 運行時替代 luajit,提供更好的平台兼容性。
  • #1622: 支持 Swig
  • #1714: 支持內置 cmake 等第三方項目的混合編譯
  • #1715: 支持探測編譯器語言標準特性,並且新增 check_macros 檢測接口
  • xmake 支持在 Loongarch 架構上運行

改進

  • #1618: 改進 vala 支持構建動態庫和靜態庫程序
  • 改進 Qt 規則去支持 Qt 4.x
  • 改進 set_symbols("debug") 支持 clang/windows 生成 pdb 文件
  • #1638: 改進合併靜態庫
  • 改進 on_load/after_load 去支持動態的添加 target deps
  • #1675: 針對 mingw 平台,重命名動態庫和導入庫文件名後綴
  • #1694: 支持在 set_configvar 中定義一個不帶引號的字符串變量
  • 改進對 Android NDK r23 的支持
  • set_languages 新增 c++latestclatest 配置值
  • #1720: 添加 save_scoperestore_scope 去修復 check_xxx 相關接口
  • #1726: 改進 compile_commands 生成器去支持 nvcc

Bugs 修復

  • #1671: 修復安裝預編譯包後,*.cmake 裏面的一些不正確的絕對路徑
  • #1689: 修復 vsxmake 插件的 unicode 字符顯示和加載問題