Skip to content

Commit

Permalink
ci: Clang tidy 2022 (#1208)
Browse files Browse the repository at this point in the history
This adds a clang-tidy static analysis job to our CI. On GitHub Actions, I tested this to run for about 3h, which is clearly too long. 

I leverage the mechanism that I originally intended for the GPU CI to run this job on CERN GitLab. Here it only takes about 35min.

This is behind additional permission checks, where we maintain an allow-list of people who the CI acts for. Otherwise this check should remain neutral, allowing merging without overriding.

The CMake configuration should allow us to relatively flexibly define which checks to include in the report, and if we want we can even fail the build for others. I would propose we go about like this: 

1. This PR enables a number of checks, not all of which we intend to completely fix. 
2. We see how this behaves in practice. This shouldn't block anything from merging for now, other than due to compilation failures (i.e. clang-tidy warnings are reported but ignored)
3. If this seems to make sense, I'll add a restricted set of checks that will fail the build, and add a PR to fix these checks.

Thoughts?
  • Loading branch information
paulgessinger authored May 10, 2022
1 parent bcd9e01 commit 8948fa3
Show file tree
Hide file tree
Showing 13 changed files with 471 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
---
Checks: '-*,readability-*,-readability-redundant-member-init,misc-*,-misc-unused-parameters,bugprone-*,performance-*,modernize-*,-modernize-use-auto,clang-analyzer-deadcode.*,clang-analyzer-*,-clang-analyzer-osx.*,-clang-analyzer-unix.*,cppcoreguidelines-*,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-constant-array'
Checks: '-*,readability-*,-readability-redundant-member-init,misc-*,-misc-unused-parameters,bugprone-*,performance-*,modernize-*,-modernize-use-auto,-modernize-use-trailing-return-type,clang-analyzer-deadcode.*,clang-analyzer-*,-clang-analyzer-osx.*,-clang-analyzer-unix.*,cppcoreguidelines-*,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-constant-array'
HeaderFilterRegex: '.*(?<!nlohmann\/json)\.(hpp|cpp|ipp)$'
AnalyzeTemporaryDtors: true
2 changes: 2 additions & 0 deletions .github/workflows/analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ jobs:
uses: codecov/codecov-action@v1
with:
file: ./build/coverage/cov.xml

build_performance:
runs-on: ubuntu-latest
container: ghcr.io/acts-project/ubuntu2004:v22
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Install dependencies
Expand Down
38 changes: 38 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
clang_tidy:
stage: build
image: ghcr.io/acts-project/ubuntu2004:v21
artifacts:
paths:
- src/clang-tidy/
when: always
expire_in: 2 weeks
script:
- git clone $CLONE_URL src
- cd src
- git checkout $HEAD_SHA
- >
apt-get update
&& apt-get install -y clang-10 clang-tidy-10 g++-8 libstdc++-8-dev
&& ln -s /usr/bin/clang++-10 /usr/bin/clang++
&& ln -s /usr/bin/clang-10 /usr/bin/clang
&& ln -s /usr/bin/clang-tidy-10 /usr/bin/clang-tidy
&& mkdir -p /opt/lib/gcc/x86_64-linux-gnu
&& ln -s /usr/lib/gcc/x86_64-linux-gnu/8/ /opt/lib/gcc/x86_64-linux-gnu/
&& clang++ --gcc-toolchain=/opt -v
- >
cmake -B build -S .
-GNinja
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_C_COMPILER=clang
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_CXX_FLAGS="-Werror --gcc-toolchain=/opt"
-DACTS_BUILD_EVERYTHING=on
-DACTS_RUN_CLANG_TIDY=on
- mkdir clang-tidy
- CI/clang_tidy/run_clang_tidy.sh build > clang-tidy/clang-tidy.log
- pip install -r CI/clang_tidy/requirements.txt
- CI/clang_tidy/parse_clang_tidy.py clang-tidy/clang-tidy.log clang-tidy/clang-tidy.json
- CI/clang_tidy/check_clang_tidy.py --report clang-tidy/clang-tidy.json --config CI/clang_tidy/limits.yml

build:
stage: build
image: ghcr.io/acts-project/ubuntu2004_exatrkx:v22
Expand Down Expand Up @@ -60,3 +97,4 @@ test:
- source build/python/setup.sh
- nvidia-smi
- python3 src/Examples/Scripts/Python/exatrkx.py

16 changes: 0 additions & 16 deletions .static_analysis_limits.yml

This file was deleted.

126 changes: 126 additions & 0 deletions CI/clang_tidy/check_clang_tidy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#!/usr/bin/env python3
import argparse
import sys
from pathlib import Path
import json
import itertools
import fnmatch
import re

import yaml
from rich.console import Console, Group
from rich.text import Text
from rich.panel import Panel
from rich.rule import Rule
from rich.emoji import Emoji
from rich.table import Table


from item import Item, ItemCollection


def main():
p = argparse.ArgumentParser()
p.add_argument("--report", type=Path, required=True)
p.add_argument("--config", type=Path, required=True)
p.add_argument("--strip-prefix-path", type=Path)

args = p.parse_args()

console = Console()

with args.config.open() as fh:
config = yaml.safe_load(fh)

data = []
with args.report.open() as fh:
data = ItemCollection(__root__=json.load(fh)).__root__
for item in data:
if args.strip_prefix_path and not item.path.is_absolute:
item.path = item.path.relative_to(args.strip_prefix_path)

counts = config["limits"].copy()

kf = lambda i: i.path
for file, items in itertools.groupby(sorted(data, key=kf), key=kf):

output = []
for item in items:
if item.code in config["ignore"]:
continue

emoji = Emoji(
{"warning": "yellow_circle", "error": "red_circle"}[item.severity]
)

style = "bold "
if item.severity == "warning":
style += "yellow"
elif item.severity == "error":
style += "red"

s = Text()
s.append(f"{emoji}")
s.append(f" {item.path}:{item.line}:{item.col}", style="bold")
s.append(f" {item.severity.upper()} ", style=style)
s.append("[")
s.append(item.code, style="bold")
s.append(f"]")

output.append(s)

def subpath(m):
return f"[bold]{m.group(1)}[/bold]:"

message = re.sub(r"([\w/.\-+]+:\d+:\d+):", subpath, item.message)
output.append(Panel(message))
output.append(Rule())

for pattern in counts.keys():

if not fnmatch.fnmatch(item.code, pattern):
continue
counts[pattern] += 1

output = output[:-1]
console.print(Panel(Group(*output), title=str(file)))

table = Table()
table.add_column("", width=2)
table.add_column("code / pattern")
table.add_column("count", justify="right")
table.add_column("limit", justify="right")
exit = 0
for pattern, count in counts.items():
limit = config["limits"][pattern]
emoji = Emoji("green_circle")
style = "green"
if count > limit:
exit = 1
emoji = Emoji("red_circle")
style = "red bold"
table.add_row(emoji, pattern, str(count), str(limit), style=style)

console.rule()
console.print(Panel.fit(table, title="Results"), justify="center")

if exit != 0:
console.print(
Panel(
Text(f"{Emoji('red_circle')} FAILURE", justify="center"),
style="red bold",
)
)
else:
console.print(
Panel(
Text(f"{Emoji('green_circle')} SUCCESS", justify="center"),
style="green bold",
)
)

sys.exit(exit)


if "__main__" == __name__:
main()
29 changes: 29 additions & 0 deletions CI/clang_tidy/item.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from dataclasses import dataclass
from pathlib import Path
from typing import List

import pydantic


class Item(pydantic.BaseModel):
path: Path
line: int
col: int
message: str
code: str
severity: str

def __hash__(self):
return hash((self.path, self.line, self.col, self.code))

def __eq__(self, other):
return (self.path, self.line, self.col, self.code) == (
other.path,
other.line,
other.col,
other.code,
)


class ItemCollection(pydantic.BaseModel):
__root__: List[Item]
16 changes: 16 additions & 0 deletions CI/clang_tidy/limits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
limits: {}
# "readability-inconsistent-declaration-parameter-name": 0
# "readability-named-parameter": 0
# "readability-container-size-empty": 0
# "modernize-use-using": 0
#"readability-braces-around-statements": 0
# "modernize-use-override": 0
# "modernize-use-equals-default" : 0
# "readability-implicit-bool-cast": 0
# "modernize-use-default-member-init": 0
# "performance-unnecessary-value-param": 0
# "modernize-use-equals-default": 0
# "modernize-use-nullptr": 0

ignore:
- cppcoreguidelines-pro-type-vararg
Loading

0 comments on commit 8948fa3

Please sign in to comment.