#!/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 }