Sfoglia il codice sorgente

build-aux: Replace cmake-format with gersemi

PatTheMav 10 mesi fa
parent
commit
5f01841da4

+ 0 - 31
.cmake-format.json

@@ -1,31 +0,0 @@
-{
-  "format": {
-    "line_width": 120,
-    "tab_size": 2,
-    "enable_sort": true,
-    "autosort": true
-  },
-  "additional_commands": {
-    "set_target_properties_obs": {
-      "pargs": 1,
-      "flags": [],
-      "kwargs": {
-        "PROPERTIES": {
-          "kwargs": {
-            "PREFIX": 1,
-            "OUTPUT_NAME": 1,
-            "FOLDER": 1,
-            "VERSION": 1,
-            "SOVERSION": 1,
-            "AUTOMOC": 1,
-            "AUTOUIC": 1,
-            "AUTORCC": 1,
-            "AUTOUIC_SEARCH_PATHS": 1,
-            "BUILD_RPATH": 1,
-            "INSTALL_RPATH": 1
-          }
-        }
-      }
-    }
-  }
-}

+ 11 - 0
.gersemirc

@@ -0,0 +1,11 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/master/gersemi/configuration.schema.json
+
+color: false
+definitions: []
+line_length: 120
+indent: 2
+list_expansion: favour-inlining
+quiet: false
+unsafe: false
+workers: 10
+warn_about_unknown_commands: false

+ 77 - 0
.github/actions/check-changes/action.yaml

@@ -0,0 +1,77 @@
+name: Check For Changed Files
+description: Checks for changed files compared to specific git reference and glob expression
+inputs:
+  baseRef:
+    description: Git reference to check against
+    required: false
+  ref:
+    description: Git reference to check with
+    required: false
+    default: HEAD
+  checkGlob:
+    description: Glob expression to limit check to specific files
+    required: false
+  useFallback:
+    description: Use fallback compare against prior commit
+    required: false
+    default: 'true'
+  diffFilter:
+    description: git diff-filter string to use
+    required: false
+    default: ''
+outputs:
+  hasChangedFiles:
+    value: ${{ steps.checks.outputs.hasChangedFiles }}
+    description: True if specified files were changed in comparison to specified git reference
+  changedFiles:
+    value: ${{ steps.checks.outputs.changedFiles }}
+    description: List of changed files
+runs:
+  using: composite
+  steps:
+    - name: Check For Changed Files ✅
+      shell: bash
+      id: checks
+      env:
+        GIT_BASE_REF: ${{ inputs.baseRef }}
+        GIT_REF: ${{ inputs.ref }}
+        GITHUB_EVENT_FORCED: ${{ github.event.forced }}
+        GITHUB_REF_BEFORE: ${{ github.event.before }}
+        USE_FALLBACK: ${{ inputs.useFallback }}
+        DIFF_FILTER: ${{ inputs.diffFilter }}
+      run: |
+        : Check for Changed Files ✅
+        if [[ "${RUNNER_DEBUG}" ]]; then set -x; fi
+        shopt -s extglob
+        shopt -s dotglob
+
+        if [[ "${GIT_BASE_REF}" ]]; then
+          if ! git cat-file -e "${GIT_BASE_REF}" &> /dev/null; then
+            echo "::warning::Provided base reference ${GIT_BASE_REF} is invalid"
+            if [[ "${USE_FALLBACK}" == 'true' ]]; then
+              GIT_BASE_REF='HEAD~1'
+            fi
+          fi
+        else
+          if ! git cat-file -e ${GITHUB_REF_BEFORE} &> /dev/null; then
+            GITHUB_REF_BEFORE='4b825dc642cb6eb9a060e54bf8d69288fbee4904'
+          fi
+
+          GIT_BASE_REF='HEAD~1'
+          case "${GITHUB_EVENT_NAME}" in
+            pull_request) GIT_BASE_REF="origin/${GITHUB_BASE_REF}" ;;
+            push) if [[ "${GITHUB_EVENT_FORCED}" != 'true' ]]; then GIT_BASE_REF="${GITHUB_REF_BEFORE}"; fi ;;
+            *) ;;
+          esac
+        fi
+
+        changes=($(git diff --name-only --diff-filter="${DIFF_FILTER}" ${GIT_BASE_REF} ${GIT_REF} -- ${{ inputs.checkGlob }}))
+
+        if (( ${#changes[@]} )); then
+          file_string="${changes[*]}"
+          echo "hasChangedFiles=true" >> $GITHUB_OUTPUT
+          echo "changedFiles=[\"${file_string// /\",\"}\"]" >> $GITHUB_OUTPUT
+        else
+          echo "hasChangedFiles=false" >> $GITHUB_OUTPUT
+          echo "changedFiles=[]" >> GITHUB_OUTPUT
+        fi

+ 18 - 19
.github/actions/run-clang-format/action.yaml

@@ -4,7 +4,7 @@ inputs:
   failCondition:
     description: Controls whether failed checks also fail the workflow run
     required: false
-    default: 'never'
+    default: never
   workingDirectory:
     description: Working directory for checks
     required: false
@@ -20,8 +20,15 @@ runs:
         echo "::notice::run-clang-format action requires a macOS-based or Linux-based runner."
         exit 2
 
+    - name: Check for Changed Files ✅
+      uses: ./.github/actions/check-changes
+      id: checks
+      with:
+        checkGlob: "'*.c' '*.h' '*.cpp' '*.hpp' '*.m' '*.mm'"
+        diffFilter: 'ACM'
+
     - name: Install Dependencies 🛍️
-      if: runner.os == 'Linux'
+      if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)
       shell: bash
       run: |
         : Install Dependencies 🛍️
@@ -33,29 +40,21 @@ runs:
         echo ::endgroup::
 
     - name: Run clang-format 🐉
+      if: fromJSON(steps.checks.outputs.hasChangedFiles)
       id: result
       shell: zsh --no-rcs --errexit --pipefail {0}
       working-directory: ${{ inputs.workingDirectory }}
       env:
-        GITHUB_EVENT_FORCED: ${{ github.event.forced }}
-        GITHUB_REF_BEFORE: ${{ github.event.before }}
+        CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}
       run: |
         : Run clang-format 🐉
         if (( ${+RUNNER_DEBUG} )) setopt XTRACE
 
-        local -a changes=($(git diff --name-only HEAD~1 HEAD))
-        case ${GITHUB_EVENT_NAME} {
-          pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;;
-          push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;;
-          *) ;;
-        }
-
-        if (( ${changes[(I)(*.c|*.h|*.cpp|*.hpp|*.m|*.mm)]} )) {
-          echo ::group::Install clang-format-17
-          brew install --quiet obsproject/tools/clang-format@17
-          echo ::endgroup::
+        print ::group::Install clang-format-17
+        brew install --quiet obsproject/tools/clang-format@17
+        print ::endgroup::
 
-          echo ::group::Run clang-format-17
-          ./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check
-          echo ::endgroup::
-        }
+        print ::group::Run clang-format-17
+        local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/})
+        ./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check ${changes}
+        print ::endgroup::

