--- title: Xmake v2.8.6 发布,新的打包插件:XPack tags: [xmake, lua, C/C++] date: 2023-12-15 author: Ruki outline: deep --- ## 新特性介绍 在介绍新特性之前,还有个好消息告诉大家,上个版本 Xmake 被收入到了 debian 仓库,而最近 Xmake 又进入了 Fedora 官方仓库,大家可以在 Fedora 39 上,直接通过下面的命令安装 Xmake。 ```bash $ sudo dnf install xmake ``` 非常感谢 @topazus @mochaaP 对 Xmake 的贡献,相关信息见:[#941](https://github.com/xmake-io/xmake/issues/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 打包 下面是一个完整例子,我们可以先简单看下: ```lua 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 配置域,以及它的所有域接口。 然后我们执行: ```bash $ xmake pack ``` 即可生成所有安装包。 ### 生成 NSIS 安装包 只要配置了 `set_formats("nsis")` 格式,然后执行 `xmake pack` 命令,就能生成 NSIS 格式的安装包。 另外,xmake 还会自动安装生成 NSIS 包所需的工具,实现真正的一键打包。 ```bash $ 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` 就是我们生成的安装包,双击运行它,就能安装我们的二进制文件到指定目录。 ![](/assets/img/manual/nsis_1.png) ![](/assets/img/manual/nsis_2.png) ![](/assets/img/manual/nsis_3.png) #### 增加组件安装 我们还可以给 NSIS 增加组件安装命令,只有当用户选择指定组件的时候,它的安装命令才会被执行。 ```lua 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 特有的自定义命令,去实现对长路径的支持。 ![](/assets/img/manual/nsis_4.png) ### 生成自安装包 我们也可以生成基于 shell 脚本的自编译安装包。我们需要配置 runself 打包格式,然后通过 `add_sourcefiles` 添加需要参与编译安装的源文件。 接着,我们需要自定义 on_installcmd 安装脚本,里面去配置如果编译源码包,我们可以简单的调用一个内置的编译安装脚本文件,也可以直接配置 `make install` 等编译安装命令。 例如: ```lua xpack("test") set_formats("runself") add_sourcefiles("(src/**)") on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install"}) end) ``` 然后,我们执行 `xmake pack` 命令,就可以生成一个自安装的 xxx.gz.run 包,默认采用 gzip 压缩。 ```bash $ xmake pack packing build/xpack/test/test-macosx-src-v1.0.0.gz.run pack ok ``` 我们可以使用 sh 去加载运行它来安装我们的程序。 ```bash $ sh ./build/xpack/test/test-macosx-src-v1.0.0.gz.run ``` 我们也可以看一个比较完整的例子: ```lua 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](https://github.com/xmake-io/xmake/blob/master/core/xpack.lua) 这里,它通过调用源码包内置的 `./scripts/get.sh` 安装脚本去执行编译安装。 ### 生成源码归档包 另外,我们也可以配置 `srczip` 和 `srctargz` 格式,来生成源码压缩包,它不是完整的安装包,也没有安装命令,仅仅用于源码包分发。 ```lua xpack("test") set_formats("srczip", "srctargz") add_sourcefiles("(src/**)") ``` ```bash $ 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 等环境变量。 ```lua xpack("test") set_formats("zip", "targz") add_installfiles("(src/**)") ``` ```bash $ 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` 去构建和安装包。 ```lua 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` 自定义构建和安装脚本。 ```lua 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 包完全一致。 ```lua xpack("test") set_formats("rpm") -- TODO ``` ### 打包命令参数 #### 指定打包格式 如果我们在配置文件中已经使用 `set_formats` 配置了多个打包格式,那么默认情况下,`xmake pack` 会自动生成所有这些格式的包。 当然,我们也可以通过 `xmake pack --formats=nsis,targz` 来选择性指定当前需要打哪些格式的包。 #### 修改打包文件名 我们可以在配置文件中,通过 `set_basename()` 来修改包名,也可以通过命令行去修改它。 ```bash $ xmake pack --basename="foo" packing build/xpack/test/foo.zip .. pack ok ``` #### 指定输出目录 默认的输出目录是在 build 目录下,但我们也可以修改输出的路径。 ```bash $ xmake pack -o /tmp/output ``` #### 禁用自动构建 如果是打 NSIS 等二进制包,`xmake pack` 会先自动编译所有被绑定的 target 目标文件,然后再去执行打包逻辑。 但是如果我们已经编译过了,不想每次都去编译它,而是直接去打包,可以通过下面的参数禁用自动构建。 ```bash $ xmake pack --autobuild=n ``` ### 接口描述 更多 XPack 打包接口描述见:[XPack 打包接口文档](https://xmake.io/zh/)。 ### 安装包到本地 默认情况先,通过 `add_requires("xxx")` 配置的包都会被安装到全局目录,不同项目共用这些包。 而新版本中,我们新增了一个 `package.install_locally` 策略,可以配置让 xmake 将包安装到当前本地项目目录。 ```lua set_policy("package.install_locally", true) ``` ## 更新日志 ### 新特性 * 添加 `network.mode` 策略 * [#1433](https://github.com/xmake-io/xmake/issues/1433): 添加 `xmake pack` 命令去生成 NSIS/zip/tar.gz/srpm/rpm/runself 安装包 * [#4435](https://github.com/xmake-io/xmake/issues/4435): 为 UnityBuild 的组模式增加 batchsize 支持 * [#4485](https://github.com/xmake-io/xmake/pull/4485): 新增 package.install_locally 策略支持 * 新增 NetBSD 支持 ### Changes * [#4484](https://github.com/xmake-io/xmake/pull/4484): 改进 swig 规则 * 改进 Haiku 支持 ### Bugs 修复 * [#4372](https://github.com/xmake-io/xmake/issues/4372): 修复 protobuf 规则 * [#4439](https://github.com/xmake-io/xmake/issues/4439): 修复 asn1c 规则