Skip to content

打包(Linux)

Linux 上的分发有几种形式。 最容易上手的是 Flatpak,所以本指南介绍它。 Flatpak 是沙箱化的,可跨发行版工作,而且创建起来很直接。

需要的工具是 flatpak 和 flatpak-builder。 同时还要添加 Flathub 远程仓库。 你的应用程序所构建所依赖的运行时和 SDK 都来自那里:

Terminal window
flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo
bash

根据你使用的编程语言,你还需要一个工具,把构建工具的锁文件转换为 flatpak-builder 能够理解的依赖项。 构建过程与 Flatpak 本身一样是沙箱化的,其内部无法访问网络。 每一个原本需要下载的依赖项都必须事先声明。

Slint 支持的每种编程语言都有对应的锁文件转换脚本:

  • Rust
  • NodeJS
    • node - 这是推荐的方法,支持 npm、yarn 和 pnpm
    • npm(如果 node 脚本不起作用)
    • yarn(如果 node 脚本不起作用)
  • Deno
  • Python

C++ 没有标准的跨平台包管理器。 请改为将依赖项随项目一同提供。 Slint 支持 Bun,但 Flatpak 不支持。

Flatpak 构建文件以包 ID 命名,包 ID 是一个你控制的、反向书写的域名。 本指南使用 com.yourorganization.YourApp,因此构建文件名为 com.yourorganization.YourApp.yml。

# The ID, explained above.
id: com.yourorganization.YourApp
# The runtime your application runs against
runtime: org.freedesktop.Platform
# A new major version comes out every August and is supported for two years.
# See https://docs.flatpak.org/en/latest/available-runtimes.html
runtime-version: '25.08'
# The command that starts your application, installed by the build below
command: your-app
# The matching SDK, which the build runs in
sdk: org.freedesktop.Sdk
# The set of permissions that this application requires
finish-args:
# OpenGL/Vulkan rendering
- --device=dri
# Allow use of IPC (required by Wayland and X11)
- --share=ipc
# Allow using Wayland via the Freedesktop sandbox protocol extensions
- --socket=wayland
# Use X11 as a fallback. There is also `--socket=x11` for applications
# that require X11, but `--socket=fallback-x11` overrides it
- --socket=fallback-x11
yaml

这些权限是每个 Slint 应用程序所需的最低权限。 如果需要更多权限,请参见 Flatpak 的文档。 常见的额外权限有:用于网络访问的 --share=network、用于音频的 --socket=pulseaudio,以及用于蓝牙的 --allow=bluetooth。

接下来是与你的语言相关的部分:依赖项和构建过程。

对锁文件运行上面提到的 flatpak-cargo-generator.py 脚本。 它会以内联方式声明自己的依赖项,因此 uv 无需虚拟环境即可运行它:

Terminal window
curl -O https://raw.githubusercontent.com/flatpak/flatpak-builder-tools/master/cargo/flatpak-cargo-generator.py
uv run flatpak-cargo-generator.py Cargo.lock -o cargo-sources.json
bash

传入 -o,否则脚本会改为写入 generated-sources.json。 如果要使用 python3 而不是 uv 运行它,请先安装它的 aiohttp 和 tomlkit 依赖项。

# ...
# This ensures that the Rust toolchain is installed inside the build environment.
sdk-extensions:
- org.freedesktop.Sdk.Extension.rust-stable
modules:
# The module name is arbitrary, but Flatpak will create a `/run/build/your-module-name`
# directory and build the package inside of it, so it should match any `/run/build/..`
# paths in the build config. It does not need to match the command name.
- name: your-app
# This tells Flatpak that you will be writing the build commands out manually, in
# `build-commands` (see below)
buildsystem: simple
build-options:
# This adds the Rust SDK to your PATH
append-path: /usr/lib/sdk/rust-stable/bin
env:
# Required to keep the Cargo build artifacts inside Flatpak's sandboxed build
# directory
CARGO_HOME: /run/build/your-app/cargo
CARGO_NET_OFFLINE: 'true'
build-commands:
- cargo --offline fetch --manifest-path Cargo.toml --verbose
# For the sake of this example, we assume that this build command produces a binary
# named `your-app`.
- cargo build --release --offline
# The right-hand side of this must be `${FLATPAK_DEST}/bin/your-command`, where
# `your-command` matches the top-level `command` field mentioned in the previous
# section
- install -Dm0755 target/release/your-app ${FLATPAK_DEST}/bin/your-app
sources:
- type: git
# This assumes that this yaml file is in the root of your project
path: ./
# `HEAD` builds the commit your checkout is on. Without a ref,
# flatpak-builder builds the repository's default branch instead
branch: HEAD
# If you named your generated sources file something different, or put it somewhere
# other than in the same directory as this file, you should specify the path to it
# here
- cargo-sources.json
yaml

