Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
4 changes: 2 additions & 2 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
description: Automatically install any specific version of clang-format and format C/C++ code
entry: clang-format-hook
language: python
files: \.(h\+\+|h|hh|hxx|hpp|c|cc|cpp|c\+\+|cxx)$
types_or: [c++, c]
require_serial: false

- id: clang-tidy
name: clang-tidy
description: Automatically install any specific version of clang-tidy and diagnose/fix typical programming errors
entry: clang-tidy-hook
language: python
files: \.(h\+\+|h|hh|hxx|hpp|c|cc|cpp|c\+\+|cxx)$
types_or: [c++, c]
require_serial: false
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ repos:
args: [--checks=.clang-tidy] # Loads checks from .clang-tidy file
```

> [!TIP]
> Install the latest version of `clang-format` and `clang-tidy` if not specified. You can specify the version using the `--version` argument in the `args` list as shown below.

### Custom Clang Tool Version

To use specific versions of clang-format and clang-tidy (using Python wheel packages):
Expand Down
7 changes: 4 additions & 3 deletions cpp_linter_hooks/clang_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from argparse import ArgumentParser
from typing import Tuple

from .util import ensure_installed, DEFAULT_CLANG_FORMAT_VERSION
from cpp_linter_hooks.util import _resolve_install, DEFAULT_CLANG_FORMAT_VERSION


parser = ArgumentParser()
Expand All @@ -15,8 +15,9 @@

def run_clang_format(args=None) -> Tuple[int, str]:
hook_args, other_args = parser.parse_known_args(args)
tool_name = ensure_installed("clang-format", hook_args.version)
command = [tool_name, "-i"]
if hook_args.version:
_resolve_install("clang-format", hook_args.version)
command = ["clang-format", "-i"]

# Add verbose flag if requested
if hook_args.verbose:
Expand Down
8 changes: 4 additions & 4 deletions cpp_linter_hooks/clang_tidy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from argparse import ArgumentParser
from typing import Tuple

from .util import ensure_installed, DEFAULT_CLANG_TIDY_VERSION
from cpp_linter_hooks.util import _resolve_install, DEFAULT_CLANG_TIDY_VERSION


parser = ArgumentParser()
Expand All @@ -11,9 +11,9 @@

def run_clang_tidy(args=None) -> Tuple[int, str]:
hook_args, other_args = parser.parse_known_args(args)
tool_name = ensure_installed("clang-tidy", hook_args.version)
command = [tool_name]
command.extend(other_args)
if hook_args.version:
_resolve_install("clang-tidy", hook_args.version)
command = ["clang-tidy"] + other_args

retval = 0
output = ""
Expand Down
68 changes: 4 additions & 64 deletions cpp_linter_hooks/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,10 @@ def get_version_from_dependency(tool: str) -> Optional[str]:
return None
with open(pyproject_path, "rb") as f:
data = tomllib.load(f)
# First try project.optional-dependencies.tools
optional_deps = data.get("project", {}).get("optional-dependencies", {})
tools_deps = optional_deps.get("tools", [])
for dep in tools_deps:
if dep.startswith(f"{tool}=="):
return dep.split("==")[1]

# Fallback to project.dependencies for backward compatibility
dependencies = data.get("project", {}).get("dependencies", [])
for dep in dependencies:
# Check build-system.requires
build_system = data.get("build-system", {})
requires = build_system.get("requires", [])
for dep in requires:
if dep.startswith(f"{tool}=="):
return dep.split("==")[1]
return None
Expand Down Expand Up @@ -148,20 +142,6 @@ def parse_version(v: str):
return None


def _get_runtime_version(tool: str) -> Optional[str]:
"""Get the runtime version of a tool."""
try:
output = subprocess.check_output([tool, "--version"], text=True)
if tool == "clang-tidy":
lines = output.strip().splitlines()
if len(lines) > 1:
return lines[1].split()[-1]
elif tool == "clang-format":
return output.strip().split()[-1]
except Exception:
return None


def _install_tool(tool: str, version: str) -> Optional[Path]:
"""Install a tool using pip."""
try:
Expand All @@ -187,44 +167,4 @@ def _resolve_install(tool: str, version: Optional[str]) -> Optional[Path]:
else DEFAULT_CLANG_TIDY_VERSION
)

# Additional safety check in case DEFAULT versions are None
if user_version is None:
user_version = (
DEFAULT_CLANG_FORMAT_VERSION
if tool == "clang-format"
else DEFAULT_CLANG_TIDY_VERSION
)

path = shutil.which(tool)
if path:
runtime_version = _get_runtime_version(tool)
if runtime_version and user_version not in runtime_version:
LOG.info(
"%s version mismatch (%s != %s), reinstalling...",
tool,
runtime_version,
user_version,
)
return _install_tool(tool, user_version)
return Path(path)

return _install_tool(tool, user_version)


def is_installed(tool: str) -> Optional[Path]:
"""Check if a tool is installed and return its path."""
path = shutil.which(tool)
if path:
return Path(path)
return None


def ensure_installed(tool: str, version: Optional[str] = None) -> str:
"""Ensure a tool is installed, resolving its version if necessary."""
LOG.info("Ensuring %s is installed", tool)
tool_path = _resolve_install(tool, version)
if tool_path:
LOG.info("%s available at %s", tool, tool_path)
return tool
LOG.warning("%s not found and could not be installed", tool)
return tool
8 changes: 1 addition & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools>=45", "setuptools-scm"]
requires = ["setuptools>=45", "setuptools-scm", "clang-format==21.1.0", "clang-tidy==21.1.0"]
build-backend = "setuptools.build_meta"

requires-python = ">=3.9"
Expand Down Expand Up @@ -46,12 +46,6 @@ source = "https://github.com/cpp-linter/cpp-linter-hooks"
tracker = "https://github.com/cpp-linter/cpp-linter-hooks/issues"

[project.optional-dependencies]
# only clang tools can added to this section to make hooks work
tools = [
"clang-format==21.1.0",
"clang-tidy==21.1.0",
]

dev = [
"coverage",
"pre-commit",
Expand Down
Loading