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

Vala 语言入门教程 / 11 - Docker 构建与部署

第 11 章:Docker 构建与部署

本章介绍如何使用 Docker 构建、测试和部署 Vala 应用,以及如何在容器环境中运行 GTK 应用。


11.1 为什么使用 Docker

11.1.1 Docker 在 Vala 开发中的价值

场景问题Docker 解决方案
环境一致性不同机器上 valac 版本不同统一的编译环境
CI/CD需要自动化构建可复现的构建镜像
交叉编译目标平台不可用多架构构建
依赖管理GTK/GLib 版本碎片化固定版本的依赖
测试隔离测试影响本地环境每次干净的测试环境
分发用户安装困难Flatpak 或容器化分发

11.1.2 典型工作流

开发者机器           Docker 构建             CI/CD            分发
┌──────────┐     ┌──────────────┐      ┌──────────┐     ┌──────────┐
│ .vala    │────→│ valac 编译   │────→│ 自动测试  │────→│ Flatpak  │
│ meson    │     │ Meson 构建   │      │ 代码检查  │     │ Container│
│ build    │     │ 生成制品     │      │ 打包发布  │     │ Package  │
└──────────┘     └──────────────┘      └──────────┘     └──────────┘

11.2 基本 Dockerfile

11.2.1 Ubuntu 基础镜像

# Dockerfile.ubuntu
# 基于 Ubuntu 的 Vala 开发环境
FROM ubuntu:24.04

# 避免交互式安装
ENV DEBIAN_FRONTEND=noninteractive

