1#!/usr/bin/env python3
2#
3# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
4#
5# SPDX-License-Identifier: BSD-2-Clause
6#
7"""
8run style tools over files
9"""
10
11import argparse
12import fnmatch
13import logging
14import os
15import re
16import subprocess
17import sys
18
19from filter import parse_filters
20
21# dict of style-tools to regex. The regex should match any full file path.
22# if the regex matches, that tool will be applied to that file. Multiple
23# tools can be defined for the same regex.
24STYLE_MAP = {
25    'style-c.sh': r'((\.c)|(\.h))$',
26    'style-cmake.sh': r'((\.cmake)|(CMakeLists.txt))$',
27    'style-py.sh': r'\.py$'
28}
29
30
31def main():
32    parser = argparse.ArgumentParser("Run style updates on files.")
33    parser.add_argument('-f', '--filters', type=str,
34                        help='File with glob filters of files to filter')
35    parser.add_argument('files', nargs='*', type=str,
36                        help='List of files to run style updates on.')
37    args = parser.parse_args()
38
39    filters = parse_filters(args.filters)
40    regexmap = {k: re.compile(v) for k, v in STYLE_MAP.items()}
41    filemap = {k: [] for k, v in STYLE_MAP.items()}
42
43    args.files = filter(os.path.isfile, args.files)
44    # construct a list of files to pass to each tool
45    for fname in args.files:
46        def matches(pattern, fname=fname):
47            """filter any files that match the filter filters"""
48            return fnmatch.fnmatch(fname, pattern)
49        if not any(map(matches, filters)):
50            for k, regex in regexmap.items():
51                if regex.search(fname):
52                    filemap.get(k, list()).append(fname)
53
54    # now spawn processes to style all the files
55    # this is currently done sequentially to collect output.
56    # if this becomes a bottle neck we can consider spawning more processes
57    return_code = 0
58    for k, files in filemap.items():
59        if files:
60            script = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), k)
61            completed = subprocess.run([script] + files, stderr=subprocess.PIPE)
62            if completed.returncode:
63                logging.fatal("%s failed with error code %d\n%s", files,
64                              completed.returncode, completed.stderr)
65                return_code = completed.returncode
66    return return_code
67
68
69if __name__ == '__main__':
70    sys.exit(main())
71