mongo/buildscripts/setup_clang_tidy.py
Daniel Moody 0216fb1e68 SERVER-122044 fix clang-tidy ide setup and coverity run (#49986)
GitOrigin-RevId: 89fe63d799c851bde830c5b9d35bc34a81f25cde
2026-03-19 20:45:09 +00:00

138 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Materialize clang-tidy IDE integration files from Bazel outputs."""
import argparse
import os
import pathlib
import platform
import sys
PLUGIN_CANDIDATES = [
"libmongo_tidy_checks.so",
"libmongo_tidy_checks.dylib",
"mongo_tidy_checks.dll",
"libmongo_tidy_checks.dll",
]
def mongo_tidy_checks_supported_platform() -> bool:
if platform.system() != "Linux":
return False
def clang_tidy_setup_recovery_message() -> str:
if mongo_tidy_checks_supported_platform():
return "Run `bazel run //:setup_clang_tidy` to materialize the clang-tidy config and mongo_tidy_checks plugin."
return (
"clang-tidy setup via Bazel is not supported on this platform. "
"The mongo_tidy_checks plugin is only supported on Linux excluding Ubuntu 18.04."
)
def _copy_if_changed(src: pathlib.Path, dst: pathlib.Path) -> bool:
src_bytes = src.read_bytes()
if dst.exists() and dst.read_bytes() == src_bytes:
return False
dst.write_bytes(src_bytes)
return True
def _write_if_changed(path: pathlib.Path, contents: str) -> bool:
if path.exists() and path.read_text(encoding="utf-8") == contents:
return False
path.write_text(contents, encoding="utf-8")
return True
def materialize_clang_tidy_ide_files(
repo_root: pathlib.Path,
config_src: pathlib.Path,
plugin_src: pathlib.Path,
) -> tuple[bool, bool]:
config_changed = _copy_if_changed(config_src, repo_root / ".clang-tidy")
marker_changed = _write_if_changed(
repo_root / ".mongo_checks_module_path", str(plugin_src.resolve())
)
return config_changed, marker_changed
def _resolve_runfile(r, rlocation_path: str) -> pathlib.Path:
resolved = r.Rlocation(rlocation_path)
if not resolved:
raise FileNotFoundError(f"Failed to resolve Bazel runfile: {rlocation_path}")
path = pathlib.Path(resolved)
if not path.is_file():
raise FileNotFoundError(f"Resolved runfile is not a file: {path}")
return path.resolve()
def _resolve_plugin(
r, workspace_prefix: str, package_path: str = "src/mongo/tools/mongo_tidy_checks"
) -> pathlib.Path:
for candidate in PLUGIN_CANDIDATES:
resolved = r.Rlocation(f"{workspace_prefix}/{package_path}/{candidate}")
if resolved and pathlib.Path(resolved).is_file():
return pathlib.Path(resolved).resolve()
candidate_list = ", ".join(PLUGIN_CANDIDATES)
raise FileNotFoundError(
"Failed to resolve mongo_tidy_checks plugin from Bazel runfiles. "
f"Tried: {candidate_list}"
)
def main() -> int:
try:
import runfiles
except ModuleNotFoundError:
print(
"The `bazel-runfiles` dependency is required to run `bazel run //:setup_clang_tidy`.",
file=sys.stderr,
)
return 1
parser = argparse.ArgumentParser()
parser.add_argument("--config-rlocation", required=True)
args = parser.parse_args()
workspace_dir = os.environ.get("BUILD_WORKSPACE_DIRECTORY")
if not workspace_dir:
print("This tool must be run with `bazel run //:setup_clang_tidy`.", file=sys.stderr)
return 1
repo_root = pathlib.Path(workspace_dir).resolve()
config_rlocation = args.config_rlocation
if "/" not in config_rlocation:
print(
f"Unexpected config runfile path: {config_rlocation}",
file=sys.stderr,
)
return 1
workspace_prefix = config_rlocation.split("/", 1)[0]
r = runfiles.Create()
if r is None:
print("Failed to initialize Bazel runfiles support.", file=sys.stderr)
return 1
config_src = _resolve_runfile(r, config_rlocation)
plugin_src = _resolve_plugin(r, workspace_prefix)
config_changed, marker_changed = materialize_clang_tidy_ide_files(
repo_root,
config_src,
plugin_src,
)
print(f"Configured clang-tidy for IDE use at {repo_root / '.clang-tidy'}")
print(f"Configured mongo tidy checks plugin at {plugin_src}")
if not config_changed and not marker_changed:
print("clang-tidy IDE files were already up to date.")
return 0
if __name__ == "__main__":
sys.exit(main())