xref: /openbmc/openbmc-build-scripts/scripts/format-code.sh (revision 38515a37856448161852c20f22f11c9273afcb78)
1#!/bin/bash
2
3# This script reformats source files using the clang-format utility.
4#
5# Files are changed in-place, so make sure you don't have anything open in an
6# editor, and you may want to commit before formatting in case of awryness.
7#
8# This must be run on a clean repository to succeed
9#
10# Input parmameter must be full path to git repo to scan
11
12DIR=$1
13WORKSPACE=$PWD
14WORKSPACE_CONFIG="${WORKSPACE}/openbmc-build-scripts/config"
15
16set -e
17
18cd "${DIR}"
19echo "Formatting code under $DIR/"
20
21ALL_OPERATIONS=( \
22        clang_format \
23        commit_gitlint \
24        commit_spelling \
25        eslint \
26        pycodestyle \
27        shellcheck \
28    )
29
30function do_commit_spelling() {
31    echo "Running spelling check on Commit Message"
32
33    # Run the codespell with openbmc spcific spellings on the patchset
34    echo "openbmc-dictionary - misspelling count >> "
35    sed "s/Signed-off-by.*//" ".git/COMMIT_EDITMSG" | \
36        codespell -D "${WORKSPACE_CONFIG}/openbmc-spelling.txt" -d --count -
37
38    # Run the codespell with generic dictionary on the patchset
39    echo "generic-dictionary - misspelling count >> "
40    sed "s/Signed-off-by.*//" ".git/COMMIT_EDITMSG" | \
41        codespell --builtin clear,rare,en-GB_to_en-US -d --count -
42}
43
44function do_commit_gitlint() {
45    # Check for commit message issues
46    gitlint \
47        --extra-path "${WORKSPACE_CONFIG}/gitlint/" \
48        --config "${WORKSPACE_CONFIG}/.gitlint"
49}
50
51function do_eslint() {
52    if [[ -f ".eslintignore" ]]; then
53        ESLINT_IGNORE="--ignore-path .eslintignore"
54    elif [[ -f ".gitignore" ]]; then
55        ESLINT_IGNORE="--ignore-path .gitignore"
56    fi
57
58    # Get the eslint configuration from the repository
59    if [[ -f ".eslintrc.json" ]]; then
60        echo "Running the json validator on the repo using it's config > "
61        ESLINT_RC="-c .eslintrc.json"
62    else
63        echo "Running the json validator on the repo using the global config"
64        ESLINT_RC="--no-eslintrc -c ${WORKSPACE_CONFIG}/eslint-global-config.json"
65    fi
66
67    ESLINT_COMMAND="eslint . ${ESLINT_IGNORE} ${ESLINT_RC} \
68               --ext .json --format=stylish \
69               --resolve-plugins-relative-to /usr/local/lib/node_modules \
70               --no-error-on-unmatched-pattern"
71
72    # Print eslint command
73    echo "$ESLINT_COMMAND"
74    # Run eslint
75    $ESLINT_COMMAND
76}
77
78function do_pycodestyle() {
79    if [[ -f "setup.cfg" ]]; then
80        pycodestyle --show-source --exclude=subprojects .
81        rc=$?
82        if [[ ${rc} -ne 0 ]]; then
83            exit ${rc}
84        fi
85    fi
86}
87
88function do_shellcheck() {
89    # If .shellcheck exists, stop on error.  Otherwise, allow pass.
90    if [[ -f ".shellcheck" ]]; then
91        local shellcheck_allowfail="false"
92    else
93        local shellcheck_allowfail="true"
94    fi
95
96    # Run shellcheck on any shell-script.
97    shell_scripts="$(git ls-files | xargs -n1 file -0 | \
98    grep -a "shell script" | cut -d '' -f 1)"
99    for script in ${shell_scripts}; do
100        shellcheck --color=never -x "${script}" || ${shellcheck_allowfail}
101    done
102}
103
104
105do_clang_format() {
106    # Allow called scripts to know which clang format we are using
107    export CLANG_FORMAT="clang-format"
108    IGNORE_FILE=".clang-ignore"
109    declare -a IGNORE_LIST
110
111    if [[ -f "${IGNORE_FILE}" ]]; then
112        readarray -t IGNORE_LIST < "${IGNORE_FILE}"
113    fi
114
115    ignorepaths=""
116    ignorefiles=""
117
118    for path in "${IGNORE_LIST[@]}"; do
119        # Check for comment, line starting with space, or zero-length string.
120        # Checking for [[:space:]] checks all options.
121        if [[ -z "${path}" ]] || [[ "${path}" =~ ^(#|[[:space:]]).*$ ]]; then
122            continue
123        fi
124
125        # All paths must start with ./ for find's path prune expectation.
126        if [[ "${path}" =~ ^\.\/.+$ ]]; then
127            ignorepaths+=" ${path}"
128        else
129            ignorefiles+=" ${path}"
130        fi
131    done
132
133    searchfiles=""
134    while read -r path; do
135        # skip ignorefiles
136        if [[ $ignorefiles == *"$(basename "${path}")"* ]]; then
137            continue
138        fi
139
140        skip=false
141        #skip paths in ingorepaths
142        for pathname in $ignorepaths; do
143            if [[ "./${path}" == "${pathname}"* ]]; then
144                skip=true
145                break
146            fi
147        done
148
149        if [ "$skip" = true ]; then
150            continue
151        fi
152        # shellcheck disable=2089
153        searchfiles+="\"./${path}\" "
154
155        # Get C and C++ files managed by git and skip the mako files
156    done <<<"$(git ls-files | grep -e '\.[ch]pp$' -e '\.[ch]$' | grep -v '\.mako\.')"
157
158    if [[ -f ".clang-format" ]]; then
159        # shellcheck disable=SC2090 disable=SC2086
160        echo ${searchfiles} | xargs "${CLANG_FORMAT}" -i
161        git --no-pager diff --exit-code
162    fi
163
164}
165
166for op in "${ALL_OPERATIONS[@]}"; do
167    "do_$op"
168done
169
170# Sometimes your situation is terrible enough that you need the flexibility.
171# For example, phosphor-mboxd.
172if [[ -f "format-code.sh" ]]; then
173    ./format-code.sh
174    git --no-pager diff --exit-code
175fi
176