Skip to content

Commit

Permalink
feat(clang-format): support multiple LLVM version downloads
Browse files Browse the repository at this point in the history
  • Loading branch information
sanrut01 authored and jlebar committed Sep 20, 2021
1 parent 2f58800 commit 7954578
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 41 deletions.
3 changes: 3 additions & 0 deletions .flake8
@@ -0,0 +1,3 @@
[flake8]
max-line-length = 88
extend-ignore = E203, E501, W503, F821, F841, E231
22 changes: 18 additions & 4 deletions .pre-commit-config.yaml
Expand Up @@ -9,10 +9,7 @@ repos:
hooks:
- id: mypy
args: [--strict]
- repo: https://github.com/psf/black
rev: 21.7b0
hooks:
- id: black
exclude: check_do_not_submit.py
- repo: https://github.com/PyCQA/isort.git
rev: 5.9.3
hooks:
Expand All @@ -37,3 +34,20 @@ repos:
- id: fix-byte-order-marker
- id: forbid-new-submodules
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.9.0
hooks:
- id: python-check-blanket-type-ignore
- id: python-use-type-annotations
- id: python-check-mock-methods
- id: python-check-blanket-noqa
- id: python-no-eval
- id: rst-backticks
- id: rst-directive-colons
- id: rst-inline-touching-normal
- id: text-unicode-replacement-char
- repo: https://gitlab.com/PyCQA/flake8
rev: 3.9.2
hooks:
- id: flake8
exclude: check_do_not_submit.py
105 changes: 102 additions & 3 deletions .pre-commit-hooks.yaml
Expand Up @@ -16,13 +16,14 @@
name: clang-format-diff
entry: clang_format.py
language: script
args: ['--diff']
args: ['11.0.0','diff']
require_serial: true


# git-clang-format does not take kindly to being run in parallel; it writes
# to a speicfic file in .git.
#
# TODO: Can we fix git-clang-format so it's parallel-friendly?
require_serial: true

# Fun fact, clang-format can format more than just C and C++!
#
Expand All @@ -36,5 +37,103 @@
name: clang-format-whole-file
entry: clang_format.py
language: script
args: ['--whole-file']
args: ['11.0.0','whole-file']
require_serial: false

- id: clang-format-diff-3.6.0
name: clang-format-diff-3.6.0
entry: clang_format.py
language: script
args: ['3.6.0','diff']
require_serial: true

- id: clang-format-whole-file-3.6.0
name: clang-format-whole-file-3.6.0
entry: clang_format.py
language: script
args: ['3.6.0','whole-file']
require_serial: false

- id: clang-format-diff-3.7.0
name: clang-format-diff-3.7.0
entry: clang_format.py
language: script
args: ['3.7.0','diff']
require_serial: true

- id: clang-format-whole-file-3.8.0
name: clang-format-whole-file-3.8.0
entry: clang_format.py
language: script
args: ['3.8.0','whole-file']
require_serial: false

- id: clang-format-diff-3.9.0
name: clang-format-diff-3.9.0
entry: clang_format.py
language: script
args: ['3.9.0','diff']
require_serial: true

- id: clang-format-whole-file-3.9.0
name: clang-format-whole-file-3.9.0
entry: clang_format.py
language: script
args: ['3.9.0','whole-file']
require_serial: false

- id: clang-format-diff-4.0.0
name: clang-format-diff-4.0.0
entry: clang_format.py
language: script
args: ['4.0.0','diff']
require_serial: true

- id: clang-format-whole-file-4.0.0
name: clang-format-whole-file-4.0.0
entry: clang_format.py
language: script
args: ['4.0.0','whole-file']
require_serial: false

- id: clang-format-diff-5.0.0
name: clang-format-diff-5.0.0
entry: clang_format.py
language: script
args: ['5.0.0','diff']
require_serial: true

- id: clang-format-whole-file-5.0.0
name: clang-format-whole-file-5.0.0
entry: clang_format.py
language: script
args: ['5.0.0','whole-file']
require_serial: false

- id: clang-format-diff-8.0.0
name: clang-format-diff-8.0.0
entry: clang_format.py
language: script
args: ['8.0.0','diff']
require_serial: true

- id: clang-format-whole-file-8.0.0
name: clang-format-whole-file-8.0.0
entry: clang_format.py
language: script
args: ['8.0.0','whole-file']
require_serial: false

- id: clang-format-diff-11.0.0
name: clang-format-diff-11.0.0
entry: clang_format.py
language: script
args: ['11.0.0','diff']
require_serial: true

- id: clang-format-whole-file-11.0.0
name: clang-format-whole-file-11.0.0
entry: clang_format.py
language: script
args: ['11.0.0','whole-file']
require_serial: false
1 change: 1 addition & 0 deletions check_do_not_submit.py
Expand Up @@ -2,6 +2,7 @@

"""Checks each file in sys.argv for the string "DO NOT SUBMIT"."""


from __future__ import annotations

import os
Expand Down
142 changes: 108 additions & 34 deletions clang_format.py
Expand Up @@ -6,10 +6,10 @@
Usage example:
clang_format.py [--diff|--whole-file] 10.0.0 foo.cpp bar.h
clang_format.py 11.0.0 [diff|whole-file] foo.cpp bar.h
"""

import glob
import argparse
import hashlib
import os
import platform
Expand All @@ -20,17 +20,57 @@
import tempfile
import urllib.request
from pathlib import Path

# clang-format sha1s were retrieved at Chromium rev
# 81cc23a856578b149a37dd109b147d8544f9cbd8.
#
# https://github.com/chromium/chromium/blob/master/buildtools/linux64/clang-format.sha1
# https://github.com/chromium/chromium/blob/master/buildtools/mac/clang-format.sha1
# https://github.com/chromium/chromium/blob/master/buildtools/win/clang-format.exe.sha1
CLANG_FORMAT_SHAS = {
"Linux": "1baf0089e895c989a311b6a38ed94d0e8be4c0a7",
"Darwin": "62bde1baa7196ad9df969fc1f06b66360b1a927b",
"Windows": "d4afd4eba27022f5f6d518133aebde57281677c9",
from typing import Final, Mapping, Optional, Sequence, Tuple

# clang-format sha1s were retrieved at
# https://commondatastorage.googleapis.com/chromium-clang-format/
# The below shas are tested across different os to identify the version.
# For getting the list a script is written to download all the sha's
# from https://commondatastorage.googleapis.com/chromium-clang-format/.
# and validate them in different os to identify the version corresponding to OS.
# The helper script to validate will be added as a future patch

CLANG_FORMAT_SHAS: Final[Mapping[Tuple[int, int, int], Mapping[str, str],]] = {
(3, 5, 0): {
"Linux": "b26f74f07f51a99d79d34be57a28bc82dee42854",
"Darwin": "ce0718a133a059aca5da5f307a36bbc310df3e12",
"Windows": "fc8a7cd2219eaa70daa01173844fad4c815394d7",
},
(3, 6, 0): {
"Linux": "f237f50fab9ceca4066788b7bf936ef2aa366239",
"Darwin": "eb3fd19492128421c3eab80f4cdeed7b07428988",
"Windows": "e93c345e8d4d003632cabae8ff4f64c3b74f0c16",
},
(3, 7, 0): {
"Linux": "acc9e19e04ad4cf6be067366d2522348cd160ae1",
"Darwin": "e66adcd1631b8d80650e7b15106033b04c9c2212",
"Windows": "2d5e931765ee1c7c4465fd6d77c8b7606c487b3f",
},
(3, 9, 0): {
"Linux": "8b68e8093516183b8f38626740eeaff97f112f7e",
"Darwin": "afe0942b94fe33619361efe1510ae081c3070dc1",
"Windows": "f80b6ab38d7c7e0903c25e968028c1eaa25bb874",
},
(4, 0, 0): {
"Linux": "06b8b3e315c1b55b58459d61fe3297e0988c6c63",
"Darwin": "e0cfdaf63938e06d05a986a0038658ec6b7cad17",
"Windows": "a15d5130e787633a119e8e0ae9b267696c4c2863",
},
(5, 0, 0): {
"Linux": "5349d1954e17f6ccafb6e6663b0f13cdb2bb33c8",
"Darwin": "0679b295e2ce2fce7919d1e8d003e497475f24a3",
"Windows": "c8455d43d052eb79f65d046c6b02c169857b963b",
},
(8, 0, 0): {
"Linux": "327721c99d40602c1829b4b682771d52e1d5f1b8",
"Darwin": "025ca7c75f37ef4a40f3a67d81ddd11d7d0cdb9b",
"Windows": "b5f5d8d5f8a8fcd2edb5b6cae37c0dc3e129c945",
},
(11, 0, 0): {
"Linux": "1baf0089e895c989a311b6a38ed94d0e8be4c0a7",
"Darwin": "62bde1baa7196ad9df969fc1f06b66360b1a927b",
"Windows": "d4afd4eba27022f5f6d518133aebde57281677c9",
},
}


Expand Down Expand Up @@ -76,22 +116,28 @@ def check_hash(sha: str, file: Path) -> None:

if d.hexdigest() != sha:
print(
f"FATAL: sha1sum mismatch on {file}. Expected {sha}, but was {d.hexdigest()}",
f"FATAL: sha1sum mismatch on {file}.\
Expected {sha}, but was {d.hexdigest()}",
file=sys.stderr,
)
print("Maybe the file is corrupted? Try deleting it.")
sys.exit(1)


def clang_format_path() -> Path:
"""Gets the path of the relevant clang-format binary.
def get_version_key(version: str) -> Tuple[int, int, int]:
major, minor, patch = version.split(".")
return (int(major), int(minor), int(patch))


