verify_filelist.py revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
1#!/usr/bin/env python 2# Copyright (c) 2013 The Chromium 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 6import optparse 7import os 8import re 9import sys 10 11from build_paths import SDK_SRC_DIR 12 13# Add SDK make tools scripts to the python path. 14sys.path.append(os.path.join(SDK_SRC_DIR, 'tools')) 15 16import getos 17 18VALID_PLATFORMS = ['linux', 'mac', 'win'] 19PLATFORM_PREFIX_RE = re.compile(r'^\[([^\]]*)\](.*)$') 20 21class ParseException(Exception): 22 def __init__(self, filename, line, message): 23 Exception.__init__(self) 24 self.filename = filename 25 self.line = line 26 self.message = message 27 28 def __str__(self): 29 return '%s:%d: %s' % (self.filename, self.line, self.message) 30 31 32def SplitPattern(pattern): 33 match = PLATFORM_PREFIX_RE.match(pattern) 34 if not match: 35 return pattern, [] 36 37 # platform-specific line 38 platforms = match.group(1).split(',') 39 40 # If this platform is included, strip the [...] part. 41 pattern = match.group(2) 42 return pattern, platforms 43 44 45class VerifyException(Exception): 46 pass 47 48class Rules(object): 49 def __init__(self, filename, platform=None, contents=None): 50 self.glob_prefixes = [] 51 self.exact_filenames = set() 52 self.filename = filename 53 self.platform = platform or getos.GetPlatform() 54 self.exe_ext = '.exe' if self.platform == 'win' else '' 55 56 if self.platform not in VALID_PLATFORMS: 57 raise ParseException(self.filename, 1, 58 'Unknown platform %s' % self.platform) 59 60 if not contents: 61 with open(filename) as f: 62 contents = f.read() 63 64 for line_no, rule in enumerate(contents.split('\n')): 65 rule = rule.strip() 66 if rule: 67 self.ParsePattern(line_no + 1, rule) 68 69 def ParsePattern(self, line_no, pattern): 70 pattern, platforms = SplitPattern(pattern) 71 if platforms: 72 unknown_platforms = set(platforms) - set(VALID_PLATFORMS) 73 if unknown_platforms: 74 msg = 'Unknown platform(s) %s.' % ( 75 ', '.join('"%s"' % platform for platform in unknown_platforms)) 76 raise ParseException(self.filename, line_no, msg) 77 if self.platform not in platforms: 78 return 79 80 pattern = pattern.replace('${PLATFORM}', self.platform) 81 pattern = pattern.replace('${EXE_EXT}', self.exe_ext) 82 83 if '*' in pattern: 84 # glob pattern 85 # We only support * at the end. 86 if pattern.find('*') != len(pattern) - 1: 87 msg = '* is only allowed at the end of the line.' 88 raise ParseException(self.filename, line_no, msg) 89 90 # Remove the * 91 pattern = pattern[:-1] 92 self.glob_prefixes.append(pattern) 93 else: 94 self.exact_filenames.add(pattern) 95 96 def VerifyDirectoryList(self, directory_list): 97 exact_filenames_used = set() 98 glob_prefixes_used = set() 99 expected_globs = set() 100 expected_filenames = set() 101 unexpected_filenames = set() 102 103 for filename in directory_list: 104 if os.path.sep != '/': 105 filename = filename.replace(os.path.sep, '/') 106 if filename in self.exact_filenames: 107 exact_filenames_used.add(filename) 108 continue 109 110 # glob pattern 111 found_prefix = False 112 for prefix in self.glob_prefixes: 113 if filename.startswith(prefix): 114 glob_prefixes_used.add(prefix) 115 found_prefix = True 116 break 117 118 if not found_prefix: 119 unexpected_filenames.add(filename) 120 121 if len(exact_filenames_used) != len(self.exact_filenames): 122 # We looped through the directory list, so if the lengths are unequal, it 123 # must be that we expected something that isn't there. 124 expected_filenames = self.exact_filenames - exact_filenames_used 125 126 if len(glob_prefixes_used) != self.glob_prefixes: 127 expected_globs = set(self.glob_prefixes) - glob_prefixes_used 128 129 if expected_filenames or unexpected_filenames or expected_globs: 130 msg = '' 131 if unexpected_filenames: 132 msg += '>>> Unexpected filenames: <<<\n%s\n' % ( 133 '\n'.join(sorted(unexpected_filenames))) 134 if expected_filenames: 135 msg += '>>> Expected filenames: <<<\n%s\n' % ( 136 '\n'.join(sorted(expected_filenames))) 137 if expected_globs: 138 msg += '>>> Expected 1+ files in these directories: <<< \n%s\n' % ( 139 '\n'.join(sorted(expected_globs))) 140 raise VerifyException(msg) 141 142 143def GetDirectoryList(directory_path): 144 result = [] 145 for root, _, files in os.walk(directory_path): 146 rel_root = os.path.relpath(root, directory_path) 147 if rel_root == '.': 148 rel_root = '' 149 for base_name in files: 150 result.append(os.path.join(rel_root, base_name)) 151 return result 152 153 154def Verify(rule_path, directory_path, platform=None): 155 rules = Rules(rule_path, platform=platform) 156 directory_list = GetDirectoryList(directory_path) 157 rules.VerifyDirectoryList(directory_list) 158 159 160def SortFile(rule_path): 161 with open(rule_path) as infile: 162 lines = infile.readlines() 163 164 def compare(line1, line2): 165 line1 = SplitPattern(line1)[0].lower() 166 line2 = SplitPattern(line2)[0].lower() 167 return cmp(line1, line2) 168 169 lines.sort(compare) 170 with open(rule_path, 'w') as output: 171 for line in lines: 172 output.write(line) 173 174 175def main(args): 176 parser = optparse.OptionParser(usage='%prog <rule file> <directory>') 177 parser.add_option('-p', '--platform', 178 help='Test with this platform, instead of the system\'s platform') 179 parser.add_option('-s', '--sort', action='store_true', 180 help='Sort the file list in place, rather than verifying the contents.') 181 options, args = parser.parse_args(args) 182 183 if options.sort: 184 if not args: 185 parser.error('Expected rule file.') 186 SortFile(args[0]) 187 return 0 188 189 if len(args) != 2: 190 parser.error('Expected rule file and directory.') 191 192 rule_path, directory_path = args 193 if options.platform: 194 if options.platform not in VALID_PLATFORMS: 195 parser.error('Unknown platform: %s' % options.platform) 196 platform = options.platform 197 else: 198 platform = getos.GetPlatform() 199 200 try: 201 return Verify(rule_path, directory_path, platform) 202 except ParseException, e: 203 print >> sys.stderr, 'Error parsing rules:\n', e 204 return 1 205 except VerifyException, e: 206 print >> sys.stderr, 'Error verifying file list:\n', e 207 return 1 208 return 0 209 210 211if __name__ == '__main__': 212 sys.exit(main(sys.argv[1:])) 213