run_local_format.sh 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. #!/bin/bash
  2. set -euo pipefail
  3. # 本地一键 clang-format。
  4. # 默认行为:格式化仓库内受管源码(C/C++ 头源文件,含未追踪新增文件)。
  5. # 可选参数:
  6. # --check 仅检查,不修改文件(不符合时返回非 0)
  7. scriptDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  8. # shellcheck source=scripts/lib/common.sh
  9. source "${scriptDir}/scripts/lib/common.sh"
  10. repoRoot="$(ryanjson_repo_root_from_source "${BASH_SOURCE[0]}" 0)"
  11. cd "${repoRoot}"
  12. ryanjson_detect_clang_format() {
  13. # 选择可用的 clang-format 可执行文件
  14. if command -v clang-format >/dev/null 2>&1; then
  15. echo "clang-format"
  16. return 0
  17. fi
  18. if command -v clang-format-21 >/dev/null 2>&1; then
  19. echo "clang-format-21"
  20. return 0
  21. fi
  22. if command -v clang-format-20 >/dev/null 2>&1; then
  23. echo "clang-format-20"
  24. return 0
  25. fi
  26. echo ""
  27. return 1
  28. }
  29. ryanjson_collect_format_files() {
  30. # 收集受管源文件,过滤第三方与构建产物
  31. local -a files=()
  32. local -a candidates=()
  33. mapfile -d '' candidates < <(git ls-files -z --cached --others --exclude-standard -- '*.c' '*.h' '*.cc' '*.cpp' '*.hpp')
  34. for f in "${candidates[@]}"; do
  35. if [[ ! -f "${f}" ]]; then
  36. continue
  37. fi
  38. case "${f}" in
  39. test/externalModule/* | build/* | coverage/* | localLogs/* | .xmake/* | test/fuzzer/corpus/*)
  40. continue
  41. ;;
  42. esac
  43. files+=("${f}")
  44. done
  45. printf '%s\n' "${files[@]}"
  46. }
  47. ryanjson_format_main() {
  48. # 参数解析:--check / --help
  49. local mode="write"
  50. while (($# > 0)); do
  51. case "$1" in
  52. --check)
  53. mode="check"
  54. ;;
  55. -h | --help)
  56. echo "用法: bash ./run_local_format.sh [--check]"
  57. return 0
  58. ;;
  59. *)
  60. ryanjson_log_error "未知参数: $1"
  61. echo "用法: bash ./run_local_format.sh [--check]"
  62. return 2
  63. ;;
  64. esac
  65. shift
  66. done
  67. # 选择格式化工具
  68. local formatter=""
  69. if ! formatter="$(ryanjson_detect_clang_format)"; then
  70. ryanjson_log_error "未找到 clang-format,可执行文件名尝试过: clang-format / clang-format-21 / clang-format-20"
  71. return 127
  72. fi
  73. # 收集文件列表
  74. local -a files=()
  75. mapfile -t files < <(ryanjson_collect_format_files)
  76. ryanjson_print_banner_begin "本地格式化启动"
  77. ryanjson_print_banner_kv "formatter" "${formatter}"
  78. ryanjson_print_banner_kv "mode" "${mode}"
  79. ryanjson_print_banner_kv "files" "${#files[@]}"
  80. ryanjson_print_banner_end
  81. # 无文件时直接返回
  82. if [[ ${#files[@]} -eq 0 ]]; then
  83. ryanjson_log_info "没有可处理的源码文件"
  84. return 0
  85. fi
  86. # 根据模式执行
  87. if [[ "${mode}" == "check" ]]; then
  88. if printf '%s\0' "${files[@]}" | xargs -0 "${formatter}" --dry-run --Werror; then
  89. ryanjson_log_done "clang-format 检查通过"
  90. else
  91. ryanjson_log_error "存在不符合 .clang-format 的文件,请执行: bash ./run_local_format.sh"
  92. return 1
  93. fi
  94. else
  95. printf '%s\0' "${files[@]}" | xargs -0 "${formatter}" -i
  96. ryanjson_log_done "clang-format 已应用到 ${#files[@]} 个文件"
  97. fi
  98. }
  99. main() {
  100. ryanjson_format_main "$@"
  101. }
  102. main "$@"