1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#!/usr/bin/env python3
"""
Runs run-clang-tidy. A bit meta. Maybe it will replace run-clang-tidy one day
because the run-clang-tidy UX is so questionable.
"""

# I hereby dedicate this script to fuck you meson.
# I cannot simply write my code to invoke a subprocess in a meson file because
# Meson corrupts backslashes in command line args to subprocesses.
# This is allegedly for "Windows support", but last time I checked Windows
# neither needs nor wants you to corrupt its command lines.
# https://github.com/mesonbuild/meson/issues/1564

import multiprocessing
import os
import sys
from pathlib import Path
import argparse


def default_concurrency() -> int:
    return min(multiprocessing.cpu_count(), int(os.environ.get("NIX_BUILD_CORES", "16")))


def go(
    exe: str,
    plugin_path: Path,
    compile_commands_json_dir: Path,
    jobs: int,
    paths: list[Path],
    werror: bool,
    fix: bool,
):
    args = [
        # XXX(Jade): This explicitly invokes it with python because of a nixpkgs bug
        # where clang-unwrapped does not patch interpreters in run-clang-tidy.
        # However, making clang-unwrapped depend on python is also silly, so idk.
        sys.executable,
        exe,
        "-quiet",
        "-load",
        plugin_path,
        "-p",
        compile_commands_json_dir,
        "-j",
        str(jobs),
        "-header-filter",
        r"lix/[^/]+/.*\.hh",
    ]
    if werror:
        args += ["-warnings-as-errors", "*"]
    if fix:
        args += ["-fix"]
    args += ["--"]
    args += paths
    os.execvp(sys.executable, args)


def main():
    ap = argparse.ArgumentParser(description="Runs run-clang-tidy for you")
    ap.add_argument(
        "--jobs", "-j", type=int, default=default_concurrency(), help="Parallel linting jobs to run"
    )
    ap.add_argument("--plugin-path", type=Path, help="Path to the Lix clang-tidy plugin")
    # FIXME(Jade): maybe we should integrate this so it just fixes the compdb for you and throws it in a tempdir?
    ap.add_argument(
        "--compdb-path",
        type=Path,
        help="Path to the directory containing the fixed-up compilation database from clean_compdb",
    )
    ap.add_argument("--werror", action="store_true", help="Warnings get turned into errors")
    ap.add_argument("--fix", action="store_true", help="Apply fixes for warnings")
    ap.add_argument(
        "--run-clang-tidy-path", default="run-clang-tidy", help="Path to run-clang-tidy"
    )
    ap.add_argument("paths", nargs="*", help="Source paths to check")
    args = ap.parse_args()

    go(
        args.run_clang_tidy_path,
        args.plugin_path,
        args.compdb_path,
        args.jobs,
        args.paths,
        args.werror,
        args.fix,
    )


if __name__ == "__main__":
    main()