GrabBag/GrabBagPrj/project_meta.sh
2026-06-26 17:49:32 +08:00

400 lines
14 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# 项目注册基础设施
# 所有函数定义。项目配置数据在 project_registry.sh 中。
ALL_PROJECTS=()
declare -A PROJECT_META=()
declare -A PROJECT_COPY_SPECS=()
declare -A PROJECT_OPTIONAL_COPY_SPECS=()
DEFAULT_VERSION_STRING="1.0.0"
DEFAULT_BUILD_NUMBER="1"
# ===== 基础工具 =====
_list_items() { printf '%s\n' "$@"; }
_array_contains() {
local expected=$1; shift
local item
for item in "$@"; do
[ "$item" = "$expected" ] && return 0
done
return 1
}
_trim_leading_spaces() { printf '%s' "${1#"${1%%[![:space:]]*}"}"; }
# 将逗号分隔的多条 spec 字符串拆分到指定数组里(空段忽略,自动去前导空格)
# 使用 read -ra 避免对 spec 中的 glob (*) 触发路径名展开
# 用法: _split_csv_specs "<csv>" out_array
_split_csv_specs() {
local _csv=$1
local -n _out=$2
local _items _item _trimmed
[ -z "$_csv" ] && return 0
IFS=',' read -ra _items <<< "$_csv"
for _item in "${_items[@]}"; do
_trimmed="$(_trim_leading_spaces "$_item")"
[ -n "$_trimmed" ] && _out+=("$_trimmed")
done
}
set_project_meta() { PROJECT_META["$1:$2"]=${3-}; }
get_project_meta() { printf '%s' "${PROJECT_META["$1:$2"]:-}"; }
# ===== 项目查询 =====
list_projects() { _list_items "${ALL_PROJECTS[@]}"; }
is_project() { _array_contains "$1" "${ALL_PROJECTS[@]}"; }
list_projects_by_kind() {
local kind=$1 project
for project in "${ALL_PROJECTS[@]}"; do
[ "$(get_project_meta "$project" package_kind)" = "$kind" ] && printf '%s\n' "$project"
done
}
# ===== 元数据加载 =====
load_version_project_meta() {
local project=$1
is_project "$project" || return 1
VERSION_PROJECT="$project"
VERSION_FILE_REL="$(get_project_meta "$project" version_file_rel)"
VERSION_MACRO="$(get_project_meta "$project" version_macro)"
PUBLISH_NAME="$(get_project_meta "$project" publish_name)"
}
load_package_project_meta() {
local project=$1
is_project "$project" || return 1
PACKAGE_PROJECT="$project"
PACKAGE_NAME="$(get_project_meta "$project" package_name)"
PACKAGE_KIND="$(get_project_meta "$project" package_kind)"
PACKAGE_DESCRIPTION="$(get_project_meta "$project" package_description)"
PACKAGE_COMMENT="$(get_project_meta "$project" package_comment)"
PACKAGE_VERSION_FILE_REL="$(get_project_meta "$project" version_file_rel)"
PACKAGE_VERSION_MACRO="$(get_project_meta "$project" version_macro)"
PACKAGE_BINARY_SOURCE_REL="$(get_project_meta "$project" package_binary_source_rel)"
PACKAGE_BINARY_DEST_REL="$(get_project_meta "$project" package_binary_dest_rel)"
PACKAGE_ICON_SOURCE_REL="$(get_project_meta "$project" package_icon_source_rel)"
PACKAGE_ICON_DEST_REL="$(get_project_meta "$project" package_icon_dest_rel)"
PACKAGE_SERVICE_SOURCE_REL="$(get_project_meta "$project" package_service_source_rel)"
PACKAGE_SERVICE_DEST_REL="$(get_project_meta "$project" package_service_dest_rel)"
PACKAGE_LD_CONF_EXTRA_LINES="$(get_project_meta "$project" package_ld_conf_extra_lines)"
PACKAGE_OPENCV_PROFILE="$(get_project_meta "$project" package_opencv_profile)"
PACKAGE_DEPENDS="$(get_project_meta "$project" package_depends)"
local serialized
serialized="${PROJECT_COPY_SPECS[$project]:-}"
PACKAGE_COPY_SPECS=()
if [ -n "$serialized" ]; then
while IFS= read -r line; do
[ -n "$line" ] && PACKAGE_COPY_SPECS+=("$line")
done <<< "$serialized"
fi
serialized="${PROJECT_OPTIONAL_COPY_SPECS[$project]:-}"
PACKAGE_OPTIONAL_COPY_SPECS=()
if [ -n "$serialized" ]; then
while IFS= read -r line; do
[ -n "$line" ] && PACKAGE_OPTIONAL_COPY_SPECS+=("$line")
done <<< "$serialized"
fi
}
# ===== 版本读取 =====
read_project_version() {
local version_file=$1 version_macro=$2
local candidate value
PROJECT_VERSION="$DEFAULT_VERSION_STRING"
PROJECT_BUILD="$DEFAULT_BUILD_NUMBER"
if [ ! -f "$version_file" ]; then
echo "错误: 版本文件不存在: $version_file" >&2
return 1
fi
for candidate in "${version_macro}_VERSION_STRING" "${version_macro}_APP_VERSION_STRING"; do
value=$(grep -E "^#define[[:space:]]+${candidate}[[:space:]]+\"" "$version_file" | sed -E 's/.*"([^"]+)".*/\1/' | head -n 1 || true)
if [ -n "$value" ]; then PROJECT_VERSION="$value"; break; fi
done
if [ -z "$PROJECT_VERSION" ]; then
echo "错误: 版本文件中未定义版本字符串" >&2
return 1
fi
for candidate in "${version_macro}_BUILD_STRING" "${version_macro}_VERSION_BUILD" "${version_macro}_APP_VERSION_BUILD"; do
value=$(grep -E "^#define[[:space:]]+${candidate}[[:space:]]+\"" "$version_file" | sed -E 's/.*"([^"]+)".*/\1/' | head -n 1 || true)
if [ -n "$value" ]; then PROJECT_BUILD="$value"; break; fi
done
if [ -z "$PROJECT_BUILD" ]; then
echo "错误: 版本文件中未定义构建号" >&2
return 1
fi
return 0
}
# ===== 内部注册函数 =====
_finalize_project_meta() {
local project=$1
local -n meta_ref=$2
if [ -n "${meta_ref[app_dir_rel]}" ]; then
local app_binary_name package_slug
app_binary_name=$(basename "${meta_ref[app_dir_rel]}")
# 服务项目: 先推导 package_name
if [ "${meta_ref[package_kind]}" = "service" ] && [ "${meta_ref[package_name]}" = "$project" ]; then
meta_ref[package_name]="$app_binary_name"
fi
package_slug=$(printf '%s' "${meta_ref[package_name]}" | tr '[:upper:]' '[:lower:]')
: "${meta_ref[version_file_rel]:=${meta_ref[app_dir_rel]}/Version.h}"
: "${meta_ref[package_binary_source_rel]:=GrabBagPrj/buildarm/${meta_ref[app_dir_rel]}/${app_binary_name}}"
if [ -z "${meta_ref[package_binary_dest_rel]}" ]; then
if [ "${meta_ref[package_kind]}" = "service" ]; then
meta_ref[package_binary_dest_rel]="opt/${package_slug}/${app_binary_name}"
else
meta_ref[package_binary_dest_rel]="usr/local/bin/${app_binary_name}"
fi
fi
if [ "${meta_ref[package_kind]}" = "desktop" ]; then
local default_icon_rel="${meta_ref[app_dir_rel]}/resource/logo.png"
if [ -z "${meta_ref[package_icon_source_rel]}" ] && [ -f "${SCRIPT_DIR}/../${default_icon_rel}" ]; then
meta_ref[package_icon_source_rel]="${default_icon_rel}"
fi
if [ -n "${meta_ref[package_icon_source_rel]}" ]; then
: "${meta_ref[package_icon_dest_rel]:=usr/share/pixmaps/${package_slug}.png}"
fi
fi
if [ "${meta_ref[package_kind]}" = "service" ]; then
# 优先使用 service/ 子目录下的 service 文件,不存在则回退到 app_dir 根目录
local _svc_in_service_dir="${meta_ref[app_dir_rel]}/service/${package_slug}.service"
local _svc_in_root="${meta_ref[app_dir_rel]}/${package_slug}.service"
if [ -f "${SCRIPT_DIR}/../${_svc_in_service_dir}" ]; then
: "${meta_ref[package_service_source_rel]:=$_svc_in_service_dir}"
else
: "${meta_ref[package_service_source_rel]:=$_svc_in_root}"
fi
: "${meta_ref[package_service_dest_rel]:=opt/${package_slug}/${package_slug}.service}"
fi
fi
# publish_name 自动推导
if [ -z "${meta_ref[publish_name]}" ]; then
if [ "${meta_ref[package_kind]}" = "service" ]; then
meta_ref[publish_name]="${meta_ref[package_name]}"
else
meta_ref[publish_name]="$project"
fi
fi
}
_register_project() {
local project=$1; shift
local -A meta=(
[app_dir_rel]=""
[version_file_rel]=""
[version_macro]=""
[publish_name]=""
[package_name]="$project"
[package_kind]="desktop"
[package_description]=""
[package_comment]=""
[package_binary_source_rel]=""
[package_binary_dest_rel]=""
[package_icon_source_rel]=""
[package_icon_dest_rel]=""
[package_service_source_rel]=""
[package_service_dest_rel]=""
[package_ld_conf_extra_lines]=""
[package_opencv_profile]=""
[package_depends]=""
)
local kv key value
for kv in "$@"; do
key="${kv%%=*}"; value="${kv#*=}"
if [[ -v "meta[$key]" ]]; then
meta["$key"]="$value"
else
echo "Unknown field: ${key} (${project})" >&2; return 1
fi
done
_finalize_project_meta "$project" meta || return 1
ALL_PROJECTS+=("$project")
local k
for k in "${!meta[@]}"; do
set_project_meta "$project" "$k" "${meta[$k]}"
done
PROJECT_COPY_SPECS["$project"]=""
PROJECT_OPTIONAL_COPY_SPECS["$project"]=""
}
_register_desktop_project() {
local project=$1 app_dir_rel=$2 version_macro=$3; shift 3
_register_project "$project" "app_dir_rel=$app_dir_rel" "version_macro=$version_macro" "$@"
}
_register_service_project() {
local project=$1 app_dir_rel=$2 version_macro=$3; shift 3
_register_project "$project" "app_dir_rel=$app_dir_rel" "version_macro=$version_macro" "package_kind=service" "$@"
}
_set_copy_specs() {
local project=$1; shift
PROJECT_COPY_SPECS["$project"]="$(printf '%s\n' "$@")"
}
_set_optional_copy_specs() {
local project=$1; shift
PROJECT_OPTIONAL_COPY_SPECS["$project"]="$(printf '%s\n' "$@")"
}
# ===== 批量注册函数(读取 project_registry.sh 中定义的数组)=====
_parse_kv_overrides() {
# 用法: _parse_kv_overrides fields_array start_index var1 var2 ...
# 从 fields[start_index] 开始解析 key=value赋值给同名变量
local -n _fields=$1
local _start=$2; shift 2
local _allowed_keys=("$@")
local _i _kv _key _value
for (( _i=_start; _i<${#_fields[@]}; _i++ )); do
_kv="$(_trim_leading_spaces "${_fields[$_i]}")"
[ -z "$_kv" ] && continue
_key="${_kv%%=*}"; _value="${_kv#*=}"
local _found=false _ak
for _ak in "${_allowed_keys[@]}"; do
if [ "$_key" = "$_ak" ]; then
eval "$_key=\"\$_value\""
_found=true; break
fi
done
if [ "$_found" = false ]; then
echo "Unknown override key: $_key" >&2
fi
done
}
_register_desktop_projects() {
local entry fields name algo
local app_dir version_macro opencv algo_copy device_copy extra_copy optional_copy ld_conf description comment package_depends
for entry in "${DESKTOP_PROJECTS[@]}"; do
IFS=';' read -ra fields <<< "$entry"
name="$(_trim_leading_spaces "${fields[0]}")"
algo="$(_trim_leading_spaces "${fields[1]}")"
# 默认值
app_dir="App/${name}/${name}App"
version_macro=$(printf '%s' "$name" | tr '[:lower:]' '[:upper:]')
opencv="" algo_copy="" device_copy="$COPY_VZNLSDK" extra_copy="" optional_copy=""
ld_conf="" description="" comment="" package_depends=""
_parse_kv_overrides fields 2 \
app_dir version_macro opencv algo_copy device_copy extra_copy optional_copy ld_conf description comment package_depends
if [ -z "$algo_copy" ] && [ "$algo" != "none" ]; then
algo_copy="glob|AppAlgo/${algo}/Arm/aarch64/*.so|usr/lib"
fi
# register
local reg_args=()
[ -n "$opencv" ] && reg_args+=("package_opencv_profile=$opencv")
[ -n "$ld_conf" ] && reg_args+=("package_ld_conf_extra_lines=$ld_conf")
[ -n "$description" ] && reg_args+=("package_description=$description")
[ -n "$comment" ] && reg_args+=("package_comment=$comment")
[ -n "$package_depends" ] && reg_args+=("package_depends=$package_depends")
_register_desktop_project "$name" "$app_dir" "$version_macro" "${reg_args[@]}"
# copy specs
local specs=()
[ -n "$algo_copy" ] && [ "$algo_copy" != "none" ] && specs+=("$algo_copy")
[ -n "$device_copy" ] && [ "$device_copy" != "none" ] && specs+=("$device_copy")
_split_csv_specs "$extra_copy" specs
_set_copy_specs "$name" "${specs[@]}"
# optional copy
local opt_specs=()
_split_csv_specs "$optional_copy" opt_specs
if [ ${#opt_specs[@]} -gt 0 ]; then
_set_optional_copy_specs "$name" "${opt_specs[@]}"
elif [ "$device_copy" != "$COPY_VZNLSDK" ]; then
_set_optional_copy_specs "$name" "$COPY_VZNLSDK"
fi
done
}
_register_service_projects() {
local entry fields name algo
local app_dir app_suffix version_macro opencv device_copy extra_copy optional_copy description package_depends
for entry in "${SERVICE_PROJECTS[@]}"; do
IFS=';' read -ra fields <<< "$entry"
name="$(_trim_leading_spaces "${fields[0]}")"
algo="$(_trim_leading_spaces "${fields[1]}")"
# 默认值
app_dir="" app_suffix="Server" version_macro="" opencv=""
device_copy="$COPY_VZNLSDK" extra_copy="" optional_copy="" description="" package_depends=""
_parse_kv_overrides fields 2 \
app_dir app_suffix version_macro opencv device_copy extra_copy optional_copy description package_depends
: "${app_dir:=App/${name}/${name}${app_suffix}}"
: "${version_macro:=$(printf '%s' "$name" | tr '[:lower:]' '[:upper:]')}"
# register
local reg_args=()
[ -n "$opencv" ] && reg_args+=("package_opencv_profile=$opencv")
[ -n "$description" ] && reg_args+=("package_description=$description")
[ -n "$package_depends" ] && reg_args+=("package_depends=$package_depends")
_register_service_project "$name" "$app_dir" "$version_macro" "${reg_args[@]}"
# copy specs
local specs=()
[ "$algo" != "none" ] && specs+=("glob|AppAlgo/${algo}/Arm/aarch64/*.so|usr/lib")
[ -n "$device_copy" ] && [ "$device_copy" != "none" ] && specs+=("$device_copy")
_split_csv_specs "$extra_copy" specs
_set_copy_specs "$name" "${specs[@]}"
# optional copy
local opt_specs=()
_split_csv_specs "$optional_copy" opt_specs
# 自动检测 config 目录
if [ -d "${SCRIPT_DIR}/../${app_dir}/config" ]; then
local pkg_name
pkg_name=$(get_project_meta "$name" package_name)
local slug
slug=$(printf '%s' "$pkg_name" | tr '[:upper:]' '[:lower:]')
opt_specs+=("dir|${app_dir}/config|opt/${slug}/config")
fi
if [ ${#opt_specs[@]} -gt 0 ]; then
_set_optional_copy_specs "$name" "${opt_specs[@]}"
fi
done
}