OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2017 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Invokes the Clang static analysis command using arguments provided on the | 6 """Adds an analysis build step to invocations of the Clang C/C++ compiler. |
7 command line. | 7 |
| 8 Usage: clang_static_analyzer_wrapper.py <compiler> [args...] |
8 """ | 9 """ |
9 | 10 |
10 import argparse | 11 import argparse |
11 import fnmatch | 12 import fnmatch |
| 13 import itertools |
12 import os | 14 import os |
13 import shutil | |
14 import sys | 15 import sys |
15 import tempfile | 16 import wrapper_utils |
16 | 17 |
17 import wrapper_utils | 18 # Flags used to enable analysis for Clang invocations. |
| 19 analyzer_enable_flags = [ |
| 20 '-analyze', |
| 21 '-fdiagnostics-show-option', |
| 22 ] |
| 23 |
| 24 # Flags used to configure the analyzer's behavior. |
| 25 analyzer_option_flags = [ |
| 26 '-analyzer-checker=cplusplus', |
| 27 '-analyzer-opt-analyze-nested-blocks', |
| 28 '-analyzer-eagerly-assume', |
| 29 '-analyzer-output=text', |
| 30 '-analyzer-config', |
| 31 'suppress-c++-stdlib=true', |
| 32 |
| 33 # List of checkers to execute. |
| 34 # The full list of checkers can be found at |
| 35 # https://clang-analyzer.llvm.org/available_checks.html. |
| 36 '-analyzer-checker=core', |
| 37 '-analyzer-checker=unix', |
| 38 '-analyzer-checker=deadcode', |
| 39 ] |
| 40 |
| 41 parser = argparse.ArgumentParser() |
| 42 parser.add_argument('--clang-prefix', |
| 43 type=str, |
| 44 action='store', |
| 45 nargs='?') |
| 46 parser.add_argument('args', nargs=argparse.REMAINDER) |
| 47 |
| 48 |
| 49 # Interleave flags w/a prefix so that the options are passed correctly |
| 50 # to the analyzer. |
| 51 # For Windows, we use '-Xclang'. |
| 52 # For Linux, we use '-Xanalyzer'. |
| 53 # |
| 54 # Example output: |
| 55 # e.g. ['-analyzer-foo', '-analyzer-bar'] => ['-Xanalyzer', '-analyzer-foo', |
| 56 # '-Xanalyzer', '-analyzer-bar'] |
| 57 def interleave_args(args, token): |
| 58 return list(sum(zip([token] * len(args), args), ())) |
18 | 59 |
19 | 60 |
20 def main(): | 61 def main(): |
21 parser = argparse.ArgumentParser(description=__doc__) | 62 parsed_args = parser.parse_args() |
22 parser.add_argument('--clang-cc-path', | |
23 help='Path to the clang compiler.', | |
24 metavar='PATH') | |
25 parser.add_argument('--clang-cxx-path', | |
26 help='Path to the clang++ compiler', | |
27 metavar='PATH') | |
28 parser.add_argument('--analyzer', | |
29 help='Path to the language-specific Clang analysis tool.', | |
30 required=True, | |
31 metavar='PATH') | |
32 args, compile_args = parser.parse_known_args() | |
33 | 63 |
34 # Check that only one of --clang-cc-path or --clang-cxx-path are set. | 64 # Build the object file and proceed with analysis if it is buildable. |
35 assert ((args.clang_cc_path != None) != (args.clang_cxx_path != None)) | 65 returncode, stderr = wrapper_utils.CaptureCommandStderr( |
| 66 wrapper_utils.CommandToRun(parsed_args.args)) |
| 67 sys.stderr.write(stderr) |
| 68 if returncode != 0: |
| 69 return returncode |
36 | 70 |
37 is_cxx = args.clang_cxx_path != None | 71 analyzer_flags = analyzer_enable_flags + analyzer_option_flags |
38 env = os.environ | 72 if parsed_args.clang_prefix is not None: |
39 env['CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE'] = '0' | 73 analyzer_flags = interleave_args(analyzer_flags, parsed_args.clang_prefix) |
40 env['CCC_ANALYZER_OUTPUT_FORMAT'] = 'text' | 74 args = parsed_args.args + analyzer_flags |
41 clang_path = args.clang_cxx_path or args.clang_cc_path | 75 returncode, stderr = wrapper_utils.CaptureCommandStderr( |
42 if is_cxx: | 76 wrapper_utils.CommandToRun(args)) |
43 env['CCC_CXX'] = clang_path | 77 sys.stderr.write(stderr) |
44 env['CLANG_CXX'] = clang_path | 78 if returncode != 0: |
45 else: | 79 sys.stderr.write( |
46 env['CCC_CC'] = clang_path | 80 """WARNING! The Clang static analyzer exited with error code %d. |
47 env['CLANG'] = clang_path | 81 Please share the error details in crbug.com/695243 if this looks like |
| 82 a new regression.\n""" % (returncode)) |
48 | 83 |
49 # TODO(kmarshall): Place the summarized output in a useful directory. | 84 return 0 |
50 temp_dir = tempfile.mkdtemp() | |
51 try: | |
52 env['CCC_ANALYZER_HTML'] = temp_dir | |
53 returncode, stderr = wrapper_utils.CaptureCommandStderr( | |
54 wrapper_utils.CommandToRun([args.analyzer] + compile_args), env) | |
55 sys.stderr.write(stderr) | |
56 return returncode | |
57 finally: | |
58 shutil.rmtree(temp_dir) | |
59 | 85 |
60 if __name__ == "__main__": | 86 if __name__ == '__main__': |
61 sys.exit(main()) | 87 sys.exit(main()) |
OLD | NEW |