From 55688240074a18a3769dcfeed9aca381583f57d2 Mon Sep 17 00:00:00 2001 From: Anshul Jain Date: Mon, 13 Jan 2020 16:34:52 -0800 Subject: [PATCH] Add tools to lint the code --- .clang-format | 3 + Documentation/developing_with_adk.md | 12 +++ Tools/download.sh | 89 ++++++++++++++++ Tools/linters/clint.sh | 145 +++++++++++++++++++++++++++ Tools/linters/shlint.sh | 49 +++++++++ 5 files changed, 298 insertions(+) create mode 100755 Tools/download.sh create mode 100755 Tools/linters/clint.sh create mode 100755 Tools/linters/shlint.sh diff --git a/.clang-format b/.clang-format index 2399c04..25f133d 100644 --- a/.clang-format +++ b/.clang-format @@ -7,6 +7,9 @@ IndentPPDirectives: None IndentWidth: 4 TabWidth: 4 UseTab: Never +TypenameMacros: + - HAP_ENUM_BEGIN + - HAP_ENUM_END AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveMacros: true diff --git a/Documentation/developing_with_adk.md b/Documentation/developing_with_adk.md index af086e3..f1b341d 100644 --- a/Documentation/developing_with_adk.md +++ b/Documentation/developing_with_adk.md @@ -1,4 +1,16 @@ ## Code Style +Please use the following tools to auto-format your code before submitting a Pull Request. + +## Linting source code +This project uses `clang-format` tool to lint and format the code. +``` +./Tools/linters/clint.sh -h +``` + +## Linting shell scripts +``` +./Tools/linters/shlint.sh +``` ## Guidelines #### Sample Application diff --git a/Tools/download.sh b/Tools/download.sh new file mode 100755 index 0000000..233032c --- /dev/null +++ b/Tools/download.sh @@ -0,0 +1,89 @@ +######################################################################################################################## +# This is a helper function to download a file from a given URL +# +# Globals: +# None +# Arguments: +# $1: file +# $2: url +# $3: SHA-256 hash (Optional) +# Returns: +# None +######################################################################################################################## +download() { + if [ "$#" -lt 2 ]; then + echo "Usage: $0 " + exit 1 + fi + + # Install uuidgen if not installed already + # uuidgen is pre-installed on OSX + if [[ "$(uname)" == "Linux" ]]; then + if ! uuidgen --version > /dev/null 2>&1; then + apt-get update + apt-get install uuid-runtime + fi + fi + + DIR=$(dirname "$1") + mkdir -p "${DIR}" + local FILE="$1" + ( + local lockFile="${FILE}.lock" + local uuid + uuid="$(uuidgen)" + trap '[ "$(cat "'"${lockFile}"'")" == "'"${uuid}"'" ] && rm -f "'"${lockFile}"'"' ERR EXIT + while ! ( set -C; echo "${uuid}" > "${lockFile}" ) 2>/dev/null; do echo -n "." && sleep 0.5; done + + if [[ -f "${FILE}" && -n $3 ]]; then + (echo "$3 *${FILE}" | shasum -a 256 -cw) || rm "${FILE}" + fi + if [ ! -f "${FILE}" ]; then + # URL encode: http://stackoverflow.com/a/7506695 + local url=$2 + + # Download & check hash. + # This is known to abort with `curl: (56) LibreSSL SSL_read: SSL_ERROR_SYSCALL, errno 54` + # when multiple downloads are started in quick succession. Therefore, retry loop. + local retryBackoff=0 + local i=0 + while :; do + if (( ++i > 10 )); then + echo "${1} download failed too often." + false + fi + if (( !retryBackoff )); then + retryBackoff=1 + else + echo "Retrying ${1} download in ${retryBackoff}s." + sleep "${retryBackoff}" + retryBackoff=$(( retryBackoff * 2 )) + maximumBackoff=1800 + if (( retryBackoff >= maximumBackoff )); then + retryBackoff=${maximumBackoff} + fi + fi + + if command -v curl > /dev/null; then + local command=( + "curl" "-L" "${url}" "-o" "${FILE}") + if command -v caffeinate >/dev/null; then + caffeinate -ims "${command[@]}" || continue + else + "${command[@]}" || continue + fi + elif command -v wget > /dev/null; then + wget -O "${FILE}" "${url}" || continue + else + echo -e "\x1B[31m/!\ No download tool installed. Please install curl or wget.\x1B[0m" + false + fi + break + done + + if [[ -n "$3" ]]; then + echo "$3 *${FILE}" | shasum -a 256 -cw + fi + fi + ) +} diff --git a/Tools/linters/clint.sh b/Tools/linters/clint.sh new file mode 100755 index 0000000..a275be7 --- /dev/null +++ b/Tools/linters/clint.sh @@ -0,0 +1,145 @@ +#!/bin/bash -e + +ADK_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../../" + +# shellcheck source=Tools/ +source "$ADK_ROOT/Tools/download.sh" + +usage() +{ + echo "A tool to check and fix code style" + echo "" + echo "Usage: $0 [options]" + echo "" + echo "OPTIONS:" + echo "-f - Lint the provided file. If no option is provided all files will be linted." + echo "-d - Lint the files in the specified directory." + echo "-c - Correct the style issues in the specified files or directories." + echo "" + echo "EXAMPLES: " + echo "$0 -f # Lint the specified file" + echo "$0 -f -c # Lint and fix the specified file" + echo "$0 -d # Lint files in the specified directory" + echo "$0 -d -c # Lint and fix all files in the specified directory" + echo "$0 # Lint all files in this repository" + echo "$0 -c # Fix all files in this repository" + exit 1 +} + +CLANG_FORMAT=clang-format +CORRECT=false +SEARCH_PATH=$ADK_ROOT + +while getopts "hcf:d:" opt; do + case ${opt} in + f ) FILE=$OPTARG + ;; + d ) SEARCH_PATH="$ADK_ROOT/$OPTARG" + ;; + c ) CORRECT=true + ;; + h ) usage + ;; + \? ) usage + ;; + esac +done + +# Install clang-format +CLANG_FORMAT_VERSION="9.0.0" +if ! "$CLANG_FORMAT" --version | grep -q $CLANG_FORMAT_VERSION; then + echo "clang-format not installed or the version is incorrect. Installing..." + + CLANG_BIN_FILE= + DEST= + case "$(uname)" in + "Darwin") + CLANG_BIN_FILE="clang+llvm-9.0.0-x86_64-darwin-apple" + DEST="/usr/local/bin" + ;; + "Linux") + CLANG_BIN_FILE="clang+llvm-9.0.0-x86_64-linux-gnu-ubuntu-18.04" + DEST="/usr/bin" + ;; + *) + echo "Unsupported system" + exit 1 + ;; + esac + + download "$CLANG_BIN_FILE.tar.xz" http://releases.llvm.org/9.0.0/"$CLANG_BIN_FILE.tar.xz" \ + && tar --xz -xvf "$CLANG_BIN_FILE.tar.xz" \ + && cp -f "$CLANG_BIN_FILE/bin/clang-format" "$DEST/clang-format" \ + && rm -rf "$CLANG_BIN_FILE" "$CLANG_BIN_FILE.tar.xz" +fi + +FILES=() +if [[ -n "$FILE" ]]; then + if [[ ! -f "$FILE" ]]; then + echo "Invalid file $FILE" + exit 1 + fi + FILES=("$FILE") +else + echo "Looking for files in $SEARCH_PATH directory..." + while IFS= read -r -d $'\0'; do + FILES+=("$REPLY") + done < <(find "$SEARCH_PATH" \ + \( \ + -name "*.h" \ + -o -name "*.c" \ + -o -name "*.cpp" \ + -o -name "*.hpp" \ + -o -name "*.cc" \ + -o -name "*.hh" \ + -o -name "*.cxx" \ + -o -name "*.m" \ + -o -name "*.mm" \ + \) \ + -not -path "*Output*" \ + ! -type l \ + -print0 + ) +fi + +if [[ ${#FILES[@]} -eq 0 ]]; then + echo "No files available to lint" + usage +fi + +# Create a random filename to store our generated patch +prefix="static-check-clang-format" +suffix="$(date +%s)" +PATCH="/tmp/$prefix-$suffix.patch" + +for file in "${FILES[@]}"; do + if $CORRECT; then + echo "Fixing file: $file" + "$CLANG_FORMAT" -style=file "$file" -i + else + echo "Checking file: $file" + "$CLANG_FORMAT" -style=file "$file" | \ + diff -u "$file" - | \ + sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$PATCH" + fi +done + +# If user asked to fix the style issues then we are done +if $CORRECT; then + exit 0 +fi + +# If no patch has been generated, clean up the file stub and exit +if [ ! -s "$PATCH" ] ; then + echo "No code style issues found" + rm -f "$PATCH" + exit 0 +fi + +# A patch has been created, notify the user and exit +echo "****************************************************************************" +echo "Code doesn't comply with style rules. Run the following command to fix them." +echo "****************************************************************************" +echo "$0 -c" +rm -f "$PATCH" +exit 1 diff --git a/Tools/linters/shlint.sh b/Tools/linters/shlint.sh new file mode 100755 index 0000000..1da56dd --- /dev/null +++ b/Tools/linters/shlint.sh @@ -0,0 +1,49 @@ +#!/bin/bash -e + +ADK_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../../" + +# shellcheck source=Tools/ +source "$ADK_ROOT/Tools/download.sh" + +# Install shellcheck +SHELLCHECK_VERSION="0.7.0" +if ! shellcheck --version | grep -q $SHELLCHECK_VERSION; then + echo "Shellcheck version incorrect. Installing..." + + SHELLCHECK_BIN_FILE= + DEST= + case "$(uname)" in + "Darwin") + SHELLCHECK_BIN_FILE="shellcheck-stable.darwin.x86_64" + DEST="/usr/local/bin" + ;; + "Linux") + SHELLCHECK_BIN_FILE="shellcheck-stable.linux.x86_64" + DEST="/usr/bin" + ;; + *) + echo "Unsupported system" + exit 1 + ;; + esac + + download shellcheck.tar.xz "https://shellcheck.storage.googleapis.com/$SHELLCHECK_BIN_FILE.tar.xz" \ + && tar -xvf "shellcheck.tar.xz" \ + && chmod 755 shellcheck-stable/shellcheck \ + && cp -f shellcheck-stable/shellcheck "$DEST/shellcheck" \ + && rm -rf shellcheck* +fi + +find "$ADK_ROOT" -type f \ + \( -perm -u=x -o -perm -g=x -o -perm -o=x \) \ + -not -path "*\.tmp*" \ + -exec bash -c 'grep -r "^#!.*\/bin\/bash" $1 1> /dev/null' _ {} \; \ + -print0 \ + -o -name "*.sh" \ + -not -path "*\.tmp*" \ + -print0 \ +| xargs -0 \ + shellcheck \ + --external-sources \ + --exclude=SC1091 \ + --shell=bash \