-
Notifications
You must be signed in to change notification settings - Fork 92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adding clang-tidy as a linter tool #2269
base: main
Are you sure you want to change the base?
Changes from 10 commits
1d93369
f724d88
efeb4fa
31aead3
600c035
9935304
5e54b91
d64fdaa
498e2a3
84e8418
df5dc10
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/bin/bash | ||
set -ex | ||
|
||
# Clone the Ceph repository | ||
git clone https://github.com/ceph/ceph.git "$WORKSPACE/ceph" | ||
cd "$WORKSPACE/ceph" | ||
|
||
# Initialize and update submodules | ||
git submodule update --init --recursive --progress | ||
|
||
# Install dependencies | ||
sudo apt-get install -y curl | ||
./install-deps.sh | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we don't plan on building, why do we need "install-deps.sh" this also takes considerable amount of time |
||
sudo apt-get install -y python3-routes | ||
|
||
# Configure the build | ||
./do_cmake.sh -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON | ||
cd build | ||
|
||
# Replace the boost and include directories from the tar file | ||
tar -xzf "$WORKSPACE/ceph_build.tar.gz" -C "$WORKSPACE" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where is the tar file being generated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tar file is generated in the Jenkins job Yes we control the content of tar file, it is being controlled in the pull request: #2276 |
||
rm "$WORKSPACE/ceph_build.tar.gz" | ||
sudo rm -rf boost include | ||
sudo mv "$WORKSPACE/build/boost" ./boost | ||
sudo mv "$WORKSPACE/build/include" ./include | ||
|
||
# Prepare for Clang-Tidy | ||
cd "$WORKSPACE/ceph" | ||
sudo apt-get install -y clang-tidy parallel | ||
output_file="clang-tidy-result" | ||
file_list="files_to_check.txt" | ||
|
||
# Get list of modified files in the current commit | ||
git diff --name-only HEAD~1 HEAD > modified_files.txt | ||
|
||
# Filter for only C++ files in rgw and osd directories | ||
cat modified_files.txt | grep -E "^(src/rgw|src/osd).*\.(cpp|hpp|cc)$" > "$file_list" | ||
|
||
# Check if there are any files to analyze | ||
if [ ! -s "$file_list" ]; then | ||
echo "No C++ files were modified in rgw or osd directories in this commit." | ||
mkdir -p "$WORKSPACE/ceph/build" | ||
echo "No files to analyze" > "$WORKSPACE/ceph/build/$output_file" | ||
# Create empty report to avoid potential CI issues | ||
echo "<?xml version='1.0' encoding='UTF-8'?><testsuites></testsuites>" > "$WORKSPACE/report.xml" | ||
exit 0 | ||
fi | ||
|
||
# Run clang-tidy only on modified files and save output | ||
{ | ||
echo "Files being checked:" | ||
cat "$file_list" | ||
echo | ||
# Add full path to files | ||
sed -i "s|^|$WORKSPACE/ceph/|" "$file_list" | ||
parallel -m clang-tidy -checks="-*,bugprone-use-after-move" -p "$WORKSPACE/ceph/build" {} < "$file_list" | ||
} | tee "$WORKSPACE/ceph/build/$output_file" | ||
|
||
# Generate the Clang-Tidy report | ||
sudo chmod +x clang-tidy-to-junit.py | ||
./clang-tidy-to-junit.py "$WORKSPACE/ceph/src" < "$WORKSPACE/ceph/build/$output_file" > "$WORKSPACE/report.xml" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#!/usr/bin/env python3 | ||
|
||
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={"\"": """}), | ||
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) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
- job: | ||
name: ceph-pr-clang-tidy | ||
node: jammy && small | ||
project-type: freestyle | ||
defaults: global | ||
display-name: 'ceph: Clang-tidy checks' | ||
concurrent: true | ||
quiet-period: 5 | ||
block-downstream: false | ||
block-upstream: false | ||
retry-count: 3 | ||
properties: | ||
- build-discarder: | ||
days-to-keep: 15 | ||
artifact-days-to-keep: 15 | ||
|
||
triggers: | ||
- github-pull-request: | ||
allow-whitelist-orgs-as-admins: true | ||
org-list: | ||
- ceph | ||
only-trigger-phrase: false | ||
status-context: "Clang-tidy lint check" | ||
started-status: "checking if bugs exist" | ||
success-status: "no bugs found" | ||
failure-status: "bugs found" | ||
|
||
builders: | ||
- copyartifact: | ||
project: ceph-pull-requests | ||
|
||
- shell: | ||
!include-raw: | ||
- ../../../scripts/build_utils.sh | ||
- ../../build/build | ||
- ../../build/clang-tidy-to-junit.py | ||
|
||
publishers: | ||
- junit: | ||
results: report.xml | ||
allow-empty-results: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you do a shallow clone of only the relevant branch?
this would save considerable amount of time