第二章: Docker环境配置和基本概念解释

159 阅读11分钟

快速开始 - 操作流程总览

项目目录结构

/Users/wangjinshan/Documents/writer/
├── android/                           # Android项目文件
├── ios/                               # iOS项目文件  
├── flutter_sdk/                       # Flutter Engine源码(Mac/Docker共享)
├── writer_build_engin_shell/          # 构建脚本和Docker配置
│   ├── docker_shell_build/           # Docker构建文件
│   │   ├── Dockerfile                # Docker镜像定义
│   │   └── scripts/                  # 容器脚本
│   └── md/                           # 文档目录
└── writer-flutter/                    # Flutter应用项目

第一步:安装 Docker Desktop

# 1. 下载并安装 Docker Desktop for Mac
# 从官网下载: https://www.docker.com/products/docker-desktop

# 2. 验证安装
docker --version
docker-compose --version

第二步:创建基础 Docker 镜像(仅提供Linux环境)

# 进入Docker构建目录
cd ~/Documents/writer/writer_build_engin_shell/docker_shell_build

# 创建最简Docker镜像(只有Linux系统)
cat > Dockerfile << 'EOF'
# 明确指定平台为 linux/amd64,提供纯净Linux环境
FROM --platform=linux/amd64 mirrors.tencent.com/ci/tlinux3_ci:latest

# 设置默认工作目录
WORKDIR /writer/flutter_sdk

# 镜像只提供Linux环境,不安装任何工具
CMD ["/bin/bash"]
EOF

第三步:构建 Docker 镜像

# 构建纯净Linux镜像
docker build -t flutter-engine-linux .

# 验证镜像(应该很小,只包含Linux系统)
docker images | grep flutter-engine-linux

第四步:创建 Docker 容器(关键:卷挂载实现文件共享)

容器创建和命名详解

# 第一次创建容器时,需要指定容器名称
# 这里我们创建一个名为 flutter_sync_dev 的容器
docker run -it \
  --name flutter_sync_dev \
  -v /Users/wangjinshan/Documents/writer/flutter_sdk:/writer/flutter_sdk \
  -v /depot_tools:/depot_tools \
  flutter-engine-linux bash

# 解释:
# --name flutter_sync_dev    # 给容器命名为 flutter_sync_dev
# -v 路径1:路径2             # 卷挂载,实现文件共享
# flutter-engine-linux       # 使用的镜像名
# bash                       # 启动后执行的命令

容器状态管理

# 第一次创建后,容器就存在了,可以查看
docker ps -a | grep flutter_sync_dev

# 输出示例:
# CONTAINER ID   IMAGE                  STATUS    NAMES
# abc123def456   flutter-engine-linux   Up        flutter_sync_dev

# 如果容器停止了,可以重新启动
docker start flutter_sync_dev

# 如果容器正在运行,可以直接进入
docker exec -it flutter_sync_dev bash

第一次创建 vs 后续使用

# 第一次创建容器(只需要执行一次)
docker run -it \
  --name flutter_sync_dev \
  -v /Users/wangjinshan/Documents/writer/flutter_sdk:/writer/flutter_sdk \
  -v /depot_tools:/depot_tools \
  flutter-engine-linux bash

# 容器创建后,后续使用只需要:
# 1. 启动容器(如果停止了)
docker start flutter_sync_dev

# 2. 进入容器
docker exec -it flutter_sync_dev bash

容器生命周期管理

# 查看容器状态
docker ps                    # 查看运行中的容器
docker ps -a                 # 查看所有容器(包括停止的)

# 容器操作
docker start flutter_sync_dev     # 启动容器
docker stop flutter_sync_dev      # 停止容器
docker restart flutter_sync_dev   # 重启容器
docker rm flutter_sync_dev        # 删除容器(会丢失容器内的配置)

# 如果容器被删除了,需要重新创建
docker run -it --name flutter_sync_dev \
  -v /Users/wangjinshan/Documents/writer/flutter_sdk:/writer/flutter_sdk \
  -v /depot_tools:/depot_tools \
  flutter-engine-linux bash

第五步:在容器内配置所有开发环境

进入容器的不同方式

# 方式1:创建容器的同时进入(第一次)
docker run -it --name flutter_sync_dev \
  -v /Users/wangjinshan/Documents/writer/flutter_sdk:/writer/flutter_sdk \
  -v /depot_tools:/depot_tools \
  flutter-engine-linux bash

# 方式2:进入已存在的容器(常用)
docker exec -it flutter_sync_dev bash

# 方式3:启动并进入容器(如果容器停止了)
docker start flutter_sync_dev && docker exec -it flutter_sync_dev bash

容器内环境配置

# 现在我们在 flutter_sync_dev 容器内

# 容器内安装所有必需工具
apt-get update && apt-get install -y \
  curl git python3 python3-pip build-essential \
  pkg-config libnss3-dev libatk-bridge2.0-dev

# 配置环境变量(在容器内执行)
export PATH="/depot_tools:$PATH"
export DEPOT_TOOLS_UPDATE=0

# 验证工具(在容器内执行)
which gclient gn ninja

