Skip to content

Xmake v2.8.6 released, New Packaging Plugin: XPack

ruki edited this page Dec 26, 2023 · 1 revision

Xmake is a lightweight cross-platform build utility based on Lua.

It is very lightweight and has no dependencies because it has a built-in Lua runtime.

It uses xmake.lua to maintain project builds and its configuration syntax is very simple and readable.

We can use it to build project directly like Make/Ninja, or generate project files like CMake/Meson, and it also has a built-in package management system to help users solve the integrated use of C/C++ dependent libraries.

Xmake = Build backend + Project Generator + Package Manager + [Remote|Distributed] Build + Cache

Although not very precise, we can still understand Xmake in the following way:

Xmake ≈ Make/Ninja + CMake/Meson + Vcpkg/Conan + distcc + ccache/sccache

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.

$ sudo dnf install xmake

Many thanks to @topazus @mochaaP for their contribution to Xmake. For related information, see: #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:

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:

$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.

$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.

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.

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.

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:

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.

$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.

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

We can also look at a more complete example:

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

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.

xpack("test")
     set_formats("srczip", "srctargz")
     add_sourcefiles("(src/**)")
$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.

xpack("test")
     set_formats("zip", "targz")
     add_installfiles("(src/**)")
$xmakepack
packing build/xpack/test/test-macosx-v1.0.0.zip ..
packing build/xpack/test/test-macosx-v1.0.0.tar.gz ..
pack ok

!> 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.

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.

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.

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.

$ 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.

$ 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.

$ xmake pack --autobuild=n

Interface description

For more descriptions of the XPack packaging interface, see: XPack Packaging Interface Document.

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.

set_policy("package.install_locally", true)

Changelog

New features

  • Add network.mode policy
  • #1433: Add xmake pack command to generate NSIS/zip/tar.gz/rpm/srpm/runself packages like cmake/cpack
  • #4435: Support batchsize for UnityBuild in Group Mode
  • #4485: Support package.install_locally
  • Support NetBSD

Changes

  • #4484: Improve swig rule
  • Improve Haiku support

Bugs fixed

  • #4372: Fix protobuf rules
  • #4439: Fix asn1c rules
Clone this wiki locally