| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 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 # To integrate dartanalyze with out build system, we take an input file, run | 6 # To integrate dartanalyze with out build system, we take an input file, run |
| 7 # the analyzer on it, and write a stamp file if it passed. | 7 # the analyzer on it, and write a stamp file if it passed. |
| 8 # | |
| 9 # The first argument to this script is a reference to this build's gen | |
| 10 # directory, which we treat as the package root. The second is the stamp file | |
| 11 # to touch if we succeed. The rest are passed to the analyzer verbatim. | |
| 12 | 8 |
| 9 # This script can either analyze a dartzip package, specified with the |
| 10 # --dartzip-file flag, or a set of entrypoints specified with the --entrypoints |
| 11 # flag. The location of the Dart SDK must be specified with the --dart-sdk |
| 12 # flag. A stamp file can optionally be written with the location given by the |
| 13 # --stamp-file flag. Any command line arguments not recognized by this script |
| 14 # are passed on to the Dart analyzer. |
| 15 |
| 16 import argparse |
| 13 import glob | 17 import glob |
| 14 import os | 18 import os |
| 15 import re | 19 import re |
| 16 import shutil | 20 import shutil |
| 17 import subprocess | 21 import subprocess |
| 18 import sys | 22 import sys |
| 19 import tempfile | 23 import tempfile |
| 20 import zipfile | 24 import zipfile |
| 21 | 25 |
| 22 _IGNORED_PATTERNS = [ | 26 _IGNORED_PATTERNS = [ |
| 23 # Ignored because they're not indicative of specific errors. | 27 # Ignored because they're not indicative of specific errors. |
| 24 re.compile(r'^$'), | 28 re.compile(r'^$'), |
| 25 re.compile(r'^Analyzing \['), | 29 re.compile(r'^Analyzing \['), |
| 26 re.compile(r'^No issues found'), | 30 re.compile(r'^No issues found'), |
| 27 re.compile(r'^[0-9]+ errors? and [0-9]+ warnings? found.'), | 31 re.compile(r'^[0-9]+ errors? and [0-9]+ warnings? found.'), |
| 28 re.compile(r'^([0-9]+|No) (error|warning|issue)s? found.'), | 32 re.compile(r'^([0-9]+|No) (error|warning|issue)s? found.'), |
| 29 | 33 |
| 30 # TODO: It seems like this should be re-enabled evenutally. | 34 # TODO: It seems like this should be re-enabled evenutally. |
| 31 re.compile(r'.*is a part and can not|^Only libraries can be analyzed'), | 35 re.compile(r'.*is a part and can not|^Only libraries can be analyzed'), |
| 32 # TODO: Remove this once dev SDK includes Uri.directory constructor. | 36 # TODO: Remove this once dev SDK includes Uri.directory constructor. |
| 33 re.compile(r'.*The class \'Uri\' does not have a constructor \'directory\''), | 37 re.compile(r'.*The class \'Uri\' does not have a constructor \'directory\''), |
| 34 # TODO: Remove this once Sky no longer generates this warning. | 38 # TODO: Remove this once Sky no longer generates this warning. |
| 35 # dartbug.com/22836 | 39 # dartbug.com/22836 |
| 36 re.compile(r'.*cannot both be unnamed'), | 40 re.compile(r'.*cannot both be unnamed'), |
| 37 ] | 41 ] |
| 38 | 42 |
| 43 |
| 39 def _success(stamp_file): | 44 def _success(stamp_file): |
| 40 # We passed cleanly, so touch the stamp file so that we don't run again. | 45 # We passed cleanly, so touch the stamp file so that we don't run again. |
| 41 with open(stamp_file, 'a'): | 46 with open(stamp_file, 'a'): |
| 42 os.utime(stamp_file, None) | 47 os.utime(stamp_file, None) |
| 43 return 0 | 48 return 0 |
| 44 | 49 |
| 45 def main(args): | |
| 46 dartzip_file = args.pop(0) | |
| 47 stamp_file = args.pop(0) | |
| 48 | 50 |
| 49 # Do not run dart analyzer on third_party sources. | 51 def analyze_and_filter(cmd, temp_dir=None, dirname=None): |
| 50 if "/third_party/" in dartzip_file: | 52 errors = None |
| 51 return _success(stamp_file) | 53 try: |
| 54 subprocess.check_output(cmd, shell=False, stderr=subprocess.STDOUT) |
| 55 except subprocess.CalledProcessError as e: |
| 56 errors = set(l for l in e.output.split('\n') |
| 57 if not any(p.match(l) for p in _IGNORED_PATTERNS)) |
| 58 for error in sorted(errors): |
| 59 if dirname is None: |
| 60 print >> sys.stderr, error |
| 61 else: |
| 62 print >> sys.stderr, error.replace(temp_dir + "/", dirname) |
| 63 return errors |
| 52 | 64 |
| 65 |
| 66 def analyze_dartzip(dart_sdk, dartzip_file, stamp_file, args): |
| 53 dartzip_basename = os.path.basename(dartzip_file) + ":" | 67 dartzip_basename = os.path.basename(dartzip_file) + ":" |
| 54 | 68 |
| 55 # Unzip |dartzip_file| to a temporary directory. | 69 # Unzip |dartzip_file| to a temporary directory. |
| 56 try: | 70 try: |
| 57 temp_dir = tempfile.mkdtemp() | 71 temp_dir = tempfile.mkdtemp() |
| 58 zipfile.ZipFile(dartzip_file).extractall(temp_dir) | 72 zipfile.ZipFile(dartzip_file).extractall(temp_dir) |
| 59 | 73 |
| 60 cmd = [ | 74 cmd = [ os.path.join(dart_sdk, 'bin', 'dartanalyzer') ] |
| 61 "../../third_party/dart-sdk/dart-sdk/bin/dartanalyzer", | |
| 62 ] | |
| 63 | 75 |
| 64 # Grab all the toplevel dart files in the archive. | 76 # Grab all the toplevel dart files in the archive. |
| 65 dart_files = glob.glob(os.path.join(temp_dir, "*.dart")) | 77 dart_files = glob.glob(os.path.join(temp_dir, "*.dart")) |
| 66 | 78 |
| 67 if not dart_files: | 79 if not dart_files: |
| 68 return _success(stamp_file) | 80 return _success(stamp_file) |
| 69 | 81 |
| 70 cmd.extend(dart_files) | 82 cmd.extend(dart_files) |
| 71 cmd.extend(args) | 83 cmd.extend(args) |
| 72 cmd.append("--package-root=%s/packages" % temp_dir) | 84 cmd.append("--package-root=%s/packages" % temp_dir) |
| 73 cmd.append("--fatal-warnings") | 85 cmd.append("--fatal-warnings") |
| 74 | 86 |
| 75 errors = 0 | 87 errors = analyze_and_filter(cmd, temp_dir, dartzip_basename) |
| 76 try: | |
| 77 subprocess.check_output(cmd, shell=False, stderr=subprocess.STDOUT) | |
| 78 except subprocess.CalledProcessError as e: | |
| 79 errors = set(l for l in e.output.split('\n') | |
| 80 if not any(p.match(l) for p in _IGNORED_PATTERNS)) | |
| 81 for error in sorted(errors): | |
| 82 print >> sys.stderr, error.replace(temp_dir + "/", dartzip_basename) | |
| 83 | 88 |
| 84 if not errors: | 89 if errors is None: |
| 85 return _success(stamp_file) | 90 return _success(stamp_file) |
| 86 return min(255, len(errors)) | 91 return min(255, len(errors)) |
| 87 finally: | 92 finally: |
| 88 shutil.rmtree(temp_dir) | 93 shutil.rmtree(temp_dir) |
| 89 | 94 |
| 95 |
| 96 def analyze_entrypoints(dart_sdk, entrypoints, args): |
| 97 cmd = [ os.path.join(dart_sdk, 'bin', 'dartanalyzer') ] |
| 98 cmd.extend(entrypoints) |
| 99 cmd.extend(args) |
| 100 cmd.append("--fatal-warnings") |
| 101 errors = analyze_and_filter(cmd) |
| 102 if errors is None: |
| 103 return 0 |
| 104 return min(255, len(errors)) |
| 105 |
| 106 |
| 107 def main(): |
| 108 parser = argparse.ArgumentParser(description='Run the Dart analyzer.') |
| 109 parser.add_argument('--dart-sdk', |
| 110 action='store', |
| 111 type=str, |
| 112 metavar='dart_sdk', |
| 113 help='Path to the Dart SDK', |
| 114 required=True) |
| 115 parser.add_argument('--dartzip-file', |
| 116 action='store', |
| 117 type=str, |
| 118 metavar='dartzip_file', |
| 119 help='dartzip file whose contents to analyze', |
| 120 default=None) |
| 121 parser.add_argument('--stamp-file', |
| 122 action='store', |
| 123 type=str, |
| 124 metavar='stamp_file', |
| 125 help='Stamp file to write on success.', |
| 126 default=None) |
| 127 parser.add_argument('--entrypoints', |
| 128 help='Entry points to analyze', |
| 129 nargs='*', |
| 130 default=[]) |
| 131 args, remainder = parser.parse_known_args() |
| 132 |
| 133 if args.dartzip_file is None and args.entrypoints == []: |
| 134 parser.print_help() |
| 135 return 1 |
| 136 |
| 137 if args.dartzip_file is not None: |
| 138 # Do not run dart analyzer on third_party sources. |
| 139 if "/third_party/" in args.dartzip_file: |
| 140 return _success(args.stamp_file) |
| 141 return analyze_dartzip(args.dart_sdk, args.dartzip_file, args.stamp_file, |
| 142 remainder) |
| 143 |
| 144 if args.entrypoints != []: |
| 145 return analyze_entrypoints(args.dart_sdk, args.entrypoints, remainder) |
| 146 |
| 90 if __name__ == '__main__': | 147 if __name__ == '__main__': |
| 91 sys.exit(main(sys.argv[1:])) | 148 sys.exit(main()) |
| OLD | NEW |