# 检查卷挂载是否成功
ls -la /writer/flutter_sdk    # 应该能看到Mac上的文件

为什么使用 flutter_sync_dev 这个名字?

# 容器命名的考虑:
# flutter_sync_dev = flutter + sync + dev
# flutter:     项目名称
# sync:        主要用途是同步和编译
# dev:         开发环境

# 您也可以使用其他名字,比如:
docker run -it --name flutter-engine-container ...
docker run -it --name my-flutter-dev ...
docker run -it --name flutter-build-env ...

# 重要的是保持一致,后续都用同一个名字

实际使用流程示例

# === 第一天开发 ===
# 1. 创建容器
docker run -it --name flutter_sync_dev \
  -v /Users/wangjinshan/Documents/writer/flutter_sdk:/writer/flutter_sdk \
  -v /depot_tools:/depot_tools \
  flutter-engine-linux bash

# 2. 在容器内配置环境
apt-get update && apt-get install -y curl git python3 build-essential
export PATH="/depot_tools:$PATH"

# 3. 退出容器
exit

# === 第二天开发 ===
# 1. 直接进入已存在的容器
docker exec -it flutter_sync_dev bash

# 2. 环境变量可能需要重新设置
export PATH="/depot_tools:$PATH"
export DEPOT_TOOLS_UPDATE=0

# 3. 开始工作
cd /writer/flutter_sdk
gclient sync

详细说明目录

2.1 Docker核心概念正确理解

Images vs Container 的本质区别

Images(镜像)- 只提供Linux环境

# 镜像是静态的、只读的Linux系统模板
# 我们的设计:镜像只包含纯净的Linux系统,不包含任何开发工具

# 查看镜像(应该很小,只有Linux系统)
docker images flutter-engine-linux
# REPOSITORY            TAG       IMAGE ID       CREATED        SIZE
# flutter-engine-linux  latest    abc123def456   2 hours ago    500MB

Container(容器)- 真正的运行环境

# 容器是镜像的运行实例,是真正的执行环境
# 我们的设计:所有工具、配置、环境变量都在容器中设置

# 查看运行中的容器
docker ps
# CONTAINER ID   IMAGE                  STATUS    NAMES
# def456abc789   flutter-engine-linux   Up        flutter-engine-container

为什么镜像不放工具?

  1. 灵活性: 不同项目可能需要不同版本的工具
  2. 体积: 镜像越小,构建和分发越快
  3. 维护: 工具更新不需要重新构建镜像
  4. 一致性: 通过卷挂载确保Mac和Docker使用完全相同的文件
# 错误的做法(不要这样)
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y \
    git python3 build-essential  # ❌ 镜像变大,不灵活

# 正确的做法
FROM --platform=linux/amd64 mirrors.tencent.com/ci/tlinux3_ci:latest
WORKDIR /writer/flutter_sdk
CMD ["/bin/bash"]  # ✅ 镜像小,纯净Linux环境

2.2 Mac与Docker文件系统共享策略

核心设计思想:同一个内存文件夹

文件系统架构图

┌─────────────────────────────────────────────┐
│                 macOS 宿主机                │
│  /Users/wangjinshan/Documents/writer/       │
│  ├── flutter_sdk/          ←─┐            │
│  ├── writer_build_engin_shell/ │            │
│  └── android/                  │            │
└─────────────────────────────────┼────────────┘
                                  │ Docker卷挂载
┌─────────────────────────────────┼────────────┐
│              Docker 容器        │            │
│  /writer/flutter_sdk/      ←───┘            │
│  ├── .gclient                               │
│  ├── flutter/                               │
│  ├── out/                                   │
│  └── third_party/                           │
└─────────────────────────────────────────────┘

卷挂载详细配置

完整的卷挂载脚本

#!/bin/bash
# 文件名:run_container.sh
# 用途:启动Docker容器并正确挂载卷

set -e

# 项目根目录
WRITER_ROOT="/Users/wangjinshan/Documents/writer"
FLUTTER_SDK_DIR="$WRITER_ROOT/flutter_sdk"
DEPOT_TOOLS_DIR="/depot_tools"

# 容器配置
CONTAINER_NAME="flutter-engine-container"
IMAGE_NAME="flutter-engine-linux"

echo "=== 启动Flutter Engine Docker容器 ==="
echo "项目根目录: $WRITER_ROOT"
echo "Flutter SDK: $FLUTTER_SDK_DIR"

# 检查目录是否存在
if [ ! -d "$FLUTTER_SDK_DIR" ]; then
    echo "错误: Flutter SDK目录不存在: $FLUTTER_SDK_DIR"
    echo "请先完成第一章的环境配置"
    exit 1
fi

# 停止并删除已存在的容器
if docker ps -a | grep -q "$CONTAINER_NAME"; then
    echo "清理已存在的容器..."
    docker stop "$CONTAINER_NAME" 2>/dev/null || true
    docker rm "$CONTAINER_NAME" 2>/dev/null || true
fi

# 启动容器,关键:卷挂载实现文件共享
echo "启动新容器..."
docker run -it \
  --name "$CONTAINER_NAME" \
  --hostname flutter-linux \
  -v "$FLUTTER_SDK_DIR:/writer/flutter_sdk" \
  -v "$DEPOT_TOOLS_DIR:/depot_tools" \
  -v "$WRITER_ROOT/writer_build_engin_shell:/writer/scripts" \
  --workdir="/writer/flutter_sdk" \
  --memory=8g \
  --cpus=4 \
  "$IMAGE_NAME"

echo "容器已启动,文件系统已共享"

文件共享验证

验证Mac和Docker是否真正共享文件

# 在Mac上创建测试文件
echo "Hello from Mac" > ~/Documents/writer/flutter_sdk/mac_test.txt

# 在Docker容器内查看
docker exec flutter-engine-container cat /writer/flutter_sdk/mac_test.txt
# 输出: Hello from Mac

# 在Docker容器内创建文件
docker exec flutter-engine-container sh -c 'echo "Hello from Docker" > /writer/flutter_sdk/docker_test.txt'

# 在Mac上查看
cat ~/Documents/writer/flutter_sdk/docker_test.txt
# 输出: Hello from Docker

2.3 Docker镜像设计原则

最小化镜像设计

我们的Dockerfile(极简设计)

# 文件名:Dockerfile
# 位置:~/Documents/writer/writer_build_engin_shell/docker_shell_build/Dockerfile

# 使用腾讯云镜像,确保平台兼容性
FROM --platform=linux/amd64 mirrors.tencent.com/ci/tlinux3_ci:latest

# 设置基本环境变量
ENV DEBIAN_FRONTEND=noninteractive
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8

# 设置工作目录(与Mac卷挂载路径一致)
WORKDIR /writer/flutter_sdk

# 不安装任何工具,保持镜像纯净
# 所有工具将在容器运行时安装

# 默认启动bash
CMD ["/bin/bash"]

为什么不在镜像中预装工具?

对比说明

# ❌ 错误做法:镜像臃肿,不灵活
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y \
    git curl python3 build-essential \
    pkg-config libnss3-dev
# 问题:
# 1. 镜像体积大(2GB+)
# 2. 工具版本固定,难以更新
# 3. 不同项目可能需要不同工具版本

# ✅ 正确做法:镜像纯净,运行时安装
FROM --platform=linux/amd64 mirrors.tencent.com/ci/tlinux3_ci:latest
WORKDIR /writer/flutter_sdk
CMD ["/bin/bash"]
# 优点:
# 1. 镜像体积小(500MB)
# 2. 工具版本灵活
# 3. 容器启动快

2.4 容器环境配置详解

容器内完整环境配置

一键环境配置脚本

#!/bin/bash
# 文件名:setup_container_env.sh
# 位置:~/Documents/writer/writer_build_engin_shell/docker_shell_build/scripts/
# 用途:在Docker容器内配置所有开发环境

set -e

echo "=== Docker容器环境配置 ==="

# 检查是否在容器内
if [ ! -f /.dockerenv ]; then
    echo "错误: 此脚本只能在Docker容器内运行"
    exit 1
fi

# 更新系统包
echo "1. 更新系统包..."
apt-get update

# 安装基础工具
echo "2. 安装基础开发工具..."
apt-get install -y \
    curl \
    wget \
    git \
    vim \
    unzip \
    python3 \
    python3-pip \
    python3-setuptools

# 安装编译工具链
echo "3. 安装编译工具链..."
apt-get install -y \
    build-essential \
    clang \
    cmake \
    ninja-build

# 安装Flutter Engine特定依赖
echo "4. 安装Flutter Engine依赖..."
apt-get install -y \
    pkg-config \
    libnss3-dev \
    libatk-bridge2.0-dev \
    libdrm-dev \
    libxkbcommon-dev \
    libgtk-3-dev \
    libgles2-mesa-dev

# 清理包缓存
echo "5. 清理缓存..."
apt-get clean
rm -rf /var/lib/apt/lists/*

# 配置depot_tools环境变量
echo "6. 配置depot_tools..."
if [ -d "/depot_tools" ]; then
    echo 'export PATH="/depot_tools:$PATH"' >> ~/.bashrc
    echo 'export DEPOT_TOOLS_UPDATE=0' >> ~/.bashrc
    export PATH="/depot_tools:$PATH"
    export DEPOT_TOOLS_UPDATE=0
    echo "✓ depot_tools 配置完成"
else
    echo "⚠ depot_tools 目录不存在,请检查卷挂载"
fi

# 配置Git
echo "7. 配置Git..."
git config --global user.name "Flutter CI"
git config --global user.email "ci@flutter.dev"
git config --global init.defaultBranch main
git config --global safe.directory '*'

# 验证环境
echo "8. 验证环境..."
echo "Python: $(python3 --version)"
echo "Git: $(git --version)"
if command -v gclient &> /dev/null; then
    echo "gclient: $(gclient --version)"
else
    echo "⚠ gclient 未找到"
fi

echo "=== 容器环境配置完成 ==="

容器内环境配置示例

# 进入容器
docker exec -it flutter-engine-container bash

# 执行环境配置脚本
bash /writer/scripts/setup_container_env.sh

# 手动验证工具安装
which gclient
which gn
which ninja

2.5 Mac与Linux环境切换机制

环境切换的核心问题

为什么需要清理?

  1. 编译产物不兼容: Mac和Linux的编译产物二进制格式不同
  2. 工具版本差异: Mac和Linux可能使用不同版本的构建工具
  3. 路径差异: 绝对路径在不同系统下不同
  4. 权限问题: 文件所有者和权限在两个系统间可能冲突

需要清理的内容详解

清理内容清单

# 1. 编译产物目录
out/                    # 所有编译输出
build/                  # 构建配置文件

# 2. 工具生成的缓存
.gclient_entries        # gclient状态文件
.cipd/                  # CIPD工具缓存
buildtools/             # 构建工具(平台相关)

# 3. Python字节码
**/__pycache__/         # Python缓存文件
**/*.pyc                # Python编译文件

# 4. Git工作区状态
flutter/.git/index      # Git索引文件
third_party/*/.git/index # 第三方仓库索引

# 5. 临时文件
tmp/                    # 临时目录
*.tmp                   # 临时文件
*.log                   # 日志文件

环境切换脚本

智能环境切换脚本

#!/bin/bash
# 文件名:switch_environment.sh
# 位置:~/Documents/writer/writer_build_engin_shell/
# 用途:在Mac和Linux环境间切换时清理不兼容内容

set -e

FLUTTER_SDK_DIR="$HOME/Documents/writer/flutter_sdk"
CURRENT_ENV_FILE="$FLUTTER_SDK_DIR/.current_env"

# 检测当前环境
detect_environment() {
    if [ -f /.dockerenv ]; then
        echo "linux"
    else
        echo "mac"
    fi
}

# 获取上次使用的环境
get_last_environment() {
    if [ -f "$CURRENT_ENV_FILE" ]; then
        cat "$CURRENT_ENV_FILE"
    else
        echo "unknown"
    fi
}

# 清理环境相关文件
clean_environment() {
    local flutter_sdk="$1"
    
    echo "清理环境相关文件..."
    
    # 进入Flutter SDK目录
    cd "$flutter_sdk"
    
    # 清理编译产物
    echo "  清理编译产物..."
    rm -rf out/
    rm -rf build/obj/
    
    # 清理工具缓存
    echo "  清理工具缓存..."
    rm -rf .gclient_entries
    rm -rf .cipd/
    rm -rf buildtools/
    
    # 清理Python缓存
    echo "  清理Python缓存..."
    find . -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
    find . -name "*.pyc" -delete 2>/dev/null || true
    
    # 清理Git索引(保留源码)
    echo "  重置Git工作区..."
    if [ -d "flutter/.git" ]; then
        cd flutter
        git reset --hard HEAD
        git clean -fd
        cd ..
    fi
    
    # 清理第三方依赖(会重新下载)
    find third_party -name ".git" -type d | while read -r git_dir; do
        repo_dir=$(dirname "$git_dir")
        echo "  重置仓库: $repo_dir"
        cd "$repo_dir"
        git reset --hard HEAD 2>/dev/null || true
        git clean -fd 2>/dev/null || true
        cd "$flutter_sdk"
    done
    
    # 清理临时文件
    echo "  清理临时文件..."
    rm -rf tmp/
    find . -name "*.tmp" -delete 2>/dev/null || true
    find . -name "*.log" -delete 2>/dev/null || true
    
    echo "环境清理完成"
}

# 主函数
main() {
    local current_env=$(detect_environment)
    local last_env=$(get_last_environment)
    
    echo "=== Flutter Engine 环境切换 ==="
    echo "当前环境: $current_env"
    echo "上次环境: $last_env"
    
    # 检查是否需要清理
    if [ "$current_env" != "$last_env" ] && [ "$last_env" != "unknown" ]; then
        echo ""
        echo "⚠ 检测到环境切换:$last_env -> $current_env"
        echo "需要清理不兼容的文件..."
        echo ""
        
        read -p "是否继续清理?(y/N): " -n 1 -r
        echo
        
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            clean_environment "$FLUTTER_SDK_DIR"
        else
            echo "跳过清理,但强烈建议清理后再编译"
        fi
    else
        echo "环境未切换,无需清理"
    fi
    
    # 更新环境标记
    echo "$current_env" > "$CURRENT_ENV_FILE"
    
    echo "环境检查完成"
}

main "$@"

自动化环境检查

集成到编译脚本中

#!/bin/bash
# 在compile_engine_linux.sh中添加环境检查

# 在编译开始前检查环境
check_environment_switch() {
    local switch_script="/writer/scripts/switch_environment.sh"
    
    if [ -f "$switch_script" ]; then
        echo "检查环境切换..."
        bash "$switch_script"
    else
        echo "⚠ 环境切换脚本未找到,建议手动清理"
    fi
}

# 在主函数开始处调用
main() {
    check_environment_switch
    
    # ...existing code...
}

2.6 环境清理与重置脚本

完整的清理脚本集合

深度清理脚本

#!/bin/bash
# 文件名:deep_clean.sh
# 用途:深度清理Flutter Engine工作空间

