blob: 2249a179623d7a96e5984bc66cf6c158a146200e [file] [log] [blame]
Miguel Young de la Sota35260352022-04-21 11:41:25 -04001#!/usr/bin/env python3
2# Copyright lowRISC contributors.
3# Licensed under the Apache License, Version 2.0, see LICENSE for details.
4# SPDX-License-Identifier: Apache-2.0
5
6# fix_trailing_whitespace.py script ensures that all files passed into it satisfy
7# various requirements in terms of whitespace:
8# - There is no leading whitespace in the file.
9# - Lines do not have trailing non-newline whitespace and have UNIX-style line endings.
10# - The file ends in a single newline.
11
12import argparse
13import sys
Miguel Young de la Sota35260352022-04-21 11:41:25 -040014import subprocess
15
16from pathlib import Path
17
18# This file is $REPO_TOP/util/fix_trailing_newlines.py, so it takes two parent()
19# calls to get back to the top.
20REPO_TOP = Path(__file__).resolve().parent.parent
21
Michael Schaffner449a7c52022-05-10 16:09:30 -070022
Miguel Young de la Sota35260352022-04-21 11:41:25 -040023def is_ignored(path):
24 return subprocess.run(['git', 'check-ignore', path]).returncode == 0
25
Michael Schaffner449a7c52022-05-10 16:09:30 -070026
Miguel Young de la Sota35260352022-04-21 11:41:25 -040027def walk_tree(paths=[REPO_TOP]):
28 for path in paths:
29 if isinstance(path, str):
30 path = Path(path)
31
32 if path.is_symlink() or is_ignored(path) or 'LICENSE' in path.parts:
33 continue
34
35 if path.is_dir() and 'vendor' not in path.parts:
36 yield from walk_tree(path.iterdir())
37 else:
38 yield path
39
Michael Schaffner449a7c52022-05-10 16:09:30 -070040
Miguel Young de la Sota35260352022-04-21 11:41:25 -040041def main():
42 parser = argparse.ArgumentParser()
43 parser.add_argument(
44 '--dry-run',
45 action='store_true',
46 help='report writes which would have happened')
47 parser.add_argument(
48 '--recursive', '-r',
49 action='store_true',
50 default=False,
51 help='traverse the entire tree modolo .gitignore'
52 )
53 parser.add_argument(
54 '--verbose', '-v',
55 action='store_true',
56 help='verbose output')
57 parser.add_argument(
58 'files',
59 type=str,
60 nargs='*',
61 help='files to fix whitespace for')
62 args = parser.parse_args()
63
64 files = args.files
65 if args.recursive:
66 files = walk_tree(args.files)
67
68 total_fixable = 0
69 for path in files:
70 path = Path(path).resolve().relative_to(REPO_TOP)
71 if not path.is_file() or path.is_symlink():
72 continue
Timothy Chen3bc8a2b2022-05-10 11:37:56 -070073 if 'vendor' in path.parts or path.suffix in ['.patch', '.svg', '.tpl']:
Miguel Young de la Sota35260352022-04-21 11:41:25 -040074 continue
75 if args.verbose:
76 print(f'Checking: "{path}"')
77
78 try:
79 old_text = path.read_text()
80 except UnicodeDecodeError:
81 print(f'Binary file: "{path}"')
82 continue
83 new_text = "\n".join([line.rstrip() for line in old_text.strip().split("\n")]) + "\n"
84
85 if old_text != new_text:
86 print(f'Fixing file: "{path}"', file=sys.stdout)
87 total_fixable += 1
88 if not args.dry_run:
89 path.write_text(new_text)
90
91 if total_fixable:
92 verb = 'Would have fixed' if args.dry_run else 'Fixed'
93 print(f'{verb} {total_fixable} files.', file=sys.stderr)
94
95 # Pass if we fixed everything or there was nothing to fix.
96 return 1 if total_fixable > 0 else 0
97
Michael Schaffner449a7c52022-05-10 16:09:30 -070098
Miguel Young de la Sota35260352022-04-21 11:41:25 -040099if __name__ == '__main__':
100 sys.exit(main())