Skip to content

Commit 19dd2c9

Browse files
committed
feat: add install-wheel.py and pre-commit config
1 parent 291c19d commit 19dd2c9

File tree

7 files changed

+315
-6
lines changed

7 files changed

+315
-6
lines changed

.pre-commit-config.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v6.0.0
4+
hooks:
5+
- id: trailing-whitespace
6+
- id: check-yaml
7+
- id: end-of-file-fixer
8+
- id: check-added-large-files
9+
- id: check-merge-conflict
10+
- repo: https://github.com/astral-sh/ruff-pre-commit
11+
rev: v0.13.3
12+
hooks:
13+
- id: ruff-check
14+
- id: ruff-format

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
# cpp-linter.github.io
1+
# cpp-linter.github.io

docs/blog/posts/myfirst.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,3 @@ authors:
1616
---
1717

1818
# cpp-linter-action supports xxx features
19-

docs/discussion.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Join our community discussions to ask questions, share ideas, and connect with o
99
Visit our GitHub Discussions to:
1010

1111
- **Ask Questions** - Get help from the community and maintainers
12-
- **Share Ideas** - Propose new features or improvements
12+
- **Share Ideas** - Propose new features or improvements
1313
- **Show and Tell** - Share your projects using cpp-linter tools
1414
- **General Discussion** - Chat about C/C++ linting and best practices
1515

