mirror of
https://github.com/X11Libre/xserver.git
synced 2026-10-21 01:13:38 +00:00
lint: Add .clang-tidy and analyzer scripts
Signed-off-by: John Studnicka <contact@zentec.dev>
This commit is contained in:
parent
1e0c1fa022
commit
ecede3875b
6 changed files with 509 additions and 42 deletions
18
.clang-tidy
Normal file
18
.clang-tidy
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Checks: >
|
||||
clang-analyzer-*,
|
||||
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
|
||||
bugprone-*,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-reserved-identifier,
|
||||
hicpp-no-assembler,
|
||||
misc-include-cleaner,
|
||||
performance-*,
|
||||
portability-*,
|
||||
readability-redundant-control-flow
|
||||
|
||||
HeaderFilterRegex: '.*'
|
||||
WarningsAsErrors: ''
|
||||
|
||||
CheckOptions:
|
||||
misc-include-cleaner.UnusedIncludes: 'true'
|
||||
misc-include-cleaner.MissingIncludes: 'false'
|
||||
321
.github/scripts/clang-tidy-analyze.sh
vendored
Executable file
321
.github/scripts/clang-tidy-analyze.sh
vendored
Executable file
|
|
@ -0,0 +1,321 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
default_build_dir="build"
|
||||
build_dir=""
|
||||
jobs=1
|
||||
show_total=0
|
||||
target_only=0
|
||||
total_only=0
|
||||
warnings_only=0
|
||||
errors_only=0
|
||||
target=""
|
||||
target_kind="all"
|
||||
target_file_abs=""
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [-p BUILD_DIR] [-j N] [--warnings-only|--errors-only] [-t|--total] [--target-only] [--total-only] [target]" >&2
|
||||
echo " target: optional .c/.h file path or directory path to check; omit for whole project." >&2
|
||||
echo " -p DIR: compile database directory. Defaults to ./compile_commands.json, then $default_build_dir/compile_commands.json." >&2
|
||||
echo " -j N: number of parallel clang-tidy jobs (positive integer, default: 1)." >&2
|
||||
echo " -t, --total: append TOTAL counts after per-file counts." >&2
|
||||
echo " --warnings-only: print warning counts only." >&2
|
||||
echo " --errors-only: print error counts only." >&2
|
||||
echo " --target-only: for file targets, count diagnostics only in that file (exclude headers)." >&2
|
||||
echo " --total-only: print only TOTAL counts (no per-file breakdown)." >&2
|
||||
echo "" >&2
|
||||
echo "Examples:" >&2
|
||||
echo " $0 -j 8 --total" >&2
|
||||
echo " $0 -j 8 hw/xfree86" >&2
|
||||
echo " $0 --target-only hw/xfree86/drivers/video/modesetting/drmmode_display.c" >&2
|
||||
echo "" >&2
|
||||
echo "Requires: clang-tidy, jq, and a Meson compile_commands.json database." >&2
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h | --help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
-t | --total)
|
||||
show_total=1
|
||||
shift
|
||||
;;
|
||||
--warnings-only)
|
||||
warnings_only=1
|
||||
shift
|
||||
;;
|
||||
--errors-only)
|
||||
errors_only=1
|
||||
shift
|
||||
;;
|
||||
-p)
|
||||
if [ "$#" -lt 2 ]; then
|
||||
echo "Missing value for -p." >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
build_dir="$2"
|
||||
shift 2
|
||||
;;
|
||||
--target-only)
|
||||
target_only=1
|
||||
shift
|
||||
;;
|
||||
--total-only)
|
||||
total_only=1
|
||||
show_total=1
|
||||
shift
|
||||
;;
|
||||
-j)
|
||||
if [ "$#" -lt 2 ]; then
|
||||
echo "Missing value for -j." >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
jobs="$2"
|
||||
case "$jobs" in
|
||||
'' | *[!0-9]* | 0)
|
||||
echo "Invalid -j value: $jobs (expected positive integer)." >&2
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option: $1" >&2
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$#" -gt 1 ]; then
|
||||
echo "Expected at most one target argument." >&2
|
||||
usage
|
||||
exit 2
|
||||
elif [ "$#" -eq 1 ]; then
|
||||
target="$1"
|
||||
fi
|
||||
|
||||
if [ "$warnings_only" -eq 1 ] && [ "$errors_only" -eq 1 ]; then
|
||||
echo "--warnings-only and --errors-only are incompatible." >&2
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if ! command -v clang-tidy >/dev/null 2>&1; then
|
||||
echo "clang-tidy not found in PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "jq not found in PATH." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$build_dir" ]; then
|
||||
compdb_dir="$build_dir"
|
||||
elif [ -f compile_commands.json ]; then
|
||||
compdb_dir="."
|
||||
else
|
||||
compdb_dir="$default_build_dir"
|
||||
fi
|
||||
|
||||
if [ ! -d "$compdb_dir" ]; then
|
||||
echo "Compile database directory '$compdb_dir' not found. Run meson setup first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
compdb="$compdb_dir/compile_commands.json"
|
||||
if [ ! -f "$compdb" ]; then
|
||||
echo "Compilation database '$compdb' not found. Run meson setup first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
compdb_dir_abs="$(cd "$compdb_dir" && pwd -P)"
|
||||
tmp_outdir="$(mktemp -d)"
|
||||
tmp_files="$tmp_outdir/files.txt"
|
||||
tmp_files_nul="$tmp_outdir/files.nul"
|
||||
tmp_parsed="$tmp_outdir/parsed.txt"
|
||||
trap 'rm -rf "$tmp_outdir"' EXIT INT TERM HUP
|
||||
|
||||
first_dir="$(jq -r '.[0].directory // empty' "$compdb")"
|
||||
case "$first_dir" in
|
||||
/*) diag_base="$(cd "$first_dir" && pwd -P)" ;;
|
||||
"") diag_base="$compdb_dir_abs" ;;
|
||||
*) diag_base="$(cd "$compdb_dir/$first_dir" && pwd -P)" ;;
|
||||
esac
|
||||
|
||||
canonical_path() {
|
||||
if command -v realpath >/dev/null 2>&1; then
|
||||
realpath "$1"
|
||||
else
|
||||
dir=$(dirname "$1")
|
||||
base=$(basename "$1")
|
||||
printf '%s/%s\n' "$(cd "$dir" && pwd -P)" "$base"
|
||||
fi
|
||||
}
|
||||
|
||||
emit_compdb_files() {
|
||||
jq -r '.[] | [.directory, .file] | @tsv' "$compdb" |
|
||||
while IFS="$(printf '\t')" read -r dir file; do
|
||||
case "$file" in
|
||||
/*) canonical_path "$file" ;;
|
||||
*) canonical_path "$dir/$file" ;;
|
||||
esac
|
||||
done |
|
||||
awk '!seen[$0]++'
|
||||
}
|
||||
|
||||
if [ -n "$target" ] && [ -f "$target" ]; then
|
||||
target_kind="file"
|
||||
target_file_abs="$(canonical_path "$target")"
|
||||
case "$target" in
|
||||
*.c | *.h) printf '%s\n' "$target_file_abs" >"$tmp_files" ;;
|
||||
*)
|
||||
echo "Not a .c or .h file: $target" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
elif [ -n "$target" ] && [ -d "$target" ]; then
|
||||
target_kind="dir"
|
||||
target_dir_abs="$(canonical_path "$target")"
|
||||
emit_compdb_files |
|
||||
while IFS= read -r file; do
|
||||
case "$file" in
|
||||
"$target_dir_abs" | "$target_dir_abs"/*) printf '%s\n' "$file" ;;
|
||||
esac
|
||||
done >"$tmp_files"
|
||||
elif [ -n "$target" ]; then
|
||||
echo "Path not found: $target" >&2
|
||||
exit 2
|
||||
else
|
||||
emit_compdb_files >"$tmp_files"
|
||||
fi
|
||||
|
||||
if [ "$target_only" -eq 1 ] && [ "$target_kind" != "file" ]; then
|
||||
echo "--target-only requires a file target." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ ! -s "$tmp_files" ]; then
|
||||
if [ "$show_total" -eq 1 ]; then
|
||||
if [ "$warnings_only" -eq 1 ] || [ "$errors_only" -eq 1 ]; then
|
||||
printf "0\tTOTAL\n"
|
||||
else
|
||||
printf "0\t0\tTOTAL\n"
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
while IFS= read -r file; do
|
||||
printf '%s\0' "$file"
|
||||
done <"$tmp_files" >"$tmp_files_nul"
|
||||
|
||||
emit_clang_output() {
|
||||
if command -v xargs >/dev/null 2>&1; then
|
||||
xargs -0 -n 1 -P "$jobs" clang-tidy -p "$compdb_dir_abs" <"$tmp_files_nul" 2>&1 || true
|
||||
else
|
||||
while IFS= read -r file; do
|
||||
clang-tidy -p "$compdb_dir_abs" "$file" 2>&1 || true
|
||||
done <"$tmp_files"
|
||||
fi
|
||||
}
|
||||
|
||||
emit_clang_output |
|
||||
awk -F: \
|
||||
-v base="$diag_base" \
|
||||
-v target_only="$target_only" \
|
||||
-v target_file="$target_file_abs" '
|
||||
function canon(p, abs, n, i, part, top, stack, out) {
|
||||
if (p ~ /^\//) {
|
||||
abs = p
|
||||
} else {
|
||||
abs = base "/" p
|
||||
}
|
||||
n = split(abs, stack, "/")
|
||||
top = 0
|
||||
for (i = 1; i <= n; i++) {
|
||||
part = stack[i]
|
||||
if (part == "" || part == ".") {
|
||||
continue
|
||||
}
|
||||
if (part == "..") {
|
||||
if (top > 0) {
|
||||
top--
|
||||
}
|
||||
continue
|
||||
}
|
||||
top++
|
||||
stack[top] = part
|
||||
}
|
||||
out = ""
|
||||
for (i = 1; i <= top; i++) {
|
||||
out = out "/" stack[i]
|
||||
}
|
||||
return (out == "" ? "/" : out)
|
||||
}
|
||||
{
|
||||
if ($0 !~ /^([^:[:space:]]+):([1-9][0-9]*):([1-9][0-9]*): (warning|error): .+ \[[[:alnum:].,-]+\]$/) {
|
||||
next
|
||||
}
|
||||
|
||||
file = canon($1)
|
||||
if (target_only == 1 && file != target_file) {
|
||||
next
|
||||
}
|
||||
|
||||
if ($0 ~ /: warning: /) {
|
||||
warnings[file]++
|
||||
total_warnings++
|
||||
} else {
|
||||
errors[file]++
|
||||
total_errors++
|
||||
}
|
||||
seen[file] = 1
|
||||
}
|
||||
END {
|
||||
for (file in seen) {
|
||||
printf "%d\t%d\t%s\n", warnings[file] + 0, errors[file] + 0, file
|
||||
}
|
||||
printf "__TOTAL__\t%d\t%d\n", total_warnings + 0, total_errors + 0
|
||||
}
|
||||
' >"$tmp_parsed"
|
||||
|
||||
print_counts() {
|
||||
while IFS="$(printf '\t')" read -r warnings errors file; do
|
||||
if [ "$warnings_only" -eq 1 ]; then
|
||||
printf '%s\t%s\n' "$warnings" "$file"
|
||||
elif [ "$errors_only" -eq 1 ]; then
|
||||
printf '%s\t%s\n' "$errors" "$file"
|
||||
else
|
||||
printf '%s\t%s\t%s\n' "$warnings" "$errors" "$file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
if [ "$total_only" -eq 0 ]; then
|
||||
grep -v '^__TOTAL__' "$tmp_parsed" | sort -nr | print_counts
|
||||
fi
|
||||
|
||||
if [ "$show_total" -eq 1 ]; then
|
||||
total_line="$(grep '^__TOTAL__' "$tmp_parsed")"
|
||||
total_warnings="$(printf '%s\n' "$total_line" | cut -f2)"
|
||||
total_errors="$(printf '%s\n' "$total_line" | cut -f3)"
|
||||
if [ "$warnings_only" -eq 1 ]; then
|
||||
printf '%s\tTOTAL\n' "$total_warnings"
|
||||
elif [ "$errors_only" -eq 1 ]; then
|
||||
printf '%s\tTOTAL\n' "$total_errors"
|
||||
else
|
||||
printf '%s\t%s\tTOTAL\n' "$total_warnings" "$total_errors"
|
||||
fi
|
||||
fi
|
||||
27
.github/scripts/link-compile-commands.sh
vendored
Executable file
27
.github/scripts/link-compile-commands.sh
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
build_dir="${1:-build}"
|
||||
root_link="compile_commands.json"
|
||||
|
||||
case "$(uname -s 2>/dev/null || echo unknown)" in
|
||||
MINGW* | MSYS* | CYGWIN*)
|
||||
echo "Skipping compile_commands.json symlink on non-Unix platform." >&2
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ ! -f "$build_dir/compile_commands.json" ]; then
|
||||
echo "Compilation database '$build_dir/compile_commands.json' not found. Run meson setup first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -e "$root_link" ] && [ ! -L "$root_link" ]; then
|
||||
echo "Refusing to overwrite non-symlink '$root_link'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tmp_link="$root_link.tmp.$$"
|
||||
rm -f "$tmp_link"
|
||||
ln -s "$build_dir/compile_commands.json" "$tmp_link"
|
||||
mv -f "$tmp_link" "$root_link"
|
||||
87
.github/scripts/meson-build.sh
vendored
87
.github/scripts/meson-build.sh
vendored
|
|
@ -5,6 +5,8 @@
|
|||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
# shellcheck disable=SC1091,SC2086
|
||||
#
|
||||
# Usage:
|
||||
# meson-build.sh
|
||||
# [-C directory] ... change to directory before doing anything
|
||||
|
|
@ -39,47 +41,47 @@ MESON_RUN_DIST="$MESON_DIST_ARGS"
|
|||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-C)
|
||||
directory=$2
|
||||
shift 2
|
||||
pushd "$directory" || exit 1
|
||||
;;
|
||||
--skip-setup)
|
||||
shift
|
||||
MESON_SKIP_SETUP="1"
|
||||
;;
|
||||
--skip-build)
|
||||
shift
|
||||
MESON_SKIP_BUILD="1"
|
||||
;;
|
||||
--skip-test)
|
||||
shift
|
||||
MESON_RUN_TEST=""
|
||||
;;
|
||||
--run-test)
|
||||
shift
|
||||
MESON_RUN_TEST="1"
|
||||
;;
|
||||
--skip-dist)
|
||||
shift
|
||||
MESON_RUN_DIST=""
|
||||
;;
|
||||
--run-dist)
|
||||
shift
|
||||
MESON_RUN_DIST="1"
|
||||
;;
|
||||
--skip-install)
|
||||
shift
|
||||
MESON_RUN_INSTALL=""
|
||||
;;
|
||||
--run-install)
|
||||
shift
|
||||
MESON_RUN_INSTALL="1"
|
||||
;;
|
||||
*)
|
||||
echo "Unknow commandline argument $1"
|
||||
exit 1
|
||||
;;
|
||||
-C)
|
||||
directory=$2
|
||||
shift 2
|
||||
pushd "$directory" || exit 1
|
||||
;;
|
||||
--skip-setup)
|
||||
shift
|
||||
MESON_SKIP_SETUP="1"
|
||||
;;
|
||||
--skip-build)
|
||||
shift
|
||||
MESON_SKIP_BUILD="1"
|
||||
;;
|
||||
--skip-test)
|
||||
shift
|
||||
MESON_RUN_TEST=""
|
||||
;;
|
||||
--run-test)
|
||||
shift
|
||||
MESON_RUN_TEST="1"
|
||||
;;
|
||||
--skip-dist)
|
||||
shift
|
||||
MESON_RUN_DIST=""
|
||||
;;
|
||||
--run-dist)
|
||||
shift
|
||||
MESON_RUN_DIST="1"
|
||||
;;
|
||||
--skip-install)
|
||||
shift
|
||||
MESON_RUN_INSTALL=""
|
||||
;;
|
||||
--run-install)
|
||||
shift
|
||||
MESON_RUN_INSTALL="1"
|
||||
;;
|
||||
*)
|
||||
echo "Unknow commandline argument $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
|
@ -124,6 +126,7 @@ if [[ -z "$MESON_SKIP_SETUP" ]]; then
|
|||
meson setup "$MESON_BUILDDIR" $MESON_ARGS
|
||||
fi
|
||||
meson configure "$MESON_BUILDDIR"
|
||||
.github/scripts/link-compile-commands.sh "$MESON_BUILDDIR"
|
||||
|
||||
if [[ -z "$MESON_SKIP_BUILD" ]]; then
|
||||
if [[ -n "$NINJA_ARGS" ]]; then
|
||||
|
|
@ -137,7 +140,7 @@ if [[ -n "$MESON_RUN_TEST" ]]; then
|
|||
fi
|
||||
|
||||
if [[ -n "$MESON_RUN_INSTALL" ]]; then
|
||||
meson install --no-rebuild -C "$MESON_BUILDDIR" $MESON_INSTALL_ARGS
|
||||
meson install --no-rebuild -C "$MESON_BUILDDIR" $MESON_INSTALL_ARGS
|
||||
fi
|
||||
|
||||
if [[ -n "$MESON_RUN_DIST" ]]; then
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -2,3 +2,6 @@
|
|||
*.swp
|
||||
*.patch
|
||||
tags
|
||||
compile_commands.json
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
|
|
|
|||
95
.vscode/tasks.json
vendored
Normal file
95
.vscode/tasks.json
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "clang-tidy (current file)",
|
||||
"type": "shell",
|
||||
"command": "clang-tidy",
|
||||
"args": [
|
||||
"${file}"
|
||||
],
|
||||
"problemMatcher": "$gcc",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "meson setup (clang)",
|
||||
"type": "shell",
|
||||
"command": "sh",
|
||||
"args": [
|
||||
"-c",
|
||||
"if [ -d build ]; then meson setup build --reconfigure; else meson setup build; fi && .github/scripts/link-compile-commands.sh build"
|
||||
],
|
||||
"options": {
|
||||
"env": {
|
||||
"CC": "clang",
|
||||
"CXX": "clang++"
|
||||
}
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "ninja (build, clang)",
|
||||
"type": "shell",
|
||||
"command": "ninja",
|
||||
"args": [
|
||||
"-C",
|
||||
"build"
|
||||
],
|
||||
"dependsOn": [
|
||||
"meson setup (clang)"
|
||||
],
|
||||
"problemMatcher": "$gcc",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "meson setup (gcc)",
|
||||
"type": "shell",
|
||||
"command": "sh",
|
||||
"args": [
|
||||
"-c",
|
||||
"if [ -d build ]; then meson setup build --reconfigure; else meson setup build; fi && .github/scripts/link-compile-commands.sh build"
|
||||
],
|
||||
"options": {
|
||||
"env": {
|
||||
"CC": "gcc",
|
||||
"CXX": "g++"
|
||||
}
|
||||
},
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "ninja (build, gcc)",
|
||||
"type": "shell",
|
||||
"command": "ninja",
|
||||
"args": [
|
||||
"-C",
|
||||
"build"
|
||||
],
|
||||
"dependsOn": [
|
||||
"meson setup (gcc)"
|
||||
],
|
||||
"problemMatcher": "$gcc",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "clang-tidy (all files)",
|
||||
"type": "shell",
|
||||
"command": ".github/scripts/clang-tidy-analyze.sh",
|
||||
"args": [
|
||||
"-j",
|
||||
"4",
|
||||
"--total"
|
||||
],
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in a new issue