clang-tidy: add a python script to tidy things up
mostly to reduce the size of the commit messages.
This script also serves as source of truth for the rules that are
being applied.
Bug: webrtc:424706384
Change-Id: I70fdef3c76f7c4a41dc8c60033b6185a5acfa3c1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/402941
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Jeremy Leconte <jleconte@google.com>
Commit-Queue: Philipp Hancke <phancke@meta.com>
Cr-Commit-Position: refs/heads/main@{#45300}
diff --git a/tools_webrtc/apply_clang_tidy.py b/tools_webrtc/apply_clang_tidy.py
new file mode 100755
index 0000000..5b715ad
--- /dev/null
+++ b/tools_webrtc/apply_clang_tidy.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env vpython3
+
+# Copyright (c) 2025 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+#
+# Run clang-tidy in the webrtc source directory.
+#
+# clang-tidy needs to be added to the .gclient file:
+# Example .gclient file:
+# solutions = [
+# {
+# "name": "src",
+# "url": "https://webrtc.googlesource.com/src.git",
+# "deps_file": "DEPS",
+# "managed": False,
+# "custom_deps": {},
+# "custom_vars" : {
+# "checkout_clangd": True,
+# "checkout_clang_tidy": True,
+# }
+# },
+# ]
+#
+# See also
+# https://chromium.googlesource.com/chromium/src/+/main/docs/clang_tidy.md
+
+import argparse
+import pathlib
+import subprocess
+
+_DEFAULT_WORKDIR = pathlib.Path("out/Default")
+
+# This is relative to src dir.
+_TIDY_BUILD = "tools/clang/scripts/build_clang_tools_extra.py"
+# These are relative to the work dir so the path needs to be constructed later.
+_LLVM = "tools/clang/third_party/llvm/"
+_TIDY_RUNNER = _LLVM + "clang-tools-extra/clang-tidy/tool/run-clang-tidy.py"
+_TIDY_BINARY = _LLVM + "build/bin/clang-tidy"
+_REPLACEMENTS_BINARY = _LLVM + "build/bin/clang-apply-replacements"
+
+# The list of checks that is getting applied.
+# TODO: bugs.webrtc.org/424706384 - move to .clang-tidy file.
+_CHECKS = [
+ "-*", # disable all checks by default.
+ "llvm-namespace-comment",
+ # conflicts with IWYU.
+ #"modernize-deprecated-headers",
+ "readability-static-definition-in-anonymous-namespace",
+ "readability-redundant-smartptr-get",
+]
+
+
+def _valid_dir(path: str) -> pathlib.Path:
+ """Checks if the given path is an existing dir
+ relative to the current working directory.
+
+ Args:
+ path: Relative dir path to the current working directory
+
+ Returns:
+ pathlib.Path object wrapping the dir path
+
+ Raises:
+ ValueError: If the dir doesn't exist
+ """
+ pathlib_handle = pathlib.Path(path)
+ if not pathlib_handle.is_dir():
+ raise ValueError(f"Dir path {pathlib_handle} does not exist!")
+ return pathlib_handle
+
+
+def _build_clang_tools(work_dir: pathlib.Path) -> None:
+ if pathlib.Path(work_dir, _TIDY_RUNNER).exists() and pathlib.Path(
+ work_dir, _TIDY_BINARY).exists() and pathlib.Path(
+ work_dir, _REPLACEMENTS_BINARY).exists():
+ return
+ print("Fetching and building clang-tidy")
+ build_clang_tools_cmd = (_TIDY_BUILD, "--fetch", work_dir, "clang-tidy",
+ "clang-apply-replacements")
+ subprocess.run(build_clang_tools_cmd,
+ capture_output=False,
+ text=True,
+ check=True)
+
+
+def _generate_compile_commands(work_dir: pathlib.Path) -> None:
+ """Automatically generates the compile_commands.json file to be used
+ by the include cleaner binary.
+
+ Args:
+ work_dir: gn out dir where the compile_commands json file exists
+ """
+ compile_commands_path = work_dir / "compile_commands.json"
+ print("Generating compile commands file...")
+ subprocess.run(
+ ["tools/clang/scripts/generate_compdb.py", "-p", work_dir],
+ stdout=compile_commands_path.open(mode="w+"),
+ check=True,
+ )
+
+
+def _run_clang_tidy(work_dir: pathlib.Path) -> None:
+ clang_tidy_cmd = (work_dir / _TIDY_RUNNER, "-p", work_dir,
+ "-clang-tidy-binary", work_dir / _TIDY_BINARY,
+ "-clang-apply-replacements-binary",
+ work_dir / _REPLACEMENTS_BINARY,
+ "-checks=" + ",".join(_CHECKS), "-fix")
+ subprocess.run(clang_tidy_cmd,
+ capture_output=False,
+ text=True,
+ check=False)
+
+
+def _parse_args() -> argparse.Namespace:
+ parser = argparse.ArgumentParser(
+ description="Runs clang-tidy with a set of rules",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+ )
+ parser.add_argument(
+ "-w",
+ "--work-dir",
+ type=_valid_dir,
+ default=str(_DEFAULT_WORKDIR),
+ help="Specify the gn workdir",
+ )
+
+ return parser.parse_args()
+
+
+def main() -> None:
+ args = _parse_args()
+ _build_clang_tools(args.work_dir)
+ _generate_compile_commands(args.work_dir)
+ _run_clang_tidy(args.work_dir)
+
+
+if __name__ == "__main__":
+ main()