---
title: Xmake v2.8.6 released, New Packaging Plugin, XPack
tags: [xmake, lua, C/C++, package, performance, API, rust]
date: 2023-12-15
author: Ruki
outline: deep
---

## Introduction of new features

Before introducing new features, there is good news to tell you that the previous version of Xmake was included in the debian repository, and recently Xmake has entered the Fedora official repository. You can install Xmake directly on Fedora 39 through the following command.

```bash
$ sudo dnf install xmake
```

Many thanks to @topazus @mochaaP for their contribution to Xmake. For related information, see: [#941](https://github.com/xmake-io/xmake/issues/941).

Next, let’s introduce the heavyweight feature brought by the new version: XPack.

It is similar to CMake's CPack command, which can quickly package user projects to generate installation packages in various formats.

Currently Xmake's XPack already supports packaging in the following formats:

- nsis: executable installation package under Windows
- runself: shell self-compiled installation package
- targz: binary file tar.gz package (green version)
- zip: Binary file zip package (green version)
- srctargz: source file tar.gz package
- srczip: source file zip package
- srpm: rpm source code installation package
- rpm: rpm binary installation package

In addition to the above-mentioned supported packaging formats, package formats such as deb are also being gradually supported, and users can also configure and generate custom package format files.







### XPack packaging

Here is a complete example, we can take a brief look at it first:

```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)
```

We introduce all configuration interfaces of xpack through `includes("@builtin/xpack")`, including the xpack configuration domain and all its domain interfaces.

Then we execute:

```bash
$xmakepack
```

All installation packages will be generated.

### Generate NSIS installation package

As long as you configure the `set_formats("nsis")` format and then execute the `xmake pack` command, you can generate an installation package in NSIS format.

In addition, xmake will also automatically install the tools required to generate NSIS packages to achieve true one-click packaging.

```bash
$xmakepack
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` is the installation package we generated. Double-click to run it to install our binary files to the specified directory.

![](/assets/img/manual/nsis_1.png)
![](/assets/img/manual/nsis_2.png)
![](/assets/img/manual/nsis_3.png)

#### Add component installation

We can also add component installation commands to NSIS. Only when the user selects the specified component, its installation command will be executed.

```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)
```

In this example, we added an NSIS-specific custom command to support long paths.

![](/assets/img/manual/nsis_4.png)

### Generate self-installation package

We can also generate self-compiled installation packages based on shell scripts. We need to configure the runself packaging format, and then add the source files that need to participate in compilation and installation through `add_sourcefiles`.

Next, we need to customize the on_installcmd installation script to configure if we compile the source code package, we can simply call a built-in compilation and installation script file, or directly configure compilation and installation commands such as `make install`.

For example:

```lua
xpack("test")
     set_formats("runself")
     add_sourcefiles("(src/**)")
     on_installcmd(function (package, batchcmds)
         batchcmds:runv("make", {"install"})
     end)
```

Then, we execute the `xmake pack` command to generate a self-installed xxx.gz.run package, which uses gzip compression by default.

```bash
$xmakepack
packing build/xpack/test/test-macosx-src-v1.0.0.gz.run
pack ok
```

We can use sh to load and run it to install our program.

```bash
$ sh ./build/xpack/test/test-macosx-src-v1.0.0.gz.run
```

We can also look at a more complete example:

```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)
```

It is the installation package configuration script of xmake's own source code. For more complete configuration, please refer to: [xpack.lua](https://github.com/xmake-io/xmake/blob/master/core/xpack.lua)

Here, it performs compilation and installation by calling the `./scripts/get.sh` installation script built into the source package.

### Generate source code archive package

In addition, we can also configure the `srczip` and `srctargz` formats to generate source code compression packages. It is not a complete installation package and has no installation commands. It is only used for source code package distribution.

```lua
xpack("test")
     set_formats("srczip", "srctargz")
     add_sourcefiles("(src/**)")
```

```bash
$xmakepack
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
```

### Generate binary archive package

We can also configure `zip` and `targz` to generate binary compressed packages. It will automatically compile all bound target target programs and package all required binary programs and library files into zip/tar.gz format.

This is usually used to create a green version of the installation package. There is no automatic installation script inside. Users need to set environment variables such as PATH themselves.

```lua
xpack("test")
     set_formats("zip", "targz")
     add_installfiles("(src/**)")
```

```bash
$xmakepack
packing build/xpack/test/test-macosx-v1.0.0.zip ..
packing build/xpack/test/test-macosx-v1.0.0.tar.gz ..
pack ok
```

:::NOTE
It should be noted that to add binary files to the package, use `add_installfiles` instead of `add_sourcefiles`.
:::

We can also use `add_targets` to bind the target target programs and libraries that need to be installed. See the interface description for `add_targets` below for more details.

### Generate SRPM source code installation package

It can generate source code installation packages in `.src.rpm` format.

We can configure add_targets to associate the targets that need to be built. In the generated srpm package, it will automatically call `xmake build` and `xmake install` to build and install the package.

```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")
```

It will generate a spec file similar to the following, and then automatically call rpmbuild to generate the `.src.rpm` package.

```
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
```

We can also customize build and installation scripts through `on_buildcmd` and `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)
```

### Generate RPM binary installation package

The RPM package will directly generate a compiled binary installation package. xmake will automatically call the `rpmbuild --rebuild` command to build the SRPM package and generate it.

In XPack, we only need to configure `set_formats("rpm")` to support rpm package generation, and other configurations are exactly the same as srpm packages.

```lua
xpack("test")
     set_formats("rpm")
     -- TODO
```

### Packaging command parameters

#### Specify packaging format

If we have configured multiple packaging formats using `set_formats` in the configuration file, then `xmake pack` will automatically generate packages for all these formats by default.

Of course, we can also use `xmake pack --formats=nsis,targz` to selectively specify which formats of packages currently need to be packaged.

#### Modify the package file name

We can modify the package name through `set_basename()` in the configuration file, or we can modify it through the command line.

```bash
$ xmake pack --basename="foo"
packing build/xpack/test/foo.zip ..
pack ok
```

#### Specify output directory

The default output directory is in the build directory, but we can also modify the output path.

```bash
$ xmake pack -o /tmp/output
```

#### Disable automatic build

If you are building a binary package such as NSIS, `xmake pack` will automatically compile all bound target files first, and then execute the packaging logic.

But if we have already compiled it and don't want to compile it every time, but package it directly, we can disable automatic building through the following parameters.

```bash
$ xmake pack --autobuild=n
```

### Interface description

For more descriptions of the XPack packaging interface, see: [XPack Packaging Interface Document](https://xmake.io).

### Install the package locally

By default, packages configured through `add_requires("xxx")` will be installed in the global directory, and different projects share these packages.

In the new version, we have added a `package.install_locally` strategy, which can be configured to let xmake install the package to the current local project directory.

```lua
set_policy("package.install_locally", true)
```

## Changelog

### New features

* Add `network.mode` policy
* [#1433](https://github.com/xmake-io/xmake/issues/1433): Add `xmake pack` command to generate NSIS/zip/tar.gz/rpm/srpm/runself packages like cmake/cpack
* [#4435](https://github.com/xmake-io/xmake/issues/4435): Support batchsize for UnityBuild in Group Mode
* [#4485](https://github.com/xmake-io/xmake/pull/4485): Support package.install_locally
* Support NetBSD

### Changes

* [#4484](https://github.com/xmake-io/xmake/pull/4484): Improve swig rule
* Improve Haiku support

### Bugs fixed

* [#4372](https://github.com/xmake-io/xmake/issues/4372): Fix protobuf rules
* [#4439](https://github.com/xmake-io/xmake/issues/4439): Fix asn1c rules
