Skip to content

Commit 3ea933e

Browse files
committed
Initial commit
1 parent bf2897e commit 3ea933e

File tree

5 files changed

+219
-0
lines changed

5 files changed

+219
-0
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# fw-precommit-hooks
2+
Contains custom hooks for the [pre-commit](https://pre-commit.com/) python package for checking source code.
3+
4+
## Support
5+
This works with two different tools to format source code with the help of pre-commit:
6+
* [astyle](https://astyle.sourceforge.net/) - an old package that does a pretty decent job, but no longer maintained
7+
* [clang-format](https://clang.llvm.org/docs/ClangFormat.html) - a newer tools that is well supported with lots of formatting options
8+
9+
## Usage
10+
In order to use this, you need to add a section to your .pre-commit-config.yaml file to get this hook that specifies which of the formatters you want to use and give it a path to the options file(s). You can use either or both `astyle` or `clang-format`
11+
12+
Here is an example of clang format usage:
13+
```
14+
- repo: https://github.com/delsauce/pre-commit-hooks
15+
rev: v1.0.0
16+
hooks:
17+
- id: format-c-source
18+
types: [file, c]
19+
args: [--clangformat, 'path-to-clang-format-options-file']
20+
```
21+
22+
If you'd rather use `astyle` change the args:
23+
```
24+
args: [--astlye, 'path-to-astyle-options-file']
25+
```

hooks/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""
2+
Version info for this package
3+
"""
4+
__version__ = "1.0.0"

hooks/format_c_source.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""
2+
This module runs various style tools to format source code files. It is meant to be used
3+
in conjunction with the python module precommit (https://pre-commit.com/) to help enforce
4+
guidelines before code is committed to a git repository.
5+
"""
6+
import os
7+
import subprocess
8+
import argparse
9+
import shutil
10+
import sys
11+
from typing import (
12+
Optional,
13+
Sequence
14+
)
15+
16+
def main(argv: Optional[Sequence[str]] = None) -> int:
17+
"""
18+
Format specified text source files
19+
20+
:param argv: list of filenames to process
21+
:return program exit code, 0 = success, > 0 fail (1 for generic error)
22+
"""
23+
24+
parser = argparse.ArgumentParser()
25+
parser.add_argument(
26+
'filenames', nargs='*',
27+
help='Filenames pre-commit believes are changed.',
28+
)
29+
30+
# Eventually we can add other tool options here...
31+
parser.add_argument(
32+
'-a', '--astyle', type=str,
33+
help='path to astyle config file',
34+
)
35+
parser.add_argument(
36+
'-c', '--clangformat', type=str,
37+
help='path to clang-format options file',
38+
)
39+
40+
args = parser.parse_args(argv)
41+
42+
if not (args.astyle or args.clangformat):
43+
parser.error('No action requested, add --astyle or --clangformat')
44+
45+
# Bail early if there are no files to process
46+
if len(args.filenames) == 0:
47+
print('No files')
48+
return 0
49+
50+
# Note: sourcetree doesn't use the same system path when running commit hooks (there
51+
# is a bug in their tool). We can try to fix that here (not really sure what
52+
# happens on windows -- so not handling that now)
53+
if sys.platform != 'win32':
54+
if '/usr/local/bin' not in os.environ['PATH']:
55+
os.environ['PATH'] += os.pathsep + '/usr/local/bin'
56+
57+
if args.astyle:
58+
# check to make sure astyle is installed
59+
locate = shutil.which('astyle')
60+
61+
if locate is None:
62+
print('astyle executable not found on in PATH, is it installed?')
63+
print('Consult your favorite package manager for installation')
64+
print('(e.g. \'brew install astyle\' on mac or \'apt-get install astyle\' on Ubuntu)')
65+
return 1
66+
67+
# Check to make sure that options file is present
68+
if not os.path.exists(args.astyle):
69+
print(f'{args.astyle} not found. Please check that the file exists')
70+
return 1
71+
72+
# Run astyle
73+
for fname in args.filenames:
74+
# Since check=True below, this will throw an exception if the call
75+
# to astyle fails for some reason. Note, as long as the options and
76+
# filename passed to astyle are OK, it should never fail (always returns
77+
# a '0' whether it formatted a file or not. Need to check the output
78+
# to know if it actually changed anything in the file or not.
79+
astyle_result = subprocess.run(["astyle",
80+
"--options=" + args.astyle, fname],
81+
capture_output=True, text=True,
82+
check=True)
83+
84+
if 'Formatted' in astyle_result.stdout:
85+
print('Formatted: ' + fname)
86+
87+
88+
if args.clangformat:
89+
# check to make sure astyle is installed
90+
locate = shutil.which('clang-format')
91+
92+
if locate is None:
93+
print('clang-format executable not found on in PATH, is it installed?')
94+
print('Consult your favorite package manager for installation')
95+
print('(e.g. \'brew install clang-format\' on mac or \'apt-get install clang-format\' on Debian/Ubuntu)')
96+
return 1
97+
98+
# Check to make sure that options file is present
99+
if not os.path.exists(args.clangformat):
100+
print(f'{args.clangformat} not found. Please check that the file exists')
101+
return 1
102+
103+
# Run clang-format
104+
for fname in args.filenames:
105+
# Since check=True below, this will throw an exception if the call
106+
# to clang-format fails for some reason. Note, as long as the options and
107+
# filename passed to clang-format are OK, it should never fail.
108+
109+
# First do a dry run to see if the file needs formatting or not
110+
# (this is just to support better info being printed during execution)
111+
clang_result = subprocess.run(["clang-format",
112+
"--dry-run",
113+
"--Werror",
114+
"--style=file:" + args.clangformat,
115+
"-i", fname],
116+
capture_output=True, text=True)
117+
118+
if clang_result.returncode != 0:
119+
# Error code set means the file needs formatting
120+
clang_result = subprocess.run(["clang-format",
121+
"--style=file:" + args.clangformat,
122+
"-i", fname])
123+
124+
if clang_result.returncode == 0:
125+
print('Formatted: ' + fname)
126+
else:
127+
print('clang-format can not format file: ' + clang_result.stdout)
128+
return 1
129+
130+
return 0
131+
132+
133+
if __name__ == '__main__':
134+
sys.exit(main())

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# No requirements necessary for the module, add to this file if changes needed
2+

setup.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'''
2+
pip package configuration for embedded c coding standard compliance
3+
'''
4+
import codecs
5+
import os
6+
from setuptools import setup
7+
8+
# pylint: disable-next=consider-using-with
9+
reqs: list = open("requirements.txt", "r",encoding='utf-8').read().splitlines()
10+
11+
def read(rel_path):
12+
'''
13+
Supporting function to read the __init__.py file from the package
14+
to get attributes. See get_version() below for more details.
15+
'''
16+
here = os.path.abspath(os.path.dirname(__file__))
17+
# intentionally *not* adding an encoding option to open, See:
18+
# https://github.com/pypa/virtualenv/issues/201#issuecomment-3145690
19+
with codecs.open(os.path.join(here, rel_path), 'r') as file_pointer:
20+
return file_pointer.read()
21+
22+
def get_version(rel_path):
23+
'''
24+
Allows the version of the package to be fetched via the one
25+
stored in the __init__.py file. This enables a single location
26+
to store this information (rather than having to pass it in
27+
with the metadata in the call to setup())
28+
29+
See here:
30+
https://packaging.python.org/guides/single-sourcing-package-version/
31+
'''
32+
for line in read(rel_path).splitlines():
33+
if line.startswith('__version__'):
34+
delim = '"' if '"' in line else "'"
35+
return line.split(delim)[1]
36+
37+
raise RuntimeError("Unable to find version string.")
38+
39+
setup(name='pre_commit_hooks',
40+
description='C/C++ File formatting and cleanup per coding standards',
41+
url='https://github.com/delsauce/pre-commit-hooks',
42+
license='MIT',
43+
packages=['pre_commit_hooks'],
44+
zip_safe=False,
45+
version=get_version("hooks/__init__.py"),
46+
entry_points={
47+
'console_scripts': [
48+
'format-c-source=hooks.format_c_source:main',
49+
],
50+
},
51+
52+
python_requires='>=3.7',
53+
install_requires=reqs
54+
)

0 commit comments

Comments
 (0)