blob: 2c7a77966fd9792b4b7dac23b733c11e8f12b78f [file] [log] [blame]
David Horstmann20d6bfa2022-11-01 15:46:16 +00001#!/usr/bin/env python3
2"""Check or fix the code style by running Uncrustify.
David Horstmannc5438702022-12-08 14:56:18 +00003
4Note: The code style enforced by this script is not yet introduced to
5Mbed TLS. At present this script will only be used to prepare for a future
6change of code style.
David Horstmann20d6bfa2022-11-01 15:46:16 +00007"""
8# Copyright The Mbed TLS Contributors
9# SPDX-License-Identifier: Apache-2.0
10#
11# Licensed under the Apache License, Version 2.0 (the "License"); you may
12# not use this file except in compliance with the License.
13# You may obtain a copy of the License at
14#
15# http://www.apache.org/licenses/LICENSE-2.0
16#
17# Unless required by applicable law or agreed to in writing, software
18# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
19# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20# See the License for the specific language governing permissions and
21# limitations under the License.
22import argparse
23import io
24import os
Gilles Peskine4ca54d42022-12-19 00:48:58 +010025import re
David Horstmann20d6bfa2022-11-01 15:46:16 +000026import subprocess
27import sys
Gilles Peskine4ca54d42022-12-19 00:48:58 +010028from typing import FrozenSet, List
David Horstmann20d6bfa2022-11-01 15:46:16 +000029
David Horstmann3a6f9f92022-12-08 14:44:36 +000030UNCRUSTIFY_SUPPORTED_VERSION = "0.75.1"
David Horstmannc747fdf2022-12-08 17:03:01 +000031CONFIG_FILE = ".uncrustify.cfg"
David Horstmann20d6bfa2022-11-01 15:46:16 +000032UNCRUSTIFY_EXE = "uncrustify"
33UNCRUSTIFY_ARGS = ["-c", CONFIG_FILE]
34STDOUT_UTF8 = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
35STDERR_UTF8 = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
Gilles Peskine4ca54d42022-12-19 00:48:58 +010036CHECK_GENERATED_FILES = "tests/scripts/check-generated-files.sh"
David Horstmann20d6bfa2022-11-01 15:46:16 +000037
David Horstmann448cfec2022-12-08 14:33:52 +000038def print_err(*args):
39 print("Error: ", *args, file=STDERR_UTF8)
40
Gilles Peskine4ca54d42022-12-19 00:48:58 +010041# Match FILENAME(s) in "check SCRIPT (FILENAME...)"
42CHECK_CALL_RE = re.compile(r"\n\s*check\s+[^\s#$&*?;|]+([^\n#$&*?;|]+)",
43 re.ASCII)
44def list_generated_files() -> FrozenSet[str]:
45 """Return the names of generated files.
46
47 We don't reformat generated files, since the result might be different
48 from the output of the generator. Ideally the result of the generator
49 would conform to the code style, but this would be difficult, especially
50 with respect to the placement of line breaks in long logical lines.
51 """
52 # Parse check-generated-files.sh to get an up-to-date list of
53 # generated files. Read the file rather than calling it so that
54 # this script only depends on Git, Python and uncrustify, and not other
55 # tools such as sh or grep which might not be available on Windows.
56 # This introduces a limitation: check-generated-files.sh must have
57 # the expected format and must list the files explicitly, not through
58 # wildcards or command substitution.
59 content = open(CHECK_GENERATED_FILES, encoding="utf-8").read()
60 checks = re.findall(CHECK_CALL_RE, content)
61 return frozenset(word for s in checks for word in s.split())
62
David Horstmann20d6bfa2022-11-01 15:46:16 +000063def get_src_files() -> List[str]:
64 """
65 Use git ls-files to get a list of the source files
66 """
David Horstmann27b37042022-12-08 13:12:21 +000067 git_ls_files_cmd = ["git", "ls-files",
David Horstmanneead72e2022-12-08 17:38:27 +000068 "*.[hc]",
69 "tests/suites/*.function",
70 "scripts/data_files/*.fmt"]
David Horstmann20d6bfa2022-11-01 15:46:16 +000071
72 result = subprocess.run(git_ls_files_cmd, stdout=subprocess.PIPE, \
73 stderr=STDERR_UTF8, check=False)
74
75 if result.returncode != 0:
David Horstmann1f8b4d92022-12-08 15:04:20 +000076 print_err("git ls-files returned: " + str(result.returncode))
David Horstmann20d6bfa2022-11-01 15:46:16 +000077 return []
78 else:
Gilles Peskine4ca54d42022-12-19 00:48:58 +010079 generated_files = list_generated_files()
David Horstmann20d6bfa2022-11-01 15:46:16 +000080 src_files = str(result.stdout, "utf-8").split()
Gilles Peskine4ca54d42022-12-19 00:48:58 +010081 # Don't correct style for third-party files (and, for simplicity,
82 # companion files in the same subtree), or for automatically
83 # generated files (we're correcting the templates instead).
84 src_files = [filename for filename in src_files
85 if not (filename.startswith("3rdparty/") or
86 filename in generated_files)]
David Horstmann20d6bfa2022-11-01 15:46:16 +000087 return src_files
88
89def get_uncrustify_version() -> str:
90 """
91 Get the version string from Uncrustify
92 """
David Horstmann27b37042022-12-08 13:12:21 +000093 result = subprocess.run([UNCRUSTIFY_EXE, "--version"], \
David Horstmann20d6bfa2022-11-01 15:46:16 +000094 stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
95 if result.returncode != 0:
David Horstmann448cfec2022-12-08 14:33:52 +000096 print_err("Could not get Uncrustify version:", str(result.stderr, "utf-8"))
David Horstmann20d6bfa2022-11-01 15:46:16 +000097 return ""
98 else:
99 return str(result.stdout, "utf-8")
100
101def check_style_is_correct(src_file_list: List[str]) -> bool:
102 """
David Horstmann99a669a2022-12-08 14:36:10 +0000103 Check the code style and output a diff for each file whose style is
David Horstmann20d6bfa2022-11-01 15:46:16 +0000104 incorrect.
105 """
106 style_correct = True
107 for src_file in src_file_list:
108 uncrustify_cmd = [UNCRUSTIFY_EXE] + UNCRUSTIFY_ARGS + [src_file]
David Horstmannb92d30f2023-01-04 18:33:25 +0000109 result = subprocess.run(uncrustify_cmd, stdout=subprocess.PIPE, \
David Horstmann20d6bfa2022-11-01 15:46:16 +0000110 stderr=subprocess.PIPE, check=False)
David Horstmannb92d30f2023-01-04 18:33:25 +0000111 if result.returncode != 0:
112 print_err("Uncrustify returned " + str(result.returncode) + \
113 " correcting file " + src_file)
114 return False
David Horstmann20d6bfa2022-11-01 15:46:16 +0000115
116 # Uncrustify makes changes to the code and places the result in a new
117 # file with the extension ".uncrustify". To get the changes (if any)
118 # simply diff the 2 files.
David Horstmann1f8b4d92022-12-08 15:04:20 +0000119 diff_cmd = ["diff", "-u", src_file, src_file + ".uncrustify"]
David Horstmann20d6bfa2022-11-01 15:46:16 +0000120 result = subprocess.run(diff_cmd, stdout=subprocess.PIPE, \
121 stderr=STDERR_UTF8, check=False)
122 if len(result.stdout) > 0:
David Horstmann1f8b4d92022-12-08 15:04:20 +0000123 print(src_file + " - Incorrect code style.", file=STDOUT_UTF8)
David Horstmann20d6bfa2022-11-01 15:46:16 +0000124 print("File changed - diff:", file=STDOUT_UTF8)
125 print(str(result.stdout, "utf-8"), file=STDOUT_UTF8)
126 style_correct = False
127 else:
David Horstmann1f8b4d92022-12-08 15:04:20 +0000128 print(src_file + " - OK.", file=STDOUT_UTF8)
David Horstmann20d6bfa2022-11-01 15:46:16 +0000129
130 # Tidy up artifact
David Horstmann1f8b4d92022-12-08 15:04:20 +0000131 os.remove(src_file + ".uncrustify")
David Horstmann20d6bfa2022-11-01 15:46:16 +0000132
133 return style_correct
134
David Horstmannfa69def2023-01-05 09:59:35 +0000135def fix_style_single_pass(src_file_list: List[str]) -> bool:
David Horstmann20d6bfa2022-11-01 15:46:16 +0000136 """
137 Run Uncrustify once over the source files.
138 """
139 code_change_args = UNCRUSTIFY_ARGS + ["--no-backup"]
140 for src_file in src_file_list:
141 uncrustify_cmd = [UNCRUSTIFY_EXE] + code_change_args + [src_file]
David Horstmannb92d30f2023-01-04 18:33:25 +0000142 result = subprocess.run(uncrustify_cmd, check=False, \
143 stdout=STDOUT_UTF8, stderr=STDERR_UTF8)
144 if result.returncode != 0:
145 print_err("Uncrustify with file returned: " + \
146 str(result.returncode) + " correcting file " + \
147 src_file)
148 return False
David Horstmannfa69def2023-01-05 09:59:35 +0000149 return True
David Horstmann20d6bfa2022-11-01 15:46:16 +0000150
151def fix_style(src_file_list: List[str]) -> int:
152 """
153 Fix the code style. This takes 2 passes of Uncrustify.
154 """
David Horstmannb92d30f2023-01-04 18:33:25 +0000155 if fix_style_single_pass(src_file_list) != True:
156 return 1
157 if fix_style_single_pass(src_file_list) != True:
158 return 1
David Horstmann20d6bfa2022-11-01 15:46:16 +0000159
160 # Guard against future changes that cause the codebase to require
161 # more passes.
162 if not check_style_is_correct(src_file_list):
David Horstmann448cfec2022-12-08 14:33:52 +0000163 print("Code style still incorrect after second run of Uncrustify.")
David Horstmann20d6bfa2022-11-01 15:46:16 +0000164 return 1
165 else:
166 return 0
167
168def main() -> int:
169 """
170 Main with command line arguments.
171 """
David Horstmann3a6f9f92022-12-08 14:44:36 +0000172 uncrustify_version = get_uncrustify_version().strip()
173 if UNCRUSTIFY_SUPPORTED_VERSION not in uncrustify_version:
David Horstmann20d6bfa2022-11-01 15:46:16 +0000174 print("Warning: Using unsupported Uncrustify version '" \
David Horstmann3a6f9f92022-12-08 14:44:36 +0000175 + uncrustify_version + "' (Note: The only supported version" \
176 "is " + UNCRUSTIFY_SUPPORTED_VERSION + ")", file=STDOUT_UTF8)
David Horstmann20d6bfa2022-11-01 15:46:16 +0000177
178 src_files = get_src_files()
179
180 parser = argparse.ArgumentParser()
181 parser.add_argument('-f', '--fix', action='store_true', \
182 help='modify source files to fix the code style')
183
184 args = parser.parse_args()
185
186 if args.fix:
187 # Fix mode
188 return fix_style(src_files)
189 else:
190 # Check mode
191 if check_style_is_correct(src_files):
192 return 0
193 else:
194 return 1
195
196if __name__ == '__main__':
197 sys.exit(main())