# 安装依赖
RUN apt-get update && apt-get install -y \
    valac \
    libglib2.0-dev \
    libgtk-4-dev \
    libadwaita-1-dev \
    meson \
    ninja-build \
    pkg-config \
    git \
    && rm -rf /var/lib/apt/lists/*

# 设置工作目录
WORKDIR /app

# 复制项目文件
COPY . .

# 构建
RUN meson setup build \
    && ninja -C build

# 运行测试(如果有)
RUN ninja -C build test || true

# 安装到 /opt
RUN ninja -C build install

# 入口点
CMD ["/app/build/src/myapp"]

11.2.2 Fedora 基础镜像

# Dockerfile.fedora
FROM fedora:40

# 安装依赖
RUN dnf install -y \
    vala \
    glib2-devel \
    gtk4-devel \
    libadwaita-devel \
    meson \
    ninja-build \
    pkgconfig \
    gcc \
    && dnf clean all

WORKDIR /app
COPY . .

RUN meson setup build \
    && ninja -C build \
    && ninja -C build test

CMD ["/app/build/src/myapp"]

11.2.3 Alpine 基础镜像(轻量级)

# Dockerfile.alpine
# 适合纯 Vala 项目(不使用 GTK)
FROM alpine:3.20

RUN apk add --no-cache \
    vala \
    glib-dev \
    gobject-introspection-dev \
    meson \
    ninja \
    gcc \
    musl-dev

WORKDIR /app
COPY . .

RUN meson setup build \
    -Dgtk=false \
    && ninja -C build

CMD ["/app/build/src/myapp"]

11.2.4 多阶段构建(减小镜像体积)

# Dockerfile.multistage
# 阶段 1:构建
FROM ubuntu:24.04 AS builder

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    valac \
    libglib2.0-dev \
    libgtk-4-dev \
    libadwaita-1-dev \
    meson \
    ninja-build \
    pkg-config \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

RUN meson setup build --prefix=/usr \
    -Dbuildtype=release \
    && ninja -C build

# 阶段 2:运行(只包含运行时依赖)
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    libglib2.0-0 \
    libgtk-4-1 \
    libadwaita-1-0 \
    && rm -rf /var/lib/apt/lists/*

# 从构建阶段复制编译产物
COPY --from=builder /app/build/src/myapp /usr/local/bin/myapp

CMD ["myapp"]

11.3 Meson 集成

11.3.1 Docker Compose 开发环境

# docker-compose.yml
version: '3.8'

services:
  # 开发环境
  dev:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/app
      - build-cache:/app/build
    environment:
      - DISPLAY=${DISPLAY}
    network_mode: host
    stdin_open: true
    tty: true

  # 构建服务
  build:
    build:
      context: .
      dockerfile: Dockerfile.ubuntu
    volumes:
      - ./build-output:/app/build

  # 测试服务
  test:
    build:
      context: .
      dockerfile: Dockerfile.test
    command: ninja -C build test

volumes:
  build-cache:

11.3.2 开发用 Dockerfile

# Dockerfile.dev
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    valac \
    libglib2.0-dev \
    libgtk-4-dev \
    libadwaita-1-dev \
    meson \
    ninja-build \
    pkg-config \
    gdb \
    valgrind \
    git \
    vim \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# 进入开发环境
CMD ["bash"]
# 使用方法
docker compose run --rm dev
# 在容器内:
meson setup build
ninja -C build
./build/src/myapp

11.4 CI/CD 集成

11.4.1 GitLab CI

# .gitlab-ci.yml
stages:
  - build
  - test
  - package

variables:
  UBUNTU_IMAGE: ubuntu:24.04

# 构建阶段
build:
  stage: build
  image: ${UBUNTU_IMAGE}
  before_script:
    - apt-get update
    - apt-get install -y valac libglib2.0-dev libgtk-4-dev
      libadwaita-1-dev meson ninja-build pkg-config
  script:
    - meson setup build --prefix=/usr
    - ninja -C build
  artifacts:
    paths:
      - build/
    expire_in: 1 hour

# 测试阶段
test:
  stage: test
  image: ${UBUNTU_IMAGE}
  before_script:
    - apt-get update
    - apt-get install -y valac libglib2.0-dev libgtk-4-dev
      libadwaita-1-dev meson ninja-build pkg-config xvfb
  script:
    - meson setup build --prefix=/usr
    - ninja -C build
    - xvfb-run ninja -C build test
  dependencies:
    - build

# 打包阶段
package:
  stage: package
  image: ${UBUNTU_IMAGE}
  script:
    - meson setup build --prefix=/usr
    - ninja -C build
    - DESTDIR=install ninja -C build install
    - tar czf myapp.tar.gz -C install .
  artifacts:
    paths:
      - myapp.tar.gz
    expire_in: 1 week
  only:
    - tags

11.4.2 GitHub Actions

# .github/workflows/build.yml
name: Build and Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: ubuntu:24.04

    steps:
      - name: 安装依赖
        run: |
          apt-get update
          apt-get install -y \
            valac libglib2.0-dev libgtk-4-dev \
            libadwaita-1-dev meson ninja-build \
            pkg-config xvfb git

      - name: 检出代码
        uses: actions/checkout@v4

      - name: 配置构建
        run: meson setup build --prefix=/usr

      - name: 编译
        run: ninja -C build

      - name: 运行测试
        run: xvfb-run ninja -C build test

      - name: 上传构建产物
        uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: build/

11.5 交叉编译

11.5.1 ARM64 交叉编译

# Dockerfile.cross-arm64
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

# 安装交叉编译工具链
RUN apt-get update && apt-get install -y \
    gcc-aarch64-linux-gnu \
    g++-aarch64-linux-gnu \
    valac \
    meson \
    ninja-build \
    pkg-config \
    && rm -rf /var/lib/apt/lists/*

# 安装 ARM64 架构的库
RUN dpkg --add-architecture arm64 \
    && apt-get update \
    && apt-get install -y \
    libglib2.0-dev:arm64 \
    libgtk-4-dev:arm64 \
    libadwaita-1-dev:arm64 \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

# 创建 Meson 交叉编译文件
RUN cat > /tmp/cross-arm64.ini << 'EOF'
[binaries]
c = 'aarch64-linux-gnu-gcc'
cpp = 'aarch64-linux-gnu-g++'
ar = 'aarch64-linux-gnu-ar'
strip = 'aarch64-linux-gnu-strip'
pkgconfig = 'pkg-config'

[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'aarch64'
endian = 'little'

[properties]
needs_exe_wrapper = true
EOF

# 交叉编译
RUN meson setup build \
    --cross-file /tmp/cross-arm64.ini \
    --prefix=/usr \
    && ninja -C build

CMD ["bash"]

11.5.2 使用 QEMU 运行 ARM64 二进制

# 在 Docker 中运行 ARM64 二进制
docker run --rm -it \
    --platform linux/arm64 \
    ubuntu:24.04 \
    /path/to/myapp

11.5.3 多架构构建(Docker Buildx)

# Dockerfile.multiarch
FROM --platform=$BUILDPLATFORM ubuntu:24.04 AS builder

ENV DEBIAN_FRONTEND=noninteractive

ARG TARGETPLATFORM
ARG TARGETARCH

RUN apt-get update && apt-get install -y \
    valac meson ninja-build pkg-config \
    gcc \
    && rm -rf /var/lib/apt/lists/*

# 根据目标架构安装对应的库
RUN if [ "$TARGETARCH" = "arm64" ]; then \
        dpkg --add-architecture arm64 && \
        apt-get update && \
        apt-get install -y \
            gcc-aarch64-linux-gnu \
            libglib2.0-dev:arm64; \
    else \
        apt-get update && \
        apt-get install -y \
            libglib2.0-dev; \
    fi

WORKDIR /app
COPY . .

RUN meson setup build && ninja -C build

# 运行阶段
FROM ubuntu:24.04
COPY --from=builder /app/build/src/myapp /usr/local/bin/
CMD ["myapp"]
# 构建多架构镜像
docker buildx build \
    --platform linux/amd64,linux/arm64 \
    -t myapp:latest \
    --push .

11.6 GTK 应用容器化

11.6.1 运行 GTK 应用(X11 转发)

# Dockerfile.gtk-app
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    valac \
    libglib2.0-dev \
    libgtk-4-dev \
    libadwaita-1-dev \
    meson \
    ninja-build \
    pkg-config \
    # X11 相关
    libx11-6 \
    libx11-xcb1 \
    libxcb1 \
    libgl1-mesa-glx \
    libegl1-mesa \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

RUN meson setup build && ninja -C build

# 使用 X11 socket
CMD ["./build/src/myapp"]
# 允许 Docker 访问 X11
xhost +local:docker

# 运行 GTK 应用
docker run --rm -it \
    -e DISPLAY=$DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    myapp:latest

# 使用 Wayland
docker run --rm -it \
    -e WAYLAND_DISPLAY=$WAYLAND_DISPLAY \
    -v $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY:/tmp/$WAYLAND_DISPLAY \
    myapp:latest

11.6.2 使用 PipeWire/PulseAudio(音频)

# 运行带音频支持的容器
docker run --rm -it \
    -e PULSE_SERVER=unix:/run/user/1000/pulse/native \
    -v /run/user/1000/pulse:/run/user/1000/pulse \
    -e DISPLAY=$DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix \
    myapp:latest

11.6.3 Flatpak 打包(推荐的桌面应用分发方式)

// com.example.MyApp.json(Flatpak manifest)
{
    "app-id": "com.example.MyApp",
    "runtime": "org.gnome.Platform",
    "runtime-version": "46",
    "sdk": "org.gnome.Sdk",
    "command": "myapp",
    "finish-args": [
        "--share=ipc",
        "--socket=x11",
        "--socket=wayland",
        "--device=dri"
    ],
    "modules": [
        {
            "name": "myapp",
            "buildsystem": "meson",
            "sources": [
                {
                    "type": "git",
                    "url": "https://github.com/example/myapp.git"
                }
            ]
        }
    ]
}
# 构建 Flatpak
flatpak-builder --force-clean build-dir com.example.MyApp.json

# 安装
flatpak-builder --user --install build-dir com.example.MyApp.json

# 运行
flatpak run com.example.MyApp

11.7 测试环境

11.7.1 测试用 Dockerfile

# Dockerfile.test
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    valac \
    libglib2.0-dev \
    libgtk-4-dev \
    libadwaita-1-dev \
    meson \
    ninja-build \
    pkg-config \
    # 测试工具
    xvfb \
    xauth \
    # 代码质量
    valgrind \
    cppcheck \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

# 配置构建(启用测试)
RUN meson setup build \
    -Dtests=true \
    -Db_sanitize=address

# 编译
RUN ninja -C build

# 运行测试(使用 Xvfb 模拟显示)
CMD ["sh", "-c", "xvfb-run ninja -C build test"]

11.7.2 Valgrind 内存检测

# Dockerfile.valgrind
FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
    valac \
    libglib2.0-dev \
    meson \
    ninja-build \
    valgrind \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .

RUN meson setup build \
    -Db_sanitize=none \
    && ninja -C build

# 使用 Valgrind 运行
CMD ["valgrind", "--leak-check=full", "--show-leak-kinds=all", \
     "./build/src/myapp"]

11.7.3 Meson 测试配置

# tests/meson.build
test_basic = executable('test_basic',
  'test_basic.vala',
  dependencies: [mylib_dep],
)

test('basic test', test_basic)

# 如果需要 GUI 测试
if get_option('tests')
  test_gui = executable('test_gui',
    'test_gui.vala',
    dependencies: [gtk4_dep, libadwaita_dep],
  )

  test('gui test',
    find_program('xvfb-run'),
    args: [test_gui.full_path()],
    workdir: meson.current_build_dir(),
  )
endif

11.8 业务场景:完整的 CI/CD 流水线

11.8.1 完整的 GitLab CI 配置

# .gitlab-ci.yml
stages:
  - lint
  - build
  - test
  - package
  - deploy

variables:
  DOCKER_IMAGE: ubuntu:24.04

# 代码检查
lint:
  stage: lint
  image: ${DOCKER_IMAGE}
  before_script:
    - apt-get update && apt-get install -y valac meson ninja-build
  script:
    - meson setup build
    - ninja -C build
    - echo "代码检查通过"
  allow_failure: true

# 多平台构建
.build_template: &build_template
  stage: build
  before_script:
    - apt-get update
    - apt-get install -y valac libglib2.0-dev libgtk-4-dev
      libadwaita-1-dev meson ninja-build pkg-config
  script:
    - meson setup build --prefix=/usr
    - ninja -C build
  artifacts:
    paths:
      - build/

build:ubuntu:
  <<: *build_template
  image: ubuntu:24.04

build:fedora:
  <<: *build_template
  image: fedora:40
  before_script:
    - dnf install -y vala glib2-devel gtk4-devel
      libadwaita-devel meson ninja-build pkgconfig

# 测试
test:
  stage: test
  image: ${DOCKER_IMAGE}
  before_script:
    - apt-get update
    - apt-get install -y valac libglib2.0-dev libgtk-4-dev
      libadwaita-1-dev meson ninja-build pkg-config xvfb
  script:
    - meson setup build --prefix=/usr -Dtests=true
    - ninja -C build
    - xvfb-run ninja -C build test
  dependencies:
    - build:ubuntu

# 打包
package:flatpak:
  stage: package
  image: bilelmoussaoui/flatpak-github-actions:gnome-46
  script:
    - flatpak-builder --force-clean build-dir com.example.MyApp.json
  artifacts:
    paths:
      - build-dir/
  only:
    - tags

# 部署
deploy:pages:
  stage: deploy
  image: alpine:latest
  script:
    - mkdir public
    - cp -r build-dir/repo public/
  artifacts:
    paths:
      - public
  only:
    - main

11.9 注意事项

⚠️ Docker 构建常见问题

  1. X11 权限:运行 GUI 应用需要 xhost +local:docker
  2. Wayland 支持:需要挂载 $XDG_RUNTIME_DIR
  3. 镜像体积:使用多阶段构建减小最终镜像
  4. 缓存:利用 Docker 层缓存,先复制 meson.build 再复制源码
  5. 架构:交叉编译时注意库的架构匹配
  6. 显示测试:使用 xvfb-run 运行需要显示的测试

11.10 扩展阅读

资源链接
Docker 官方文档https://docs.docker.com/
Flatpak 文档https://docs.flatpak.org/
Meson Docker 支持https://mesonbuild.com/Cross-compilation.html
GNOME Flatpak 指南https://wiki.gnome.org/HowDoI/Flatpak
Docker Buildxhttps://docs.docker.com/build/buildx/

11.11 总结

要点说明
基础镜像Ubuntu / Fedora / Alpine
多阶段构建缩小镜像体积
CI/CDGitLab CI / GitHub Actions
交叉编译Meson 交叉编译文件
GTK 容器化X11 转发 + Wayland 支持
分发方式Flatpak(推荐)/ Docker
测试xvfb-run + Valgrind

下一章我们将学习 Vala 的最佳实践。→ 第 12 章:最佳实践