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