set -e

FLUTTER_SDK_DIR="$HOME/Documents/writer/flutter_sdk"

echo "=== Flutter Engine 深度清理 ==="
echo "目标目录: $FLUTTER_SDK_DIR"

# 确认操作
echo ""
echo "⚠ 警告:此操作将删除所有编译产物和缓存"
echo "⚠ 源码将保留,但需要重新运行 gclient sync"
echo ""
read -p "确认继续?(yes/NO): " -r

if [ "$REPLY" != "yes" ]; then
    echo "操作已取消"
    exit 0
fi

cd "$FLUTTER_SDK_DIR"

# 保留源码,删除其他所有内容
echo "开始深度清理..."

# 备份重要配置
echo "备份配置文件..."
cp .gclient .gclient.backup 2>/dev/null || echo "无.gclient文件需要备份"

# 删除编译相关目录
echo "删除编译产物..."
rm -rf out/
rm -rf build/

# 删除工具和缓存
echo "删除工具缓存..."
rm -rf .gclient_entries
rm -rf .cipd/
rm -rf buildtools/
rm -rf tools/

# 保留flutter目录,但清理其状态
if [ -d "flutter" ]; then
    echo "清理flutter仓库状态..."
    cd flutter
    git reset --hard HEAD
    git clean -fdx
    cd ..
fi

# 删除第三方依赖(会重新下载)
echo "删除第三方依赖..."
rm -rf third_party/

# 恢复配置文件
echo "恢复配置文件..."
if [ -f ".gclient.backup" ]; then
    mv .gclient.backup .gclient
fi

echo ""
echo "=== 深度清理完成 ==="
echo "下一步:"
echo "1. 运行 gclient sync 重新同步依赖"
echo "2. 等待依赖下载完成(可能需要30分钟-2小时)"
echo "3. 重新编译"

快速清理脚本

#!/bin/bash
# 文件名:quick_clean.sh
# 用途:快速清理编译产物,保留依赖

set -e

FLUTTER_SDK_DIR="$HOME/Documents/writer/flutter_sdk"

echo "=== 快速清理编译产物 ==="

cd "$FLUTTER_SDK_DIR"

# 只清理编译产物和缓存
echo "清理编译产物..."
rm -rf out/

echo "清理构建缓存..."
rm -rf build/obj/
rm -rf .gclient_entries

echo "清理Python缓存..."
find . -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
find . -name "*.pyc" -delete 2>/dev/null || true

echo "快速清理完成,可以直接重新编译"

2.7 项目目录结构详解

完整的目录结构说明

/Users/wangjinshan/Documents/writer/
├── android/                                    # Android项目
│   ├── app/
│   │   ├── libs/                              # 自定义engine产物存放处
│   │   │   ├── flutter.jar                   # 自定义Java部分
│   │   │   └── arm64-v8a/
│   │   │       └── libflutter.so              # 自定义native部分
│   │   └── src/main/java/
│   │       └── fix/                           # Android修复代码
│   └── gradle/                                # Gradle配置
│
├── ios/                                        # iOS项目
│   ├── Flutter.framework/                     # iOS自定义engine
│   └── fix/                                   # iOS修复代码
│
├── flutter_sdk/                               # Flutter Engine源码(Mac/Docker共享)
│   ├── .gclient                               # gclient配置文件
│   ├── .current_env                           # 当前环境标记文件
│   ├── flutter/                               # Flutter Engine核心源码
│   │   ├── shell/                             # Shell层代码
│   │   │   └── platform/android/             # Android平台代码
│   │   │       └── io/flutter/plugin/editing/ # 光标问题修复位置
│   │   └── tools/gn                          # 构建配置工具
│   ├── out/                                   # 编译输出目录
│   │   ├── android_release_arm64/             # Android ARM64 Release
│   │   │   ├── flutter.jar                   # Java部分产物
│   │   │   └── libflutter.so                 # Native部分产物
│   │   └── ios_release_arm64/                  # iOS ARM64 Release
│   │       └── Flutter.framework/            # iOS Framework
│   ├── third_party/                          # 第三方依赖
│   │   ├── skia/                             # Skia图形库
│   │   ├── dart/                             # Dart VM
│   │   └── icu/                              # ICU国际化库
│   └── build/                                # 构建系统文件
│
├── writer_build_engin_shell/                   # 构建脚本和配置
│   ├── docker_shell_build/                   # Docker相关文件
│   │   ├── Dockerfile                        # Docker镜像定义
│   │   ├── scripts/                          # 容器内脚本
│   │   │   ├── setup_container_env.sh        # 容器环境配置
│   │   │   ├── container_entry.sh            # 容器入口脚本
│   │   │   └── compile_engine_linux.sh       # Linux编译脚本
│   │   └── docker-compose.yml               # Docker Compose配置
│   ├── compile_engine.sh                     # Mac编译脚本
│   ├── switch_environment.sh                 # 环境切换脚本
│   ├── deep_clean.sh                         # 深度清理脚本
│   ├── quick_clean.sh                        # 快速清理脚本
│   └── md/                                   # 文档目录
│       ├── 01_preparation_work.md            # 第一章
│       ├── 02_docker_environment.md          # 第二章
│       └── readme.md                         # 总目录
│
└── writer-flutter/                            # Flutter应用项目
    ├── lib/                                   # Flutter应用代码
    ├── android/                               # Android部分
    │   └── app/build.gradle                   # 配置使用自定义engine
    ├── ios/                                   # iOS部分
    └── pubspec.yaml                           # Flutter项目配置

目录设计原理

1. 文件共享设计

# Mac和Docker共享同一个flutter_sdk目录
# Mac路径: /Users/wangjinshan/Documents/writer/flutter_sdk
# Docker路径: /writer/flutter_sdk
# 实际上是同一个物理位置,通过Docker卷挂载实现

2. 脚本分离设计

# Mac脚本
writer_build_engin_shell/compile_engine.sh          # Mac环境编译

# Linux脚本  
writer_build_engin_shell/docker_shell_build/scripts/compile_engine_linux.sh      
                echo "错误: 不支持debug模式,请使用 release 或 profile"
                exit 1
            fi
            shift 2
            ;;
        --platform)
            TARGET_PLATFORM="$2"
            shift 2
            ;;
        --cpu)
            TARGET_CPU="$2"
            shift 2
            ;;
        --clean)
            CLEAN_BUILD=true
            shift
            ;;
        --help|-h)
            echo "用法: $0 [选项]"
            echo "选项:"
            echo "  --mode <profile|release>         构建模式"
            echo "  --platform <android|linux>      目标平台"
            echo "  --cpu <arm|arm64|x64>           目标CPU架构"
            echo "  --clean                         清理构建"
            exit 0
            ;;
        *)
            echo "未知参数: $1"
            exit 1
            ;;
    esac
done

# 检查当前目录
if [ ! -f ".gclient" ] || [ ! -d "flutter" ]; then
    echo "错误: 请在Flutter Engine源码根目录下运行此脚本"
    exit 1
fi

