first
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# 打包 C++ 项目为 .deb(自包含:可执行文件 + 运行库,装到 /opt/fpv-relay)
|
||||
# 用法:
|
||||
# ./packaging/build-deb.sh # 仅命令行版
|
||||
# ./packaging/build-deb.sh --with-gui # 含 GUI(需 QT_PREFIX)
|
||||
# 产物: dist/fpv-relay_<版本>_amd64.deb
|
||||
# ============================================================
|
||||
set -e
|
||||
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
WITH_GUI=0
|
||||
[ "$1" = "--with-gui" ] && WITH_GUI=1
|
||||
|
||||
VERSION="$(cd "$REPO" && git describe --tags --always 2>/dev/null | tr -d 'v' || date +%Y%m%d)"
|
||||
[ -z "$VERSION" ] && VERSION="$(date +%Y%m%d)"
|
||||
DEB_VER="${VERSION}-1"
|
||||
PKG="fpv-relay"
|
||||
ARCH="amd64"
|
||||
DIST="${REPO}/dist"
|
||||
ROOT="$(mktemp -d)"
|
||||
APP="${ROOT}/opt/fpv-relay"
|
||||
|
||||
echo "==> 构建(CLI)"
|
||||
cd "${REPO}"
|
||||
cmake --preset cli >/dev/null
|
||||
cmake --build --preset cli -j"$(nproc)" >/dev/null
|
||||
|
||||
if [ "$WITH_GUI" = "1" ]; then
|
||||
: "${QT_PREFIX:?--with-gui 需要设置 QT_PREFIX}"
|
||||
echo "==> 构建(GUI)"
|
||||
cmake --preset gui >/dev/null
|
||||
cmake --build --preset gui -j"$(nproc)" >/dev/null
|
||||
fi
|
||||
|
||||
echo "==> 准备 ${APP}"
|
||||
mkdir -p "${APP}/bin" "${APP}/lib" "${APP}/tools" "${ROOT}/DEBIAN" "${ROOT}/usr/bin"
|
||||
cp "${REPO}/build/fpv-relay" "${APP}/bin/"
|
||||
cp "${REPO}/tools/play-udp.sh" "${APP}/tools/" 2>/dev/null || true
|
||||
cp "${REPO}/config.example.yaml" "${APP}/"
|
||||
cp "${REPO}/README.md" "${APP}/" 2>/dev/null || true
|
||||
|
||||
bundle_libs() {
|
||||
ldd "$1" 2>/dev/null | awk '{print $3}' | grep '^/' | while read -r lib; do
|
||||
base="$(basename "$lib")"
|
||||
case "$base" in
|
||||
libc.so*|libm.so*|libpthread*|librt*|libdl*|libgcc_s*|libstdc++*|ld-linux*|libnsl*|libresolv*) continue ;;
|
||||
libgst*|libglib-2.0*|libgobject-2.0*|libgmodule-2.0*|libgio-2.0*|libgthread-2.0*|liborc-0.4*|libav*|libswscale*|libswresample*|libpostproc*|libx264*|libx265*|libvpx*) continue ;;
|
||||
esac
|
||||
if [ ! -e "${APP}/lib/${base}" ]; then
|
||||
cp -L "$lib" "${APP}/lib/${base}"
|
||||
bundle_libs "$lib"
|
||||
fi
|
||||
done
|
||||
}
|
||||
echo "==> 收集运行库"
|
||||
bundle_libs "${APP}/bin/fpv-relay"
|
||||
|
||||
if [ "$WITH_GUI" = "1" ]; then
|
||||
echo "==> 打包 GUI 与 Qt 运行环境"
|
||||
cp "${REPO}/build-gui/fpv-relay-gui" "${APP}/bin/"
|
||||
bundle_libs "${APP}/bin/fpv-relay-gui"
|
||||
cp -L "${QT_PREFIX}/lib/"libQt6*.so* "${APP}/lib/" 2>/dev/null || true
|
||||
for l in "${APP}"/lib/libQt6*.so*; do
|
||||
[ -e "$l" ] && bundle_libs "$l"
|
||||
done
|
||||
cp -rL "${REPO}/build-gui/FpvRelay" "${APP}/bin/" 2>/dev/null || true
|
||||
mkdir -p "${APP}/plugins/platforms" "${APP}/plugins/xcbglintegrations" "${APP}/qml"
|
||||
for p in libqxcb.so libqminimal.so libqoffscreen.so; do
|
||||
[ -f "${QT_PREFIX}/plugins/platforms/${p}" ] && cp -L "${QT_PREFIX}/plugins/platforms/${p}" "${APP}/plugins/platforms/"
|
||||
done
|
||||
cp -L "${QT_PREFIX}/plugins/xcbglintegrations/"*.so "${APP}/plugins/xcbglintegrations/" 2>/dev/null || true
|
||||
for m in QtQml QtQuick Qt Qt5Compat; do
|
||||
[ -d "${QT_PREFIX}/qml/${m}" ] && cp -rL "${QT_PREFIX}/qml/${m}" "${APP}/qml/"
|
||||
done
|
||||
find "${APP}/qml" "${APP}/plugins" -name "*.so" 2>/dev/null | while read -r so; do
|
||||
bundle_libs "$so"
|
||||
done
|
||||
fi
|
||||
|
||||
cat > "${APP}/run.sh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
export LD_LIBRARY_PATH="$DIR/lib:${LD_LIBRARY_PATH}"
|
||||
exec "$DIR/bin/fpv-relay" "$@"
|
||||
EOF
|
||||
chmod 755 "${APP}/run.sh"
|
||||
|
||||
if [ "$WITH_GUI" = "1" ]; then
|
||||
cat > "${APP}/run-gui.sh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
export LD_LIBRARY_PATH="$DIR/lib:${LD_LIBRARY_PATH}"
|
||||
export QT_PLUGIN_PATH="$DIR/plugins"
|
||||
export QT_QPA_PLATFORM_PLUGIN_PATH="$DIR/plugins/platforms"
|
||||
export QML2_IMPORT_PATH="$DIR/qml"
|
||||
export QML_IMPORT_PATH="$DIR/qml"
|
||||
exec "$DIR/bin/fpv-relay-gui" "$@"
|
||||
EOF
|
||||
chmod 755 "${APP}/run-gui.sh"
|
||||
fi
|
||||
|
||||
# /usr/bin 下的启动器
|
||||
cat > "${ROOT}/usr/bin/fpv-relay" <<'EOF'
|
||||
#!/bin/sh
|
||||
exec /opt/fpv-relay/run.sh "$@"
|
||||
EOF
|
||||
chmod 755 "${ROOT}/usr/bin/fpv-relay"
|
||||
|
||||
if [ "$WITH_GUI" = "1" ]; then
|
||||
cat > "${ROOT}/usr/bin/fpv-relay-gui" <<'EOF'
|
||||
#!/bin/sh
|
||||
exec /opt/fpv-relay/run-gui.sh "$@"
|
||||
EOF
|
||||
chmod 755 "${ROOT}/usr/bin/fpv-relay-gui"
|
||||
fi
|
||||
|
||||
cat > "${ROOT}/DEBIAN/control" <<EOF
|
||||
Package: ${PKG}
|
||||
Version: ${DEB_VER}
|
||||
Architecture: ${ARCH}
|
||||
Maintainer: fpv-relay <noreply@example.com>
|
||||
Section: net
|
||||
Priority: optional
|
||||
Depends: libc6, iw, ffmpeg
|
||||
Description: Multi-input FPV video relay (capture + low-latency forward)
|
||||
RTL8812AU capture (wfb-ng), low-latency UDP forwarding, DVR recording,
|
||||
RTSP output, mavlink uplink/downlink. Self-contained build with bundled
|
||||
runtime libraries under /opt/fpv-relay.
|
||||
EOF
|
||||
|
||||
mkdir -p "${DIST}"
|
||||
dpkg-deb --build --root-owner-group "${ROOT}" "${DIST}/${PKG}_${DEB_VER}_${ARCH}.deb"
|
||||
rm -rf "${ROOT}"
|
||||
echo
|
||||
echo "完成: ${DIST}/${PKG}_${DEB_VER}_${ARCH}.deb"
|
||||
du -sh "${DIST}/${PKG}_${DEB_VER}_${ARCH}.deb"
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# 打包 C++ 项目为 tar.gz(自包含:可执行文件 + 运行库)
|
||||
# 用法:
|
||||
# ./packaging/build-release.sh # 仅命令行版
|
||||
# ./packaging/build-release.sh --with-gui # 含 GUI(需 QT_PREFIX)
|
||||
# 产物: dist/fpv-relay-<版本>-linux-x86_64.tar.gz
|
||||
# ============================================================
|
||||
set -e
|
||||
|
||||
REPO="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
WITH_GUI=0
|
||||
[ "$1" = "--with-gui" ] && WITH_GUI=1
|
||||
|
||||
VERSION="$(cd "$REPO" && git describe --tags --always 2>/dev/null || date +%Y%m%d)"
|
||||
NAME="fpv-relay-${VERSION}-linux-x86_64"
|
||||
DIST="${REPO}/dist"
|
||||
STAGE="${DIST}/${NAME}"
|
||||
LIBDIR="${STAGE}/lib"
|
||||
|
||||
echo "==> 构建(CLI)"
|
||||
cd "${REPO}"
|
||||
cmake --preset cli >/dev/null
|
||||
cmake --build --preset cli -j"$(nproc)" >/dev/null
|
||||
|
||||
if [ "$WITH_GUI" = "1" ]; then
|
||||
: "${QT_PREFIX:?--with-gui 需要设置 QT_PREFIX 指向 Qt 安装目录}"
|
||||
echo "==> 构建(GUI)"
|
||||
cmake --preset gui >/dev/null
|
||||
cmake --build --preset gui -j"$(nproc)" >/dev/null
|
||||
fi
|
||||
|
||||
echo "==> 准备目录 ${STAGE}"
|
||||
rm -rf "${STAGE}"
|
||||
mkdir -p "${STAGE}/bin" "${LIBDIR}" "${STAGE}/tools"
|
||||
|
||||
cp "${REPO}/build/fpv-relay" "${STAGE}/bin/"
|
||||
cp "${REPO}/tools/play-udp.sh" "${STAGE}/tools/" 2>/dev/null || true
|
||||
cp "${REPO}/config.example.yaml" "${STAGE}/"
|
||||
cp "${REPO}/README.md" "${STAGE}/" 2>/dev/null || true
|
||||
|
||||
# 收集并复制非系统共享库(递归)
|
||||
bundle_libs() {
|
||||
ldd "$1" 2>/dev/null | awk '{print $3}' | grep '^/' | while read -r lib; do
|
||||
base="$(basename "$lib")"
|
||||
case "$base" in
|
||||
libc.so*|libm.so*|libpthread*|librt*|libdl*|libgcc_s*|libstdc++*|ld-linux*|libnsl*|libresolv*) continue ;;
|
||||
libgst*|libglib-2.0*|libgobject-2.0*|libgmodule-2.0*|libgio-2.0*|libgthread-2.0*|liborc-0.4*|libav*|libswscale*|libswresample*|libpostproc*|libx264*|libx265*|libvpx*) continue ;;
|
||||
esac
|
||||
if [ ! -e "${LIBDIR}/${base}" ]; then
|
||||
cp -L "$lib" "${LIBDIR}/${base}"
|
||||
bundle_libs "$lib"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
echo "==> 打包命令行版运行库"
|
||||
bundle_libs "${STAGE}/bin/fpv-relay"
|
||||
|
||||
if [ "$WITH_GUI" = "1" ]; then
|
||||
echo "==> 打包 GUI 与 Qt 运行环境"
|
||||
cp "${REPO}/build-gui/fpv-relay-gui" "${STAGE}/bin/"
|
||||
bundle_libs "${STAGE}/bin/fpv-relay-gui"
|
||||
|
||||
# 复制全部 Qt6 运行库(含 QML 插件会用到的),并补齐它们的依赖
|
||||
cp -L "${QT_PREFIX}/lib/"libQt6*.so* "${LIBDIR}/" 2>/dev/null || true
|
||||
for l in "${LIBDIR}"/libQt6*.so*; do
|
||||
[ -e "$l" ] && bundle_libs "$l"
|
||||
done
|
||||
|
||||
# QML 模块的 qmldir(依赖“程序目录”作为导入路径)
|
||||
cp -rL "${REPO}/build-gui/FpvRelay" "${STAGE}/bin/" 2>/dev/null || true
|
||||
|
||||
# Qt 插件(平台/GL/图像格式)
|
||||
mkdir -p "${STAGE}/plugins/platforms" "${STAGE}/plugins/xcbglintegrations"
|
||||
for p in libqxcb.so libqminimal.so libqoffscreen.so; do
|
||||
[ -f "${QT_PREFIX}/plugins/platforms/${p}" ] && cp -L "${QT_PREFIX}/plugins/platforms/${p}" "${STAGE}/plugins/platforms/"
|
||||
done
|
||||
cp -L "${QT_PREFIX}/plugins/xcbglintegrations/"*.so "${STAGE}/plugins/xcbglintegrations/" 2>/dev/null || true
|
||||
|
||||
# 必要 QML 模块
|
||||
mkdir -p "${STAGE}/qml"
|
||||
for m in QtQml QtQuick Qt Qt5Compat; do
|
||||
[ -d "${QT_PREFIX}/qml/${m}" ] && cp -rL "${QT_PREFIX}/qml/${m}" "${STAGE}/qml/"
|
||||
done
|
||||
|
||||
# 打包 QML 插件与 Qt 插件自身的依赖
|
||||
find "${STAGE}/qml" "${STAGE}/plugins" -name "*.so" 2>/dev/null | while read -r so; do
|
||||
bundle_libs "$so"
|
||||
done
|
||||
fi
|
||||
|
||||
cat > "${STAGE}/run.sh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
export LD_LIBRARY_PATH="$DIR/lib:${LD_LIBRARY_PATH}"
|
||||
exec "$DIR/bin/fpv-relay" "$@"
|
||||
EOF
|
||||
chmod +x "${STAGE}/run.sh"
|
||||
|
||||
if [ "$WITH_GUI" = "1" ]; then
|
||||
cat > "${STAGE}/run-gui.sh" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
export LD_LIBRARY_PATH="$DIR/lib:${LD_LIBRARY_PATH}"
|
||||
export QT_PLUGIN_PATH="$DIR/plugins"
|
||||
export QT_QPA_PLATFORM_PLUGIN_PATH="$DIR/plugins/platforms"
|
||||
export QML2_IMPORT_PATH="$DIR/qml"
|
||||
export QML_IMPORT_PATH="$DIR/qml"
|
||||
exec "$DIR/bin/fpv-relay-gui" "$@"
|
||||
EOF
|
||||
chmod +x "${STAGE}/run-gui.sh"
|
||||
fi
|
||||
|
||||
echo "==> 生成 tar.gz"
|
||||
tar -C "${DIST}" -czf "${DIST}/${NAME}.tar.gz" "${NAME}"
|
||||
echo
|
||||
echo "完成: ${DIST}/${NAME}.tar.gz"
|
||||
du -sh "${DIST}/${NAME}.tar.gz"
|
||||
@@ -0,0 +1,92 @@
|
||||
# ============================================================
|
||||
# Windows 自包含打包(MinGW + Qt6 + ffmpeg)
|
||||
# 用法(PowerShell):
|
||||
# ./packaging/build-win.ps1 -QtPrefix "E:\QT6.6.3\6.6.3\mingw_64" `
|
||||
# -MinGwBin "E:\QT6.6.3\Tools\mingw1120_64\bin" `
|
||||
# -Ffmpeg "C:\path\ffmpeg.exe" `
|
||||
# -DepsPrefix "C:\path\deps-prefix" `
|
||||
# -DevourerSrc "C:\path\devourer" # 可选,离线构建用
|
||||
# 产物: dist/fpv-relay-win-x64/ (含 exe + Qt + ffmpeg + 依赖,双击 run-gui.bat 即用)
|
||||
#
|
||||
# 前置:本机已装 libusb-1.0 / libsodium(MinGW) 到 -DepsPrefix(含 include/lib/bin),
|
||||
# 且 pkg-config 可用(devourer 的 CMake 需要)。
|
||||
# ============================================================
|
||||
param(
|
||||
[string]$QtPrefix = $env:QT_PREFIX,
|
||||
[string]$MinGwBin = $env:MINGW_BIN,
|
||||
[string]$Ffmpeg = $env:FFMPEG_EXE,
|
||||
[string]$DepsPrefix = $env:FPV_DEPS_PREFIX,
|
||||
[string]$DevourerSrc = $env:DEVOURER_SRC,
|
||||
[string]$BuildDir = "build-win-gui"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$Repo = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $Repo
|
||||
|
||||
if (-not $QtPrefix) { throw "需要 -QtPrefix(Qt 安装目录,如 E:\QT6.6.3\6.6.3\mingw_64)" }
|
||||
if (-not $MinGwBin) { throw "需要 -MinGwBin(MinGW bin 目录)" }
|
||||
if (-not $Ffmpeg) { throw "需要 -Ffmpeg(静态 ffmpeg.exe 路径)" }
|
||||
if (-not $DepsPrefix) { throw "需要 -DepsPrefix(含 libusb/libsodium 的 include/lib/bin)" }
|
||||
|
||||
$env:PATH = "$MinGwBin;$QtPrefix\bin;$DepsPrefix\bin;$env:PATH"
|
||||
if (-not $env:PKG_CONFIG_PATH) { $env:PKG_CONFIG_PATH = "$DepsPrefix\lib\pkgconfig" }
|
||||
|
||||
# ---- 构建 ----
|
||||
$cmakeArgs = @(
|
||||
"-S", $Repo, "-B", (Join-Path $Repo $BuildDir), "-G", "Ninja",
|
||||
"-DCMAKE_BUILD_TYPE=Release",
|
||||
"-DFPC_BUILD_GUI=ON", "-DFPC_BUILD_WFB_USB=ON", "-DFPC_BUILD_WFB_CAPTURE=OFF",
|
||||
"-DFPC_QT_PREFIX=$QtPrefix"
|
||||
)
|
||||
if ($DevourerSrc) { $cmakeArgs += "-DFETCHCONTENT_SOURCE_DIR_DEVOURER=$DevourerSrc" }
|
||||
cmake @cmakeArgs
|
||||
cmake --build (Join-Path $Repo $BuildDir)
|
||||
|
||||
# ---- 组装 ----
|
||||
$Name = "fpv-relay-win-x64"
|
||||
$Stage = Join-Path $Repo "dist\$Name"
|
||||
Remove-Item -Recurse -Force $Stage -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $Stage | Out-Null
|
||||
|
||||
Copy-Item (Join-Path $Repo "$BuildDir\fpv-relay.exe") $Stage
|
||||
Copy-Item (Join-Path $Repo "$BuildDir\fpv-relay-gui.exe") $Stage
|
||||
Copy-Item $Ffmpeg $Stage
|
||||
Copy-Item (Join-Path $DepsPrefix "bin\libusb-1.0.dll") $Stage
|
||||
Copy-Item (Join-Path $DepsPrefix "bin\libsodium-26.dll") $Stage
|
||||
Copy-Item (Join-Path $Repo "ass\gs.key") $Stage -ErrorAction SilentlyContinue
|
||||
Copy-Item (Join-Path $Repo "config.windows.example.yaml") $Stage
|
||||
Copy-Item (Join-Path $Repo "config.windows.example.yaml") (Join-Path $Stage "config.yaml")
|
||||
|
||||
# Qt 运行库
|
||||
& (Join-Path $QtPrefix "bin\windeployqt.exe") --release --qmldir (Join-Path $Repo "src\gui") (Join-Path $Stage "fpv-relay-gui.exe")
|
||||
Copy-Item (Join-Path $QtPrefix "bin\Qt6ShaderTools.dll") $Stage -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path (Join-Path $Stage "qml") | Out-Null
|
||||
Copy-Item -Recurse -Force (Join-Path $QtPrefix "qml\Qt5Compat") (Join-Path $Stage "qml\Qt5Compat")
|
||||
# FpvRelay QML 模块目录
|
||||
Copy-Item -Recurse -Force (Join-Path $Repo "$BuildDir\FpvRelay") (Join-Path $Stage "FpvRelay")
|
||||
|
||||
# qt.conf:让 Qt 从程序目录找 QML
|
||||
Set-Content -Path (Join-Path $Stage "qt.conf") -Value "[Paths]`nPrefix=.`nQmlImportsPath=qml`n" -Encoding ASCII
|
||||
|
||||
# 启动脚本
|
||||
Set-Content -Path (Join-Path $Stage "run-cli.bat") -Encoding ASCII -Value @"
|
||||
@echo off
|
||||
setlocal
|
||||
set DIR=%~dp0
|
||||
cd /d "%DIR%"
|
||||
set PATH=%DIR%;%PATH%
|
||||
"%DIR%fpv-relay.exe" %*
|
||||
"@
|
||||
Set-Content -Path (Join-Path $Stage "run-gui.bat") -Encoding ASCII -Value @"
|
||||
@echo off
|
||||
setlocal
|
||||
set DIR=%~dp0
|
||||
cd /d "%DIR%"
|
||||
set PATH=%DIR%;%PATH%
|
||||
start "" "%DIR%fpv-relay-gui.exe" %*
|
||||
"@
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "完成: $Stage"
|
||||
Write-Host "用户侧只需先用 Zadig 把网卡驱动换成 WinUSB,然后双击 run-gui.bat。"
|
||||
Reference in New Issue
Block a user