Verified Commit 5ee545dc authored by Daniel Vrátil's avatar Daniel Vrátil 🤖
Browse files

Only run clang-tidy on changed files

parent 748ab672
# Ignore generated files that we can't do anything about (no major issues there, though)
resourcebasesettings.cpp
designerplugin.cpp
# Ignore generated Q_LOGGING_CATEGORY files, the checks take forever for some reason
# and are generated anyway.
[a-z_]+_debug.cpp
mocs_[a-z_]+.cpp
# Ignore autotests and tests, it halves the run time
autotests/
tests/
......@@ -9,7 +9,7 @@ build_clazy_clang_tidy:
only:
- merge_requests
before_script:
- zypper install -y clazy jq
- zypper install -y clazy gnu_parallel
- git clone --depth=1 https://invent.kde.org/sysadmin/ci-tooling.git $CI_TOOLING
- git clone --depth=1 https://invent.kde.org/sysadmin/repo-metadata.git $CI_TOOLING/repo-metadata
- git clone --depth=1 https://invent.kde.org/sysadmin/kde-build-metadata.git $CI_TOOLING/kde-build-metadata
......@@ -24,7 +24,12 @@ build_clazy_clang_tidy:
- python3 -u $CI_TOOLING/helpers/prepare-dependencies.py --product $PRODUCT --project $PROJECT --branchGroup $BRANCH_GROUP --environment production --platform $PLATFORM --installTo $INSTALL_PREFIX
- python3 -u $CI_TOOLING/helpers/configure-build.py --product $PRODUCT --project $PROJECT --branchGroup $BRANCH_GROUP --platform $PLATFORM --installTo $INSTALL_PREFIX
- python3 -u $CI_TOOLING/helpers/compile-build.py --product $PRODUCT --project $PROJECT --branchGroup $BRANCH_GROUP --platform $PLATFORM --usingInstall $INSTALL_PREFIX
- time ./tools/run-clang-tidy.sh $(pwd)/build
- time ./tools/run-clang-tidy.sh $(pwd) $(pwd)/build $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
variables:
PLATFORM: SUSEQt5.14
BRANCH_GROUP: kf5-qt5
artifacts:
paths:
- build/clang-tidy-report.xml
reports:
junit: build/clang-tidy-report.xml
#!/usr/bin/env python3
#
# MIT License
#
# Copyright (c) 2018 PSPDFKit
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Originally taken from https://github.com/PSPDFKit-labs/clang-tidy-to-junit
import sys
import collections
import re
import logging
import itertools
from xml.sax.saxutils import escape
# Create a `ErrorDescription` tuple with all the information we want to keep.
ErrorDescription = collections.namedtuple(
'ErrorDescription', 'file line column error error_identifier description')
class ClangTidyConverter:
# All the errors encountered.
errors = []
# Parses the error.
# Group 1: file path
# Group 2: line
# Group 3: column
# Group 4: error message
# Group 5: error identifier
error_regex = re.compile(
r"^([\w\/\.\-\ ]+):(\d+):(\d+): (.+) (\[[\w\-,\.]+\])$")
# This identifies the main error line (it has a [the-warning-type] at the end)
# We only create a new error when we encounter one of those.
main_error_identifier = re.compile(r'\[[\w\-,\.]+\]$')
def __init__(self, basename):
self.basename = basename
def print_junit_file(self, output_file):
# Write the header.
output_file.write("""<?xml version="1.0" encoding="UTF-8" ?>
<testsuites id="1" name="Clang-Tidy" tests="{error_count}" errors="{error_count}" failures="0" time="0">""".format(error_count=len(self.errors)))
sorted_errors = sorted(self.errors, key=lambda x: x.file)
# Iterate through the errors, grouped by file.
for file, errorIterator in itertools.groupby(sorted_errors, key=lambda x: x.file):
errors = list(errorIterator)
error_count = len(errors)
# Each file gets a test-suite
output_file.write("""\n <testsuite errors="{error_count}" name="{file}" tests="{error_count}" failures="0" time="0">\n"""
.format(error_count=error_count, file=file))
for error in errors:
# Write each error as a test case.
output_file.write("""
<testcase id="{id}" name="{id}" time="0">
<failure message="{message}">
{htmldata}
</failure>
</testcase>""".format(id="[{}/{}] {}".format(error.line, error.column, error.error_identifier),
message=escape(error.error, entities={"\"": "&quot;"}),
htmldata=escape(error.description)))
output_file.write("\n </testsuite>\n")
output_file.write("</testsuites>\n")
def process_error(self, error_array):
if len(error_array) == 0:
return
result = self.error_regex.match(error_array[0])
if result is None:
logging.warning(
'Could not match error_array to regex: %s', error_array)
return
# We remove the `basename` from the `file_path` to make prettier filenames in the JUnit file.
file_path = result.group(1).replace(self.basename, "")
error = ErrorDescription(file_path, int(result.group(2)), int(
result.group(3)), result.group(4), result.group(5), "\n".join(error_array[1:]))
self.errors.append(error)
def convert(self, input_file, output_file):
# Collect all lines related to one error.
current_error = []
for line in input_file:
# If the line starts with a `/`, it is a line about a file.
if line[0] == '/':
# Look if it is the start of a error
if self.main_error_identifier.search(line, re.M):
# If so, process any `current_error` we might have
self.process_error(current_error)
# Initialize `current_error` with the first line of the error.
current_error = [line]
else:
# Otherwise, append the line to the error.
current_error.append(line)
elif len(current_error) > 0:
# If the line didn't start with a `/` and we have a `current_error`, we simply append
# the line as additional information.
current_error.append(line)
else:
pass
# If we still have any current_error after we read all the lines,
# process it.
if len(current_error) > 0:
self.process_error(current_error)
# Print the junit file.
self.print_junit_file(output_file)
if __name__ == "__main__":
if len(sys.argv) < 2:
logging.error("Usage: %s base-filename-path", sys.argv[0])
logging.error(
" base-filename-path: Removed from the filenames to make nicer paths.")
sys.exit(1)
converter = ClangTidyConverter(sys.argv[1])
converter.convert(sys.stdin, sys.stdout)
#!/bin/sh
#!/bin/bash
# Copyright (c) 2020 Daniel Vrátil <dvratil@kde.org>
#
# This library is free software; you can redistribute it and/or modify it
......@@ -16,42 +16,31 @@
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
if [ $# -lt 1 ]; then
>&2 echo "Usage: $0 BUILD_DIR"
if [ $# -lt 2 ]; then
>&2 echo "Usage: $0 SRC_DIR BUILD_DIR [TARGET_BRANCH]"
exit 1
fi
set -xe
BUILDDIR=$1; shift 1
function sanitize_compile_commands
{
local cc_file=${BUILDDIR}/compile_commands.json
local filter_file=".clang-tidy-ignore"
if [ ! -f "${cc_file}" ]; then
>&2 echo "Couldn't find compile_commands.json"
exit 1
fi
if [ ! -f "${filter_file}" ]; then
return 0
fi
filter_files=$(cat ${filter_file} | grep -vE "^#\.*|^$" | tr '\n' '|' | head -c -1)
local cc_bak_file=${cc_file}.bak
mv ${cc_file} ${cc_bak_file}
cat ${cc_bak_file} \
| jq -r "map(select(.file|test(\"${filter_files}\")|not))" \
> ${cc_file}
if [ ! -d .git ]; then
>&2 echo "run-clang-tidy.sh must be ran in a git clone of the Akonadi repository!"
exit 1
fi
task_count=$(cat ${cc_file} | jq "length")
}
dir=$(dirname $(readlink -f "$0"))
src_dir=$1; shift
build_dir=$1; shift
if [ $# -ge 1 ]; then
merge_branch=$1; shift
else
merge_branch="master"
fi
base_commit=$(git merge-base refs/remotes/origin/${merge_branch} HEAD)
changed_files=$(git diff-tree --name-only --diff-filter=d -r ${base_commit} HEAD \
| grep -E "\.cpp|\.h" \
| grep -Ev "^autotests/|^tests/")
sanitize_compile_commands
parallel run-clang-tidy -q -p ${build_dir} {} ::: ${changed_files} | tee "${build_dir}/clang-tidy.log"
cd ${BUILDDIR}
run-clang-tidy -j$(nproc) -q $@
cat "${build_dir}/clang-tidy.log" | ${dir}/clang-tidy-to-junit.py ${src_dir} > "${build_dir}/clang-tidy-report.xml"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment