| Mahesh Saripalli | b7cebe9 | 2023-08-08 20:35:22 +0000 | [diff] [blame] | 1 | #!/usr/bin/env fuchsia-vendored-python |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 2 | # Copyright 2017 The Fuchsia Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | """Runs clang-tidy on modified files. |
| 6 | |
| 7 | The tool uses `git diff-index` against the newest parent commit in the upstream |
| Gabriel Kerneis | d5c391c | 2018-07-02 15:20:50 +0200 | [diff] [blame] | 8 | branch (or against HEAD if no such commit is found) in order to find the files |
| 9 | to be formatted. In result, the tool lints files that are locally modified, |
| 10 | staged or touched by any commits introduced on the local branch. |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 11 | """ |
| 12 | |
| 13 | import argparse |
| Przemyslaw Pietrzkiewicz | 49f5c9e | 2017-08-02 13:11:25 +0200 | [diff] [blame] | 14 | import multiprocessing |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 15 | import os |
| 16 | import platform |
| 17 | import re |
| 18 | import subprocess |
| 19 | import sys |
| 20 | |
| Ian McKellar | 91e0f9e | 2019-04-04 18:25:52 +0000 | [diff] [blame] | 21 | sys.path.append(os.path.dirname(os.path.dirname(__file__))) |
| 22 | |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 23 | import git_utils |
| Adam MacBeth | a225d5d | 2019-05-03 20:50:50 +0000 | [diff] [blame] | 24 | |
| 25 | FUCHSIA_ROOT = os.path.dirname( # $root |
| 26 | os.path.dirname( # scripts |
| 27 | os.path.dirname( # git |
| 28 | os.path.abspath(__file__)))) |
| Adam MacBeth | 7828929 | 2019-08-08 22:01:42 +0000 | [diff] [blame] | 29 | PREBUILT_ROOT = os.path.join(FUCHSIA_ROOT, "prebuilt/third_party") |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 30 | |
| Adam MacBeth | 7828929 | 2019-08-08 22:01:42 +0000 | [diff] [blame] | 31 | local_os = "linux" |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 32 | if platform.platform().startswith("Darwin"): |
| Adam MacBeth | 7828929 | 2019-08-08 22:01:42 +0000 | [diff] [blame] | 33 | local_os = "mac" |
| 34 | CLANG_TIDY_TOOL = os.path.join(PREBUILT_ROOT, "clang", |
| 35 | "%s-x64" % local_os, "bin", |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 36 | "clang-tidy") |
| Adam MacBeth | 7828929 | 2019-08-08 22:01:42 +0000 | [diff] [blame] | 37 | NINJA_TOOL = os.path.join(PREBUILT_ROOT, "ninja", |
| 38 | "%s-x64" % local_os, "ninja") |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 39 | |
| Przemyslaw Pietrzkiewicz | 74a450e | 2017-08-08 11:36:10 +0200 | [diff] [blame] | 40 | def find_ancestor_with(filepath, relpath): |
| 41 | """Returns the lowest ancestor of |filepath| that contains |relpath|.""" |
| 42 | cur_dir_path = os.path.abspath(os.path.dirname(filepath)) |
| 43 | while True: |
| 44 | if os.path.exists(os.path.join(cur_dir_path, relpath)): |
| 45 | return cur_dir_path |
| 46 | |
| 47 | next_dir_path = os.path.dirname(cur_dir_path) |
| 48 | if next_dir_path != cur_dir_path: |
| 49 | cur_dir_path = next_dir_path |
| 50 | else: |
| 51 | return None |
| 52 | |
| 53 | |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 54 | def get_out_dir(args): |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 55 | if args.out_dir: |
| 56 | out_dir = args.out_dir |
| 57 | |
| 58 | if not os.path.isabs(out_dir): |
| Adam MacBeth | a225d5d | 2019-05-03 20:50:50 +0000 | [diff] [blame] | 59 | out_dir = os.path.join(FUCHSIA_ROOT, out_dir) |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 60 | |
| 61 | if not os.path.isdir(out_dir): |
| 62 | print out_dir + " is not a directory" |
| 63 | sys.exit(-1) |
| 64 | return out_dir |
| 65 | |
| Adam MacBeth | a225d5d | 2019-05-03 20:50:50 +0000 | [diff] [blame] | 66 | fuchsia_config_file = os.path.join(FUCHSIA_ROOT, '.fx-build-dir') |
| Ian McKellar | 311ff97 | 2018-07-02 13:30:37 -0700 | [diff] [blame] | 67 | if os.path.isfile(fuchsia_config_file): |
| 68 | fuchsia_config = open(fuchsia_config_file).read() |
| Adam MacBeth | a225d5d | 2019-05-03 20:50:50 +0000 | [diff] [blame] | 69 | return os.path.join(FUCHSIA_ROOT, fuchsia_config.strip()) |
| Ian McKellar | 311ff97 | 2018-07-02 13:30:37 -0700 | [diff] [blame] | 70 | |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 71 | print("Couldn't find the output directory, pass --out-dir " + |
| Adam MacBeth | a225d5d | 2019-05-03 20:50:50 +0000 | [diff] [blame] | 72 | "(absolute or relative to Fuchsia root)") |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 73 | sys.exit(-1) |
| 74 | |
| 75 | |
| 76 | def generate_db(out_dir): |
| 77 | cmd = [NINJA_TOOL, "-C", out_dir, "-t", "compdb", "cc", "cxx"] |
| 78 | db = subprocess.check_output( |
| Adam MacBeth | a225d5d | 2019-05-03 20:50:50 +0000 | [diff] [blame] | 79 | cmd, cwd=FUCHSIA_ROOT, universal_newlines=True) |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 80 | |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 81 | with open(os.path.join(out_dir, "compile_commands.json"), "w+") as db_file: |
| 82 | db_file.write(db) |
| 83 | |
| 84 | |
| 85 | def go(args): |
| 86 | out_dir = get_out_dir(args) |
| 87 | |
| 88 | # generate the compilation database |
| 89 | generate_db(out_dir) |
| 90 | |
| Przemyslaw Pietrzkiewicz | 74a450e | 2017-08-08 11:36:10 +0200 | [diff] [blame] | 91 | # Find the files to be checked. |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 92 | if args.all: |
| 93 | files = git_utils.get_all_files() |
| 94 | else: |
| 95 | files = git_utils.get_diff_files() |
| 96 | |
| Przemyslaw Pietrzkiewicz | 74a450e | 2017-08-08 11:36:10 +0200 | [diff] [blame] | 97 | filtered_files = [] |
| 98 | for file_path in files: |
| 99 | # Skip deleted files. |
| 100 | if not os.path.isfile(file_path): |
| 101 | if args.verbose: |
| 102 | print "skipping " + file_path + " (deleted)" |
| 103 | continue |
| 104 | |
| 105 | # Skip files with parent directories containing .nolint |
| 106 | if find_ancestor_with(file_path, ".nolint"): |
| 107 | if args.verbose: |
| 108 | print "skipping " + file_path + " (.nolint)" |
| 109 | continue |
| 110 | filtered_files.append(file_path) |
| 111 | |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 112 | if args.verbose: |
| 113 | print |
| Przemyslaw Pietrzkiewicz | 74a450e | 2017-08-08 11:36:10 +0200 | [diff] [blame] | 114 | print "Files to be checked:" |
| 115 | for file in filtered_files: |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 116 | print " - " + file |
| Przemyslaw Pietrzkiewicz | 74a450e | 2017-08-08 11:36:10 +0200 | [diff] [blame] | 117 | if not filtered_files: |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 118 | print " (no files)" |
| 119 | print |
| 120 | |
| 121 | # change the working directory to Fuchsia root. |
| Adam MacBeth | a225d5d | 2019-05-03 20:50:50 +0000 | [diff] [blame] | 122 | os.chdir(FUCHSIA_ROOT) |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 123 | |
| Przemyslaw Pietrzkiewicz | 5637c8c | 2017-08-03 15:57:52 +0200 | [diff] [blame] | 124 | # It's not safe to run in parallel with "--fix", as clang-tidy traverses and |
| 125 | # fixes header files, and we might end up with concurrent writes to the same |
| 126 | # header file. |
| 127 | if args.no_parallel or args.fix: |
| Przemyslaw Pietrzkiewicz | 49f5c9e | 2017-08-02 13:11:25 +0200 | [diff] [blame] | 128 | parallel_jobs = 1 |
| 129 | else: |
| 130 | parallel_jobs = multiprocessing.cpu_count() |
| 131 | print("Running " + str(parallel_jobs) + |
| 132 | " jobs in parallel, pass --no-parallel to disable") |
| 133 | |
| 134 | jobs = set() |
| 135 | |
| Przemyslaw Pietrzkiewicz | 74a450e | 2017-08-08 11:36:10 +0200 | [diff] [blame] | 136 | for file_path in filtered_files: |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 137 | _, extension = os.path.splitext(file_path) |
| 138 | if extension == ".cc": |
| 139 | relpath = os.path.relpath(file_path) |
| 140 | cmd = [CLANG_TIDY_TOOL, "-p", out_dir, relpath] |
| 141 | if args.checks: |
| 142 | cmd.append("-checks=" + args.checks) |
| 143 | if args.fix: |
| 144 | cmd.append("-fix") |
| Przemyslaw Pietrzkiewicz | 74a450e | 2017-08-08 11:36:10 +0200 | [diff] [blame] | 145 | if not args.verbose: |
| 146 | cmd.append("-quiet") |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 147 | |
| 148 | if args.verbose: |
| Przemyslaw Pietrzkiewicz | 49f5c9e | 2017-08-02 13:11:25 +0200 | [diff] [blame] | 149 | print "checking " + file_path + ": " + str(cmd) |
| 150 | jobs.add(subprocess.Popen(cmd)) |
| 151 | if len(jobs) >= parallel_jobs: |
| 152 | os.wait() |
| 153 | jobs.difference_update( |
| 154 | [job for job in jobs if job.poll() is not None]) |
| 155 | for job in jobs: |
| 156 | if job.poll() is None: |
| 157 | job.wait() |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 158 | |
| 159 | |
| 160 | def main(): |
| 161 | parser = argparse.ArgumentParser(description="Lint modified files.") |
| 162 | parser.add_argument( |
| 163 | "--all", |
| 164 | dest="all", |
| Mahesh Saripalli | 8e446e70 | 2023-08-02 19:40:42 +0000 | [diff] [blame] | 165 | action=argparse.BooleanOptionalAction, |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 166 | default=False, |
| 167 | help="process all files in the repo under current working directory") |
| 168 | parser.add_argument( |
| 169 | "--fix", |
| 170 | dest="fix", |
| Mahesh Saripalli | 8e446e70 | 2023-08-02 19:40:42 +0000 | [diff] [blame] | 171 | action=argparse.BooleanOptionalAction, |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 172 | default=False, |
| 173 | help="automatically generate fixes when possible") |
| 174 | parser.add_argument("--checks", help="overrides the list of checks to use") |
| 175 | parser.add_argument( |
| 176 | "--out-dir", |
| 177 | help="Output directory, needed to generate compilation db for clang.") |
| 178 | parser.add_argument( |
| Przemyslaw Pietrzkiewicz | 49f5c9e | 2017-08-02 13:11:25 +0200 | [diff] [blame] | 179 | "--no-parallel", |
| Mahesh Saripalli | 8e446e70 | 2023-08-02 19:40:42 +0000 | [diff] [blame] | 180 | action=argparse.BooleanOptionalAction, |
| Przemyslaw Pietrzkiewicz | 49f5c9e | 2017-08-02 13:11:25 +0200 | [diff] [blame] | 181 | default=False, |
| 182 | help="Process one file at a time") |
| 183 | parser.add_argument( |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 184 | "--verbose", |
| 185 | dest="verbose", |
| Mahesh Saripalli | 8e446e70 | 2023-08-02 19:40:42 +0000 | [diff] [blame] | 186 | action=argparse.BooleanOptionalAction, |
| Przemyslaw Pietrzkiewicz | 3fa398e | 2017-07-31 16:40:11 +0200 | [diff] [blame] | 187 | default=False, |
| 188 | help="tell me what you're doing") |
| 189 | args = parser.parse_args() |
| 190 | go(args) |
| 191 | |
| 192 | return 0 |
| 193 | |
| 194 | |
| 195 | if __name__ == "__main__": |
| 196 | sys.exit(main()) |