Skip to content

Commit

Permalink
Add github job for code formatting check
Browse files Browse the repository at this point in the history
  • Loading branch information
sogartar committed Feb 1, 2023
1 parent 26006ef commit b9b3e3c
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 0 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2022 The IREE Authors
#
# Licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

name: Lint

on: [pull_request]

jobs:
yapf:
runs-on: ubuntu-20.04
steps:
- name: Checking out repository
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
- name: Setting up python
uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # v4.5.0
- name: Fetching Base Branch
# We have to explicitly fetch the base branch as well
run: git fetch --no-tags --prune --depth=1 origin "${GITHUB_BASE_REF?}:${GITHUB_BASE_REF?}"
- name: Install yapf
run: |
python3 -m pip install yapf==0.32.0
- name: Run format_diff.py with yapf
run: |
git diff -U0 "${GITHUB_BASE_REF?}" | python3 third_party/format_diff/format_diff.py yapf -i
git diff --exit-code
- name: Instructions for fixing the above linting errors
if: ${{ failure() }}
run: |
printf "You can fix the lint errors above by running\n"
1 change: 1 addition & 0 deletions third_party/format_diff/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Forked from https://github.com/llvm/llvm-project/blob/master/clang/tools/clang-format/clang-format-diff.py
165 changes: 165 additions & 0 deletions third_party/format_diff/format_diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#!/usr/bin/env python3
#
#===- format_diff.py - Diff Reformatter ----*- python3 -*--===#
#
# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#===------------------------------------------------------------------------===#
"""
This script reads input from a unified diff and reformats all the changed
lines. This is useful to reformat all the lines touched by a specific patch.
Example usage:
git diff -U0 HEAD^ | python3 format_diff.py yapf -i
git diff -U0 HEAD^ | python3 format_diff.py clang-format -i
svn diff --diff-cmd=diff -x-U0 | python3 format_diff.py -p0 clang-format -i
General usage:
<some diff> | python3 format_diff.py [--regex] [--lines-style] [-p] binary [args for binary]
It should be noted that the filename contained in the diff is used unmodified
to determine the source file to update. Users calling this script directly
should be careful to ensure that the path in the diff is correct relative to the
current working directory.
"""

import argparse
import difflib
import io
import re
import subprocess
import sys

BINARY_TO_DEFAULT_REGEX = {
"yapf": r".*\.py",
"clang-format":
r".*\.(cpp|cc|c\+\+|cxx|c|cl|h|hh|hpp|hxx|m|mm|inc|js|ts|proto|"
r"protodevel|java|cs)",
}


def parse_arguments():
parser = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
"binary",
help="Location of binary to use for formatting. This controls the "
"default values of --regex and --lines-style. If binary isn't 'yapf' "
"or 'clang-format' then --regex and --lines-style are required.")
parser.add_argument(
"--regex",
metavar="PATTERN",
default=None,
help="Regex pattern for selecting file paths to reformat from the piped "
"diff. This flag is required if 'binary' is not set to 'yapf' or "
"'clang-format'. Otherwise, this flag overrides the default pattern that "
"--binary sets.")
parser.add_argument(
"--lines-style",
default=None,
help="How to style the 'lines' argument for the given binary. Can be set "
"to 'yapf' or 'clang-format'. This flag is required if 'binary' is not "
"set to 'yapf' or 'clang-format'.")
parser.add_argument(
"-p",
metavar="NUM",
default=1,
help="Strip the smallest prefix containing P slashes. Set to 0 if "
"passing `--no-prefix` to `git diff` or using `svn`")

# Parse and error-check arguments
args, binary_args = parser.parse_known_args()
if args.binary not in BINARY_TO_DEFAULT_REGEX:
if not args.regex:
raise parser.error("If 'binary' is not 'yapf' or 'clang-format' then "
"--regex must be set.")
if not args.lines_style:
raise parser.error("If 'binary' is not 'yapf' or 'clang-format' then "
"--lines-style must be set.")
else:
# Set defaults based off of 'binary'.
if not args.regex:
args.regex = BINARY_TO_DEFAULT_REGEX[args.binary]
if not args.lines_style:
args.lines_style = args.binary

if args.lines_style not in ["yapf", "clang-format"]:
raise parser.error(f"Unexpected value for --line-style {args.lines_style}")

return args, binary_args


def main():
args, binary_args = parse_arguments()

# Extract changed lines for each file.
filename = None
lines_by_file = {}
for line in sys.stdin:
# Match all filenames.
match = re.search(fr"^\+\+\+\ (.*?/){{{args.p}}}(\S*)", line)
if match:
filename = match.group(2)
if filename is None:
continue

# Match all filenames specified by --regex.
if not re.match(f"^{args.regex}$", filename):
continue

# Match unified diff line numbers.
match = re.search(r"^@@.*\+(\d+)(,(\d+))?", line)
if match:
start_line = int(match.group(1))
line_count = 1
if match.group(3):
line_count = int(match.group(3))
if line_count == 0:
continue
end_line = start_line + line_count - 1

if args.lines_style == "yapf":
lines = ["--lines", f"{start_line}-{end_line}"]
elif args.lines_style == "clang-format":
lines = ["-lines", f"{start_line}:{end_line}"]
lines_by_file.setdefault(filename, []).extend(lines)

# Pass the changed lines to 'binary' alongside any unparsed args (e.g. -i).
for filename, lines in lines_by_file.items():
command = [args.binary, filename]
command.extend(lines)
command.extend(binary_args)

print(f"Running `{' '.join(command)}`")
p = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=None,
stdin=subprocess.PIPE,
universal_newlines=True)
stdout, stderr = p.communicate()
if p.returncode != 0:
sys.exit(p.returncode)

# If the formatter printed the formatted code to stdout then print out
# a unified diff between the formatted and unformatted code.
# If flags like --verbose are passed to the binary then the diffs this
# produces won't be particularly helpful.
formatted_code = io.StringIO(stdout).readlines()
if len(formatted_code):
with open(filename) as f:
unformatted_code = f.readlines()
diff = difflib.unified_diff(unformatted_code,
formatted_code,
fromfile=filename,
tofile=filename,
fromfiledate="(before formatting)",
tofiledate="(after formatting)")
diff_string = "".join(diff)
if len(diff_string) > 0:
sys.stdout.write(diff_string)


if __name__ == "__main__":
main()

0 comments on commit b9b3e3c

Please sign in to comment.