Skip to content

Commit b758abc

Browse files
authored
Add cmake_format_linter adapter (#117)
1 parent d091d29 commit b758abc

File tree

4 files changed

+235
-0
lines changed

4 files changed

+235
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[[linter]]
2+
code = 'CMAKE-FORMAT'
3+
is_formatter = true
4+
include_patterns = [
5+
'**/CMakeLists.txt',
6+
'**/*.cmake',
7+
'**/*.cmake.in',
8+
]
9+
exclude_patterns = []
10+
command = [
11+
'python',
12+
'-m',
13+
'lintrunner_adapters',
14+
'run',
15+
'cmake_format_linter',
16+
'--',
17+
'@{{PATHSFILE}}',
18+
]
19+
init_command = [
20+
'python',
21+
'-m',
22+
'lintrunner_adapters',
23+
'run',
24+
'pip_init',
25+
'--dry-run={{DRYRUN}}',
26+
'cmakelang==0.6.13',
27+
]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# cmake-format linter
2+
3+
This example demonstrates how to use cmake-format with lintrunner-adapters.
4+
5+
## Usage
6+
7+
```bash
8+
lintrunner init && lintrunner
9+
```
10+
11+
## Configuration file
12+
13+
You can optionally provide a cmake-format configuration file using the `--config-file` argument:
14+
15+
```toml
16+
command = [
17+
'python',
18+
'-m',
19+
'lintrunner_adapters',
20+
'run',
21+
'cmake_format_linter',
22+
'--config-file=.cmake-format.yaml',
23+
'--',
24+
'@{{PATHSFILE}}',
25+
]
26+
```
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Sample CMake file for testing cmake-format
2+
cmake_minimum_required(VERSION 3.10)
3+
project(TestProject)
4+
5+
# Poorly formatted CMake that cmake-format should fix
6+
set(SOURCES
7+
main.cpp
8+
utils.cpp
9+
helper.cpp)
10+
11+
find_package(Threads REQUIRED)
12+
13+
add_executable(test_app ${SOURCES})
14+
15+
target_link_libraries(test_app
16+
PRIVATE
17+
Threads::Threads
18+
)
19+
20+
# Function with bad formatting
21+
function(my_function arg1 arg2)
22+
if(${arg1} STREQUAL "test")
23+
message(STATUS "Testing")
24+
endif()
25+
endfunction()
26+
27+
my_function("test" "value")
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
"""Adapter for https://github.com/cheshirekow/cmake_format."""
2+
3+
from __future__ import annotations
4+
5+
import argparse
6+
import concurrent.futures
7+
import logging
8+
import os
9+
import subprocess
10+
import sys
11+
12+
from lintrunner_adapters import (
13+
LintMessage,
14+
LintSeverity,
15+
add_default_options,
16+
as_posix,
17+
run_command,
18+
)
19+
20+
LINTER_CODE = "CMAKEFORMAT"
21+
22+
23+
def check_file(
24+
filename: str,
25+
retries: int,
26+
timeout: int,
27+
config_file: str | None = None,
28+
) -> list[LintMessage]:
29+
try:
30+
with open(filename, "rb") as f:
31+
original = f.read()
32+
with open(filename, "rb") as f:
33+
command = ["cmake-format", "-"]
34+
if config_file:
35+
command.extend(["-c", config_file])
36+
37+
proc = run_command(
38+
command,
39+
stdin=f,
40+
retries=retries,
41+
timeout=timeout,
42+
check=True,
43+
)
44+
except subprocess.TimeoutExpired:
45+
return [
46+
LintMessage(
47+
path=filename,
48+
line=None,
49+
char=None,
50+
code=LINTER_CODE,
51+
severity=LintSeverity.ERROR,
52+
name="timeout",
53+
original=None,
54+
replacement=None,
55+
description=f"cmake-format timed out while trying to process {filename}.",
56+
)
57+
]
58+
except (OSError, subprocess.CalledProcessError) as err:
59+
return [
60+
LintMessage(
61+
path=filename,
62+
line=None,
63+
char=None,
64+
code=LINTER_CODE,
65+
severity=LintSeverity.ADVICE,
66+
name="command-failed",
67+
original=None,
68+
replacement=None,
69+
description=(
70+
f"Failed due to {err.__class__.__name__}:\n{err}"
71+
if not isinstance(err, subprocess.CalledProcessError)
72+
else (
73+
f"COMMAND (exit code {err.returncode})\n"
74+
f"{' '.join(as_posix(x) for x in err.cmd)}\n\n"
75+
f"STDERR\n{err.stderr.decode('utf-8').strip() or '(empty)'}\n\n"
76+
f"STDOUT\n{err.stdout.decode('utf-8').strip() or '(empty)'}"
77+
)
78+
),
79+
)
80+
]
81+
82+
replacement = proc.stdout
83+
if original == replacement:
84+
return []
85+
86+
return [
87+
LintMessage(
88+
path=filename,
89+
line=None,
90+
char=None,
91+
code=LINTER_CODE,
92+
severity=LintSeverity.WARNING,
93+
name="format",
94+
original=original.decode("utf-8"),
95+
replacement=replacement.decode("utf-8"),
96+
description="Run `lintrunner -a` to apply this patch.",
97+
)
98+
]
99+
100+
101+
def main() -> None:
102+
parser = argparse.ArgumentParser(
103+
description=f"Format files with cmake-format. Linter code: {LINTER_CODE}",
104+
fromfile_prefix_chars="@",
105+
)
106+
parser.add_argument(
107+
"--config-file",
108+
help="Path to cmake-format configuration file",
109+
)
110+
parser.add_argument(
111+
"--timeout",
112+
default=90,
113+
type=int,
114+
help="seconds to wait for cmake-format",
115+
)
116+
add_default_options(parser)
117+
args = parser.parse_args()
118+
119+
logging.basicConfig(
120+
format="<%(threadName)s:%(levelname)s> %(message)s",
121+
level=(
122+
logging.NOTSET
123+
if args.verbose
124+
else logging.DEBUG
125+
if len(args.filenames) < 1000
126+
else logging.INFO
127+
),
128+
stream=sys.stderr,
129+
)
130+
131+
with concurrent.futures.ThreadPoolExecutor(
132+
max_workers=os.cpu_count(),
133+
thread_name_prefix="Thread",
134+
) as executor:
135+
futures = {
136+
executor.submit(
137+
check_file,
138+
x,
139+
args.retries,
140+
args.timeout,
141+
args.config_file,
142+
): x
143+
for x in args.filenames
144+
}
145+
for future in concurrent.futures.as_completed(futures):
146+
try:
147+
for lint_message in future.result():
148+
lint_message.display()
149+
except Exception:
150+
logging.critical('Failed at "%s".', futures[future])
151+
raise
152+
153+
154+
if __name__ == "__main__":
155+
main()

0 commit comments

Comments
 (0)