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 被收入到了 debian 倉庫,而最近 Xmake 又進入了 Fedora 官方倉庫,大家可以在 Fedora 39 上,直接通過下面的命令安裝 Xmake。
$ sudo dnf install xmake
非常感謝 @topazus @mochaaP 對 Xmake 的貢獻,相關信息見:#941。
接下來,我們來介紹下,新版本帶來的重量級特性:XPack。
它類似於 CMake 的 CPack 命令,可以將用戶工程快速打包生成各種格式的安裝包。
目前 Xmake 的 XPack 已經支持以下格式的打包:
- nsis: Windows 下的可執行安裝包
- runself: shell 自編譯安裝包
- targz: 二進制文件 tar.gz 包(綠色版)
- zip: 二進制文件 zip 包(綠色版)
- srctargz:源文件 tar.gz 包
- srczip: 源文件 zip 包
- srpm: rpm 源碼安裝包
- rpm: rpm 二進制安裝包
除了上述已經支持的打包格式,還有 deb 等包格式也在陸續支持中,並且用戶也可以配置生成自定義的包格式文件。
XPack 打包
下面是一個完整例子,我們可以先簡單看下:
set_version("1.0.0")
add_rules("mode.debug", "mode.release")
includes("@builtin/xpack")
target("test")
set_kind("binary")
add_files("src/*.cpp")
xpack("test")
set_formats("nsis", "zip", "targz", "runself")
set_title("hello")
set_author("ruki")
set_description("A test installer.")
set_homepage("https://xmake.io")
set_licensefile("LICENSE.md")
add_targets("test")
add_installfiles("src/(assets/*.png)", {prefixdir = "images"})
add_sourcefiles("(src/**)")
set_iconfile("src/assets/xmake.ico")
after_installcmd(function (package, batchcmds)
batchcmds:mkdir(package:installdir("resources"))
batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"})
batchcmds:mkdir(package:installdir("stub"))
end)
after_uninstallcmd(function (package, batchcmds)
batchcmds:rmdir(package:installdir("resources"))
batchcmds:rmdir(package:installdir("stub"))
end)
我們通過 includes("@builtin/xpack")
引入 xpack 的所有配置接口,包括 xpack 配置域,以及它的所有域接口。
然後我們執行:
$ xmake pack
即可生成所有安裝包。
生成 NSIS 安裝包
只要配置了 set_formats("nsis")
格式,然後執行 xmake pack
命令,就能生成 NSIS 格式的安裝包。
另外,xmake 還會自動安裝生成 NSIS 包所需的工具,實現真正的一鍵打包。
$ xmake pack
note: install or modify (m) these packages (pass -y to skip confirm)?
in xmake-repo:
-> nsis 3.09
please input: y (y/n/m)
=> install nsis 3.09 .. ok
[ 25%]: compiling.release src\main.cpp
[ 37%]: compiling.release src\main.cpp
[ 50%]: linking.release foo.dll
[ 62%]: linking.release test.exe
packing build\xpack\test\test-windows-x64-v1.0.0.exe
pack ok
test-windows-x64-v1.0.0.exe
就是我們生成的安裝包,雙擊運行它,就能安裝我們的二進制文件到指定目錄。
增加組件安裝
我們還可以給 NSIS 增加組件安裝命令,只有當用戶選擇指定組件的時候,它的安裝命令纔會被執行。
xpack("test")
add_components("LongPath")
xpack_component("LongPath")
set_default(false)
set_title("Enable Long Path")
set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).")
on_installcmd(function (component, batchcmds)
batchcmds:rawcmd("nsis", [[
${If} $NoAdmin == "false"
; Enable long path
WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
${EndIf}]])
end)
這個例子中,我們在裏面添加了一個 NSIS 特有的自定義命令,去實現對長路徑的支持。
生成自安裝包
我們也可以生成基於 shell 腳本的自編譯安裝包。我們需要配置 runself 打包格式,然後通過 add_sourcefiles
添加需要參與編譯安裝的源文件。
接着,我們需要自定義 on_installcmd 安裝腳本,裏面去配置如果編譯源碼包,我們可以簡單的調用一個內置的編譯安裝腳本文件,也可以直接配置 make install
等編譯安裝命令。
例如:
xpack("test")
set_formats("runself")
add_sourcefiles("(src/**)")
on_installcmd(function (package, batchcmds)
batchcmds:runv("make", {"install"})
end)
然後,我們執行 xmake pack
命令,就可以生成一個自安裝的 xxx.gz.run 包,默認採用 gzip 壓縮。
$ xmake pack
packing build/xpack/test/test-macosx-src-v1.0.0.gz.run
pack ok
我們可以使用 sh 去加載運行它來安裝我們的程序。
$ sh ./build/xpack/test/test-macosx-src-v1.0.0.gz.run
我們也可以看一個比較完整的例子:
xpack("xmakesrc")
set_formats("runself")
set_basename("xmake-v$(version)")
set_prefixdir("xmake-$(version)")
before_package(function (package)
import("devel.git")
local rootdir = path.join(os.tmpfile(package:basename()) .. ".dir", "repo")
if not os.isdir(rootdir) then
os.tryrm(rootdir)
os.cp(path.directory(os.projectdir()), rootdir)
git.clean({repodir = rootdir, force = true, all = true})
git.reset({repodir = rootdir, hard = true})
if os.isfile(path.join(rootdir, ".gitmodules")) then
git.submodule.clean({repodir = rootdir, force = true, all = true})
git.submodule.reset({repodir = rootdir, hard = true})
end
end
local extraconf = {rootdir = rootdir}
package:add("sourcefiles", path.join(rootdir, "core/**|src/pdcurses/**|src/luajit/**|src/tbox/tbox/src/demo/**"), extraconf)
package:add("sourcefiles", path.join(rootdir, "xmake/**"), extraconf)
package:add("sourcefiles", path.join(rootdir, "*.md"), extraconf)
package:add("sourcefiles", path.join(rootdir, "configure"), extraconf)
package:add("sourcefiles", path.join(rootdir, "scripts/*.sh"), extraconf)
package:add("sourcefiles", path.join(rootdir, "scripts/man/**"), extraconf)
package:add("sourcefiles", path.join(rootdir, "scripts/debian/**"), extraconf)
package:add("sourcefiles", path.join(rootdir, "scripts/msys/**"), extraconf)
end)
on_installcmd(function (package, batchcmds)
batchcmds:runv("./scripts/get.sh", {"__local__"})
end)
它是 xmake 自身源碼的安裝包配置腳本,更完整的配置可以參考:xpack.lua
這裏,它通過調用源碼包內置的 ./scripts/get.sh
安裝腳本去執行編譯安裝。
生成源碼歸檔包
另外,我們也可以配置 srczip
和 srctargz
格式,來生成源碼壓縮包,它不是完整的安裝包,也沒有安裝命令,僅僅用於源碼包分發。
xpack("test")
set_formats("srczip", "srctargz")
add_sourcefiles("(src/**)")
$ xmake pack
packing build/xpack/test/test-macosx-src-v1.0.0.zip ..
packing build/xpack/test/test-macosx-src-v1.0.0.tar.gz ..
pack ok
生成二進制歸檔包
我們也可以配置 zip
和 targz
來生成二進制的壓縮包,它會先自動編譯所有綁定的 target 目標程序,將所有需要的二進制程序,庫文件打包到 zip/tar.gz 格式。
這通常用於製作綠色版的安裝包,內部不太任何自動安裝腳本,用戶需要自己設置 PATH 等環境變量。
xpack("test")
set_formats("zip", "targz")
add_installfiles("(src/**)")
$ xmake pack
packing build/xpack/test/test-macosx-v1.0.0.zip ..
packing build/xpack/test/test-macosx-v1.0.0.tar.gz ..
pack ok
!> 需要注意的是,打二進制文件到包裏,使用的是 add_installfiles
而不是 add_sourcefiles
。
我們也可以通過 add_targets
去綁定需要安裝的 target 目標程序和庫。更多詳情見下面關於 add_targets
的接口描述。
生成 SRPM 源碼安裝包
它可以生成 .src.rpm
格式的源碼安裝包。
我們可以通過配置 add_targets 關聯需要構建的目標,在生成的 srpm 包中,它會自動調用 xmake build
和 xmake install
去構建和安裝包。
xpack("test")
set_homepage("https://xmake.io")
set_license("Apache-2.0")
set_description("A cross-platform build utility based on Lua.")
set_formats("srpm")
add_sourcefiles("(src/**)")
add_sourcefiles("./xmake.lua")
add_targets("demo")
它會生成類似下面的 spec 文件,然後自動調用 rpmbuild 去生成 .src.rpm
包。
Name: test
Version: 1.0.0
Release: 1%{?dist}
Summary: hello
License: Apache-2.0
URL: https://xmake.io
Source0: test-linux-src-v1.0.0.tar.gz
BuildRequires: xmake
BuildRequires: gcc
BuildRequires: gcc-c++
%description
A test installer.
%prep
%autosetup -n test-1.0.0 -p1
%build
xmake build -y test
%install
xmake install -o %{buildroot}/%{_exec_prefix} test
cd %{buildroot}
find . -type f | sed 's!^\./!/!' > %{_builddir}/_installedfiles.txt
%check
%files -f %{_builddir}/_installedfiles.txt
%changelog
* Fri Dec 22 2023 ruki - 1.0.0-1
- Update to 1.0.0
我們也可以通過 on_buildcmd
和 on_installcmd
自定義構建和安裝腳本。
xpack("test")
set_homepage("https://xmake.io")
set_license("Apache-2.0")
set_description("A cross-platform build utility based on Lua.")
set_formats("srpm")
add_sourcefiles("(src/**)")
add_sourcefiles("./configure")
on_buildcmd(function (package, batchcmds)
batchcmds:runv("./configure")
batchcmds:runv("make")
end)
on_installcmd(function (package, batchcmds)
batchcmds:runv("make", {"install", "PREFIX=%{buildroot}"})
end)
生成 RPM 二進制安裝包
RPM 包將會直接生成編譯好的二進制安裝包。xmake 會自動調用 rpmbuild --rebuild
命令去構建 SRPM 包生成它。
而在 XPack 中,我們僅僅只需要配置 set_formats("rpm")
即可支持 rpm 包生成,其他配置與 srpm 包完全一致。
xpack("test")
set_formats("rpm")
-- TODO
打包命令參數
指定打包格式
如果我們在配置文件中已經使用 set_formats
配置了多個打包格式,那麼默認情況下,xmake pack
會自動生成所有這些格式的包。
當然,我們也可以通過 xmake pack --formats=nsis,targz
來選擇性指定當前需要打哪些格式的包。
修改打包文件名
我們可以在配置文件中,通過 set_basename()
來修改包名,也可以通過命令行去修改它。
$ xmake pack --basename="foo"
packing build/xpack/test/foo.zip ..
pack ok
指定輸出目錄
默認的輸出目錄是在 build 目錄下,但我們也可以修改輸出的路徑。
$ xmake pack -o /tmp/output
禁用自動構建
如果是打 NSIS 等二進制包,xmake pack
會先自動編譯所有被綁定的 target 目標文件,然後再去執行打包邏輯。
但是如果我們已經編譯過了,不想每次都去編譯它,而是直接去打包,可以通過下面的參數禁用自動構建。
$ xmake pack --autobuild=n
接口描述
更多 XPack 打包接口描述見:XPack 打包接口文檔。
安裝包到本地
默認情況先,通過 add_requires("xxx")
配置的包都會被安裝到全局目錄,不同項目共用這些包。
而新版本中,我們新增了一個 package.install_locally
策略,可以配置讓 xmake 將包安裝到當前本地項目目錄。
set_policy("package.install_locally", true)
更新日誌
新特性
- 添加
network.mode
策略 - #1433: 添加
xmake pack
命令去生成 NSIS/zip/tar.gz/srpm/rpm/runself 安裝包 - #4435: 爲 UnityBuild 的組模式增加 batchsize 支持
- #4485: 新增 package.install_locally 策略支持
- 新增 NetBSD 支持
Changes
- #4484: 改進 swig 規則
- 改進 Haiku 支持