Skip to content

Instantly share code, notes, and snippets.

@paulgessinger
Created February 17, 2025 08:07
Show Gist options
  • Save paulgessinger/2cc73b32a5ae70728364b524fe987789 to your computer and use it in GitHub Desktop.
Save paulgessinger/2cc73b32a5ae70728364b524fe987789 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# /// script
# dependencies = [
# "rich",
# "typer",
# ]
# ///
from typing import Annotated
import rich
import typer
from rich.progress import Progress, SpinnerColumn, MofNCompleteColumn, Task
from rich.panel import Panel
from rich.console import Group, Console
from rich.rule import Rule
import sys
from rich.columns import Columns
from pathlib import Path
import subprocess
from concurrent.futures import ThreadPoolExecutor, as_completed
app = typer.Typer()
@app.command()
def run(
build_dir: Annotated[str | None, typer.Option("--build-dir", "-p")] = None,
jobs: Annotated[int, typer.Option("--jobs", "-j")] = 1,
fix: bool = False,
output: Annotated[Path | None, typer.Option("--output", "-o")] = None,
):
clang_tidy_exe = subprocess.run(
["which", "clang-tidy"], capture_output=True, text=True, check=True
).stdout.strip()
find_cmd = ["find", "Core", "Tests", "Plugins", "Examples"]
find_cmd += ["(", "-name", "*.cpp", "-or", "-name", "*.hpp", ")"]
for k in [
"Onnx",
"ExaTrkX",
"codegen",
"Legacy",
"AtlasStepper",
"Gbts",
"Cuda",
"Hashing",
"Examples/Scripts",
"Onnx",
"TrackFindingML",
"Framework/ML",
]:
find_cmd += ["-and", "-not", "-path", f"*{k}*"]
r"""
find Core
Tests
Plugins
Examples
\( -name "*.cpp" -or -name "*.hpp" \)
-and -not -path "*Onnx*"
-and -not -path "*ExaTrkX*"
-and -not -path "*codegen*"
-and -not -path "*Legacy*"
-and -not -path "*AtlasStepper*"
-and -not -path "*Gbts*"
-and -not -path "*Cuda*"
-and -not -path "*Hashing*"
-and -not -path "*Examples/Scripts*"
-and -not -path "*Onnx*"
-and -not -path "*TrackFindingML*"
-and -not -path "*Framework/ML*"
"""
files = (
subprocess.run(find_cmd, capture_output=True, text=True, check=True)
.stdout.strip()
.split("\n")
)
# return
# print(files)
class ProgressWithRule(Progress):
def __init__(self, *args, jobs: int, width: int, **kwargs) -> None:
self.active_files: list[str | None] = [None] * jobs
self.width = width
super().__init__(*args, **kwargs)
def get_renderables(self):
files = [f if f is not None else "" for f in self.active_files]
g = Group(
Columns(
[
Group(*files[: len(files) // 2]),
Group(*files[len(files) // 2 + 1 :]),
],
width=self.width // 2 - 1,
expand=True,
equal=True,
align="left",
),
)
yield Group(
Rule(title="Active files"),
g,
Rule(),
self.make_tasks_table(self.tasks),
)
def add_file(self, file: str):
for i, v in enumerate(self.active_files):
if v is None:
self.active_files[i] = file
break
def remove_file(self, file: str):
index = self.active_files.index(file)
self.active_files[index] = None
def clang_tidy(progress, file):
progress.add_file(file)
result = subprocess.run(cmd, capture_output=True, encoding="utf-8")
progress.remove_file(file)
progress.advance(task)
return file, result
console = Console()
if output is not None:
with output.open("w") as f:
pass
exit = 0
with ProgressWithRule(
SpinnerColumn(),
MofNCompleteColumn(),
*Progress.get_default_columns(),
jobs=jobs,
width=console.width,
console=console,
) as progress:
futures = []
with ThreadPoolExecutor(max_workers=jobs) as executor:
task = progress.add_task("Running...", total=len(files))
for file in files:
cmd = [clang_tidy_exe]
cmd += ["-p", build_dir]
if fix:
cmd += ["-fix"]
cmd += [file]
futures.append(executor.submit(clang_tidy, progress, file))
for future in as_completed(futures):
file, result = future.result()
if result.returncode != 0:
exit = 1
console.print(
result.stdout, end="", highlight=False, markup=False, emoji=False
)
if output is not None:
with output.open("a") as f:
f.write(f"--- {file}")
f.write(result.stdout)
f.write(result.stderr)
sys.exit(exit)
app()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment