[lint] add cmakelint to lintrunner (#68191)
Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/68191
+ fix filename of exec_linter
Test Plan: Imported from OSS
Reviewed By: anjali411
Differential Revision: D32364022
Pulled By: suo
fbshipit-source-id: 740892d9580edc348c3e818664fd37f145669fda
diff --git a/.lintrunner.toml b/.lintrunner.toml
index f07be52..9311247 100644
--- a/.lintrunner.toml
+++ b/.lintrunner.toml
@@ -340,7 +340,7 @@
]
command = [
'python3',
- 'tools/linter/adapters/cppexec_linter.py',
+ 'tools/linter/adapters/exec_linter.py',
'--',
'@{{PATHSFILE}}',
]
@@ -388,3 +388,34 @@
'--',
'@{{PATHSFILE}}'
]
+
+[[linter]]
+code = 'CMAKE'
+include_patterns = [
+ "**/*.cmake",
+ "**/*.cmake.in",
+ "**/CMakeLists.txt",
+]
+exclude_patterns = [
+ 'cmake/Modules/**',
+ 'cmake/Modules_CUDA_fix/**',
+ 'cmake/Caffe2Config.cmake.in',
+ 'aten/src/ATen/ATenConfig.cmake.in',
+ 'cmake/Caffe2ConfigVersion.cmake.in',
+ 'cmake/TorchConfig.cmake.in',
+ 'cmake/TorchConfigVersion.cmake.in',
+ 'cmake/cmake_uninstall.cmake.i',
+]
+command = [
+ 'python3',
+ 'tools/linter/adapters/cmake_linter.py',
+ '--config=.cmakelintrc',
+ '--',
+ '@{{PATHSFILE}}',
+]
+init_command = [
+ 'python3',
+ 'tools/linter/adapters/pip_init.py',
+ '--dry-run={{DRYRUN}}',
+ 'cmakelint==1.4.1',
+]
diff --git a/tools/linter/adapters/cmake_linter.py b/tools/linter/adapters/cmake_linter.py
new file mode 100644
index 0000000..0847f56
--- /dev/null
+++ b/tools/linter/adapters/cmake_linter.py
@@ -0,0 +1,139 @@
+import argparse
+import concurrent.futures
+import json
+import logging
+import os
+import re
+import subprocess
+import time
+from enum import Enum
+from typing import List, NamedTuple, Optional, Pattern
+
+
+LINTER_CODE = "CMAKE"
+
+
+class LintSeverity(str, Enum):
+ ERROR = "error"
+ WARNING = "warning"
+ ADVICE = "advice"
+ DISABLED = "disabled"
+
+
+class LintMessage(NamedTuple):
+ path: Optional[str]
+ line: Optional[int]
+ char: Optional[int]
+ code: str
+ severity: LintSeverity
+ name: str
+ original: Optional[str]
+ replacement: Optional[str]
+ description: Optional[str]
+
+
+# CMakeLists.txt:901: Lines should be <= 80 characters long [linelength]
+RESULTS_RE: Pattern[str] = re.compile(
+ r"""(?mx)
+ ^
+ (?P<file>.*?):
+ (?P<line>\d+):
+ \s(?P<message>.*)
+ \s(?P<code>\[.*\])
+ $
+ """
+)
+
+
+def run_command(
+ args: List[str],
+) -> "subprocess.CompletedProcess[bytes]":
+ logging.debug("$ %s", " ".join(args))
+ start_time = time.monotonic()
+ try:
+ return subprocess.run(
+ args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ finally:
+ end_time = time.monotonic()
+ logging.debug("took %dms", (end_time - start_time) * 1000)
+
+
+def check_file(
+ filename: str,
+ config: str,
+) -> List[LintMessage]:
+ try:
+ proc = run_command(
+ ["cmakelint", f"--config={config}", filename],
+ )
+ except OSError as err:
+ return [
+ LintMessage(
+ path=None,
+ line=None,
+ char=None,
+ code=LINTER_CODE,
+ severity=LintSeverity.ERROR,
+ name="command-failed",
+ original=None,
+ replacement=None,
+ description=(f"Failed due to {err.__class__.__name__}:\n{err}"),
+ )
+ ]
+ stdout = str(proc.stdout, "utf-8").strip()
+ return [
+ LintMessage(
+ path=match["file"],
+ name=match["code"],
+ description=match["message"],
+ line=int(match["line"]),
+ char=None,
+ code=LINTER_CODE,
+ severity=LintSeverity.ERROR,
+ original=None,
+ replacement=None,
+ )
+ for match in RESULTS_RE.finditer(stdout)
+ ]
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(
+ description="cmakelint runner",
+ fromfile_prefix_chars="@",
+ )
+ parser.add_argument(
+ "--config",
+ required=True,
+ help="location of cmakelint config",
+ )
+ parser.add_argument(
+ "filenames",
+ nargs="+",
+ help="paths to lint",
+ )
+
+ args = parser.parse_args()
+
+ with concurrent.futures.ThreadPoolExecutor(
+ max_workers=os.cpu_count(),
+ thread_name_prefix="Thread",
+ ) as executor:
+ futures = {
+ executor.submit(
+ check_file,
+ filename,
+ args.config,
+ ): filename
+ for filename in args.filenames
+ }
+ for future in concurrent.futures.as_completed(futures):
+ try:
+ for lint_message in future.result():
+ print(json.dumps(lint_message._asdict()), flush=True)
+ except Exception:
+ logging.critical('Failed at "%s".', futures[future])
+ raise