xref: /aosp_15_r20/external/vixl/tools/clang_format.py (revision f5c631da2f1efdd72b5fd1e20510e4042af13d77)
1*f5c631daSSadaf Ebrahimi#!/usr/bin/env python2.7
2*f5c631daSSadaf Ebrahimi
3*f5c631daSSadaf Ebrahimi# Copyright 2016, VIXL authors
4*f5c631daSSadaf Ebrahimi# All rights reserved.
5*f5c631daSSadaf Ebrahimi#
6*f5c631daSSadaf Ebrahimi# Redistribution and use in source and binary forms, with or without
7*f5c631daSSadaf Ebrahimi# modification, are permitted provided that the following conditions are met:
8*f5c631daSSadaf Ebrahimi#
9*f5c631daSSadaf Ebrahimi#   * Redistributions of source code must retain the above copyright notice,
10*f5c631daSSadaf Ebrahimi#     this list of conditions and the following disclaimer.
11*f5c631daSSadaf Ebrahimi#   * Redistributions in binary form must reproduce the above copyright notice,
12*f5c631daSSadaf Ebrahimi#     this list of conditions and the following disclaimer in the documentation
13*f5c631daSSadaf Ebrahimi#     and/or other materials provided with the distribution.
14*f5c631daSSadaf Ebrahimi#   * Neither the name of ARM Limited nor the names of its contributors may be
15*f5c631daSSadaf Ebrahimi#     used to endorse or promote products derived from this software without
16*f5c631daSSadaf Ebrahimi#     specific prior written permission.
17*f5c631daSSadaf Ebrahimi#
18*f5c631daSSadaf Ebrahimi# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
19*f5c631daSSadaf Ebrahimi# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20*f5c631daSSadaf Ebrahimi# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21*f5c631daSSadaf Ebrahimi# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
22*f5c631daSSadaf Ebrahimi# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*f5c631daSSadaf Ebrahimi# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24*f5c631daSSadaf Ebrahimi# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25*f5c631daSSadaf Ebrahimi# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26*f5c631daSSadaf Ebrahimi# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*f5c631daSSadaf Ebrahimi# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*f5c631daSSadaf Ebrahimi
29*f5c631daSSadaf Ebrahimiimport argparse
30*f5c631daSSadaf Ebrahimiimport multiprocessing
31*f5c631daSSadaf Ebrahimiimport os
32*f5c631daSSadaf Ebrahimiimport re
33*f5c631daSSadaf Ebrahimiimport subprocess
34*f5c631daSSadaf Ebrahimiimport sys
35*f5c631daSSadaf Ebrahimiimport tempfile
36*f5c631daSSadaf Ebrahimi
37*f5c631daSSadaf Ebrahimifrom threaded_tests import Test, TestQueue
38*f5c631daSSadaf Ebrahimiimport printer
39*f5c631daSSadaf Ebrahimiimport util
40*f5c631daSSadaf Ebrahimi
41*f5c631daSSadaf EbrahimiCLANG_FORMAT_VERSION_MAJOR = 4
42*f5c631daSSadaf EbrahimiCLANG_FORMAT_VERSION_MINOR = 0
43*f5c631daSSadaf Ebrahimi
44*f5c631daSSadaf EbrahimiDEFAULT_CLANG_FORMAT = \
45*f5c631daSSadaf Ebrahimi    'clang-format-{}.{}'.format(CLANG_FORMAT_VERSION_MAJOR,
46*f5c631daSSadaf Ebrahimi                                CLANG_FORMAT_VERSION_MINOR)
47*f5c631daSSadaf Ebrahimi
48*f5c631daSSadaf Ebrahimiis_output_redirected = not sys.stdout.isatty()
49*f5c631daSSadaf Ebrahimi
50*f5c631daSSadaf Ebrahimidef BuildOptions():
51*f5c631daSSadaf Ebrahimi  parser = argparse.ArgumentParser(
52*f5c631daSSadaf Ebrahimi    description = '''This tool runs `clang-format` on C++ files.
53*f5c631daSSadaf Ebrahimi    If no files are provided on the command-line, all C++ source files are
54*f5c631daSSadaf Ebrahimi    processed, except for the test traces.
55*f5c631daSSadaf Ebrahimi    When available, `colordiff` is automatically used to colour the output.''',
56*f5c631daSSadaf Ebrahimi    # Print default values.
57*f5c631daSSadaf Ebrahimi    formatter_class = argparse.ArgumentDefaultsHelpFormatter)
58*f5c631daSSadaf Ebrahimi  parser.add_argument('files', nargs = '*')
59*f5c631daSSadaf Ebrahimi  parser.add_argument('--clang-format', default=DEFAULT_CLANG_FORMAT,
60*f5c631daSSadaf Ebrahimi                      help='Path to clang-format.')
61*f5c631daSSadaf Ebrahimi  parser.add_argument('--in-place', '-i',
62*f5c631daSSadaf Ebrahimi                      action = 'store_true', default = False,
63*f5c631daSSadaf Ebrahimi                      help = 'Edit files in place.')
64*f5c631daSSadaf Ebrahimi  parser.add_argument('--jobs', '-j', metavar = 'N', type = int, nargs = '?',
65*f5c631daSSadaf Ebrahimi                      default = multiprocessing.cpu_count(),
66*f5c631daSSadaf Ebrahimi                      const = multiprocessing.cpu_count(),
67*f5c631daSSadaf Ebrahimi                      help = '''Runs the tests using N jobs. If the option is set
68*f5c631daSSadaf Ebrahimi                      but no value is provided, the script will use as many jobs
69*f5c631daSSadaf Ebrahimi                      as it thinks useful.''')
70*f5c631daSSadaf Ebrahimi  return parser.parse_args()
71*f5c631daSSadaf Ebrahimi
72*f5c631daSSadaf Ebrahimi
73*f5c631daSSadaf Ebrahimidef ClangFormatIsAvailable(clang_format):
74*f5c631daSSadaf Ebrahimi  if not util.IsCommandAvailable(clang_format):
75*f5c631daSSadaf Ebrahimi    return False
76*f5c631daSSadaf Ebrahimi  cmd = '%s -version' % clang_format
77*f5c631daSSadaf Ebrahimi  rc, version = util.getstatusoutput(cmd)
78*f5c631daSSadaf Ebrahimi  if rc != 0:
79*f5c631daSSadaf Ebrahimi      util.abort("Failed to execute %s: %s" % (cmd, version))
80*f5c631daSSadaf Ebrahimi  m = re.search("^clang-format version (\d)\.(\d)\.\d.*$",
81*f5c631daSSadaf Ebrahimi                version.decode(), re.M)
82*f5c631daSSadaf Ebrahimi  if not m:
83*f5c631daSSadaf Ebrahimi      util.abort("Failed to get clang-format's version: %s" % version)
84*f5c631daSSadaf Ebrahimi  major, minor = m.groups()
85*f5c631daSSadaf Ebrahimi  return int(major) == CLANG_FORMAT_VERSION_MAJOR and \
86*f5c631daSSadaf Ebrahimi      int(minor) == CLANG_FORMAT_VERSION_MINOR
87*f5c631daSSadaf Ebrahimi
88*f5c631daSSadaf Ebrahimidef RunTest(test):
89*f5c631daSSadaf Ebrahimi  filename = test.args['filename']
90*f5c631daSSadaf Ebrahimi  clang_format = test.args['clang_format']
91*f5c631daSSadaf Ebrahimi  in_place = test.args['in_place']
92*f5c631daSSadaf Ebrahimi
93*f5c631daSSadaf Ebrahimi  rc = 0
94*f5c631daSSadaf Ebrahimi
95*f5c631daSSadaf Ebrahimi  cmd_format = [clang_format, filename]
96*f5c631daSSadaf Ebrahimi  temp_file, temp_file_name = tempfile.mkstemp(prefix = 'clang_format_')
97*f5c631daSSadaf Ebrahimi  cmd_format_string = '$ ' + ' '.join(cmd_format) + ' > %s' % temp_file_name
98*f5c631daSSadaf Ebrahimi  p_format = subprocess.Popen(cmd_format,
99*f5c631daSSadaf Ebrahimi                              stdout = temp_file, stderr = subprocess.STDOUT)
100*f5c631daSSadaf Ebrahimi
101*f5c631daSSadaf Ebrahimi  rc += p_format.wait()
102*f5c631daSSadaf Ebrahimi
103*f5c631daSSadaf Ebrahimi  cmd_diff = ['diff', '--unified', filename, temp_file_name]
104*f5c631daSSadaf Ebrahimi  cmd_diff_string = '$ ' + ' '.join(cmd_diff)
105*f5c631daSSadaf Ebrahimi  p_diff = subprocess.Popen(cmd_diff,
106*f5c631daSSadaf Ebrahimi                            stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
107*f5c631daSSadaf Ebrahimi
108*f5c631daSSadaf Ebrahimi  if util.IsCommandAvailable('colordiff') and not is_output_redirected:
109*f5c631daSSadaf Ebrahimi    p_colordiff = subprocess.Popen(
110*f5c631daSSadaf Ebrahimi            ['colordiff', '--unified'],
111*f5c631daSSadaf Ebrahimi            stdin = p_diff.stdout,
112*f5c631daSSadaf Ebrahimi            stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
113*f5c631daSSadaf Ebrahimi    out, unused = p_colordiff.communicate()
114*f5c631daSSadaf Ebrahimi  else:
115*f5c631daSSadaf Ebrahimi    out, unused = p_diff.communicate()
116*f5c631daSSadaf Ebrahimi
117*f5c631daSSadaf Ebrahimi  rc += p_diff.wait()
118*f5c631daSSadaf Ebrahimi
119*f5c631daSSadaf Ebrahimi  if in_place:
120*f5c631daSSadaf Ebrahimi      cmd_format = [clang_format, '-i', filename]
121*f5c631daSSadaf Ebrahimi      p_format = subprocess.Popen(cmd_format,
122*f5c631daSSadaf Ebrahimi                                  stdout=temp_file, stderr=subprocess.STDOUT)
123*f5c631daSSadaf Ebrahimi
124*f5c631daSSadaf Ebrahimi  if rc != 0:
125*f5c631daSSadaf Ebrahimi    with Test.n_tests_failed.get_lock(): Test.n_tests_failed.value += 1
126*f5c631daSSadaf Ebrahimi  else:
127*f5c631daSSadaf Ebrahimi    with Test.n_tests_passed.get_lock(): Test.n_tests_passed.value += 1
128*f5c631daSSadaf Ebrahimi
129*f5c631daSSadaf Ebrahimi  printer.__print_lock__.acquire()
130*f5c631daSSadaf Ebrahimi
131*f5c631daSSadaf Ebrahimi  printer.UpdateProgress(test.shared.start_time,
132*f5c631daSSadaf Ebrahimi                         Test.n_tests_passed.value,
133*f5c631daSSadaf Ebrahimi                         Test.n_tests_failed.value,
134*f5c631daSSadaf Ebrahimi                         test.shared.n_tests,
135*f5c631daSSadaf Ebrahimi                         Test.n_tests_skipped.value,
136*f5c631daSSadaf Ebrahimi                         test.shared.n_known_failures,
137*f5c631daSSadaf Ebrahimi                         test.name,
138*f5c631daSSadaf Ebrahimi                         prevent_next_overwrite = rc != 0,
139*f5c631daSSadaf Ebrahimi                         has_lock = True,
140*f5c631daSSadaf Ebrahimi                         prefix = test.shared.progress_prefix)
141*f5c631daSSadaf Ebrahimi
142*f5c631daSSadaf Ebrahimi  if rc != 0:
143*f5c631daSSadaf Ebrahimi    printer.Print('Incorrectly formatted file: ' + filename + '\n' + \
144*f5c631daSSadaf Ebrahimi                  cmd_format_string + '\n' + \
145*f5c631daSSadaf Ebrahimi                  cmd_diff_string + '\n' + \
146*f5c631daSSadaf Ebrahimi                  out, has_lock = True)
147*f5c631daSSadaf Ebrahimi  printer.__print_lock__.release()
148*f5c631daSSadaf Ebrahimi
149*f5c631daSSadaf Ebrahimi  os.remove(temp_file_name)
150*f5c631daSSadaf Ebrahimi
151*f5c631daSSadaf Ebrahimi# Returns the total number of files incorrectly formatted.
152*f5c631daSSadaf Ebrahimidef ClangFormatFiles(files, clang_format, in_place = False, jobs = 1,
153*f5c631daSSadaf Ebrahimi                     progress_prefix = ''):
154*f5c631daSSadaf Ebrahimi  if not ClangFormatIsAvailable(clang_format):
155*f5c631daSSadaf Ebrahimi    error_message = "`{}` version {}.{} not found. Please ensure it " \
156*f5c631daSSadaf Ebrahimi                    "is installed, in your PATH and the correct version." \
157*f5c631daSSadaf Ebrahimi                    .format(clang_format,
158*f5c631daSSadaf Ebrahimi                            CLANG_FORMAT_VERSION_MAJOR,
159*f5c631daSSadaf Ebrahimi                            CLANG_FORMAT_VERSION_MINOR)
160*f5c631daSSadaf Ebrahimi    print(printer.COLOUR_RED + error_message + printer.NO_COLOUR)
161*f5c631daSSadaf Ebrahimi    return -1
162*f5c631daSSadaf Ebrahimi
163*f5c631daSSadaf Ebrahimi  queue = TestQueue(prefix = progress_prefix)
164*f5c631daSSadaf Ebrahimi  for f in files:
165*f5c631daSSadaf Ebrahimi    queue.AddTest(f, filename = f, clang_format = clang_format, in_place = in_place)
166*f5c631daSSadaf Ebrahimi
167*f5c631daSSadaf Ebrahimi  rc = queue.Run(jobs, True, RunTest)
168*f5c631daSSadaf Ebrahimi
169*f5c631daSSadaf Ebrahimi  printer.PrintOverwritableLine(
170*f5c631daSSadaf Ebrahimi      progress_prefix + '%d files are incorrectly formatted.' % rc,
171*f5c631daSSadaf Ebrahimi      type = printer.LINE_TYPE_LINTER)
172*f5c631daSSadaf Ebrahimi  printer.EnsureNewLine()
173*f5c631daSSadaf Ebrahimi
174*f5c631daSSadaf Ebrahimi  return rc
175*f5c631daSSadaf Ebrahimi
176*f5c631daSSadaf Ebrahimiif __name__ == '__main__':
177*f5c631daSSadaf Ebrahimi  # Parse the arguments.
178*f5c631daSSadaf Ebrahimi  args = BuildOptions()
179*f5c631daSSadaf Ebrahimi  files = args.files or util.get_source_files()
180*f5c631daSSadaf Ebrahimi
181*f5c631daSSadaf Ebrahimi  rc = ClangFormatFiles(files, clang_format = args.clang_format,
182*f5c631daSSadaf Ebrahimi                        in_place = args.in_place, jobs = args.jobs)
183*f5c631daSSadaf Ebrahimi
184*f5c631daSSadaf Ebrahimi  sys.exit(rc)
185