1cb93a386Sopenharmony_ci# Copyright (c) 2012 The Chromium Authors. All rights reserved. 2cb93a386Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be 3cb93a386Sopenharmony_ci# found in the LICENSE file. 4cb93a386Sopenharmony_ci 5cb93a386Sopenharmony_ci"""Checks protobuf files for illegal imports.""" 6cb93a386Sopenharmony_ci 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci 9cb93a386Sopenharmony_ciimport codecs 10cb93a386Sopenharmony_ciimport os 11cb93a386Sopenharmony_ciimport re 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ciimport results 14cb93a386Sopenharmony_cifrom rules import Rule, MessageRule 15cb93a386Sopenharmony_ci 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_ciclass ProtoChecker(object): 18cb93a386Sopenharmony_ci 19cb93a386Sopenharmony_ci EXTENSIONS = [ 20cb93a386Sopenharmony_ci '.proto', 21cb93a386Sopenharmony_ci ] 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ci # The maximum number of non-import lines we can see before giving up. 24cb93a386Sopenharmony_ci _MAX_UNINTERESTING_LINES = 50 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci # The maximum line length, this is to be efficient in the case of very long 27cb93a386Sopenharmony_ci # lines (which can't be import). 28cb93a386Sopenharmony_ci _MAX_LINE_LENGTH = 128 29cb93a386Sopenharmony_ci 30cb93a386Sopenharmony_ci # This regular expression will be used to extract filenames from import 31cb93a386Sopenharmony_ci # statements. 32cb93a386Sopenharmony_ci _EXTRACT_IMPORT_PATH = re.compile( 33cb93a386Sopenharmony_ci r'[ \t]*[ \t]*import[ \t]+"(.*)"') 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci def __init__(self, verbose, resolve_dotdot=False, root_dir=''): 36cb93a386Sopenharmony_ci self._verbose = verbose 37cb93a386Sopenharmony_ci self._resolve_dotdot = resolve_dotdot 38cb93a386Sopenharmony_ci self._root_dir = root_dir 39cb93a386Sopenharmony_ci 40cb93a386Sopenharmony_ci def IsFullPath(self, import_path): 41cb93a386Sopenharmony_ci """Checks if the given path is a valid path starting from |_root_dir|.""" 42cb93a386Sopenharmony_ci match = re.match(r'(.*)/([^/]*\.proto)', import_path) 43cb93a386Sopenharmony_ci if not match: 44cb93a386Sopenharmony_ci return False 45cb93a386Sopenharmony_ci return os.path.isdir(self._root_dir + "/" + match.group(1)) 46cb93a386Sopenharmony_ci 47cb93a386Sopenharmony_ci def CheckLine(self, rules, line, dependee_path, fail_on_temp_allow=False): 48cb93a386Sopenharmony_ci """Checks the given line with the given rule set. 49cb93a386Sopenharmony_ci 50cb93a386Sopenharmony_ci Returns a tuple (is_import, dependency_violation) where 51cb93a386Sopenharmony_ci is_import is True only if the line is an import 52cb93a386Sopenharmony_ci statement, and dependency_violation is an instance of 53cb93a386Sopenharmony_ci results.DependencyViolation if the line violates a rule, or None 54cb93a386Sopenharmony_ci if it does not. 55cb93a386Sopenharmony_ci """ 56cb93a386Sopenharmony_ci found_item = self._EXTRACT_IMPORT_PATH.match(line) 57cb93a386Sopenharmony_ci if not found_item: 58cb93a386Sopenharmony_ci return False, None # Not a match 59cb93a386Sopenharmony_ci 60cb93a386Sopenharmony_ci import_path = found_item.group(1) 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ci if '\\' in import_path: 63cb93a386Sopenharmony_ci return True, results.DependencyViolation( 64cb93a386Sopenharmony_ci import_path, 65cb93a386Sopenharmony_ci MessageRule('Import paths may not include backslashes.'), 66cb93a386Sopenharmony_ci rules) 67cb93a386Sopenharmony_ci 68cb93a386Sopenharmony_ci if '/' not in import_path: 69cb93a386Sopenharmony_ci # Don't fail when no directory is specified. We may want to be more 70cb93a386Sopenharmony_ci # strict about this in the future. 71cb93a386Sopenharmony_ci if self._verbose: 72cb93a386Sopenharmony_ci print(' WARNING: import specified with no directory: ' + import_path) 73cb93a386Sopenharmony_ci return True, None 74cb93a386Sopenharmony_ci 75cb93a386Sopenharmony_ci if self._resolve_dotdot and '../' in import_path: 76cb93a386Sopenharmony_ci dependee_dir = os.path.dirname(dependee_path) 77cb93a386Sopenharmony_ci import_path = os.path.join(dependee_dir, import_path) 78cb93a386Sopenharmony_ci import_path = os.path.relpath(import_path, self._root_dir) 79cb93a386Sopenharmony_ci 80cb93a386Sopenharmony_ci if not self.IsFullPath(import_path): 81cb93a386Sopenharmony_ci return True, None 82cb93a386Sopenharmony_ci 83cb93a386Sopenharmony_ci rule = rules.RuleApplyingTo(import_path, dependee_path) 84cb93a386Sopenharmony_ci 85cb93a386Sopenharmony_ci if (rule.allow == Rule.DISALLOW or 86cb93a386Sopenharmony_ci (fail_on_temp_allow and rule.allow == Rule.TEMP_ALLOW)): 87cb93a386Sopenharmony_ci return True, results.DependencyViolation(import_path, rule, rules) 88cb93a386Sopenharmony_ci return True, None 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci def CheckFile(self, rules, filepath): 91cb93a386Sopenharmony_ci if self._verbose: 92cb93a386Sopenharmony_ci print('Checking: ' + filepath) 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci dependee_status = results.DependeeStatus(filepath) 95cb93a386Sopenharmony_ci last_import = 0 96cb93a386Sopenharmony_ci with codecs.open(filepath, encoding='utf-8') as f: 97cb93a386Sopenharmony_ci for line_num, line in enumerate(f): 98cb93a386Sopenharmony_ci if line_num - last_import > self._MAX_UNINTERESTING_LINES: 99cb93a386Sopenharmony_ci break 100cb93a386Sopenharmony_ci 101cb93a386Sopenharmony_ci line = line.strip() 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_ci is_import, violation = self.CheckLine(rules, line, filepath) 104cb93a386Sopenharmony_ci if is_import: 105cb93a386Sopenharmony_ci last_import = line_num 106cb93a386Sopenharmony_ci if violation: 107cb93a386Sopenharmony_ci dependee_status.AddViolation(violation) 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci return dependee_status 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_ci @staticmethod 112cb93a386Sopenharmony_ci def IsProtoFile(file_path): 113cb93a386Sopenharmony_ci """Returns True iff the given path ends in one of the extensions 114cb93a386Sopenharmony_ci handled by this checker. 115cb93a386Sopenharmony_ci """ 116cb93a386Sopenharmony_ci return os.path.splitext(file_path)[1] in ProtoChecker.EXTENSIONS 117cb93a386Sopenharmony_ci 118cb93a386Sopenharmony_ci def ShouldCheck(self, file_path): 119cb93a386Sopenharmony_ci """Check if the new #include file path should be presubmit checked. 120cb93a386Sopenharmony_ci 121cb93a386Sopenharmony_ci Args: 122cb93a386Sopenharmony_ci file_path: file path to be checked 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci Return: 125cb93a386Sopenharmony_ci bool: True if the file should be checked; False otherwise. 126cb93a386Sopenharmony_ci """ 127cb93a386Sopenharmony_ci return self.IsProtoFile(file_path) 128