ci: build Windows release package
Some checks failed
Build Windows Release / build-windows (push) Has been cancelled

This commit is contained in:
luochen570
2026-05-29 22:59:55 +08:00
parent 8c9a92f2cb
commit 2a70d6245e
9 changed files with 183 additions and 224 deletions

View File

@@ -1,49 +0,0 @@
name: Build Linux Release
on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install host tools
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
ca-certificates \
curl \
bzip2 \
tar \
file
- name: Build release package
run: |
chmod +x scripts/*.sh
./scripts/package-linux-release.sh
- name: Verify package
run: |
test -s dist-linux/chengnan-linux-x86_64.tar.gz
sha256sum -c dist-linux/chengnan-linux-x86_64.tar.gz.sha256
tar -tzf dist-linux/chengnan-linux-x86_64.tar.gz | grep -E '^chengnan-linux-x86_64/(bin/chengnan|run-chengnan.sh)$'
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: chengnan-linux-x86_64
path: |
dist-linux/chengnan-linux-x86_64.tar.gz
dist-linux/chengnan-linux-x86_64.tar.gz.sha256
if-no-files-found: error

View File

@@ -0,0 +1,49 @@
name: Build Windows Release
on:
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
jobs:
build-windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure MSVC environment
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Build and package Windows app
shell: pwsh
run: |
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
.\scripts\package-windows-release.ps1 -Toolchain msvc
- name: Verify package
shell: pwsh
run: |
if (!(Test-Path .\dist-windows\chengnan-windows-x86_64.zip)) { throw "missing zip" }
if (!(Test-Path .\dist-windows\chengnan-windows-x86_64.zip.sha256)) { throw "missing sha256" }
$zip = Get-Item .\dist-windows\chengnan-windows-x86_64.zip
if ($zip.Length -le 0) { throw "empty zip" }
$hashLine = Get-Content .\dist-windows\chengnan-windows-x86_64.zip.sha256
if ($hashLine -notmatch '^[0-9a-f]{64}\s+chengnan-windows-x86_64\.zip$') { throw "invalid sha256 file" }
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: chengnan-windows-x86_64
path: |
dist-windows/chengnan-windows-x86_64.zip
dist-windows/chengnan-windows-x86_64.zip.sha256
if-no-files-found: error

View File

@@ -8,11 +8,6 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC OFF)
# 本项目目标为 Linux 本地构建。Windows 工具链文件仅作为历史参考,不参与构建。
if (WIN32)
message(FATAL_ERROR "chengnan 当前按 README 要求只支持 Linux 构建,请不要使用 Windows 编译工具链。")
endif()
find_package(Qt6 QUIET COMPONENTS Widgets Concurrent)
if (NOT Qt6_FOUND)
find_package(Qt5 REQUIRED COMPONENTS Widgets Concurrent)
@@ -53,6 +48,10 @@ target_compile_definitions(chengnan PRIVATE
CHENGNAN_APP_VERSION="${PROJECT_VERSION}"
)
target_compile_options(chengnan PRIVATE -Wall -Wextra -Wpedantic)
if (MSVC)
target_compile_options(chengnan PRIVATE /W4 /permissive-)
else()
target_compile_options(chengnan PRIVATE -Wall -Wextra -Wpedantic)
endif()
install(TARGETS chengnan RUNTIME DESTINATION bin)

133
README.md
View File

@@ -6,7 +6,7 @@ chengnan基于 Qt 与 OpenCV 的图像显示与处理系统
## 当前实现状态
本项目已初始化为一个 Qt Widgets + OpenCV + CMake 的 C++ 图像处理应用。当前构建策略已调整为:**在 Linux 中编译,不使用 Windows 编译工具链;所有构建项目所需工具和依赖均采用项目目录内的二进制安装方式,不使用源码编译安装依赖。**
本项目已初始化为一个 Qt Widgets + OpenCV + CMake 的 C++ 图像处理应用。当前发布目标为:**Windows x64 桌面应用**,使用 MSVC、Qt Windows 运行时、OpenCV Windows 预编译 DLL 构建并打包Linux 构建仅作为代码验证用途。
已实现功能:
@@ -24,59 +24,80 @@ chengnan基于 Qt 与 OpenCV 的图像显示与处理系统
- 基于 HSV 均值的简单规则分类
- 使用 `QThread` + 信号槽执行 OpenCV 处理任务,避免阻塞 UI
## Linux 二进制构建原则
## Windows 发布与隔离依赖原则
本项目后续构建以 Linux 环境为准,要求如下:
本项目发布目标为 Windows x64 桌面应用,要求如下:
-Linux 中编译本项目
- 使用 Windows 编译工具链
- 不使用 `cl.exe``windeployqt.exe`、Windows `cmake.exe`、Windows `ninja.exe` 构建本项目
-使用当前系统上的 GCC / G++ 作为项目构建工具链
- 不全局安装 Qt、OpenCV、CMake、Ninja、Python 包等依赖
- 不污染系统 `/usr``/usr/local`、系统 Python site-packages 或全局环境变量
- 所有构建项目所需工具和依赖必须放在项目目录内
- 所有构建项目所需依赖必须优先使用官方或可信来源的二进制包
- 不使用源码编译安装 Qt / OpenCV 等依赖
- Python 使用项目目录内虚拟环境,不使用 uv 安装或管理环境
- 构建脚本只能临时设置当前进程环境变量,不能修改系统环境变量
-Windows x64 + Visual Studio MSVC 环境中构建发布包
- 使用 `cl.exe`、Windows `cmake.exe`、Windows `ninja.exe``windeployqt.exe` 构建和部署
- Qt、OpenCV、CMake、Ninja 等依赖优先使用项目目录内二进制包或 CI 缓存中的可信二进制包
-把 Qt/OpenCV 安装到系统目录,不修改系统全局环境变量
- 发布包必须包含 `chengnan.exe`、Qt 运行时、Qt plugins、OpenCV DLL 和校验文件
- Linux 构建脚本仅用于代码验证,不作为最终发布制品来源
推荐目录约定:
推荐 Windows 依赖目录约定:
```text
chengnan/
├── tools/
│ ├── cmake-linux/ # Linux 版 CMake 二进制
── ninja-linux/ # Linux 版 Ninja 二进制
│ ├── compiler-linux/ # Linux 版二进制编译器工具链,如 LLVM/Clang
│ ├── python-bin/ # Python 二进制包
│ └── python-venv/ # Python 虚拟环境
│ ├── cmake-current-windows/ # Windows 版 CMake
── python-bin/ # 可选:项目内 Python 二进制
├── third_party/
│ ├── qt-linux/ # Linux 版 Qt 二进制包
── opencv-linux/ # Linux 版 OpenCV 二进制
└── build-linux/ # Linux 构建输出目录
│ ├── qt-windows-msvc/ # Windows 版 Qt MSVC
── opencv-windows/ # Windows 版 OpenCV 预编译
│ └── qt-tools/ # Ninja 等 Qt 工具
├── build-windows/ # Windows 构建输出目录
└── dist-windows/ # Windows 发布包输出目录
```
> 注意:如果 OpenCV 没有适配当前 Linux 发行版的官方二进制包,不应退回源码编译安装。应改为寻找可信的预编译包、容器化二进制 SDK、发行版无污染解包方案或更换依赖来源
> 注意:Windows 发布包依赖 Qt 与 OpenCV Windows 预编译二进制。CI Runner 必须能访问这些依赖,或仓库/缓存中已经准备好对应目录
## 编译工具链与版本声明
本项目当前已验证的 Linux 二进制工具链如下,全部位于项目目录内的 `tools/linux-build-env/`,不依赖全局安装的 Qt / OpenCV / CMake / Ninja也不使用 Windows 编译工具链。
本项目发布目标为 Windows x64推荐工具链如下
| 工具 / 依赖 | 当前版本 | 项目内路径 | 说明 |
| --- | --- | --- | --- |
| micromamba | 2.6.2 | `tools/micromamba/bin/micromamba` | 仅用于在项目目录内获取二进制包 |
| CMake | 4.3.3 | `tools/linux-build-env/bin/cmake` | CMake 最低要求 3.21,当前验证版本 4.3.3 |
| Ninja | 1.13.2 | `tools/linux-build-env/bin/ninja` | CMake 使用 `-G Ninja` 生成构建系统 |
| Clang / Clang++ | 22.1.6 | `tools/linux-build-env/bin/clang++` | 本项目指定使用项目内 Clang不使用系统 GCC / G++ |
| Qt | 5.15.15 | `tools/linux-build-env/``third_party/qt-linux` | 使用 Qt Widgets / Qt ConcurrentCMake 也兼容 Qt6 |
| OpenCV | 4.13.0 | `tools/linux-build-env/``third_party/opencv-linux` | 使用 `core``imgproc``imgcodecs` 组件 |
| C++ 标准 | C++17 | `CMakeLists.txt` | `CMAKE_CXX_STANDARD 17` |
| 工具 / 依赖 | 项目内路径 | 说明 |
| --- | --- | --- |
| Visual Studio Build Tools / MSVC | Runner 系统环境 | 提供 `cl.exe`,需通过 x64 Native Tools 或 `msvc-dev-cmd` 初始化 |
| CMake | `tools/cmake-current-windows/bin/cmake.exe` | CMake 最低要求 3.21 |
| Ninja | `third_party/qt-tools/Tools/Ninja/ninja.exe` | CMake 使用 `-G Ninja` 生成构建系统 |
| Qt | `third_party/qt-windows-msvc` | 使用 Qt Widgets / Qt Concurrent部署时调用 `windeployqt.exe` |
| OpenCV | `third_party/opencv-windows/x64/vc16` | 使用 `core``imgproc``imgcodecs` 组件,发布时复制 DLL |
| C++ 标准 | `CMakeLists.txt` | `CMAKE_CXX_STANDARD 17` |
> 说明:`third_party/qt-linux` 和 `third_party/opencv-linux` 是指向 `tools/linux-build-env` 的项目内软链接,方便保持 README 中的依赖目录语义;真正的二进制依赖仍统一存放在 `tools/linux-build-env/`
Linux 二进制工具链仍可用于代码验证,但不作为发布包来源
## 如何构建
### 一键构建
### Windows 发布构建
本项目目标是 Windows 桌面应用。推荐在 Windows x64 + Visual Studio MSVC 环境中构建,仓库内 Windows 依赖约定如下:
```text
third_party/qt-windows-msvc/
third_party/opencv-windows/x64/vc16/
third_party/qt-tools/Tools/Ninja/ninja.exe
tools/cmake-current-windows/bin/cmake.exe
```
在 Windows Runner 或 “x64 Native Tools Command Prompt for VS” 中执行:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\package-windows-release.ps1 -Toolchain msvc
```
生成的发布包:
```text
dist-windows/chengnan-windows-x86_64.zip
dist-windows/chengnan-windows-x86_64.zip.sha256
```
发布包内包含 `chengnan.exe`、Qt 运行时、Qt plugins 和 OpenCV DLL。解压后双击 `chengnan.exe` 运行。
### Linux 本地验证构建
Linux 构建仅用于本地验证 Qt/OpenCV 代码,不作为最终 Windows 发布包。
在 Linux 中进入项目目录执行:
@@ -97,29 +118,6 @@ cd /root/chengnan
build-linux/chengnan
```
### 生成发布包
如需生成可分发的 Linux x86_64 发布包,执行:
```bash
cd /root/chengnan
./scripts/package-linux-release.sh
```
脚本会先执行 Release 构建,再将程序、项目内 Qt/OpenCV 运行时库、Qt 插件和启动脚本打包到:
```text
dist-linux/chengnan-linux-x86_64.tar.gz
dist-linux/chengnan-linux-x86_64.tar.gz.sha256
```
解压后在有 X11 / Wayland 图形会话的 Linux 桌面环境中运行:
```bash
tar -xzf chengnan-linux-x86_64.tar.gz
./chengnan-linux-x86_64/run-chengnan.sh
```
### 分步构建
如果希望先准备依赖,再单独构建:
@@ -194,7 +192,7 @@ tools/cmake.tar.gz
## 当前项目内已有的二进制依赖记录
当前目录中仍保留过 Windows 方案准备的二进制和脚本,作为历史记录和可选参考,但**后续 Linux 构建不应使用这些 Windows 工具链**
当前目录中准备或约定了 Windows 方案所需的二进制依赖和脚本:
```text
third_party/qt-windows-msvc-sdk/
@@ -223,11 +221,11 @@ tools/cmake-current-windows/bin/cmake.exe
tools/python-bin/python.exe
```
这些 Windows 二进制内容不满足“在 Linux 里面编译,不使用 Windows 编译工具链”的新要求,因此 Linux 构建流程不应引用它们。
这些 Windows 二进制内容是发布包构建所需依赖Linux 验证构建不会引用它们。
## Linux 构建目标
## Linux 验证构建说明
后续应补充或替换为 Linux 二进制依赖:
如需在 Linux 上做代码验证,可使用项目内 Linux 二进制依赖:
- Linux 版 CMake 二进制包
- Linux 版 Ninja 二进制包
@@ -385,9 +383,8 @@ export CMAKE_PREFIX_PATH="$PWD/third_party/qt-linux;$PWD/third_party/opencv-linu
要求:
-Linux 中编译本项目
- 不使用 Windows 编译工具链
- 不使用系统 GCC / G++ 作为项目构建工具链
- Windows 发布包Windows x64 + MSVC 环境中构建
- Linux 构建仅用于本地代码验证
- 不使用源码编译安装 Qt / OpenCV 等依赖
- 不建议直接全局安装 Qt 或 OpenCV
- 不建议污染系统 `/usr``/usr/local` 等全局路径
@@ -424,7 +421,7 @@ chengnan/
│ ├── ImageWorker.cpp
│ └── ImageConverter.cpp
├── docs/
│ └── windows/ # 历史 Windows 依赖说明和脚本,仅作参考,不参与 Linux 构建
│ └── windows/ # Windows 依赖说明和构建脚本
├── scripts/
│ ├── setup-linux-binary-deps.sh
│ ├── setup-python-venv-linux.sh
@@ -502,7 +499,7 @@ export LD_LIBRARY_PATH="$PWD/third_party/qt-linux/lib:$PWD/third_party/opencv-li
- 图像显示使用 QWidget 体系,不使用 `cv::imshow`
- 图像处理任务不会明显阻塞 UI
- 依赖安装和构建必须限制在项目目录内,不污染系统环境
- 在 Linux 中编译,不使用 Windows 编译工具链
- Windows 发布包可通过 Windows x64 + MSVC 构建生成
- 所有工具和依赖使用二进制包或解压式安装,不使用源码编译安装
## 注意事项

View File

@@ -1,13 +1,13 @@
param(
[ValidateSet("msvc", "mingw")]
[string]$Toolchain = "msvc",
[string]$BuildDir = "$PSScriptRoot\..\build-windows",
[string]$BuildDir = "$PSScriptRoot\..\..\build-windows",
[switch]$Deploy
)
$ErrorActionPreference = "Stop"
$ProjectRoot = Resolve-Path "$PSScriptRoot\.."
$ProjectRoot = Resolve-Path "$PSScriptRoot\..\.."
$BuildDir = [System.IO.Path]::GetFullPath($BuildDir)
$ThirdParty = Join-Path $ProjectRoot "third_party"
$Tools = Join-Path $ProjectRoot "tools"

View File

@@ -1,5 +1,5 @@
param(
[string]$InstallRoot = "$PSScriptRoot\..\third_party"
[string]$InstallRoot = "$PSScriptRoot\..\..\third_party"
)
$ErrorActionPreference = "Stop"

View File

@@ -1,6 +1,6 @@
param(
[string]$PythonBin = "$PSScriptRoot\..\tools\python-bin",
[string]$VenvDir = "$PSScriptRoot\..\tools\python-venv"
[string]$PythonBin = "$PSScriptRoot\..\..\tools\python-bin",
[string]$VenvDir = "$PSScriptRoot\..\..\tools\python-venv"
)
$ErrorActionPreference = "Stop"

View File

@@ -1,96 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENV_DIR="$ROOT_DIR/tools/linux-build-env"
BUILD_DIR="$ROOT_DIR/build-linux"
DIST_DIR="$ROOT_DIR/dist-linux"
PACKAGE_ROOT="$DIST_DIR/chengnan-linux-x86_64"
APP_BIN="$PACKAGE_ROOT/bin/chengnan"
LIB_DIR="$PACKAGE_ROOT/lib"
PLUGIN_DIR="$PACKAGE_ROOT/plugins"
QT_CONF="$PACKAGE_ROOT/bin/qt.conf"
ARCHIVE="$DIST_DIR/chengnan-linux-x86_64.tar.gz"
"$ROOT_DIR/scripts/build-linux-binary.sh"
rm -rf "$PACKAGE_ROOT" "$ARCHIVE"
mkdir -p "$PACKAGE_ROOT/bin" "$LIB_DIR" "$PLUGIN_DIR"
"$ENV_DIR/bin/cmake" --install "$BUILD_DIR" --prefix "$PACKAGE_ROOT"
if [[ ! -x "$APP_BIN" ]]; then
echo "[package] 未找到可执行文件:$APP_BIN" >&2
exit 1
fi
copy_lib() {
local lib="$1"
if [[ -f "$lib" ]]; then
cp -L -n "$lib" "$LIB_DIR/"
fi
}
# 只打包项目内二进制环境中的依赖,避免把系统 /lib、/usr/lib 复制进制品。
while IFS= read -r lib; do
case "$lib" in
"$ENV_DIR"/*) copy_lib "$lib" ;;
esac
done < <(LD_LIBRARY_PATH="$ENV_DIR/lib:${LD_LIBRARY_PATH:-}" ldd "$APP_BIN" | awk '/=>/ {print $3}' | sort -u)
# Qt GUI 程序运行需要平台插件;同时带上常用图片格式插件,保证 jpg/png 等格式可打开。
for subdir in platforms imageformats xcbglintegrations platformthemes styles iconengines; do
if [[ -d "$ENV_DIR/plugins/$subdir" ]]; then
mkdir -p "$PLUGIN_DIR/$subdir"
cp -L "$ENV_DIR/plugins/$subdir"/*.so "$PLUGIN_DIR/$subdir/" 2>/dev/null || true
fi
done
# 平台插件自身还有二级依赖,继续收集项目内环境中的 .so。
if [[ -d "$PLUGIN_DIR" ]]; then
while IFS= read -r plugin; do
while IFS= read -r lib; do
case "$lib" in
"$ENV_DIR"/*) copy_lib "$lib" ;;
esac
done < <(LD_LIBRARY_PATH="$ENV_DIR/lib:$LIB_DIR:${LD_LIBRARY_PATH:-}" ldd "$plugin" | awk '/=>/ {print $3}' | sort -u || true)
done < <(find "$PLUGIN_DIR" -type f -name '*.so' | sort)
fi
cat > "$QT_CONF" <<'QTCFG'
[Paths]
Plugins = ../plugins
QTCFG
cat > "$PACKAGE_ROOT/run-chengnan.sh" <<'RUNNER'
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export LD_LIBRARY_PATH="$APP_DIR/lib:${LD_LIBRARY_PATH:-}"
export QT_PLUGIN_PATH="$APP_DIR/plugins"
exec "$APP_DIR/bin/chengnan" "$@"
RUNNER
chmod +x "$PACKAGE_ROOT/run-chengnan.sh"
if command -v patchelf >/dev/null 2>&1; then
patchelf --set-rpath '$ORIGIN/../lib' "$APP_BIN" || true
elif [[ -x "$ENV_DIR/bin/patchelf" ]]; then
"$ENV_DIR/bin/patchelf" --set-rpath '$ORIGIN/../lib' "$APP_BIN" || true
else
echo "[package] 未找到 patchelf制品可通过 run-chengnan.sh 设置 LD_LIBRARY_PATH 运行。"
fi
find "$PACKAGE_ROOT" -type f -name '*.so*' -exec chmod 0644 {} +
chmod +x "$APP_BIN"
tar -C "$DIST_DIR" -czf "$ARCHIVE" "$(basename "$PACKAGE_ROOT")"
if [[ ! -s "$ARCHIVE" ]]; then
echo "[package] 打包失败:$ARCHIVE 为空或不存在" >&2
exit 1
fi
sha256sum "$ARCHIVE" > "$ARCHIVE.sha256"
echo "[package] 发布包:$ARCHIVE"
echo "[package] 校验文件:$ARCHIVE.sha256"

View File

@@ -0,0 +1,59 @@
param(
[ValidateSet("msvc")]
[string]$Toolchain = "msvc",
[string]$BuildDir = "$PSScriptRoot\..\build-windows",
[string]$DistDir = "$PSScriptRoot\..\dist-windows"
)
$ErrorActionPreference = "Stop"
$ProjectRoot = Resolve-Path "$PSScriptRoot\.."
$BuildDir = [System.IO.Path]::GetFullPath($BuildDir)
$DistDir = [System.IO.Path]::GetFullPath($DistDir)
$PackageName = "chengnan-windows-x86_64"
$PackageDir = Join-Path $DistDir $PackageName
$ZipPath = Join-Path $DistDir "$PackageName.zip"
$HashPath = "$ZipPath.sha256"
Write-Host "[package] ProjectRoot: $ProjectRoot"
Write-Host "[package] BuildDir: $BuildDir"
Write-Host "[package] DistDir: $DistDir"
& "$ProjectRoot\docs\windows\build-windows.ps1" -Toolchain $Toolchain -BuildDir $BuildDir -Deploy
$DeployDir = Join-Path $BuildDir "deploy"
$Exe = Join-Path $DeployDir "chengnan.exe"
if (!(Test-Path $Exe)) {
throw "未找到部署后的 Windows 可执行文件:$Exe"
}
if (Test-Path $PackageDir) { Remove-Item -Recurse -Force $PackageDir }
if (Test-Path $ZipPath) { Remove-Item -Force $ZipPath }
if (Test-Path $HashPath) { Remove-Item -Force $HashPath }
New-Item -ItemType Directory -Force -Path $DistDir | Out-Null
Copy-Item -Recurse -Force $DeployDir $PackageDir
$ReadmePath = Join-Path $PackageDir "README.txt"
@"
chengnan Windows x86_64
1.
2. chengnan.exe
- Qt OpenCV DLL
- chengnan.exe DLL Qt plugins
"@ | Set-Content -Encoding UTF8 $ReadmePath
Compress-Archive -Path (Join-Path $PackageDir "*") -DestinationPath $ZipPath -Force
$Hash = Get-FileHash -Algorithm SHA256 $ZipPath
"$($Hash.Hash.ToLower()) $(Split-Path -Leaf $ZipPath)" | Set-Content -Encoding ASCII $HashPath
if (!(Test-Path $ZipPath) -or ((Get-Item $ZipPath).Length -le 0)) {
throw "发布包生成失败:$ZipPath"
}
Write-Host "[package] 发布包:$ZipPath"
Write-Host "[package] 校验文件:$HashPath"