Skip to content
This repository has been archived by the owner on Dec 25, 2022. It is now read-only.

Commit

Permalink
Meta: Add minimalistic formatting/style checks
Browse files Browse the repository at this point in the history
This patch adds a modified copy of the Serenity repo style checking
scripts.
  • Loading branch information
faxe1008 committed Oct 16, 2022
1 parent addabea commit db3b753
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 0 deletions.
67 changes: 67 additions & 0 deletions Meta/check-newlines-at-eof.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env python3

import os
import re
import subprocess
import sys


RE_RELEVANT_FILE_EXTENSION = re.compile('\\.(cpp|h|gml|html|js|css|sh|py|json|txt)$')


def should_check_file(filename):
if not RE_RELEVANT_FILE_EXTENSION.search(filename):
return False
if filename.endswith('.txt'):
return 'CMake' in filename
return True


def find_files_here_or_argv():
if len(sys.argv) > 1:
raw_list = sys.argv[1:]
else:
process = subprocess.run(["git", "ls-files"], check=True, capture_output=True)
raw_list = process.stdout.decode().strip('\n').split('\n')

return filter(should_check_file, raw_list)


def run():
"""Check files checked in to git for trailing newlines at end of file."""
no_newline_at_eof_errors = []
blank_lines_at_eof_errors = []

did_fail = False
for filename in find_files_here_or_argv():
with open(filename, "r") as f:
f.seek(0, os.SEEK_END)

f.seek(f.tell() - 1, os.SEEK_SET)
if f.read(1) != '\n':
did_fail = True
no_newline_at_eof_errors.append(filename)
continue

while True:
f.seek(f.tell() - 2, os.SEEK_SET)
char = f.read(1)
if not char.isspace():
break
if char == '\n':
did_fail = True
blank_lines_at_eof_errors.append(filename)
break

if no_newline_at_eof_errors:
print("Files with no newline at the end:", " ".join(no_newline_at_eof_errors))
if blank_lines_at_eof_errors:
print("Files that have blank lines at the end:", " ".join(blank_lines_at_eof_errors))

if did_fail:
sys.exit(1)


if __name__ == '__main__':
os.chdir(os.path.dirname(__file__) + "/..")
run()
86 changes: 86 additions & 0 deletions Meta/check-style.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env python3

import os
import re
import subprocess
import sys

# Ensure copyright headers match this format and are followed by a blank line:
# /*
# * Copyright (c) YYYY(-YYYY), Whatever
# * ... more of these ...
# *
# * SPDX-License-Identifier: BSD-2-Clause
# */
GOOD_LICENSE_HEADER_PATTERN = re.compile(
'^/\\*\n' +
'( \\* Copyright \\(c\\) [0-9]{4}(-[0-9]{4})?, .*\n)+' +
' \\*\n' +
' \\* SPDX-License-Identifier: BSD-2-Clause\n' +
' \\*/\n' +
'\n')
LICENSE_HEADER_CHECK_EXCLUDES = {}

# We check that "#pragma once" is present
PRAGMA_ONCE_STRING = '#pragma once'
PRAGMA_ONCE_CHECK_EXCLUDES = {}

# We make sure that there's a blank line before and after pragma once
GOOD_PRAGMA_ONCE_PATTERN = re.compile('(^|\\S\n\n)#pragma once(\n\n\\S.|$)')


def should_check_file(filename):
if not filename.endswith('.cpp') and not filename.endswith('.h'):
return False
return True


def find_files_here_or_argv():
if len(sys.argv) > 1:
raw_list = sys.argv[1:]
else:
process = subprocess.run(["git", "ls-files"], check=True, capture_output=True)
raw_list = process.stdout.decode().strip('\n').split('\n')

return filter(should_check_file, raw_list)


def run():
errors_license = []
errors_pragma_once_bad = []
errors_pragma_once_missing = []

for filename in find_files_here_or_argv():
with open(filename, "r") as f:
file_content = f.read()
if not any(filename.startswith(forbidden_prefix) for forbidden_prefix in LICENSE_HEADER_CHECK_EXCLUDES):
if not GOOD_LICENSE_HEADER_PATTERN.search(file_content):
errors_license.append(filename)
if filename.endswith('.h'):
if any(filename.startswith(forbidden_prefix) for forbidden_prefix in PRAGMA_ONCE_CHECK_EXCLUDES):
# File was excluded
pass
elif GOOD_PRAGMA_ONCE_PATTERN.search(file_content):
# Excellent, the formatting is correct.
pass
elif PRAGMA_ONCE_STRING in file_content:
# Bad, the '#pragma once' is present but it's formatted wrong.
errors_pragma_once_bad.append(filename)
else:
# Bad, the '#pragma once' is missing completely.
errors_pragma_once_missing.append(filename)

if errors_license:
print("Files with bad licenses:", " ".join(errors_license))
if errors_pragma_once_missing:
print("Files without #pragma once:", " ".join(errors_pragma_once_missing))
if errors_pragma_once_bad:
print("Files with a bad #pragma once:", " ".join(errors_pragma_once_bad))

if errors_license or errors_pragma_once_missing or errors_pragma_once_bad:
sys.exit(1)


