7f37e069d71980bcb9894009e3bf7298f45963ed
[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     # Exceptional case: one-cmds don't have '.py' extension
199     if [[ ${f} == compiler/one-cmds/* ]]; then
200       # Ignore non-python source (cmake, etc)
201       # Ignore shell script: one-prepare-venv
202       if [[ ${f} != compiler/one-cmds/*.* ]] && [[ ${f} != compiler/one-cmds/one-prepare-venv ]]; then
203         FILES_TO_CHECK_PYTHON+=("${f}")
204       fi
205     fi
206   done
207   for s in ${DIRECTORIES_NOT_TO_BE_TESTED[@]}; do
208     skip=${s#'.'/}/
209     FILES_TO_CHECK_PYTHON=(${FILES_TO_CHECK_PYTHON[*]/$skip*/})
210   done
211
212   if [[ ${#FILES_TO_CHECK_PYTHON} -ne 0 ]]; then
213     yapf -i ${FILES_TO_CHECK_PYTHON[@]}
214     EXIT_CODE=$?
215     if [[ ${EXIT_CODE} -ne 0 ]]; then
216       INVALID_EXIT=${EXIT_CODE}
217     fi
218   fi
219 }
220
221 pushd ${NNAS_PROJECT_PATH}
222
223 if [[ -n "$(git diff)" ]] && { [[ "${CHECK_DIFF_ONLY}" != "1" ]] || [[ "${CHECK_STAGED_ONLY}" != "1" ]]; }; then
224   echo "[WARNING] Commit all the changes before running format check"
225   echo "          ${PATCH_FILE} file will contain unstaged files"
226 fi
227
228 __Check_CPP=${CHECK_CPP:-"1"}
229 __Check_PYTHON=${CHECK_PYTHON:-"1"}
230
231 FILES_TO_CHECK=$(git ls-files -c --exclude-standard ${DIRECTORIES_TO_BE_TESTED[@]})
232 if [[ "${CHECK_DIFF_ONLY}" = "1" ]]; then
233   MASTER_EXIST=$(git rev-parse --verify master)
234   CURRENT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2-)
235   DIFF_COMMITS=`git log --graph --oneline master..HEAD | wc -l`
236   if [[ -z "${MASTER_EXIST}" ]]; then
237     echo "Cannot found local master branch"
238   elif [[ "${CURRENT_BRANCH}" = "master" ]]; then
239     echo "Current branch is master"
240   else
241     if [[ "${CHECK_STAGED_ONLY}" = "1" ]]; then
242       FILES_TO_CHECK=$(git diff --staged --name-only --diff-filter=d)
243     else
244       FILES_TO_CHECK=$(git diff --name-only --diff-filter=d HEAD~${DIFF_COMMITS})
245     fi
246   fi
247 fi
248
249 for DIR_NOT_TO_BE_TESTED in $(git ls-files -co --exclude-standard '*/.FORMATDENY'); do
250   DIRECTORIES_NOT_TO_BE_TESTED+=($(dirname "${DIR_NOT_TO_BE_TESTED}"))
251 done
252
253 exclude_symbolic_links
254 check_newline
255 check_permission
256 check_cpp_files
257 check_python_files
258
259 if [[ "${CHECK_DIFF_ONLY}" = "1" ]] && [[ "${CHECK_STAGED_ONLY}" = "1" ]]; then
260   if [[ ! -z "${FILES_TO_CHECK}" ]]; then
261     DIFF=$(git diff ${FILES_TO_CHECK} | tee ${PATCH_FILE})
262   fi
263 else
264   DIFF=$(git diff | tee ${PATCH_FILE})
265 fi
266
267 popd
268
269 if [[ -z "${CRCHECK}" ]] && [[ ! -n "${DIFF}" ]] && [[ ${INVALID_EXIT} -eq 0 ]]; then
270   echo "[PASSED] Format checker succeed."
271   return
272 fi
273
274 # Something went wrong
275
276 if [[ ! -z "${CRCHECK}" ]]; then
277   echo "[FAILED] Please use LF for newline for following files."
278   echo "${CRCHECK}"
279 fi
280
281 if [[ -s ${PATCH_FILE} ]]; then
282   echo "[FAILED] Format checker failed and update code to follow convention."
283   echo "         You can find changes in ${PATCH_FILE}"
284 fi
285
286 if [[ ${INVALID_EXIT} -ne 0 ]]; then
287   echo "[[FAILED] Invalid format checker exit."
288 fi
289
290 exit 1