mirror of
https://github.com/lifting-bits/mcsema
synced 2026-06-08 15:31:09 +00:00
194 lines
5.3 KiB
Python
Executable File
194 lines
5.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright 2020, Trail of Bits, Inc. All rights reserved.
|
|
|
|
|
|
import argparse
|
|
import itertools
|
|
import os
|
|
import pathlib
|
|
import subprocess
|
|
import sys
|
|
import traceback
|
|
import shutil
|
|
|
|
|
|
REFLOW_COMMENT = "// !!! REFLOW-COMMENT"
|
|
|
|
|
|
def find_comment_lines(path):
|
|
new_lines = []
|
|
old_comments = []
|
|
needs_fixes = False
|
|
with open(path, "r") as lines:
|
|
num_leading_comment_lines = 0
|
|
for line_ in lines:
|
|
line = line_.rstrip("\n\r\t ")
|
|
unindented_line = line.lstrip("\t ")
|
|
|
|
new_lines.append(line)
|
|
|
|
if unindented_line.startswith("//"):
|
|
|
|
# Make sure that every line that begins a comment block
|
|
# is preceded by a new line or a closing brace.
|
|
if not num_leading_comment_lines and 1 < len(new_lines):
|
|
prev_line = new_lines[-2].strip()
|
|
if prev_line != "" and prev_line != "}":
|
|
new_lines[-1] = ""
|
|
new_lines.append(line)
|
|
needs_fixes = True
|
|
|
|
num_leading_comment_lines += 1
|
|
|
|
# The code style has it so that the comment on an `else` or `else if` statement
|
|
# appears on the line before the `else` or `else if`.
|
|
elif num_leading_comment_lines:
|
|
if unindented_line.startswith("} else"):
|
|
comment_lines = list(new_lines[-(num_leading_comment_lines + 1):-1])
|
|
del new_lines[-(num_leading_comment_lines+1):]
|
|
new_lines.append(REFLOW_COMMENT)
|
|
new_lines.append(line)
|
|
old_comments.append(comment_lines)
|
|
needs_fixes = True
|
|
|
|
num_leading_comment_lines = 0
|
|
else:
|
|
num_leading_comment_lines = 0
|
|
|
|
# Force an empty newline at the end of the file.
|
|
if len(new_lines) and new_lines[-1]:
|
|
new_lines.append("")
|
|
needs_fixes = True
|
|
|
|
return new_lines, old_comments, needs_fixes
|
|
|
|
|
|
def run_clang_format_on_files(format_exe, format_file, paths):
|
|
args = [format_exe, '-i']
|
|
args.extend(paths)
|
|
try:
|
|
subprocess.check_call(args)
|
|
except:
|
|
return
|
|
|
|
|
|
def run_clang_format_on_lines(format_exe, format_file, path, lines):
|
|
p = subprocess.Popen(
|
|
[format_exe,
|
|
'--assume-filename={}'.format(path),
|
|
'--style=file'],
|
|
stdin=subprocess.PIPE,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines=True)
|
|
try:
|
|
stdout_data, stderr_data = p.communicate("\n".join(lines))
|
|
return stdout_data.split("\n"), False
|
|
except:
|
|
p.kill()
|
|
stdout_data, stderr_data = p.communicate()
|
|
return stdout_data.split("\n"), True
|
|
|
|
|
|
def fixup_file(path, lines, old_comments):
|
|
new_lines = []
|
|
i = 0
|
|
for line in lines:
|
|
if line.strip() == REFLOW_COMMENT:
|
|
num_spaces = max(0, (len(line) - len(line.lstrip())) - 2)
|
|
new_indent = " " * num_spaces
|
|
for old_comment_line in old_comments[i]:
|
|
old_comment_line = old_comment_line.strip("\r\n\t ")
|
|
new_lines.append("{}{}".format(new_indent, old_comment_line))
|
|
i += 1
|
|
else:
|
|
new_lines.append(line)
|
|
|
|
with open(path, "w") as file:
|
|
file.write("\n".join(new_lines))
|
|
|
|
|
|
def main():
|
|
clang_format_exe = shutil.which('clang-format')
|
|
|
|
arg_parser = argparse.ArgumentParser()
|
|
|
|
if clang_format_exe:
|
|
arg_parser.add_argument(
|
|
'--format_exe',
|
|
help='Path to clang-format executable.',
|
|
required=False,
|
|
default=str(clang_format_exe))
|
|
else:
|
|
arg_parser.add_argument(
|
|
'--format_exe',
|
|
help='Path to clang-format executable.',
|
|
required=True)
|
|
|
|
arg_parser.add_argument(
|
|
'--source_dir',
|
|
help='Path to root of source directory.',
|
|
required=False,
|
|
default=str(os.path.dirname(os.path.dirname(__file__))))
|
|
|
|
arg_parser.add_argument(
|
|
'--files',
|
|
help='Specific files to format',
|
|
nargs='*')
|
|
|
|
args, _ = arg_parser.parse_known_args()
|
|
|
|
try:
|
|
source_dir = os.path.abspath(args.source_dir)
|
|
os.chdir(source_dir)
|
|
except:
|
|
print("'{}' passed to --source_dir is not a directory".format(args.source_dir))
|
|
return 1
|
|
|
|
format_file = os.path.abspath(os.path.join(source_dir, ".clang-format"))
|
|
if not os.path.isfile(format_file):
|
|
print("Could not find clang-format file at '{}'".format(format_file))
|
|
return 1
|
|
|
|
if not os.path.isfile(args.format_exe):
|
|
print("'{}' passed to --format_exe does not exist".format(args.format_exe))
|
|
return 1
|
|
|
|
# NOTE(pag): Go get the abs paths of the files here before we change the current
|
|
# working directory.
|
|
cxx_files = args.files
|
|
if cxx_files:
|
|
cxx_files = list(os.path.abspath(cxx_file) for cxx_file in cxx_files)
|
|
|
|
if not cxx_files:
|
|
cxx_header_files = pathlib.Path(source_dir).rglob("*.h")
|
|
cxx_source_files = pathlib.Path(source_dir).rglob("*.cpp")
|
|
cxx_files = itertools.chain(cxx_header_files, cxx_source_files)
|
|
|
|
in_place_files = []
|
|
for cxx_file_ in cxx_files:
|
|
cxx_file = os.path.abspath(cxx_file_)
|
|
if not (cxx_file.endswith(".h") or cxx_file.endswith(".cpp")):
|
|
continue
|
|
|
|
try:
|
|
new_lines, old_comments, needs_fixes = find_comment_lines(cxx_file)
|
|
except:
|
|
continue
|
|
|
|
if not needs_fixes:
|
|
in_place_files.append(cxx_file)
|
|
else:
|
|
new_lines, has_error = run_clang_format_on_lines(
|
|
args.format_exe, format_file, cxx_file, new_lines)
|
|
if not has_error:
|
|
fixup_file(cxx_file, new_lines, old_comments)
|
|
|
|
if in_place_files:
|
|
run_clang_format_on_files(args.format_exe, format_file, in_place_files)
|
|
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
exit(main())
|