if __name__ == '__main__':
os.chdir(os.path.dirname(__file__) + "/..")
run()
36 changes: 36 additions & 0 deletions Meta/lint-ci.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash

set -e

script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
cd "${script_path}/.." || exit 1

RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color

FAILURES=0

set +e

for cmd in \
Meta/check-newlines-at-eof.py \
Meta/check-style.py; do
echo "Running ${cmd}"
if time "${cmd}" "$@"; then
echo -e "[${GREEN}OK${NC}]: ${cmd}"
else
echo -e "[${RED}FAIL${NC}]: ${cmd}"
((FAILURES+=1))
fi
done

echo "Running Meta/lint-clang-format.sh"
if time Meta/lint-clang-format.sh --overwrite-inplace "$@" && git diff --exit-code; then
echo -e "[${GREEN}OK${NC}]: Meta/lint-clang-format.sh"
else
echo -e "[${RED}FAIL${NC}]: Meta/lint-clang-format.sh"
((FAILURES+=1))
fi

exit "${FAILURES}"
54 changes: 54 additions & 0 deletions Meta/lint-clang-format.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash

set -e

script_path=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
cd "${script_path}/.." || exit 1

if [ "$#" -eq "1" ]; then
mapfile -t files < <(
git ls-files -- \
'*.cpp' \
'*.h'
)
else
files=()
for file in "${@:2}"; do
if [[ "${file}" == *".cpp" || "${file}" == *".h" ]]; then
files+=("${file}")
fi
done
fi

if (( ${#files[@]} )); then
CLANG_FORMAT=false
if command -v clang-format-14 >/dev/null 2>&1 ; then
CLANG_FORMAT=clang-format-14
elif command -v clang-format >/dev/null 2>&1 ; then
CLANG_FORMAT=clang-format
if ! "${CLANG_FORMAT}" --version | awk '{ if (substr($NF, 1, index($NF, ".") - 1) < 14) exit 1; }'; then
echo "You are using '$("${CLANG_FORMAT}" --version)', which appears to not be clang-format 14 or later."
echo "It is very likely that the resulting changes are not what you wanted."
fi
else
echo "clang-format-14 is not available, but C or C++ files need linting! Either skip this script, or install clang-format-14."
echo "(If you install a package 'clang-format', please make sure it's version 14 or later.)"
exit 1
fi

if [ "$#" -gt "0" ] && [ "--overwrite-inplace" = "$1" ] ; then
true # The only way to run this script.
else
# Note that this branch also covers --help, -h, -help, -?, etc.
echo "USAGE: $0 --overwrite-inplace"
echo "The argument is necessary to make you aware that this *will* overwrite your local files."
exit 1
fi

echo "Using ${CLANG_FORMAT}"

"${CLANG_FORMAT}" -style=file -i "${files[@]}"
echo "Maybe some files have changed. Sorry, but clang-format doesn't indicate what happened."
else
echo "No .cpp or .h files to check."
fi
61 changes: 61 additions & 0 deletions Meta/lint-commit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash

# the file containing the commit message is passed as the first argument
commit_file="$1"
commit_message=$(cat "$commit_file")

error() {
echo -e "\033[0;31m$1:\033[0m"
echo "$commit_message"
exit 1
}

# fail if the commit message contains windows style line breaks (carriage returns)
if grep -q -U $'\x0D' "$commit_file"; then
error "Commit message contains CRLF line breaks (only unix-style LF linebreaks are allowed)"
fi

line_number=0
while read -r line; do
# break on git cut line, used by git commit --verbose
if [[ "$line" == "# ------------------------ >8 ------------------------" ]]; then
break
fi

# ignore comment lines
[[ "$line" =~ ^#.* ]] && continue
# ignore overlong 'fixup!' commit descriptions
[[ "$line" =~ ^fixup!\ .* ]] && continue

((line_number += 1))
line_length=${#line}

if [[ $line_number -eq 2 ]] && [[ $line_length -ne 0 ]]; then
error "Empty line between commit title and body is missing"
fi

category_pattern='^(Revert "|\S+: )'
if [[ $line_number -eq 1 ]] && (echo "$line" | grep -E -v -q "$category_pattern"); then
error "Missing category in commit title (if this is a fix up of a previous commit, it should be squashed)"
fi

title_case_pattern="^\S.*?: [A-Z0-9]"
if [[ $line_number -eq 1 ]] && (echo "$line" | grep -E -v -q "$title_case_pattern"); then
error "First word of commit after the subsystem is not capitalized"
fi

if [[ $line_number -eq 1 ]] && [[ "$line" =~ \.$ ]]; then
error "Commit title ends in a period"
fi

url_pattern="([a-z]+:\/\/)?(([a-zA-Z0-9_]|-)+\.)+[a-z]{2,}(:\d+)?([a-zA-Z_0-9@:%\+.~\?&\/=]|-)+"
if [[ $line_length -gt 72 ]] && (echo "$line" | grep -E -v -q "$url_pattern"); then
error "Commit message lines are too long (maximum allowed is 72 characters)"
fi

if [[ "$line" == "Signed-off-by: "* ]]; then
error "Commit body contains a Signed-off-by tag"
fi

done <"$commit_file"
exit 0

0 comments on commit db3b753

Please sign in to comment.