第 6 章:构建应用
第 6 章:构建应用
本章目标:掌握使用
flatpak-builder构建 Flatpak 应用的完整流程,理解构建目录结构与缓存机制。
6.1 构建工具概览
6.1.1 flatpak-builder 简介
flatpak-builder 是 Flatpak 官方提供的自动化构建工具。它根据 Manifest 文件:
- 下载源代码(git clone、wget 等)
- 创建沙箱构建环境
- 按顺序构建模块
- 生成 Flatpak 应用包
6.1.2 安装 flatpak-builder
# Fedora
sudo dnf install flatpak-builder
# Ubuntu / Debian
sudo apt install flatpak-builder
# Arch Linux
sudo pacman -S flatpak-builder
# openSUSE
sudo zypper install flatpak-builder
# 验证安装
flatpak-builder --version
# 输出示例:flatpak-builder 1.4.4
6.1.3 必需的 SDK
构建前必须安装对应的 SDK:
# 安装 Freedesktop SDK(最基础)
flatpak install flathub org.freedesktop.Sdk//24.08 org.freedesktop.Platform//24.08
# 安装 GNOME SDK(如需构建 GNOME 应用)
flatpak install flathub org.gnome.Sdk//47 org.gnome.Platform//47
# 安装 KDE SDK(如需构建 KDE 应用)
flatpak install flathub org.kde.Sdk//6.8 org.kde.Platform//6.8
6.2 构建流程详解
6.2.1 基本构建命令
# 标准构建流程
flatpak-builder --force-clean --repo=repo builddir com.example.MyApp.json
# 参数说明:
# --force-clean 清除之前的构建目录
# --repo=repo 输出到本地 OSTree 仓库 "repo"
# builddir 构建工作目录
# com.example.MyApp.json Manifest 文件路径
6.2.2 完整构建示例
以一个简单的 “Hello World” 应用为例:
# 步骤 1:创建工作目录
mkdir -p ~/flatpak-build && cd ~/flatpak-build
# 步骤 2:创建源代码
mkdir -p src
cat > src/hello.c << 'EOF'
#include <stdio.h>
#include <gtk/gtk.h>
static void on_activate(GtkApplication *app) {
GtkWidget *window = gtk_application_window_new(app);
GtkWidget *label = gtk_label_new("Hello, Flatpak!");
gtk_window_set_title(GTK_WINDOW(window), "Hello");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 200);
gtk_window_set_child(GTK_WINDOW(window), label);
gtk_window_present(GTK_WINDOW(window));
}
int main(int argc, char **argv) {
GtkApplication *app = gtk_application_new("com.example.Hello", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
int status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
EOF
# 步骤 3:创建 Manifest
cat > com.example.Hello.json << 'EOF'
{
"app-id": "com.example.Hello",
"runtime": "org.gnome.Platform",
"runtime-version": "47",
"sdk": "org.gnome.Sdk",
"command": "hello",
"finish-args": [
"--share=ipc",
"--socket=wayland",
"--socket=fallback-x11"
],
"modules": [
{
"name": "hello",
"buildsystem": "meson",
"sources": [
{
"type": "dir",
"path": "src"
}
]
}
]
}
EOF
# 步骤 4:在 src 目录创建 meson.build
cat > src/meson.build << 'EOF'
project('hello', 'c',
version: '1.0.0',
default_options: ['warning_level=2'])
gtk_dep = dependency('gtk4')
executable('hello',
'hello.c',
dependencies: gtk_dep,
install: true)
EOF
# 步骤 5:构建
flatpak-builder --force-clean --repo=repo builddir com.example.Hello.json
# 步骤 6:添加本地仓库并安装
flatpak remote-add --no-gpg-verify --if-not-exists myrepo repo
flatpak install myrepo com.example.Hello
# 步骤 7:运行
flatpak run com.example.Hello
6.2.3 构建过程详解
flatpak-builder 执行流程:
1. 解析 Manifest
└─ 读取 app-id、runtime、sdk 等信息
2. 准备构建目录 (builddir/)
└─ 清除旧目录(如果使用 --force-clean)
3. 下载源代码
├─ git clone / wget / curl
└─ 验证 SHA256 校验和
4. 创建沙箱构建环境
├─ 挂载 SDK 作为 /usr
├─ 挂载应用目录为 /app
└─ 应用 finish-args 中的权限
5. 依次构建每个模块
├─ 模块 1: ./configure && make && make install
├─ 模块 2: cmake && cmake --build && cmake --install
├─ 模块 3: meson setup && ninja && ninja install
└─ ...
6. 清理不需要的文件
└─ 根据 cleanup 规则删除
7. 生成元数据
├─ metadata (权限信息)
└─ export/share/ (desktop 文件、图标、appdata)
8. 提交到 OSTree 仓库
└─ repo/ (本地仓库)
6.3 构建选项详解
6.3.1 常用构建参数
# 基本构建
flatpak-builder --repo=repo builddir app.json
# 强制重新构建
flatpak-builder --force-clean --repo=repo builddir app.json
# 只下载源代码(不构建)
flatpak-builder --download-only builddir app.json
# 只更新源代码(不构建)
flatpak-builder --download-only --update-sources builddir app.json
# 禁用网络(离线构建)
flatpak-builder --disable-download --repo=repo builddir app.json
# 并行构建线程数
flatpak-builder --jobs=8 --repo=repo builddir app.json
# 构建指定模块(调试用)
flatpak-builder --build-only=module-name --repo=repo builddir app.json
# 从指定模块开始构建(跳过之前的模块)
flatpak-builder --start-at=module-name --repo=repo builddir app.json
# 构建后停止(不安装到仓库)
flatpak-builder --stop-at=module-name builddir app.json
# 详细输出
flatpak-builder --verbose --repo=repo builddir app.json
# 允许构建时使用网络(Flathub 禁止)
flatpak-builder --share-network --repo=repo builddir app.json
6.3.2 构建调试
# 构建失败时,进入沙箱手动调试
flatpak-builder --build-shell=module-name builddir app.json
# 这会进入模块的构建沙箱,可以手动执行构建命令
# 在沙箱中:
ls /run/build/module-name/ # 源代码目录
cat /run/build/module-name/config.log # 查看配置日志
# 退出沙箱
exit
# 查看构建日志
ls builddir/logs/
# 每个模块都有对应的日志文件
# 查看特定模块日志
cat builddir/logs/module-name/build.log
6.3.3 构建环境探索
# 构建完成后,查看 /app 目录内容
flatpak-builder --run builddir app.json ls /app/
# 查看 /app/bin
flatpak-builder --run builddir app.json ls /app/bin/
# 查看 /app/lib
flatpak-builder --run builddir app.json ls /app/lib/
# 在构建环境中运行 shell
flatpak-builder --run builddir app.json bash
6.4 构建目录结构
6.4.1 目录布局
project/
├── com.example.App.json # Manifest
├── src/ # 本地源代码(可选)
├── builddir/ # 构建工作目录
│ ├── .flatpak-builder/ # flatpak-builder 内部状态
│ │ ├── build/ # 各模块的构建目录
│ │ │ ├── module-a/ # 模块 A 的源码和构建产物
│ │ │ └── module-b/ # 模块 B
│ │ ├── checksums/ # 源代码校验和缓存
│ │ ├── download/ # 下载缓存
│ │ ├── repo/ # 临时仓库
│ │ └── logs/ # 构建日志
│ └── export/ # 最终的应用文件
│ ├── bin/
│ ├── lib/
│ ├── share/
│ └── ...
├── repo/ # 输出的 OSTree 仓库
│ ├── config
│ ├── objects/
│ ├── refs/
│ └── ...
└── .flatpak-builder/ # 全局缓存(可选)
└── git/ # Git 仓库缓存
6.4.2 管理构建目录
# 清除构建目录并重新构建
rm -rf builddir
flatpak-builder --force-clean --repo=repo builddir app.json
# 保留构建缓存但重新构建
flatpak-builder --repo=repo builddir app.json
# 只清除特定模块的构建缓存
rm -rf builddir/.flatpak-builder/build/module-name
# 查看构建目录大小
du -sh builddir/
du -sh builddir/.flatpak-builder/
6.5 缓存机制
6.5.1 缓存类型
| 缓存类型 | 位置 | 说明 |
|---|---|---|
| Git 缓存 | ~/.local/share/flatpak-builder/git/ | Git 仓库的本地镜像 |
| 下载缓存 | ~/.local/share/flatpak-builder/downloads/ | 下载的源码包 |
| 校验和缓存 | builddir/.flatpak-builder/checksums/ | 源码校验和记录 |
| 构建缓存 | builddir/.flatpak-builder/build/ | 各模块的构建产物 |
6.5.2 优化缓存
# 查看缓存大小
du -sh ~/.local/share/flatpak-builder/
du -sh ~/.local/share/flatpak-builder/git/
du -sh ~/.local/share/flatpak-builder/downloads/
# 清除所有下载缓存
rm -rf ~/.local/share/flatpak-builder/downloads/
# 清除所有 Git 缓存
rm -rf ~/.local/share/flatpak-builder/git/
# 使用 --ccache 启用 C/C++ 编译缓存
flatpak-builder --ccache --repo=repo builddir app.json
# 查看 ccache 统计
flatpak-builder --run builddir app.json ccache -s
6.5.3 增量构建
# 增量构建(默认行为)
# 只有修改过的模块才会重新构建
flatpak-builder --repo=repo builddir app.json
# 强制重建特定模块
rm -rf builddir/.flatpak-builder/build/myapp
flatpak-builder --repo=repo builddir app.json
# 构建时跳过测试(加速)
# 在 Manifest 中:
# "no-debuginfo": true
# 或使用命令行:
flatpak-builder --repo=repo --install-deps-from=flathub builddir app.json
6.6 安装到本地
6.6.1 从本地仓库安装
# 构建后添加本地仓库
flatpak remote-add --no-gpg-verify --if-not-exists myrepo repo
# 安装应用
flatpak install myrepo com.example.App
# 运行
flatpak run com.example.App
# 更新(修改后重新构建)
flatpak-builder --force-clean --repo=repo builddir com.example.App.json
flatpak update com.example.App
6.6.2 生成 Bundle 文件
# 从本地仓库生成 .flatpak bundle 文件
flatpak build-bundle repo com.example.App.flatpak com.example.App stable
# 包含运行时的 bundle
flatpak build-bundle repo com.example.App.flatpak com.example.App stable --runtime
# 从 bundle 文件安装
flatpak install com.example.App.flatpak
# 查看 bundle 信息
flatpak info com.example.App.flatpak
6.6.3 生成 Flatpakref 文件
# 生成 .flatpakref 文件(用于远程分发)
cat > com.example.App.flatpakref << EOF
[Flatpak Ref]
Title=My Application
Name=com.example.App
Branch=stable
Url=https://flatpak.example.com/repo
IsRuntime=False
GPGKey=$(base64 < repo/key.gpg | tr -d '\n')
RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo
EOF
6.7 使用 flatpak build 命令(手动构建)
除了 flatpak-builder,你也可以使用 flatpak build 命令手动控制构建过程:
# 步骤 1:初始化构建目录
flatpak build-init builddir com.example.App org.gnome.Sdk org.gnome.Platform 47
# 步骤 2:在沙箱中构建
flatpak build --share=network builddir meson setup --prefix=/app _build
flatpak build builddir ninja -C _build
flatpak build builddir ninja -C _build install
# 步骤 3:完成构建
flatpak build-finish builddir --socket=wayland --share=network
# 步骤 4:导出到仓库
flatpak build-export repo builddir stable
# 步骤 5:安装
flatpak install repo com.example.App
手动构建的沙箱探索
# 在构建目录中运行 shell
flatpak build builddir bash
# 查看沙箱内的文件系统
ls /app/
ls /usr/
ls /run/build/
# 退出
exit
6.8 构建优化技巧
6.8.1 使用 --install-deps-from
# 自动从 Flathub 安装缺少的运行时和 SDK
flatpak-builder --install-deps-from=flathub --repo=repo builddir app.json
6.8.2 构建时允许网络
# 某些项目(如 Go、Rust、Node.js)需要构建时下载依赖
# 注意:Flathub 禁止此操作,仅限本地开发
# Manifest 中配置
{
"build-options": {
"build-args": ["--share=network"]
}
}
# 命令行中启用
flatpak-builder --share-network --repo=repo builddir app.json
6.8.3 使用 ccache
# 启用 C/C++ 编译缓存
flatpak-builder --ccache --repo=repo builddir app.json
# 配置 ccache 大小限制
flatpak-builder --run builddir app.json ccache -M 5G
6.8.4 并行构建
# 使用所有 CPU 核心
nproc # 查看核心数
flatpak-builder --jobs=$(nproc) --repo=repo builddir app.json
# 限制并行数(内存不足时)
flatpak-builder --jobs=4 --repo=repo builddir app.json
6.9 常见构建问题
问题 1:缺少依赖
# 错误:configure: error: Package 'gtk4' not found
# 原因:缺少 GTK4 开发头文件
# 解决:确保使用正确的 SDK
# Manifest 中检查:
# "sdk": "org.gnome.Sdk" # GNOME SDK 包含 GTK4
# 或安装额外的 SDK 扩展
flatpak install flathub org.freedesktop.Sdk.Extension.rust-stable//24.08
问题 2:SHA256 校验和不匹配
# 错误:Checksum mismatch for ...
# 解决:更新 Manifest 中的 sha256 值
# 计算新的校验和
wget https://example.com/source.tar.gz
sha256sum source.tar.gz
# 将输出的 hash 更新到 Manifest
问题 3:源代码获取失败
# 错误:Failed to download sources: ...
# 解决方案 1:手动下载
flatpak-builder --download-only builddir app.json
# 解决方案 2:禁用网络,使用预下载的源码
# 将源码放在 ~/.local/share/flatpak-builder/downloads/
问题 4:构建超时
# 错误:Build timeout
# 原因:构建时间超过默认限制
# 解决:
# 1. 增加构建时间限制
# 2. 使用 --ccache 缓存编译结果
# 3. 减少并行任务数
6.10 业务场景
场景 1:自动化构建脚本
#!/bin/bash
# build-flatpak.sh - 自动化 Flatpak 构建脚本
set -e
APP_ID="com.example.MyApp"
BUILD_DIR="builddir"
REPO_DIR="repo"
MANIFEST="${APP_ID}.json"
echo "=== Flatpak 自动构建 ==="
echo "应用 ID: $APP_ID"
echo "构建目录: $BUILD_DIR"
echo "仓库目录: $REPO_DIR"
echo ""
# 清理旧构建
echo "清理旧构建..."
rm -rf "${BUILD_DIR}"
rm -rf "${REPO_DIR}"
# 安装依赖
echo "安装依赖..."
flatpak-builder --install-deps-from=flathub "${BUILD_DIR}" "${MANIFEST}"
# 构建
echo "开始构建..."
flatpak-builder \
--force-clean \
--ccache \
--jobs=$(nproc) \
--repo="${REPO_DIR}" \
"${BUILD_DIR}" \
"${MANIFEST}"
# 添加本地仓库
echo "添加本地仓库..."
flatpak remote-add --no-gpg-verify --if-not-exists local-repo "${REPO_DIR}"
# 安装应用
echo "安装应用..."
flatpak install -y local-repo "${APP_ID}"
# 生成 bundle
echo "生成 Bundle..."
flatpak build-bundle "${REPO_DIR}" "${APP_ID}.flatpak" "${APP_ID}" stable
# 验证
echo "验证应用..."
flatpak info "${APP_ID}"
flatpak run "${APP_ID}" --version 2>/dev/null || echo "应用无法显示版本"
echo ""
echo "=== 构建完成 ==="
echo "Bundle 文件: ${APP_ID}.flatpak"
echo "仓库目录: ${REPO_DIR}"
场景 2:多架构构建
#!/bin/bash
# build-multiarch.sh - 多架构 Flatpak 构建
APP_ID="com.example.MyApp"
ARCHS=("x86_64" "aarch64")
for arch in "${ARCHS[@]}"; do
echo "构建 ${arch} 架构..."
flatpak-builder \
--force-clean \
--arch="${arch}" \
--repo="repo-${arch}" \
"builddir-${arch}" \
"${APP_ID}.json"
# 生成 bundle
flatpak build-bundle \
--arch="${arch}" \
"repo-${arch}" \
"${APP_ID}-${arch}.flatpak" \
"${APP_ID}" stable
echo "✓ ${arch} 构建完成"
done
echo "所有架构构建完成!"
6.11 注意事项
⚠️ 磁盘空间
构建过程可能占用大量磁盘空间。一个完整的 GNOME SDK 约 2.5 GB,加上构建缓存,一个项目可能需要 5-10 GB。建议在空间充足的分区进行构建。
⚠️ 构建时间
首次构建大型项目可能需要数小时。启用--ccache和增量构建可以显著加速后续构建。
⚠️ Flathub 要求
提交到 Flathub 的应用必须支持离线构建(--share-network不被允许)。所有依赖必须在sources中预先声明。
⚠️ 版本锁定
Manifest 中的 Git 源必须指定commit或tag,不允许使用branch(Flathub 策略)。