e2e 补 TCP echo:ecm/rndis 连设备侧 5000 端口发 5 字节验证原样回显(bash /dev/tcp 零依赖),… #101
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 多平台 CMake 编译 + 测试 | |
| # 触发:push/PR 到 main,也可手动 workflow_dispatch | |
| # 矩阵:Linux(gcc) 8 种 × macOS(clang) 8 种 × Windows(msvc) 4 种选项排列 | |
| name: CMake on multiple platforms | |
| on: | |
| push: | |
| branches: [ "main" ] | |
| # 文档等非代码文件的改动不触发编译 | |
| paths-ignore: | |
| - '**/*.md' | |
| - 'LICENSE' | |
| - '.gitignore' | |
| - '.github/**' | |
| pull_request: | |
| branches: [ "main" ] | |
| paths-ignore: | |
| - '**/*.md' | |
| - 'LICENSE' | |
| - '.gitignore' | |
| - '.github/**' | |
| workflow_dispatch: | |
| jobs: | |
| build: | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # ======== Linux (gcc) : VIRTUAL_DEVICE × LIBUSB × SHARED 全排列 (8) ======== | |
| - { name: linux-gcc vdev+libusb+shared, os: ubuntu-24.04, cpp: g++, vdev: ON, libusb: ON, shared: ON } | |
| - { name: linux-gcc vdev+libusb static, os: ubuntu-24.04, cpp: g++, vdev: ON, libusb: ON, shared: OFF } | |
| - { name: linux-gcc vdev shared, os: ubuntu-24.04, cpp: g++, vdev: ON, libusb: OFF, shared: ON } | |
| - { name: linux-gcc vdev static, os: ubuntu-24.04, cpp: g++, vdev: ON, libusb: OFF, shared: OFF } | |
| - { name: linux-gcc libusb+shared, os: ubuntu-24.04, cpp: g++, vdev: OFF, libusb: ON, shared: ON } | |
| - { name: linux-gcc libusb static, os: ubuntu-24.04, cpp: g++, vdev: OFF, libusb: ON, shared: OFF } | |
| - { name: linux-gcc shared only, os: ubuntu-24.04, cpp: g++, vdev: OFF, libusb: OFF, shared: ON } | |
| - { name: linux-gcc static only, os: ubuntu-24.04, cpp: g++, vdev: OFF, libusb: OFF, shared: OFF } | |
| # ======== macOS (clang) : VIRTUAL_DEVICE × LIBUSB × SHARED 全排列 (8) ======== | |
| - { name: macos-clang vdev+libusb+shared, os: macos-latest, cpp: clang++, vdev: ON, libusb: ON, shared: ON } | |
| - { name: macos-clang vdev+libusb static, os: macos-latest, cpp: clang++, vdev: ON, libusb: ON, shared: OFF } | |
| - { name: macos-clang vdev shared, os: macos-latest, cpp: clang++, vdev: ON, libusb: OFF, shared: ON } | |
| - { name: macos-clang vdev static, os: macos-latest, cpp: clang++, vdev: ON, libusb: OFF, shared: OFF } | |
| - { name: macos-clang libusb+shared, os: macos-latest, cpp: clang++, vdev: OFF, libusb: ON, shared: ON } | |
| - { name: macos-clang libusb static, os: macos-latest, cpp: clang++, vdev: OFF, libusb: ON, shared: OFF } | |
| - { name: macos-clang shared only, os: macos-latest, cpp: clang++, vdev: OFF, libusb: OFF, shared: ON } | |
| - { name: macos-clang static only, os: macos-latest, cpp: clang++, vdev: OFF, libusb: OFF, shared: OFF } | |
| # ======== Windows (msvc) : VIRTUAL_DEVICE × SHARED (libusb 不开,runner 无 libusb) (4) ======== | |
| - { name: win-msvc vdev+shared, os: windows-latest, cpp: cl, vdev: ON, libusb: OFF, shared: ON } | |
| - { name: win-msvc vdev static, os: windows-latest, cpp: cl, vdev: ON, libusb: OFF, shared: OFF } | |
| - { name: win-msvc shared only, os: windows-latest, cpp: cl, vdev: OFF, libusb: OFF, shared: ON } | |
| - { name: win-msvc static only, os: windows-latest, cpp: cl, vdev: OFF, libusb: OFF, shared: OFF } | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # ======== 依赖 ======== | |
| - name: Install dependencies (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libasio-dev libspdlog-dev libgtest-dev libcxxopts-dev libevdev-dev libminiaudio-dev | |
| if [ "${{ matrix.libusb }}" = "ON" ]; then | |
| sudo apt-get install -y libusb-1.0-0-dev | |
| fi | |
| - name: Install dependencies (Windows) | |
| if: runner.os == 'Windows' | |
| run: | | |
| vcpkg install asio spdlog gtest cxxopts miniaudio --triplet x64-windows | |
| - name: Install dependencies (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| brew install asio spdlog libusb cxxopts googletest miniaudio | |
| # ======== CMake 配置 ======== | |
| - name: Configure CMake (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_CXX_COMPILER=${{ matrix.cpp }} \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DUSBIPDCPP_USE_PKGCONF_ASIO=ON \ | |
| -DUSBIPDCPP_BUILD_EXAMPLES=ON \ | |
| -DUSBIPDCPP_BUILD_TESTS=ON \ | |
| -DUSBIPDCPP_BUILD_VIRTUAL_DEVICE=${{ matrix.vdev }} \ | |
| -DUSBIPDCPP_BUILD_LIBUSB_COMPONENTS=${{ matrix.libusb }} \ | |
| -DUSBIPDCPP_BUILD_SHARED_LIBS=${{ matrix.shared }} \ | |
| -S ${{ github.workspace }} | |
| - name: Configure CMake (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| cmake -B build ` | |
| -DCMAKE_CXX_COMPILER=${{ matrix.cpp }} ` | |
| -DCMAKE_BUILD_TYPE=Release ` | |
| -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" ` | |
| -DUSBIPDCPP_BUILD_EXAMPLES=ON ` | |
| -DUSBIPDCPP_BUILD_TESTS=ON ` | |
| -DUSBIPDCPP_BUILD_VIRTUAL_DEVICE=${{ matrix.vdev }} ` | |
| -DUSBIPDCPP_BUILD_LIBUSB_COMPONENTS=${{ matrix.libusb }} ` | |
| -DUSBIPDCPP_BUILD_SHARED_LIBS=${{ matrix.shared }} ` | |
| -S ${{ github.workspace }} | |
| - name: Configure CMake (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| cmake -B build \ | |
| -DCMAKE_CXX_COMPILER=${{ matrix.cpp }} \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_PREFIX_PATH="$(brew --prefix)" \ | |
| -DUSBIPDCPP_USE_PKGCONF_ASIO=ON \ | |
| -DUSBIPDCPP_BUILD_EXAMPLES=ON \ | |
| -DUSBIPDCPP_BUILD_TESTS=ON \ | |
| -DUSBIPDCPP_BUILD_VIRTUAL_DEVICE=${{ matrix.vdev }} \ | |
| -DUSBIPDCPP_BUILD_LIBUSB_COMPONENTS=${{ matrix.libusb }} \ | |
| -DUSBIPDCPP_BUILD_SHARED_LIBS=${{ matrix.shared }} \ | |
| -S ${{ github.workspace }} | |
| # ======== 编译 + 测试 ======== | |
| - name: Build | |
| run: cmake --build build --config Release --parallel | |
| - name: Test | |
| working-directory: build | |
| run: ctest --build-config Release --output-on-failure | |
| # ======== 高频稳定性测试 ======== | |
| # 线程竞争、停止流程等偶发 bug 单次运行无法暴露(本地曾出现第 1303 次 | |
| # 循环才挂起的竞态),因此对曾出过 bug 的生命周期路径重复跑 50 轮直到失败。 | |
| # TestNetworkConnection 是连接/停止/竞态类测试(独立文件,与纯数据结构 | |
| # 测试分开),其中 StopDuringClientConnectRace 是唯一在 stop 时刻有连接 | |
| # 涌入的测试,直接覆盖 acceptor close 与 accept 的竞态(本次 CI 崩溃的 | |
| # 根因),虽在 Windows 上单次约 2 秒(线程/socket 开销),但保留。 | |
| # 注意必须 shell: bash:行尾 \ 续行是 bash 语法,Windows runner 默认 | |
| # pwsh 会把它当字面参数,导致 -R 过滤丢失、全部测试重复 50 轮(曾 | |
| # 造成 Windows job 跑 50 分钟) | |
| - name: Stress test (TestNetworkConnection) | |
| shell: bash | |
| working-directory: build | |
| run: | | |
| # tee 保持实时日志输出;失败时在 runner 上就地符号化崩溃栈 | |
| # (二进制还在,tools/addr2line_crash.sh 解析日志里的偏移), | |
| # 否则地址离开 runner 后永远无法解析 | |
| set +e | |
| ctest --build-config Release --repeat until-fail:50 --timeout 120 --output-on-failure \ | |
| -R "TestNetworkConnection\.(ServerCanRestartAfterStop|ManyQuickConnections|StopWithMultipleClients|StopDuringClientConnectRace)" \ | |
| 2>&1 | tee /tmp/stress.log | |
| status=${PIPESTATUS[0]} | |
| set -e | |
| if [ $status -ne 0 ]; then | |
| echo "=== 符号化崩溃栈 ===" | |
| bash ${{ github.workspace }}/tools/addr2line_crash.sh /tmp/stress.log || true | |
| exit $status | |
| fi | |
| - name: Stress test (TestNetworkVdev) | |
| if: matrix.vdev == 'ON' | |
| shell: bash | |
| working-directory: build | |
| run: | | |
| set +e | |
| ctest --build-config Release --repeat until-fail:50 --timeout 120 --output-on-failure \ | |
| -R "TestNetworkVdev\.(ServerCanStopWithImportedDevice|ClientDisconnectsDuringUrbTransfer)" \ | |
| 2>&1 | tee /tmp/stress_vdev.log | |
| status=${PIPESTATUS[0]} | |
| set -e | |
| if [ $status -ne 0 ]; then | |
| echo "=== 符号化崩溃栈 ===" | |
| bash ${{ github.workspace }}/tools/addr2line_crash.sh /tmp/stress_vdev.log || true | |
| exit $status | |
| fi | |
| # Termux (Android) 编译验证。 | |
| # Termux rootfs 无 glibc,actions 的 node 程序(含 actions/checkout)无法在容器内运行, | |
| # 因此 checkout 在 host 执行,构建挂载进 termux-docker 镜像进行。 | |
| # 镜像 entrypoint 自动从 root 切到容器用户(1000)(pkg 拒绝 root); | |
| # workspace 挂载属主是 host 的 runner(1001),容器用户不可写, | |
| # 构建目录放容器 HOME,workspace 仅作只读源码。 | |
| # cxxopts 不在 Termux 仓库,按 README 指南手动安装(其测试依赖 Android 系统库 | |
| # liblog 而镜像无 Android 运行时,需禁用);miniaudio 也没有, | |
| # mock_audio 的 --audio 选项(AudioFileSource)自动禁用。 | |
| termux-build: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build and test in Termux container | |
| run: | | |
| cat > /tmp/termux_ci.sh <<'EOF' | |
| #!/bin/bash | |
| set -e | |
| pkg update -y | |
| pkg install -y bash git clang cmake ninja pkg-config libasio libspdlog googletest libusb | |
| git clone --depth 1 https://github.com/jarro2783/cxxopts.git ~/cxxopts | |
| cd ~/cxxopts | |
| cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ | |
| -DCXXOPTS_BUILD_TESTS=OFF -DCXXOPTS_BUILD_EXAMPLES=OFF \ | |
| -DCMAKE_INSTALL_PREFIX="$PREFIX/opt/cxxopts" | |
| cmake --build build | |
| cmake --install build | |
| cd /src | |
| cmake -B ~/build-usbipdcpp -G Ninja \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DUSBIPDCPP_USE_PKGCONF_ASIO=ON \ | |
| -Dcxxopts_DIR="$PREFIX/opt/cxxopts/share/cmake/cxxopts" \ | |
| -DUSBIPDCPP_BUILD_EXAMPLES=ON \ | |
| -DUSBIPDCPP_BUILD_TESTS=ON \ | |
| -DUSBIPDCPP_BUILD_VIRTUAL_DEVICE=ON \ | |
| -DUSBIPDCPP_BUILD_LIBUSB_COMPONENTS=ON | |
| cmake --build ~/build-usbipdcpp | |
| cd ~/build-usbipdcpp && ctest | |
| EOF | |
| docker run --rm \ | |
| -v "$GITHUB_WORKSPACE:/src" \ | |
| -v /tmp/termux_ci.sh:/ci.sh \ | |
| termux/termux-docker:latest \ | |
| bash /ci.sh |