这涵盖了默认渲染器所需的一切。 正如下一节所解释的,Skia 渲染器需要更多内容。

Skia 是一个用 C++ 编写的大型图形库。 通常,使用 Skia 渲染器构建 Slint 时会下载一份预编译的副本,但沙箱内无法访问网络,因此构建过程必须自行编译 Skia。 这意味着要在清单中声明 Skia 自身的源代码,足足有数百兆字节。

下面的脚本可以完成这件事。 它会查找你的 Slint 版本所期望的确切 Skia 修订版本,然后把 Skia 的依赖项列表转换为各自固定到某个提交的源代码。 把它保存到 flatpak-cargo-generator.py 旁边,并在依赖项发生变化时从项目根目录运行它:

Terminal window
python3 flatpak-skia-generator.py
bash

它只使用标准库,因此自身不需要任何依赖项。

flatpak-skia-generator.py
#!/usr/bin/env python3
# Copyright © SixtyFPS GmbH <info@slint.dev>
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
"""Turn the Skia revision that skia-bindings pins into flatpak-builder sources.
Usage: flatpak-skia-generator.py [-o skia-sources.json] [--dest deps/skia-src]
"""
import json
import re
import subprocess
import sys
import urllib.request
flags = dict(zip(sys.argv[1::2], sys.argv[2::2]))
output = flags.get("-o", "skia-sources.json")
dest = flags.get("--dest", "deps/skia-src")
def fetch(url):
return urllib.request.urlopen(url).read().decode()
cargo = ["cargo", "metadata", "--format-version", "1", "--locked"]
packages = json.loads(subprocess.check_output(cargo))["packages"]
bindings = [p for p in packages if p["name"] == "skia-bindings"]
if not bindings:
sys.exit("no skia-bindings; is the renderer-skia feature enabled?")
tag = bindings[0]["metadata"]["skia"]
print(f"skia-bindings {bindings[0]['version']}, skia fork tag {tag}", file=sys.stderr)
raw = f"https://raw.githubusercontent.com/rust-skia/skia/{tag}"
# Skia's DEPS is Python, listing every checkout its own build expects
ns = {"Var": lambda name: ns["vars"][name]}
exec(fetch(f"{raw}/DEPS"), ns) # noqa: S102 -- evaluating DEPS is the point
fork = "https://github.com/rust-skia/skia.git"
sources = [{"type": "git", "url": fork, "tag": tag, "dest": dest}]
for path, spec in sorted(ns["deps"].items()):
if not isinstance(spec, str) or "emsdk" in path:
continue # cipd packages and the wasm-only emsdk are not needed
url, _, commit = spec.partition("@")
assert commit, f"DEPS entry {path} has no pinned commit: {spec}"
sources.append(
{
"type": "git",
"url": url,
"commit": commit,
"dest": f"{dest}/{path}",
"disable-submodules": True,
}
)
with open(output, "w") as f:
json.dump(sources, f, indent=4)
f.write("\n")
print(f"wrote {output} ({len(sources)} sources)", file=sys.stderr)
# Skia records the gn revision its own CI builds with; print it so the gn
# module in the manifest can follow along when the Skia pin moves
gn = re.search(r"rev = '(\w{40})'", fetch(f"{raw}/bin/fetch-gn"))
if gn:
print(f"skia pins gn revision {gn.group(1)}", file=sys.stderr)

Skia 使用 gn 进行配置,而 SDK 并不附带这个构建工具;它使用 clang 而不是 SDK 的 gcc 进行编译。 这给清单带来了四项新增内容:

# ...
sdk-extensions:
- org.freedesktop.Sdk.Extension.rust-stable
# clang, and the libclang that the bindings generator loads
- org.freedesktop.Sdk.Extension.llvm20
# Applies to every module, unlike the build-options of a single module, so that
# both gn and Skia find clang
build-options:
append-path: /usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin
# gn is only needed to build Skia, so keep it out of the finished application
cleanup:
- /bin/gn
modules:
- name: gn
buildsystem: simple
build-commands:
- python3 build/gen.py --out-path=out --allow-warnings
- ninja -C out gn
- install -Dm0755 out/gn ${FLATPAK_DEST}/bin/gn
sources:
- type: git
url: https://gn.googlesource.com/gn.git
# The generator prints the revision Skia itself uses
commit: b2afae122eeb6ce09c52d63f67dc53fc517dbdc8
# gn stamps its version from a tag, which a shallow clone leaves behind
disable-shallow-clone: true
- name: your-app
# ...
build-options:
env:
# ...
# Points the build at the prepared Skia sources, which is what makes it
# compile them instead of downloading anything
SKIA_SOURCE_DIR: /run/build/your-app/deps/skia-src
LIBCLANG_PATH: /usr/lib/sdk/llvm20/lib
# Use the gn built above rather than the one Skia would fetch
SKIA_GN_COMMAND: /app/bin/gn
sources:
# ...
- skia-sources.json
yaml

编译 Skia 需要一段时间,而且它的源代码很大。 每个输入都固定到某个提交,因此构建保持可复现、能在运行时支持的所有架构上工作,并且在你重新运行生成器时会跟随你的 Slint 版本。

要让你的应用程序出现在启动器中,请编写一个 .desktop 文件并提供图标。 在本例中,桌面条目如下所示:

[Desktop Entry]
# The version of the desktop entry specification this file follows, not the
# version of your application
Version=1.0
Type=Application
Terminal=false
# This should be the same as the binary specified in the `command` section
# of your build `.yaml`
Exec=your-app
# This should be the human-readable name of your application
Name=Your App
# This should be the same as your Flatpak package ID, see below for details
Icon=com.yourorganization.YourApp
ini

使用 Freedesktop SDK 附带的 desktop-file-validate 检查该文件:

Terminal window
flatpak run --command=desktop-file-validate org.freedesktop.Sdk//25.08 com.yourorganization.YourApp.desktop
bash

从 build-commands 中安装它:

# ...
build-commands:
# ...
- install -Dm0644 path/to/${FLATPAK_ID}.desktop ${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
# ...
yaml

Flatpak 会在以下目录中查找图标:

  • ${FLATPAK_DEST}/share/icons/hicolor/WIDTHxHEIGHT/apps/ 中的 .png 文件,其中 WIDTHxHEIGHT 与图像(最大 512)的宽高相匹配
  • ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/ 中的 .svg 文件

无论你选择哪种格式,都应按照你的 Flatpak 包 ID 命名文件,并从 build-commands 中安装它:

# ...
build-commands:
# ...
# Replace 512x512 with the width and height of your icon
- install -Dm0644 path/to/icon.png ${FLATPAK_DEST}/share/icons/hicolor/512x512/apps/${FLATPAK_ID}.png
# ..or...
- install -Dm0644 path/to/icon.svg ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/${FLATPAK_ID}.svg
# ...
yaml

最后,你的包需要一个 .metainfo.xml 文件。 包管理器和 Flathub 会读取它来向用户描述你的应用程序。 完整规范不在本文范围内,因此下面是通过校验所需的最少字段:

<?xml version="1.0" encoding="UTF-8" ?>
<component type="desktop-application">
<!-- This must be the same as your Flatpak package ID -->
<id>com.yourorganization.YourApp</id>
<name>Your App</name>
<summary>A short line describing what your application does</summary>
<categories>
<category>Development</category>
</categories>
<keywords>
<keyword>development</keyword>
</keywords>
<developer id="com.yourorganization">
<name>Your Organization</name>
</developer>
<icon type="stock">com.yourorganization.YourApp</icon>
<metadata_license>MIT</metadata_license>
<project_license>MIT</project_license>
<description>
<p>
A paragraph about your application. Software centers show this
underneath its name, so write a sentence or two rather than
repeating the summary above.
</p>
</description>
<url type="homepage">https://slint.dev/</url>
<!-- The name of your .desktop file, including the extension -->
<launchable type="desktop-id">com.yourorganization.YourApp.desktop</launchable>
<!-- Required by Flathub. `oars-1.1` with no attributes declares that
your application has nothing to disclose -->
<content_rating type="oars-1.1"/>
<releases>
<release version="1.0" date="2026-07-07"/>
</releases>
</component>
xml

在构建之前,使用 Freedesktop SDK 中的 appstreamcli 校验该文件:

Terminal window
flatpak run --command=appstreamcli org.freedesktop.Sdk//25.08 validate --no-net com.yourorganization.YourApp.metainfo.xml
bash

像桌面条目和图标一样,从 build-commands 中安装它:

# ...
build-commands:
# ...
- install -Dm0644 path/to/${FLATPAK_ID}.metainfo.xml ${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.metainfo.xml
# ...
yaml

构建文件是零散地逐步展开的,因此这里给出完整且不带注释的版本,可供复制和调整。 它假设桌面条目、图标和 metainfo 与清单一起位于项目根目录。 在构建之前提交它们:git 源代码打包的是仓库所保存的内容,而不是你的工作树。

id: com.yourorganization.YourApp
runtime: org.freedesktop.Platform
runtime-version: '25.08'
command: your-app
sdk: org.freedesktop.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.rust-stable
- org.freedesktop.Sdk.Extension.llvm20
finish-args:
- --device=dri
- --share=ipc
- --socket=wayland
- --socket=fallback-x11
cleanup:
- /bin/gn
build-options:
append-path: /usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm20/bin
modules:
- name: gn
buildsystem: simple
build-commands:
- python3 build/gen.py --out-path=out --allow-warnings
- ninja -C out gn
- install -Dm0755 out/gn ${FLATPAK_DEST}/bin/gn
sources:
- type: git
url: https://gn.googlesource.com/gn.git
commit: b2afae122eeb6ce09c52d63f67dc53fc517dbdc8
disable-shallow-clone: true
- name: your-app
buildsystem: simple
build-options:
env:
CARGO_HOME: /run/build/your-app/cargo
CARGO_NET_OFFLINE: 'true'
SKIA_SOURCE_DIR: /run/build/your-app/deps/skia-src
LIBCLANG_PATH: /usr/lib/sdk/llvm20/lib
SKIA_GN_COMMAND: /app/bin/gn
build-commands:
- cargo --offline fetch --manifest-path Cargo.toml --verbose
- cargo build --release --offline
- install -Dm0755 target/release/your-app ${FLATPAK_DEST}/bin/your-app
- install -Dm0644 ${FLATPAK_ID}.desktop ${FLATPAK_DEST}/share/applications/${FLATPAK_ID}.desktop
- install -Dm0644 icon.png ${FLATPAK_DEST}/share/icons/hicolor/512x512/apps/${FLATPAK_ID}.png
- install -Dm0644 ${FLATPAK_ID}.metainfo.xml ${FLATPAK_DEST}/share/metainfo/${FLATPAK_ID}.metainfo.xml
sources:
- type: git
path: ./
branch: HEAD
- cargo-sources.json
- skia-sources.json
yaml

对于 FemtoVG 渲染器,请去掉 llvm20 扩展、gn 模块、cleanup 条目、三个 Skia 环境变量,以及 skia-sources.json。

清单、桌面条目、图标和 metainfo 都就绪后, 构建包并为你自己的用户安装它:

Terminal window
flatpak-builder --user --install --force-clean --install-deps-from=flathub \
build-dir com.yourorganization.YourApp.yml
bash

build-dir 是 flatpak-builder 填充的临时目录, --force-clean 会先清空它。 --install-deps-from=flathub 会在运行时和 SDK 缺失时获取它们。 首次构建会下载几 GB 内容并编译每一个依赖项, 之后的构建会复用 .flatpak-builder 中的缓存。 请把该目录连同生成的源代码一起加入你的 .gitignore。

按照用户将采用的方式运行它:

Terminal window
flatpak run com.yourorganization.YourApp
bash

要把包交给别人, 可以将其导出到一个仓库,然后把该仓库转换为单个文件:

Terminal window
flatpak-builder --user --force-clean --repo=repo build-dir com.yourorganization.YourApp.yml
flatpak build-bundle repo com.yourorganization.YourApp.flatpak com.yourorganization.YourApp \
--runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo
bash

使用 flatpak install ./com.yourorganization.YourApp.flatpak 安装结果。 一个 bundle 只包含你的应用程序本身, 因此 --runtime-repo 会告诉另一台机器到何处获取运行时。 它也是一份快照:flatpak update 无法更新从 bundle 安装的应用程序。 要发布更新,请通过 HTTPS 提供 repo 目录,并让你的用户将其添加为远程仓库。


© 2026 SixtyFPS GmbH