Skip to content

Commit 29c5700

Browse files
New CLI to install clang-format and clang-tidy Python wheels (#133)
* feat: support install python wheels * fix: Update python-test.yml to upload only clang-tools wheel * chore: save progress * fix: remove duplicate code * test: add test to test wheel * test: add condition to test * test: add condition to test * docs: update readme * Update clang_tools/wheel.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update clang_tools/wheel.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: add version_str to show version * fix: update dependencies and use public function * docs: udpate README.rst * docs: udpate README.rst * fix: add required=True and update test.yml * test: update to fix test * Update clang_tools/wheel.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update README.rst Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: update if condition using fromJSON * fix: skip 12.0.1 --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent 327e2a9 commit 29c5700

File tree

11 files changed

+263
-61
lines changed

11 files changed

+263
-61
lines changed

.github/workflows/python-test.yml renamed to .github/workflows/test.yml

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# This workflow will install Python dependencies, run tests and lint with a single version of Python
2-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3-
41
name: Test
52

63
on:
@@ -71,7 +68,7 @@ jobs:
7168
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 #v4
7269
with:
7370
name: clang-tools-pip_wheel
74-
path: dist/*.whl
71+
path: dist/clang_tools*.whl
7572

7673
install:
7774
needs: [build]
@@ -101,7 +98,7 @@ jobs:
10198
- name: Install clang-tools binaries
10299
run: clang-tools --install ${{ matrix.version }} --tool clang-format clang-tidy clang-query clang-apply-replacements
103100

104-
- name: Show path of binaries
101+
- name: Show path of clang-tools binaries
105102
shell: bash
106103
run: |
107104
if [ "${{ matrix.version }}" = "15" -o "${{ matrix.version }}" = "16" ] && [ "${{ matrix.os }}" = "windows-latest" ]; then
@@ -116,7 +113,7 @@ jobs:
116113
which "clang-apply-replacements-${{ matrix.version }}"
117114
fi
118115
119-
- name: Check clang-tools on Windows
116+
- name: Check clang-tools binaries on Windows
120117
if: matrix.os == 'windows-latest'
121118
shell: bash
122119
run: |
@@ -135,7 +132,7 @@ jobs:
135132
;;
136133
esac
137134
138-
- name: Check clang-tools on Unix
135+
- name: Check clang-tools binaries on Unix
139136
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'
140137
run: |
141138
if [ "${{ matrix.version }}" = "12.0.1" -a "${{ matrix.os }}" = "ubuntu-latest" ]; then
@@ -150,6 +147,50 @@ jobs:
150147
clang-apply-replacements-${{ matrix.version }} --version
151148
fi
152149
150+
- name: Install and check clang-format wheels
151+
if: ${{ matrix.version != '12.0.1' && fromJSON(matrix.version) >= 10 }} # Skip 12.0.1
152+
shell: bash
153+
run: |
154+
set -e
155+
clang-tools-wheel --tool clang-format --version ${{ matrix.version }}
156+
157+
echo "Checking clang-format versions..."
158+
clang-format --version
159+
160+
# Verify versions contain expected string
161+
clang_format_version=$(clang-format --version)
162+
163+
echo "clang-format version: $clang_format_version"
164+
165+
if ! echo "$clang_format_version" | grep -q "version ${{ matrix.version }}"; then
166+
echo "❌ Unexpected clang-format version!"
167+
exit 1
168+
fi
169+
170+
echo "✅ clang-format Versions are correct."
171+
172+
- name: Install and check clang-tidy wheels
173+
if: ${{ matrix.version != '12.0.1' && fromJSON(matrix.version) >= 13 }}
174+
shell: bash
175+
run: |
176+
set -e
177+
clang-tools-wheel --tool clang-tidy --version ${{ matrix.version }}
178+
179+
echo "Checking clang-tidy versions..."
180+
clang-tidy --version
181+
182+
# Verify versions contain expected string
183+
clang_tidy_version=$(clang-tidy --version)
184+
185+
echo "clang-tidy version: $clang_tidy_version"
186+
187+
if ! echo "$clang_tidy_version" | grep -q "version ${{ matrix.version }}"; then
188+
echo "❌ Unexpected clang-tidy version!"
189+
exit 1
190+
fi
191+
192+
echo "✅ clang-tidy Versions are correct."
193+
153194
docs:
154195
uses: cpp-linter/.github/.github/workflows/sphinx.yml@main
155196
with:

.pre-commit-config.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v5.0.0
3+
rev: v6.0.0
44
hooks:
55
- id: trailing-whitespace
66
exclude: \.output
@@ -12,13 +12,13 @@ repos:
1212
- id: debug-statements
1313
- id: requirements-txt-fixer
1414
- repo: https://github.com/asottile/pyupgrade
15-
rev: v3.20.0
15+
rev: v3.21.0
1616
hooks:
1717
- id: pyupgrade
1818
- repo: https://github.com/astral-sh/ruff-pre-commit
19-
rev: v0.11.11
19+
rev: v0.14.3
2020
hooks:
21-
- id: ruff
21+
- id: ruff-check
2222
- id: ruff-format
2323
# - repo: local
2424
# hooks:

README.rst

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
clang-tools CLI
22
===============
33

4-
**Install clang-format, clang-tidy, clang-query, and clang-apply-replacements binaries with clang-tools CLI.**
5-
64
.. |latest-version| image:: https://img.shields.io/pypi/v/clang-tools?color=blue
75
:target: https://pypi.org/project/clang-tools/
86
:alt: PyPI
9-
.. |python-test| image:: https://github.com/cpp-linter/clang-tools-pip/actions/workflows/python-test.yml/badge.svg
10-
:target: https://github.com/cpp-linter/clang-tools-pip/actions/workflows/python-test.yml
11-
:alt: Python test
7+
.. |test| image:: https://github.com/cpp-linter/clang-tools-pip/actions/workflows/test.yml/badge.svg
8+
:target: https://github.com/cpp-linter/clang-tools-pip/actions/workflows/test.yml
9+
:alt: test
1210
.. |codecov-badge| image:: https://codecov.io/gh/cpp-linter/clang-tools-pip/branch/main/graph/badge.svg?token=40G5ZOIRRR
1311
:target: https://codecov.io/gh/cpp-linter/clang-tools-pip
1412
:alt: codecov
@@ -22,16 +20,23 @@ clang-tools CLI
2220
:target: https://pypistats.org/packages/clang-tools
2321
:alt: PyPI - Downloads
2422

25-
|latest-version| |python-test| |codecov-badge| |sonar-badge| |platform-badge| |pypi-badge|
23+
|latest-version| |test| |codecov-badge| |sonar-badge| |platform-badge| |pypi-badge|
24+
25+
26+
Easily install clang-format, clang-tidy, clang-query, and clang-apply-replacements static binaries or Python wheels using the ``clang-tools`` CLI.
27+
2628

2729
.. important::
2830
This package only manages binary executables (& corresponding symbolic links) that
2931
are installed using this package's executable script. It does not intend to change or
3032
modify any binary executable installed from other sources (like LLVM releases).
3133

34+
For Python wheels, this CLI only support clang-format and clang-tidy tools.
35+
3236
Features
3337
--------
3438

39+
- Support clang tools binaries and Python wheels.
3540
- Binaries are statically linked for improved portability.
3641
- Binaries can be specified installed for increased flexibility.
3742
- Binaries are checked with SHA512 checksum. This ensures:
@@ -48,6 +53,7 @@ Features
4853
category.
4954
- Customizable install path.
5055

56+
5157
Install clang-tools CLI
5258
-----------------------
5359

@@ -71,26 +77,27 @@ Install clang-tools CLI
7177
2. the installed path (for MacOS and Windows) is within the environment's
7278
variable ``PATH``.
7379

74-
Install `clang-tools` command with pip
80+
Install ``clang-tools`` command with pip
7581

7682
.. code-block:: shell
7783
7884
pip install clang-tools
7985
80-
Install `clang-tools` from git repo
86+
Install ``clang-tools`` from git repo
8187

8288
.. code-block:: shell
8389
8490
pip install git+https://github.com/cpp-linter/clang-tools-pip.git@main
8591
92+
8693
CLI Usage
8794
---------
8895

8996
For a list of supported Command Line Interface options, see
9097
`the CLI documentation <https://cpp-linter.github.io/clang-tools-pip/cli_args.html>`_
9198

92-
Examples
93-
********
99+
Install binaries examples
100+
~~~~~~~~~~~~~~~~~~~~~~~~~
94101

95102
Use ``clang-tools`` command to install version 13 binaries.
96103

@@ -126,17 +133,56 @@ If the installed directory is in your path, you can run the installed tools.
126133
Default target: x86_64-unknown-linux-gnu
127134
Host CPU: skylake
128135
129-
Supported versions
136+
137+
Install wheels examples
138+
~~~~~~~~~~~~~~~~~~~~~~~~~
139+
140+
After installing the ``clang-tools`` CLI, you can install the Python wheels using the ``clang-tools-wheel`` command.
141+
142+
.. important::
143+
144+
The ``clang-tools-wheel`` command is primarily intended for cpp-linter projects to simplify installing clang tools Python wheels.
145+
For general use, it is recommended to install the wheels directly using ``pip``, ``pipx``, ``uv``, or similar tools.
146+
147+
148+
.. code-block:: shell
149+
150+
# Install latest clang-format wheel
151+
clang-tools-wheel --tool clang-format
152+
# Install specific version clang-format wheel
153+
clang-tools-wheel --tool clang-format --version 21
154+
155+
# Install latest clang-tidy wheel
156+
clang-tools-wheel --tool clang-tidy
157+
# Install specific version clang-tidy wheel
158+
clang-tools-wheel --tool clang-tidy --version 21
159+
160+
161+
Supported Versions
130162
------------------
131163

132-
clang-format, clang-tidy, clang-query, clang-apply-replacements
133-
***************************************************************
164+
165+
clang tools binaries
166+
~~~~~~~~~~~~~~~~~~~~
167+
168+
The following table shows the supported versions of clang-format, clang-tidy, clang-query, and clang-apply-replacements binaries for each platform:
169+
134170
.. csv-table::
135-
:header: "Version", "21", "20", "19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "9"
171+
:header: "Platform", "21", "20", "19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "9"
136172
:stub-columns: 1
137173

138174
Linux,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️
139175
Windows,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️
140176
macOS,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️
141177

142178
For more details, visit the `clang-tools-static-binaries <https://github.com/cpp-linter/clang-tools-static-binaries>`_ repository.
179+
180+
clang tools Python wheels
181+
~~~~~~~~~~~~~~~~~~~~~~~~~
182+
183+
The following Python wheels are supported:
184+
185+
- `clang-format <https://pypi.org/project/clang-format/#history>`_
186+
- `clang-tidy <https://pypi.org/project/clang-tidy/#history>`_
187+
188+
Check the respective PyPI pages for available versions and platform support.

clang_tools/wheel.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from argparse import ArgumentParser
2+
from cpp_linter_hooks.util import resolve_install
3+
4+
5+
def get_parser() -> ArgumentParser:
6+
"""Get a parser to interpret CLI args."""
7+
parser = ArgumentParser(description="Install specified clang tool wheel")
8+
parser.add_argument(
9+
"--tool",
10+
required=True,
11+
choices=["clang-format", "clang-tidy"],
12+
help="Tool to install (clang-format or clang-tidy)",
13+
)
14+
parser.add_argument(
15+
"--version",
16+
default=None,
17+
help="Version to install (e.g., 21 or 21.1.2). Defaults to latest compatible version.",
18+
)
19+
return parser
20+
21+
22+
def main() -> int:
23+
parser = get_parser()
24+
args = parser.parse_args()
25+
path = resolve_install(args.tool, args.version)
26+
version_str = f" version {args.version}" if args.version else " latest version"
27+
if path:
28+
print(f"{args.tool}{version_str} installed at: {path}")
29+
return 0
30+
else:
31+
print(f"Failed to install {args.tool}{version_str}")
32+
return 1
33+
34+
35+
if __name__ == "__main__":
36+
raise SystemExit(main())

docs/api.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ API Reference
99

1010
.. automodule:: clang_tools.util
1111
:members:
12+
13+
.. automodule:: clang_tools.wheel
14+
:members:

docs/conf.py

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from sphinx.util.docutils import SphinxRole
1515
from sphinx_immaterial.inline_icons import load_svg_into_builder_env
1616
from clang_tools.main import get_parser
17+
from clang_tools.wheel import get_parser as get_wheel_parser
1718

1819
# -- Path setup --------------------------------------------------------------
1920

@@ -207,38 +208,49 @@ def setup(app: Sphinx):
207208
app.add_role("badge-default", CliBadgeDefault())
208209
app.add_role("badge-switch", CliBadgeSwitch())
209210

211+
def write_cli_doc(doc_path, parser, prog_name):
212+
with open(doc_path, mode="w") as doc:
213+
doc.write(f"{prog_name} --help\n{'=' * 30}\n\n")
214+
doc.write(
215+
".. code-block:: text\n :caption: Usage\n :class: no-copy\n\n"
216+
)
217+
parser.prog = prog_name
218+
str_buf = StringIO()
219+
parser.print_usage(str_buf)
220+
usage = str_buf.getvalue()
221+
start = usage.find(parser.prog)
222+
for line in usage.splitlines():
223+
doc.write(f" {line[start:]}\n")
224+
args = parser._optionals._actions
225+
for arg in args:
226+
aliases = arg.option_strings
227+
if not aliases or arg.default == "==SUPPRESS==":
228+
continue
229+
assert arg.help is not None
230+
doc.write("\n.. std:option:: " + ", ".join(aliases) + "\n")
231+
req_ver = next(
232+
(
233+
ver
234+
for ver, names in REQUIRED_VERSIONS.items()
235+
if arg.dest in names
236+
),
237+
"0.1.0",
238+
)
239+
doc.write(f"\n :badge-version:`{req_ver}` ")
240+
if arg.default:
241+
default = (
242+
" ".join(arg.default)
243+
if isinstance(arg.default, list)
244+
else arg.default
245+
)
246+
doc.write(f":badge-default:`{default}` ")
247+
if isinstance(arg, _StoreTrueAction):
248+
doc.write(":badge-switch:`Accepts no value` ")
249+
doc.write("\n\n ")
250+
doc.write("\n ".join(arg.help.splitlines()) + "\n")
251+
210252
cli_doc = Path(app.srcdir, "cli_args.rst")
211-
with open(cli_doc, mode="w") as doc:
212-
doc.write("Command Line Interface Options\n==============================\n\n")
213-
parser = get_parser()
214-
doc.write(".. code-block:: text\n :caption: Usage\n :class: no-copy\n\n")
215-
parser.prog = "clang-tools"
216-
str_buf = StringIO()
217-
parser.print_usage(str_buf)
218-
usage = str_buf.getvalue()
219-
start = usage.find(parser.prog)
220-
for line in usage.splitlines():
221-
doc.write(f" {line[start:]}\n")
222-
223-
args = parser._optionals._actions
224-
for arg in args:
225-
aliases = arg.option_strings
226-
if not aliases or arg.default == "==SUPPRESS==":
227-
continue
228-
assert arg.help is not None
229-
doc.write("\n.. std:option:: " + ", ".join(aliases) + "\n")
230-
req_ver = "0.1.0"
231-
for ver, names in REQUIRED_VERSIONS.items():
232-
if arg.dest in names:
233-
req_ver = ver
234-
break
235-
doc.write(f"\n :badge-version:`{req_ver}` ")
236-
if arg.default:
237-
default = arg.default
238-
if isinstance(arg.default, list):
239-
default = " ".join(arg.default)
240-
doc.write(f":badge-default:`{default}` ")
241-
if isinstance(arg, _StoreTrueAction):
242-
doc.write(":badge-switch:`Accepts no value` ")
243-
doc.write("\n\n ")
244-
doc.write("\n ".join(arg.help.splitlines()) + "\n")
253+
write_cli_doc(cli_doc, get_parser(), "clang-tools")
254+
255+
wheel_cli_doc = Path(app.srcdir, "wheel_cli_args.rst")
256+
write_cli_doc(wheel_cli_doc, get_wheel_parser(), "clang-tools-wheel")

0 commit comments

Comments
 (0)