2026-04-10 20:02:57 +08:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
|
|
|
|
|
|
# 通用打包函数(由 project_pkg_desktop.sh 和 project_pkg_service.sh 引用)
|
|
|
|
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
|
REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
|
|
|
|
|
|
|
|
|
|
source "${SCRIPT_DIR}/project_registry.sh"
|
|
|
|
|
|
|
|
|
|
|
|
# ===== 打包常量 =====
|
|
|
|
|
|
PKG_ARCH="arm64"
|
|
|
|
|
|
QT_PKG_PATH="/opt/firefly_qt5.15_arm64_20.04"
|
|
|
|
|
|
QT_LIB_PATH="/opt/sysroot/firefly-arm64-sysroot-20.04/lib/aarch64-linux-gnu"
|
|
|
|
|
|
DEFAULT_OPENCV_PROFILE="opencv320"
|
|
|
|
|
|
OPENCV320_LIB_GLOB="SDK/OpenCV320/Arm/aarch64/libopencv_*.so*"
|
|
|
|
|
|
OPENCV480_LIB_GLOB="SDK/OpenCV480/Arm/aarch64/lib/libopencv_*.so*"
|
2026-06-26 17:49:32 +08:00
|
|
|
|
OPENCV450_LIB_DIR_CANDIDATES=(
|
|
|
|
|
|
"AppAlgo/stereo_bolt_delivery/lib/opencv"
|
|
|
|
|
|
"AppAlgo/stereo_bolt_delivery/lib"
|
|
|
|
|
|
"SDK/OpenCV450/Arm/aarch64/lib"
|
|
|
|
|
|
"SDK/OpenCV45D/Arm/aarch64/lib"
|
|
|
|
|
|
"SDK/OpenCV454/Arm/aarch64/lib"
|
|
|
|
|
|
"SDK/OpenCV45/Arm/aarch64/lib"
|
|
|
|
|
|
)
|
|
|
|
|
|
OPENCV450_REQUIRED_LIBS=(
|
|
|
|
|
|
"libopencv_calib3d.so.4.5"
|
|
|
|
|
|
"libopencv_imgcodecs.so.4.5"
|
|
|
|
|
|
"libopencv_dnn.so.4.5"
|
|
|
|
|
|
"libopencv_imgproc.so.4.5"
|
|
|
|
|
|
"libopencv_core.so.4.5"
|
|
|
|
|
|
)
|
2026-04-10 20:02:57 +08:00
|
|
|
|
FIXED_PACKAGE_MAINTAINER="visionrobot"
|
|
|
|
|
|
FIXED_QT_REMOVE_WEBENGINE=true
|
|
|
|
|
|
FIXED_QT_REMOVE_NFC=true
|
|
|
|
|
|
|
|
|
|
|
|
# ===== postinst/postrm 中复用的代码片段 =====
|
|
|
|
|
|
DETECT_CURRENT_USER_SNIPPET=$(cat <<'_SNIPPET_'
|
|
|
|
|
|
if [ -n "${SUDO_USER:-}" ]; then
|
|
|
|
|
|
current_user="${SUDO_USER}"
|
|
|
|
|
|
current_home=$(getent passwd "${current_user}" | cut -d: -f6)
|
|
|
|
|
|
else
|
|
|
|
|
|
current_user=$(whoami)
|
|
|
|
|
|
current_home="${HOME}"
|
|
|
|
|
|
fi
|
|
|
|
|
|
_SNIPPET_
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
RESOLVE_DESKTOP_DIR_SNIPPET=$(cat <<'_SNIPPET_'
|
|
|
|
|
|
resolve_desktop_dir() {
|
|
|
|
|
|
local home_dir=$1
|
|
|
|
|
|
local candidate=""
|
|
|
|
|
|
|
|
|
|
|
|
if command -v xdg-user-dir >/dev/null 2>&1; then
|
|
|
|
|
|
candidate=$(xdg-user-dir DESKTOP 2>/dev/null || true)
|
|
|
|
|
|
if [ -n "${candidate}" ] && [ -d "${candidate}" ]; then
|
|
|
|
|
|
printf '%s\n' "${candidate}"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ -f "${home_dir}/.config/user-dirs.dirs" ]; then
|
|
|
|
|
|
candidate=$(grep '^XDG_DESKTOP_DIR=' "${home_dir}/.config/user-dirs.dirs" | head -n 1 | cut -d= -f2- | tr -d '"')
|
|
|
|
|
|
candidate=${candidate//\$HOME/${home_dir}}
|
|
|
|
|
|
if [ -n "${candidate}" ] && [ -d "${candidate}" ]; then
|
|
|
|
|
|
printf '%s\n' "${candidate}"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
for dir_name in Desktop $'\346\241\214\351\235\242' desktop; do
|
|
|
|
|
|
candidate="${home_dir}/${dir_name}"
|
|
|
|
|
|
if [ -d "${candidate}" ]; then
|
|
|
|
|
|
printf '%s\n' "${candidate}"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
return 1
|
|
|
|
|
|
}
|
|
|
|
|
|
_SNIPPET_
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# ===== 通用函数 =====
|
|
|
|
|
|
|
|
|
|
|
|
log_info() {
|
|
|
|
|
|
printf '[项目打包] %s\n' "$*"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
show_pkg_help() {
|
|
|
|
|
|
local kind=$1
|
|
|
|
|
|
local script_name="project_pkg_${kind}.sh"
|
|
|
|
|
|
|
|
|
|
|
|
cat <<EOF
|
|
|
|
|
|
用法:
|
|
|
|
|
|
./${script_name} <ProjectName>
|
|
|
|
|
|
./${script_name} --list
|
|
|
|
|
|
|
|
|
|
|
|
可打包项目:
|
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
|
|
|
|
list_projects_by_kind "${kind}" | sed 's/^/ - /'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
copy_glob_items() {
|
|
|
|
|
|
local src_pattern=$1
|
|
|
|
|
|
local dest_rel=$2
|
|
|
|
|
|
local copy_mode=$3
|
|
|
|
|
|
local required=$4
|
|
|
|
|
|
local matches=()
|
|
|
|
|
|
|
|
|
|
|
|
shopt -s nullglob
|
|
|
|
|
|
matches=( ${REPO_DIR}/${src_pattern} )
|
|
|
|
|
|
shopt -u nullglob
|
|
|
|
|
|
|
|
|
|
|
|
if [ ${#matches[@]} -eq 0 ]; then
|
|
|
|
|
|
if [ "$required" = true ]; then
|
|
|
|
|
|
echo "缺少文件: ${src_pattern}" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/${dest_rel}"
|
|
|
|
|
|
|
|
|
|
|
|
case "$copy_mode" in
|
|
|
|
|
|
globa)
|
|
|
|
|
|
cp -a "${matches[@]}" "${PKG_PATH}/${dest_rel}/"
|
|
|
|
|
|
;;
|
|
|
|
|
|
glob)
|
|
|
|
|
|
cp "${matches[@]}" "${PKG_PATH}/${dest_rel}/"
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
echo "不支持的 glob 复制模式: ${copy_mode}" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
copy_file_item() {
|
|
|
|
|
|
local src_rel=$1
|
|
|
|
|
|
local dest_rel=$2
|
|
|
|
|
|
local required=$3
|
|
|
|
|
|
local src_abs="${REPO_DIR}/${src_rel}"
|
|
|
|
|
|
|
|
|
|
|
|
if [ ! -f "${src_abs}" ]; then
|
|
|
|
|
|
if [ "$required" = true ]; then
|
|
|
|
|
|
echo "缺少文件: ${src_rel}" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
mkdir -p "$(dirname "${PKG_PATH}/${dest_rel}")"
|
|
|
|
|
|
cp "${src_abs}" "${PKG_PATH}/${dest_rel}"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
copy_directory_item() {
|
|
|
|
|
|
local src_rel=$1
|
|
|
|
|
|
local dest_rel=$2
|
|
|
|
|
|
local required=$3
|
|
|
|
|
|
local src_abs="${REPO_DIR}/${src_rel}"
|
|
|
|
|
|
|
|
|
|
|
|
if [ ! -d "${src_abs}" ]; then
|
|
|
|
|
|
if [ "$required" = true ]; then
|
|
|
|
|
|
echo "缺少目录: ${src_rel}" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/${dest_rel}"
|
|
|
|
|
|
cp -a "${src_abs}/." "${PKG_PATH}/${dest_rel}/"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
copy_from_spec() {
|
|
|
|
|
|
local required=$1
|
|
|
|
|
|
local spec=$2
|
|
|
|
|
|
local kind=""
|
|
|
|
|
|
local src=""
|
|
|
|
|
|
local dest=""
|
|
|
|
|
|
|
|
|
|
|
|
IFS='|' read -r kind src dest <<< "${spec}"
|
|
|
|
|
|
|
|
|
|
|
|
case "$kind" in
|
|
|
|
|
|
glob|globa)
|
|
|
|
|
|
copy_glob_items "${src}" "${dest}" "${kind}" "${required}"
|
|
|
|
|
|
;;
|
|
|
|
|
|
file)
|
|
|
|
|
|
copy_file_item "${src}" "${dest}" "${required}"
|
|
|
|
|
|
;;
|
|
|
|
|
|
dir)
|
|
|
|
|
|
copy_directory_item "${src}" "${dest}" "${required}"
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
echo "不支持的复制规则: ${spec}" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
process_specs() {
|
|
|
|
|
|
local required=$1
|
|
|
|
|
|
shift
|
|
|
|
|
|
|
|
|
|
|
|
local spec=""
|
|
|
|
|
|
for spec in "$@"; do
|
|
|
|
|
|
copy_from_spec "${required}" "${spec}"
|
|
|
|
|
|
done
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 17:49:32 +08:00
|
|
|
|
resolve_package_source_path() {
|
|
|
|
|
|
local src=$1
|
|
|
|
|
|
|
|
|
|
|
|
if [[ "${src}" = /* ]]; then
|
|
|
|
|
|
printf '%s\n' "${src}"
|
|
|
|
|
|
else
|
|
|
|
|
|
printf '%s/%s\n' "${REPO_DIR}" "${src}"
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 20:02:57 +08:00
|
|
|
|
apply_package_defaults() {
|
|
|
|
|
|
PACKAGE_SLUG=$(printf '%s' "${PACKAGE_NAME}" | tr '[:upper:]' '[:lower:]')
|
|
|
|
|
|
PACKAGE_SECTION="${PACKAGE_SLUG}app"
|
|
|
|
|
|
[ "${PACKAGE_KIND}" = "service" ] && PACKAGE_SECTION="${PACKAGE_SLUG}"
|
|
|
|
|
|
PACKAGE_MAINTAINER="${FIXED_PACKAGE_MAINTAINER}"
|
|
|
|
|
|
: "${PACKAGE_DESCRIPTION:=${PACKAGE_NAME} application}"
|
|
|
|
|
|
: "${PACKAGE_COMMENT:=${PACKAGE_NAME} App}"
|
|
|
|
|
|
: "${PACKAGE_OPENCV_PROFILE:=${DEFAULT_OPENCV_PROFILE}}"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
prepare_package_layout() {
|
|
|
|
|
|
rm -rf "${PKG_PATH}"
|
|
|
|
|
|
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/DEBIAN"
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/etc/profile.d"
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/opt/sysroot/lib"
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/usr/lib"
|
|
|
|
|
|
|
|
|
|
|
|
if [ "${PACKAGE_KIND}" = "desktop" ]; then
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/etc/xdg/autostart"
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/usr/local/bin"
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/usr/share/applications"
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/usr/share/pixmaps"
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
copy_qt_runtime() {
|
|
|
|
|
|
cp -rfd "${QT_PKG_PATH}/ext" "${PKG_PATH}/opt/firefly_qt5.15"
|
|
|
|
|
|
cp "${QT_PKG_PATH}/target_qtEnv.sh" "${PKG_PATH}/etc/profile.d/"
|
|
|
|
|
|
|
|
|
|
|
|
local libfile=""
|
|
|
|
|
|
local filename=""
|
|
|
|
|
|
for libfile in "${QT_LIB_PATH}"/*.so*; do
|
|
|
|
|
|
filename=$(basename "${libfile}")
|
|
|
|
|
|
if [[ "${filename}" == *icu* ]]; then
|
|
|
|
|
|
cp -rfd "${libfile}" "${PKG_PATH}/opt/sysroot/lib/"
|
|
|
|
|
|
fi
|
|
|
|
|
|
done
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cleanup_qt_runtime() {
|
|
|
|
|
|
if [ "${FIXED_QT_REMOVE_WEBENGINE}" = true ]; then
|
|
|
|
|
|
rm -f "${PKG_PATH}/opt/firefly_qt5.15/lib/libQt5WebEngine"*
|
|
|
|
|
|
rm -rf "${PKG_PATH}/opt/firefly_qt5.15/libexec/QtWebEngineProcess"
|
|
|
|
|
|
rm -rf "${PKG_PATH}/opt/firefly_qt5.15/resources/qtwebengine"*
|
|
|
|
|
|
rm -rf "${PKG_PATH}/opt/firefly_qt5.15/translations/qtwebengine"*
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ "${FIXED_QT_REMOVE_NFC}" = true ]; then
|
|
|
|
|
|
rm -rf "${PKG_PATH}/opt/firefly_qt5.15/translations/Nfc"*
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
copy_opencv_runtime() {
|
|
|
|
|
|
local src_pattern=""
|
|
|
|
|
|
|
|
|
|
|
|
case "${PACKAGE_OPENCV_PROFILE}" in
|
|
|
|
|
|
opencv320)
|
|
|
|
|
|
src_pattern="${OPENCV320_LIB_GLOB}"
|
|
|
|
|
|
;;
|
|
|
|
|
|
opencv480)
|
|
|
|
|
|
src_pattern="${OPENCV480_LIB_GLOB}"
|
|
|
|
|
|
;;
|
2026-06-26 17:49:32 +08:00
|
|
|
|
opencv450|opencv45d)
|
|
|
|
|
|
copy_opencv450_runtime
|
|
|
|
|
|
return 0
|
|
|
|
|
|
;;
|
2026-04-10 20:02:57 +08:00
|
|
|
|
none)
|
|
|
|
|
|
return 0
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
echo "不支持的 OpenCV 配置: ${PACKAGE_OPENCV_PROFILE}" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
|
|
log_info "复制 OpenCV 运行库 (${PACKAGE_OPENCV_PROFILE})"
|
|
|
|
|
|
copy_glob_items "${src_pattern}" "usr/lib" "globa" true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-26 17:49:32 +08:00
|
|
|
|
copy_opencv450_runtime() {
|
|
|
|
|
|
local candidates=()
|
|
|
|
|
|
local candidate=""
|
|
|
|
|
|
local candidate_abs=""
|
|
|
|
|
|
local selected_dir=""
|
|
|
|
|
|
local lib=""
|
|
|
|
|
|
local missing=false
|
|
|
|
|
|
|
|
|
|
|
|
if [ -n "${DRONESCREW_OPENCV450_LIB_DIR:-}" ]; then
|
|
|
|
|
|
candidates+=("${DRONESCREW_OPENCV450_LIB_DIR}")
|
|
|
|
|
|
fi
|
|
|
|
|
|
candidates+=("${OPENCV450_LIB_DIR_CANDIDATES[@]}")
|
|
|
|
|
|
|
|
|
|
|
|
for candidate in "${candidates[@]}"; do
|
|
|
|
|
|
candidate_abs="$(resolve_package_source_path "${candidate}")"
|
|
|
|
|
|
[ -d "${candidate_abs}" ] || continue
|
|
|
|
|
|
|
|
|
|
|
|
missing=false
|
|
|
|
|
|
for lib in "${OPENCV450_REQUIRED_LIBS[@]}"; do
|
|
|
|
|
|
if [ ! -e "${candidate_abs}/${lib}" ]; then
|
|
|
|
|
|
missing=true
|
|
|
|
|
|
break
|
|
|
|
|
|
fi
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
if [ "${missing}" = false ]; then
|
|
|
|
|
|
selected_dir="${candidate_abs}"
|
|
|
|
|
|
break
|
|
|
|
|
|
fi
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "${selected_dir}" ]; then
|
|
|
|
|
|
{
|
|
|
|
|
|
echo "Missing OpenCV 4.5.0 runtime for ${PACKAGE_NAME}."
|
|
|
|
|
|
echo "libstereo_bolt.so requires these exact sonames:"
|
|
|
|
|
|
printf ' %s\n' "${OPENCV450_REQUIRED_LIBS[@]}"
|
|
|
|
|
|
echo "Put them in AppAlgo/stereo_bolt_delivery/lib/opencv,"
|
|
|
|
|
|
echo "or set DRONESCREW_OPENCV450_LIB_DIR to the directory that contains them."
|
|
|
|
|
|
} >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
log_info "Copy OpenCV runtime (opencv450) from ${selected_dir}"
|
|
|
|
|
|
mkdir -p "${PKG_PATH}/usr/lib"
|
|
|
|
|
|
|
|
|
|
|
|
shopt -s nullglob
|
|
|
|
|
|
local opencv_libs=( "${selected_dir}"/libopencv_*.so.4.5* )
|
|
|
|
|
|
shopt -u nullglob
|
|
|
|
|
|
|
|
|
|
|
|
for lib in "${opencv_libs[@]}"; do
|
|
|
|
|
|
cp -a "${lib}" "${PKG_PATH}/usr/lib/"
|
|
|
|
|
|
done
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 20:02:57 +08:00
|
|
|
|
copy_required_files() {
|
|
|
|
|
|
copy_opencv_runtime
|
|
|
|
|
|
process_specs true "${PACKAGE_COPY_SPECS[@]}"
|
|
|
|
|
|
process_specs false "${PACKAGE_OPTIONAL_COPY_SPECS[@]}"
|
|
|
|
|
|
|
|
|
|
|
|
copy_file_item "${PACKAGE_BINARY_SOURCE_REL}" "${PACKAGE_BINARY_DEST_REL}" true
|
|
|
|
|
|
|
|
|
|
|
|
if [ -n "${PACKAGE_ICON_SOURCE_REL}" ] && [ -n "${PACKAGE_ICON_DEST_REL}" ]; then
|
|
|
|
|
|
copy_file_item "${PACKAGE_ICON_SOURCE_REL}" "${PACKAGE_ICON_DEST_REL}" true
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ -n "${PACKAGE_SERVICE_SOURCE_REL}" ] && [ -n "${PACKAGE_SERVICE_DEST_REL}" ]; then
|
|
|
|
|
|
copy_file_item "${PACKAGE_SERVICE_SOURCE_REL}" "${PACKAGE_SERVICE_DEST_REL}" true
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
write_control_file() {
|
|
|
|
|
|
cat > "${PKG_PATH}/DEBIAN/control" <<EOF
|
|
|
|
|
|
Package: ${PACKAGE_NAME}
|
|
|
|
|
|
Version: ${PKG_RELEASE_VERSION}
|
|
|
|
|
|
Section: ${PACKAGE_SECTION}
|
|
|
|
|
|
Architecture: ${PKG_ARCH}
|
|
|
|
|
|
Priority: optional
|
|
|
|
|
|
Maintainer: ${PACKAGE_MAINTAINER}
|
|
|
|
|
|
Description: ${PACKAGE_DESCRIPTION}
|
|
|
|
|
|
EOF
|
2026-06-26 17:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
if [ -n "${PACKAGE_DEPENDS:-}" ]; then
|
|
|
|
|
|
printf 'Depends: %s\n' "${PACKAGE_DEPENDS}" >> "${PKG_PATH}/DEBIAN/control"
|
|
|
|
|
|
fi
|
2026-04-10 20:02:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
set_permissions() {
|
|
|
|
|
|
local target=""
|
|
|
|
|
|
for target in "${PKG_PATH}/usr" "${PKG_PATH}/etc" "${PKG_PATH}/opt"; do
|
|
|
|
|
|
if [ -d "${target}" ]; then
|
|
|
|
|
|
chmod -R 755 "${target}"
|
|
|
|
|
|
fi
|
|
|
|
|
|
done
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# ===== 流程编排函数 =====
|
|
|
|
|
|
|
|
|
|
|
|
# 加载打包上下文:参数解析 + meta 加载 + 派生变量计算
|
|
|
|
|
|
# 用法: load_package_context "$@" "desktop" 或 load_package_context "$@" "service"
|
|
|
|
|
|
load_package_context() {
|
2026-04-12 19:11:36 +08:00
|
|
|
|
|
2026-04-10 20:02:57 +08:00
|
|
|
|
local expected_kind="${!#}" # 最后一个参数是期望的 kind
|
|
|
|
|
|
local args=("${@:1:$#-1}") # 去掉最后一个参数
|
2026-04-12 19:11:36 +08:00
|
|
|
|
echo "开始加载 ${expected_kind} 上下文"
|
2026-04-10 20:02:57 +08:00
|
|
|
|
|
|
|
|
|
|
if [ ${#args[@]} -eq 1 ] && [ "${args[0]}" = "--list" ]; then
|
|
|
|
|
|
list_projects_by_kind "${expected_kind}"
|
|
|
|
|
|
exit 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ ${#args[@]} -eq 1 ] && { [ "${args[0]}" = "-h" ] || [ "${args[0]}" = "--help" ]; }; then
|
|
|
|
|
|
show_pkg_help "${expected_kind}"
|
|
|
|
|
|
exit 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ ${#args[@]} -ne 1 ]; then
|
|
|
|
|
|
show_pkg_help "${expected_kind}"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
PACKAGE_TARGET="${args[0]}"
|
2026-04-12 19:11:36 +08:00
|
|
|
|
echo "加载项目 ${PACKAGE_TARGET} 上下文"
|
2026-04-10 20:02:57 +08:00
|
|
|
|
|
|
|
|
|
|
if ! load_package_project_meta "${PACKAGE_TARGET}"; then
|
|
|
|
|
|
echo "未知项目: ${PACKAGE_TARGET}" >&2
|
|
|
|
|
|
echo >&2
|
|
|
|
|
|
show_pkg_help "${expected_kind}" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ "${PACKAGE_KIND}" != "${expected_kind}" ]; then
|
|
|
|
|
|
echo "错误: ${PACKAGE_TARGET} 不是 ${expected_kind} 类型项目" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
apply_package_defaults
|
|
|
|
|
|
|
2026-04-12 19:11:36 +08:00
|
|
|
|
echo "读取 ${PACKAGE_TARGET} 版本信息"
|
|
|
|
|
|
|
2026-04-10 20:02:57 +08:00
|
|
|
|
VERSION_FILE="${REPO_DIR}/${PACKAGE_VERSION_FILE_REL}"
|
|
|
|
|
|
read_project_version "${VERSION_FILE}" "${PACKAGE_VERSION_MACRO}"
|
|
|
|
|
|
|
2026-04-12 19:11:36 +08:00
|
|
|
|
echo "生成 ${expected_kind} 打包名称"
|
|
|
|
|
|
|
2026-04-10 20:02:57 +08:00
|
|
|
|
PKG_VERSION="${PROJECT_VERSION}"
|
|
|
|
|
|
BUILD_NUMBER="${PROJECT_BUILD}"
|
|
|
|
|
|
PKG_RELEASE_VERSION="${PKG_VERSION}.${BUILD_NUMBER}"
|
|
|
|
|
|
PKG_PATH="${HOME}/${PACKAGE_NAME}Pkg"
|
|
|
|
|
|
RELEASE_PATH="${REPO_DIR}/Publish"
|
|
|
|
|
|
TIMESTAMP="$(date +%Y%m%d%H%M%S)"
|
|
|
|
|
|
DEB_FILENAME="${RELEASE_PATH}/${PACKAGE_NAME}_${PKG_VERSION}_${BUILD_NUMBER}_${PKG_ARCH}_${TIMESTAMP}.deb"
|
|
|
|
|
|
DESKTOP_FILE_NAME="${PACKAGE_SLUG}.desktop"
|
|
|
|
|
|
EXEC_INSTALL_PATH="/${PACKAGE_BINARY_DEST_REL}"
|
2026-06-26 17:49:32 +08:00
|
|
|
|
if [ -n "${PACKAGE_ICON_DEST_REL}" ]; then
|
|
|
|
|
|
ICON_INSTALL_PATH="/${PACKAGE_ICON_DEST_REL}"
|
|
|
|
|
|
else
|
|
|
|
|
|
ICON_INSTALL_PATH="application-x-executable"
|
|
|
|
|
|
fi
|
2026-04-10 20:02:57 +08:00
|
|
|
|
SERVICE_INSTALL_DIR="/opt/${PACKAGE_SLUG}"
|
2026-04-12 19:11:36 +08:00
|
|
|
|
echo "加载项目 ${PACKAGE_TARGET} 上下文完成"
|
2026-04-10 20:02:57 +08:00
|
|
|
|
|
|
|
|
|
|
if [ -n "${PACKAGE_SERVICE_DEST_REL}" ]; then
|
|
|
|
|
|
SERVICE_INSTALL_PATH="/${PACKAGE_SERVICE_DEST_REL}"
|
|
|
|
|
|
SERVICE_UNIT_NAME="$(basename "${PACKAGE_SERVICE_DEST_REL}")"
|
|
|
|
|
|
else
|
|
|
|
|
|
SERVICE_INSTALL_PATH=""
|
|
|
|
|
|
SERVICE_UNIT_NAME=""
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
mkdir -p "${RELEASE_PATH}"
|
|
|
|
|
|
log_info "开始打包 ${PACKAGE_NAME} ${PKG_RELEASE_VERSION}"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 通用打包步骤
|
|
|
|
|
|
run_common_packaging_steps() {
|
|
|
|
|
|
log_info "准备打包目录"
|
|
|
|
|
|
prepare_package_layout
|
|
|
|
|
|
log_info "复制 Qt 运行库"
|
|
|
|
|
|
copy_qt_runtime
|
|
|
|
|
|
log_info "清理 Qt 冗余文件"
|
|
|
|
|
|
cleanup_qt_runtime
|
|
|
|
|
|
log_info "复制应用文件"
|
|
|
|
|
|
copy_required_files
|
|
|
|
|
|
log_info "写入软件包元数据"
|
|
|
|
|
|
write_control_file
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# 最终打包
|
|
|
|
|
|
finalize_package() {
|
|
|
|
|
|
log_info "设置文件权限"
|
|
|
|
|
|
set_permissions
|
|
|
|
|
|
log_info "生成 DEB 安装包"
|
2026-06-26 17:49:32 +08:00
|
|
|
|
|
|
|
|
|
|
# 使用低压缩等级避免 OOM(大包 + xz 默认等级 6 会吃几百 MB 内存)
|
|
|
|
|
|
# 可通过 DEB_COMPRESS_OPTS 环境变量覆盖,例如 DEB_COMPRESS_OPTS="-Zgzip" 或 "-Zxz -z9"
|
|
|
|
|
|
local compress_opts="${DEB_COMPRESS_OPTS:--Zxz -z1}"
|
|
|
|
|
|
fakeroot dpkg-deb ${compress_opts} --build "${PKG_PATH}" "${DEB_FILENAME}" >/dev/null
|
2026-04-10 20:02:57 +08:00
|
|
|
|
|
|
|
|
|
|
log_info "安装包已生成: ${DEB_FILENAME}"
|
|
|
|
|
|
log_info "安装包大小: $(ls -lh "${DEB_FILENAME}" | awk '{print $5}')"
|
|
|
|
|
|
}
|