强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Flatpak 应用打包完整教程 / 第 6 章:构建应用

第 6 章:构建应用

本章目标:掌握使用 flatpak-builder 构建 Flatpak 应用的完整流程,理解构建目录结构与缓存机制。


6.1 构建工具概览

6.1.1 flatpak-builder 简介

flatpak-builder 是 Flatpak 官方提供的自动化构建工具。它根据 Manifest 文件:

  1. 下载源代码(git clone、wget 等)
  2. 创建沙箱构建环境
  3. 按顺序构建模块
  4. 生成 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 源必须指定 committag,不允许使用 branch(Flathub 策略)。


6.12 扩展阅读