2
import concurrent.futures
10
from typing import List, NamedTuple, Optional, Pattern
16
class LintSeverity(str, Enum):
23
class LintMessage(NamedTuple):
28
severity: LintSeverity
30
original: Optional[str]
31
replacement: Optional[str]
32
description: Optional[str]
36
RESULTS_RE: Pattern[str] = re.compile(
50
) -> "subprocess.CompletedProcess[bytes]":
51
logging.debug("$ %s", " ".join(args))
52
start_time = time.monotonic()
54
return subprocess.run(
59
end_time = time.monotonic()
60
logging.debug("took %dms", (end_time - start_time) * 1000)
66
) -> List[LintMessage]:
69
["cmakelint", f"--config={config}", filename],
71
except OSError as err:
78
severity=LintSeverity.ERROR,
79
name="command-failed",
82
description=(f"Failed due to {err.__class__.__name__}:\n{err}"),
85
stdout = str(proc.stdout, "utf-8").strip()
90
description=match["message"],
91
line=int(match["line"]),
94
severity=LintSeverity.ERROR,
98
for match in RESULTS_RE.finditer(stdout)
102
if __name__ == "__main__":
103
parser = argparse.ArgumentParser(
104
description="cmakelint runner",
105
fromfile_prefix_chars="@",
110
help="location of cmakelint config",
115
help="paths to lint",
118
args = parser.parse_args()
120
with concurrent.futures.ThreadPoolExecutor(
121
max_workers=os.cpu_count(),
122
thread_name_prefix="Thread",
130
for filename in args.filenames
132
for future in concurrent.futures.as_completed(futures):
134
for lint_message in future.result():
135
print(json.dumps(lint_message._asdict()), flush=True)
137
logging.critical('Failed at "%s".', futures[future])