+ 0 - 59
.github/actions/run-cmake-format/action.yaml

@@ -1,59 +0,0 @@
-name: Run cmake-format
-description: Runs cmake-format and checks for any changes introduced by it
-inputs:
-  failCondition:
-    description: Controls whether failed checks also fail the workflow run
-    required: false
-    default: 'never'
-  workingDirectory:
-    description: Working directory for checks
-    required: false
-    default: ${{ github.workspace }}
-runs:
-  using: composite
-  steps:
-    - name: Check Runner Operating System 🏃‍♂️
-      if: runner.os == 'Windows'
-      shell: bash
-      run: |
-        : Check Runner Operating System 🏃‍♂️
-        echo "::notice::run-cmake-format action requires a macOS-based or Linux-based runner."
-        exit 2
-
-    - name: Install Dependencies 🛍️
-      if: runner.os == 'Linux'
-      shell: bash
-      run: |
-        : Install Dependencies 🛍️
-        echo ::group::Install Dependencies
-        eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
-        echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
-        brew install --quiet zsh
-        echo ::endgroup::
-
-    - name: Run cmake-format 🎛️
-      id: result
-      shell: zsh --no-rcs --errexit --pipefail {0}
-      working-directory: ${{ github.workspace }}
-      env:
-        GITHUB_EVENT_FORCED: ${{ github.event.forced }}
-        GITHUB_REF_BEFORE: ${{ github.event.before }}
-      run: |
-        : Run cmake-format 🎛️
-        if (( ${+RUNNER_DEBUG} )) setopt XTRACE
-
-        local -a changes=($(git diff --name-only HEAD~1 HEAD))
-        case ${GITHUB_EVENT_NAME} {
-          pull_request) changes=($(git diff --name-only origin/${GITHUB_BASE_REF} HEAD)) ;;
-          push) if [[ ${GITHUB_EVENT_FORCED} != true ]] changes=($(git diff --name-only ${GITHUB_REF_BEFORE} HEAD)) ;;
-          *) ;;
-        }
-
-        if (( ${changes[(I)*.cmake|*CMakeLists.txt]} )) {
-          echo ::group::Install cmakelang
-          pip3 install cmakelang
-          echo ::endgroup::
-          echo ::group::Run cmake-format
-          ./build-aux/run-cmake-format --fail-${{ inputs.failCondition }} --check
-          echo ::endgroup::
-        }

