.run-format.zsh 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #!/usr/bin/env zsh
  2. builtin emulate -L zsh
  3. setopt EXTENDED_GLOB
  4. setopt PUSHD_SILENT
  5. setopt ERR_EXIT
  6. setopt ERR_RETURN
  7. setopt NO_UNSET
  8. setopt PIPE_FAIL
  9. setopt NO_AUTO_PUSHD
  10. setopt NO_PUSHD_IGNORE_DUPS
  11. setopt FUNCTION_ARGZERO
  12. ## Enable for script debugging
  13. # setopt WARN_CREATE_GLOBAL
  14. # setopt WARN_NESTED_VAR
  15. # setopt XTRACE
  16. autoload -Uz is-at-least && if ! is-at-least 5.8; then
  17. print -u2 -PR "%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade zsh to fix this issue."
  18. exit 1
  19. fi
  20. invoke_formatter() {
  21. if (( # < 1 )) {
  22. log_error "Usage invoke_formatter [formatter_name]"
  23. exit 2
  24. }
  25. local formatter="${1}"
  26. shift
  27. local -a source_files=(${@})
  28. case ${formatter} {
  29. clang)
  30. if (( ${+commands[clang-format-17]} )) {
  31. local formatter=clang-format-17
  32. } elif (( ${+commands[clang-format]} )) {
  33. local formatter=clang-format
  34. } else {
  35. log_error "No viable clang-format version found (required 17.0.3)"
  36. exit 2
  37. }
  38. local -a formatter_version=($(${formatter} --version))
  39. if ! is-at-least 17.0.3 ${formatter_version[-1]}; then
  40. log_error "clang-format is not version 17.0.3 or above (found ${formatter_version[-1]}."
  41. exit 2
  42. fi
  43. if ! is-at-least ${formatter_version[-1]} 17.0.3; then
  44. log_error "clang-format is more recent than version 17.0.3 (found ${formatter_version[-1]})."
  45. exit 2
  46. fi
  47. if (( ! #source_files )) source_files=(src/**/*.(c|cpp|h|hpp|m|mm)(.N))
  48. local -a format_args=(-style=file -fallback-style=none)
  49. if (( _loglevel > 2 )) format_args+=(--verbose)
  50. check_files() {
  51. local -i num_failures=0
  52. local -a source_files=($@)
  53. local file
  54. local -a format_args=(-style=file -fallback-style=none)
  55. if (( _loglevel > 2 )) format_args+=(--verbose)
  56. local -a command=(${formatter} ${format_args})
  57. for file (${source_files}) {
  58. if ! ${command} "${file}" | diff -q "${file}" - &> /dev/null; then
  59. log_error "${file} requires formatting changes."
  60. if (( fail_on_error == 2 )) return 2;
  61. num_failures=$(( num_failures + 1 ))
  62. fi
  63. }
  64. if (( num_failures && fail_on_error == 1 )) return 2
  65. }
  66. format_files() {
  67. local -a source_files=($@)
  68. if (( ${#source_files} )) {
  69. local -a format_args=(-style=file -fallback-style=none -i)
  70. if (( _loglevel > 2 )) format_args+=(--verbose)
  71. "${formatter}" ${format_args} ${source_files}
  72. }
  73. }
  74. ;;
  75. gersemi)
  76. local formatter=gersemi
  77. if (( ${+commands[gersemi]} )) {
  78. local gersemi_version=($(gersemi --version))
  79. if ! is-at-least 0.12.0 ${gersemi_version[2]}; then
  80. log_error "gersemi is not version 0.12.0 or above (found ${gersemi_version[2]}."
  81. exit 2
  82. fi
  83. }
  84. if (( ! #source_files )) source_files=(CMakeLists.txt (cmake)/**/(CMakeLists.txt|*.cmake)(.N))
  85. check_files() {
  86. local -i num_failures=0
  87. local -a source_files=($@)
  88. local file
  89. local -a command=(${formatter} -c --no-cache ${source_files})
  90. if (( ${#source_files} )) {
  91. while read -r line; do
  92. local -a line_tokens=(${(z)line})
  93. if (( #line_tokens )) {
  94. file=${line_tokens[1]//*${project_root}\//}
  95. log_error "${file} requires formatting changes."
  96. } else {
  97. log_error "${line}"
  98. }
  99. if (( fail_on_error == 2 )) return 2
  100. num_failures=$(( num_failures + 1 ))
  101. done < <(${command} 2>&1)
  102. if (( num_failures && fail_on_error == 1 )) return 2
  103. }
  104. }
  105. format_files() {
  106. local -a source_files=($@)
  107. if (( ${#source_files} )) {
  108. "${formatter}" -i ${source_files}
  109. }
  110. }
  111. ;;
  112. swift)
  113. local formatter=swift-format
  114. if (( ${+commands[swift-format]} )) {
  115. local swift_format_version=$(swift-format --version)
  116. if ! is-at-least 508.0.0 ${swift_format_version}; then
  117. log_error "swift-format is not version 508.0.0 or above (found ${swift_format_version})."
  118. exit 2
  119. fi
  120. } else {
  121. log_error "No viable swift-format version found (required 508.0.0)"
  122. exit 2
  123. }
  124. if (( ! #source_files )) source_files=(src/**/*.swift(.N))
  125. check_files() {
  126. local -i num_failures=0
  127. local -a source_files=($@)
  128. local file
  129. local -a format_args=()
  130. local -a command=(${formatter} ${format_args})
  131. for file (${source_files}) {
  132. if ! "${command}" "${file}" | diff -q "${file}" - &> /dev/null; then
  133. log_error "${file} requires formatting changes."
  134. if (( fail_on_error == 2 )) return 2;
  135. num_failures=$(( num_failures + 1 ))
  136. fi
  137. }
  138. if (( num_failures && fail_on_error == 1 )) return 2
  139. }
  140. format_files() {
  141. local -a source_files=($@)
  142. if (( ${#source_files} )) {
  143. local -a format_args=(-i)
  144. "${formatter}" ${format_args} ${source_files}
  145. }
  146. }
  147. ;;
  148. *) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, gersemi, and swift-format."; exit 2 ;;
  149. }
  150. local file
  151. local -i num_failures=0
  152. if (( check_only )) {
  153. if (( ${+functions[check_files]} )) {
  154. check_files ${source_files}
  155. } else {
  156. log_error "No format check function defined for formatter '${formatter}'"
  157. exit 2
  158. }
  159. } else {
  160. if (( ${+functions[format_files]} )) {
  161. format_files ${source_files}
  162. } else {
  163. log_error "No format function defined for formatter '${formatter}'"
  164. exit 2
  165. }
  166. }
  167. }
  168. run_format() {
  169. if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
  170. if (( ! ${+FORMATTER_NAME} )) typeset -g FORMATTER_NAME=${${(s:-:)ZSH_ARGZERO:t:r}[2]}
  171. local project_root=${SCRIPT_HOME:A:h}
  172. typeset -g host_os=${${(L)$(uname -s)}//darwin/macos}
  173. local -i fail_on_error=0
  174. local -i check_only=0
  175. local -i verbosity=1
  176. local -r _version='2.0.0'
  177. fpath=("${SCRIPT_HOME}/.functions" ${fpath})
  178. autoload -Uz log_info log_error log_output set_loglevel log_status log_warning
  179. local -r _usage="
  180. Usage: %B${functrace[1]%:*}%b <option> [SOURCE_FILES]
  181. %BOptions%b:
  182. %F{yellow} Formatting options%f
  183. -----------------------------------------------------------------------------
  184. %B-c | --check%b Check only, no actual formatting takes place
  185. %F{yellow} Output options%f
  186. -----------------------------------------------------------------------------
  187. %B-v | --verbose%b Verbose (more detailed output)
  188. %B--fail-[never|error] Fail script never/on formatting change - default: %B%F{green}never%f%b
  189. %B--debug%b Debug (very detailed and added output)
  190. %F{yellow} General options%f
  191. -----------------------------------------------------------------------------
  192. %B-h | --help%b Print this usage help
  193. %B-V | --version%b Print script version information"
  194. local -a args
  195. while (( # )) {
  196. case ${1} {
  197. -c|--check) check_only=1; shift ;;
  198. -v|--verbose) (( verbosity += 1 )); shift ;;
  199. -h|--help) log_output ${_usage}; exit 0 ;;
  200. -V|--version) print -Pr "${_version}"; exit 0 ;;
  201. --debug) verbosity=3; shift ;;
  202. --fail-never)
  203. fail_on_error=0
  204. shift
  205. ;;
  206. --fail-error)
  207. fail_on_error=1
  208. shift
  209. ;;
  210. --fail-fast)
  211. fail_on_error=2
  212. shift
  213. ;;
  214. *)
  215. args+=($@)
  216. break
  217. ;;
  218. }
  219. }
  220. set -- ${(@)args}
  221. set_loglevel ${verbosity}
  222. invoke_formatter ${FORMATTER_NAME} ${args}
  223. }
  224. run_format ${@}