5 DIRECTORIES_TO_BE_TESTED=()
6 DIRECTORIES_NOT_TO_BE_TESTED=()
7 CLANG_FORMAT_CANDIDATES=()
8 PATCH_FILE=format.patch
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"
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"
33 CLANG_FORMAT_CANDIDATES=($2)
37 CLANG_FORMAT_CANDIDATES=(${1#*=})
50 DIRECTORIES_TO_BE_TESTED+=($1)
57 command pushd "$@" > /dev/null
61 command popd "$@" > /dev/null
64 function command_exists() {
65 command -v $1 > /dev/null 2>&1
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}
76 function check_newline() {
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}")
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')
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
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
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
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}")
117 if [[ ${#FILES_TO_CHECK_PERMISSION} -eq 0 ]]; then
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}
128 function check_cpp_files() {
129 if [[ ${__Check_CPP} -eq 0 ]]; then
130 echo "[SKIPPED] C/C++ check is skipped"
134 CLANG_FORMAT_CANDIDATES+=("clang-format-3.9")
135 for CLANG_FORMAT_CANDIDATE in ${CLANG_FORMAT_CANDIDATES[@]}; do
136 if command_exists ${CLANG_FORMAT_CANDIDATE} ; then
137 CLANG_FORMAT="${CLANG_FORMAT_CANDIDATE}"
142 if [[ -z ${CLANG_FORMAT} ]]; then
143 echo "[ERROR] clang-format-3.9 is unavailable"
145 echo " Please install clang-format-3.9 before running format check"
149 # Migration to clang-format-8
150 # TODO Remove this after migration to clang-format-8
151 CLANG_FORMAT_8="clang-format-8"
152 if ! command_exists $CLANG_FORMAT_8_CANDIDATE; then
153 echo "[ERROR] clang-format-8 is unavailable"
155 echo " Please install clang-format-8 before running format check"
156 echo " (or use latest docker image if you are using docker for format check)"
159 for DIR_CLANG_FORMAT_8 in $(git ls-files -co --exclude-standard '*/.clang-format'); do
160 DIRECTORIES_USE_CLANG_FORMAT_8+=($(dirname "${DIR_CLANG_FORMAT_8}"))
164 FILES_TO_CHECK_CPP=()
165 FILES_TO_CHECK_CPP_BY_CLANG_FORMAT_8=()
166 for f in ${FILES_TO_CHECK[@]}; do
167 # Manually ignore style checking
168 if [[ ${f} == +(*/NeuralNetworks.h|*/NeuralNetworksExtensions.h) ]]; then
172 # File extension to check
173 if [[ ${f} == +(*.h|*.hpp|*.cpp|*.cc|*.c|*.cl) ]]; then
175 # Check clang-format-8 target files first
176 # TODO Remove this after migration to clang-format-8
178 for USE_CLANG_FORMAT_8 in ${DIRECTORIES_USE_CLANG_FORMAT_8[@]}; do
179 if [[ $f = $USE_CLANG_FORMAT_8* ]]; then
180 FILES_TO_CHECK_CPP_BY_CLANG_FORMAT_8+=("$f")
186 if [[ $FOUND_CLANG_8 -ne 1 ]]; then
187 FILES_TO_CHECK_CPP+=("${f}")
192 # Skip by '.FORMATDENY' file
193 for s in ${DIRECTORIES_NOT_TO_BE_TESTED[@]}; do
194 FILES_TO_CHECK_CPP=(${FILES_TO_CHECK_CPP[*]/$s*/})
195 FILES_TO_CHECK_CPP_BY_CLANG_FORMAT_8=(${FILES_TO_CHECK_CPP_BY_CLANG_FORMAT_8[*]/$s*/})
198 if [[ ${#FILES_TO_CHECK_CPP} -ne 0 ]]; then
199 ${CLANG_FORMAT} -i ${FILES_TO_CHECK_CPP[@]}
201 if [[ ${EXIT_CODE} -ne 0 ]]; then
202 INVALID_EXIT=${EXIT_CODE}
206 # Check by clang-format-8
207 # TODO Remove this after migration to clang-format-8
208 if [[ ${#FILES_TO_CHECK_CPP_BY_CLANG_FORMAT_8} -ne 0 ]]; then
209 ${CLANG_FORMAT_8} -i ${FILES_TO_CHECK_CPP_BY_CLANG_FORMAT_8[@]}
211 if [[ ${EXIT_CODE} -ne 0 ]]; then
212 INVALID_EXIT=${EXIT_CODE}
217 function check_python_files() {
218 if [[ ${__Check_PYTHON} -eq 0 ]]; then
219 echo "[SKIPPED] Python check is skipped"
223 if ! command_exists yapf; then
224 echo "[ERROR] yapf is unavailable"
225 echo " Please install yapf."
230 FILES_TO_CHECK_PYTHON=()
231 for f in ${FILES_TO_CHECK[@]}; do
232 # File extension to check
233 if [[ ${f} == *.py ]]; then
234 FILES_TO_CHECK_PYTHON+=("${f}")
236 # Exceptional case: one-cmds don't have '.py' extension
237 if [[ ${f} == compiler/one-cmds/* ]]; then
238 # Ignore non-python source (cmake, etc)
239 # Ignore shell script: one-prepare-venv
240 if [[ ${f} != compiler/one-cmds/*.* ]] && [[ ${f} != compiler/one-cmds/one-prepare-venv ]]; then
241 FILES_TO_CHECK_PYTHON+=("${f}")
245 for s in ${DIRECTORIES_NOT_TO_BE_TESTED[@]}; do
247 FILES_TO_CHECK_PYTHON=(${FILES_TO_CHECK_PYTHON[*]/$skip*/})
250 if [[ ${#FILES_TO_CHECK_PYTHON} -ne 0 ]]; then
251 yapf -i ${FILES_TO_CHECK_PYTHON[@]}
253 if [[ ${EXIT_CODE} -ne 0 ]]; then
254 INVALID_EXIT=${EXIT_CODE}
259 pushd ${NNAS_PROJECT_PATH}
261 if [[ -n "$(git diff)" ]] && { [[ "${CHECK_DIFF_ONLY}" != "1" ]] || [[ "${CHECK_STAGED_ONLY}" != "1" ]]; }; then
262 echo "[WARNING] Commit all the changes before running format check"
263 echo " ${PATCH_FILE} file will contain unstaged files"
266 __Check_CPP=${CHECK_CPP:-"1"}
267 __Check_PYTHON=${CHECK_PYTHON:-"1"}
269 FILES_TO_CHECK=$(git ls-files -c --exclude-standard ${DIRECTORIES_TO_BE_TESTED[@]})
270 if [[ "${CHECK_DIFF_ONLY}" = "1" ]]; then
271 MASTER_EXIST=$(git rev-parse --verify master)
272 CURRENT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2-)
273 DIFF_COMMITS=`git log --graph --oneline master..HEAD | wc -l`
274 if [[ -z "${MASTER_EXIST}" ]]; then
275 echo "Cannot found local master branch"
276 elif [[ "${CURRENT_BRANCH}" = "master" ]]; then
277 echo "Current branch is master"
279 if [[ "${CHECK_STAGED_ONLY}" = "1" ]]; then
280 FILES_TO_CHECK=$(git diff --staged --name-only --diff-filter=d)
282 FILES_TO_CHECK=$(git diff --name-only --diff-filter=d HEAD~${DIFF_COMMITS})
287 for DIR_NOT_TO_BE_TESTED in $(git ls-files -co --exclude-standard '*/.FORMATDENY'); do
288 DIRECTORIES_NOT_TO_BE_TESTED+=($(dirname "${DIR_NOT_TO_BE_TESTED}"))
291 exclude_symbolic_links
297 if [[ "${CHECK_DIFF_ONLY}" = "1" ]] && [[ "${CHECK_STAGED_ONLY}" = "1" ]]; then
298 if [[ ! -z "${FILES_TO_CHECK}" ]]; then
299 DIFF=$(git diff ${FILES_TO_CHECK} | tee ${PATCH_FILE})
302 DIFF=$(git diff | tee ${PATCH_FILE})
307 if [[ -z "${CRCHECK}" ]] && [[ ! -n "${DIFF}" ]] && [[ ${INVALID_EXIT} -eq 0 ]]; then
308 echo "[PASSED] Format checker succeed."
312 # Something went wrong
314 if [[ ! -z "${CRCHECK}" ]]; then
315 echo "[FAILED] Please use LF for newline for following files."
319 if [[ -s ${PATCH_FILE} ]]; then
320 echo "[FAILED] Format checker failed and update code to follow convention."
321 echo " You can find changes in ${PATCH_FILE}"
324 if [[ ${INVALID_EXIT} -ne 0 ]]; then
325 echo "[[FAILED] Invalid format checker exit."