+ 59 - 0
.github/actions/run-gersemi/action.yaml

@@ -0,0 +1,59 @@
+name: Run gersemi
+description: Runs gersemi and checks for any changes introduced by it
+inputs:
+  failCondition:
+    description: Controls whether failed checks also fail the workflow run
+    required: false
+    default: never
+  workingDirectory:
+    description: Working directory for checks
+    required: false
+    default: ${{ github.workspace }}
+runs:
+  using: composite
+  steps:
+    - name: Check Runner Operating System 🏃‍♂️
+      if: runner.os == 'Windows'
+      shell: bash
+      run: |
+        : Check Runner Operating System 🏃‍♂️
+        echo "::notice::run-gersemi action requires a macOS-based or Linux-based runner."
+        exit 2
+
+    - name: Check for Changed Files ✅
+      uses: ./.github/actions/check-changes
+      id: checks
+      with:
+        checkGlob: "'*.cmake' '*CMakeLists.txt'"
+        diffFilter: 'ACM'
+
+    - name: Install Dependencies 🛍️
+      if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)
+      shell: bash
+      run: |
+        : Install Dependencies 🛍️
+        echo ::group::Install Dependencies
+        eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
+        echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH
+        brew install --quiet zsh
+        echo ::endgroup::
+
+    - name: Run gersemi 🎛️
+      if: fromJSON(steps.checks.outputs.hasChangedFiles)
+      id: result
+      shell: zsh --no-rcs --errexit --pipefail {0}
+      working-directory: ${{ github.workspace }}
+      env:
+        CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}
+      run: |
+        : Run gersemi 🎛️
+        if (( ${+RUNNER_DEBUG} )) setopt XTRACE
+
+        print ::group::Install gersemi
+        brew install --quiet obsproject/tools/gersemi
+        print ::endgroup::
+
+        print ::group::Run gersemi
+        local -a changes=(${(s:,:)CHANGED_FILES//[\[\]\'\"]/})
+        ./build-aux/run-gersemi --fail-${{ inputs.failCondition }} --check ${changes}
+        print ::endgroup::

+ 1 - 1
.gitignore

@@ -8,7 +8,7 @@
 !/data
 !/src
 !.clang-format
-!.cmake-format.json
+!.gersemirc
 !.gitignore
 !buildspec.json
 !CMakeLists.txt

+ 139 - 55
build-aux/.run-format.zsh

@@ -16,7 +16,7 @@ setopt FUNCTION_ARGZERO
 # setopt WARN_NESTED_VAR
 # setopt XTRACE
 
-autoload -Uz is-at-least && if ! is-at-least 5.2; then
+autoload -Uz is-at-least && if ! is-at-least 5.8; then
   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."
   exit 1
 fi
@@ -27,52 +27,113 @@ invoke_formatter() {
     exit 2
   }
 
-  case ${1} {
+  local formatter="${1}"
+  shift
+  local -a source_files=(${@})
+
+  case ${formatter} {
     clang)
       if (( ${+commands[clang-format-17]} )) {
         local formatter=clang-format-17
       } elif (( ${+commands[clang-format]} )) {
         local formatter=clang-format
-        local -a formatter_version=($(clang-format --version))
-
-        if ! is-at-least 17.0.3 ${formatter_version[-1]}; then
-          log_error "clang-format is not version 17.0.3 or above (found ${formatter_version[-1]}."
-          exit 2
-        fi
-
-        if ! is-at-least ${formatter_version[-1]} 17.0.3; then
-          log_error "clang-format is more recent than version 17.0.3 (found ${formatter_version[-1]})."
-          exit 2
-        fi
       } else {
         log_error "No viable clang-format version found (required 17.0.3)"
         exit 2
       }
 
-      local -a source_files=(src/**/*.(c|cpp|h|hpp|m|mm)(.N))
+      local -a formatter_version=($(${formatter} --version))
+
+      if ! is-at-least 17.0.3 ${formatter_version[-1]}; then
+        log_error "clang-format is not version 17.0.3 or above (found ${formatter_version[-1]}."
+        exit 2
+      fi
+
+      if ! is-at-least ${formatter_version[-1]} 17.0.3; then
+        log_error "clang-format is more recent than version 17.0.3 (found ${formatter_version[-1]})."
+        exit 2
+      fi
+
+      if (( ! #source_files )) source_files=(src/**/*.(c|cpp|h|hpp|m|mm)(.N))
 
       local -a format_args=(-style=file -fallback-style=none)
       if (( _loglevel > 2 )) format_args+=(--verbose)
+
+      check_files() {
+        local -i num_failures=0
+        local -a source_files=($@)
+        local file
+        local -a format_args=(-style=file -fallback-style=none)
+        if (( _loglevel > 2 )) format_args+=(--verbose)
+
+        local -a command=(${formatter} ${format_args})
+
+        for file (${source_files}) {
+          if ! ${command} "${file}" | diff -q "${file}" - &> /dev/null; then
+            log_error "${file} requires formatting changes."
+            if (( fail_on_error == 2 )) return 2;
+            num_failures=$(( num_failures + 1 ))
+          fi
+        }
+        if (( num_failures && fail_on_error == 1 )) return 2
+      }
+
+      format_files() {
+        local -a source_files=($@)
+
+        if (( ${#source_files} )) {
+          local -a format_args=(-style=file -fallback-style=none -i)
+          if (( _loglevel > 2 )) format_args+=(--verbose)
+
+          "${formatter}" ${format_args} ${source_files}
+        }
+      }
       ;;
-    cmake)
-      local formatter=cmake-format
-      if (( ${+commands[cmake-format]} )) {
-        local cmake_format_version=$(cmake-format --version)
+    gersemi)
+      local formatter=gersemi
+      if (( ${+commands[gersemi]} )) {
+        local gersemi_version=($(gersemi --version))
 
-        if ! is-at-least 0.6.13 ${cmake_format_version}; then
-          log_error "cmake-format is not version 0.6.13 or above (found ${cmake_format_version})."
+        if ! is-at-least 0.12.0 ${gersemi_version[2]}; then
+          log_error "gersemi is not version 0.12.0 or above (found ${gersemi_version[2]}."
           exit 2
         fi
-      } else {
-        log_error "No viable cmake-format version found (required 0.6.13)"
-        exit 2
       }
 
-      local -a source_files=(**/(CMakeLists.txt|*.cmake)(.N))
-      source_files=(${source_files:#(build_*)/*})
+      if (( ! #source_files )) source_files=(CMakeLists.txt (cmake)/**/(CMakeLists.txt|*.cmake)(.N))
+
+      check_files() {
+        local -i num_failures=0
+        local -a source_files=($@)
+        local file
+        local -a command=(${formatter} -c --no-cache ${source_files})
+
+        if (( ${#source_files} )) {
+          while read -r line; do
+            local -a line_tokens=(${(z)line})
+            if (( #line_tokens )) {
+              file=${line_tokens[1]//*${project_root}\//}
 
-      local -a format_args=()
-      if (( _loglevel > 2 )) format_args+=(--log-level debug)
+              log_error "${file} requires formatting changes."
+            } else {
+              log_error "${line}"
+            }
+
+            if (( fail_on_error == 2 )) return 2
+            num_failures=$(( num_failures + 1 ))
+          done < <(${command} 2>&1)
+
+          if (( num_failures && fail_on_error == 1 )) return 2
+        }
+      }
+
+      format_files() {
+        local -a source_files=($@)
+
+        if (( ${#source_files} )) {
+          "${formatter}" -i ${source_files}
+        }
+      }
       ;;
     swift)
       local formatter=swift-format
@@ -88,50 +149,75 @@ invoke_formatter() {
         exit 2
       }
 
-      local -a source_files=(**/*.swift(.N))
+      if (( ! #source_files )) source_files=(src/**/*.swift(.N))
+
+      check_files() {
+        local -i num_failures=0
+        local -a source_files=($@)
+        local file
+        local -a format_args=()
 
-      local -a format_args=()
+        local -a command=(${formatter} ${format_args})
+
+        for file (${source_files}) {
+          if ! "${command}" "${file}" | diff -q "${file}" - &> /dev/null; then
+            log_error "${file} requires formatting changes."
+            if (( fail_on_error == 2 )) return 2;
+            num_failures=$(( num_failures + 1 ))
+          fi
+        }
+        if (( num_failures && fail_on_error == 1 )) return 2
+      }
+
+      format_files() {
+        local -a source_files=($@)
+
+        if (( ${#source_files} )) {
+          local -a format_args=(-i)
+
+          "${formatter}" ${format_args} ${source_files}
+        }
+      }
       ;;
-    *) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, cmake-format, and swift-format."; exit 2 ;;
+    *) log_error "Invalid formatter specified: ${1}. Valid options are clang-format, gersemi, and swift-format."; exit 2 ;;
   }
 
+
   local file
   local -i num_failures=0
   if (( check_only )) {
-    for file (${source_files}) {
-      if (( _loglevel > 1 )) log_info "Checking format of ${file}..."
-
-      if ! "${formatter}" ${format_args} "${file}" | diff -q "${file}" - &> /dev/null; then
-        log_error "${file} requires formatting changes."
-
-        if (( fail_on_error == 2 )) return 2;
-        num_failures=$(( num_failures + 1 ))
-      else
-        if (( _loglevel > 1 )) log_status "${file} requires no formatting changes."
-      fi
+    if (( ${+functions[check_files]} )) {
+      check_files ${source_files}
+    } else {
+      log_error "No format check function defined for formatter '${formatter}'"
+      exit 2
+    }
+  } else {
+    if (( ${+functions[format_files]} )) {
+      format_files ${source_files}
+    } else {
+      log_error "No format function defined for formatter '${formatter}'"
+      exit 2
     }
-    if (( fail_on_error && num_failures )) return 2;
-  } elif (( ${#source_files} )) {
-    format_args+=(-i)
-    "${formatter}" ${format_args} ${source_files}
   }
 }
 
 run_format() {
   if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}
   if (( ! ${+FORMATTER_NAME} )) typeset -g FORMATTER_NAME=${${(s:-:)ZSH_ARGZERO:t:r}[2]}
+  local project_root=${SCRIPT_HOME:A:h}
 
   typeset -g host_os=${${(L)$(uname -s)}//darwin/macos}
   local -i fail_on_error=0
   local -i check_only=0
   local -i verbosity=1
-  local -r _version='1.0.0'
+  local -r _version='2.0.0'
 
   fpath=("${SCRIPT_HOME}/.functions" ${fpath})
   autoload -Uz log_info log_error log_output set_loglevel log_status log_warning
 
   local -r _usage="
-Usage: %B${functrace[1]%:*}%b <option>
+Usage: %B${functrace[1]%:*}%b <option> [SOURCE_FILES]
 
 %BOptions%b:
 
@@ -153,13 +239,8 @@ Usage: %B${functrace[1]%:*}%b <option>
   local -a args
   while (( # )) {
     case ${1} {
-      --)
-        shift
-        args+=($@)
-        break
-        ;;
       -c|--check) check_only=1; shift ;;
-      -v|--verbose) (( _verbosity += 1 )); shift ;;
+      -v|--verbose) (( verbosity += 1 )); shift ;;
       -h|--help) log_output ${_usage}; exit 0 ;;
       -V|--version) print -Pr "${_version}"; exit 0 ;;
       --debug) verbosity=3; shift ;;
@@ -175,14 +256,17 @@ Usage: %B${functrace[1]%:*}%b <option>
         fail_on_error=2
         shift
         ;;
-      *) log_error "Unknown option: %B${1}%b"; log_output ${_usage}; exit 2 ;;
+      *)
+        args+=($@)
+        break
+        ;;
     }
   }
 
   set -- ${(@)args}
   set_loglevel ${verbosity}
 
-  invoke_formatter ${FORMATTER_NAME}
+  invoke_formatter ${FORMATTER_NAME} ${args}
 }
 
 run_format ${@}

+ 0 - 0
build-aux/run-cmake-format → build-aux/run-gersemi