Imported Upstream version 1.7.0
[platform/core/ml/nnfw.git] / infra / command / format
1 #!/bin/bash
2
3 INVALID_EXIT=0
4 FILES_TO_CHECK=()
5 DIRECTORIES_TO_BE_TESTED=()
6 DIRECTORIES_NOT_TO_BE_TESTED=()
7 CLANG_FORMAT_CANDIDATES=()
8 PATCH_FILE=format.patch
9 CHECK_DIFF_ONLY="0"
10 CHECK_STAGED_ONLY="0"
11
12 function Usage()
13 {
14   echo "Usage: $0 $(basename ${BASH_SOURCE[0]}) [OPTIONS] [<file|dir> ...]"
15   echo "If no arguments are specified, it formats all nnas codes"
16   echo "If <file>s are given, it reformats the files"
17   echo ""
18   echo "Options:"
19   echo "      --clang-format <TOOL>     clang format bin (default: clang-format-3.9, clang-format)"
20   echo "      --diff-only               check diff files with master"
21   echo "      --staged-only             check git staged files"
22 }
23
24 while [[ $# -gt 0 ]]
25 do
26   arg="$1"
27   case $arg in
28     -h|--help|help)
29       Usage
30       exit 0
31       ;;
32     --clang-format)
33       CLANG_FORMAT_CANDIDATES=($2)
34       shift 2
35       ;;
36     --clang-format=*)
37       CLANG_FORMAT_CANDIDATES=(${1#*=})
38       shift
39       ;;
40     --staged-only)
41       CHECK_STAGED_ONLY="1"
42       CHECK_DIFF_ONLY="1"
43       shift
44       ;;
45     --diff-only)
46       CHECK_DIFF_ONLY="1"
47       shift
48       ;;
49     *)
50       DIRECTORIES_TO_BE_TESTED+=($1)
51       shift
52       ;;
53   esac
54 done
55
56 function pushd () {
57   command pushd "$@" > /dev/null
58 }
59
60 function popd () {
61   command popd "$@" > /dev/null
62 }
63
64 function command_exists() {
65   command -v $1 > /dev/null 2>&1
66 }
67
68 function exclude_symbolic_links() {
69   # Check all files (CMakeLists.txt, *.cl, ... not only for C++, Python)
70   if [[ ${#FILES_TO_CHECK} -ne 0 ]]; then
71     FILES_EXCLUDE_SYMLINKS=$(file ${FILES_TO_CHECK} | grep -v "symbolic link" | cut -d':' -f1)
72     FILES_TO_CHECK=${FILES_EXCLUDE_SYMLINKS}
73   fi
74 }
75
76 function check_newline() {
77   FILES_TO_CHECK_CR=()
78   for f in ${FILES_TO_CHECK[@]}; do
79     # Manually ignore style checking
80     if [[ ${f} == !(*.svg|*.pdf|*.png) ]]; then
81       FILES_TO_CHECK_CR+=("${f}")
82     fi
83   done
84
85   # Check all files (CMakeLists.txt, *.cl, ... not only for C++, Python)
86   if [[ ${#FILES_TO_CHECK_CR} -ne 0 ]]; then
87     CRCHECK=$(file ${FILES_TO_CHECK_CR} | grep 'with CR')
88   else
89     return
90   fi
91   FILES_TO_FIX=($(echo "$CRCHECK" | grep "with CRLF line" | cut -d':' -f1))
92   for f in ${FILES_TO_FIX[@]}; do
93     tr -d '\r' < $f > $f.fixed && cat $f.fixed > $f && rm $f.fixed
94   done
95   FILES_TO_FIX=($(echo "${CRCHECK}" | grep "with CR line" | cut -d':' -f1))
96   for f in ${FILES_TO_FIX[@]}; do
97     tr '\r' '\n' < $f > $f.fixed && cat $f.fixed > $f && rm $f.fixed
98   done
99   # Check no new line at end of file
100   for f in ${FILES_TO_CHECK_CR[@]}; do
101     if diff /dev/null "$f" | tail -1 | grep '^\\ No newline' > /dev/null; then
102       echo >> "$f"
103     fi
104   done
105 }
106
107 function check_permission() {
108   # Check all files except script
109   FILES_TO_CHECK_PERMISSION=()
110   for f in ${FILES_TO_CHECK[@]}; do
111     # Manually ignore permission checking
112     if [[ ${f} == !(nnas|nnfw|nncc|*.sh|*.py|*/gradlew) ]] || [[ ${f} == tests/nnapi/specs/**/*.py ]]; then
113       FILES_TO_CHECK_PERMISSION+=("${f}")
114     fi
115   done
116
117   if [[ ${#FILES_TO_CHECK_PERMISSION} -eq 0 ]]; then
118     return
119   fi
120   for FILE_TO_CHECK in ${FILES_TO_CHECK_PERMISSION[@]}; do
121     RESULT=$(stat -c '%A' ${FILE_TO_CHECK} | grep 'x')
122     if [ "${RESULT}" != "" ]; then
123       chmod a-x ${FILE_TO_CHECK}
124     fi
125   done
126 }
127
128 function check_cpp_files() {
129   if [[ ${__Check_CPP} -eq 0 ]]; then
130     echo "[SKIPPED] C/C++ check is skipped"
131     return
132   fi
133
134   CLANG_FORMAT_CANDIDATES+=("clang-format-3.9")
135   CLANG_FORMAT_CANDIDATES+=("clang-format")
136
137   for CLANG_FORMAT_CANDIDATE in ${CLANG_FORMAT_CANDIDATES[@]}; do
138     if command_exists ${CLANG_FORMAT_CANDIDATE} ; then
139       CLANG_FORMAT="${CLANG_FORMAT_CANDIDATE}"
140       break
141     fi
142   done
143
144   if [[ -z ${CLANG_FORMAT}  ]]; then
145     echo "[ERROR] clang-format is unavailable"
146     echo
147     echo "Please install clang-format before running format check"
148     exit 1
149   fi
150
151   # Check c++ files
152   FILES_TO_CHECK_CPP=()
153   for f in ${FILES_TO_CHECK[@]}; do
154     # Manually ignore style checking
155     if [[ ${f} == +(*/NeuralNetworks.h|*/NeuralNetworksExtensions.h) ]]; then
156       continue
157     fi
158
159     # File extension to check
160     if [[ ${f} == +(*.h|*.hpp|*.cpp|*.cc|*.c|*.cl) ]]; then
161       FILES_TO_CHECK_CPP+=("${f}")
162     fi
163   done
164
165   # Skip by '.FORMATDENY' file
166   for s in ${DIRECTORIES_NOT_TO_BE_TESTED[@]}; do
167     FILES_TO_CHECK_CPP=(${FILES_TO_CHECK_CPP[*]/$s*/})
168   done
169
170   if [[ ${#FILES_TO_CHECK_CPP} -ne 0 ]]; then
171     ${CLANG_FORMAT} -i ${FILES_TO_CHECK_CPP[@]}
172     EXIT_CODE=$?
173     if [[ ${EXIT_CODE} -ne 0 ]]; then
174       INVALID_EXIT=${EXIT_CODE}
175     fi
176   fi
177 }
178
179 function check_python_files() {
180   if [[ ${__Check_PYTHON} -eq 0 ]]; then
181     echo "[SKIPPED] Python check is skipped"
182     return
183   fi
184
185   if ! command_exists yapf; then
186     echo "[ERROR] yapf is unavailable"
187     echo "       Please install yapf."
188     exit 1
189   fi
190
191   # Check python files
192   FILES_TO_CHECK_PYTHON=()
193   for f in ${FILES_TO_CHECK[@]}; do
194     # File extension to check
195     if [[ ${f} == *.py ]]; then
196       FILES_TO_CHECK_PYTHON+=("${f}")
197     fi
198   done
199   for s in ${DIRECTORIES_NOT_TO_BE_TESTED[@]}; do
200     skip=${s#'.'/}/
201     FILES_TO_CHECK_PYTHON=(${FILES_TO_CHECK_PYTHON[*]/$skip*/})
202   done
203
204   if [[ ${#FILES_TO_CHECK_PYTHON} -ne 0 ]]; then
205     yapf -i --style='{based_on_style: pep8, column_limit: 90}' ${FILES_TO_CHECK_PYTHON[@]}
206     EXIT_CODE=$?
207     if [[ ${EXIT_CODE} -ne 0 ]]; then
208       INVALID_EXIT=${EXIT_CODE}
209     fi
210   fi
211 }
212
213 pushd ${NNAS_PROJECT_PATH}
214
215 if [[ -n "$(git diff)" ]] && { [[ "${CHECK_DIFF_ONLY}" != "1" ]] || [[ "${CHECK_STAGED_ONLY}" != "1" ]]; }; then
216   echo "[WARNING] Commit all the changes before running format check"
217   echo "          ${PATCH_FILE} file will contain unstaged files"
218 fi
219
220 __Check_CPP=${CHECK_CPP:-"1"}
221 __Check_PYTHON=${CHECK_PYTHON:-"1"}
222
223 FILES_TO_CHECK=$(git ls-files -c --exclude-standard ${DIRECTORIES_TO_BE_TESTED[@]})
224 if [[ "${CHECK_DIFF_ONLY}" = "1" ]]; then
225   MASTER_EXIST=$(git rev-parse --verify master)
226   CURRENT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2-)
227   DIFF_COMMITS=`git log --graph --oneline master..HEAD | wc -l`
228   if [[ -z "${MASTER_EXIST}" ]]; then
229     echo "Cannot found local master branch"
230   elif [[ "${CURRENT_BRANCH}" = "master" ]]; then
231     echo "Current branch is master"
232   else
233     if [[ "${CHECK_STAGED_ONLY}" = "1" ]]; then
234       FILES_TO_CHECK=$(git diff --staged --name-only --diff-filter=d)
235     else
236       FILES_TO_CHECK=$(git diff --name-only --diff-filter=d HEAD~${DIFF_COMMITS})
237     fi
238   fi
239 fi
240
241 for DIR_NOT_TO_BE_TESTED in $(git ls-files -co --exclude-standard '*/.FORMATDENY'); do
242   DIRECTORIES_NOT_TO_BE_TESTED+=($(dirname "${DIR_NOT_TO_BE_TESTED}"))
243 done
244
245 exclude_symbolic_links
246 check_newline
247 check_permission
248 check_cpp_files
249 check_python_files
250
251 if [[ "${CHECK_DIFF_ONLY}" = "1" ]] && [[ "${CHECK_STAGED_ONLY}" = "1" ]]; then
252   if [[ ! -z "${FILES_TO_CHECK}" ]]; then
253     DIFF=$(git diff ${FILES_TO_CHECK} | tee ${PATCH_FILE})
254   fi
255 else
256   DIFF=$(git diff | tee ${PATCH_FILE})
257 fi
258
259 popd
260
261 if [[ -z "${CRCHECK}" ]] && [[ ! -n "${DIFF}" ]] && [[ ${INVALID_EXIT} -eq 0 ]]; then
262   echo "[PASSED] Format checker succeed."
263   return
264 fi
265
266 # Something went wrong
267
268 if [[ ! -z "${CRCHECK}" ]]; then
269   echo "[FAILED] Please use LF for newline for following files."
270   echo "${CRCHECK}"
271 fi
272
273 if [[ -s ${PATCH_FILE} ]]; then
274   echo "[FAILED] Format checker failed and update code to follow convention."
275   echo "         You can find changes in ${PATCH_FILE}"
276 fi
277
278 if [[ ${INVALID_EXIT} -ne 0 ]]; then
279   echo "[[FAILED] Invalid format checker exit."
280 fi
281
282 exit 1