Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ A pre-commit hook that automatically formats and lints your C/C++ code using `cl
- [Quick Start](#quick-start)
- [Custom Configuration Files](#custom-configuration-files)
- [Custom Clang Tool Version](#custom-clang-tool-version)
- [Clang Tool Wheel CLI](#clang-tool-wheel-cli)
- [Output](#output)
- [clang-format Output](#clang-format-output)
- [clang-tidy Output](#clang-tidy-output)
Expand Down Expand Up @@ -72,6 +73,35 @@ repos:
args: [--checks=.clang-tidy, --version=21] # Specifies version
```

### Clang Tool Wheel CLI

This package also provides a CLI tool `clang-tools-wheel` to install specific versions of clang-format and clang-tidy wheels directly.

It can automatically resolve and install compatible versions even if no explicit version number is provided.

```bash
# Install the package
pip install cpp-linter-hooks

# Install specific version of clang-format
clang-tools-wheel --tool clang-format --version 21
clang-format installed at: /home/sxp/.local/bin/clang-format

# Check clang-format version
/home/sxp/.local/bin/clang-format --version
clang-format version 21.1.2

# Install specific version of clang-tidy
clang-tools-wheel --tool clang-tidy --version 21
clang-tidy installed at: /home/sxp/.local/bin/clang-tidy

# Check clang-tidy version
/home/sxp/.local/bin/clang-tidy --version
LLVM (http://llvm.org/):
LLVM version 21.1.1
Optimized build.
```

## Output

### clang-format Output
Expand Down
19 changes: 19 additions & 0 deletions cpp_linter_hooks/util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sys
import shutil
import subprocess
from argparse import ArgumentParser
from pathlib import Path
import logging
from typing import Optional, List
Expand Down Expand Up @@ -86,3 +87,21 @@ def _resolve_install(tool: str, version: Optional[str]) -> Optional[Path]:
)

return _install_tool(tool, user_version)


def main():
parser = ArgumentParser("Install specified clang tool wheel")
parser.add_argument("--tool", default="clang-format")
parser.add_argument("--version", default=None)
args = parser.parse_args()
path = _resolve_install(args.tool, args.version)
if path:
print(f"{args.tool} installed at: {path}")
return 0
else:
print(f"Failed to install {args.tool} version {args.version}")
return 1


if __name__ == "__main__":
raise SystemExit(main())
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies = [
dynamic = ["version"]

[project.scripts]
clang-tools-wheel = "cpp_linter_hooks.util:main"
clang-format-hook = "cpp_linter_hooks.clang_format:main"
clang-tidy-hook = "cpp_linter_hooks.clang_tidy:main"

Expand Down Expand Up @@ -79,3 +80,8 @@ exclude_also = [
"__name__",
"FileNotFoundError"
]

[tool.pytest.ini_options]
markers = [
"benchmark: mark test as a benchmark test"
]
40 changes: 40 additions & 0 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import subprocess
import sys

from cpp_linter_hooks import util
from cpp_linter_hooks.util import (
get_version_from_dependency,
_resolve_version,
Expand Down Expand Up @@ -278,3 +279,42 @@ def test_resolve_install_with_none_default_version():

# Should fallback to hardcoded version when DEFAULT is None
mock_install.assert_called_once_with("clang-format", None)


@pytest.mark.benchmark
def test_main_success(monkeypatch):
# Patch _resolve_install to simulate success
monkeypatch.setattr(
"cpp_linter_hooks.util._resolve_install",
lambda tool, version: "/usr/bin/clang-format",
)
monkeypatch.setattr(
sys, "argv", ["util.py", "--tool", "clang-format", "--version", "15.0.7"]
)
exit_code = util.main()
assert exit_code == 0


@pytest.mark.benchmark
def test_main_failure(monkeypatch):
# Patch _resolve_install to simulate failure
monkeypatch.setattr(
"cpp_linter_hooks.util._resolve_install", lambda tool, version: None
)
monkeypatch.setattr(
sys, "argv", ["util.py", "--tool", "clang-format", "--version", "99.99.99"]
)
exit_code = util.main()
assert exit_code == 1


@pytest.mark.benchmark
def test_main_default_tool(monkeypatch):
# Patch _resolve_install to simulate success for default tool
monkeypatch.setattr(
"cpp_linter_hooks.util._resolve_install",
lambda tool, version: "/usr/bin/clang-format",
)
monkeypatch.setattr(sys, "argv", ["util.py"])
exit_code = util.main()
assert exit_code == 0
Loading