common.sh 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #!/usr/bin/env bash
  2. # 通用脚本工具函数(供 run_local_* 使用)
  3. ryanjson_init_utf8_locale() {
  4. # Force UTF-8 locale for script-generated files/logs when available.
  5. local localeValue=""
  6. if command -v locale >/dev/null 2>&1; then
  7. if locale -a 2>/dev/null | grep -qx "C.UTF-8"; then
  8. localeValue="C.UTF-8"
  9. elif locale -a 2>/dev/null | grep -qx "en_US.UTF-8"; then
  10. localeValue="en_US.UTF-8"
  11. fi
  12. fi
  13. if [[ -n "${localeValue}" ]]; then
  14. export LC_ALL="${localeValue}"
  15. export LANG="${localeValue}"
  16. fi
  17. }
  18. ryanjson_init_utf8_locale
  19. ryanjson_init_utf8_log() {
  20. # Initialize a UTF-8 log file (no BOM).
  21. local path="$1"
  22. mkdir -p "$(dirname "${path}")"
  23. : > "${path}"
  24. }
  25. ryanjson_abs_dir() {
  26. # 返回文件/目录的绝对路径(仅取目录部分)
  27. local inputPath="$1"
  28. (cd "$(dirname "${inputPath}")" && pwd)
  29. }
  30. ryanjson_repo_root_from_source() {
  31. # 从脚本路径向上回溯,拿到仓库根目录
  32. local sourcePath="$1"
  33. local levelsUp="${2:-0}"
  34. local currentDir=""
  35. currentDir="$(ryanjson_abs_dir "${sourcePath}")"
  36. while (( levelsUp > 0 )); do
  37. currentDir="$(cd "${currentDir}/.." && pwd)"
  38. levelsUp=$((levelsUp - 1))
  39. done
  40. printf '%s\n' "${currentDir}"
  41. }
  42. ryanjson_print_banner_begin() {
  43. # 统一 banner 输出(开始)
  44. local title="$1"
  45. echo "===================================================="
  46. echo "${title}"
  47. }
  48. ryanjson_print_banner_kv() {
  49. # 统一 banner 输出(KV)
  50. local key="$1"
  51. local value="$2"
  52. echo " - ${key}=${value}"
  53. }
  54. ryanjson_print_banner_kv_optional() {
  55. # 仅在值非空时输出 banner KV
  56. local key="$1"
  57. local value="$2"
  58. if [[ -n "${value}" ]]; then
  59. ryanjson_print_banner_kv "${key}" "${value}"
  60. fi
  61. }
  62. ryanjson_print_banner_end() {
  63. # 统一 banner 输出(结束)
  64. echo "===================================================="
  65. }
  66. ryanjson_log_phase() {
  67. # 阶段提示
  68. echo "[阶段] $*"
  69. }
  70. ryanjson_log_info() {
  71. # 普通信息
  72. echo "[信息] $*"
  73. }
  74. ryanjson_log_warn() {
  75. # 警告提示
  76. echo "[警告] $*"
  77. }
  78. ryanjson_log_error() {
  79. # 错误提示
  80. echo "[错误] $*"
  81. }
  82. ryanjson_log_done() {
  83. # 完成提示
  84. echo "[完成] $*"
  85. }
  86. ryanjson_is_nonneg_int() {
  87. # 非负整数判断
  88. [[ "$1" =~ ^[0-9]+$ ]]
  89. }
  90. ryanjson_is_positive_int() {
  91. # 正整数判断
  92. [[ "$1" =~ ^[0-9]+$ ]] && (( $1 > 0 ))
  93. }
  94. ryanjson_is_01() {
  95. # 0/1 开关判断
  96. [[ "$1" == "0" || "$1" == "1" ]]
  97. }
  98. ryanjson_require_01() {
  99. # 校验 0/1 开关
  100. local name="$1"
  101. local value="$2"
  102. if ! ryanjson_is_01 "${value}"; then
  103. ryanjson_log_error "${name} 仅支持 0/1,当前值:${value}"
  104. return 1
  105. fi
  106. }
  107. ryanjson_require_pos_int() {
  108. # 校验正整数(可选附加单位)
  109. local name="$1"
  110. local value="$2"
  111. local unit="${3:-}"
  112. if ! ryanjson_is_positive_int "${value}"; then
  113. if [[ -n "${unit}" ]]; then
  114. ryanjson_log_error "${name} 仅支持正整数(${unit}),当前值:${value}"
  115. else
  116. ryanjson_log_error "${name} 仅支持正整数,当前值:${value}"
  117. fi
  118. return 1
  119. fi
  120. }
  121. ryanjson_require_nonneg_int() {
  122. # 校验非负整数
  123. local name="$1"
  124. local value="$2"
  125. if ! ryanjson_is_nonneg_int "${value}"; then
  126. ryanjson_log_error "${name} 仅支持非负整数,当前值:${value}"
  127. return 1
  128. fi
  129. }
  130. ryanjson_require_pos_int_optional() {
  131. # 可选正整数校验(空值跳过)
  132. local name="$1"
  133. local value="$2"
  134. local unit="${3:-}"
  135. if [[ -n "${value}" ]]; then
  136. ryanjson_require_pos_int "${name}" "${value}" "${unit}"
  137. return $?
  138. fi
  139. return 0
  140. }
  141. ryanjson_require_nonneg_int_optional() {
  142. # 可选非负整数校验(空值跳过)
  143. local name="$1"
  144. local value="$2"
  145. if [[ -n "${value}" ]]; then
  146. ryanjson_require_nonneg_int "${name}" "${value}"
  147. return $?
  148. fi
  149. return 0
  150. }
  151. ryanjson_require_cmds() {
  152. # 批量检查命令依赖
  153. local hint="${1:-}"
  154. shift || true
  155. local missing=0
  156. for cmdName in "$@"; do
  157. if ! command -v "${cmdName}" >/dev/null 2>&1; then
  158. ryanjson_log_error "缺少命令: ${cmdName}"
  159. missing=1
  160. fi
  161. done
  162. if [[ "${missing}" -ne 0 && -n "${hint}" ]]; then
  163. ryanjson_log_info "提示: ${hint}"
  164. fi
  165. return "${missing}"
  166. }
  167. ryanjson_require_cmd() {
  168. # 单命令检查(复用批量版本)
  169. local cmdName="$1"
  170. local hint="${2:-}"
  171. ryanjson_require_cmds "${hint}" "${cmdName}"
  172. }
  173. ryanjson_prepare_clean_dir() {
  174. # 清理并重新创建目录
  175. local dirPath="$1"
  176. rm -rf "${dirPath}"
  177. mkdir -p "${dirPath}"
  178. }
  179. ryanjson_run_xmake_config() {
  180. # 运行 xmake 配置(支持 clean/增量)
  181. local forceClean="$1"
  182. local caseName="$2"
  183. if [[ "${forceClean}" == "1" ]]; then
  184. ryanjson_log_phase "正在执行 xmake 配置(clean 模式)..."
  185. if ! xmake f -c; then
  186. ryanjson_log_error "xmake 配置失败:${caseName}"
  187. return 1
  188. fi
  189. else
  190. ryanjson_log_phase "正在执行 xmake 配置(增量模式)..."
  191. if ! xmake f; then
  192. ryanjson_log_error "xmake 配置失败:${caseName}"
  193. return 1
  194. fi
  195. fi
  196. }
  197. ryanjson_run_xmake_build() {
  198. # 运行 xmake 构建
  199. local targetName="$1"
  200. local caseName="$2"
  201. ryanjson_log_phase "正在执行 xmake 构建(target=${targetName})..."
  202. if ! xmake -b "${targetName}"; then
  203. ryanjson_log_error "xmake 构建失败:${caseName}"
  204. return 1
  205. fi
  206. }
  207. ryanjson_export_semantic_macros() {
  208. # 统一导出语义宏(strict/addAtHead/scientific)
  209. local strictKey="$1"
  210. local addAtHead="$2"
  211. local scientific="$3"
  212. export RYANJSON_STRICT_OBJECT_KEY_CHECK="${strictKey}"
  213. export RYANJSON_DEFAULT_ADD_AT_HEAD="${addAtHead}"
  214. export RYANJSON_SNPRINTF_SUPPORT_SCIENTIFIC="${scientific}"
  215. }
  216. ryanjson_print_semantic_kv() {
  217. # 统一输出语义宏 KV(配合 banner 使用)
  218. local strictKey="$1"
  219. local addAtHead="$2"
  220. local scientific="$3"
  221. ryanjson_print_banner_kv "RyanJsonStrictObjectKeyCheck" "${strictKey}"
  222. ryanjson_print_banner_kv "RyanJsonDefaultAddAtHead" "${addAtHead}"
  223. ryanjson_print_banner_kv "RyanJsonSnprintfSupportScientific" "${scientific}"
  224. }
  225. ryanjson_semantic_log_error() {
  226. # 语义矩阵输出错误提示
  227. ryanjson_log_error "$@"
  228. }
  229. ryanjson_emit_semantic_cases() {
  230. # 根据模式输出三元组:strictKey addAtHead scientific
  231. local mode="$1"
  232. local modeName="${2:-MODE}"
  233. case "${mode}" in
  234. quick)
  235. printf '%s\n' 'false true true' 'true false true'
  236. ;;
  237. nightly)
  238. local strictKey=""
  239. local addAtHead=""
  240. for strictKey in false true; do
  241. for addAtHead in false true; do
  242. printf '%s %s true\n' "${strictKey}" "${addAtHead}"
  243. done
  244. done
  245. ;;
  246. full)
  247. local strictKey=""
  248. local addAtHead=""
  249. local scientific=""
  250. for strictKey in false true; do
  251. for addAtHead in false true; do
  252. for scientific in false true; do
  253. printf '%s %s %s\n' "${strictKey}" "${addAtHead}" "${scientific}"
  254. done
  255. done
  256. done
  257. ;;
  258. *)
  259. ryanjson_semantic_log_error "${modeName} 仅支持 quick/nightly/full,当前值:${mode}"
  260. return 1
  261. ;;
  262. esac
  263. }
  264. ryanjson_semantic_case_name() {
  265. # 生成可读的用例名称
  266. local strictKey="$1"
  267. local addAtHead="$2"
  268. local scientific="$3"
  269. printf 'strict_%s__head_%s__sci_%s\n' "${strictKey}" "${addAtHead}" "${scientific}"
  270. }