Skip to content

第 15 章:构建、测试与发布——把 C 与 Java 变成一个发行包

scrcpy 的运行时跨桌面和 Android,构建时也跨 Meson/Ninja 与 Gradle/Android SDK。发布工程的目标,是保证 client、server、协议和版本始终锁步。

一、顶层 Meson 是总编排器

顶层 meson.build 声明版本 4.1,并按选项进入:

  • app/:编译桌面 executable;
  • server/:调用 Gradle 构建或复制 prebuilt server。

compile_app / compile_server 可独立关闭,支持只开发一端。版本字符串被注入 C 的 config.h,同时 Gradle versionName 独立声明;发布脚本必须保证两处一致。

二、客户端依赖与条件能力

app/meson.build 的固定依赖:

  • libavformat、libavcodec、libavutil、libswresample;
  • SDL 3.2+。

条件依赖:

  • Linux V4L2 → libavdevice;
  • USB/AOA → libusb 1.0;
  • Windows → mingw32、ws2_32;
  • 平台文件分别来自 sys/unixsys/win

配置阶段还探测 strdup/asprintf/reallocarray/SOCK_CLOEXEC 等能力并生成宏,让源码在 libc/平台差异间选择兼容实现。

三、服务端为何可用 prebuilt

默认 server/meson.build 通过 wrapper 调 Gradle,产出安装名 scrcpy-server。若设置 prebuilt_server,Meson 只复制已有产物,见 server/meson.build

这有两个用途:

  1. 桌面开发者没有 Android SDK 也能编译 client;
  2. 发布流水线可先构建并校验唯一 server artifact,再复用到多平台 client 包,避免每个平台重复产生潜在不同 server。

四、Android 构建的特殊形态

Gradle 配置使用 application plugin、compile/target SDK 36、min SDK 21,并启用 AIDL,见 server/build.gradle

虽然使用 Android application 构建链,运行时不安装/启动 Activity;构建产物的 dex/classes 被 app_process 通过 CLASSPATH 加载。使用 application plugin 主要是获得 Android SDK、AIDL、资源处理和测试生态。

五、客户端测试保护什么

Debug Meson 构建注册 12 个 C 测试,重点覆盖:

  • ADB devices 输出解析;
  • 大端 binary 读写;
  • audio ring buffer;
  • CLI 选项与边界组合;
  • Windows command quoting;
  • 控制消息序列化;
  • 设备消息反序列化;
  • orientation 解析;
  • string/string buffer;
  • vector/vecdeque 容器。

清单见 app/meson.build。这些测试集中在“纯函数 + 协议边界 + 自制基础设施”,因为它们最适合无设备确定性验证。

六、服务端测试保护什么

JVM 测试覆盖:

  • ControlMessageReader 的逐类型解析;
  • DeviceMessageWriter 的二进制输出;
  • codec options;
  • binary / binary search;
  • command/string parsing。

注意两端都有协议测试,但协议 schema 仍是手工同步的。最有价值的扩展是加入 golden vectors:同一字节样本由 C 序列化、Java 解析,反方向同理。

七、Sanitizer 是 C 测试的一部分

release/test_client.sh-Db_sanitize=address,undefined 配置测试构建,再运行 Ninja test。ASan 抓越界/use-after-free,UBSan 抓整数/移位/对齐等未定义行为。

对大量手工所有权、容器宏和二进制解析的 C 项目,sanitizer 不是附加质量项,而是单元测试断言的第二层。脚本入口见 release/test_client.sh

八、CI 的构建矩阵

release workflow 大致分为:

  1. server test;
  2. server Gradle build;
  3. 无 Gradle server build 验证;
  4. Linux client test;
  5. Linux x86_64 build;
  6. Windows 32/64 交叉编译;
  7. macOS arm64/x86_64 build;
  8. 最终打包、校验和与 release。

工作流源文件见 .github/workflows/release.yml。各平台 client 可不同,但应嵌入同一 server 版本。

九、release scripts 为什么分层

release/ 把操作拆为 build_server、build_linux、build_macos、build_windows、package_client、package_server、generate_checksums 等脚本,而不是一个巨大脚本。build_common 统一工作目录和版本环境。

这种分层让 CI 可以单独缓存/上传中间 artifact,也让本地维护者复现某一阶段。最终 release.sh 只编排,不承载每个平台细节。

十、协议改动的发布检查表

修改 wire protocol 时至少同步:

  1. C enum/struct;
  2. C serializer 或 deserializer;
  3. Java mirror enum/model;
  4. Java reader 或 writer;
  5. 双端消息处理分支;
  6. 两端单元测试/golden bytes;
  7. 最大长度与所有权规则;
  8. client/server version;
  9. 文档与兼容说明。

由于没有运行时协议协商,“半升级”不能靠对端优雅识别;发行原子性就是兼容机制。

十一、从源码学习可改进的测试层

现有测试对纯逻辑覆盖良好,但完整系统天然需要更多层:

  • 协议契约测试:跨语言 golden corpus;
  • 虚拟 socket 集成测试:部分读取、粘包、断开、错误长度;
  • 媒体样本测试:config/session/rotation 包序列;
  • 设备矩阵测试:Android API × 厂商 × 编码器;
  • 退出压力测试:在每个初始化阶段注入失败,验证无挂起/泄漏;
  • 长时音频测试:模拟 ppm 时钟漂移与突发 jitter。

这些测试不一定都适合每次 PR,但与源码里的主要风险一一对应。

十二、系列总复盘

15 章之后,scrcpy 可以压缩成五条可迁移原则:

  1. 围绕真实数据形态分层:packet 与 frame 是不同扩展边界;
  2. 低延迟依赖有界状态:最新帧覆盖、消息合并、音频闭环,而非无限排队;
  3. 跨进程协议必须双端对照:连接顺序、字节序、flags 和长度都是契约;
  4. 生命周期是依赖图问题:stop、interrupt、join、destroy 各自有职责;
  5. 把平台不稳定性关进边界层:ADB、sys、wrapper、workaround 不污染核心数据流。

继续阅读可以使用协议速查表定位字节布局,或用源码地图按功能找到入口。

基于 scrcpy 4.1 源码的独立学习笔记