Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
55 changes: 48 additions & 7 deletions .github/workflows/python-test.yml → .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Test

on:
Expand Down Expand Up @@ -71,7 +68,7 @@ jobs:
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 #v4
with:
name: clang-tools-pip_wheel
path: dist/*.whl
path: dist/clang_tools*.whl

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

- name: Show path of binaries
- name: Show path of clang-tools binaries
shell: bash
run: |
if [ "${{ matrix.version }}" = "15" -o "${{ matrix.version }}" = "16" ] && [ "${{ matrix.os }}" = "windows-latest" ]; then
Expand All @@ -116,7 +113,7 @@ jobs:
which "clang-apply-replacements-${{ matrix.version }}"
fi

- name: Check clang-tools on Windows
- name: Check clang-tools binaries on Windows
if: matrix.os == 'windows-latest'
shell: bash
run: |
Expand All @@ -135,7 +132,7 @@ jobs:
;;
esac

- name: Check clang-tools on Unix
- name: Check clang-tools binaries on Unix
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'
run: |
if [ "${{ matrix.version }}" = "12.0.1" -a "${{ matrix.os }}" = "ubuntu-latest" ]; then
Expand All @@ -150,6 +147,50 @@ jobs:
clang-apply-replacements-${{ matrix.version }} --version
fi

- name: Install and check clang-format wheels
if: ${{ matrix.version != '12.0.1' && fromJSON(matrix.version) >= 10 }} # Skip 12.0.1
shell: bash
run: |
set -e
clang-tools-wheel --tool clang-format --version ${{ matrix.version }}

echo "Checking clang-format versions..."
clang-format --version

# Verify versions contain expected string
clang_format_version=$(clang-format --version)

echo "clang-format version: $clang_format_version"

if ! echo "$clang_format_version" | grep -q "version ${{ matrix.version }}"; then
echo "❌ Unexpected clang-format version!"
exit 1
fi

echo "✅ clang-format Versions are correct."

- name: Install and check clang-tidy wheels
if: ${{ matrix.version != '12.0.1' && fromJSON(matrix.version) >= 13 }}
shell: bash
run: |
set -e
clang-tools-wheel --tool clang-tidy --version ${{ matrix.version }}

echo "Checking clang-tidy versions..."
clang-tidy --version

# Verify versions contain expected string
clang_tidy_version=$(clang-tidy --version)

echo "clang-tidy version: $clang_tidy_version"

if ! echo "$clang_tidy_version" | grep -q "version ${{ matrix.version }}"; then
echo "❌ Unexpected clang-tidy version!"
exit 1
fi

echo "✅ clang-tidy Versions are correct."

docs:
uses: cpp-linter/.github/.github/workflows/sphinx.yml@main
with:
Expand Down
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
exclude: \.output
Expand All @@ -12,13 +12,13 @@ repos:
- id: debug-statements
- id: requirements-txt-fixer
- repo: https://github.com/asottile/pyupgrade
rev: v3.20.0
rev: v3.21.0
hooks:
- id: pyupgrade
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.11.11
rev: v0.14.3
hooks:
- id: ruff
- id: ruff-check
- id: ruff-format
# - repo: local
# hooks:
Expand Down
74 changes: 60 additions & 14 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
clang-tools CLI
===============

**Install clang-format, clang-tidy, clang-query, and clang-apply-replacements binaries with clang-tools CLI.**

.. |latest-version| image:: https://img.shields.io/pypi/v/clang-tools?color=blue
:target: https://pypi.org/project/clang-tools/
:alt: PyPI
.. |python-test| image:: https://github.com/cpp-linter/clang-tools-pip/actions/workflows/python-test.yml/badge.svg
:target: https://github.com/cpp-linter/clang-tools-pip/actions/workflows/python-test.yml
:alt: Python test
.. |test| image:: https://github.com/cpp-linter/clang-tools-pip/actions/workflows/test.yml/badge.svg
:target: https://github.com/cpp-linter/clang-tools-pip/actions/workflows/test.yml
:alt: test
.. |codecov-badge| image:: https://codecov.io/gh/cpp-linter/clang-tools-pip/branch/main/graph/badge.svg?token=40G5ZOIRRR
:target: https://codecov.io/gh/cpp-linter/clang-tools-pip
:alt: codecov
Expand All @@ -22,16 +20,23 @@ clang-tools CLI
:target: https://pypistats.org/packages/clang-tools
:alt: PyPI - Downloads

|latest-version| |python-test| |codecov-badge| |sonar-badge| |platform-badge| |pypi-badge|
|latest-version| |test| |codecov-badge| |sonar-badge| |platform-badge| |pypi-badge|


Easily install clang-format, clang-tidy, clang-query, and clang-apply-replacements static binaries or Python wheels using the ``clang-tools`` CLI.


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

For Python wheels, this CLI only support clang-format and clang-tidy tools.

Features
--------

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


Install clang-tools CLI
-----------------------

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

Install `clang-tools` command with pip
Install ``clang-tools`` command with pip

.. code-block:: shell

pip install clang-tools

Install `clang-tools` from git repo
Install ``clang-tools`` from git repo

.. code-block:: shell

pip install git+https://github.com/cpp-linter/clang-tools-pip.git@main


CLI Usage
---------

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

Examples
********
Install binaries examples
~~~~~~~~~~~~~~~~~~~~~~~~~

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

Expand Down Expand Up @@ -126,17 +133,56 @@ If the installed directory is in your path, you can run the installed tools.
Default target: x86_64-unknown-linux-gnu
Host CPU: skylake

Supported versions

Install wheels examples
~~~~~~~~~~~~~~~~~~~~~~~~~

After installing the ``clang-tools`` CLI, you can install the Python wheels using the ``clang-tools-wheel`` command.

.. important::

The ``clang-tools-wheel`` command is primarily intended for cpp-linter projects to simplify installing clang tools Python wheels.
For general use, it is recommended to install the wheels directly using ``pip``, ``pipx``, ``uv``, or similar tools.


.. code-block:: shell

# Install latest clang-format wheel
clang-tools-wheel --tool clang-format
# Install specific version clang-format wheel
clang-tools-wheel --tool clang-format --version 21

# Install latest clang-tidy wheel
clang-tools-wheel --tool clang-tidy
# Install specific version clang-tidy wheel
clang-tools-wheel --tool clang-tidy --version 21


Supported Versions
------------------

clang-format, clang-tidy, clang-query, clang-apply-replacements
***************************************************************

clang tools binaries
~~~~~~~~~~~~~~~~~~~~

The following table shows the supported versions of clang-format, clang-tidy, clang-query, and clang-apply-replacements binaries for each platform:

.. csv-table::
:header: "Version", "21", "20", "19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "9"
:header: "Platform", "21", "20", "19", "18", "17", "16", "15", "14", "13", "12", "11", "10", "9"
:stub-columns: 1

Linux,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️
Windows,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️
macOS,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️,✔️

For more details, visit the `clang-tools-static-binaries <https://github.com/cpp-linter/clang-tools-static-binaries>`_ repository.

clang tools Python wheels
~~~~~~~~~~~~~~~~~~~~~~~~~

The following Python wheels are supported:

- `clang-format <https://pypi.org/project/clang-format/#history>`_
- `clang-tidy <https://pypi.org/project/clang-tidy/#history>`_

Check the respective PyPI pages for available versions and platform support.
36 changes: 36 additions & 0 deletions clang_tools/wheel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from argparse import ArgumentParser
from cpp_linter_hooks.util import resolve_install


def get_parser() -> ArgumentParser:
"""Get a parser to interpret CLI args."""
parser = ArgumentParser(description="Install specified clang tool wheel")
parser.add_argument(
"--tool",
required=True,
choices=["clang-format", "clang-tidy"],
help="Tool to install (clang-format or clang-tidy)",
)
parser.add_argument(
"--version",
default=None,
help="Version to install (e.g., 21 or 21.1.2). Defaults to latest compatible version.",
)
return parser


def main() -> int:
parser = get_parser()
args = parser.parse_args()
path = resolve_install(args.tool, args.version)
version_str = f" version {args.version}" if args.version else " latest version"
if path:
print(f"{args.tool}{version_str} installed at: {path}")
return 0
else:
print(f"Failed to install {args.tool}{version_str}")
return 1


if __name__ == "__main__":
raise SystemExit(main())
3 changes: 3 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ API Reference

.. automodule:: clang_tools.util
:members:

.. automodule:: clang_tools.wheel
:members:
80 changes: 46 additions & 34 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from sphinx.util.docutils import SphinxRole
from sphinx_immaterial.inline_icons import load_svg_into_builder_env
from clang_tools.main import get_parser
from clang_tools.wheel import get_parser as get_wheel_parser

# -- Path setup --------------------------------------------------------------

Expand Down Expand Up @@ -207,38 +208,49 @@ def setup(app: Sphinx):
app.add_role("badge-default", CliBadgeDefault())
app.add_role("badge-switch", CliBadgeSwitch())

def write_cli_doc(doc_path, parser, prog_name):
with open(doc_path, mode="w") as doc:
doc.write(f"{prog_name} --help\n{'=' * 30}\n\n")
doc.write(
".. code-block:: text\n :caption: Usage\n :class: no-copy\n\n"
)
parser.prog = prog_name
str_buf = StringIO()
parser.print_usage(str_buf)
usage = str_buf.getvalue()
start = usage.find(parser.prog)
for line in usage.splitlines():
doc.write(f" {line[start:]}\n")
args = parser._optionals._actions
for arg in args:
aliases = arg.option_strings
if not aliases or arg.default == "==SUPPRESS==":
continue
assert arg.help is not None
doc.write("\n.. std:option:: " + ", ".join(aliases) + "\n")
req_ver = next(
(
ver
for ver, names in REQUIRED_VERSIONS.items()
if arg.dest in names
),
"0.1.0",
)
doc.write(f"\n :badge-version:`{req_ver}` ")
if arg.default:
default = (
" ".join(arg.default)
if isinstance(arg.default, list)
else arg.default
)
doc.write(f":badge-default:`{default}` ")
if isinstance(arg, _StoreTrueAction):
doc.write(":badge-switch:`Accepts no value` ")
doc.write("\n\n ")
doc.write("\n ".join(arg.help.splitlines()) + "\n")

cli_doc = Path(app.srcdir, "cli_args.rst")
with open(cli_doc, mode="w") as doc:
doc.write("Command Line Interface Options\n==============================\n\n")
parser = get_parser()
doc.write(".. code-block:: text\n :caption: Usage\n :class: no-copy\n\n")
parser.prog = "clang-tools"
str_buf = StringIO()
parser.print_usage(str_buf)
usage = str_buf.getvalue()
start = usage.find(parser.prog)
for line in usage.splitlines():
doc.write(f" {line[start:]}\n")

args = parser._optionals._actions
for arg in args:
aliases = arg.option_strings
if not aliases or arg.default == "==SUPPRESS==":
continue
assert arg.help is not None
doc.write("\n.. std:option:: " + ", ".join(aliases) + "\n")
req_ver = "0.1.0"
for ver, names in REQUIRED_VERSIONS.items():
if arg.dest in names:
req_ver = ver
break
doc.write(f"\n :badge-version:`{req_ver}` ")
if arg.default:
default = arg.default
if isinstance(arg.default, list):
default = " ".join(arg.default)
doc.write(f":badge-default:`{default}` ")
if isinstance(arg, _StoreTrueAction):
doc.write(":badge-switch:`Accepts no value` ")
doc.write("\n\n ")
doc.write("\n ".join(arg.help.splitlines()) + "\n")
write_cli_doc(cli_doc, get_parser(), "clang-tools")

wheel_cli_doc = Path(app.srcdir, "wheel_cli_args.rst")
write_cli_doc(wheel_cli_doc, get_wheel_parser(), "clang-tools-wheel")
Loading
Loading