351 lines
12 KiB
Bash
351 lines
12 KiB
Bash
#!/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:]]*}"}"; }
|
||
|
||
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)"
|
||
|
||
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
|
||
|
||
: "${meta_ref[package_icon_source_rel]:=${meta_ref[app_dir_rel]}/resource/logo.png}"
|
||
|
||
if [ "${meta_ref[package_kind]}" = "desktop" ]; then
|
||
: "${meta_ref[package_icon_dest_rel]:=usr/share/pixmaps/${package_slug}.png}"
|
||
fi
|
||
|
||
if [ "${meta_ref[package_kind]}" = "service" ]; then
|
||
: "${meta_ref[package_service_source_rel]:=${meta_ref[app_dir_rel]}/${package_slug}.service}"
|
||
: "${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]=""
|
||
)
|
||
|
||
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
|
||
|
||
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=""
|
||
|
||
_parse_kv_overrides fields 2 \
|
||
app_dir version_macro opencv algo_copy device_copy extra_copy optional_copy ld_conf description comment
|
||
|
||
: "${algo_copy:=glob|AppAlgo/${algo}/Arm/aarch64/*.so|usr/lib}"
|
||
|
||
# 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")
|
||
|
||
_register_desktop_project "$name" "$app_dir" "$version_macro" "${reg_args[@]}"
|
||
|
||
# copy specs
|
||
local specs=("$algo_copy")
|
||
[ -n "$device_copy" ] && [ "$device_copy" != "none" ] && specs+=("$device_copy")
|
||
[ -n "$extra_copy" ] && specs+=("$extra_copy")
|
||
_set_copy_specs "$name" "${specs[@]}"
|
||
|
||
# optional copy
|
||
if [ -n "$optional_copy" ]; then
|
||
_set_optional_copy_specs "$name" "$optional_copy"
|
||
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_suffix version_macro opencv device_copy description
|
||
|
||
for entry in "${SERVICE_PROJECTS[@]}"; do
|
||
IFS=';' read -ra fields <<< "$entry"
|
||
name="$(_trim_leading_spaces "${fields[0]}")"
|
||
algo="$(_trim_leading_spaces "${fields[1]}")"
|
||
|
||
# 默认值
|
||
app_suffix="Server" version_macro="" opencv=""
|
||
device_copy="$COPY_VZNLSDK" description=""
|
||
|
||
_parse_kv_overrides fields 2 \
|
||
app_suffix version_macro opencv device_copy description
|
||
|
||
: "${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")
|
||
|
||
_register_service_project "$name" "App/${name}/${name}${app_suffix}" "$version_macro" "${reg_args[@]}"
|
||
_set_copy_specs "$name" "glob|AppAlgo/${algo}/Arm/aarch64/*.so|usr/lib" "$device_copy"
|
||
|
||
# 自动检测 config 目录
|
||
local app_dir="App/${name}/${name}${app_suffix}"
|
||
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:]')
|
||
_set_optional_copy_specs "$name" "dir|${app_dir}/config|opt/${slug}/config"
|
||
fi
|
||
done
|
||
}
|