# 检查Linux依赖
check_dependencies() {
    echo "检查Linux编译依赖..."
    
    required_packages=("build-essential" "git" "python3" "pkg-config")
    missing_packages=()
    
    for package in "${required_packages[@]}"; do
        if ! dpkg -l | grep -q "^ii  $package "; then
            missing_packages+=("$package")
        fi
    done
    
    if [ ${#missing_packages[@]} -ne 0 ]; then
        echo "缺少以下包: ${missing_packages[*]}"
        echo "请运行: sudo apt install ${missing_packages[*]}"
        exit 1
    fi
}

check_dependencies

echo "构建配置:"
echo "  模式: $TARGET_MODE"
echo "  平台: $TARGET_PLATFORM"
echo "  CPU: $TARGET_CPU"

# 清理构建目录
if [ "$CLEAN_BUILD" = true ]; then
    echo "清理构建目录..."
    rm -rf out/
fi

# 构建输出目录
OUTPUT_DIR="out/${TARGET_PLATFORM}_${TARGET_MODE}_${TARGET_CPU}"

# 配置GN参数
echo "配置构建环境..."
GN_ARGS="--${TARGET_PLATFORM}"

if [ "$TARGET_PLATFORM" = "android" ]; then
    GN_ARGS+=" --android-cpu ${TARGET_CPU}"
elif [ "$TARGET_PLATFORM" = "linux" ]; then
    GN_ARGS+=" --linux-cpu ${TARGET_CPU}"
fi

GN_ARGS+=" --runtime-mode ${TARGET_MODE} --optimized"

# 执行GN配置
echo "执行: ./flutter/tools/gn $GN_ARGS"
./flutter/tools/gn $GN_ARGS

# 获取CPU核心数并检查内存
CORES=$(nproc)
MEMORY_GB=$(free -g | awk '/^Mem:/{print $2}')

if [ "$MEMORY_GB" -lt 8 ]; then
    echo "警告: 系统内存少于8GB,限制并行任务数"
    CORES=$((CORES / 2))
fi

echo "使用 $CORES 个CPU核心进行编译"

# 开始编译
echo "开始编译..."
start_time=$(date +%s)

ninja -C "$OUTPUT_DIR" -j"$CORES"

end_time=$(date +%s)
build_time=$((end_time - start_time))

echo "=== 编译完成 ==="
echo "编译时间: ${build_time}秒"
echo "输出目录: $OUTPUT_DIR"

# 验证构建产物
echo "验证构建产物..."
if [ "$TARGET_PLATFORM" = "android" ]; then
    if [ -f "$OUTPUT_DIR/flutter.jar" ]; then
        echo "✓ flutter.jar 构建成功"
    else
        echo "✗ flutter.jar 构建失败"
    fi
    
    if [ -f "$OUTPUT_DIR/libflutter.so" ]; then
        echo "✓ libflutter.so 构建成功"
    else
        echo "✗ libflutter.so 构建失败"
    fi
fi

echo "Linux编译脚本执行完成"

2.7 Docker网络与端口映射

网络配置基础

查看Docker网络

# 列出所有网络
docker network ls

# 查看默认bridge网络详情
docker network inspect bridge

# 创建自定义网络
docker network create flutter-network

# 删除网络
docker network rm flutter-network

容器网络连接

# 连接容器到网络
docker network connect flutter-network flutter-engine-container

# 断开网络连接
docker network disconnect flutter-network flutter-engine-container

# 创建容器时指定网络
docker run -it --network flutter-network flutter-engine-dev

端口映射配置

虽然Flutter Engine编译通常不需要端口映射,但在某些调试场景下可能需要:

# 映射端口(如果需要Web调试)
docker run -it \
  -p 8080:8080 \
  -p 9090:9090 \
  flutter-engine-dev

# 映射所有端口
docker run -it -P flutter-engine-dev

# 查看端口映射
docker port flutter-engine-container

2.8 蓝盾流水线集成准备

蓝盾流水线概述

蓝盾(BlueKing CI)是腾讯开源的一站式DevOps平台,Flutter Engine的CI/CD流程需要在蓝盾的Docker环境中运行。

蓝盾环境特点:

  • 基于Docker容器运行
  • 使用Ubuntu Linux环境
  • 资源有限(CPU/内存)
  • 网络访问受限
  • 需要优化构建时间

蓝盾专用Docker配置

# 文件名:Dockerfile.blueking
# 用途:蓝盾流水线专用镜像

FROM ubuntu:20.04

# 蓝盾环境变量
ENV DEBIAN_FRONTEND=noninteractive \
    WORKSPACE=/data/landun/workspace \
    BK_CI_BUILD_NUM=${BK_CI_BUILD_NUM} \
    BK_CI_PIPELINE_ID=${BK_CI_PIPELINE_ID}

# 安装基础依赖(精简版)
RUN apt-get update && apt-get install -y \
    curl git python3 python3-pip \
    build-essential pkg-config \
    && rm -rf /var/lib/apt/lists/* \
    && apt-get clean

# 创建工作目录
WORKDIR $WORKSPACE

# 复制必要的脚本
COPY scripts/ /opt/scripts/
RUN chmod +x /opt/scripts/*.sh

# 设置入口点
ENTRYPOINT ["/opt/scripts/blueking_entry.sh"]

蓝盾流水线脚本

#!/bin/bash
# 文件名:blueking_entry.sh
# 用途:蓝盾流水线入口脚本

set -e

echo "=== 蓝盾Flutter Engine构建流水线 ==="

# 环境信息
echo "构建信息:"
echo "  流水线ID: ${BK_CI_PIPELINE_ID}"
echo "  构建号: ${BK_CI_BUILD_NUM}"
echo "  工作空间: ${WORKSPACE}"

# 检查环境
check_environment() {
    echo "检查构建环境..."
    
    # 检查磁盘空间
    available_space=$(df -h $WORKSPACE | tail -1 | awk '{print $4}')
    echo "可用空间: $available_space"
    
    # 检查内存
    memory_info=$(free -h | grep '^Mem:')
    echo "内存信息: $memory_info"
    
    # 检查CPU
    cpu_count=$(nproc)
    echo "CPU核心数: $cpu_count"
}

# 配置depot_tools
setup_depot_tools() {
    echo "配置depot_tools..."
    
    if [ ! -d "/depot_tools" ]; then
        echo "下载depot_tools..."
        git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git /depot_tools
    fi
    
    export PATH="/depot_tools:$PATH"
    export DEPOT_TOOLS_UPDATE=0
}

# 同步源码
sync_source() {
    echo "同步Flutter Engine源码..."
    
    if [ ! -f ".gclient" ]; then
        echo "初始化gclient配置..."
        cat > .gclient << 'EOF'
solutions = [
  {
    "managed": False,
    "name": ".",
    "url": "https://github.com/flutter/flutter.git@3.29.0",
    "custom_deps": {},
    "deps_file": "DEPS",
    "safesync_url": "",
  },
]
EOF
    fi
    
    # 使用较少的并行任务以适应蓝盾环境
    gclient sync --verbose --jobs=2
}

# 执行构建
build_engine() {
    echo "开始构建Flutter Engine..."
    
    # 使用release模式和较少的并行任务
    ./compile_engine_linux.sh \
        --mode release \
        --platform android \
        --cpu arm64
}

# 上传产物
upload_artifacts() {
    echo "准备构建产物..."
    
    OUTPUT_DIR="out/android_release_arm64"
    ARTIFACT_DIR="artifacts"
    
    mkdir -p "$ARTIFACT_DIR"
    
    # 复制关键文件
    if [ -f "$OUTPUT_DIR/flutter.jar" ]; then
        cp "$OUTPUT_DIR/flutter.jar" "$ARTIFACT_DIR/"
        echo "✓ flutter.jar 已复制"
    fi
    
    if [ -f "$OUTPUT_DIR/libflutter.so" ]; then
        cp "$OUTPUT_DIR/libflutter.so" "$ARTIFACT_DIR/"
        echo "✓ libflutter.so 已复制"
    fi
    
    # 创建版本信息文件
    cat > "$ARTIFACT_DIR/build_info.json" << EOF
{
  "pipeline_id": "${BK_CI_PIPELINE_ID}",
  "build_num": "${BK_CI_BUILD_NUM}",
  "build_time": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "git_commit": "$(git rev-parse HEAD)",
  "flutter_version": "3.29.0"
}
EOF
    
    echo "构建产物已准备完成"
}

# 主函数
main() {
    check_environment
    setup_depot_tools
    sync_source
    build_engine
    upload_artifacts
    
    echo "=== 蓝盾构建流水线完成 ==="
}

main "$@"

Docker Compose配置

# 文件名:docker-compose.yml
# 用途:本地测试蓝盾环境

version: '3.8'

services:
  flutter-engine-build:
    build:
      context: .
      dockerfile: Dockerfile.blueking
    container_name: flutter-engine-blueking
    volumes:
      - ./:/workspace
      - flutter-cache:/tmp/flutter-cache
    environment:
      - BK_CI_BUILD_NUM=local-001
      - BK_CI_PIPELINE_ID=test-pipeline
    working_dir: /workspace
    command: /bin/bash
    
  flutter-engine-dev:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: flutter-engine-local
    volumes:
      - ./:/workspace
      - /depot_tools:/depot_tools
    environment:
      - PATH=/depot_tools:$PATH
      - DEPOT_TOOLS_UPDATE=0
    working_dir: /workspace
    tty: true
    stdin_open: true

volumes:
  flutter-cache:
    driver: local

容器管理脚本

#!/bin/bash
# 文件名:docker_manager.sh
# 用途:Docker容器管理脚本

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

# 显示帮助信息
show_help() {
    echo "Flutter Engine Docker 管理脚本"
    echo ""
    echo "用法: $0 <command> [options]"
    echo ""
    echo "命令:"
    echo "  build     构建Docker镜像"
    echo "  run       运行开发容器"
    echo "  test      运行蓝盾测试环境"
    echo "  clean     清理Docker资源"
    echo "  logs      查看容器日志"
    echo "  exec      进入运行中的容器"
    echo ""
    echo "示例:"
    echo "  $0 build               # 构建镜像"
    echo "  $0 run                 # 运行开发容器"
    echo "  $0 test                # 测试蓝盾环境"
    echo "  $0 exec flutter-dev    # 进入开发容器"
}

# 构建镜像
build_images() {
    echo "构建Flutter Engine Docker镜像..."
    
    # 构建开发环境镜像
    docker build -t flutter-engine-dev:latest .
    
    # 构建蓝盾环境镜像
    docker build -f Dockerfile.blueking -t flutter-engine-blueking:latest .
    
    echo "镜像构建完成"
}

# 运行开发容器
run_dev_container() {
    echo "启动Flutter Engine开发容器..."
    
    docker-compose up -d flutter-engine-dev
    docker-compose exec flutter-engine-dev /bin/bash
}

# 测试蓝盾环境
test_blueking_env() {
    echo "测试蓝盾构建环境..."
    
    docker-compose up --build flutter-engine-build
}

# 清理Docker资源
clean_docker() {
    echo "清理Docker资源..."
    
    # 停止所有容器
    docker-compose down
    
    # 删除悬空镜像
    docker image prune -f
    
    # 删除未使用的卷
    docker volume prune -f
    
    echo "清理完成"
}

# 查看日志
show_logs() {
    local service=${1:-flutter-engine-dev}
    docker-compose logs -f "$service"
}

# 进入容器
exec_container() {
    local service=${1:-flutter-engine-dev}
    docker-compose exec "$service" /bin/bash
}

# 主函数
main() {
    case "${1:-help}" in
        build)
            build_images
            ;;
        run)
            run_dev_container
            ;;
        test)
            test_blueking_env
            ;;
        clean)
            clean_docker
            ;;
        logs)
            show_logs "$2"
            ;;
        exec)
            exec_container "$2"
            ;;
        help|--help|-h)
            show_help
            ;;
        *)
            echo "未知命令: $1"
            show_help
            exit 1
            ;;
    esac
}

main "$@"

小结

本章详细介绍了Flutter Engine开发的Docker环境配置:

核心内容回顾:

  1. Docker基础概念: 镜像、容器、数据卷的原理和使用
  2. Dockerfile配置: 从基础到优化的多种配置方案
  3. 镜像管理: 构建、标签、清理等管理操作
  4. 容器运行: 创建、启动、监控容器的完整流程
  5. 数据共享: Volume挂载和文件共享策略
  6. 环境脚本: Linux环境下的编译脚本配置
  7. 蓝盾集成: 为蓝盾流水线定制的Docker配置

关键要点:

  • Docker环境统一了开发、测试、生产环境
  • 使用release模式进行所有构建
  • 合理配置资源限制以适应CI环境
  • 通过数据卷实现Mac和Docker的文件共享
  • 为蓝盾流水线优化了镜像大小和构建时间

准备时间:

  • Docker环境搭建:1-2小时
  • 镜像构建:30-60分钟
  • 容器配置调试:1-2小时

完成本章配置后,即可在统一的Docker环境中进行Flutter Engine开发,为下一章的具体修改工作做好环境准备。