def clang_format_path(version: Tuple[int, int, int]) -> Path:
"""
Gets the path of the relevant clang-format binary.
Downloads it if necessary.
"""
try:
base_cachedir = Path(os.environ["XDG_CACHE_HOME"])
except KeyError:
base_cachedir = Path(os.environ["HOME"]).joinpath(".cache")
base_cachedir = Path.home().joinpath(".cache")

cachedir = base_cachedir.joinpath("pre-commit-jlebar")

Expand All @@ -105,8 +151,8 @@ def clang_format_path() -> Path:
"""
)

clang_format_sha = CLANG_FORMAT_SHAS[platform.system()]
clang_format_file = cachedir.joinpath("clang-format-" + clang_format_sha)
clang_format_sha = CLANG_FORMAT_SHAS[version][platform.system()]
clang_format_file = cachedir / f"clang-format-{clang_format_sha}"

if not clang_format_file.exists():
download_clang_format(clang_format_sha, clang_format_file)
Expand All @@ -115,29 +161,57 @@ def clang_format_path() -> Path:
return clang_format_file


def main() -> None:
def main(argv: Optional[Sequence[str]] = None) -> int:
parser = argparse.ArgumentParser(description="Arguments for pre commit.")
parser.add_argument(
"version",
choices=tuple(
".".join(f"{element}" for element in version)
for version in CLANG_FORMAT_SHAS.keys()
),
help="Clang format version to run",
)
parser.add_argument("scope", choices=["diff", "whole-file"], help="Run on files")
parser.add_argument(
"files",
nargs="*",
type=Path,
help="Files pre-commit believes are changed.",
)
try:
args = parser.parse_args(argv)
except SystemExit as e:
assert e.code is not None
return e.code

this_dir = os.path.dirname(__file__)
git_cf_path = os.path.join(this_dir, "git-clang-format")

if sys.argv[1] == "--diff":
print("Formatting changed lines in " + " ".join(sys.argv[2:]))
subprocess.run(
[
if args.scope == "diff":
print("Formatting changed lines in " + " ".join(f"{args.files}"))
clang_format_run = subprocess.run(
(
sys.executable,
git_cf_path,
"-f",
"--binary",
str(clang_format_path()),
f"{clang_format_path(get_version_key(args.version))}",
"--",
]
+ sys.argv[2:]
*args.files,
),
check=True,
)
elif sys.argv[1] == "--whole-file":
print("Formatting all lines in " + " ".join(sys.argv[2:]))
subprocess.run([str(clang_format_path()), "-i", "--"] + sys.argv[2:])
else:
print("Must pass --diff or --whole-file as first argument.")
sys.exit(1)
elif args.scope == "whole-file":
print("Formatting all lines in " + " ".join(f"{args.files}"))
clang_format_run = subprocess.run(
(
f"{clang_format_path(get_version_key(args.version))}",
"-i",
"--",
*args.files,
),
check=True,
)
return clang_format_run.returncode


if __name__ == "__main__":
Expand Down
3 changes: 3 additions & 0 deletions mypy.ini
@@ -0,0 +1,3 @@
[mypy]
show_error_codes = true
python_version = 3.9

0 comments on commit 7954578

Please sign in to comment.