docs/static/install-wheel.py

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Download clang-format or clang-tidy wheel for the current platform.
4+
5+
This script automatically detects your platform and downloads the appropriate
6+
wheel from the GitHub releases of cpp-linter/clang-tools-wheel.
7+
"""
8+
9+
import argparse
10+
import platform
11+
import subprocess
12+
import sys
13+
import urllib.request
14+
import json
15+
from pathlib import Path
16+
17+
18+
def get_platform_tag():
19+
"""Detect the current platform and return the appropriate wheel tag."""
20+
system = platform.system().lower()
21+
machine = platform.machine().lower()
22+
23+
# Normalize machine architecture names
24+
arch_map = {
25+
"x86_64": "x86_64",
26+
"amd64": "x86_64",
27+
"aarch64": "aarch64",
28+
"arm64": "arm64",
29+
"armv7l": "armv7l",
30+
"i386": "i686",
31+
"i686": "i686",
32+
"ppc64le": "ppc64le",
33+
"s390x": "s390x",
34+
}
35+
36+
arch = arch_map.get(machine, machine)
37+
38+
if system == "darwin": # macOS
39+
if arch in ["arm64", "aarch64"]:
40+
return ["macosx_11_0_arm64"]
41+
else:
42+
return ["macosx_10_9_x86_64"]
43+
44+
elif system == "linux":
45+
# Try to detect libc type
46+
try:
47+
# Check if we're on musl
48+
result = subprocess.run(
49+
["ldd", "--version"], capture_output=True, text=True, check=False
50+
)
51+
if "musl" in result.stderr.lower():
52+
libc = "musllinux"
53+
else:
54+
libc = "manylinux"
55+
except (FileNotFoundError, subprocess.SubprocessError):
56+
# Default to manylinux if ldd is not available
57+
libc = "manylinux"
58+
59+
if libc == "manylinux":
60+
if arch == "x86_64":
61+
# Try multiple manylinux versions in order of preference
62+
return [
63+
"manylinux_2_27_x86_64.manylinux_2_28_x86_64",
64+
"manylinux_2_17_x86_64.manylinux2014_x86_64",
65+
]
66+
elif arch == "aarch64":
67+
return [
68+
"manylinux_2_26_aarch64.manylinux_2_28_aarch64",
69+
"manylinux_2_17_aarch64.manylinux2014_aarch64",
70+
]
71+
elif arch == "i686":
72+
return [
73+
"manylinux_2_26_i686.manylinux_2_28_i686",
74+
"manylinux_2_17_i686.manylinux2014_i686",
75+
]
76+
elif arch == "ppc64le":
77+
return ["manylinux_2_26_ppc64le.manylinux_2_28_ppc64le"]
78+
elif arch == "s390x":
79+
return ["manylinux_2_26_s390x.manylinux_2_28_s390x"]
80+
elif arch == "armv7l":
81+
return ["manylinux_2_31_armv7l"]
82+
else: # musllinux
83+
return [f"musllinux_1_2_{arch}"]
84+
85+
elif system == "windows":
86+
if arch == "amd64" or arch == "x86_64":
87+
return ["win_amd64"]
88+
elif arch == "arm64":
89+
return ["win_arm64"]
90+
else:
91+
return ["win32"]
92+
93+
raise ValueError(f"Unsupported platform: {system} {machine}")
94+
95+
96+
def get_release_info(repo="cpp-linter/clang-tools-wheel", version=None):
97+
"""Get information about a specific release or latest release from GitHub API."""
98+
if version:
99+
# Remove 'v' prefix if present for the API call
100+
clean_version = version.lstrip("v")
101+
url = f"https://api.github.com/repos/{repo}/releases/tags/v{clean_version}"
102+
else:
103+
url = f"https://api.github.com/repos/{repo}/releases/latest"
104+
105+
try:
106+
with urllib.request.urlopen(url) as response:
107+
return json.loads(response.read().decode())
108+
except Exception as e:
109+
if version:
110+
raise RuntimeError(
111+
f"Failed to fetch release info for version {version}: {e}"
112+
)
113+
else:
114+
raise RuntimeError(f"Failed to fetch latest release info: {e}")
115+
116+
117+
def get_latest_release_info(repo="cpp-linter/clang-tools-wheel"):
118+
"""Get information about the latest release from GitHub API."""
119+
return get_release_info(repo)
120+
121+
122+
def find_wheel_asset(assets, tool, platform_tags, version=None):
123+
"""Find the appropriate wheel asset for the given tool and platform."""
124+
# Try both naming conventions: clang-format and clang_format
125+
tool_underscore = tool.replace("-", "_")
126+
127+
wheel_pattern_hyphen = f"{tool}-"
128+
wheel_pattern_underscore = f"{tool_underscore}-"
129+
130+
if version:
131+
wheel_pattern_hyphen += f"{version}-"
132+
wheel_pattern_underscore += f"{version}-"
133+
134+
wheel_pattern_hyphen += "py2.py3-none-"
135+
wheel_pattern_underscore += "py2.py3-none-"
136+
137+
# Try each platform tag
138+
for platform_tag in platform_tags:
139+
for asset in assets:
140+
name = asset["name"]
141+
if (
142+
(
143+
name.startswith(wheel_pattern_hyphen)
144+
or name.startswith(wheel_pattern_underscore)
145+
)
146+
and name.endswith(".whl")
147+
and platform_tag in name
148+
):
149+
return asset
150+
151+
return None
152+
153+
154+
def download_file(url, filename):
155+
"""Download a file from URL to the specified filename."""
156+
print(f"Downloading {filename}...")
157+
try:
158+
urllib.request.urlretrieve(url, filename)
159+
print(f"Successfully downloaded {filename}")
160+
return True
161+
except Exception as e:
162+
print(f"Failed to download {filename}: {e}")
163+
return False
164+
165+
166+
def main():
167+
parser = argparse.ArgumentParser(
168+
description="Download clang-format or clang-tidy wheel for current platform",
169+
formatter_class=argparse.RawDescriptionHelpFormatter,
170+
epilog="""
171+
Examples:
172+
%(prog)s clang-format # Download latest clang-format
173+
%(prog)s clang-tidy # Download latest clang-tidy
174+
%(prog)s clang-format --version 20.1.8 # Download specific version
175+
%(prog)s clang-format --output ./wheels # Download to specific directory
176+
""",
177+
)
178+
179+
parser.add_argument(
180+
"tool",
181+
choices=["clang-format", "clang-tidy"],
182+
help="Tool to download (clang-format or clang-tidy)",
183+
)
184+
parser.add_argument(
185+
"--version", "-v", help="Specific version to download (default: latest)"
186+
)
187+
parser.add_argument(
188+
"--output",
189+
"-o",
190+
default=".",
191+
help="Output directory (default: current directory)",
192+
)
193+
parser.add_argument(
194+
"--platform", help="Override platform detection (advanced usage)"
195+
)
196+
parser.add_argument(
197+
"--list-platforms",
198+
action="store_true",
199+
help="List all available platforms for the latest release",
200+
)
201+
202+
args = parser.parse_args()
203+
204+
try:
205+
# Get release information
206+
if args.version:
207+
print(f"Fetching release information for version {args.version}...")
208+
else:
209+
print("Fetching latest release information...")
210+
211+
release_info = get_release_info(version=args.version)
212+
213+
if args.list_platforms:
214+
print(
215+
f"Available platforms for {args.tool} in release {release_info['tag_name']}:"
216+
)
217+
# Try both naming conventions: clang-format and clang_format
218+
tool_underscore = args.tool.replace("-", "_")
219+
tool_assets = [
220+
a
221+
for a in release_info["assets"]
222+
if (
223+
(
224+
a["name"].startswith(f"{args.tool}-")
225+
or a["name"].startswith(f"{tool_underscore}-")
226+
)
227+
and a["name"].endswith(".whl")
228+
)
229+
]
230+
platforms = set()
231+
for asset in tool_assets:
232+
name = asset["name"]
233+
# Extract platform part after py2.py3-none-
234+
# First remove .whl extension, then split by '-'
235+
name_without_ext = name.replace(".whl", "")
236+
parts = name_without_ext.split("-")
237+
if len(parts) >= 5:
238+
# Platform part starts after 'py2.py3-none'
239+
platform_part = "-".join(parts[4:])
240+
platforms.add(platform_part)
241+
242+
for platform_tag in sorted(platforms):
243+
print(f" {platform_tag}")
244+
return
245+
246+
# Detect platform
247+
if args.platform:
248+
platform_tags = [args.platform]
249+
else:
250+
try:
251+
platform_tags = get_platform_tag()
252+
print(f"Detected platform: {platform_tags[0]}")
253+
except ValueError as e:
254+
print(f"Error: {e}")
255+
print(
256+
"Use --platform to specify manually, or --list-platforms to see available options"
257+
)
258+
return 1
259+
260+
# Find the wheel
261+
# Extract version from release tag for pattern matching
262+
release_version = release_info["tag_name"].lstrip("v")
263+
wheel_asset = find_wheel_asset(
264+
release_info["assets"], args.tool, platform_tags, release_version
265+
)
266+
267+
if not wheel_asset:
268+
print(f"No wheel found for {args.tool} on platform {platform_tags[0]}")
269+
if args.version:
270+
print(f"Requested version: {args.version}")
271+
print(f"Available release: {release_info['tag_name']}")
272+
print("Use --list-platforms to see all available platforms")
273+
return 1
274+
275+
# Create output directory if it doesn't exist
276+
output_dir = Path(args.output)
277+
output_dir.mkdir(parents=True, exist_ok=True)
278+
279+
# Download the wheel
280+
filename = output_dir / wheel_asset["name"]
281+
if download_file(wheel_asset["browser_download_url"], filename):
282+
print("\nWheel downloaded successfully!")
283+
print(f"File: {filename}")
284+
print(f"Size: {wheel_asset['size'] / (1024 * 1024):.1f} MB")
285+
print(f"\nTo install: pip install {filename}")
286+
else:
287+
return 1
288+
289+
except Exception as e:
290+
print(f"Error: {e}")
291+
return 1
292+
293+
294+
if __name__ == "__main__":
295+
sys.exit(main())

docs/stylesheets/extra.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,12 @@ th {
201201
gap: 1rem;
202202
padding: 0 1rem;
203203
}
204-
204+
205205
.stats-grid {
206206
grid-template-columns: repeat(2, 1fr);
207207
padding: 0 1rem;
208208
}
209-
209+
210210
.stat strong {
211211
font-size: 1.5rem;
212212
}
@@ -467,4 +467,4 @@ a.md-annotation__index {
467467
box-shadow: 0 0 0 0 transparent;
468468
transform: scale(.95)
469469
}
470-
}
470+
}

noxfile.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
nox.options.reuse_existing_virtualenvs = True
44

5+
56
@nox.session
67
def docs(session: nox.Session) -> None:
78
"""Build the docs with sphinx."""

0 commit comments

Comments
 (0)